int permission(struct inode *inode, int mask, struct nameidata *nd)
{
int retval, submask;
- umode_t mode = inode->i_mode;
if (mask & MAY_WRITE) {
+ umode_t mode = inode->i_mode;
+
/*
* Nobody gets write access to a read-only fs.
*/
- if (IS_RDONLY(inode) &&
+ if ((IS_RDONLY(inode) || (nd && MNT_IS_RDONLY(nd->mnt))) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
return -EACCES;
}
+
/* Ordinary permission routines do not understand MAY_APPEND. */
submask = mask & ~MAY_APPEND;
- if (nd && (mask & MAY_WRITE) && MNT_IS_RDONLY(nd->mnt) &&
- (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
- return -EROFS;
if ((retval = xid_permission(inode, mask, nd)))
return retval;
if (inode->i_op && inode->i_op->permission)
inode = dentry->d_inode;
if (!inode)
goto done;
+ if (inode->i_sb->s_magic == PROC_SUPER_MAGIC) {
+ struct proc_dir_entry *de = PDE(inode);
+
+ if (de && !vx_hide_check(0, de->vx_flags))
+ goto hidden;
+ }
#ifdef CONFIG_VSERVER_FILESHARING
/* MEF: PlanetLab FS module assumes that any file that can be
* named (e.g., via a cross mount) is not hidden from another
#endif
if (!vx_check(inode->i_xid, VX_WATCH|VX_ADMIN|VX_HOSTID|VX_IDENT))
goto hidden;
- if (inode->i_sb->s_magic == PROC_SUPER_MAGIC) {
- struct proc_dir_entry *de = PDE(inode);
-
- if (de && !vx_hide_check(0, de->vx_flags))
- goto hidden;
- }
done:
path->mnt = mnt;
path->dentry = dentry;
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static inline int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+static inline int may_delete(struct inode *dir, struct dentry *victim,
+ int isdir, struct nameidata *nd)
{
int error;
BUG_ON(victim->d_parent->d_inode != dir);
- error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+ error = permission(dir,MAY_WRITE | MAY_EXEC, nd);
if (error)
return error;
if (IS_APPEND(dir))
return permission(dir,MAY_WRITE | MAY_EXEC, nd);
}
-static inline int mnt_may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child) {
- if (child->d_inode)
- return -EEXIST;
- if (IS_DEADDIR(dir))
- return -ENOENT;
- if (mnt->mnt_flags & MNT_RDONLY)
- return -EROFS;
- return 0;
-}
-
-static inline int mnt_may_unlink(struct vfsmount *mnt, struct inode *dir, struct dentry *child) {
- if (!child->d_inode)
- return -ENOENT;
- if (mnt->mnt_flags & MNT_RDONLY)
- return -EROFS;
- return 0;
-}
-
/*
* Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security
* reasons.
return -EACCES;
flag &= ~O_TRUNC;
- } else if ((IS_RDONLY(inode) || (nd && MNT_IS_RDONLY(nd->mnt)))
+ } else if ((IS_RDONLY(inode) || MNT_IS_RDONLY(nd->mnt))
&& (flag & FMODE_WRITE))
return -EROFS;
/*
struct dentry *lookup_create(struct nameidata *nd, int is_dir)
{
struct dentry *dentry;
- int error;
down(&nd->dentry->d_inode->i_sem);
- error = -EEXIST;
+ dentry = ERR_PTR(-EEXIST);
if (nd->last_type != LAST_NORM)
- goto out;
+ goto fail;
nd->flags &= ~LOOKUP_PARENT;
dentry = lookup_hash(&nd->last, nd->dentry);
if (IS_ERR(dentry))
- goto ret;
- error = mnt_may_create(nd->mnt, nd->dentry->d_inode, dentry);
- if (error)
goto fail;
- error = -ENOENT;
if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
- goto fail;
-ret:
+ goto enoent;
return dentry;
-fail:
+enoent:
dput(dentry);
-out:
- return ERR_PTR(error);
+ dentry = ERR_PTR(-ENOENT);
+fail:
+ return dentry;
}
EXPORT_SYMBOL_GPL(lookup_create);
-int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+int vfs_mknod(struct inode *dir, struct dentry *dentry,
+ int mode, dev_t dev, struct nameidata *nd)
{
- int error = may_create(dir, dentry, NULL);
+ int error = may_create(dir, dentry, nd);
if (error)
return error;
goto out;
dentry = lookup_create(&nd, 0);
error = PTR_ERR(dentry);
-
if (!IS_POSIXACL(nd.dentry->d_inode))
mode &= ~current->fs->umask;
if (!IS_ERR(dentry)) {
error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(nd.dentry->d_inode,dentry,mode,
- new_decode_dev(dev));
+ error = vfs_mknod(nd.dentry->d_inode, dentry, mode,
+ new_decode_dev(dev), &nd);
break;
case S_IFIFO: case S_IFSOCK:
- error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0);
+ error = vfs_mknod(nd.dentry->d_inode, dentry, mode,
+ 0, &nd);
break;
case S_IFDIR:
error = -EPERM;
return error;
}
-int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+int vfs_mkdir(struct inode *dir, struct dentry *dentry,
+ int mode, struct nameidata *nd)
{
- int error = may_create(dir, dentry, NULL);
+ int error = may_create(dir, dentry, nd);
if (error)
return error;
if (!IS_ERR(dentry)) {
if (!IS_POSIXACL(nd.dentry->d_inode))
mode &= ~current->fs->umask;
- error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+ error = vfs_mkdir(nd.dentry->d_inode, dentry,
+ mode, &nd);
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
spin_unlock(&dcache_lock);
}
-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
{
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(dir, dentry, 1, nd);
if (error)
return error;
dentry = lookup_hash(&nd.last, nd.dentry);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- error = mnt_may_unlink(nd.mnt, nd.dentry->d_inode, dentry);
- if (error)
- goto exit2;
- error = vfs_rmdir(nd.dentry->d_inode, dentry);
- exit2:
+ error = vfs_rmdir(nd.dentry->d_inode, dentry, &nd);
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
return error;
}
-int vfs_unlink(struct inode *dir, struct dentry *dentry)
+int vfs_unlink(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
{
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(dir, dentry, 0, nd);
if (error)
return error;
/* Why not before? Because we want correct error value */
if (nd.last.name[nd.last.len])
goto slashes;
- error = mnt_may_unlink(nd.mnt, nd.dentry->d_inode, dentry);
- if (error)
- goto exit2;
inode = dentry->d_inode;
if (inode)
atomic_inc(&inode->i_count);
- error = vfs_unlink(nd.dentry->d_inode, dentry);
+ error = vfs_unlink(nd.dentry->d_inode, dentry, &nd);
exit2:
dput(dentry);
}
goto exit2;
}
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
+int vfs_symlink(struct inode *dir, struct dentry *dentry,
+ const char *oldname, int mode, struct nameidata *nd)
{
- int error = may_create(dir, dentry, NULL);
+ int error = may_create(dir, dentry, nd);
if (error)
return error;
dentry = lookup_create(&nd, 0);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
+ error = vfs_symlink(nd.dentry->d_inode, dentry,
+ from, S_IALLUGO, &nd);
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
return error;
}
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+int vfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry, struct nameidata *nd)
{
struct inode *inode = old_dentry->d_inode;
int error;
if (!inode)
return -ENOENT;
- error = may_create(dir, new_dentry, NULL);
+ error = may_create(dir, new_dentry, nd);
if (error)
return error;
new_dentry = lookup_create(&nd, 0);
error = PTR_ERR(new_dentry);
if (!IS_ERR(new_dentry)) {
- error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+ error = vfs_link(old_nd.dentry, nd.dentry->d_inode,
+ new_dentry, &nd);
dput(new_dentry);
}
up(&nd.dentry->d_inode->i_sem);
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;
- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(old_dir, old_dentry, is_dir, NULL);
if (error)
return error;
if (!new_dentry->d_inode)
error = may_create(new_dir, new_dentry, NULL);
else
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(new_dir, new_dentry, is_dir, NULL);
if (error)
return error;