Commit d65f5c58 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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] pass struct path * to do_add_mount()
  [PATCH] switch mtd and dm-table to lookup_bdev()
  [patch 3/4] vfs: remove unused nameidata argument of may_create()
  [PATCH] devpts: switch to IDA
  [PATCH 2/2] proc: switch inode number allocation to IDA
  [PATCH 1/2] proc: fix inode number bogorithmetic
  [PATCH] fix bdev leak in block_dev.c do_open()
  [PATCH] fix races and leaks in vfs_quota_on() users
  [PATCH] clean dup2() up a bit
  [PATCH] merge locate_fd() and get_unused_fd()
  [PATCH] ipv4_static_sysctl_init() should be under CONFIG_SYSCTL
  Re: BUG at security/selinux/avc.c:883 (was: Re: linux-next: Tree
parents a8086ad8 8d66bf54
......@@ -316,29 +316,12 @@ static inline int check_space(struct dm_table *t)
*/
static int lookup_device(const char *path, dev_t *dev)
{
int r;
struct nameidata nd;
struct inode *inode;
if ((r = path_lookup(path, LOOKUP_FOLLOW, &nd)))
return r;
inode = nd.path.dentry->d_inode;
if (!inode) {
r = -ENOENT;
goto out;
}
if (!S_ISBLK(inode->i_mode)) {
r = -ENOTBLK;
goto out;
}
*dev = inode->i_rdev;
out:
path_put(&nd.path);
return r;
struct block_device *bdev = lookup_bdev(path);
if (IS_ERR(bdev))
return PTR_ERR(bdev);
*dev = bdev->bd_dev;
bdput(bdev);
return 0;
}
/*
......
......@@ -125,7 +125,7 @@ int get_sb_mtd(struct file_system_type *fs_type, int flags,
int (*fill_super)(struct super_block *, void *, int),
struct vfsmount *mnt)
{
struct nameidata nd;
struct block_device *bdev;
int mtdnr, ret;
if (!dev_name)
......@@ -181,29 +181,20 @@ int get_sb_mtd(struct file_system_type *fs_type, int flags,
/* try the old way - the hack where we allowed users to mount
* /dev/mtdblock$(n) but didn't actually _use_ the blockdev
*/
ret = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
DEBUG(1, "MTDSB: path_lookup() returned %d, inode %p\n",
ret, nd.path.dentry ? nd.path.dentry->d_inode : NULL);
if (ret)
bdev = lookup_bdev(dev_name);
if (IS_ERR(bdev)) {
ret = PTR_ERR(bdev);
DEBUG(1, "MTDSB: lookup_bdev() returned %d\n", ret);
return ret;
ret = -EINVAL;
if (!S_ISBLK(nd.path.dentry->d_inode->i_mode))
goto out;
if (nd.path.mnt->mnt_flags & MNT_NODEV) {
ret = -EACCES;
goto out;
}
DEBUG(1, "MTDSB: lookup_bdev() returned 0\n");
if (imajor(nd.path.dentry->d_inode) != MTD_BLOCK_MAJOR)
ret = -EINVAL;
if (MAJOR(bdev->bd_dev) != MTD_BLOCK_MAJOR)
goto not_an_MTD_device;
mtdnr = iminor(nd.path.dentry->d_inode);
path_put(&nd.path);
mtdnr = MINOR(bdev->bd_dev);
bdput(bdev);
return get_sb_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super,
mnt);
......@@ -213,10 +204,8 @@ not_an_MTD_device:
printk(KERN_NOTICE
"MTD: Attempt to mount non-MTD device \"%s\"\n",
dev_name);
out:
path_put(&nd.path);
bdput(bdev);
return ret;
}
EXPORT_SYMBOL_GPL(get_sb_mtd);
......
......@@ -232,7 +232,7 @@ static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
}
mntget(newmnt);
err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts);
err = do_add_mount(newmnt, &nd->path, MNT_SHRINKABLE, &afs_vfsmounts);
switch (err) {
case 0:
path_put(&nd->path);
......
......@@ -941,8 +941,10 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part)
* hooks: /n/, see "layering violations".
*/
ret = devcgroup_inode_permission(bdev->bd_inode, perm);
if (ret != 0)
if (ret != 0) {
bdput(bdev);
return ret;
}
ret = -ENXIO;
file->f_mapping = bdev->bd_inode->i_mapping;
......@@ -1234,6 +1236,7 @@ fail:
bdev = ERR_PTR(error);
goto out;
}
EXPORT_SYMBOL(lookup_bdev);
/**
* open_bdev_excl - open a block device by name and set it up for use
......
......@@ -226,7 +226,7 @@ static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd,
int err;
mntget(newmnt);
err = do_add_mount(newmnt, nd, nd->path.mnt->mnt_flags, mntlist);
err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags, mntlist);
switch (err) {
case 0:
path_put(&nd->path);
......
......@@ -29,7 +29,7 @@
#define DEVPTS_DEFAULT_MODE 0600
extern int pty_limit; /* Config limit on Unix98 ptys */
static DEFINE_IDR(allocated_ptys);
static DEFINE_IDA(allocated_ptys);
static DEFINE_MUTEX(allocated_ptys_lock);
static struct vfsmount *devpts_mnt;
......@@ -180,24 +180,24 @@ static struct dentry *get_node(int num)
int devpts_new_index(void)
{
int index;
int idr_ret;
int ida_ret;
retry:
if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
if (!ida_pre_get(&allocated_ptys, GFP_KERNEL)) {
return -ENOMEM;
}
mutex_lock(&allocated_ptys_lock);
idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
if (idr_ret < 0) {
ida_ret = ida_get_new(&allocated_ptys, &index);
if (ida_ret < 0) {
mutex_unlock(&allocated_ptys_lock);
if (idr_ret == -EAGAIN)
if (ida_ret == -EAGAIN)
goto retry;
return -EIO;
}
if (index >= pty_limit) {
idr_remove(&allocated_ptys, index);
ida_remove(&allocated_ptys, index);
mutex_unlock(&allocated_ptys_lock);
return -EIO;
}
......@@ -208,7 +208,7 @@ retry:
void devpts_kill_index(int idx)
{
mutex_lock(&allocated_ptys_lock);
idr_remove(&allocated_ptys, idx);
ida_remove(&allocated_ptys, idx);
mutex_unlock(&allocated_ptys_lock);
}
......
......@@ -1793,6 +1793,21 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
return ret;
}
int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
struct path *path)
{
int error = security_quota_on(path->dentry);
if (error)
return error;
/* Quota file not on the same filesystem? */
if (path->mnt->mnt_sb != sb)
error = -EXDEV;
else
error = vfs_quota_on_inode(path->dentry->d_inode, type,
format_id);
return error;
}
/* Actual function called from quotactl() */
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
int remount)
......@@ -1804,19 +1819,10 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
return vfs_quota_on_remount(sb, type);
error = path_lookup(path, LOOKUP_FOLLOW, &nd);
if (error < 0)
return error;
error = security_quota_on(nd.path.dentry);
if (error)
goto out_path;
/* Quota file not on the same filesystem? */
if (nd.path.mnt->mnt_sb != sb)
error = -EXDEV;
else
error = vfs_quota_on_inode(nd.path.dentry->d_inode, type,
format_id);
out_path:
path_put(&nd.path);
if (!error) {
error = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
}
return error;
}
......@@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on_path);
EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_quota_off);
EXPORT_SYMBOL(vfs_quota_sync);
......
......@@ -2810,8 +2810,9 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
journal_unlock_updates(EXT3_SB(sb)->s_journal);
}
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, remount);
return err;
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford
......
......@@ -3352,8 +3352,9 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
}
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, remount);
return err;
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford
......
......@@ -49,73 +49,6 @@ static int get_close_on_exec(unsigned int fd)
return res;
}
/*
* locate_fd finds a free file descriptor in the open_fds fdset,
* expanding the fd arrays if necessary. Must be called with the
* file_lock held for write.
*/
static int locate_fd(unsigned int orig_start, int cloexec)
{
struct files_struct *files = current->files;
unsigned int newfd;
unsigned int start;
int error;
struct fdtable *fdt;
spin_lock(&files->file_lock);
repeat:
fdt = files_fdtable(files);
/*
* Someone might have closed fd's in the range
* orig_start..fdt->next_fd
*/
start = orig_start;
if (start < files->next_fd)
start = files->next_fd;
newfd = start;
if (start < fdt->max_fds)
newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
fdt->max_fds, start);
error = expand_files(files, newfd);
if (error < 0)
goto out;
/*
* If we needed to expand the fs array we
* might have blocked - try again.
*/
if (error)
goto repeat;
if (start <= files->next_fd)
files->next_fd = newfd + 1;
FD_SET(newfd, fdt->open_fds);
if (cloexec)
FD_SET(newfd, fdt->close_on_exec);
else
FD_CLR(newfd, fdt->close_on_exec);
error = newfd;
out:
spin_unlock(&files->file_lock);
return error;
}
static int dupfd(struct file *file, unsigned int start, int cloexec)
{
int fd = locate_fd(start, cloexec);
if (fd >= 0)
fd_install(fd, file);
else
fput(file);
return fd;
}
asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
{
int err = -EBADF;
......@@ -130,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
return -EINVAL;
spin_lock(&files->file_lock);
if (!(file = fcheck(oldfd)))
goto out_unlock;
get_file(file); /* We are now finished with oldfd */
err = expand_files(files, newfd);
file = fcheck(oldfd);
if (unlikely(!file))
goto Ebadf;
if (unlikely(err < 0)) {
if (err == -EMFILE)
err = -EBADF;
goto out_fput;
goto Ebadf;
goto out_unlock;
}
/* To avoid races with open() and dup(), we will mark the fd as
* in-use in the open-file bitmap throughout the entire dup2()
* process. This is quite safe: do_close() uses the fd array
* entry, not the bitmap, to decide what work needs to be
* done. --sct */
/* Doesn't work. open() might be there first. --AV */
/* Yes. It's a race. In user space. Nothing sane to do */
/*
* We need to detect attempts to do dup2() over allocated but still
* not finished descriptor. NB: OpenBSD avoids that at the price of
* extra work in their equivalent of fget() - they insert struct
* file immediately after grabbing descriptor, mark it larval if
* more work (e.g. actual opening) is needed and make sure that
* fget() treats larval files as absent. Potentially interesting,
* but while extra work in fget() is trivial, locking implications
* and amount of surgery on open()-related paths in VFS are not.
* FreeBSD fails with -EBADF in the same situation, NetBSD "solution"
* deadlocks in rather amusing ways, AFAICS. All of that is out of
* scope of POSIX or SUS, since neither considers shared descriptor
* tables and this condition does not arise without those.
*/
err = -EBUSY;
fdt = files_fdtable(files);
tofree = fdt->fd[newfd];
if (!tofree && FD_ISSET(newfd, fdt->open_fds))
goto out_fput;
goto out_unlock;
get_file(file);
rcu_assign_pointer(fdt->fd[newfd], file);
FD_SET(newfd, fdt->open_fds);
if (flags & O_CLOEXEC)
......@@ -165,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
if (tofree)
filp_close(tofree, files);
err = newfd;
out:
return err;
out_unlock:
spin_unlock(&files->file_lock);
goto out;
out_fput:
return newfd;
Ebadf:
err = -EBADF;
out_unlock:
spin_unlock(&files->file_lock);
fput(file);
goto out;
return err;
}
asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
......@@ -194,10 +128,15 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
asmlinkage long sys_dup(unsigned int fildes)
{
int ret = -EBADF;
struct file * file = fget(fildes);
if (file)
ret = dupfd(file, 0, 0);
struct file *file = fget(fildes);
if (file) {
ret = get_unused_fd();
if (ret >= 0)
fd_install(ret, file);
else
fput(file);
}
return ret;
}
......@@ -322,8 +261,11 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
case F_DUPFD_CLOEXEC:
if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
break;
get_file(filp);
err = dupfd(filp, arg, cmd == F_DUPFD_CLOEXEC);
err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0);
if (err >= 0) {
get_file(filp);
fd_install(err, filp);
}
break;
case F_GETFD:
err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;
......
......@@ -6,6 +6,7 @@
* Manage the dynamic fd arrays in the process files_struct.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/time.h>
......@@ -432,3 +433,63 @@ struct files_struct init_files = {
},
.file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock),
};
/*
* allocate a file descriptor, mark it busy.
*/
int alloc_fd(unsigned start, unsigned flags)
{
struct files_struct *files = current->files;
unsigned int fd;
int error;
struct fdtable *fdt;
spin_lock(&files->file_lock);
repeat:
fdt = files_fdtable(files);
fd = start;
if (fd < files->next_fd)
fd = files->next_fd;
if (fd < fdt->max_fds)
fd = find_next_zero_bit(fdt->open_fds->fds_bits,
fdt->max_fds, fd);
error = expand_files(files, fd);
if (error < 0)
goto out;
/*
* If we needed to expand the fs array we
* might have blocked - try again.
*/
if (error)
goto repeat;
if (start <= files->next_fd)
files->next_fd = fd + 1;
FD_SET(fd, fdt->open_fds);
if (flags & O_CLOEXEC)
FD_SET(fd, fdt->close_on_exec);
else
FD_CLR(fd, fdt->close_on_exec);
error = fd;
#if 1
/* Sanity check */
if (rcu_dereference(fdt->fd[fd]) != NULL) {
printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);
rcu_assign_pointer(fdt->fd[fd], NULL);
}
#endif
out:
spin_unlock(&files->file_lock);
return error;
}
int get_unused_fd(void)
{
return alloc_fd(0, 0);
}
EXPORT_SYMBOL(get_unused_fd);
......@@ -274,7 +274,7 @@ int inode_permission(struct inode *inode, int mask)
return retval;
return security_inode_permission(inode,
mask & (MAY_READ|MAY_WRITE|MAY_EXEC));
mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
}
/**
......@@ -1431,8 +1431,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
static inline int may_create(struct inode *dir, struct dentry *child,
struct nameidata *nd)
static inline int may_create(struct inode *dir, struct dentry *child)
{
if (child->d_inode)
return -EEXIST;
......@@ -1504,7 +1503,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
struct nameidata *nd)
{
int error = may_create(dir, dentry, nd);
int error = may_create(dir, dentry);
if (error)
return error;
......@@ -1948,7 +1947,7 @@ EXPORT_SYMBOL_GPL(lookup_create);
int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
int error = may_create(dir, dentry, NULL);
int error = may_create(dir, dentry);
if (error)
return error;
......@@ -2049,7 +2048,7 @@ asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev)
int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
int error = may_create(dir, dentry, NULL);
int error = may_create(dir, dentry);
if (error)
return error;
......@@ -2316,7 +2315,7 @@ asmlinkage long sys_unlink(const char __user *pathname)
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
{
int error = may_create(dir, dentry, NULL);
int error = may_create(dir, dentry);
if (error)
return error;
......@@ -2386,7 +2385,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (!inode)
return -ENOENT;
error = may_create(dir, new_dentry, NULL);
error = may_create(dir, new_dentry);
if (error)
return error;
......@@ -2595,7 +2594,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error;
if (!new_dentry->d_inode)
error = may_create(new_dir, new_dentry, NULL);
error = may_create(new_dir, new_dentry);
else
error = may_delete(new_dir, new_dentry, is_dir);
if (error)
......
......@@ -1667,31 +1667,31 @@ static noinline int do_new_mount(struct nameidata *nd, char *type, int flags,
if (IS_ERR(mnt))
return PTR_ERR(mnt);
return do_add_mount(mnt, nd, mnt_flags, NULL);
return do_add_mount(mnt, &nd->path, mnt_flags, NULL);
}
/*
* add a mount into a namespace's mount tree
* - provide the option of adding the new mount to an expiration list
*/
int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
int do_add_mount(struct vfsmount *newmnt, struct path *path,
int mnt_flags, struct list_head *fslist)
{
int err;
down_write(&namespace_sem);
/* Something was mounted here while we slept */
while (d_mountpoint(nd->path.dentry) &&
follow_down(&nd->path.mnt, &nd->path.dentry))
while (d_mountpoint(path->dentry) &&
follow_down(&path->mnt, &path->dentry))
;
err = -EINVAL;
if (!check_mnt(nd->path.mnt))
if (!check_mnt(path->mnt))
goto unlock;
/* Refuse the same filesystem on the same mount point */
err = -EBUSY;
if (nd->path.mnt->mnt_sb == newmnt->mnt_sb &&
nd->path.m