mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 15:14:18 +08:00
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: (57 commits) tidy the trailing symlinks traversal up Turn resolution of trailing symlinks iterative everywhere simplify link_path_walk() tail Make trailing symlink resolution in path_lookupat() iterative update nd->inode in __do_follow_link() instead of after do_follow_link() pull handling of one pathname component into a helper fs: allow AT_EMPTY_PATH in linkat(), limit that to CAP_DAC_READ_SEARCH Allow passing O_PATH descriptors via SCM_RIGHTS datagrams readlinkat(), fchownat() and fstatat() with empty relative pathnames Allow O_PATH for symlinks New kind of open files - "location only". ext4: Copy fs UUID to superblock ext3: Copy fs UUID to superblock. vfs: Export file system uuid via /proc/<pid>/mountinfo unistd.h: Add new syscalls numbers to asm-generic x86: Add new syscalls for x86_64 x86: Add new syscalls for x86_32 fs: Remove i_nlink check from file system link callback fs: Don't allow to create hardlink for deleted file vfs: Add open by file handle support ...
This commit is contained in:
commit
422e6c4bc4
@ -230,44 +230,24 @@ linux_to_osf_statfs(struct kstatfs *linux_stat, struct osf_statfs __user *osf_st
|
||||
return copy_to_user(osf_stat, &tmp_stat, bufsiz) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_osf_statfs(struct path *path, struct osf_statfs __user *buffer,
|
||||
unsigned long bufsiz)
|
||||
SYSCALL_DEFINE3(osf_statfs, const char __user *, pathname,
|
||||
struct osf_statfs __user *, buffer, unsigned long, bufsiz)
|
||||
{
|
||||
struct kstatfs linux_stat;
|
||||
int error = vfs_statfs(path, &linux_stat);
|
||||
int error = user_statfs(pathname, &linux_stat);
|
||||
if (!error)
|
||||
error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(osf_statfs, const char __user *, pathname,
|
||||
struct osf_statfs __user *, buffer, unsigned long, bufsiz)
|
||||
{
|
||||
struct path path;
|
||||
int retval;
|
||||
|
||||
retval = user_path(pathname, &path);
|
||||
if (!retval) {
|
||||
retval = do_osf_statfs(&path, buffer, bufsiz);
|
||||
path_put(&path);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(osf_fstatfs, unsigned long, fd,
|
||||
struct osf_statfs __user *, buffer, unsigned long, bufsiz)
|
||||
{
|
||||
struct file *file;
|
||||
int retval;
|
||||
|
||||
retval = -EBADF;
|
||||
file = fget(fd);
|
||||
if (file) {
|
||||
retval = do_osf_statfs(&file->f_path, buffer, bufsiz);
|
||||
fput(file);
|
||||
}
|
||||
return retval;
|
||||
struct kstatfs linux_stat;
|
||||
int error = fd_statfs(fd, &linux_stat);
|
||||
if (!error)
|
||||
error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -185,26 +185,21 @@ struct hpux_statfs {
|
||||
int16_t f_pad;
|
||||
};
|
||||
|
||||
static int do_statfs_hpux(struct path *path, struct hpux_statfs *buf)
|
||||
static int do_statfs_hpux(struct kstatfs *st, struct hpux_statfs __user *p)
|
||||
{
|
||||
struct kstatfs st;
|
||||
int retval;
|
||||
|
||||
retval = vfs_statfs(path, &st);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
buf->f_type = st.f_type;
|
||||
buf->f_bsize = st.f_bsize;
|
||||
buf->f_blocks = st.f_blocks;
|
||||
buf->f_bfree = st.f_bfree;
|
||||
buf->f_bavail = st.f_bavail;
|
||||
buf->f_files = st.f_files;
|
||||
buf->f_ffree = st.f_ffree;
|
||||
buf->f_fsid[0] = st.f_fsid.val[0];
|
||||
buf->f_fsid[1] = st.f_fsid.val[1];
|
||||
|
||||
struct hpux_statfs buf;
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.f_type = st->f_type;
|
||||
buf.f_bsize = st->f_bsize;
|
||||
buf.f_blocks = st->f_blocks;
|
||||
buf.f_bfree = st->f_bfree;
|
||||
buf.f_bavail = st->f_bavail;
|
||||
buf.f_files = st->f_files;
|
||||
buf.f_ffree = st->f_ffree;
|
||||
buf.f_fsid[0] = st->f_fsid.val[0];
|
||||
buf.f_fsid[1] = st->f_fsid.val[1];
|
||||
if (copy_to_user(p, &buf, sizeof(buf)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -212,35 +207,19 @@ static int do_statfs_hpux(struct path *path, struct hpux_statfs *buf)
|
||||
asmlinkage long hpux_statfs(const char __user *pathname,
|
||||
struct hpux_statfs __user *buf)
|
||||
{
|
||||
struct path path;
|
||||
int error;
|
||||
|
||||
error = user_path(pathname, &path);
|
||||
if (!error) {
|
||||
struct hpux_statfs tmp;
|
||||
error = do_statfs_hpux(&path, &tmp);
|
||||
if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
|
||||
error = -EFAULT;
|
||||
path_put(&path);
|
||||
}
|
||||
struct kstatfs st;
|
||||
int error = user_statfs(pathname, &st);
|
||||
if (!error)
|
||||
error = do_statfs_hpux(&st, buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long hpux_fstatfs(unsigned int fd, struct hpux_statfs __user * buf)
|
||||
{
|
||||
struct file *file;
|
||||
struct hpux_statfs tmp;
|
||||
int error;
|
||||
|
||||
error = -EBADF;
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
goto out;
|
||||
error = do_statfs_hpux(&file->f_path, &tmp);
|
||||
if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
|
||||
error = -EFAULT;
|
||||
fput(file);
|
||||
out:
|
||||
struct kstatfs st;
|
||||
int error = fd_statfs(fd, &st);
|
||||
if (!error)
|
||||
error = do_statfs_hpux(&st, buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ static long do_spu_create(const char __user *pathname, unsigned int flags,
|
||||
if (!IS_ERR(tmp)) {
|
||||
struct nameidata nd;
|
||||
|
||||
ret = path_lookup(tmp, LOOKUP_PARENT, &nd);
|
||||
ret = kern_path_parent(tmp, &nd);
|
||||
if (!ret) {
|
||||
nd.flags |= LOOKUP_OPEN | LOOKUP_CREATE;
|
||||
ret = spufs_create(&nd, flags, mode, neighbor);
|
||||
|
@ -124,35 +124,18 @@ void mconsole_log(struct mc_request *req)
|
||||
#if 0
|
||||
void mconsole_proc(struct mc_request *req)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt;
|
||||
struct file *file;
|
||||
int n, err;
|
||||
int n;
|
||||
char *ptr = req->request.data, *buf;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
ptr += strlen("proc");
|
||||
ptr = skip_spaces(ptr);
|
||||
|
||||
err = vfs_path_lookup(mnt->mnt_root, mnt, ptr, LOOKUP_FOLLOW, &nd);
|
||||
if (err) {
|
||||
mconsole_reply(req, "Failed to look up file", 1, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = may_open(&nd.path, MAY_READ, O_RDONLY);
|
||||
if (result) {
|
||||
mconsole_reply(req, "Failed to open file", 1, 0);
|
||||
path_put(&nd.path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
|
||||
current_cred());
|
||||
err = PTR_ERR(file);
|
||||
file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY);
|
||||
if (IS_ERR(file)) {
|
||||
mconsole_reply(req, "Failed to open file", 1, 0);
|
||||
path_put(&nd.path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -851,4 +851,6 @@ ia32_sys_call_table:
|
||||
.quad sys_fanotify_init
|
||||
.quad sys32_fanotify_mark
|
||||
.quad sys_prlimit64 /* 340 */
|
||||
.quad sys_name_to_handle_at
|
||||
.quad compat_sys_open_by_handle_at
|
||||
ia32_syscall_end:
|
||||
|
@ -346,10 +346,12 @@
|
||||
#define __NR_fanotify_init 338
|
||||
#define __NR_fanotify_mark 339
|
||||
#define __NR_prlimit64 340
|
||||
#define __NR_name_to_handle_at 341
|
||||
#define __NR_open_by_handle_at 342
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#define NR_syscalls 341
|
||||
#define NR_syscalls 343
|
||||
|
||||
#define __ARCH_WANT_IPC_PARSE_VERSION
|
||||
#define __ARCH_WANT_OLD_READDIR
|
||||
|
@ -669,6 +669,10 @@ __SYSCALL(__NR_fanotify_init, sys_fanotify_init)
|
||||
__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark)
|
||||
#define __NR_prlimit64 302
|
||||
__SYSCALL(__NR_prlimit64, sys_prlimit64)
|
||||
#define __NR_name_to_handle_at 303
|
||||
__SYSCALL(__NR_name_to_handle_at, sys_name_to_handle_at)
|
||||
#define __NR_open_by_handle_at 304
|
||||
__SYSCALL(__NR_open_by_handle_at, sys_open_by_handle_at)
|
||||
|
||||
#ifndef __NO_STUBS
|
||||
#define __ARCH_WANT_OLD_READDIR
|
||||
|
@ -340,3 +340,5 @@ ENTRY(sys_call_table)
|
||||
.long sys_fanotify_init
|
||||
.long sys_fanotify_mark
|
||||
.long sys_prlimit64 /* 340 */
|
||||
.long sys_name_to_handle_at
|
||||
.long sys_open_by_handle_at
|
||||
|
@ -47,7 +47,7 @@ config FS_POSIX_ACL
|
||||
def_bool n
|
||||
|
||||
config EXPORTFS
|
||||
tristate
|
||||
bool
|
||||
|
||||
config FILE_LOCKING
|
||||
bool "Enable POSIX file locking API" if EXPERT
|
||||
|
@ -48,6 +48,8 @@ obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
|
||||
obj-$(CONFIG_NFS_COMMON) += nfs_common/
|
||||
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
|
||||
|
||||
obj-$(CONFIG_FHANDLE) += fhandle.o
|
||||
|
||||
obj-y += quota/
|
||||
|
||||
obj-$(CONFIG_PROC_FS) += proc/
|
||||
|
@ -21,9 +21,13 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
|
||||
int len = *max_len;
|
||||
int type;
|
||||
|
||||
if ((len < BTRFS_FID_SIZE_NON_CONNECTABLE) ||
|
||||
(connectable && len < BTRFS_FID_SIZE_CONNECTABLE))
|
||||
if (connectable && (len < BTRFS_FID_SIZE_CONNECTABLE)) {
|
||||
*max_len = BTRFS_FID_SIZE_CONNECTABLE;
|
||||
return 255;
|
||||
} else if (len < BTRFS_FID_SIZE_NON_CONNECTABLE) {
|
||||
*max_len = BTRFS_FID_SIZE_NON_CONNECTABLE;
|
||||
return 255;
|
||||
}
|
||||
|
||||
len = BTRFS_FID_SIZE_NON_CONNECTABLE;
|
||||
type = FILEID_BTRFS_WITHOUT_PARENT;
|
||||
|
@ -4806,9 +4806,6 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
int err;
|
||||
int drop_inode = 0;
|
||||
|
||||
if (inode->i_nlink == 0)
|
||||
return -ENOENT;
|
||||
|
||||
/* do not allow sys_link's with other subvols of the same device */
|
||||
if (root->objectid != BTRFS_I(inode)->root->objectid)
|
||||
return -EPERM;
|
||||
|
81
fs/compat.c
81
fs/compat.c
@ -262,35 +262,19 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *
|
||||
*/
|
||||
asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf)
|
||||
{
|
||||
struct path path;
|
||||
int error;
|
||||
|
||||
error = user_path(pathname, &path);
|
||||
if (!error) {
|
||||
struct kstatfs tmp;
|
||||
error = vfs_statfs(&path, &tmp);
|
||||
if (!error)
|
||||
error = put_compat_statfs(buf, &tmp);
|
||||
path_put(&path);
|
||||
}
|
||||
struct kstatfs tmp;
|
||||
int error = user_statfs(pathname, &tmp);
|
||||
if (!error)
|
||||
error = put_compat_statfs(buf, &tmp);
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf)
|
||||
{
|
||||
struct file * file;
|
||||
struct kstatfs tmp;
|
||||
int error;
|
||||
|
||||
error = -EBADF;
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
goto out;
|
||||
error = vfs_statfs(&file->f_path, &tmp);
|
||||
int error = fd_statfs(fd, &tmp);
|
||||
if (!error)
|
||||
error = put_compat_statfs(buf, &tmp);
|
||||
fput(file);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -329,41 +313,29 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat
|
||||
|
||||
asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf)
|
||||
{
|
||||
struct path path;
|
||||
int error;
|
||||
|
||||
if (sz != sizeof(*buf))
|
||||
return -EINVAL;
|
||||
|
||||
error = user_path(pathname, &path);
|
||||
if (!error) {
|
||||
struct kstatfs tmp;
|
||||
error = vfs_statfs(&path, &tmp);
|
||||
if (!error)
|
||||
error = put_compat_statfs64(buf, &tmp);
|
||||
path_put(&path);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf)
|
||||
{
|
||||
struct file * file;
|
||||
struct kstatfs tmp;
|
||||
int error;
|
||||
|
||||
if (sz != sizeof(*buf))
|
||||
return -EINVAL;
|
||||
|
||||
error = -EBADF;
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
goto out;
|
||||
error = vfs_statfs(&file->f_path, &tmp);
|
||||
error = user_statfs(pathname, &tmp);
|
||||
if (!error)
|
||||
error = put_compat_statfs64(buf, &tmp);
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf)
|
||||
{
|
||||
struct kstatfs tmp;
|
||||
int error;
|
||||
|
||||
if (sz != sizeof(*buf))
|
||||
return -EINVAL;
|
||||
|
||||
error = fd_statfs(fd, &tmp);
|
||||
if (!error)
|
||||
error = put_compat_statfs64(buf, &tmp);
|
||||
fput(file);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -2312,3 +2284,16 @@ asmlinkage long compat_sys_timerfd_gettime(int ufd,
|
||||
}
|
||||
|
||||
#endif /* CONFIG_TIMERFD */
|
||||
|
||||
#ifdef CONFIG_FHANDLE
|
||||
/*
|
||||
* Exactly like fs/open.c:sys_open_by_handle_at(), except that it
|
||||
* doesn't set the O_LARGEFILE flag.
|
||||
*/
|
||||
asmlinkage long
|
||||
compat_sys_open_by_handle_at(int mountdirfd,
|
||||
struct file_handle __user *handle, int flags)
|
||||
{
|
||||
return do_handle_open(mountdirfd, handle, flags);
|
||||
}
|
||||
#endif
|
||||
|
18
fs/exec.c
18
fs/exec.c
@ -115,13 +115,16 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
|
||||
struct file *file;
|
||||
char *tmp = getname(library);
|
||||
int error = PTR_ERR(tmp);
|
||||
static const struct open_flags uselib_flags = {
|
||||
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
|
||||
.acc_mode = MAY_READ | MAY_EXEC | MAY_OPEN,
|
||||
.intent = LOOKUP_OPEN
|
||||
};
|
||||
|
||||
if (IS_ERR(tmp))
|
||||
goto out;
|
||||
|
||||
file = do_filp_open(AT_FDCWD, tmp,
|
||||
O_LARGEFILE | O_RDONLY | __FMODE_EXEC, 0,
|
||||
MAY_READ | MAY_EXEC | MAY_OPEN);
|
||||
file = do_filp_open(AT_FDCWD, tmp, &uselib_flags, LOOKUP_FOLLOW);
|
||||
putname(tmp);
|
||||
error = PTR_ERR(file);
|
||||
if (IS_ERR(file))
|
||||
@ -721,10 +724,13 @@ struct file *open_exec(const char *name)
|
||||
{
|
||||
struct file *file;
|
||||
int err;
|
||||
static const struct open_flags open_exec_flags = {
|
||||
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
|
||||
.acc_mode = MAY_EXEC | MAY_OPEN,
|
||||
.intent = LOOKUP_OPEN
|
||||
};
|
||||
|
||||
file = do_filp_open(AT_FDCWD, name,
|
||||
O_LARGEFILE | O_RDONLY | __FMODE_EXEC, 0,
|
||||
MAY_EXEC | MAY_OPEN);
|
||||
file = do_filp_open(AT_FDCWD, name, &open_exec_flags, LOOKUP_FOLLOW);
|
||||
if (IS_ERR(file))
|
||||
goto out;
|
||||
|
||||
|
@ -320,9 +320,14 @@ static int export_encode_fh(struct dentry *dentry, struct fid *fid,
|
||||
struct inode * inode = dentry->d_inode;
|
||||
int len = *max_len;
|
||||
int type = FILEID_INO32_GEN;
|
||||
|
||||
if (len < 2 || (connectable && len < 4))
|
||||
|
||||
if (connectable && (len < 4)) {
|
||||
*max_len = 4;
|
||||
return 255;
|
||||
} else if (len < 2) {
|
||||
*max_len = 2;
|
||||
return 255;
|
||||
}
|
||||
|
||||
len = 2;
|
||||
fid->i32.ino = inode->i_ino;
|
||||
@ -369,6 +374,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
|
||||
/*
|
||||
* Try to get any dentry for the given file handle from the filesystem.
|
||||
*/
|
||||
if (!nop || !nop->fh_to_dentry)
|
||||
return ERR_PTR(-ESTALE);
|
||||
result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
|
||||
if (!result)
|
||||
result = ERR_PTR(-ESTALE);
|
||||
|
@ -2253,13 +2253,6 @@ static int ext3_link (struct dentry * old_dentry,
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
/*
|
||||
* Return -ENOENT if we've raced with unlink and i_nlink is 0. Doing
|
||||
* otherwise has the potential to corrupt the orphan inode list.
|
||||
*/
|
||||
if (inode->i_nlink == 0)
|
||||
return -ENOENT;
|
||||
|
||||
retry:
|
||||
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||
EXT3_INDEX_EXTRA_TRANS_BLOCKS);
|
||||
|
@ -1936,6 +1936,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
|
||||
sb->s_qcop = &ext3_qctl_operations;
|
||||
sb->dq_op = &ext3_quota_operations;
|
||||
#endif
|
||||
memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
|
||||
INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
|
||||
mutex_init(&sbi->s_orphan_lock);
|
||||
mutex_init(&sbi->s_resize_lock);
|
||||
|
@ -2304,13 +2304,6 @@ static int ext4_link(struct dentry *old_dentry,
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
/*
|
||||
* Return -ENOENT if we've raced with unlink and i_nlink is 0. Doing
|
||||
* otherwise has the potential to corrupt the orphan inode list.
|
||||
*/
|
||||
if (inode->i_nlink == 0)
|
||||
return -ENOENT;
|
||||
|
||||
retry:
|
||||
handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||
EXT4_INDEX_EXTRA_TRANS_BLOCKS);
|
||||
|
@ -3415,6 +3415,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
||||
sb->s_qcop = &ext4_qctl_operations;
|
||||
sb->dq_op = &ext4_quota_operations;
|
||||
#endif
|
||||
memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
|
||||
|
||||
INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
|
||||
mutex_init(&sbi->s_orphan_lock);
|
||||
mutex_init(&sbi->s_resize_lock);
|
||||
|
@ -757,8 +757,10 @@ fat_encode_fh(struct dentry *de, __u32 *fh, int *lenp, int connectable)
|
||||
struct inode *inode = de->d_inode;
|
||||
u32 ipos_h, ipos_m, ipos_l;
|
||||
|
||||
if (len < 5)
|
||||
if (len < 5) {
|
||||
*lenp = 5;
|
||||
return 255; /* no room */
|
||||
}
|
||||
|
||||
ipos_h = MSDOS_I(inode)->i_pos >> 8;
|
||||
ipos_m = (MSDOS_I(inode)->i_pos & 0xf0) << 24;
|
||||
|
37
fs/fcntl.c
37
fs/fcntl.c
@ -131,7 +131,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
|
||||
SYSCALL_DEFINE1(dup, unsigned int, fildes)
|
||||
{
|
||||
int ret = -EBADF;
|
||||
struct file *file = fget(fildes);
|
||||
struct file *file = fget_raw(fildes);
|
||||
|
||||
if (file) {
|
||||
ret = get_unused_fd();
|
||||
@ -426,15 +426,35 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int check_fcntl_cmd(unsigned cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case F_DUPFD:
|
||||
case F_DUPFD_CLOEXEC:
|
||||
case F_GETFD:
|
||||
case F_SETFD:
|
||||
case F_GETFL:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
|
||||
{
|
||||
struct file *filp;
|
||||
long err = -EBADF;
|
||||
|
||||
filp = fget(fd);
|
||||
filp = fget_raw(fd);
|
||||
if (!filp)
|
||||
goto out;
|
||||
|
||||
if (unlikely(filp->f_mode & FMODE_PATH)) {
|
||||
if (!check_fcntl_cmd(cmd)) {
|
||||
fput(filp);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
err = security_file_fcntl(filp, cmd, arg);
|
||||
if (err) {
|
||||
fput(filp);
|
||||
@ -456,10 +476,17 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
|
||||
long err;
|
||||
|
||||
err = -EBADF;
|
||||
filp = fget(fd);
|
||||
filp = fget_raw(fd);
|
||||
if (!filp)
|
||||
goto out;
|
||||
|
||||
if (unlikely(filp->f_mode & FMODE_PATH)) {
|
||||
if (!check_fcntl_cmd(cmd)) {
|
||||
fput(filp);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
err = security_file_fcntl(filp, cmd, arg);
|
||||
if (err) {
|
||||
fput(filp);
|
||||
@ -808,14 +835,14 @@ static int __init fcntl_init(void)
|
||||
* Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
|
||||
* is defined as O_NONBLOCK on some platforms and not on others.
|
||||
*/
|
||||
BUILD_BUG_ON(18 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32(
|
||||
BUILD_BUG_ON(19 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32(
|
||||
O_RDONLY | O_WRONLY | O_RDWR |
|
||||
O_CREAT | O_EXCL | O_NOCTTY |
|
||||
O_TRUNC | O_APPEND | /* O_NONBLOCK | */
|
||||
__O_SYNC | O_DSYNC | FASYNC |
|
||||
O_DIRECT | O_LARGEFILE | O_DIRECTORY |
|
||||
O_NOFOLLOW | O_NOATIME | O_CLOEXEC |
|
||||
__FMODE_EXEC
|
||||
__FMODE_EXEC | O_PATH
|
||||
));
|
||||
|
||||
fasync_cache = kmem_cache_create("fasync_cache",
|
||||
|
265
fs/fhandle.c
Normal file
265
fs/fhandle.c
Normal file
@ -0,0 +1,265 @@
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/exportfs.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
||||
static long do_sys_name_to_handle(struct path *path,
|
||||
struct file_handle __user *ufh,
|
||||
int __user *mnt_id)
|
||||
{
|
||||
long retval;
|
||||
struct file_handle f_handle;
|
||||
int handle_dwords, handle_bytes;
|
||||
struct file_handle *handle = NULL;
|
||||
|
||||
/*
|
||||
* We need t make sure wether the file system
|
||||
* support decoding of the file handle
|
||||
*/
|
||||
if (!path->mnt->mnt_sb->s_export_op ||
|
||||
!path->mnt->mnt_sb->s_export_op->fh_to_dentry)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))
|
||||
return -EFAULT;
|
||||
|
||||
if (f_handle.handle_bytes > MAX_HANDLE_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
|
||||
GFP_KERNEL);
|
||||
if (!handle)
|
||||
return -ENOMEM;
|
||||
|
||||
/* convert handle size to multiple of sizeof(u32) */
|
||||
handle_dwords = f_handle.handle_bytes >> 2;
|
||||
|
||||
/* we ask for a non connected handle */
|
||||
retval = exportfs_encode_fh(path->dentry,
|
||||
(struct fid *)handle->f_handle,
|
||||
&handle_dwords, 0);
|
||||
handle->handle_type = retval;
|
||||
/* convert handle size to bytes */
|
||||
handle_bytes = handle_dwords * sizeof(u32);
|
||||
handle->handle_bytes = handle_bytes;
|
||||
if ((handle->handle_bytes > f_handle.handle_bytes) ||
|
||||
(retval == 255) || (retval == -ENOSPC)) {
|
||||
/* As per old exportfs_encode_fh documentation
|
||||
* we could return ENOSPC to indicate overflow
|
||||
* But file system returned 255 always. So handle
|
||||
* both the values
|
||||
*/
|
||||
/*
|
||||
* set the handle size to zero so we copy only
|
||||
* non variable part of the file_handle
|
||||
*/
|
||||
handle_bytes = 0;
|
||||
retval = -EOVERFLOW;
|
||||
} else
|
||||
retval = 0;
|
||||
/* copy the mount id */
|
||||
if (copy_to_user(mnt_id, &path->mnt->mnt_id, sizeof(*mnt_id)) ||
|
||||
copy_to_user(ufh, handle,
|
||||
sizeof(struct file_handle) + handle_bytes))
|
||||
retval = -EFAULT;
|
||||
kfree(handle);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* sys_name_to_handle_at: convert name to handle
|
||||
* @dfd: directory relative to which name is interpreted if not absolute
|
||||
* @name: name that should be converted to handle.
|
||||
* @handle: resulting file handle
|
||||
* @mnt_id: mount id of the file system containing the file
|
||||
* @flag: flag value to indicate whether to follow symlink or not
|
||||
*
|
||||
* @handle->handle_size indicate the space available to store the
|
||||
* variable part of the file handle in bytes. If there is not
|
||||
* enough space, the field is updated to return the minimum
|
||||
* value required.
|
||||
*/
|
||||
SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
||||
struct file_handle __user *, handle, int __user *, mnt_id,
|
||||
int, flag)
|
||||
{
|
||||
struct path path;
|
||||
int lookup_flags;
|
||||
int err;
|
||||
|
||||
if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
|
||||
if (flag & AT_EMPTY_PATH)
|
||||
lookup_flags |= LOOKUP_EMPTY;
|
||||
err = user_path_at(dfd, name, lookup_flags, &path);
|
||||
if (!err) {
|
||||
err = do_sys_name_to_handle(&path, handle, mnt_id);
|
||||
path_put(&path);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct vfsmount *get_vfsmount_from_fd(int fd)
|
||||
{
|
||||
struct path path;
|
||||
|
||||
if (fd == AT_FDCWD) {
|
||||
struct fs_struct *fs = current->fs;
|
||||
spin_lock(&fs->lock);
|
||||
path = fs->pwd;
|
||||
mntget(path.mnt);
|
||||
spin_unlock(&fs->lock);
|
||||
} else {
|
||||
int fput_needed;
|
||||
struct file *file = fget_light(fd, &fput_needed);
|
||||
if (!file)
|
||||
return ERR_PTR(-EBADF);
|
||||
path = file->f_path;
|
||||
mntget(path.mnt);
|
||||
fput_light(file, fput_needed);
|
||||
}
|
||||
return path.mnt;
|
||||
}
|
||||
|
||||
static int vfs_dentry_acceptable(void *context, struct dentry *dentry)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_handle_to_path(int mountdirfd, struct file_handle *handle,
|
||||
struct path *path)
|
||||
{
|
||||
int retval = 0;
|
||||
int handle_dwords;
|
||||
|
||||
path->mnt = get_vfsmount_from_fd(mountdirfd);
|
||||
if (IS_ERR(path->mnt)) {
|
||||
retval = PTR_ERR(path->mnt);
|
||||
goto out_err;
|
||||
}
|
||||
/* change the handle size to multiple of sizeof(u32) */
|
||||
handle_dwords = handle->handle_bytes >> 2;
|
||||
path->dentry = exportfs_decode_fh(path->mnt,
|
||||
(struct fid *)handle->f_handle,
|
||||
handle_dwords, handle->handle_type,
|
||||
vfs_dentry_acceptable, NULL);
|
||||
if (IS_ERR(path->dentry)) {
|
||||
retval = PTR_ERR(path->dentry);
|
||||
goto out_mnt;
|
||||
}
|
||||
return 0;
|
||||
out_mnt:
|
||||
mntput(path->mnt);
|
||||
out_err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
|
||||
struct path *path)
|
||||
{
|
||||
int retval = 0;
|
||||
struct file_handle f_handle;
|
||||
struct file_handle *handle = NULL;
|
||||
|
||||
/*
|
||||
* With handle we don't look at the execute bit on the
|
||||
* the directory. Ideally we would like CAP_DAC_SEARCH.
|
||||
* But we don't have that
|
||||
*/
|
||||
if (!capable(CAP_DAC_READ_SEARCH)) {
|
||||
retval = -EPERM;
|
||||
goto out_err;
|
||||
}
|
||||
if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
|
||||
retval = -EFAULT;
|
||||
goto out_err;
|
||||
}
|
||||
if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
|
||||
(f_handle.handle_bytes == 0)) {
|
||||
retval = -EINVAL;
|
||||
goto out_err;
|
||||
}
|
||||
handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
|
||||
GFP_KERNEL);
|
||||
if (!handle) {
|
||||
retval = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
/* copy the full handle */
|
||||
if (copy_from_user(handle, ufh,
|
||||
sizeof(struct file_handle) +
|
||||
f_handle.handle_bytes)) {
|
||||
retval = -EFAULT;
|
||||
goto out_handle;
|
||||
}
|
||||
|
||||
retval = do_handle_to_path(mountdirfd, handle, path);
|
||||
|
||||
out_handle:
|
||||
kfree(handle);
|
||||
out_err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
long do_handle_open(int mountdirfd,
|
||||
struct file_handle __user *ufh, int open_flag)
|
||||
{
|
||||
long retval = 0;
|
||||
struct path path;
|
||||
struct file *file;
|
||||
int fd;
|
||||
|
||||
retval = handle_to_path(mountdirfd, ufh, &path);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
fd = get_unused_fd_flags(open_flag);
|
||||
if (fd < 0) {
|
||||
path_put(&path);
|
||||
return fd;
|
||||
}
|
||||
file = file_open_root(path.dentry, path.mnt, "", open_flag);
|
||||
if (IS_ERR(file)) {
|
||||
put_unused_fd(fd);
|
||||
retval = PTR_ERR(file);
|
||||
} else {
|
||||
retval = fd;
|
||||
fsnotify_open(file);
|
||||
fd_install(fd, file);
|
||||
}
|
||||
path_put(&path);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* sys_open_by_handle_at: Open the file handle
|
||||
* @mountdirfd: directory file descriptor
|
||||
* @handle: file handle to be opened
|
||||
* @flag: open flags.
|
||||
*
|
||||
* @mountdirfd indicate the directory file descriptor
|
||||
* of the mount point. file handle is decoded relative
|
||||
* to the vfsmount pointed by the @mountdirfd. @flags
|
||||
* value is same as the open(2) flags.
|
||||
*/
|
||||
SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
|
||||
struct file_handle __user *, handle,
|
||||
int, flags)
|
||||
{
|
||||
long ret;
|
||||
|
||||
if (force_o_largefile())
|
||||
flags |= O_LARGEFILE;
|
||||
|
||||
ret = do_handle_open(mountdirfd, handle, flags);
|
||||
return ret;
|
||||
}
|
@ -276,11 +276,10 @@ struct file *fget(unsigned int fd)
|
||||
rcu_read_lock();
|
||||
file = fcheck_files(files, fd);
|
||||
if (file) {
|
||||
if (!atomic_long_inc_not_zero(&file->f_count)) {
|
||||
/* File object ref couldn't be taken */
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
/* File object ref couldn't be taken */
|
||||
if (file->f_mode & FMODE_PATH ||
|
||||
!atomic_long_inc_not_zero(&file->f_count))
|
||||
file = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
@ -289,6 +288,25 @@ struct file *fget(unsigned int fd)
|
||||
|
||||
EXPORT_SYMBOL(fget);
|
||||
|
||||
struct file *fget_raw(unsigned int fd)
|
||||
{
|
||||
struct file *file;
|
||||
struct files_struct *files = current->files;
|
||||
|
||||
rcu_read_lock();
|
||||
file = fcheck_files(files, fd);
|
||||
if (file) {
|
||||
/* File object ref couldn't be taken */
|
||||
if (!atomic_long_inc_not_zero(&file->f_count))
|
||||
file = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(fget_raw);
|
||||
|
||||
/*
|
||||
* Lightweight file lookup - no refcnt increment if fd table isn't shared.
|
||||
*
|
||||
@ -310,6 +328,33 @@ struct file *fget_light(unsigned int fd, int *fput_needed)
|
||||
struct file *file;
|
||||
struct files_struct *files = current->files;
|
||||
|
||||
*fput_needed = 0;
|
||||
if (atomic_read(&files->count) == 1) {
|
||||
file = fcheck_files(files, fd);
|
||||
if (file && (file->f_mode & FMODE_PATH))
|
||||
file = NULL;
|
||||
} else {
|
||||
rcu_read_lock();
|
||||
file = fcheck_files(files, fd);
|
||||
if (file) {
|
||||
if (!(file->f_mode & FMODE_PATH) &&
|
||||
atomic_long_inc_not_zero(&file->f_count))
|
||||
*fput_needed = 1;
|
||||
else
|
||||
/* Didn't get the reference, someone's freed */
|
||||
file = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
struct file *fget_raw_light(unsigned int fd, int *fput_needed)
|
||||
{
|
||||
struct file *file;
|
||||
struct files_struct *files = current->files;
|
||||
|
||||
*fput_needed = 0;
|
||||
if (atomic_read(&files->count) == 1) {
|
||||
file = fcheck_files(files, fd);
|
||||
|
@ -637,8 +637,10 @@ static int fuse_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
|
||||
u64 nodeid;
|
||||
u32 generation;
|
||||
|
||||
if (*max_len < len)
|
||||
if (*max_len < len) {
|
||||
*max_len = len;
|
||||
return 255;
|
||||
}
|
||||
|
||||
nodeid = get_fuse_inode(inode)->nodeid;
|
||||
generation = inode->i_generation;
|
||||
|
@ -36,9 +36,13 @@ static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len,
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct gfs2_inode *ip = GFS2_I(inode);
|
||||
|
||||
if (*len < GFS2_SMALL_FH_SIZE ||
|
||||
(connectable && *len < GFS2_LARGE_FH_SIZE))
|
||||
if (connectable && (*len < GFS2_LARGE_FH_SIZE)) {
|
||||
*len = GFS2_LARGE_FH_SIZE;
|
||||
return 255;
|
||||
} else if (*len < GFS2_SMALL_FH_SIZE) {
|
||||
*len = GFS2_SMALL_FH_SIZE;
|
||||
return 255;
|
||||
}
|
||||
|
||||
fh[0] = cpu_to_be32(ip->i_no_formal_ino >> 32);
|
||||
fh[1] = cpu_to_be32(ip->i_no_formal_ino & 0xFFFFFFFF);
|
||||
|
@ -106,6 +106,19 @@ extern void put_super(struct super_block *sb);
|
||||
struct nameidata;
|
||||
extern struct file *nameidata_to_filp(struct nameidata *);
|
||||
extern void release_open_intent(struct nameidata *);
|
||||
struct open_flags {
|
||||
int open_flag;
|
||||
int mode;
|
||||
int acc_mode;
|
||||
int intent;
|
||||
};
|
||||
extern struct file *do_filp_open(int dfd, const char *pathname,
|
||||
const struct open_flags *op, int lookup_flags);
|
||||
extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
|
||||
const char *, const struct open_flags *, int lookup_flags);
|
||||
|
||||
extern long do_handle_open(int mountdirfd,
|
||||
struct file_handle __user *ufh, int open_flag);
|
||||
|
||||
/*
|
||||
* inode.c
|
||||
|
@ -124,9 +124,13 @@ isofs_export_encode_fh(struct dentry *dentry,
|
||||
* offset of the inode and the upper 16 bits of fh32[1] to
|
||||
* hold the offset of the parent.
|
||||
*/
|
||||
|
||||
if (len < 3 || (connectable && len < 5))
|
||||
if (connectable && (len < 5)) {
|
||||
*max_len = 5;
|
||||
return 255;
|
||||
} else if (len < 3) {
|
||||
*max_len = 3;
|
||||
return 255;
|
||||
}
|
||||
|
||||
len = 3;
|
||||
fh32[0] = ei->i_iget5_block;
|
||||
|
@ -809,9 +809,6 @@ static int jfs_link(struct dentry *old_dentry,
|
||||
if (ip->i_nlink == JFS_LINK_MAX)
|
||||
return -EMLINK;
|
||||
|
||||
if (ip->i_nlink == 0)
|
||||
return -ENOENT;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
tid = txBegin(ip->i_sb, 0);
|
||||
|
1526
fs/namei.c
1526
fs/namei.c
File diff suppressed because it is too large
Load Diff
@ -1002,6 +1002,18 @@ const struct seq_operations mounts_op = {
|
||||
.show = show_vfsmnt
|
||||
};
|
||||
|
||||
static int uuid_is_nil(u8 *uuid)
|
||||
{
|
||||
int i;
|
||||
u8 *cp = (u8 *)uuid;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (*cp++)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int show_mountinfo(struct seq_file *m, void *v)
|
||||
{
|
||||
struct proc_mounts *p = m->private;
|
||||
@ -1040,6 +1052,10 @@ static int show_mountinfo(struct seq_file *m, void *v)
|
||||
if (IS_MNT_UNBINDABLE(mnt))
|
||||
seq_puts(m, " unbindable");
|
||||
|
||||
if (!uuid_is_nil(mnt->mnt_sb->s_uuid))
|
||||
/* print the uuid */
|
||||
seq_printf(m, " uuid:%pU", mnt->mnt_sb->s_uuid);
|
||||
|
||||
/* Filesystem specific data */
|
||||
seq_puts(m, " - ");
|
||||
show_type(m, sb);
|
||||
|
21
fs/nfsctl.c
21
fs/nfsctl.c
@ -22,30 +22,17 @@
|
||||
|
||||
static struct file *do_open(char *name, int flags)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct vfsmount *mnt;
|
||||
int error;
|
||||
struct file *file;
|
||||
|
||||
mnt = do_kern_mount("nfsd", 0, "nfsd", NULL);
|
||||
if (IS_ERR(mnt))
|
||||
return (struct file *)mnt;
|
||||
|
||||
error = vfs_path_lookup(mnt->mnt_root, mnt, name, 0, &nd);
|
||||
file = file_open_root(mnt->mnt_root, mnt, name, flags);
|
||||
|
||||
mntput(mnt); /* drop do_kern_mount reference */
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
if (flags == O_RDWR)
|
||||
error = may_open(&nd.path, MAY_READ|MAY_WRITE, flags);
|
||||
else
|
||||
error = may_open(&nd.path, MAY_WRITE, flags);
|
||||
|
||||
if (!error)
|
||||
return dentry_open(nd.path.dentry, nd.path.mnt, flags,
|
||||
current_cred());
|
||||
|
||||
path_put(&nd.path);
|
||||
return ERR_PTR(error);
|
||||
return file;
|
||||
}
|
||||
|
||||
static struct {
|
||||
|
@ -197,8 +197,12 @@ static int ocfs2_encode_fh(struct dentry *dentry, u32 *fh_in, int *max_len,
|
||||
dentry->d_name.len, dentry->d_name.name,
|
||||
fh, len, connectable);
|
||||
|
||||
if (len < 3 || (connectable && len < 6)) {
|
||||
mlog(ML_ERROR, "fh buffer is too small for encoding\n");
|
||||
if (connectable && (len < 6)) {
|
||||
*max_len = 6;
|
||||
type = 255;
|
||||
goto bail;
|
||||
} else if (len < 3) {
|
||||
*max_len = 3;
|
||||
type = 255;
|
||||
goto bail;
|
||||
}
|
||||
|
@ -4379,7 +4379,7 @@ static int ocfs2_user_path_parent(const char __user *path,
|
||||
if (IS_ERR(s))
|
||||
return PTR_ERR(s);
|
||||
|
||||
error = path_lookup(s, LOOKUP_PARENT, nd);
|
||||
error = kern_path_parent(s, nd);
|
||||
if (error)
|
||||
putname(s);
|
||||
else
|
||||
|
126
fs/open.c
126
fs/open.c
@ -573,13 +573,15 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,
|
||||
{
|
||||
struct path path;
|
||||
int error = -EINVAL;
|
||||
int follow;
|
||||
int lookup_flags;
|
||||
|
||||
if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
|
||||
goto out;
|
||||
|
||||
follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
|
||||
error = user_path_at(dfd, filename, follow, &path);
|
||||
lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
|
||||
if (flag & AT_EMPTY_PATH)
|
||||
lookup_flags |= LOOKUP_EMPTY;
|
||||
error = user_path_at(dfd, filename, lookup_flags, &path);
|
||||
if (error)
|
||||
goto out;
|
||||
error = mnt_want_write(path.mnt);
|
||||
@ -669,11 +671,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
|
||||
int (*open)(struct inode *, struct file *),
|
||||
const struct cred *cred)
|
||||
{
|
||||
static const struct file_operations empty_fops = {};
|
||||
struct inode *inode;
|
||||
int error;
|
||||
|
||||
f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
|
||||
FMODE_PREAD | FMODE_PWRITE;
|
||||
|
||||
if (unlikely(f->f_flags & O_PATH))
|
||||
f->f_mode = FMODE_PATH;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
if (f->f_mode & FMODE_WRITE) {
|
||||
error = __get_file_write_access(inode, mnt);
|
||||
@ -687,9 +694,15 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
|
||||
f->f_path.dentry = dentry;
|
||||
f->f_path.mnt = mnt;
|
||||
f->f_pos = 0;
|
||||
f->f_op = fops_get(inode->i_fop);
|
||||
file_sb_list_add(f, inode->i_sb);
|
||||
|
||||
if (unlikely(f->f_mode & FMODE_PATH)) {
|
||||
f->f_op = &empty_fops;
|
||||
return f;
|
||||
}
|
||||
|
||||
f->f_op = fops_get(inode->i_fop);
|
||||
|
||||
error = security_dentry_open(f, cred);
|
||||
if (error)
|
||||
goto cleanup_all;
|
||||
@ -890,15 +903,110 @@ void fd_install(unsigned int fd, struct file *file)
|
||||
|
||||
EXPORT_SYMBOL(fd_install);
|
||||
|
||||
static inline int build_open_flags(int flags, int mode, struct open_flags *op)
|
||||
{
|
||||
int lookup_flags = 0;
|
||||
int acc_mode;
|
||||
|
||||
if (!(flags & O_CREAT))
|
||||
mode = 0;
|
||||
op->mode = mode;
|
||||
|
||||
/* Must never be set by userspace */
|
||||
flags &= ~FMODE_NONOTIFY;
|
||||
|
||||
/*
|
||||
* O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only
|
||||
* check for O_DSYNC if the need any syncing at all we enforce it's
|
||||
* always set instead of having to deal with possibly weird behaviour
|
||||
* for malicious applications setting only __O_SYNC.
|
||||
*/
|
||||
if (flags & __O_SYNC)
|
||||
flags |= O_DSYNC;
|
||||
|
||||
/*
|
||||
* If we have O_PATH in the open flag. Then we
|
||||
* cannot have anything other than the below set of flags
|
||||
*/
|
||||
if (flags & O_PATH) {
|
||||
flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
|
||||
acc_mode = 0;
|
||||
} else {
|
||||
acc_mode = MAY_OPEN | ACC_MODE(flags);
|
||||
}
|
||||
|
||||
op->open_flag = flags;
|
||||
|
||||
/* O_TRUNC implies we need access checks for write permissions */
|
||||
if (flags & O_TRUNC)
|
||||
acc_mode |= MAY_WRITE;
|
||||
|
||||
/* Allow the LSM permission hook to distinguish append
|
||||
access from general write access. */
|
||||
if (flags & O_APPEND)
|
||||
acc_mode |= MAY_APPEND;
|
||||
|
||||
op->acc_mode = acc_mode;
|
||||
|
||||
op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
|
||||
|
||||
if (flags & O_CREAT) {
|
||||
op->intent |= LOOKUP_CREATE;
|
||||
if (flags & O_EXCL)
|
||||
op->intent |= LOOKUP_EXCL;
|
||||
}
|
||||
|
||||
if (flags & O_DIRECTORY)
|
||||
lookup_flags |= LOOKUP_DIRECTORY;
|
||||
if (!(flags & O_NOFOLLOW))
|
||||
lookup_flags |= LOOKUP_FOLLOW;
|
||||
return lookup_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* filp_open - open file and return file pointer
|
||||
*
|
||||
* @filename: path to open
|
||||
* @flags: open flags as per the open(2) second argument
|
||||
* @mode: mode for the new file if O_CREAT is set, else ignored
|
||||
*
|
||||
* This is the helper to open a file from kernelspace if you really
|
||||
* have to. But in generally you should not do this, so please move
|
||||
* along, nothing to see here..
|
||||
*/
|
||||
struct file *filp_open(const char *filename, int flags, int mode)
|
||||
{
|
||||
struct open_flags op;
|
||||
int lookup = build_open_flags(flags, mode, &op);
|
||||
return do_filp_open(AT_FDCWD, filename, &op, lookup);
|
||||
}
|
||||
EXPORT_SYMBOL(filp_open);
|
||||
|
||||
struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
|
||||
const char *filename, int flags)
|
||||
{
|
||||
struct open_flags op;
|
||||
int lookup = build_open_flags(flags, 0, &op);
|
||||
if (flags & O_CREAT)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!filename && (flags & O_DIRECTORY))
|
||||
if (!dentry->d_inode->i_op->lookup)
|
||||
return ERR_PTR(-ENOTDIR);
|
||||
return do_file_open_root(dentry, mnt, filename, &op, lookup);
|
||||
}
|
||||
EXPORT_SYMBOL(file_open_root);
|
||||
|
||||
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
|
||||
{
|
||||
struct open_flags op;
|
||||
int lookup = build_open_flags(flags, mode, &op);
|
||||
char *tmp = getname(filename);
|
||||
int fd = PTR_ERR(tmp);
|
||||
|
||||
if (!IS_ERR(tmp)) {
|
||||
fd = get_unused_fd_flags(flags);
|
||||
if (fd >= 0) {
|
||||
struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
|
||||
struct file *f = do_filp_open(dfd, tmp, &op, lookup);
|
||||
if (IS_ERR(f)) {
|
||||
put_unused_fd(fd);
|
||||
fd = PTR_ERR(f);
|
||||
@ -968,8 +1076,10 @@ int filp_close(struct file *filp, fl_owner_t id)
|
||||
if (filp->f_op && filp->f_op->flush)
|
||||
retval = filp->f_op->flush(filp, id);
|
||||
|
||||
dnotify_flush(filp, id);
|
||||
locks_remove_posix(filp, id);
|
||||
if (likely(!(filp->f_mode & FMODE_PATH))) {
|
||||
dnotify_flush(filp, id);
|
||||
locks_remove_posix(filp, id);
|
||||
}
|
||||
fput(filp);
|
||||
return retval;
|
||||
}
|
||||
|
@ -1593,8 +1593,13 @@ int reiserfs_encode_fh(struct dentry *dentry, __u32 * data, int *lenp,
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int maxlen = *lenp;
|
||||
|
||||
if (maxlen < 3)
|
||||
if (need_parent && (maxlen < 5)) {
|
||||
*lenp = 5;
|
||||
return 255;
|
||||
} else if (maxlen < 3) {
|
||||
*lenp = 3;
|
||||
return 255;
|
||||
}
|
||||
|
||||
data[0] = inode->i_ino;
|
||||
data[1] = le32_to_cpu(INODE_PKEY(inode)->k_dir_id);
|
||||
|
@ -1122,10 +1122,6 @@ static int reiserfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
reiserfs_write_unlock(dir->i_sb);
|
||||
return -EMLINK;
|
||||
}
|
||||
if (inode->i_nlink == 0) {
|
||||
reiserfs_write_unlock(dir->i_sb);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* inc before scheduling so reiserfs_unlink knows we are here */
|
||||
inc_nlink(inode);
|
||||
|
@ -75,13 +75,16 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int error = -EINVAL;
|
||||
int lookup_flags = 0;
|
||||
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) != 0)
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
|
||||
AT_EMPTY_PATH)) != 0)
|
||||
goto out;
|
||||
|
||||
if (!(flag & AT_SYMLINK_NOFOLLOW))
|
||||
lookup_flags |= LOOKUP_FOLLOW;
|
||||
if (flag & AT_NO_AUTOMOUNT)
|
||||
lookup_flags |= LOOKUP_NO_AUTOMOUNT;
|
||||
if (flag & AT_EMPTY_PATH)
|
||||
lookup_flags |= LOOKUP_EMPTY;
|
||||
|
||||
error = user_path_at(dfd, filename, lookup_flags, &path);
|
||||
if (error)
|
||||
@ -297,7 +300,7 @@ SYSCALL_DEFINE4(readlinkat, int, dfd, const char __user *, pathname,
|
||||
if (bufsiz <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
error = user_path_at(dfd, pathname, 0, &path);
|
||||
error = user_path_at(dfd, pathname, LOOKUP_EMPTY, &path);
|
||||
if (!error) {
|
||||
struct inode *inode = path.dentry->d_inode;
|
||||
|
||||
|
176
fs/statfs.c
176
fs/statfs.c
@ -73,149 +73,135 @@ int vfs_statfs(struct path *path, struct kstatfs *buf)
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_statfs);
|
||||
|
||||
static int do_statfs_native(struct path *path, struct statfs *buf)
|
||||
int user_statfs(const char __user *pathname, struct kstatfs *st)
|
||||
{
|
||||
struct kstatfs st;
|
||||
int retval;
|
||||
struct path path;
|
||||
int error = user_path(pathname, &path);
|
||||
if (!error) {
|
||||
error = vfs_statfs(&path, st);
|
||||
path_put(&path);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
retval = vfs_statfs(path, &st);
|
||||
if (retval)
|
||||
return retval;
|
||||
int fd_statfs(int fd, struct kstatfs *st)
|
||||
{
|
||||
struct file *file = fget(fd);
|
||||
int error = -EBADF;
|
||||
if (file) {
|
||||
error = vfs_statfs(&file->f_path, st);
|
||||
fput(file);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
if (sizeof(*buf) == sizeof(st))
|
||||
memcpy(buf, &st, sizeof(st));
|
||||
static int do_statfs_native(struct kstatfs *st, struct statfs __user *p)
|
||||
{
|
||||
struct statfs buf;
|
||||
|
||||
if (sizeof(buf) == sizeof(*st))
|
||||
memcpy(&buf, st, sizeof(*st));
|
||||
else {
|
||||
if (sizeof buf->f_blocks == 4) {
|
||||
if ((st.f_blocks | st.f_bfree | st.f_bavail |
|
||||
st.f_bsize | st.f_frsize) &
|
||||
if (sizeof buf.f_blocks == 4) {
|
||||
if ((st->f_blocks | st->f_bfree | st->f_bavail |
|
||||
st->f_bsize | st->f_frsize) &
|
||||
0xffffffff00000000ULL)
|
||||
return -EOVERFLOW;
|
||||
/*
|
||||
* f_files and f_ffree may be -1; it's okay to stuff
|
||||
* that into 32 bits
|
||||
*/
|
||||
if (st.f_files != -1 &&
|
||||
(st.f_files & 0xffffffff00000000ULL))
|
||||
if (st->f_files != -1 &&
|
||||
(st->f_files & 0xffffffff00000000ULL))
|
||||
return -EOVERFLOW;
|
||||
if (st.f_ffree != -1 &&
|
||||
(st.f_ffree & 0xffffffff00000000ULL))
|
||||
if (st->f_ffree != -1 &&
|
||||
(st->f_ffree & 0xffffffff00000000ULL))
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
buf->f_type = st.f_type;
|
||||
buf->f_bsize = st.f_bsize;
|
||||
buf->f_blocks = st.f_blocks;
|
||||
buf->f_bfree = st.f_bfree;
|
||||
buf->f_bavail = st.f_bavail;
|
||||
buf->f_files = st.f_files;
|
||||
buf->f_ffree = st.f_ffree;
|
||||
buf->f_fsid = st.f_fsid;
|
||||
buf->f_namelen = st.f_namelen;
|
||||
buf->f_frsize = st.f_frsize;
|
||||
buf->f_flags = st.f_flags;
|
||||
memset(buf->f_spare, 0, sizeof(buf->f_spare));
|
||||
buf.f_type = st->f_type;
|
||||
buf.f_bsize = st->f_bsize;
|
||||
buf.f_blocks = st->f_blocks;
|
||||
buf.f_bfree = st->f_bfree;
|
||||
buf.f_bavail = st->f_bavail;
|
||||
buf.f_files = st->f_files;
|
||||
buf.f_ffree = st->f_ffree;
|
||||
buf.f_fsid = st->f_fsid;
|
||||
buf.f_namelen = st->f_namelen;
|
||||
buf.f_frsize = st->f_frsize;
|
||||
buf.f_flags = st->f_flags;
|
||||
memset(buf.f_spare, 0, sizeof(buf.f_spare));
|
||||
}
|
||||
if (copy_to_user(p, &buf, sizeof(buf)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_statfs64(struct path *path, struct statfs64 *buf)
|
||||
static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p)
|
||||
{
|
||||
struct kstatfs st;
|
||||
int retval;
|
||||
|
||||
retval = vfs_statfs(path, &st);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (sizeof(*buf) == sizeof(st))
|
||||
memcpy(buf, &st, sizeof(st));
|
||||
struct statfs64 buf;
|
||||
if (sizeof(buf) == sizeof(*st))
|
||||
memcpy(&buf, st, sizeof(*st));
|
||||
else {
|
||||
buf->f_type = st.f_type;
|
||||
buf->f_bsize = st.f_bsize;
|
||||
buf->f_blocks = st.f_blocks;
|
||||
buf->f_bfree = st.f_bfree;
|
||||
buf->f_bavail = st.f_bavail;
|
||||
buf->f_files = st.f_files;
|
||||
buf->f_ffree = st.f_ffree;
|
||||
buf->f_fsid = st.f_fsid;
|
||||
buf->f_namelen = st.f_namelen;
|
||||
buf->f_frsize = st.f_frsize;
|
||||
buf->f_flags = st.f_flags;
|
||||
memset(buf->f_spare, 0, sizeof(buf->f_spare));
|
||||
buf.f_type = st->f_type;
|
||||
buf.f_bsize = st->f_bsize;
|
||||
buf.f_blocks = st->f_blocks;
|
||||
buf.f_bfree = st->f_bfree;
|
||||
buf.f_bavail = st->f_bavail;
|
||||
buf.f_files = st->f_files;
|
||||
buf.f_ffree = st->f_ffree;
|
||||
buf.f_fsid = st->f_fsid;
|
||||
buf.f_namelen = st->f_namelen;
|
||||
buf.f_frsize = st->f_frsize;
|
||||
buf.f_flags = st->f_flags;
|
||||
memset(buf.f_spare, 0, sizeof(buf.f_spare));
|
||||
}
|
||||
if (copy_to_user(p, &buf, sizeof(buf)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf)
|
||||
{
|
||||
struct path path;
|
||||
int error;
|
||||
|
||||
error = user_path(pathname, &path);
|
||||
if (!error) {
|
||||
struct statfs tmp;
|
||||
error = do_statfs_native(&path, &tmp);
|
||||
if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
|
||||
error = -EFAULT;
|
||||
path_put(&path);
|
||||
}
|
||||
struct kstatfs st;
|
||||
int error = user_statfs(pathname, &st);
|
||||
if (!error)
|
||||
error = do_statfs_native(&st, buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf)
|
||||
{
|
||||
struct path path;
|
||||
long error;
|
||||
|
||||
struct kstatfs st;
|
||||
int error;
|
||||
if (sz != sizeof(*buf))
|
||||
return -EINVAL;
|
||||
error = user_path(pathname, &path);
|
||||
if (!error) {
|
||||
struct statfs64 tmp;
|
||||
error = do_statfs64(&path, &tmp);
|
||||
if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
|
||||
error = -EFAULT;
|
||||
path_put(&path);
|
||||
}
|
||||
error = user_statfs(pathname, &st);
|
||||
if (!error)
|
||||
error = do_statfs64(&st, buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf)
|
||||
{
|
||||
struct file *file;
|
||||
struct statfs tmp;
|
||||
int error;
|
||||
|
||||
error = -EBADF;
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
goto out;
|
||||
error = do_statfs_native(&file->f_path, &tmp);
|
||||
if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
|
||||
error = -EFAULT;
|
||||
fput(file);
|
||||
out:
|
||||
struct kstatfs st;
|
||||
int error = fd_statfs(fd, &st);
|
||||
if (!error)
|
||||
error = do_statfs_native(&st, buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf)
|
||||
{
|
||||
struct file *file;
|
||||
struct statfs64 tmp;
|
||||
struct kstatfs st;
|
||||
int error;
|
||||
|
||||
if (sz != sizeof(*buf))
|
||||
return -EINVAL;
|
||||
|
||||
error = -EBADF;
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
goto out;
|
||||
error = do_statfs64(&file->f_path, &tmp);
|
||||
if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
|
||||
error = -EFAULT;
|
||||
fput(file);
|
||||
out:
|
||||
error = fd_statfs(fd, &st);
|
||||
if (!error)
|
||||
error = do_statfs64(&st, buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -522,24 +522,6 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
ubifs_assert(mutex_is_locked(&dir->i_mutex));
|
||||
ubifs_assert(mutex_is_locked(&inode->i_mutex));
|
||||
|
||||
/*
|
||||
* Return -ENOENT if we've raced with unlink and i_nlink is 0. Doing
|
||||
* otherwise has the potential to corrupt the orphan inode list.
|
||||
*
|
||||
* Indeed, consider a scenario when 'vfs_link(dirA/fileA)' and
|
||||
* 'vfs_unlink(dirA/fileA, dirB/fileB)' race. 'vfs_link()' does not
|
||||
* lock 'dirA->i_mutex', so this is possible. Both of the functions
|
||||
* lock 'fileA->i_mutex' though. Suppose 'vfs_unlink()' wins, and takes
|
||||
* 'fileA->i_mutex' mutex first. Suppose 'fileA->i_nlink' is 1. In this
|
||||
* case 'ubifs_unlink()' will drop the last reference, and put 'inodeA'
|
||||
* to the list of orphans. After this, 'vfs_link()' will link
|
||||
* 'dirB/fileB' to 'inodeA'. This is a problem because, for example,
|
||||
* the subsequent 'vfs_unlink(dirB/fileB)' will add the same inode
|
||||
* to the list of orphans.
|
||||
*/
|
||||
if (inode->i_nlink == 0)
|
||||
return -ENOENT;
|
||||
|
||||
err = dbg_check_synced_i_size(inode);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -1286,8 +1286,13 @@ static int udf_encode_fh(struct dentry *de, __u32 *fh, int *lenp,
|
||||
struct fid *fid = (struct fid *)fh;
|
||||
int type = FILEID_UDF_WITHOUT_PARENT;
|
||||
|
||||
if (len < 3 || (connectable && len < 5))
|
||||
if (connectable && (len < 5)) {
|
||||
*lenp = 5;
|
||||
return 255;
|
||||
} else if (len < 3) {
|
||||
*lenp = 3;
|
||||
return 255;
|
||||
}
|
||||
|
||||
*lenp = 3;
|
||||
fid->udf.block = location.logicalBlockNum;
|
||||
|
@ -89,8 +89,10 @@ xfs_fs_encode_fh(
|
||||
* seven combinations work. The real answer is "don't use v2".
|
||||
*/
|
||||
len = xfs_fileid_length(fileid_type);
|
||||
if (*max_len < len)
|
||||
if (*max_len < len) {
|
||||
*max_len = len;
|
||||
return 255;
|
||||
}
|
||||
*max_len = len;
|
||||
|
||||
switch (fileid_type) {
|
||||
|
@ -80,6 +80,10 @@
|
||||
#define O_SYNC (__O_SYNC|O_DSYNC)
|
||||
#endif
|
||||
|
||||
#ifndef O_PATH
|
||||
#define O_PATH 010000000
|
||||
#endif
|
||||
|
||||
#ifndef O_NDELAY
|
||||
#define O_NDELAY O_NONBLOCK
|
||||
#endif
|
||||
|
@ -646,9 +646,13 @@ __SYSCALL(__NR_prlimit64, sys_prlimit64)
|
||||
__SYSCALL(__NR_fanotify_init, sys_fanotify_init)
|
||||
#define __NR_fanotify_mark 263
|
||||
__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark)
|
||||
#define __NR_name_to_handle_at 264
|
||||
__SYSCALL(__NR_name_to_handle_at, sys_name_to_handle_at)
|
||||
#define __NR_open_by_handle_at 265
|
||||
__SYSCALL(__NR_open_by_handle_at, sys_open_by_handle_at)
|
||||
|
||||
#undef __NR_syscalls
|
||||
#define __NR_syscalls 264
|
||||
#define __NR_syscalls 266
|
||||
|
||||
/*
|
||||
* All syscalls below here should go away really,
|
||||
|
@ -8,6 +8,9 @@ struct inode;
|
||||
struct super_block;
|
||||
struct vfsmount;
|
||||
|
||||
/* limit the handle size to NFSv4 handle size now */
|
||||
#define MAX_HANDLE_SZ 128
|
||||
|
||||
/*
|
||||
* The fileid_type identifies how the file within the filesystem is encoded.
|
||||
* In theory this is freely set and parsed by the filesystem, but we try to
|
||||
@ -121,8 +124,10 @@ struct fid {
|
||||
* set, the encode_fh() should store sufficient information so that a good
|
||||
* attempt can be made to find not only the file but also it's place in the
|
||||
* filesystem. This typically means storing a reference to de->d_parent in
|
||||
* the filehandle fragment. encode_fh() should return the number of bytes
|
||||
* stored or a negative error code such as %-ENOSPC
|
||||
* the filehandle fragment. encode_fh() should return the fileid_type on
|
||||
* success and on error returns 255 (if the space needed to encode fh is
|
||||
* greater than @max_len*4 bytes). On error @max_len contains the minimum
|
||||
* size(in 4 byte unit) needed to encode the file handle.
|
||||
*
|
||||
* fh_to_dentry:
|
||||
* @fh_to_dentry is given a &struct super_block (@sb) and a file handle
|
||||
|
@ -46,6 +46,7 @@
|
||||
unlinking file. */
|
||||
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
|
||||
#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal */
|
||||
#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
|
@ -29,6 +29,8 @@ static inline void fput_light(struct file *file, int fput_needed)
|
||||
|
||||
extern struct file *fget(unsigned int fd);
|
||||
extern struct file *fget_light(unsigned int fd, int *fput_needed);
|
||||
extern struct file *fget_raw(unsigned int fd);
|
||||
extern struct file *fget_raw_light(unsigned int fd, int *fput_needed);
|
||||
extern void set_close_on_exec(unsigned int fd, int flag);
|
||||
extern void put_filp(struct file *);
|
||||
extern int alloc_fd(unsigned start, unsigned flags);
|
||||
|
@ -102,6 +102,9 @@ struct inodes_stat_t {
|
||||
/* File is huge (eg. /dev/kmem): treat loff_t as unsigned */
|
||||
#define FMODE_UNSIGNED_OFFSET ((__force fmode_t)0x2000)
|
||||
|
||||
/* File is opened with O_PATH; almost nothing can be done with it */
|
||||
#define FMODE_PATH ((__force fmode_t)0x4000)
|
||||
|
||||
/* File was opened by fanotify and shouldn't generate fanotify events */
|
||||
#define FMODE_NONOTIFY ((__force fmode_t)0x1000000)
|
||||
|
||||
@ -978,6 +981,13 @@ struct file {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct file_handle {
|
||||
__u32 handle_bytes;
|
||||
int handle_type;
|
||||
/* file identifier */
|
||||
unsigned char f_handle[0];
|
||||
};
|
||||
|
||||
#define get_file(x) atomic_long_inc(&(x)->f_count)
|
||||
#define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1)
|
||||
#define file_count(x) atomic_long_read(&(x)->f_count)
|
||||
@ -1401,6 +1411,7 @@ struct super_block {
|
||||
wait_queue_head_t s_wait_unfrozen;
|
||||
|
||||
char s_id[32]; /* Informational name */
|
||||
u8 s_uuid[16]; /* UUID */
|
||||
|
||||
void *s_fs_info; /* Filesystem private info */
|
||||
fmode_t s_mode;
|
||||
@ -1874,6 +1885,8 @@ extern void drop_collected_mounts(struct vfsmount *);
|
||||
extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
|
||||
struct vfsmount *);
|
||||
extern int vfs_statfs(struct path *, struct kstatfs *);
|
||||
extern int user_statfs(const char __user *, struct kstatfs *);
|
||||
extern int fd_statfs(int, struct kstatfs *);
|
||||
extern int statfs_by_dentry(struct dentry *, struct kstatfs *);
|
||||
extern int freeze_super(struct super_block *super);
|
||||
extern int thaw_super(struct super_block *super);
|
||||
@ -1990,6 +2003,8 @@ extern int do_fallocate(struct file *file, int mode, loff_t offset,
|
||||
extern long do_sys_open(int dfd, const char __user *filename, int flags,
|
||||
int mode);
|
||||
extern struct file *filp_open(const char *, int, int);
|
||||
extern struct file *file_open_root(struct dentry *, struct vfsmount *,
|
||||
const char *, int);
|
||||
extern struct file * dentry_open(struct dentry *, struct vfsmount *, int,
|
||||
const struct cred *);
|
||||
extern int filp_close(struct file *, fl_owner_t id);
|
||||
@ -2205,10 +2220,6 @@ extern struct file *create_read_pipe(struct file *f, int flags);
|
||||
extern struct file *create_write_pipe(int flags);
|
||||
extern void free_write_pipe(struct file *);
|
||||
|
||||
extern struct file *do_filp_open(int dfd, const char *pathname,
|
||||
int open_flag, int mode, int acc_mode);
|
||||
extern int may_open(struct path *, int, int);
|
||||
|
||||
extern int kernel_read(struct file *, loff_t, char *, unsigned long);
|
||||
extern struct file * open_exec(const char *);
|
||||
|
||||
|
@ -19,7 +19,6 @@ struct nameidata {
|
||||
struct path path;
|
||||
struct qstr last;
|
||||
struct path root;
|
||||
struct file *file;
|
||||
struct inode *inode; /* path.dentry.d_inode */
|
||||
unsigned int flags;
|
||||
unsigned seq;
|
||||
@ -63,6 +62,10 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
|
||||
#define LOOKUP_EXCL 0x0400
|
||||
#define LOOKUP_RENAME_TARGET 0x0800
|
||||
|
||||
#define LOOKUP_JUMPED 0x1000
|
||||
#define LOOKUP_ROOT 0x2000
|
||||
#define LOOKUP_EMPTY 0x4000
|
||||
|
||||
extern int user_path_at(int, const char __user *, unsigned, struct path *);
|
||||
|
||||
#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
|
||||
@ -72,7 +75,7 @@ extern int user_path_at(int, const char __user *, unsigned, struct path *);
|
||||
|
||||
extern int kern_path(const char *, unsigned, struct path *);
|
||||
|
||||
extern int path_lookup(const char *, unsigned, struct nameidata *);
|
||||
extern int kern_path_parent(const char *, struct nameidata *);
|
||||
extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
|
||||
const char *, unsigned int, struct nameidata *);
|
||||
|
||||
|
@ -62,6 +62,7 @@ struct robust_list_head;
|
||||
struct getcpu_cache;
|
||||
struct old_linux_dirent;
|
||||
struct perf_event_attr;
|
||||
struct file_handle;
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/aio_abi.h>
|
||||
@ -832,5 +833,10 @@ asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long fd, unsigned long pgoff);
|
||||
asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg);
|
||||
|
||||
asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
|
||||
struct file_handle __user *handle,
|
||||
int __user *mnt_id, int flag);
|
||||
asmlinkage long sys_open_by_handle_at(int mountdirfd,
|
||||
struct file_handle __user *handle,
|
||||
int flags);
|
||||
#endif
|
||||
|
12
init/Kconfig
12
init/Kconfig
@ -287,6 +287,18 @@ config BSD_PROCESS_ACCT_V3
|
||||
for processing it. A preliminary version of these tools is available
|
||||
at <http://www.gnu.org/software/acct/>.
|
||||
|
||||
config FHANDLE
|
||||
bool "open by fhandle syscalls"
|
||||
select EXPORTFS
|
||||
help
|
||||
If you say Y here, a user level program will be able to map
|
||||
file names to handle and then later use the handle for
|
||||
different file system operations. This is useful in implementing
|
||||
userspace file servers, which now track files using handles instead
|
||||
of names. The handle would remain the same even if file names
|
||||
get renamed. Enables open_by_handle_at(2) and name_to_handle_at(2)
|
||||
syscalls.
|
||||
|
||||
config TASKSTATS
|
||||
bool "Export task/process statistics through netlink (EXPERIMENTAL)"
|
||||
depends on NET
|
||||
|
@ -144,9 +144,9 @@ int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
|
||||
}
|
||||
|
||||
/* Initialize a parent watch entry. */
|
||||
static struct audit_parent *audit_init_parent(struct nameidata *ndp)
|
||||
static struct audit_parent *audit_init_parent(struct path *path)
|
||||
{
|
||||
struct inode *inode = ndp->path.dentry->d_inode;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct audit_parent *parent;
|
||||
int ret;
|
||||
|
||||
@ -353,53 +353,40 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
|
||||
}
|
||||
|
||||
/* Get path information necessary for adding watches. */
|
||||
static int audit_get_nd(char *path, struct nameidata **ndp, struct nameidata **ndw)
|
||||
static int audit_get_nd(struct audit_watch *watch, struct path *parent)
|
||||
{
|
||||
struct nameidata *ndparent, *ndwatch;
|
||||
struct nameidata nd;
|
||||
struct dentry *d;
|
||||
int err;
|
||||
|
||||
ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL);
|
||||
if (unlikely(!ndparent))
|
||||
return -ENOMEM;
|
||||
|
||||
ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL);
|
||||
if (unlikely(!ndwatch)) {
|
||||
kfree(ndparent);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = path_lookup(path, LOOKUP_PARENT, ndparent);
|
||||
if (err) {
|
||||
kfree(ndparent);
|
||||
kfree(ndwatch);
|
||||
err = kern_path_parent(watch->path, &nd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (nd.last_type != LAST_NORM) {
|
||||
path_put(&nd.path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = path_lookup(path, 0, ndwatch);
|
||||
if (err) {
|
||||
kfree(ndwatch);
|
||||
ndwatch = NULL;
|
||||
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
d = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
|
||||
if (IS_ERR(d)) {
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
path_put(&nd.path);
|
||||
return PTR_ERR(d);
|
||||
}
|
||||
if (d->d_inode) {
|
||||
/* update watch filter fields */
|
||||
watch->dev = d->d_inode->i_sb->s_dev;
|
||||
watch->ino = d->d_inode->i_ino;
|
||||
}
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
|
||||
*ndp = ndparent;
|
||||
*ndw = ndwatch;
|
||||
|
||||
*parent = nd.path;
|
||||
dput(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Release resources used for watch path information. */
|
||||
static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
|
||||
{
|
||||
if (ndp) {
|
||||
path_put(&ndp->path);
|
||||
kfree(ndp);
|
||||
}
|
||||
if (ndw) {
|
||||
path_put(&ndw->path);
|
||||
kfree(ndw);
|
||||
}
|
||||
}
|
||||
|
||||
/* Associate the given rule with an existing parent.
|
||||
* Caller must hold audit_filter_mutex. */
|
||||
static void audit_add_to_parent(struct audit_krule *krule,
|
||||
@ -440,31 +427,24 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
|
||||
{
|
||||
struct audit_watch *watch = krule->watch;
|
||||
struct audit_parent *parent;
|
||||
struct nameidata *ndp = NULL, *ndw = NULL;
|
||||
struct path parent_path;
|
||||
int h, ret = 0;
|
||||
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
|
||||
/* Avoid calling path_lookup under audit_filter_mutex. */
|
||||
ret = audit_get_nd(watch->path, &ndp, &ndw);
|
||||
if (ret) {
|
||||
/* caller expects mutex locked */
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
goto error;
|
||||
}
|
||||
ret = audit_get_nd(watch, &parent_path);
|
||||
|
||||
/* caller expects mutex locked */
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
|
||||
/* update watch filter fields */
|
||||
if (ndw) {
|
||||
watch->dev = ndw->path.dentry->d_inode->i_sb->s_dev;
|
||||
watch->ino = ndw->path.dentry->d_inode->i_ino;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* either find an old parent or attach a new one */
|
||||
parent = audit_find_parent(ndp->path.dentry->d_inode);
|
||||
parent = audit_find_parent(parent_path.dentry->d_inode);
|
||||
if (!parent) {
|
||||
parent = audit_init_parent(ndp);
|
||||
parent = audit_init_parent(&parent_path);
|
||||
if (IS_ERR(parent)) {
|
||||
ret = PTR_ERR(parent);
|
||||
goto error;
|
||||
@ -479,9 +459,8 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
|
||||
h = audit_hash_ino((u32)watch->ino);
|
||||
*list = &audit_inode_hash[h];
|
||||
error:
|
||||
audit_put_nd(ndp, ndw); /* NULL args OK */
|
||||
path_put(&parent_path);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void audit_remove_watch_rule(struct audit_krule *krule)
|
||||
|
@ -186,3 +186,8 @@ cond_syscall(sys_perf_event_open);
|
||||
/* fanotify! */
|
||||
cond_syscall(sys_fanotify_init);
|
||||
cond_syscall(sys_fanotify_mark);
|
||||
|
||||
/* open by handle */
|
||||
cond_syscall(sys_name_to_handle_at);
|
||||
cond_syscall(sys_open_by_handle_at);
|
||||
cond_syscall(compat_sys_open_by_handle_at);
|
||||
|
@ -1321,13 +1321,11 @@ static ssize_t binary_sysctl(const int *name, int nlen,
|
||||
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
|
||||
{
|
||||
const struct bin_table *table = NULL;
|
||||
struct nameidata nd;
|
||||
struct vfsmount *mnt;
|
||||
struct file *file;
|
||||
ssize_t result;
|
||||
char *pathname;
|
||||
int flags;
|
||||
int acc_mode;
|
||||
|
||||
pathname = sysctl_getname(name, nlen, &table);
|
||||
result = PTR_ERR(pathname);
|
||||
@ -1337,28 +1335,17 @@ static ssize_t binary_sysctl(const int *name, int nlen,
|
||||
/* How should the sysctl be accessed? */
|
||||
if (oldval && oldlen && newval && newlen) {
|
||||
flags = O_RDWR;
|
||||
acc_mode = MAY_READ | MAY_WRITE;
|
||||
} else if (newval && newlen) {
|
||||
flags = O_WRONLY;
|
||||
acc_mode = MAY_WRITE;
|
||||
} else if (oldval && oldlen) {
|
||||
flags = O_RDONLY;
|
||||
acc_mode = MAY_READ;
|
||||
} else {
|
||||
result = 0;
|
||||
goto out_putname;
|
||||
}
|
||||
|
||||
mnt = current->nsproxy->pid_ns->proc_mnt;
|
||||
result = vfs_path_lookup(mnt->mnt_root, mnt, pathname, 0, &nd);
|
||||
if (result)
|
||||
goto out_putname;
|
||||
|
||||
result = may_open(&nd.path, acc_mode, flags);
|
||||
if (result)
|
||||
goto out_putpath;
|
||||
|
||||
file = dentry_open(nd.path.dentry, nd.path.mnt, flags, current_cred());
|
||||
file = file_open_root(mnt->mnt_root, mnt, pathname, flags);
|
||||
result = PTR_ERR(file);
|
||||
if (IS_ERR(file))
|
||||
goto out_putname;
|
||||
@ -1370,10 +1357,6 @@ out_putname:
|
||||
putname(pathname);
|
||||
out:
|
||||
return result;
|
||||
|
||||
out_putpath:
|
||||
path_put(&nd.path);
|
||||
goto out_putname;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2144,8 +2144,10 @@ static int shmem_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
if (*len < 3)
|
||||
if (*len < 3) {
|
||||
*len = 3;
|
||||
return 255;
|
||||
}
|
||||
|
||||
if (inode_unhashed(inode)) {
|
||||
/* Unfortunately insert_inode_hash is not idempotent,
|
||||
|
@ -95,7 +95,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
|
||||
int fd = fdp[i];
|
||||
struct file *file;
|
||||
|
||||
if (fd < 0 || !(file = fget(fd)))
|
||||
if (fd < 0 || !(file = fget_raw(fd)))
|
||||
return -EBADF;
|
||||
*fpp++ = file;
|
||||
fpl->count++;
|
||||
|
@ -850,7 +850,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
|
||||
* Get the parent directory, calculate the hash for last
|
||||
* component.
|
||||
*/
|
||||
err = path_lookup(sunaddr->sun_path, LOOKUP_PARENT, &nd);
|
||||
err = kern_path_parent(sunaddr->sun_path, &nd);
|
||||
if (err)
|
||||
goto out_mknod_parent;
|
||||
|
||||
|
@ -104,7 +104,7 @@ struct sock *unix_get_socket(struct file *filp)
|
||||
/*
|
||||
* Socket ?
|
||||
*/
|
||||
if (S_ISSOCK(inode->i_mode)) {
|
||||
if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
|
||||
struct socket *sock = SOCKET_I(inode);
|
||||
struct sock *s = sock->sk;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user