patch-2.1.52 linux/fs/namei.c
Next file: linux/fs/nfs/dir.c
Previous file: linux/fs/msdos/namei.c
Back to the patch index
Back to the overall index
- Lines: 545
- Date:
Mon Sep 1 13:04:13 1997
- Orig file:
v2.1.51/linux/fs/namei.c
- Orig date:
Mon Aug 18 18:19:46 1997
diff -u --recursive --new-file v2.1.51/linux/fs/namei.c linux/fs/namei.c
@@ -460,16 +460,42 @@
return dentry;
}
-static inline struct inode *get_parent(struct dentry *dentry)
+static inline struct dentry *get_parent(struct dentry *dentry)
{
- return dentry->d_parent->d_inode;
+ return dget(dentry->d_parent);
}
-static inline struct inode *lock_parent(struct dentry *dentry)
+static inline void unlock_dir(struct dentry *dir)
{
- struct inode *dir = dentry->d_parent->d_inode;
+ up(&dir->d_inode->i_sem);
+ dput(dir);
+}
- down(&dir->i_sem);
+/*
+ * Locking the parent is needed to:
+ * - serialize directory operations
+ * - make sure the parent doesn't change from
+ * under us in the middle of an operation.
+ *
+ * NOTE! Right now we'd rather use a "struct inode"
+ * for this, but as I expect things to move toward
+ * using dentries instead for most things it is
+ * probably better to start with the conceptually
+ * better interface of relying on a path of dentries.
+ */
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+ struct dentry *dir = dget(dentry->d_parent);
+
+ down(&dir->d_inode->i_sem);
+
+ /* Un-hashed or moved? Punt if so.. */
+ if (dir != dentry->d_parent || list_empty(&dentry->d_hash)) {
+ if (dir != dentry) {
+ unlock_dir(dir);
+ dir = ERR_PTR(-ENOENT);
+ }
+ }
return dir;
}
@@ -501,9 +527,12 @@
acc_mode = ACC_MODE(flag);
if (flag & O_CREAT) {
- struct inode *dir;
+ struct dentry *dir;
dir = lock_parent(dentry);
+ error = PTR_ERR(dir);
+ if (IS_ERR(dir))
+ goto exit;
/*
* The existence test must be done _after_ getting the directory
* semaphore - the dentry might otherwise change.
@@ -512,19 +541,19 @@
error = 0;
if (flag & O_EXCL)
error = -EEXIST;
- } else if (IS_RDONLY(dir))
+ } else if (IS_RDONLY(dir->d_inode))
error = -EROFS;
- else if (!dir->i_op || !dir->i_op->create)
+ else if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
error = -EACCES;
- else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) == 0) {
- if (dir->i_sb && dir->i_sb->dq_op)
- dir->i_sb->dq_op->initialize(dir, -1);
- error = dir->i_op->create(dir, dentry, mode);
+ else if ((error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC)) == 0) {
+ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+ dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+ error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);
/* Don't check for write permission, don't truncate */
acc_mode = 0;
flag &= ~O_TRUNC;
}
- up(&dir->i_sem);
+ unlock_dir(dir);
if (error)
goto exit;
}
@@ -600,7 +629,7 @@
struct dentry * do_mknod(const char * filename, int mode, dev_t dev)
{
int error;
- struct inode *dir;
+ struct dentry *dir;
struct dentry *dentry, *retval;
mode &= ~current->fs->umask;
@@ -609,33 +638,37 @@
return dentry;
dir = lock_parent(dentry);
+ retval = dir;
+ if (IS_ERR(dir))
+ goto exit;
retval = ERR_PTR(-EEXIST);
if (dentry->d_inode)
goto exit_lock;
retval = ERR_PTR(-EROFS);
- if (IS_RDONLY(dir))
+ if (IS_RDONLY(dir->d_inode))
goto exit_lock;
- error = permission(dir,MAY_WRITE | MAY_EXEC);
+ error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
retval = ERR_PTR(error);
if (error)
goto exit_lock;
retval = ERR_PTR(-EPERM);
- if (!dir->i_op || !dir->i_op->mknod)
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod)
goto exit_lock;
- if (dir->i_sb && dir->i_sb->dq_op)
- dir->i_sb->dq_op->initialize(dir, -1);
- error = dir->i_op->mknod(dir, dentry, mode, dev);
+ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+ dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+ error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);
retval = ERR_PTR(error);
if (!error)
retval = dget(dentry);
exit_lock:
- up(&dir->i_sem);
+ unlock_dir(dir);
+exit:
dput(dentry);
return retval;
}
@@ -682,7 +715,7 @@
static inline int do_mkdir(const char * pathname, int mode)
{
int error;
- struct inode *dir;
+ struct dentry *dir;
struct dentry *dentry;
dentry = lookup_dentry(pathname, NULL, 1);
@@ -692,29 +725,33 @@
dir = lock_parent(dentry);
+ error = PTR_ERR(dir);
+ if (IS_ERR(dir))
+ goto exit;
+
error = -EEXIST;
if (dentry->d_inode)
goto exit_lock;
error = -EROFS;
- if (IS_RDONLY(dir))
+ if (IS_RDONLY(dir->d_inode))
goto exit_lock;
- error = permission(dir,MAY_WRITE | MAY_EXEC);
+ error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
error = -EPERM;
- if (!dir->i_op || !dir->i_op->mkdir)
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->mkdir)
goto exit_lock;
- if (dir->i_sb && dir->i_sb->dq_op)
- dir->i_sb->dq_op->initialize(dir, -1);
+ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+ dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
mode &= 0777 & ~current->fs->umask;
- error = dir->i_op->mkdir(dir, dentry, mode);
+ error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);
exit_lock:
- up(&dir->i_sem);
+ unlock_dir(dir);
dput(dentry);
exit:
return error;
@@ -739,7 +776,7 @@
static inline int do_rmdir(const char * name)
{
int error;
- struct inode *dir;
+ struct dentry *dir;
struct dentry *dentry;
dentry = lookup_dentry(name, NULL, 0);
@@ -748,15 +785,20 @@
goto exit;
dir = lock_parent(dentry);
+
+ error = PTR_ERR(dir);
+ if (IS_ERR(dir))
+ goto exit;
+
error = -ENOENT;
if (!dentry->d_inode)
goto exit_lock;
error = -EROFS;
- if (IS_RDONLY(dir))
+ if (IS_RDONLY(dir->d_inode))
goto exit_lock;
- error = permission(dir,MAY_WRITE | MAY_EXEC);
+ error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
@@ -764,25 +806,25 @@
* A subdirectory cannot be removed from an append-only directory.
*/
error = -EPERM;
- if (IS_APPEND(dir))
+ if (IS_APPEND(dir->d_inode))
goto exit_lock;
/* Disallow removals of mountpoints. */
error = -EBUSY;
- if (dentry->d_covers != dentry)
+ if (dentry == dir)
goto exit_lock;
error = -EPERM;
- if (!dir->i_op || !dir->i_op->rmdir)
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->rmdir)
goto exit_lock;
- if (dir->i_sb && dir->i_sb->dq_op)
- dir->i_sb->dq_op->initialize(dir, -1);
+ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+ dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
- error = dir->i_op->rmdir(dir, dentry);
+ error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry);
exit_lock:
- up(&dir->i_sem);
+ unlock_dir(dir);
dput(dentry);
exit:
return error;
@@ -807,7 +849,7 @@
static inline int do_unlink(const char * name)
{
int error;
- struct inode *dir;
+ struct dentry *dir;
struct dentry *dentry;
dentry = lookup_dentry(name, NULL, 0);
@@ -817,15 +859,24 @@
dir = lock_parent(dentry);
+ error = PTR_ERR(dir);
+ if (IS_ERR(dir))
+ goto exit;
+
error = -ENOENT;
if (!dentry->d_inode)
goto exit_lock;
+ /* Mount point? */
+ error = -EBUSY;
+ if (dentry == dir)
+ goto exit_lock;
+
error = -EROFS;
- if (IS_RDONLY(dir))
+ if (IS_RDONLY(dir->d_inode))
goto exit_lock;
- error = permission(dir,MAY_WRITE | MAY_EXEC);
+ error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
@@ -833,20 +884,20 @@
* A file cannot be removed from an append-only directory.
*/
error = -EPERM;
- if (IS_APPEND(dir))
+ if (IS_APPEND(dir->d_inode))
goto exit_lock;
error = -EPERM;
- if (!dir->i_op || !dir->i_op->unlink)
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->unlink)
goto exit_lock;
- if (dir->i_sb && dir->i_sb->dq_op)
- dir->i_sb->dq_op->initialize(dir, -1);
+ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+ dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
- error = dir->i_op->unlink(dir, dentry);
+ error = dir->d_inode->i_op->unlink(dir->d_inode, dentry);
exit_lock:
- up(&dir->i_sem);
+ unlock_dir(dir);
dput(dentry);
exit:
return error;
@@ -871,7 +922,7 @@
static inline int do_symlink(const char * oldname, const char * newname)
{
int error;
- struct inode *dir;
+ struct dentry *dir;
struct dentry *dentry;
dentry = lookup_dentry(newname, NULL, 0);
@@ -882,28 +933,32 @@
dir = lock_parent(dentry);
+ error = PTR_ERR(dir);
+ if (IS_ERR(dir))
+ goto exit;
+
error = -EEXIST;
if (dentry->d_inode)
goto exit_lock;
error = -EROFS;
- if (IS_RDONLY(dir))
+ if (IS_RDONLY(dir->d_inode))
goto exit_lock;
- error = permission(dir,MAY_WRITE | MAY_EXEC);
+ error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
error = -EPERM;
- if (!dir->i_op || !dir->i_op->symlink)
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->symlink)
goto exit_lock;
- if (dir->i_sb && dir->i_sb->dq_op)
- dir->i_sb->dq_op->initialize(dir, -1);
- error = dir->i_op->symlink(dir, dentry, oldname);
+ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+ dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+ error = dir->d_inode->i_op->symlink(dir->d_inode, dentry, oldname);
exit_lock:
- up(&dir->i_sem);
+ unlock_dir(dir);
dput(dentry);
exit:
return error;
@@ -933,8 +988,8 @@
static inline int do_link(const char * oldname, const char * newname)
{
- struct dentry *old_dentry, *new_dentry;
- struct inode *dir, *inode;
+ struct dentry *old_dentry, *new_dentry, *dir;
+ struct inode *inode;
int error;
old_dentry = lookup_dentry(oldname, NULL, 1);
@@ -949,6 +1004,10 @@
dir = lock_parent(new_dentry);
+ error = PTR_ERR(dir);
+ if (IS_ERR(dir))
+ goto exit;
+
error = -ENOENT;
inode = old_dentry->d_inode;
if (!inode)
@@ -959,14 +1018,14 @@
goto exit_lock;
error = -EROFS;
- if (IS_RDONLY(dir))
+ if (IS_RDONLY(dir->d_inode))
goto exit_lock;
error = -EXDEV;
- if (dir->i_dev != inode->i_dev)
+ if (dir->d_inode->i_dev != inode->i_dev)
goto exit_lock;
- error = permission(dir, MAY_WRITE | MAY_EXEC);
+ error = permission(dir->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
@@ -978,15 +1037,15 @@
goto exit_lock;
error = -EPERM;
- if (!dir->i_op || !dir->i_op->link)
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->link)
goto exit_lock;
- if (dir->i_sb && dir->i_sb->dq_op)
- dir->i_sb->dq_op->initialize(dir, -1);
- error = dir->i_op->link(inode, dir, new_dentry);
+ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
+ dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+ error = dir->d_inode->i_op->link(inode, dir->d_inode, new_dentry);
exit_lock:
- up(&dir->i_sem);
+ unlock_dir(dir);
dput(new_dentry);
exit_old:
dput(old_dentry);
@@ -1020,30 +1079,37 @@
* Whee.. Deadlock country. Happily there is only one VFS
* operation that does this..
*/
-static inline void double_down(struct semaphore *s1, struct semaphore *s2)
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
{
- if ((unsigned long) s1 < (unsigned long) s2) {
- down(s1);
- down(s2);
- } else if (s1 == s2) {
- down(s1);
- } else {
- down(s2);
+ 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_up(struct semaphore *s1, struct semaphore *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)
{
int error;
- struct inode * old_dir, * new_dir;
+ struct dentry * old_dir, * new_dir;
struct dentry * old_dentry, *new_dentry;
old_dentry = lookup_dentry(oldname, NULL, 0);
@@ -1061,52 +1127,49 @@
new_dir = get_parent(new_dentry);
old_dir = get_parent(old_dentry);
- double_down(&new_dir->i_sem, &old_dir->i_sem);
+ double_lock(new_dir, old_dir);
error = -ENOENT;
if (!old_dentry->d_inode)
goto exit_lock;
- error = permission(old_dir,MAY_WRITE | MAY_EXEC);
+ error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
- error = permission(new_dir,MAY_WRITE | MAY_EXEC);
+ error = permission(new_dir->d_inode,MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
- /*
- * Disallow moves of mountpoints. There is no technical
- * reason for this, but user level stuff gets too confused.
- */
+ /* Disallow moves of mountpoints. */
error = -EBUSY;
- if (old_dentry->d_covers != old_dentry)
+ if (old_dir == old_dentry || new_dir == new_dentry)
goto exit_lock;
error = -EXDEV;
- if (new_dir->i_dev != old_dir->i_dev)
+ if (new_dir->d_inode->i_dev != old_dir->d_inode->i_dev)
goto exit_lock;
error = -EROFS;
- if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir))
+ if (IS_RDONLY(new_dir->d_inode) || IS_RDONLY(old_dir->d_inode))
goto exit_lock;
/*
* A file cannot be removed from an append-only directory.
*/
error = -EPERM;
- if (IS_APPEND(old_dir))
+ if (IS_APPEND(old_dir->d_inode))
goto exit_lock;
error = -EPERM;
- if (!old_dir->i_op || !old_dir->i_op->rename)
+ if (!old_dir->d_inode->i_op || !old_dir->d_inode->i_op->rename)
goto exit_lock;
- if (new_dir->i_sb && new_dir->i_sb->dq_op)
- new_dir->i_sb->dq_op->initialize(new_dir, -1);
- error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+ if (new_dir->d_inode->i_sb && new_dir->d_inode->i_sb->dq_op)
+ new_dir->d_inode->i_sb->dq_op->initialize(new_dir->d_inode, -1);
+ error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry);
exit_lock:
- double_up(&new_dir->i_sem, &old_dir->i_sem);
+ double_unlock(new_dir, old_dir);
dput(new_dentry);
exit_old:
dput(old_dentry);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov