Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: [patch 2/3] vfs: dcache cleanups [patch 1/3] vfs: dcache sparse fixes [patch 3/3] vfs: make d_path() consistent across mount operations [patch 4/4] flock: remove unused fields from file_lock_operations [patch 3/4] vfs: fix ERR_PTR abuse in generic_readlink [patch 2/4] fs: make struct file arg to d_path const [patch 1/4] vfs: path_{get,put}() cleanups [patch for 2.6.26 4/4] vfs: utimensat(): fix write access check for futimens() [patch for 2.6.26 3/4] vfs: utimensat(): fix error checking for {UTIME_NOW,UTIME_OMIT} case [patch for 2.6.26 1/4] vfs: utimensat(): ignore tv_sec if tv_nsec == UTIME_OMIT or UTIME_NOW [patch for 2.6.26 2/4] vfs: utimensat(): be consistent with utime() for immutable and append-only files [PATCH] fix cgroup-inflicted breakage in block_dev.c
This commit is contained in:
commit
4f46accee4
|
@ -931,8 +931,16 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part)
|
|||
struct gendisk *disk;
|
||||
int ret;
|
||||
int part;
|
||||
int perm = 0;
|
||||
|
||||
ret = devcgroup_inode_permission(bdev->bd_inode, file->f_mode);
|
||||
if (file->f_mode & FMODE_READ)
|
||||
perm |= MAY_READ;
|
||||
if (file->f_mode & FMODE_WRITE)
|
||||
perm |= MAY_WRITE;
|
||||
/*
|
||||
* hooks: /n/, see "layering violations".
|
||||
*/
|
||||
ret = devcgroup_inode_permission(bdev->bd_inode, perm);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
|
|
68
fs/dcache.c
68
fs/dcache.c
|
@ -17,6 +17,7 @@
|
|||
#include <linux/syscalls.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -106,9 +107,10 @@ static void dentry_lru_remove(struct dentry *dentry)
|
|||
/*
|
||||
* Release the dentry's inode, using the filesystem
|
||||
* d_iput() operation if defined.
|
||||
* Called with dcache_lock and per dentry lock held, drops both.
|
||||
*/
|
||||
static void dentry_iput(struct dentry * dentry)
|
||||
__releases(dentry->d_lock)
|
||||
__releases(dcache_lock)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
if (inode) {
|
||||
|
@ -132,12 +134,13 @@ static void dentry_iput(struct dentry * dentry)
|
|||
* d_kill - kill dentry and return parent
|
||||
* @dentry: dentry to kill
|
||||
*
|
||||
* Called with dcache_lock and d_lock, releases both. The dentry must
|
||||
* already be unhashed and removed from the LRU.
|
||||
* The dentry must already be unhashed and removed from the LRU.
|
||||
*
|
||||
* If this is the root of the dentry tree, return NULL.
|
||||
*/
|
||||
static struct dentry *d_kill(struct dentry *dentry)
|
||||
__releases(dentry->d_lock)
|
||||
__releases(dcache_lock)
|
||||
{
|
||||
struct dentry *parent;
|
||||
|
||||
|
@ -383,11 +386,11 @@ restart:
|
|||
* Try to prune ancestors as well. This is necessary to prevent
|
||||
* quadratic behavior of shrink_dcache_parent(), but is also expected
|
||||
* to be beneficial in reducing dentry cache fragmentation.
|
||||
*
|
||||
* Called with dcache_lock, drops it and then regains.
|
||||
* Called with dentry->d_lock held, drops it.
|
||||
*/
|
||||
static void prune_one_dentry(struct dentry * dentry)
|
||||
__releases(dentry->d_lock)
|
||||
__releases(dcache_lock)
|
||||
__acquires(dcache_lock)
|
||||
{
|
||||
__d_drop(dentry);
|
||||
dentry = d_kill(dentry);
|
||||
|
@ -1604,10 +1607,9 @@ static int d_isparent(struct dentry *p1, struct dentry *p2)
|
|||
*
|
||||
* Note: If ever the locking in lock_rename() changes, then please
|
||||
* remember to update this too...
|
||||
*
|
||||
* On return, dcache_lock will have been unlocked.
|
||||
*/
|
||||
static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias)
|
||||
__releases(dcache_lock)
|
||||
{
|
||||
struct mutex *m1 = NULL, *m2 = NULL;
|
||||
struct dentry *ret;
|
||||
|
@ -1743,11 +1745,9 @@ out_nolock:
|
|||
shouldnt_be_hashed:
|
||||
spin_unlock(&dcache_lock);
|
||||
BUG();
|
||||
goto shouldnt_be_hashed;
|
||||
}
|
||||
|
||||
static int prepend(char **buffer, int *buflen, const char *str,
|
||||
int namelen)
|
||||
static int prepend(char **buffer, int *buflen, const char *str, int namelen)
|
||||
{
|
||||
*buflen -= namelen;
|
||||
if (*buflen < 0)
|
||||
|
@ -1757,8 +1757,13 @@ static int prepend(char **buffer, int *buflen, const char *str,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int prepend_name(char **buffer, int *buflen, struct qstr *name)
|
||||
{
|
||||
return prepend(buffer, buflen, name->name, name->len);
|
||||
}
|
||||
|
||||
/**
|
||||
* d_path - return the path of a dentry
|
||||
* __d_path - return the path of a dentry
|
||||
* @path: the dentry/vfsmount to report
|
||||
* @root: root vfsmnt/dentry (may be modified by this function)
|
||||
* @buffer: buffer to return value in
|
||||
|
@ -1779,9 +1784,10 @@ char *__d_path(const struct path *path, struct path *root,
|
|||
{
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct vfsmount *vfsmnt = path->mnt;
|
||||
char * end = buffer+buflen;
|
||||
char * retval;
|
||||
char *end = buffer + buflen;
|
||||
char *retval;
|
||||
|
||||
spin_lock(&vfsmount_lock);
|
||||
prepend(&end, &buflen, "\0", 1);
|
||||
if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
|
||||
(prepend(&end, &buflen, " (deleted)", 10) != 0))
|
||||
|
@ -1800,38 +1806,37 @@ char *__d_path(const struct path *path, struct path *root,
|
|||
break;
|
||||
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
|
||||
/* Global root? */
|
||||
spin_lock(&vfsmount_lock);
|
||||
if (vfsmnt->mnt_parent == vfsmnt) {
|
||||
spin_unlock(&vfsmount_lock);
|
||||
goto global_root;
|
||||
}
|
||||
dentry = vfsmnt->mnt_mountpoint;
|
||||
vfsmnt = vfsmnt->mnt_parent;
|
||||
spin_unlock(&vfsmount_lock);
|
||||
continue;
|
||||
}
|
||||
parent = dentry->d_parent;
|
||||
prefetch(parent);
|
||||
if ((prepend(&end, &buflen, dentry->d_name.name,
|
||||
dentry->d_name.len) != 0) ||
|
||||
if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
|
||||
(prepend(&end, &buflen, "/", 1) != 0))
|
||||
goto Elong;
|
||||
retval = end;
|
||||
dentry = parent;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&vfsmount_lock);
|
||||
return retval;
|
||||
|
||||
global_root:
|
||||
retval += 1; /* hit the slash */
|
||||
if (prepend(&retval, &buflen, dentry->d_name.name,
|
||||
dentry->d_name.len) != 0)
|
||||
if (prepend_name(&retval, &buflen, &dentry->d_name) != 0)
|
||||
goto Elong;
|
||||
root->mnt = vfsmnt;
|
||||
root->dentry = dentry;
|
||||
return retval;
|
||||
goto out;
|
||||
|
||||
Elong:
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
retval = ERR_PTR(-ENAMETOOLONG);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1845,9 +1850,9 @@ Elong:
|
|||
*
|
||||
* Returns the buffer or an error code if the path was too long.
|
||||
*
|
||||
* "buflen" should be positive. Caller holds the dcache_lock.
|
||||
* "buflen" should be positive.
|
||||
*/
|
||||
char *d_path(struct path *path, char *buf, int buflen)
|
||||
char *d_path(const struct path *path, char *buf, int buflen)
|
||||
{
|
||||
char *res;
|
||||
struct path root;
|
||||
|
@ -1915,16 +1920,11 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
|
|||
retval = end-1;
|
||||
*retval = '/';
|
||||
|
||||
for (;;) {
|
||||
struct dentry *parent;
|
||||
if (IS_ROOT(dentry))
|
||||
break;
|
||||
while (!IS_ROOT(dentry)) {
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
|
||||
parent = dentry->d_parent;
|
||||
prefetch(parent);
|
||||
|
||||
if ((prepend(&end, &buflen, dentry->d_name.name,
|
||||
dentry->d_name.len) != 0) ||
|
||||
if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
|
||||
(prepend(&end, &buflen, "/", 1) != 0))
|
||||
goto Elong;
|
||||
|
||||
|
@ -1975,7 +1975,7 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
|
|||
error = -ENOENT;
|
||||
/* Has the current directory has been unlinked? */
|
||||
spin_lock(&dcache_lock);
|
||||
if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) {
|
||||
if (IS_ROOT(pwd.dentry) || !d_unhashed(pwd.dentry)) {
|
||||
unsigned long len;
|
||||
struct path tmp = root;
|
||||
char * cwd;
|
||||
|
|
|
@ -561,9 +561,6 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
|
|||
/* insert into file's list */
|
||||
fl->fl_next = *pos;
|
||||
*pos = fl;
|
||||
|
||||
if (fl->fl_ops && fl->fl_ops->fl_insert)
|
||||
fl->fl_ops->fl_insert(fl);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -586,9 +583,6 @@ static void locks_delete_lock(struct file_lock **thisfl_p)
|
|||
fl->fl_fasync = NULL;
|
||||
}
|
||||
|
||||
if (fl->fl_ops && fl->fl_ops->fl_remove)
|
||||
fl->fl_ops->fl_remove(fl);
|
||||
|
||||
if (fl->fl_nspid) {
|
||||
put_pid(fl->fl_nspid);
|
||||
fl->fl_nspid = NULL;
|
||||
|
|
26
fs/namei.c
26
fs/namei.c
|
@ -581,15 +581,13 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd
|
|||
int result;
|
||||
|
||||
/* make sure the stuff we saved doesn't go away */
|
||||
dget(save.dentry);
|
||||
mntget(save.mnt);
|
||||
path_get(&save);
|
||||
|
||||
result = __link_path_walk(name, nd);
|
||||
if (result == -ESTALE) {
|
||||
/* nd->path had been dropped */
|
||||
nd->path = save;
|
||||
dget(nd->path.dentry);
|
||||
mntget(nd->path.mnt);
|
||||
path_get(&nd->path);
|
||||
nd->flags |= LOOKUP_REVAL;
|
||||
result = __link_path_walk(name, nd);
|
||||
}
|
||||
|
@ -1216,8 +1214,9 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
|
|||
nd->flags = flags;
|
||||
nd->depth = 0;
|
||||
|
||||
nd->path.mnt = mntget(mnt);
|
||||
nd->path.dentry = dget(dentry);
|
||||
nd->path.dentry = dentry;
|
||||
nd->path.mnt = mnt;
|
||||
path_get(&nd->path);
|
||||
|
||||
retval = path_walk(name, nd);
|
||||
if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
|
||||
|
@ -2857,16 +2856,17 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
|||
{
|
||||
struct nameidata nd;
|
||||
void *cookie;
|
||||
int res;
|
||||
|
||||
nd.depth = 0;
|
||||
cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
|
||||
if (!IS_ERR(cookie)) {
|
||||
int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
|
||||
if (dentry->d_inode->i_op->put_link)
|
||||
dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
|
||||
cookie = ERR_PTR(res);
|
||||
}
|
||||
return PTR_ERR(cookie);
|
||||
if (IS_ERR(cookie))
|
||||
return PTR_ERR(cookie);
|
||||
|
||||
res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
|
||||
if (dentry->d_inode->i_op->put_link)
|
||||
dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
|
||||
return res;
|
||||
}
|
||||
|
||||
int vfs_follow_link(struct nameidata *nd, const char *link)
|
||||
|
|
10
fs/pipe.c
10
fs/pipe.c
|
@ -1003,8 +1003,7 @@ struct file *create_write_pipe(void)
|
|||
void free_write_pipe(struct file *f)
|
||||
{
|
||||
free_pipe_info(f->f_dentry->d_inode);
|
||||
dput(f->f_path.dentry);
|
||||
mntput(f->f_path.mnt);
|
||||
path_put(&f->f_path);
|
||||
put_filp(f);
|
||||
}
|
||||
|
||||
|
@ -1015,8 +1014,8 @@ struct file *create_read_pipe(struct file *wrf)
|
|||
return ERR_PTR(-ENFILE);
|
||||
|
||||
/* Grab pipe from the writer */
|
||||
f->f_path.mnt = mntget(wrf->f_path.mnt);
|
||||
f->f_path.dentry = dget(wrf->f_path.dentry);
|
||||
f->f_path = wrf->f_path;
|
||||
path_get(&wrf->f_path);
|
||||
f->f_mapping = wrf->f_path.dentry->d_inode->i_mapping;
|
||||
|
||||
f->f_pos = 0;
|
||||
|
@ -1068,8 +1067,7 @@ int do_pipe(int *fd)
|
|||
err_fdr:
|
||||
put_unused_fd(fdr);
|
||||
err_read_pipe:
|
||||
dput(fr->f_dentry);
|
||||
mntput(fr->f_vfsmnt);
|
||||
path_put(&fr->f_path);
|
||||
put_filp(fr);
|
||||
err_write_pipe:
|
||||
free_write_pipe(fw);
|
||||
|
|
59
fs/utimes.c
59
fs/utimes.c
|
@ -40,14 +40,9 @@ asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times)
|
|||
|
||||
#endif
|
||||
|
||||
static bool nsec_special(long nsec)
|
||||
{
|
||||
return nsec == UTIME_OMIT || nsec == UTIME_NOW;
|
||||
}
|
||||
|
||||
static bool nsec_valid(long nsec)
|
||||
{
|
||||
if (nsec_special(nsec))
|
||||
if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
|
||||
return true;
|
||||
|
||||
return nsec >= 0 && nsec <= 999999999;
|
||||
|
@ -102,7 +97,11 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags
|
|||
if (error)
|
||||
goto dput_and_out;
|
||||
|
||||
/* Don't worry, the checks are done in inode_change_ok() */
|
||||
if (times && times[0].tv_nsec == UTIME_NOW &&
|
||||
times[1].tv_nsec == UTIME_NOW)
|
||||
times = NULL;
|
||||
|
||||
/* In most cases, the checks are done in inode_change_ok() */
|
||||
newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
|
||||
if (times) {
|
||||
error = -EPERM;
|
||||
|
@ -124,28 +123,34 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags
|
|||
newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
|
||||
newattrs.ia_valid |= ATTR_MTIME_SET;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If times is NULL or both times are either UTIME_OMIT or
|
||||
* UTIME_NOW, then need to check permissions, because
|
||||
* inode_change_ok() won't do it.
|
||||
*/
|
||||
if (!times || (nsec_special(times[0].tv_nsec) &&
|
||||
nsec_special(times[1].tv_nsec))) {
|
||||
/*
|
||||
* For the UTIME_OMIT/UTIME_NOW and UTIME_NOW/UTIME_OMIT
|
||||
* cases, we need to make an extra check that is not done by
|
||||
* inode_change_ok().
|
||||
*/
|
||||
if (((times[0].tv_nsec == UTIME_NOW &&
|
||||
times[1].tv_nsec == UTIME_OMIT)
|
||||
||
|
||||
(times[0].tv_nsec == UTIME_OMIT &&
|
||||
times[1].tv_nsec == UTIME_NOW))
|
||||
&& !is_owner_or_cap(inode))
|
||||
goto mnt_drop_write_and_out;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If times is NULL (or both times are UTIME_NOW),
|
||||
* then we need to check permissions, because
|
||||
* inode_change_ok() won't do it.
|
||||
*/
|
||||
error = -EACCES;
|
||||
if (IS_IMMUTABLE(inode))
|
||||
goto mnt_drop_write_and_out;
|
||||
|
||||
if (!is_owner_or_cap(inode)) {
|
||||
if (f) {
|
||||
if (!(f->f_mode & FMODE_WRITE))
|
||||
goto mnt_drop_write_and_out;
|
||||
} else {
|
||||
error = vfs_permission(&nd, MAY_WRITE);
|
||||
if (error)
|
||||
goto mnt_drop_write_and_out;
|
||||
}
|
||||
error = permission(inode, MAY_WRITE, NULL);
|
||||
if (error)
|
||||
goto mnt_drop_write_and_out;
|
||||
}
|
||||
}
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
@ -169,14 +174,6 @@ asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __
|
|||
if (utimes) {
|
||||
if (copy_from_user(&tstimes, utimes, sizeof(tstimes)))
|
||||
return -EFAULT;
|
||||
if ((tstimes[0].tv_nsec == UTIME_OMIT ||
|
||||
tstimes[0].tv_nsec == UTIME_NOW) &&
|
||||
tstimes[0].tv_sec != 0)
|
||||
return -EINVAL;
|
||||
if ((tstimes[1].tv_nsec == UTIME_OMIT ||
|
||||
tstimes[1].tv_nsec == UTIME_NOW) &&
|
||||
tstimes[1].tv_sec != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Nothing to do, we must not even check the path. */
|
||||
if (tstimes[0].tv_nsec == UTIME_OMIT &&
|
||||
|
|
|
@ -300,7 +300,7 @@ extern int d_validate(struct dentry *, struct dentry *);
|
|||
extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
|
||||
|
||||
extern char *__d_path(const struct path *path, struct path *root, char *, int);
|
||||
extern char *d_path(struct path *, char *, int);
|
||||
extern char *d_path(const struct path *, char *, int);
|
||||
extern char *dentry_path(struct dentry *, char *, int);
|
||||
|
||||
/* Allocation counts.. */
|
||||
|
|
|
@ -894,8 +894,6 @@ static inline int file_check_writeable(struct file *filp)
|
|||
typedef struct files_struct *fl_owner_t;
|
||||
|
||||
struct file_lock_operations {
|
||||
void (*fl_insert)(struct file_lock *); /* lock insertion callback */
|
||||
void (*fl_remove)(struct file_lock *); /* lock removal callback */
|
||||
void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
|
||||
void (*fl_release_private)(struct file_lock *);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue