patch-2.3.99-pre6 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: 549
- Date:
Mon Apr 24 18:56:33 2000
- Orig file:
v2.3.99-pre5/linux/fs/namei.c
- Orig date:
Wed Apr 12 10:02:34 2000
diff -u --recursive --new-file v2.3.99-pre5/linux/fs/namei.c linux/fs/namei.c
@@ -286,11 +286,12 @@
*
* We expect 'base' to be positive and a directory.
*/
-int walk_name(const char * name, unsigned lookup_flags, struct nameidata *nd)
+int walk_name(const char * name, struct nameidata *nd)
{
struct dentry *dentry;
struct inode *inode;
int err;
+ unsigned int lookup_flags = nd->flags;
while (*name=='/')
name++;
@@ -301,9 +302,6 @@
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(;;) {
unsigned long hash;
@@ -379,10 +377,10 @@
err = -ENOENT;
inode = dentry->d_inode;
if (!inode)
- break;
+ goto out_dput;
err = -ENOTDIR;
if (!inode->i_op)
- break;
+ goto out_dput;
if (inode->i_op->follow_link) {
err = do_follow_link(dentry, nd);
@@ -464,16 +462,23 @@
goto return_base;
no_inode:
err = -ENOENT;
- if (lookup_flags & LOOKUP_POSITIVE)
+ if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY))
break;
- if (lookup_flags & LOOKUP_DIRECTORY)
- if (!(lookup_flags & LOOKUP_SLASHOK))
- break;
goto return_base;
lookup_parent:
nd->last = this;
+ nd->last_type = LAST_NORM;
+ if (this.name[0] != '.')
+ goto return_base;
+ if (this.len == 1)
+ nd->last_type = LAST_DOT;
+ else if (this.len == 2 && this.name[1] == '.')
+ nd->last_type = LAST_DOTDOT;
return_base:
return 0;
+out_dput:
+ dput(dentry);
+ break;
}
dput(nd->dentry);
mntput(nd->mnt);
@@ -482,27 +487,20 @@
}
/* returns 1 if everything is done */
-static int __emul_lookup_dentry(const char *name, int lookup_flags,
- struct nameidata *nd)
+static int __emul_lookup_dentry(const char *name, 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))
+ nd->mnt = mntget(current->fs->altrootmnt);
+ nd->dentry = dget(current->fs->altroot);
+ if (walk_name(name, nd))
return 0;
if (!nd->dentry->d_inode) {
struct nameidata nd_root;
- nd_root.last.len = 0;
+ nd_root.last_type = LAST_ROOT;
+ nd_root.flags = nd->flags;
nd_root.mnt = mntget(current->fs->rootmnt);
nd_root.dentry = dget(current->fs->root);
- if (walk_name(name, lookup_flags, &nd_root))
+ if (walk_name(name, &nd_root))
return 1;
if (nd_root.dentry->d_inode) {
dput(nd->dentry);
@@ -518,11 +516,36 @@
return 1;
}
+void set_fs_altroot(void)
+{
+ char *emul = __emul_prefix();
+ struct nameidata nd;
+ struct vfsmount *mnt = NULL, *oldmnt;
+ struct dentry *dentry = NULL, *olddentry;
+ if (emul) {
+ nd.mnt = mntget(current->fs->rootmnt);
+ nd.dentry = dget(current->fs->root);
+ nd.flags = LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_POSITIVE;
+ if (walk_name(emul,&nd) == 0) {
+ mnt = nd.mnt;
+ dentry = nd.dentry;
+ }
+ }
+ oldmnt = current->fs->altrootmnt;
+ olddentry = current->fs->altroot;
+ current->fs->altrootmnt = mnt;
+ current->fs->altroot = dentry;
+ if (olddentry) {
+ dput(olddentry);
+ mntput(oldmnt);
+ }
+}
+
static inline int
-walk_init_root(const char *name, unsigned flags, struct nameidata *nd)
+walk_init_root(const char *name, struct nameidata *nd)
{
- if (current->personality != PER_LINUX)
- if (__emul_lookup_dentry(name,flags,nd))
+ if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT))
+ if (__emul_lookup_dentry(name,nd))
return 0;
nd->mnt = mntget(current->fs->rootmnt);
nd->dentry = dget(current->fs->root);
@@ -531,9 +554,10 @@
int walk_init(const char *name,unsigned int flags,struct nameidata *nd)
{
- nd->last.len = 0;
+ nd->last_type = LAST_ROOT; /* if there are only slashes... */
+ nd->flags = flags;
if (*name=='/')
- return walk_init_root(name,flags,nd);
+ return walk_init_root(name,nd);
nd->mnt = mntget(current->fs->pwdmnt);
nd->dentry = dget(current->fs->pwd);
return 1;
@@ -545,7 +569,7 @@
int err = 0;
if (walk_init(name, lookup_flags, &nd))
- err = walk_name(name, lookup_flags, &nd);
+ err = walk_name(name, &nd);
if (!err) {
mntput(nd.mnt);
return nd.dentry;
@@ -589,15 +613,10 @@
dentry = inode->i_op->lookup(inode, new);
if (!dentry)
dentry = new;
- else {
+ else
dput(new);
- if (IS_ERR(dentry))
- goto out;
- }
}
-
out:
- dput(base);
return dentry;
}
@@ -689,6 +708,8 @@
int error;
if (!victim->d_inode || victim->d_parent->d_inode != dir)
return -ENOENT;
+ if (IS_DEADDIR(dir))
+ return -ENOENT;
error = permission(dir,MAY_WRITE | MAY_EXEC);
if (error)
return error;
@@ -720,6 +741,8 @@
static inline int may_create(struct inode *dir, struct dentry *child) {
if (child->d_inode)
return -EEXIST;
+ if (IS_DEADDIR(dir))
+ return -ENOENT;
return permission(dir,MAY_WRITE | MAY_EXEC);
}
@@ -790,7 +813,7 @@
acc_mode = ACC_MODE(flag);
if (!(flag & O_CREAT)) {
if (walk_init(pathname, lookup_flags(flag), nd))
- error = walk_name(pathname, lookup_flags(flag), nd);
+ error = walk_name(pathname, nd);
if (error)
return error;
@@ -799,7 +822,7 @@
struct dentry *dir;
if (walk_init(pathname, LOOKUP_PARENT, nd))
- error = walk_name(pathname, LOOKUP_PARENT, nd);
+ error = walk_name(pathname, nd);
if (error)
return error;
/*
@@ -810,9 +833,7 @@
* 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))))
+ if (nd->last_type != LAST_NORM)
goto exit;
/* same for foo/ */
if (nd->last.name[nd->last.len])
@@ -821,7 +842,7 @@
dir = dget(nd->dentry);
down(&dir->d_inode->i_sem);
- dentry = lookup_hash(&nd->last, dget(nd->dentry));
+ dentry = lookup_hash(&nd->last, nd->dentry);
error = PTR_ERR(dentry);
if (IS_ERR(dentry)) {
up(&dir->d_inode->i_sem);
@@ -834,7 +855,7 @@
dput(dir);
error = -EEXIST;
if (flag & O_EXCL)
- goto exit;
+ goto exit_dput;
if (dentry->d_inode->i_op &&
dentry->d_inode->i_op->follow_link) {
/*
@@ -936,6 +957,8 @@
return 0;
+exit_dput:
+ dput(dentry);
exit:
dput(nd->dentry);
mntput(nd->mnt);
@@ -948,16 +971,15 @@
struct dentry *dentry;
int err = 0;
if (walk_init(name, LOOKUP_PARENT, &nd))
- err = walk_name(name, LOOKUP_PARENT, &nd);
+ err = walk_name(name, &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))))
+ if (nd.last_type != LAST_NORM)
goto fail;
- dentry = lookup_hash(&nd.last, dget(nd.dentry));
+ dentry = lookup_hash(&nd.last, nd.dentry);
if (IS_ERR(dentry))
goto fail;
if (!is_dir && nd.last.name[nd.last.len] && !dentry->d_inode)
@@ -1155,47 +1177,53 @@
double_down(&dir->i_zombie, &dentry->d_inode->i_zombie);
d_unhash(dentry);
error = dir->i_op->rmdir(dir, dentry);
+ if (!error)
+ dentry->d_inode->i_flags |= S_DEAD;
double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
dput(dentry);
return error;
}
-static inline int do_rmdir(const char * name)
+asmlinkage long sys_rmdir(const char * pathname)
{
- int error;
- struct dentry *dir;
+ int error = 0;
+ char * name;
struct dentry *dentry;
+ struct nameidata nd;
- dentry = lookup_dentry(name, LOOKUP_POSITIVE);
- error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
+ name = getname(pathname);
+ if(IS_ERR(name))
+ return PTR_ERR(name);
+ lock_kernel();
+
+ if (walk_init(name, LOOKUP_PARENT, &nd))
+ error = walk_name(name, &nd);
+ if (error)
goto exit;
- dir = lock_parent(dentry);
- error = -ENOENT;
- if (check_parent(dir, dentry))
- error = vfs_rmdir(dir->d_inode, dentry);
- unlock_dir(dir);
- dput(dentry);
+ switch(nd.last_type) {
+ case LAST_DOTDOT:
+ error = -ENOTEMPTY;
+ goto exit1;
+ case LAST_ROOT: case LAST_DOT:
+ error = -EBUSY;
+ goto exit1;
+ }
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = vfs_rmdir(nd.dentry->d_inode, dentry);
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+exit1:
+ dput(nd.dentry);
+ mntput(nd.mnt);
exit:
- return error;
-}
-
-asmlinkage long sys_rmdir(const char * pathname)
-{
- int error;
- char * tmp;
-
- tmp = getname(pathname);
- if(IS_ERR(tmp))
- return PTR_ERR(tmp);
- lock_kernel();
- error = do_rmdir(tmp);
unlock_kernel();
-
- putname(tmp);
-
+ putname(name);
return error;
}
@@ -1216,42 +1244,50 @@
return error;
}
-static int do_unlink(const char * name)
+asmlinkage long sys_unlink(const char * pathname)
{
- int error;
- struct dentry *dir;
+ int error = 0;
+ char * name;
struct dentry *dentry;
+ struct nameidata nd;
- dentry = lookup_dentry(name, LOOKUP_POSITIVE);
- error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto exit;
-
- dir = lock_parent(dentry);
- error = -ENOENT;
- if (check_parent(dir, dentry))
- error = vfs_unlink(dir->d_inode, dentry);
+ name = getname(pathname);
+ if(IS_ERR(name))
+ return PTR_ERR(name);
+ lock_kernel();
- unlock_dir(dir);
- dput(dentry);
+ if (walk_init(name, LOOKUP_PARENT, &nd))
+ error = walk_name(name, &nd);
+ if (error)
+ goto exit;
+ error = -EISDIR;
+ if (nd.last_type != LAST_NORM)
+ goto exit1;
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ /* Why not before? Because we want correct error value */
+ if (nd.last.name[nd.last.len])
+ goto slashes;
+ error = vfs_unlink(nd.dentry->d_inode, dentry);
+ exit2:
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+exit1:
+ dput(nd.dentry);
+ mntput(nd.mnt);
exit:
- return error;
-}
-
-asmlinkage long sys_unlink(const char * pathname)
-{
- int error;
- char * tmp;
-
- tmp = getname(pathname);
- if(IS_ERR(tmp))
- return PTR_ERR(tmp);
- lock_kernel();
- error = do_unlink(tmp);
unlock_kernel();
- putname(tmp);
+ putname(name);
return error;
+
+slashes:
+ error = !dentry->d_inode ? -ENOENT :
+ S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+ goto exit2;
}
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
@@ -1468,6 +1504,8 @@
&new_dir->i_zombie);
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
if (target) {
+ if (!error)
+ target->i_flags |= S_DEAD;
triple_up(&old_dir->i_zombie,
&new_dir->i_zombie,
&target->i_zombie);
@@ -1534,41 +1572,72 @@
static inline int do_rename(const char * oldname, const char * newname)
{
- int error;
+ int error = 0;
struct dentry * old_dir, * new_dir;
struct dentry * old_dentry, *new_dentry;
+ struct nameidata oldnd, newnd;
- old_dentry = lookup_dentry(oldname, LOOKUP_POSITIVE);
+ if (walk_init(oldname, LOOKUP_PARENT, &oldnd))
+ error = walk_name(oldname, &oldnd);
- error = PTR_ERR(old_dentry);
- if (IS_ERR(old_dentry))
+ if (error)
goto exit;
- {
- unsigned int flags = 0;
- if (S_ISDIR(old_dentry->d_inode->i_mode))
- flags = LOOKUP_SLASHOK;
- new_dentry = lookup_dentry(newname, flags);
- }
+ if (walk_init(newname, LOOKUP_PARENT, &newnd))
+ error = walk_name(newname, &newnd);
+ if (error)
+ goto exit1;
- error = PTR_ERR(new_dentry);
- if (IS_ERR(new_dentry))
- goto exit_old;
+ error = -EXDEV;
+ if (oldnd.mnt != newnd.mnt)
+ goto exit2;
- new_dir = get_parent(new_dentry);
- old_dir = get_parent(old_dentry);
+ old_dir = oldnd.dentry;
+ error = -EBUSY;
+ if (oldnd.last_type != LAST_NORM)
+ goto exit2;
+
+ new_dir = newnd.dentry;
+ if (newnd.last_type != LAST_NORM)
+ goto exit2;
double_lock(new_dir, old_dir);
+ old_dentry = lookup_hash(&oldnd.last, old_dir);
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto exit3;
+ /* source must exist */
error = -ENOENT;
- if (check_parent(old_dir, old_dentry) && check_parent(new_dir, new_dentry))
- error = vfs_rename(old_dir->d_inode, old_dentry,
+ if (!old_dentry->d_inode)
+ goto exit4;
+ /* unless the source is a directory trailing slashes give -ENOTDIR */
+ if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+ error = -ENOTDIR;
+ if (oldnd.last.name[oldnd.last.len])
+ goto exit4;
+ if (newnd.last.name[newnd.last.len])
+ goto exit4;
+ }
+ new_dentry = lookup_hash(&newnd.last, new_dir);
+ error = PTR_ERR(new_dentry);
+ if (IS_ERR(new_dentry))
+ goto exit4;
+
+ error = vfs_rename(old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry);
- double_unlock(new_dir, old_dir);
dput(new_dentry);
-exit_old:
+exit4:
dput(old_dentry);
+exit3:
+ double_up(&new_dir->d_inode->i_sem, &old_dir->d_inode->i_sem);
+exit2:
+ dput(newnd.dentry);
+ mntput(newnd.mnt);
+exit1:
+ dput(oldnd.dentry);
+ mntput(oldnd.mnt);
exit:
return error;
}
@@ -1620,11 +1689,11 @@
if (*link == '/') {
dput(nd->dentry);
mntput(nd->mnt);
- if (!walk_init_root(link, LOOKUP_FOLLOW, nd))
+ if (!walk_init_root(link, nd))
/* weird __emul_prefix() stuff did it */
return 0;
}
- return walk_name(link, LOOKUP_FOLLOW, nd);
+ return walk_name(link, nd);
fail:
dput(nd->dentry);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)