summaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-04-19 04:00:00 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-04-19 04:00:00 +0000
commit46e045034336a2cc90c1798cd7cc07af744ddfd6 (patch)
tree3b9b51fc482e729f663d25333e77fbed9aaa939a /fs/namei.c
parent31dc59d503a02e84c4de98826452acaeb56dc15a (diff)
Merge with Linux 2.3.99-pre4.
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c801
1 files changed, 450 insertions, 351 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 8675e28c5..58f7a590a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -192,31 +192,6 @@ void put_write_access(struct inode * inode)
}
/*
- * "." and ".." are special - ".." especially so because it has to be able
- * to know about the current root directory and parent relationships
- */
-static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name)
-{
- struct dentry *result = NULL;
- if (name->name[0] == '.') {
- switch (name->len) {
- default:
- break;
- case 2:
- if (name->name[1] != '.')
- break;
-
- if (parent != current->fs->root)
- parent = parent->d_covers->d_parent;
- /* fallthrough */
- case 1:
- result = parent;
- }
- }
- return dget(result);
-}
-
-/*
* Internal lookup() using the new generic dcache.
*/
static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags)
@@ -279,39 +254,28 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, i
return result;
}
-static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow)
+static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)
{
- struct inode * inode = dentry->d_inode;
-
- if ((follow & LOOKUP_FOLLOW)
- && inode && inode->i_op && inode->i_op->follow_link) {
- if (current->link_count < 32) {
- struct dentry * result;
-
- current->link_count++;
- /* This eats the base */
- result = inode->i_op->follow_link(dentry, base, follow);
- current->link_count--;
- dput(dentry);
- return result;
- }
- dput(dentry);
- dentry = ERR_PTR(-ELOOP);
- }
- dput(base);
- return dentry;
+ int err;
+ if (current->link_count >= 32)
+ goto loop;
+ current->link_count++;
+ UPDATE_ATIME(dentry->d_inode);
+ err = dentry->d_inode->i_op->follow_link(dentry, nd);
+ current->link_count--;
+ return err;
+loop:
+ dput(nd->dentry);
+ mntput(nd->mnt);
+ return -ELOOP;
}
-static inline struct dentry * follow_mount(struct dentry * dentry)
+static inline int follow_down(struct dentry ** dentry, struct vfsmount **mnt)
{
- struct dentry * mnt = dentry->d_mounts;
-
- if (mnt != dentry) {
- dget(mnt);
- dput(dentry);
- dentry = mnt;
- }
- return dentry;
+ struct dentry * parent = dget((*dentry)->d_mounts);
+ dput(*dentry);
+ *dentry = parent;
+ return 1;
}
/*
@@ -319,36 +283,31 @@ static inline struct dentry * follow_mount(struct dentry * dentry)
*
* This is the basic name resolution function, turning a pathname
* into the final dentry.
+ *
+ * We expect 'base' to be positive and a directory.
*/
-struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int lookup_flags)
+int walk_name(const char * name, unsigned lookup_flags, struct nameidata *nd)
{
- struct dentry * dentry;
+ struct dentry *dentry;
struct inode *inode;
+ int err;
- if (*name == '/') {
- if (base)
- dput(base);
- do {
- name++;
- } while (*name == '/');
- __prefix_lookup_dentry(name, lookup_flags);
- base = dget(current->fs->root);
- } else if (!base) {
- base = dget(current->fs->pwd);
- }
-
+ while (*name=='/')
+ name++;
if (!*name)
goto return_base;
- inode = base->d_inode;
- lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK;
+ inode = nd->dentry->d_inode;
+ if (current->link_count)
+ lookup_flags = LOOKUP_FOLLOW;
+
+ lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY |
+ LOOKUP_SLASHOK | LOOKUP_POSITIVE | LOOKUP_PARENT;
/* At this point we know we have a real path component. */
for(;;) {
- int err;
unsigned long hash;
struct qstr this;
- unsigned int flags;
unsigned int c;
err = permission(inode, MAY_EXEC);
@@ -369,99 +328,240 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned
this.hash = end_name_hash(hash);
/* remove trailing slashes? */
- flags = lookup_flags;
- if (c) {
- char tmp;
-
- flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
- do {
- tmp = *++name;
- } while (tmp == '/');
- if (tmp)
- flags |= LOOKUP_CONTINUE;
- }
+ if (!c)
+ goto last_component;
+ while (*++name == '/');
+ if (!*name)
+ goto last_with_slashes;
/*
+ * "." and ".." are special - ".." especially so because it has
+ * to be able to know about the current root directory and
+ * parent relationships.
+ */
+ if (this.name[0] == '.') switch (this.len) {
+ default:
+ break;
+ case 2:
+ if (this.name[1] != '.')
+ break;
+ if (nd->dentry != current->fs->root) {
+ dentry = dget(nd->dentry->d_covers->d_parent);
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ inode = dentry->d_inode;
+ }
+ /* fallthrough */
+ case 1:
+ continue;
+ }
+ /*
* See if the low-level filesystem might want
* to use its own hash..
*/
- if (base->d_op && base->d_op->d_hash) {
- int error;
- error = base->d_op->d_hash(base, &this);
- if (error < 0) {
- dentry = ERR_PTR(error);
+ if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+ err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+ if (err < 0)
break;
- }
}
-
/* This does the actual lookups.. */
- dentry = reserved_lookup(base, &this);
+ dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
if (!dentry) {
- dentry = cached_lookup(base, &this, flags);
- if (!dentry) {
- dentry = real_lookup(base, &this, flags);
- if (IS_ERR(dentry))
- break;
- }
-
- /* Check mountpoints.. */
- dentry = follow_mount(dentry);
+ dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ break;
}
+ /* Check mountpoints.. */
+ while (d_mountpoint(dentry) && follow_down(&dentry, &nd->mnt))
+ ;
- base = do_follow_link(base, dentry, flags);
- if (IS_ERR(base))
- goto return_base;
+ err = -ENOENT;
+ inode = dentry->d_inode;
+ if (!inode)
+ break;
+ err = -ENOTDIR;
+ if (!inode->i_op)
+ break;
- inode = base->d_inode;
- if (flags & LOOKUP_DIRECTORY) {
+ if (inode->i_op->follow_link) {
+ err = do_follow_link(dentry, nd);
+ dput(dentry);
+ if (err)
+ goto return_err;
+ err = -ENOENT;
+ inode = nd->dentry->d_inode;
if (!inode)
- goto no_inode;
- dentry = ERR_PTR(-ENOTDIR);
+ break;
+ err = -ENOTDIR;
+ if (!inode->i_op)
+ break;
+ } else {
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ }
+ err = -ENOTDIR;
+ if (!inode->i_op->lookup)
+ break;
+ continue;
+ /* here ends the main loop */
+
+last_with_slashes:
+ lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+last_component:
+ if (lookup_flags & LOOKUP_PARENT)
+ goto lookup_parent;
+ if (this.name[0] == '.') switch (this.len) {
+ default:
+ break;
+ case 2:
+ if (this.name[1] != '.')
+ break;
+ if (nd->dentry != current->fs->root) {
+ dentry = dget(nd->dentry->d_covers->d_parent);
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ inode = dentry->d_inode;
+ }
+ /* fallthrough */
+ case 1:
+ goto return_base;
+ }
+ if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+ err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+ if (err < 0)
+ break;
+ }
+ dentry = cached_lookup(nd->dentry, &this, 0);
+ if (!dentry) {
+ dentry = real_lookup(nd->dentry, &this, 0);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ break;
+ }
+ while (d_mountpoint(dentry) && follow_down(&dentry, &nd->mnt))
+ ;
+ inode = dentry->d_inode;
+ if ((lookup_flags & LOOKUP_FOLLOW)
+ && inode && inode->i_op && inode->i_op->follow_link) {
+ err = do_follow_link(dentry, nd);
+ dput(dentry);
+ if (err)
+ goto return_err;
+ inode = nd->dentry->d_inode;
+ } else {
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ }
+ err = -ENOENT;
+ if (!inode)
+ goto no_inode;
+ if (lookup_flags & LOOKUP_DIRECTORY) {
+ err = -ENOTDIR;
if (!inode->i_op || !inode->i_op->lookup)
break;
- if (flags & LOOKUP_CONTINUE)
- continue;
}
-return_base:
- return base;
-/*
- * The case of a nonexisting file is special.
- *
- * In the middle of a pathname lookup (ie when
- * LOOKUP_CONTINUE is set), it's an obvious
- * error and returns ENOENT.
- *
- * At the end of a pathname lookup it's legal,
- * and we return a negative dentry. However, we
- * get here only if there were trailing slashes,
- * which is legal only if we know it's supposed
- * to be a directory (ie "mkdir"). Thus the
- * LOOKUP_SLASHOK flag.
- */
+ goto return_base;
no_inode:
- dentry = ERR_PTR(-ENOENT);
- if (flags & LOOKUP_CONTINUE)
+ err = -ENOENT;
+ if (lookup_flags & LOOKUP_POSITIVE)
break;
- if (flags & LOOKUP_SLASHOK)
- goto return_base;
- break;
+ if (lookup_flags & LOOKUP_DIRECTORY)
+ if (!(lookup_flags & LOOKUP_SLASHOK))
+ break;
+ goto return_base;
+lookup_parent:
+ nd->last = this;
+return_base:
+ return 0;
}
- dput(base);
- return dentry;
+ dput(nd->dentry);
+ mntput(nd->mnt);
+return_err:
+ return err;
+}
+
+/* returns 1 if everything is done */
+static int __emul_lookup_dentry(const char *name, int lookup_flags,
+ struct nameidata *nd)
+{
+ char *emul = __emul_prefix();
+
+ if (!emul)
+ return 0;
+
+ nd->mnt = mntget(current->fs->rootmnt);
+ nd->dentry = dget(current->fs->root);
+ if (walk_name(emul,LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_POSITIVE,nd))
+ return 0;
+ if (walk_name(name, lookup_flags, nd))
+ return 0;
+
+ if (!nd->dentry->d_inode) {
+ struct nameidata nd_root;
+ nd_root.last.len = 0;
+ nd_root.mnt = mntget(current->fs->rootmnt);
+ nd_root.dentry = dget(current->fs->root);
+ if (walk_name(name, lookup_flags, &nd_root))
+ return 1;
+ if (nd_root.dentry->d_inode) {
+ dput(nd->dentry);
+ mntput(nd->mnt);
+ nd->dentry = nd_root.dentry;
+ nd->mnt = nd_root.mnt;
+ nd->last = nd_root.last;
+ return 1;
+ }
+ dput(nd_root.dentry);
+ mntput(nd_root.mnt);
+ }
+ return 1;
+}
+
+static inline int
+walk_init_root(const char *name, unsigned flags, struct nameidata *nd)
+{
+ if (current->personality != PER_LINUX)
+ if (__emul_lookup_dentry(name,flags,nd));
+ return 0;
+ nd->mnt = mntget(current->fs->rootmnt);
+ nd->dentry = dget(current->fs->root);
+ return 1;
+}
+
+int walk_init(const char *name,unsigned int flags,struct nameidata *nd)
+{
+ nd->last.len = 0;
+ if (*name=='/')
+ return walk_init_root(name,flags,nd);
+ nd->mnt = mntget(current->fs->pwdmnt);
+ nd->dentry = dget(current->fs->pwd);
+ return 1;
+}
+
+struct dentry * lookup_dentry(const char * name, unsigned int lookup_flags)
+{
+ struct nameidata nd;
+ int err = 0;
+
+ if (walk_init(name, lookup_flags, &nd))
+ err = walk_name(name, lookup_flags, &nd);
+ if (!err) {
+ mntput(nd.mnt);
+ return nd.dentry;
+ }
+ return ERR_PTR(err);
}
/*
* Restricted form of lookup. Doesn't follow links, single-component only,
* needs parent already locked. Doesn't follow mounts.
*/
-struct dentry * lookup_one(const char * name, struct dentry * base)
+static inline struct dentry * lookup_hash(struct qstr *name, struct dentry * base)
{
struct dentry * dentry;
struct inode *inode;
int err;
- unsigned long hash;
- struct qstr this;
- unsigned int c;
inode = base->d_inode;
err = permission(inode, MAY_EXEC);
@@ -469,36 +569,20 @@ struct dentry * lookup_one(const char * name, struct dentry * base)
if (err)
goto out;
- this.name = name;
- c = *(const unsigned char *)name;
- if (!c)
- goto access;
-
- hash = init_name_hash();
- do {
- name++;
- if (c == '/')
- goto access;
- hash = partial_name_hash(c, hash);
- c = *(const unsigned char *)name;
- } while (c);
- this.len = name - (const char *) this.name;
- this.hash = end_name_hash(hash);
-
/*
* See if the low-level filesystem might want
* to use its own hash..
*/
if (base->d_op && base->d_op->d_hash) {
- err = base->d_op->d_hash(base, &this);
+ err = base->d_op->d_hash(base, name);
dentry = ERR_PTR(err);
if (err < 0)
goto out;
}
- dentry = cached_lookup(base, &this, 0);
+ dentry = cached_lookup(base, name, 0);
if (!dentry) {
- struct dentry *new = d_alloc(base, &this);
+ struct dentry *new = d_alloc(base, name);
dentry = ERR_PTR(-ENOMEM);
if (!new)
goto out;
@@ -515,9 +599,33 @@ struct dentry * lookup_one(const char * name, struct dentry * base)
out:
dput(base);
return dentry;
+}
+
+struct dentry * lookup_one(const char * name, struct dentry * base)
+{
+ unsigned long hash;
+ struct qstr this;
+ unsigned int c;
+
+ this.name = name;
+ c = *(const unsigned char *)name;
+ if (!c)
+ goto access;
+
+ hash = init_name_hash();
+ do {
+ name++;
+ if (c == '/')
+ goto access;
+ hash = partial_name_hash(c, hash);
+ c = *(const unsigned char *)name;
+ } while (c);
+ this.len = name - (const char *) this.name;
+ this.hash = end_name_hash(hash);
+
+ return lookup_hash(&this, base);
access:
- dentry = ERR_PTR(-EACCES);
- goto out;
+ return ERR_PTR(-EACCES);
}
/*
@@ -538,14 +646,8 @@ struct dentry * __namei(const char *pathname, unsigned int lookup_flags)
name = getname(pathname);
dentry = (struct dentry *) name;
if (!IS_ERR(name)) {
- dentry = lookup_dentry(name, NULL, lookup_flags);
+ dentry = lookup_dentry(name,lookup_flags|LOOKUP_POSITIVE);
putname(name);
- if (!IS_ERR(dentry)) {
- if (!dentry->d_inode) {
- dput(dentry);
- dentry = ERR_PTR(-ENOENT);
- }
- }
}
return dentry;
}
@@ -600,7 +702,7 @@ static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir)
return -ENOTDIR;
if (IS_ROOT(victim))
return -EBUSY;
- if (victim->d_mounts != victim->d_covers)
+ if (d_mountpoint(victim))
return -EBUSY;
} else if (S_ISDIR(victim->d_inode->i_mode))
return -EISDIR;
@@ -639,7 +741,7 @@ static inline int lookup_flags(unsigned int f)
if (f & O_DIRECTORY)
retval |= LOOKUP_DIRECTORY;
-
+
return retval;
}
@@ -679,64 +781,92 @@ exit_lock:
* which is a lot more logical, and also allows the "no perm" needed
* for symlinks (where the permissions are checked later).
*/
-struct dentry * __open_namei(const char * pathname, int flag, int mode, struct dentry * dir)
+int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
{
- int acc_mode, error;
+ int acc_mode, error = 0;
struct inode *inode;
struct dentry *dentry;
- dentry = lookup_dentry(pathname, dir, lookup_flags(flag));
- if (IS_ERR(dentry))
- return dentry;
-
acc_mode = ACC_MODE(flag);
- if (flag & O_CREAT) {
+ if (!(flag & O_CREAT)) {
+ if (walk_init(pathname, lookup_flags(flag), nd))
+ error = walk_name(pathname, lookup_flags(flag), nd);
+ if (error)
+ return error;
+
+ dentry = nd->dentry;
+ } else {
struct dentry *dir;
- if (dentry->d_inode) {
- if (!(flag & O_EXCL))
- goto nocreate;
- error = -EEXIST;
+ if (walk_init(pathname, LOOKUP_PARENT, nd))
+ error = walk_name(pathname, LOOKUP_PARENT, nd);
+ if (error)
+ return error;
+ /*
+ * It's not obvious that open(".", O_CREAT, foo) should
+ * fail, but it's even less obvious that it should succeed.
+ * Since O_CREAT means an intention to create the thing and
+ * open(2) had never created directories, count it as caller's
+ * luserdom and let him sod off - -EISDIR it is.
+ */
+ error = -EISDIR;
+ if (!nd->last.len || (nd->last.name[0] == '.' &&
+ (nd->last.len == 1 ||
+ (nd->last.name[1] == '.' && nd->last.len == 2))))
+ goto exit;
+ /* same for foo/ */
+ if (nd->last.name[nd->last.len])
goto exit;
- }
- dir = lock_parent(dentry);
- if (!check_parent(dir, dentry)) {
- /*
- * Really nasty race happened. What's the
- * right error code? We had a dentry, but
- * before we could use it it was removed
- * by somebody else. We could just re-try
- * everything, I guess.
- *
- * ENOENT is definitely wrong.
- */
- error = -ENOENT;
- unlock_dir(dir);
+ dir = dget(nd->dentry);
+ down(&dir->d_inode->i_sem);
+
+ dentry = lookup_hash(&nd->last, dget(nd->dentry));
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry)) {
+ up(&dir->d_inode->i_sem);
+ dput(dir);
goto exit;
}
- /*
- * Somebody might have created the file while we
- * waited for the directory lock.. So we have to
- * re-do the existence test.
- */
if (dentry->d_inode) {
- error = 0;
+ up(&dir->d_inode->i_sem);
+ dput(dir);
+ error = -EEXIST;
if (flag & O_EXCL)
- error = -EEXIST;
+ goto exit;
+ if (dentry->d_inode->i_op &&
+ dentry->d_inode->i_op->follow_link) {
+ /*
+ * With O_EXCL it would be -EEXIST.
+ * If symlink is a dangling one it's -ENOENT.
+ * Otherwise we open the object it points to.
+ */
+ error = do_follow_link(dentry, nd);
+ dput(dentry);
+ if (error)
+ return error;
+ dentry = nd->dentry;
+ } else {
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ }
+ error = -EISDIR;
+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
+ goto exit;
} else {
- error = vfs_create(dir->d_inode, dentry,mode);
+ error = vfs_create(dir->d_inode, dentry, mode);
/* Don't check for write permission, don't truncate */
acc_mode = 0;
flag &= ~O_TRUNC;
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ unlock_dir(dir);
+ if (error)
+ goto exit;
}
- unlock_dir(dir);
- if (error)
- goto exit;
}
-nocreate:
error = -ENOENT;
inode = dentry->d_inode;
if (!inode)
@@ -804,11 +934,45 @@ nocreate:
if (flag & FMODE_WRITE)
DQUOT_INIT(inode);
- return dentry;
+ return 0;
exit:
+ dput(nd->dentry);
+ mntput(nd->mnt);
+ return error;
+}
+
+static struct dentry *lookup_create(const char *name, int is_dir)
+{
+ struct nameidata nd;
+ struct dentry *dentry;
+ int err = 0;
+ if (walk_init(name, LOOKUP_PARENT, &nd))
+ err = walk_name(name, LOOKUP_PARENT, &nd);
+ dentry = ERR_PTR(err);
+ if (err)
+ goto out;
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = ERR_PTR(-EEXIST);
+ if (!nd.last.len || (nd.last.name[0] == '.' &&
+ (nd.last.len == 1 || (nd.last.name[1] == '.' && nd.last.len == 2))))
+ goto fail;
+ dentry = lookup_hash(&nd.last, dget(nd.dentry));
+ if (IS_ERR(dentry))
+ goto fail;
+ if (!is_dir && nd.last.name[nd.last.len] && !dentry->d_inode)
+ goto enoent;
+out_dput:
+ dput(nd.dentry);
+ mntput(nd.mnt);
+out:
+ return dentry;
+enoent:
dput(dentry);
- return ERR_PTR(error);
+ dentry = ERR_PTR(-ENOENT);
+fail:
+ up(&nd.dentry->d_inode->i_sem);
+ goto out_dput;
}
int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
@@ -842,18 +1006,14 @@ struct dentry * do_mknod(const char * filename, int mode, dev_t dev)
struct dentry *dir;
struct dentry *dentry, *retval;
- dentry = lookup_dentry(filename, NULL, 0);
+ dentry = lookup_create(filename, 0);
if (IS_ERR(dentry))
return dentry;
- dir = lock_parent(dentry);
- error = -ENOENT;
- if (!check_parent(dir, dentry))
- goto exit_lock;
+ dir = dget(dentry->d_parent);
error = vfs_mknod(dir->d_inode, dentry, mode, dev);
-exit_lock:
retval = ERR_PTR(error);
if (!error)
retval = dget(dentry);
@@ -875,14 +1035,11 @@ asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev)
return PTR_ERR(tmp);
lock_kernel();
- dentry = lookup_dentry(tmp, NULL, 0);
+ dentry = lookup_create(tmp, 0);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out;
- dir = lock_parent(dentry);
- error = -ENOENT;
- if (!check_parent(dir, dentry))
- goto out_unlock;
+ dir = dget(dentry->d_parent);
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(dir->d_inode, dentry, mode);
@@ -896,7 +1053,6 @@ asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev)
default:
error = -EINVAL;
}
-out_unlock:
unlock_dir(dir);
dput(dentry);
out:
@@ -928,50 +1084,28 @@ exit_lock:
return error;
}
-static inline int do_mkdir(const char * pathname, int mode)
-{
- int error;
- struct dentry *dir;
- struct dentry *dentry;
-
- dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK);
- error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto exit;
-
- /*
- * EEXIST is kind of a strange error code to
- * return, but basically if the dentry was moved
- * or unlinked while we locked the parent, we
- * do know that it _did_ exist before, and as
- * such it makes perfect sense.. In contrast,
- * ENOENT doesn't make sense for mkdir.
- */
- dir = lock_parent(dentry);
- error = -EEXIST;
- if (!check_parent(dir, dentry))
- goto exit_lock;
-
- error = vfs_mkdir(dir->d_inode, dentry, mode);
-
-exit_lock:
- unlock_dir(dir);
- dput(dentry);
-exit:
- return error;
-}
-
asmlinkage long sys_mkdir(const char * pathname, int mode)
{
int error;
char * tmp;
tmp = getname(pathname);
- if(IS_ERR(tmp))
- return PTR_ERR(tmp);
- lock_kernel();
- error = do_mkdir(tmp,mode);
- unlock_kernel();
+ error = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
+ struct dentry *dir;
+ struct dentry *dentry;
+
+ lock_kernel();
+ dentry = lookup_create(tmp, 1);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ dir = dget(dentry->d_parent);
+ error = vfs_mkdir(dir->d_inode, dentry, mode);
+ unlock_dir(dir);
+ dput(dentry);
+ }
+ unlock_kernel();
+ }
putname(tmp);
return error;
@@ -1033,7 +1167,7 @@ static inline int do_rmdir(const char * name)
struct dentry *dir;
struct dentry *dentry;
- dentry = lookup_dentry(name, NULL, 0);
+ dentry = lookup_dentry(name, LOOKUP_POSITIVE);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto exit;
@@ -1082,13 +1216,13 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
return error;
}
-int do_unlink(const char * name, struct dentry * base)
+static int do_unlink(const char * name)
{
int error;
struct dentry *dir;
struct dentry *dentry;
- dentry = lookup_dentry(name, base, 0);
+ dentry = lookup_dentry(name, LOOKUP_POSITIVE);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto exit;
@@ -1113,7 +1247,7 @@ asmlinkage long sys_unlink(const char * pathname)
if(IS_ERR(tmp))
return PTR_ERR(tmp);
lock_kernel();
- error = do_unlink(tmp, NULL);
+ error = do_unlink(tmp);
unlock_kernel();
putname(tmp);
@@ -1141,32 +1275,6 @@ exit_lock:
return error;
}
-static inline int do_symlink(const char * oldname, const char * newname)
-{
- int error;
- struct dentry *dir;
- struct dentry *dentry;
-
- dentry = lookup_dentry(newname, NULL, 0);
-
- error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto exit;
-
- dir = lock_parent(dentry);
- error = -ENOENT;
- if (!check_parent(dir, dentry))
- goto exit_lock;
-
- error = vfs_symlink(dir->d_inode, dentry, oldname);
-
-exit_lock:
- unlock_dir(dir);
- dput(dentry);
-exit:
- return error;
-}
-
asmlinkage long sys_symlink(const char * oldname, const char * newname)
{
int error;
@@ -1179,8 +1287,18 @@ asmlinkage long sys_symlink(const char * oldname, const char * newname)
to = getname(newname);
error = PTR_ERR(to);
if (!IS_ERR(to)) {
+ struct dentry *dir;
+ struct dentry *dentry;
+
lock_kernel();
- error = do_symlink(from,to);
+ dentry = lookup_create(to, 0);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ dir = dget(dentry->d_parent);
+ error = vfs_symlink(dir->d_inode, dentry, from);
+ unlock_dir(dir);
+ dput(dentry);
+ }
unlock_kernel();
putname(to);
}
@@ -1224,51 +1342,20 @@ exit_lock:
return error;
}
-static inline int do_link(const char * oldname, const char * newname)
-{
- struct dentry *old_dentry, *new_dentry, *dir;
- int error;
-
- /*
- * Hardlinks are often used in delicate situations. We avoid
- * security-related surprises by not following symlinks on the
- * newname. --KAB
- *
- * We don't follow them on the oldname either to be compatible
- * with linux 2.0, and to avoid hard-linking to directories
- * and other special files. --ADM
- */
- old_dentry = lookup_dentry(oldname, NULL, 0);
- error = PTR_ERR(old_dentry);
- if (IS_ERR(old_dentry))
- goto exit;
-
- new_dentry = lookup_dentry(newname, NULL, 0);
- error = PTR_ERR(new_dentry);
- if (IS_ERR(new_dentry))
- goto exit_old;
-
- dir = lock_parent(new_dentry);
- error = -ENOENT;
- if (!check_parent(dir, new_dentry))
- goto exit_lock;
-
- error = vfs_link(old_dentry, dir->d_inode, new_dentry);
-
-exit_lock:
- unlock_dir(dir);
- dput(new_dentry);
-exit_old:
- dput(old_dentry);
-exit:
- return error;
-}
-
+/*
+ * Hardlinks are often used in delicate situations. We avoid
+ * security-related surprises by not following symlinks on the
+ * newname. --KAB
+ *
+ * We don't follow them on the oldname either to be compatible
+ * with linux 2.0, and to avoid hard-linking to directories
+ * and other special files. --ADM
+ */
asmlinkage long sys_link(const char * oldname, const char * newname)
{
int error;
char * from;
- char * to;
+ char * to;
from = getname(oldname);
if(IS_ERR(from))
@@ -1276,8 +1363,24 @@ asmlinkage long sys_link(const char * oldname, const char * newname)
to = getname(newname);
error = PTR_ERR(to);
if (!IS_ERR(to)) {
+ struct dentry *old_dentry, *new_dentry, *dir;
+
lock_kernel();
- error = do_link(from,to);
+ old_dentry = lookup_dentry(from, LOOKUP_POSITIVE);
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto exit;
+
+ new_dentry = lookup_create(to, 0);
+ error = PTR_ERR(new_dentry);
+ if (!IS_ERR(new_dentry)) {
+ dir = dget(new_dentry->d_parent);
+ error = vfs_link(old_dentry, dir->d_inode, new_dentry);
+ unlock_dir(dir);
+ dput(new_dentry);
+ }
+ dput(old_dentry);
+exit:
unlock_kernel();
putname(to);
}
@@ -1435,21 +1538,17 @@ static inline int do_rename(const char * oldname, const char * newname)
struct dentry * old_dir, * new_dir;
struct dentry * old_dentry, *new_dentry;
- old_dentry = lookup_dentry(oldname, NULL, 0);
+ old_dentry = lookup_dentry(oldname, LOOKUP_POSITIVE);
error = PTR_ERR(old_dentry);
if (IS_ERR(old_dentry))
goto exit;
- error = -ENOENT;
- if (!old_dentry->d_inode)
- goto exit_old;
-
{
unsigned int flags = 0;
if (S_ISDIR(old_dentry->d_inode->i_mode))
flags = LOOKUP_SLASHOK;
- new_dentry = lookup_dentry(newname, NULL, flags);
+ new_dentry = lookup_dentry(newname, flags);
}
error = PTR_ERR(new_dentry);
@@ -1512,29 +1611,30 @@ out:
return len;
}
-static inline struct dentry *
-__vfs_follow_link(struct dentry *dentry, struct dentry *base,
- unsigned follow, const char *link)
+static inline int
+__vfs_follow_link(struct nameidata *nd, const char *link)
{
- struct dentry *result;
- UPDATE_ATIME(dentry->d_inode);
-
if (IS_ERR(link))
goto fail;
- result = lookup_dentry(link, base, follow);
- return result;
+ if (*link == '/') {
+ dput(nd->dentry);
+ mntput(nd->mnt);
+ if (!walk_init_root(link, LOOKUP_FOLLOW, nd))
+ /* weird __emul_prefix() stuff did it */
+ return 0;
+ }
+ return walk_name(link, LOOKUP_FOLLOW, nd);
fail:
- dput(base);
- return (struct dentry *)link;
+ dput(nd->dentry);
+ mntput(nd->mnt);
+ return PTR_ERR(link);
}
-struct dentry *
-vfs_follow_link(struct dentry *dentry, struct dentry *base,
-unsigned int follow, const char *link)
+int vfs_follow_link(struct nameidata *nd, const char *link)
{
- return __vfs_follow_link(dentry,base,follow,link);
+ return __vfs_follow_link(nd, link);
}
/* get the link contents into pagecache */
@@ -1572,12 +1672,11 @@ int page_readlink(struct dentry *dentry, char *buffer, int buflen)
return res;
}
-struct dentry *
-page_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
+int page_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct page *page = NULL;
char *s = page_getlink(dentry, &page);
- struct dentry *res = __vfs_follow_link(dentry,base,follow,s);
+ int res = __vfs_follow_link(nd, s);
if (page) {
kunmap(page);
page_cache_release(page);