patch-2.1.131 linux/fs/namei.c
Next file: linux/fs/ncpfs/dir.c
Previous file: linux/fs/msdos/namei.c
Back to the patch index
Back to the overall index
- Lines: 159
- Date:
Tue Dec 1 22:09:48 1998
- Orig file:
v2.1.130/linux/fs/namei.c
- Orig date:
Thu Nov 19 09:56:28 1998
diff -u --recursive --new-file v2.1.130/linux/fs/namei.c linux/fs/namei.c
@@ -834,6 +834,37 @@
return error;
}
+/*
+ * Whee.. Deadlock country. Happily there are only two VFS
+ * operations that do this..
+ */
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
+{
+ struct semaphore *s1 = &d1->d_inode->i_sem;
+ struct semaphore *s2 = &d2->d_inode->i_sem;
+
+ if (s1 != s2) {
+ if ((unsigned long) s1 < (unsigned long) s2) {
+ struct semaphore *tmp = s2;
+ s2 = s1; s1 = tmp;
+ }
+ down(s1);
+ }
+ down(s2);
+}
+
+static inline void double_unlock(struct dentry *d1, struct dentry *d2)
+{
+ struct semaphore *s1 = &d1->d_inode->i_sem;
+ struct semaphore *s2 = &d2->d_inode->i_sem;
+
+ up(s1);
+ if (s1 != s2)
+ up(s2);
+ dput(d1);
+ dput(d2);
+}
+
static inline int do_rmdir(const char * name)
{
int error;
@@ -845,13 +876,20 @@
if (IS_ERR(dentry))
goto exit;
- dir = lock_parent(dentry);
- error = PTR_ERR(dir);
- if (IS_ERR(dir))
- goto exit_dput;
+ dir = dget(dentry->d_parent);
error = -ENOENT;
if (!dentry->d_inode)
+ goto exit;
+ /*
+ * The dentry->d_count stuff confuses d_delete() enough to
+ * not kill the inode from under us while it is locked. This
+ * wouldn't be needed, except the dentry semaphore is really
+ * in the inode, not in the dentry..
+ */
+ dentry->d_count++;
+ double_lock(dir, dentry);
+ if (dentry->d_parent != dir)
goto exit_lock;
error = -EROFS;
@@ -880,15 +918,35 @@
DQUOT_INIT(dir->d_inode);
- if (dentry->d_count > 1)
+ /*
+ * We try to drop the dentry early: we should have
+ * a usage count of 2 if we're the only user of this
+ * dentry, and if that is true (possibly after pruning
+ * the dcache), then we drop the dentry now.
+ *
+ * A low-level filesystem can, if it choses, legally
+ * do a
+ *
+ * if (!list_empty(&dentry->d_hash))
+ * return -EBUSY;
+ *
+ * if it cannot handle the case of removing a directory
+ * that is still in use by something else..
+ */
+ switch (dentry->d_count) {
+ default:
shrink_dcache_parent(dentry);
+ if (dentry->d_count != 2)
+ break;
+ case 2:
+ d_drop(dentry);
+ }
error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry);
exit_lock:
- unlock_dir(dir);
-exit_dput:
- dput(dentry);
+ dentry->d_count--;
+ double_unlock(dentry, dir);
exit:
return error;
}
@@ -943,13 +1001,16 @@
goto exit_lock;
/*
+ * A directory can't be unlink'ed.
* A file cannot be removed from an append-only directory.
*/
error = -EPERM;
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ goto exit_lock;
+
if (IS_APPEND(dir->d_inode))
goto exit_lock;
- error = -EPERM;
if (!dir->d_inode->i_op || !dir->d_inode->i_op->unlink)
goto exit_lock;
@@ -1142,37 +1203,6 @@
}
unlock_kernel();
return error;
-}
-
-/*
- * Whee.. Deadlock country. Happily there is only one VFS
- * operation that does this..
- */
-static inline void double_lock(struct dentry *d1, struct dentry *d2)
-{
- struct semaphore *s1 = &d1->d_inode->i_sem;
- struct semaphore *s2 = &d2->d_inode->i_sem;
-
- if (s1 != s2) {
- if ((unsigned long) s1 < (unsigned long) s2) {
- struct semaphore *tmp = s2;
- s2 = s1; s1 = tmp;
- }
- down(s1);
- }
- down(s2);
-}
-
-static inline void double_unlock(struct dentry *d1, struct dentry *d2)
-{
- struct semaphore *s1 = &d1->d_inode->i_sem;
- struct semaphore *s2 = &d2->d_inode->i_sem;
-
- up(s1);
- if (s1 != s2)
- up(s2);
- dput(d1);
- dput(d2);
}
static inline int do_rename(const char * oldname, const char * newname)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov