diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 141 |
1 files changed, 97 insertions, 44 deletions
diff --git a/fs/namei.c b/fs/namei.c index d89d6d64c..b91b43a1f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -23,18 +23,6 @@ #include <asm/page.h> #include <asm/pgtable.h> -/* - * The bitmask for a lookup event: - * - follow links at the end - * - require a directory - * - ending slashes ok even for nonexistent files - * - internal "there are more path compnents" flag - */ -#define LOOKUP_FOLLOW (1) -#define LOOKUP_DIRECTORY (2) -#define LOOKUP_SLASHOK (4) -#define LOOKUP_CONTINUE (8) - #include <asm/namei.h> /* This can be removed after the beta phase. */ @@ -225,12 +213,12 @@ static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * nam /* * Internal lookup() using the new generic dcache. */ -static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name) +static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags) { struct dentry * dentry = d_lookup(parent, name); if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { - if (!dentry->d_op->d_revalidate(dentry) && !d_invalidate(dentry)) { + if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) { dput(dentry); dentry = NULL; } @@ -245,7 +233,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name) * We get the directory semaphore, and after getting that we also * make sure that nobody added the entry to the dcache in the meantime.. */ -static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) +static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags) { struct dentry * result; struct inode *dir = parent->d_inode; @@ -258,17 +246,16 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) * FIXME! This could use version numbering or similar to * avoid unnecessary cache lookups. */ - result = cached_lookup(parent, name); + result = cached_lookup(parent, name, flags); if (!result) { struct dentry * dentry = d_alloc(parent, name); result = ERR_PTR(-ENOMEM); if (dentry) { - int error = dir->i_op->lookup(dir, dentry); - result = dentry; - if (error) { + result = dir->i_op->lookup(dir, dentry); + if (result) dput(dentry); - result = ERR_PTR(error); - } + else + result = dentry; } } up(&dir->i_sem); @@ -279,7 +266,8 @@ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry { struct inode * inode = dentry->d_inode; - if (inode && inode->i_op && inode->i_op->follow_link) { + if ((follow & LOOKUP_FOLLOW) + && inode && inode->i_op && inode->i_op->follow_link) { if (current->link_count < 5) { struct dentry * result; @@ -392,9 +380,9 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned /* This does the actual lookups.. */ dentry = reserved_lookup(base, &this); if (!dentry) { - dentry = cached_lookup(base, &this); + dentry = cached_lookup(base, &this, flags); if (!dentry) { - dentry = real_lookup(base, &this); + dentry = real_lookup(base, &this, flags); if (IS_ERR(dentry)) break; } @@ -403,9 +391,6 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned /* Check mountpoints.. */ dentry = follow_mount(dentry); - if (!(flags & LOOKUP_FOLLOW)) - break; - base = do_follow_link(base, dentry, flags); if (IS_ERR(base)) goto return_base; @@ -1034,17 +1019,13 @@ 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: + if (!error) { + error = -EPERM; + if (dir->i_op && dir->i_op->unlink) { + DQUOT_INIT(dir); + error = dir->i_op->unlink(dir, dentry); + } + } return error; } @@ -1231,15 +1212,16 @@ asmlinkage int sys_link(const char * oldname, const char * newname) return error; } -int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, +int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int error; - int isdir; + int need_rehash = 0; - isdir = S_ISDIR(old_dentry->d_inode->i_mode); + if (old_dentry->d_inode == new_dentry->d_inode) + return 0; - error = may_delete(old_dir, old_dentry, isdir); /* XXX */ + error = may_delete(old_dir, old_dentry, 1); if (error) return error; @@ -1249,20 +1231,91 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!new_dentry->d_inode) error = may_create(new_dir, new_dentry); else - error = may_delete(new_dir, new_dentry, isdir); + error = may_delete(new_dir, new_dentry, 1); if (error) return error; if (!old_dir->i_op || !old_dir->i_op->rename) return -EPERM; + /* + * If we are going to change the parent - check write permissions, + * we'll need to flip '..'. + */ + if (new_dir != old_dir) { + error = permission(old_dentry->d_inode, MAY_WRITE); + } + if (error) + return error; + DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); + down(&old_dir->i_sb->s_vfs_rename_sem); + error = -EINVAL; + if (is_subdir(new_dentry, old_dentry)) + goto out_unlock; + if (new_dentry->d_inode) { + error = -EBUSY; + if (d_invalidate(new_dentry)<0) + goto out_unlock; + need_rehash = 1; + } error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); - + if (need_rehash) + d_rehash(new_dentry); + if (!error) + d_move(old_dentry,new_dentry); +out_unlock: + up(&old_dir->i_sb->s_vfs_rename_sem); return error; } +int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int error; + + if (old_dentry->d_inode == new_dentry->d_inode) + return 0; + + error = may_delete(old_dir, old_dentry, 0); + 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, 0); + 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); + if (error) + return error; + /* The following d_move() should become unconditional */ + if (!(old_dir->i_sb->s_flags & MS_ODD_RENAME)) { + d_move(old_dentry, new_dentry); + } + return 0; +} + +int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); + else + return vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); +} + static inline int do_rename(const char * oldname, const char * newname) { int error; |