patch-2.1.132 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: 466
- Date:
Fri Dec 18 07:09:35 1998
- Orig file:
v2.1.131/linux/fs/namei.c
- Orig date:
Wed Dec 16 10:32:55 1998
diff -u --recursive --new-file v2.1.131/linux/fs/namei.c linux/fs/namei.c
@@ -490,6 +490,77 @@
return dentry;
}
+/*
+ * It's inline, so penalty for filesystems that don't use sticky bit is
+ * minimal.
+ */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+ if (!(dir->i_mode & S_ISVTX))
+ return 0;
+ if (inode->i_uid == current->fsuid)
+ return 0;
+ if (dir->i_uid == current->fsuid)
+ return 0;
+ return !capable(CAP_FOWNER);
+}
+
+/*
+ * Check whether we can remove a link victim from directory dir, check
+ * whether the type of victim is right.
+ * 1. We can't do it if dir is read-only (done in permission())
+ * 2. We should have write and exec permissions on dir
+ * 3. We can't remove anything from append-only dir
+ * 4. We can't do anything with immutable dir (done in permission())
+ * 5. If the sticky bit on dir is set we should either
+ * a. be owner of dir, or
+ * b. be owner of victim, or
+ * c. have CAP_FOWNER capability
+ * 6. If the victim is append-only or immutable we can't do antyhing with
+ * links pointing to it.
+ * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
+ * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
+ * 9. We can't remove a root or mountpoint.
+ */
+static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir)
+{
+ int error;
+ if (!victim->d_inode || victim->d_parent->d_inode != dir)
+ return -ENOENT;
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ return error;
+ if (IS_APPEND(dir))
+ return -EPERM;
+ if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
+ IS_IMMUTABLE(victim->d_inode))
+ return -EPERM;
+ if (isdir) {
+ if (!S_ISDIR(victim->d_inode->i_mode))
+ return -ENOTDIR;
+ if (IS_ROOT(victim))
+ return -EBUSY;
+ if (victim->d_mounts != victim->d_covers)
+ return -EBUSY;
+ } else if (S_ISDIR(victim->d_inode->i_mode))
+ return -EISDIR;
+ return 0;
+}
+
+/* Check whether we can create an object with dentry child in directory
+ * dir.
+ * 1. We can't do it if child already exists (open has special treatment for
+ * this case, but since we are inlined it's OK)
+ * 2. We can't do it if dir is read-only (done in permission())
+ * 3. We should have write and exec permissions on dir
+ * 4. We can't do it if dir is immutable (done in permission())
+ */
+static inline int may_create(struct inode *dir, struct dentry *child) {
+ if (child->d_inode)
+ return -EEXIST;
+ return permission(dir,MAY_WRITE | MAY_EXEC);
+}
+
static inline struct dentry *get_parent(struct dentry *dentry)
{
return dget(dentry->d_parent);
@@ -599,16 +670,16 @@
error = 0;
if (flag & O_EXCL)
error = -EEXIST;
- } else if (IS_RDONLY(dir->d_inode))
- error = -EROFS;
- else if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
- error = -EACCES;
- else if ((error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC)) == 0) {
- DQUOT_INIT(dir->d_inode);
- 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;
+ } else if ((error = may_create(dir->d_inode, dentry)) == 0) {
+ if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
+ error = -EACCES;
+ else {
+ DQUOT_INIT(dir->d_inode);
+ 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;
+ }
}
unlock_dir(dir);
if (error)
@@ -705,30 +776,20 @@
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->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
- retval = ERR_PTR(error);
+ error = may_create(dir->d_inode, dentry);
if (error)
goto exit_lock;
- retval = ERR_PTR(-EPERM);
+ error = -EPERM;
if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod)
goto exit_lock;
DQUOT_INIT(dir->d_inode);
error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);
+exit_lock:
retval = ERR_PTR(error);
if (!error)
retval = dget(dentry);
-
-exit_lock:
unlock_dir(dir);
exit:
dput(dentry);
@@ -790,15 +851,7 @@
if (IS_ERR(dir))
goto exit_dput;
- error = -EEXIST;
- if (dentry->d_inode)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(dir->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
+ error = may_create(dir->d_inode, dentry);
if (error)
goto exit_lock;
@@ -865,58 +918,20 @@
dput(d2);
}
-static inline int do_rmdir(const char * name)
-{
- int error;
- struct dentry *dir;
- struct dentry *dentry;
-
- dentry = lookup_dentry(name, NULL, 0);
- error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto exit;
-
- 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;
- if (IS_RDONLY(dir->d_inode))
- goto exit_lock;
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int error;
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
+ error = may_delete(dir, dentry, 1);
if (error)
- goto exit_lock;
+ return error;
- /*
- * A subdirectory cannot be removed from an append-only directory.
- */
- error = -EPERM;
- if (IS_APPEND(dir->d_inode))
- goto exit_lock;
-
- /* Disallow removals of mountpoints. */
- error = -EBUSY;
- if (dentry->d_mounts != dentry->d_covers)
- goto exit_lock;
+ if (!dir->i_op || !dir->i_op->rmdir)
+ return -EPERM;
- error = -EPERM;
- if (!dir->d_inode->i_op || !dir->d_inode->i_op->rmdir)
- goto exit_lock;
-
- DQUOT_INIT(dir->d_inode);
+ DQUOT_INIT(dir);
/*
* We try to drop the dentry early: we should have
@@ -942,11 +957,42 @@
d_drop(dentry);
}
- error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry);
+ error = dir->i_op->rmdir(dir, dentry);
+
+ return error;
+}
+
+static inline int do_rmdir(const char * name)
+{
+ int error;
+ struct dentry *dir;
+ struct dentry *dentry;
+
+ dentry = lookup_dentry(name, NULL, 0);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto exit;
+
+ error = -ENOENT;
+ if (!dentry->d_inode)
+ goto exit_dput;
+
+ dir = dget(dentry->d_parent);
+
+ /*
+ * 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);
+
+ error = vfs_rmdir(dir->d_inode, dentry);
-exit_lock:
- dentry->d_count--;
double_unlock(dentry, dir);
+exit_dput:
+ dput(dentry);
exit:
return error;
}
@@ -967,6 +1013,25 @@
return error;
}
+int vfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ error = may_delete(dir, dentry, 0);
+ if (error)
+ goto exit_lock;
+
+ if (!dir->i_op || !dir->i_op->unlink)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+
+ error = dir->i_op->unlink(dir, dentry);
+
+exit_lock:
+ return error;
+}
+
static inline int do_unlink(const char * name)
{
int error;
@@ -983,42 +1048,8 @@
if (IS_ERR(dir))
goto exit_dput;
- 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->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
- if (error)
- 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;
-
- if (!dir->d_inode->i_op || !dir->d_inode->i_op->unlink)
- goto exit_lock;
+ error = vfs_unlink(dir->d_inode, dentry);
- DQUOT_INIT(dir->d_inode);
-
- error = dir->d_inode->i_op->unlink(dir->d_inode, dentry);
-
-exit_lock:
unlock_dir(dir);
exit_dput:
dput(dentry);
@@ -1059,15 +1090,7 @@
if (IS_ERR(dir))
goto exit_dput;
- error = -EEXIST;
- if (dentry->d_inode)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(dir->d_inode))
- goto exit_lock;
-
- error = permission(dir->d_inode,MAY_WRITE | MAY_EXEC);
+ error = may_create(dir->d_inode, dentry);
if (error)
goto exit_lock;
@@ -1143,22 +1166,14 @@
if (!inode)
goto exit_lock;
- error = -EEXIST;
- if (new_dentry->d_inode)
- goto exit_lock;
-
- error = -EROFS;
- if (IS_RDONLY(dir->d_inode))
+ error = may_create(dir->d_inode, new_dentry);
+ if (error)
goto exit_lock;
error = -EXDEV;
if (dir->d_inode->i_dev != inode->i_dev)
goto exit_lock;
- error = permission(dir->d_inode, MAY_WRITE | MAY_EXEC);
- if (error)
- goto exit_lock;
-
/*
* A link to an append-only or immutable file cannot be created.
*/
@@ -1205,6 +1220,38 @@
return error;
}
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+ int isdir;
+
+ isdir = S_ISDIR(old_dentry->d_inode->i_mode);
+
+ error = may_delete(old_dir, old_dentry, isdir); /* XXX */
+ if (error)
+ return error;
+
+ if (new_dir->i_dev != old_dir->i_dev)
+ return -EXDEV;
+
+ if (!new_dentry->d_inode)
+ error = may_create(new_dir, new_dentry);
+ else
+ error = may_delete(new_dir, new_dentry, isdir);
+ if (error)
+ return error;
+
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+
+ DQUOT_INIT(old_dir);
+ DQUOT_INIT(new_dir);
+ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+
+ return error;
+}
+
static inline int do_rename(const char * oldname, const char * newname)
{
int error;
@@ -1237,43 +1284,9 @@
double_lock(new_dir, old_dir);
- error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
- if (error)
- goto exit_lock;
- error = permission(new_dir->d_inode,MAY_WRITE | MAY_EXEC);
- if (error)
- goto exit_lock;
-
- /* Disallow moves of mountpoints. */
- error = -EBUSY;
- if (old_dir == old_dentry || new_dir == new_dentry)
- goto exit_lock;
-
- error = -EXDEV;
- if (new_dir->d_inode->i_dev != old_dir->d_inode->i_dev)
- goto exit_lock;
-
- error = -EROFS;
- 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->d_inode))
- goto exit_lock;
+ error = vfs_rename(old_dir->d_inode, old_dentry,
+ new_dir->d_inode, new_dentry);
- error = -EPERM;
- if (!old_dir->d_inode->i_op || !old_dir->d_inode->i_op->rename)
- goto exit_lock;
-
- DQUOT_INIT(old_dir->d_inode);
- DQUOT_INIT(new_dir->d_inode);
- error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry,
- new_dir->d_inode, new_dentry);
-
-exit_lock:
double_unlock(new_dir, old_dir);
dput(new_dentry);
exit_old:
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov