/* 
   Copyright (C) 1994, 1995, 1996 Free Software Foundation

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#include "priv.h"
#include "diskfs.h"

/* Free the list of modification requests MR */
static void
free_modreqs (struct modreq *mr)
{
  struct modreq *tmp;
  for (; mr; mr = tmp)
    {
      mach_port_deallocate (mach_task_self (), mr->port);
      tmp = mr->next;
      free (mr);
    }
}


/* Node NP now has no more references; clean all state.  NP must be
   locked.  */
void
diskfs_drop_node (struct node *np)
{
  mode_t savemode;
  diskfs_transaction_t *txn = diskfs_journal_start_transaction ();

  /* XXX: if the filesystem is readonly, we cannot remove the files with no link
     but e.g. memory mapping still in memory.  This notably happens when
     upgrading packages without restarting the corresponding processes.  Fsck
     will have to fix them.  */
  if (np->dn_stat.st_nlink == 0 && !diskfs_readonly)
    {
      diskfs_check_readonly ();

      if (np->dn_stat.st_mode & S_IPTRANS)
	diskfs_set_translator (np, 0, 0, 0);

      if (np->allocsize != 0
	  || (diskfs_create_symlink_hook 
	      && S_ISLNK (np->dn_stat.st_mode)
	      && np->dn_stat.st_size))
	{
	  /* If the node needs to be truncated, then a complication
	     arises, because truncation might require gaining
	     new references to the node.  So, we give ourselves
	     a reference back, unlock the refcnt lock.  Then
	     we are in the state of a normal user, and do the truncate
	     and an nput.  The next time through, this routine
	     will notice that the size is zero, and not have to
	     do anything. */
	  refcounts_unsafe_ref (&np->refcounts, NULL);
	  diskfs_truncate (np, 0);

	  /* Force allocsize to zero; if truncate consistently fails this
	     will at least prevent an infinite loop in this routine. */
	  np->allocsize = 0;
	  if (diskfs_synchronous)
	    diskfs_journal_set_sync (txn);
	  diskfs_journal_stop_transaction (txn);

	  diskfs_nput (np);
	  return;
	}

      assert_backtrace (np->dn_stat.st_size == 0);

      savemode = np->dn_stat.st_mode;
      np->dn_stat.st_mode = 0;
      np->dn_stat.st_rdev = 0;
      np->dn_set_ctime = np->dn_set_atime = 1;
      diskfs_node_update (np, diskfs_synchronous);
      diskfs_free_node (np, savemode);
    }
  else
    diskfs_node_update (np,  diskfs_synchronous);

  fshelp_drop_transbox (&np->transbox);

  if (np->dirmod_reqs)
    free_modreqs (np->dirmod_reqs);
  if (np->filemod_reqs)
    free_modreqs (np->filemod_reqs);

  assert_backtrace (!np->sockaddr);

  pthread_mutex_unlock(&np->lock);
  if (diskfs_synchronous)
    diskfs_journal_set_sync (txn);
  diskfs_journal_stop_transaction (txn);
  pthread_mutex_destroy(&np->lock);
  diskfs_node_norefs (np);
}
