Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 234284
b: refs/heads/master
c: 1abf0c7
h: refs/heads/master
v: v3
  • Loading branch information
Al Viro committed Mar 15, 2011
1 parent 16cc277 commit 54eb2ff
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 18 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f2fa2ffc2046fdc35f96366d1ec8675f4d578522
refs/heads/master: 1abf0c718f15a56a0a435588d1b104c7a37dc9bd
37 changes: 32 additions & 5 deletions trunk/fs/fcntl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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",
Expand Down
53 changes: 48 additions & 5 deletions trunk/fs/file_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -289,6 +288,23 @@ 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;
}

/*
* Lightweight file lookup - no refcnt increment if fd table isn't shared.
*
Expand All @@ -310,6 +326,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);
Expand Down
2 changes: 1 addition & 1 deletion trunk/fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -1544,7 +1544,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
} else {
struct dentry *dentry;

file = fget_light(dfd, &fput_needed);
file = fget_raw_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
Expand Down
35 changes: 29 additions & 6 deletions trunk/fs/open.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,11 +669,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);
Expand All @@ -687,9 +692,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;
Expand Down Expand Up @@ -911,9 +922,18 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op)
if (flags & __O_SYNC)
flags |= O_DSYNC;

op->open_flag = flags;
/*
* 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);
}

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)
Expand All @@ -926,7 +946,8 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op)

op->acc_mode = acc_mode;

op->intent = LOOKUP_OPEN;
op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;

if (flags & O_CREAT) {
op->intent |= LOOKUP_CREATE;
if (flags & O_EXCL)
Expand Down Expand Up @@ -1053,8 +1074,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;
}
Expand Down
4 changes: 4 additions & 0 deletions trunk/include/asm-generic/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions trunk/include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit 54eb2ff

Please sign in to comment.