summaryrefslogtreecommitdiff
path: root/libdiskfs/node-drop.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdiskfs/node-drop.c')
-rw-r--r--libdiskfs/node-drop.c23
1 files changed, 21 insertions, 2 deletions
diff --git a/libdiskfs/node-drop.c b/libdiskfs/node-drop.c
index 8e0fccde..aafbc730 100644
--- a/libdiskfs/node-drop.c
+++ b/libdiskfs/node-drop.c
@@ -18,7 +18,8 @@
#include "priv.h"
/* Node NP now has no more references; clean all state. The
- diskfs_node_refcnt_lock must be held. */
+ diskfs_node_refcnt_lock must be held, and will be released
+ upon return. NP must be locked. */
void
diskfs_drop_node (struct node *np)
{
@@ -27,7 +28,24 @@ diskfs_drop_node (struct node *np)
if (np->dn_stat.st_nlink == 0)
{
assert (!diskfs_readonly);
- diskfs_truncate (np, 0);
+
+ if (np->allocsize != 0)
+ {
+ /* 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. */
+ np->references++;
+ spin_unlock (&diskfs_node_refcnt_lock);
+ diskfs_truncate (np, 0);
+ diskfs_nput (np);
+ return;
+ }
+
savemode = np->dn_stat.st_mode;
np->dn_stat.st_mode = 0;
np->dn_stat.st_rdev = 0;
@@ -39,6 +57,7 @@ diskfs_drop_node (struct node *np)
diskfs_node_update (np, 0);
diskfs_node_norefs (np);
+ spin_unlock (&diskfs_node_refcnt_lock);
}