diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c index 76c2f34e9e62..c427090abc18 100644 --- a/arch/arm/kernel/sys_oabi-compat.c +++ b/arch/arm/kernel/sys_oabi-compat.c @@ -232,6 +232,9 @@ asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) { switch (cmd) { + case F_GETLKP: + case F_SETLKP: + case F_SETLKPW: case F_GETLK64: case F_SETLK64: case F_SETLKW64: diff --git a/fs/compat.c b/fs/compat.c index 2ae9b6da6a14..f11345da9f2d 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -401,12 +401,28 @@ static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *u } #endif +static unsigned int +convert_fcntl_cmd(unsigned int cmd) +{ + switch (cmd) { + case F_GETLK64: + return F_GETLK; + case F_SETLK64: + return F_SETLK; + case F_SETLKW64: + return F_SETLKW; + } + + return cmd; +} + asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) { mm_segment_t old_fs; struct flock f; long ret; + unsigned int conv_cmd; switch (cmd) { case F_GETLK: @@ -443,16 +459,18 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, case F_GETLK64: case F_SETLK64: case F_SETLKW64: + case F_GETLKP: + case F_SETLKP: + case F_SETLKPW: ret = get_compat_flock64(&f, compat_ptr(arg)); if (ret != 0) break; old_fs = get_fs(); set_fs(KERNEL_DS); - ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK : - ((cmd == F_SETLK64) ? F_SETLK : F_SETLKW), - (unsigned long)&f); + conv_cmd = convert_fcntl_cmd(cmd); + ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f); set_fs(old_fs); - if (cmd == F_GETLK64 && ret == 0) { + if ((conv_cmd == F_GETLK || conv_cmd == F_GETLKP) && ret == 0) { /* need to return lock information - see above for commentary */ if (f.l_start > COMPAT_LOFF_T_MAX) ret = -EOVERFLOW; @@ -473,8 +491,15 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) { - if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64)) + switch (cmd) { + case F_GETLK64: + case F_SETLK64: + case F_SETLKW64: + case F_GETLKP: + case F_SETLKP: + case F_SETLKPW: return -EINVAL; + } return compat_sys_fcntl64(fd, cmd, arg); } diff --git a/fs/fcntl.c b/fs/fcntl.c index 1c5bd2427492..ca2c5c321188 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -370,9 +370,19 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, case F_SETFL: err = setfl(fd, filp, arg); break; +#if BITS_PER_LONG != 32 + /* 32-bit arches must use fcntl64() */ + case F_GETLKP: +#endif case F_GETLK: err = fcntl_getlk(filp, cmd, (struct flock __user *) arg); break; +#if BITS_PER_LONG != 32 + /* 32-bit arches must use fcntl64() */ + case F_SETLKP: + case F_SETLKPW: +#endif + /* Fallthrough */ case F_SETLK: case F_SETLKW: err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg); @@ -500,17 +510,20 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd, err = -EBADF; switch (cmd) { - case F_GETLK64: - err = fcntl_getlk64(filp, cmd, (struct flock64 __user *) arg); - break; - case F_SETLK64: - case F_SETLKW64: - err = fcntl_setlk64(fd, filp, cmd, - (struct flock64 __user *) arg); - break; - default: - err = do_fcntl(fd, cmd, arg, filp); - break; + case F_GETLK64: + case F_GETLKP: + err = fcntl_getlk64(filp, cmd, (struct flock64 __user *) arg); + break; + case F_SETLK64: + case F_SETLKW64: + case F_SETLKP: + case F_SETLKPW: + err = fcntl_setlk64(fd, filp, cmd, + (struct flock64 __user *) arg); + break; + default: + err = do_fcntl(fd, cmd, arg, filp); + break; } fput(filp); out: diff --git a/fs/locks.c b/fs/locks.c index ca3d75cec03c..810ba20044e5 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1749,6 +1749,12 @@ int fcntl_getlk(struct file *filp, unsigned int cmd, struct flock __user *l) if (error) goto out; + if (cmd == F_GETLKP) { + cmd = F_GETLK; + file_lock.fl_flags |= FL_FILE_PVT; + file_lock.fl_owner = (fl_owner_t)filp; + } + error = vfs_test_lock(filp, &file_lock); if (error) goto out; @@ -1868,10 +1874,26 @@ again: error = flock_to_posix_lock(filp, file_lock, &flock); if (error) goto out; - if (cmd == F_SETLKW) { + + /* + * If the cmd is requesting file-private locks, then set the + * FL_FILE_PVT flag and override the owner. + */ + switch (cmd) { + case F_SETLKP: + cmd = F_SETLK; + file_lock->fl_flags |= FL_FILE_PVT; + file_lock->fl_owner = (fl_owner_t)filp; + break; + case F_SETLKPW: + cmd = F_SETLKW; + file_lock->fl_flags |= FL_FILE_PVT; + file_lock->fl_owner = (fl_owner_t)filp; + /* Fallthrough */ + case F_SETLKW: file_lock->fl_flags |= FL_SLEEP; } - + error = -EBADF; switch (flock.l_type) { case F_RDLCK: @@ -1934,6 +1956,12 @@ int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l) if (error) goto out; + if (cmd == F_GETLKP) { + cmd = F_GETLK64; + file_lock.fl_flags |= FL_FILE_PVT; + file_lock.fl_owner = (fl_owner_t)filp; + } + error = vfs_test_lock(filp, &file_lock); if (error) goto out; @@ -1986,10 +2014,26 @@ again: error = flock64_to_posix_lock(filp, file_lock, &flock); if (error) goto out; - if (cmd == F_SETLKW64) { + + /* + * If the cmd is requesting file-private locks, then set the + * FL_FILE_PVT flag and override the owner. + */ + switch (cmd) { + case F_SETLKP: + cmd = F_SETLK64; + file_lock->fl_flags |= FL_FILE_PVT; + file_lock->fl_owner = (fl_owner_t)filp; + break; + case F_SETLKPW: + cmd = F_SETLKW64; + file_lock->fl_flags |= FL_FILE_PVT; + file_lock->fl_owner = (fl_owner_t)filp; + /* Fallthrough */ + case F_SETLKW64: file_lock->fl_flags |= FL_SLEEP; } - + error = -EBADF; switch (flock.l_type) { case F_RDLCK: @@ -2074,6 +2118,8 @@ void locks_remove_file(struct file *filp) if (!inode->i_flock) return; + locks_remove_posix(filp, (fl_owner_t)filp); + if (filp->f_op && filp->f_op->flock) { struct file_lock fl = { .fl_pid = current->tgid, diff --git a/include/asm-generic/fcntl.h b/include/asm-generic/fcntl.h index 1add79f11f38..1b8cbeef15c8 100644 --- a/include/asm-generic/fcntl.h +++ b/include/asm-generic/fcntl.h @@ -128,6 +128,22 @@ #define F_GETOWN_EX 16 #endif +/* + * fd "private" POSIX locks. + * + * Usually POSIX locks held by a process are released on *any* close and are + * not inherited across a fork(). + * + * These cmd values will set locks that conflict with normal POSIX locks, but + * are "owned" by the opened file, not the process. This means that they are + * inherited across fork() like BSD (flock) locks, and they are only released + * automatically when the last reference to the the open file against which + * they were acquired is put. + */ +#define F_GETLKP 36 +#define F_SETLKP 37 +#define F_SETLKPW 38 + #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 34b711e36b3f..1de9a312d55c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3323,6 +3323,9 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, case F_GETLK: case F_SETLK: case F_SETLKW: + case F_GETLKP: + case F_SETLKP: + case F_SETLKPW: #if BITS_PER_LONG == 32 case F_GETLK64: case F_SETLK64: