* [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS
@ 2013-07-01 16:49 Pavel Shilovsky
2013-07-01 16:49 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky
` (6 more replies)
0 siblings, 7 replies; 17+ messages in thread
From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel
Main changes from v6:
1) Fix LOCK_DELETE mandatory lock detection for O_DENYDELETE opens.
2) Add ESHAREDENIED error code definition for alpha, mips, parisc and sparc arches.
3) Remove code changes in do_last function if sharelocks are disabled.
Main changes from v5:
1) O_DENYDELETE is now supported by VFS.
2) ESHAREDENIED erro code is introduced to be returned on sharelock conflicts.
3) forcemand dependancy is removed. CIFS module detects if it should use NTCreateAndX command according to existence of O_DENY flags.
4) Term 'deny lock' is replaced by 'sharelock' (function names, code comments, patch descriptions).
Main changes from v4:
1) deny_lock_file uses FS_DOES_SHARELOCK flag from fs_flags to determine whether to use VFS locks or not.
2) Make nfs code return -EBUSY for share conflicts (was -EACCESS).
Main changes from v3
1) O_DENYMAND is removed, sharelock mount option is introduced.
2) Patch fcntl.h and VFS patches are united into one.
3) flock/LOCK_MAND is disabled for sharelock mounts.
This patchset adds support of O_DENY* flags for Linux fs layer. These flags can be used by any application that needs share reservations to organize a file access. VFS already has some sort of this capability - now it's done through flock/LOCK_MAND mechanism, but that approach is non-atomic. This patchset builds new capabilities on top of the existing one but doesn't bring any changes into the flock call semantic.
These flags can be used by NFS (built-in-kernel) and CIFS (Samba) servers and Wine applications through VFS (for local filesystems) or CIFS/NFS modules. This will help when e.g. Samba and NFS server share the same directory for Windows and Linux users or Wine applications use Samba/NFS share to access the same data from different clients.
According to the previous discussions the most problematic question is how to prevent situations like DoS attacks where e.g /lib/liba.so file can be open with DENYREAD, or smth like this. That's why extra mount option 'sharelock' is added for switching on/off O_DENY* flags processing. It allows us to avoid use of these flags on critical places (like /, /lib) and turn them on if we really want it proccessed that way.
The patchset introduces 3 new flags:
O_DENYREAD - to prevent other opens with read access,
O_DENYWRITE - to prevent other opens with write access,
O_DENYDELETE - to prevent delete operations (this flag can't be implemented for NFS due to the protocol restrictions).
The patchset avoids data races problem on newely created files: open with O_CREAT can return new -ESHAREDENIED error code for a successfully created file if this files was locked with a sharelock by another task. Also, it turns off flock/LOCK_MAND capability for mounts with 'sharelock' option. This lets us not mix one share reservation approach with another.
The #1 and #2 patches add flags to fcntl and implements VFS part. The patches #3 and #4 are related to CIFS-specific changes, #5 and #6 describe NFS and NFSD parts, #7 turns off flock/LOCK_MAND for mounts with 'sharelock' option.
The preliminary patch for Samba that replaces the existing use of flock/LOCK_MAND mechanism with O_DENY* flags:
http://git.etersoft.ru/people/piastry/packages/?p=samba.git;a=commitdiff;h=173070262b7f29134ad6b2e49a760017c11aec4a
The future part of open man page patch:
-----
O_DENYREAD, O_DENYWRIYE, O_DENYDELETE - used to inforce a mandatory share reservation scheme of the file access. If these flag is passed, the open fails with -ESHAREDENIED in following cases:
1) if O_DENYREAD flag is specified and there is another open with READ access to the file;
2) if O_DENYWRITE flag is specified and there is another open with WRITE access to the file;
3) if READ access is requested and there is another open with O_DENYREAD flags;
4) if WRITE access is requested and there is another open with O_DENYWRITE flags.
If O_DENYDELETE flag is specified and the open succeded, any further unlink operation will fail with -ESHAREDENIED untill this open is closed. Now this flag is processed by VFS and CIFS filesystem.
This mechanism is done through flocks. If O_DENY* flags are specified, flock syscall is processed after the open. The share modes are translated into flock according the following rules:
1) !O_DENYREAD -> LOCK_READ | LOCK_MAND
2) !O_DENYWRITE -> LOCK_WRITE | LOCK_MAND
3) !O_DENYDELETE -> LOCK_DELETE | LOCK_MAND
---
Pavel Shilovsky (7):
VFS: Introduce new O_DENY* open flags
VFS: Add O_DENYDELETE support for VFS
CIFS: Add share_access parm to open request
CIFS: Add O_DENY* open flags support
NFSv4: Add O_DENY* open flags support
NFSD: Pass share reservations flags to VFS
locks: Disable LOCK_MAND support for MS_SHARELOCK mounts
arch/alpha/include/uapi/asm/errno.h | 2 +
arch/mips/include/uapi/asm/errno.h | 2 +
arch/parisc/include/uapi/asm/errno.h | 2 +
arch/sparc/include/uapi/asm/errno.h | 2 +
fs/cifs/cifsacl.c | 8 +-
fs/cifs/cifsfs.c | 2 +-
fs/cifs/cifsglob.h | 18 +++-
fs/cifs/cifsproto.h | 8 +-
fs/cifs/cifssmb.c | 50 ++++++-----
fs/cifs/dir.c | 16 ++--
fs/cifs/file.c | 25 ++++--
fs/cifs/inode.c | 21 ++---
fs/cifs/link.c | 25 ++----
fs/cifs/netmisc.c | 2 +-
fs/cifs/readdir.c | 5 +-
fs/cifs/smb1ops.c | 16 ++--
fs/cifs/smb2file.c | 10 +--
fs/cifs/smb2inode.c | 4 +-
fs/cifs/smb2maperror.c | 2 +-
fs/cifs/smb2ops.c | 10 ++-
fs/cifs/smb2pdu.c | 6 +-
fs/cifs/smb2proto.h | 14 +--
fs/fcntl.c | 5 +-
fs/locks.c | 160 ++++++++++++++++++++++++++++++++---
fs/namei.c | 56 +++++++++++-
fs/nfs/dir.c | 4 +
fs/nfs/internal.h | 3 +-
fs/nfs/nfs4file.c | 4 +
fs/nfs/nfs4proc.c | 4 +-
fs/nfs/nfs4super.c | 9 +-
fs/nfs/nfs4xdr.c | 21 ++++-
fs/nfs/super.c | 6 +-
fs/nfsd/nfs4state.c | 46 +++++++++-
fs/nfsd/nfsproc.c | 1 +
fs/proc_namespace.c | 1 +
include/linux/fs.h | 14 +++
include/uapi/asm-generic/errno.h | 2 +
include/uapi/asm-generic/fcntl.h | 12 +++
include/uapi/linux/fs.h | 1 +
39 files changed, 464 insertions(+), 135 deletions(-)
--
1.8.1.5
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky @ 2013-07-01 16:49 ` Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 2/7] VFS: Add O_DENYDELETE support for VFS Pavel Shilovsky ` (5 subsequent siblings) 6 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel This patch adds 3 flags: 1) O_DENYREAD that doesn't permit read access, 2) O_DENYWRITE that doesn't permit write access, 3) O_DENYDELETE that doesn't permit delete or rename. Network filesystems CIFS, SMB2.0, SMB3.0 and NFSv4 have such flags - this change can benefit cifs and nfs modules as well as Samba and NFS file servers that export the same directory for Windows clients, or Wine applications that access the same files simultaneously. These flags are only take affect for opens on mounts with new sharelock option. They are translated to flock's flags: !O_DENYREAD -> LOCK_READ | LOCK_MAND !O_DENYWRITE -> LOCK_WRITE | LOCK_MAND and set through flock_lock_file on a file. If the file can't be locked due conflicts with another open with O_DENY* flags, a new -ESHAREDENIED error code is returned. Create codepath is slightly changed to prevent data races on newly created files: when open with O_CREAT can return -ESHAREDENIED error for successfully created files due to a sharelock set by another task. Temporary disable O_DENYDELETE support - will enable it in further patches. Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- arch/alpha/include/uapi/asm/errno.h | 2 + arch/mips/include/uapi/asm/errno.h | 2 + arch/parisc/include/uapi/asm/errno.h | 2 + arch/sparc/include/uapi/asm/errno.h | 2 + fs/fcntl.c | 5 +- fs/locks.c | 97 +++++++++++++++++++++++++++++++++--- fs/namei.c | 53 +++++++++++++++++++- fs/proc_namespace.c | 1 + include/linux/fs.h | 7 +++ include/uapi/asm-generic/errno.h | 2 + include/uapi/asm-generic/fcntl.h | 11 ++++ include/uapi/linux/fs.h | 1 + 12 files changed, 175 insertions(+), 10 deletions(-) diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h index e5f29ca..f51b81b 100644 --- a/arch/alpha/include/uapi/asm/errno.h +++ b/arch/alpha/include/uapi/asm/errno.h @@ -124,4 +124,6 @@ #define EHWPOISON 139 /* Memory page has hardware error */ +#define ESHAREDENIED 140 /* File is locked with a sharelock */ + #endif diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h index 31575e2f..dda1c73 100644 --- a/arch/mips/include/uapi/asm/errno.h +++ b/arch/mips/include/uapi/asm/errno.h @@ -123,6 +123,8 @@ #define EHWPOISON 168 /* Memory page has hardware error */ +#define ESHAREDENIED 169 /* File is locked with a sharelock */ + #define EDQUOT 1133 /* Quota exceeded */ diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h index 135ad60..bda01d5 100644 --- a/arch/parisc/include/uapi/asm/errno.h +++ b/arch/parisc/include/uapi/asm/errno.h @@ -124,4 +124,6 @@ #define EHWPOISON 257 /* Memory page has hardware error */ +#define ESHAREDENIED 258 /* File is locked with a sharelock */ + #endif diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h index c351aba..836eded 100644 --- a/arch/sparc/include/uapi/asm/errno.h +++ b/arch/sparc/include/uapi/asm/errno.h @@ -114,4 +114,6 @@ #define EHWPOISON 135 /* Memory page has hardware error */ +#define ESHAREDENIED 136 /* File is locked with a sharelock */ + #endif diff --git a/fs/fcntl.c b/fs/fcntl.c index 6599222..a1eee47 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -730,14 +730,15 @@ 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(19 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( + BUILD_BUG_ON(22 - 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 | O_PATH + __FMODE_EXEC | O_PATH | O_DENYREAD | + O_DENYWRITE | O_DENYDELETE )); fasync_cache = kmem_cache_create("fasync_cache", diff --git a/fs/locks.c b/fs/locks.c index cb424a4..dbc5557 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -605,20 +605,73 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s return (locks_conflict(caller_fl, sys_fl)); } -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific - * checking before calling the locks_conflict(). +static unsigned int +deny_flags_to_cmd(unsigned int flags) +{ + unsigned int cmd = LOCK_MAND; + + if (!(flags & O_DENYREAD)) + cmd |= LOCK_READ; + if (!(flags & O_DENYWRITE)) + cmd |= LOCK_WRITE; + + return cmd; +} + +/* + * locks_mand_conflict - Determine if there's a share reservation conflict + * @caller_fl: lock we're attempting to acquire + * @sys_fl: lock already present on system that we're checking against + * + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE + * tell us whether the reservation allows other readers and writers. + */ +static int +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + unsigned char caller_type = caller_fl->fl_type; + unsigned char sys_type = sys_fl->fl_type; + fmode_t caller_fmode = caller_fl->fl_file->f_mode; + fmode_t sys_fmode = sys_fl->fl_file->f_mode; + + /* they can only conflict if FS is mounted with MS_SHARELOCK */ + if (!IS_SHARELOCK(caller_fl->fl_file->f_path.dentry->d_inode)) + return 0; + + /* they can only conflict if they're both LOCK_MAND */ + if (!(caller_type & LOCK_MAND) || !(sys_type & LOCK_MAND)) + return 0; + + if (!(caller_type & LOCK_READ) && (sys_fmode & FMODE_READ)) + return 1; + if (!(caller_type & LOCK_WRITE) && (sys_fmode & FMODE_WRITE)) + return 1; + if (!(sys_type & LOCK_READ) && (caller_fmode & FMODE_READ)) + return 1; + if (!(sys_type & LOCK_WRITE) && (caller_fmode & FMODE_WRITE)) + return 1; + + return 0; +} + +/* + * Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking + * before calling the locks_conflict(). */ static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - /* FLOCK locks referring to the same filp do not conflict with + if (!IS_FLOCK(sys_fl)) + return 0; + if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) + return locks_mand_conflict(caller_fl, sys_fl); + /* + * FLOCK locks referring to the same filp do not conflict with * each other. */ - if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) - return (0); - if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) + if (caller_fl->fl_file == sys_fl->fl_file) return 0; - return (locks_conflict(caller_fl, sys_fl)); + return locks_conflict(caller_fl, sys_fl); } void @@ -783,6 +836,36 @@ out: return error; } +/* + * Determine if a file is allowed to be opened with specified access and share + * modes. Lock the file and return 0 if checks passed, otherwise return + * -ESHAREDENIED. + */ +int +sharelock_lock_file(struct file *filp) +{ + struct file_lock *lock; + int error = 0; + + if (!IS_SHARELOCK(filp->f_path.dentry->d_inode)) + return error; + + /* Disable O_DENYDELETE support for now */ + if (filp->f_flags & O_DENYDELETE) + return -EINVAL; + + error = flock_make_lock(filp, &lock, deny_flags_to_cmd(filp->f_flags)); + if (error) + return error; + + error = flock_lock_file(filp, lock); + if (error == -EAGAIN) + error = -ESHAREDENIED; + + locks_free_lock(lock); + return error; +} + static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) { struct file_lock *fl; diff --git a/fs/namei.c b/fs/namei.c index 85e40d1..7f6edb9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2548,9 +2548,14 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, * here. */ error = may_open(&file->f_path, acc_mode, open_flag); - if (error) + if (error) { fput(file); + goto out; + } + error = sharelock_lock_file(file); + if (error) + fput(file); out: dput(dentry); return error; @@ -2760,6 +2765,40 @@ retry_lookup: } mutex_lock(&dir->d_inode->i_mutex); error = lookup_open(nd, path, file, op, got_write, opened); + + /* + * For sharelock mounts if a file was created but not opened, we need + * to keep parent i_mutex until we finish the open to prevent races when + * somebody opens newly created by us file and locks it with a sharelock + * before we open it. + */ + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { + /* Don't check for write permission, don't truncate */ + open_flag &= ~O_TRUNC; + will_truncate = false; + acc_mode = MAY_OPEN; + path_to_nameidata(path, nd); + + error = may_open(&nd->path, acc_mode, open_flag); + if (error) { + mutex_unlock(&dir->d_inode->i_mutex); + goto out; + } + file->f_path.mnt = nd->path.mnt; + error = finish_open(file, nd->path.dentry, NULL, opened); + if (error) { + mutex_unlock(&dir->d_inode->i_mutex); + if (error == -EOPENSTALE) + goto stale_open; + goto out; + } + error = sharelock_lock_file(file); + mutex_unlock(&dir->d_inode->i_mutex); + if (error) + goto exit_fput; + goto opened; + } + mutex_unlock(&dir->d_inode->i_mutex); if (error <= 0) { @@ -2874,6 +2913,18 @@ finish_open_created: goto stale_open; goto out; } + + if (IS_SHARELOCK(dir->d_inode)) { + /* + * Lock parent i_mutex to prevent races with sharelocks on + * newly created files. + */ + mutex_lock(&dir->d_inode->i_mutex); + error = sharelock_lock_file(file); + mutex_unlock(&dir->d_inode->i_mutex); + if (error) + goto exit_fput; + } opened: error = open_check_o_direct(file); if (error) diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 2033f74..6edbadc 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) { MS_SYNCHRONOUS, ",sync" }, { MS_DIRSYNC, ",dirsync" }, { MS_MANDLOCK, ",mand" }, + { MS_SHARELOCK, ",sharelock" }, { 0, NULL } }; const struct proc_fs_info *fs_infop; diff --git a/include/linux/fs.h b/include/linux/fs.h index 5c49108..774be38 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1005,6 +1005,7 @@ extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); extern void locks_delete_block(struct file_lock *waiter); +extern int sharelock_lock_file(struct file *); extern void lock_flocks(void); extern void unlock_flocks(void); #else /* !CONFIG_FILE_LOCKING */ @@ -1153,6 +1154,11 @@ static inline void locks_delete_block(struct file_lock *waiter) { } +static inline int sharelock_lock_file(struct file *filp) +{ + return 0; +} + static inline void lock_flocks(void) { } @@ -1668,6 +1674,7 @@ struct super_operations { #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) #define IS_IMA(inode) ((inode)->i_flags & S_IMA) #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) +#define IS_SHARELOCK(inode) __IS_FLG(inode, MS_SHARELOCK) #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) /* diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h index a1331ce..19ab5f4 100644 --- a/include/uapi/asm-generic/errno.h +++ b/include/uapi/asm-generic/errno.h @@ -110,4 +110,6 @@ #define EHWPOISON 133 /* Memory page has hardware error */ +#define ESHAREDENIED 134 /* File is locked with a sharelock */ + #endif diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index a48937d..5ac0d49 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -84,6 +84,17 @@ #define O_PATH 010000000 #endif +#ifndef O_DENYREAD +#define O_DENYREAD 020000000 /* Do not permit read access */ +#endif +#ifndef O_DENYWRITE +#define O_DENYWRITE 040000000 /* Do not permit write access */ +#endif +/* FMODE_NONOTIFY 0100000000 */ +#ifndef O_DENYDELETE +#define O_DENYDELETE 0200000000 /* Do not permit delete or rename */ +#endif + #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index c7fc1e6..2af7269 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -86,6 +86,7 @@ struct inodes_stat_t { #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ #define MS_I_VERSION (1<<23) /* Update inode I_version field */ #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#define MS_SHARELOCK (1<<25) /* Allow share locks on an FS */ /* These sb flags are internal to the kernel */ #define MS_SNAP_STABLE (1<<27) /* Snapshot pages during writeback, if needed */ -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 2/7] VFS: Add O_DENYDELETE support for VFS 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky @ 2013-07-01 16:49 ` Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 3/7] CIFS: Add share_access parm to open request Pavel Shilovsky ` (4 subsequent siblings) 6 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel Introduce new LOCK_DELETE flock flag that is suggested to be used internally only to map O_DENYDELETE open flag: !O_DENYDELETE -> LOCK_DELETE | LOCK_MAND. Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- fs/locks.c | 55 +++++++++++++++++++++++++++++++++------- fs/namei.c | 3 +++ include/linux/fs.h | 6 +++++ include/uapi/asm-generic/fcntl.h | 1 + 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index dbc5557..c2fa136 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -269,7 +269,7 @@ EXPORT_SYMBOL(locks_copy_lock); static inline int flock_translate_cmd(int cmd) { if (cmd & LOCK_MAND) - return cmd & (LOCK_MAND | LOCK_RW); + return cmd & (LOCK_MAND | LOCK_RW | LOCK_DELETE); switch (cmd) { case LOCK_SH: return F_RDLCK; @@ -614,6 +614,8 @@ deny_flags_to_cmd(unsigned int flags) cmd |= LOCK_READ; if (!(flags & O_DENYWRITE)) cmd |= LOCK_WRITE; + if (!(flags & O_DENYDELETE)) + cmd |= LOCK_DELETE; return cmd; } @@ -836,6 +838,33 @@ out: return error; } +int +sharelock_may_delete(struct dentry *dentry) +{ + struct file_lock **before; + int rc = 0; + + if (!IS_SHARELOCK(dentry->d_inode)) + return rc; + + lock_flocks(); + for_each_lock(dentry->d_inode, before) { + struct file_lock *fl = *before; + if (IS_POSIX(fl)) + break; + if (IS_LEASE(fl)) + continue; + if (!(fl->fl_type & LOCK_MAND)) + continue; + if (fl->fl_type & LOCK_DELETE) + continue; + rc = 1; + break; + } + unlock_flocks(); + return rc; +} + /* * Determine if a file is allowed to be opened with specified access and share * modes. Lock the file and return 0 if checks passed, otherwise return @@ -850,10 +879,6 @@ sharelock_lock_file(struct file *filp) if (!IS_SHARELOCK(filp->f_path.dentry->d_inode)) return error; - /* Disable O_DENYDELETE support for now */ - if (filp->f_flags & O_DENYDELETE) - return -EINVAL; - error = flock_make_lock(filp, &lock, deny_flags_to_cmd(filp->f_flags)); if (error) return error; @@ -1717,6 +1742,12 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd) if (!f.file) goto out; + /* LOCK_DELETE is defined to be translated from O_DENYDELETE only */ + if (cmd & LOCK_DELETE) { + error = -EINVAL; + goto out; + } + can_sleep = !(cmd & LOCK_NB); cmd &= ~LOCK_NB; unlock = (cmd == LOCK_UN); @@ -2261,10 +2292,16 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, seq_printf(f, "UNKNOWN UNKNOWN "); } if (fl->fl_type & LOCK_MAND) { - seq_printf(f, "%s ", - (fl->fl_type & LOCK_READ) - ? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ " - : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); + if (fl->fl_type & LOCK_DELETE) + seq_printf(f, "%s ", + (fl->fl_type & LOCK_READ) ? + (fl->fl_type & LOCK_WRITE) ? "RWDEL" : "RDDEL" : + (fl->fl_type & LOCK_WRITE) ? "WRDEL" : "DEL "); + else + seq_printf(f, "%s ", + (fl->fl_type & LOCK_READ) ? + (fl->fl_type & LOCK_WRITE) ? "RW " : "READ " : + (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); } else { seq_printf(f, "%s ", (lease_breaking(fl)) diff --git a/fs/namei.c b/fs/namei.c index 7f6edb9..8aaffd4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2220,6 +2220,7 @@ static inline int check_sticky(struct inode *dir, struct inode *inode) * 9. We can't remove a root or mountpoint. * 10. We don't allow removal of NFS sillyrenamed files; it's handled by * nfs_async_unlink(). + * 11. We can't do it if victim is locked by O_DENYDELETE sharelock. */ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) { @@ -2250,6 +2251,8 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) return -ENOENT; if (victim->d_flags & DCACHE_NFSFS_RENAMED) return -EBUSY; + if (sharelock_may_delete(victim)) + return -ESHAREDENIED; return 0; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 774be38..3247fb4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1006,6 +1006,7 @@ extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); extern void locks_delete_block(struct file_lock *waiter); extern int sharelock_lock_file(struct file *); +extern int sharelock_may_delete(struct dentry *); extern void lock_flocks(void); extern void unlock_flocks(void); #else /* !CONFIG_FILE_LOCKING */ @@ -1159,6 +1160,11 @@ static inline int sharelock_lock_file(struct file *filp) return 0; } +static inline int sharelock_may_delete(struct dentry *dentry) +{ + return 0; +} + static inline void lock_flocks(void) { } diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 5ac0d49..a3e6349 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -167,6 +167,7 @@ struct f_owner_ex { blocking */ #define LOCK_UN 8 /* remove lock */ +#define LOCK_DELETE 16 /* which allows to delete a file */ #define LOCK_MAND 32 /* This is a mandatory flock ... */ #define LOCK_READ 64 /* which allows concurrent read operations */ #define LOCK_WRITE 128 /* which allows concurrent write operations */ -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 3/7] CIFS: Add share_access parm to open request 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 2/7] VFS: Add O_DENYDELETE support for VFS Pavel Shilovsky @ 2013-07-01 16:49 ` Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 4/7] CIFS: Add O_DENY* open flags support Pavel Shilovsky ` (3 subsequent siblings) 6 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel and simplify CIFSSMBOpen params. Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- fs/cifs/cifsacl.c | 8 ++++---- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifsproto.h | 8 ++++---- fs/cifs/cifssmb.c | 50 +++++++++++++++++++++++++++----------------------- fs/cifs/dir.c | 12 ++++++------ fs/cifs/file.c | 10 ++++++---- fs/cifs/inode.c | 16 ++++++---------- fs/cifs/link.c | 25 ++++++++----------------- fs/cifs/readdir.c | 5 ++--- fs/cifs/smb1ops.c | 16 +++++++--------- fs/cifs/smb2file.c | 10 +++++----- fs/cifs/smb2inode.c | 4 ++-- fs/cifs/smb2ops.c | 10 ++++++---- fs/cifs/smb2pdu.c | 6 +++--- fs/cifs/smb2proto.h | 14 ++++++++------ 15 files changed, 95 insertions(+), 101 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index f1e3f25..56bdae2 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -908,8 +908,8 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, create_options |= CREATE_OPEN_BACKUP_INTENT; rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, - create_options, &fid, &oplock, NULL, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + FILE_SHARE_ALL, create_options, &fid, &oplock, + NULL, cifs_sb); if (!rc) { rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen); CIFSSMBClose(xid, tcon, fid); @@ -969,8 +969,8 @@ int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, access_flags = WRITE_DAC; rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, access_flags, - create_options, &fid, &oplock, NULL, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + FILE_SHARE_ALL, create_options, &fid, &oplock, + NULL, cifs_sb); if (rc) { cERROR(1, "Unable to open file to set ACL"); goto out; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4f07f6f..80af61d 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -310,7 +310,7 @@ struct smb_version_operations { struct cifs_sb_info *); /* open a file for non-posix mounts */ int (*open)(const unsigned int, struct cifs_tcon *, const char *, int, - int, int, struct cifs_fid *, __u32 *, FILE_ALL_INFO *, + int, int, int, struct cifs_fid *, __u32 *, FILE_ALL_INFO *, struct cifs_sb_info *); /* set fid protocol-specific info */ void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index f450f06..d285b81 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -362,10 +362,10 @@ extern int CIFSSMBQueryReparseLinkInfo(const unsigned int xid, const struct nls_table *nls_codepage); #endif /* temporarily unused until cifs_symlink fixed */ extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const int disposition, - const int access_flags, const int omode, - __u16 *netfid, int *pOplock, FILE_ALL_INFO *, - const struct nls_table *nls_codepage, int remap); + const char *file_name, const int disposition, + const int desired_access, const int share_access, + const int omode, __u16 *netfid, int *oplock, + FILE_ALL_INFO *, struct cifs_sb_info *); extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon, const char *fileName, const int disposition, const int access_flags, const int omode, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 8e2e799..c857b02 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1289,10 +1289,10 @@ OldOpenRetry: int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const int openDisposition, - const int access_flags, const int create_options, __u16 *netfid, - int *pOplock, FILE_ALL_INFO *pfile_info, - const struct nls_table *nls_codepage, int remap) + const char *file_name, const int disposition, + const int desired_access, const int share_access, + const int create_options, __u16 *netfid, int *oplock, + FILE_ALL_INFO *file_info, struct cifs_sb_info *cifs_sb) { int rc = -EACCES; OPEN_REQ *pSMB = NULL; @@ -1300,6 +1300,8 @@ CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, int bytes_returned; int name_len; __u16 count; + struct nls_table *nls_codepage = cifs_sb->local_nls; + int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; openRetry: rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB, @@ -1313,26 +1315,28 @@ openRetry: count = 1; /* account for one byte pad to word boundary */ name_len = cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), - fileName, PATH_MAX, nls_codepage, remap); + file_name, PATH_MAX, nls_codepage, + remap); name_len++; /* trailing null */ name_len *= 2; pSMB->NameLength = cpu_to_le16(name_len); } else { /* BB improve check for buffer overruns BB */ count = 0; /* no pad */ - name_len = strnlen(fileName, PATH_MAX); + name_len = strnlen(file_name, PATH_MAX); name_len++; /* trailing null */ pSMB->NameLength = cpu_to_le16(name_len); - strncpy(pSMB->fileName, fileName, name_len); + strncpy(pSMB->fileName, file_name, name_len); } - if (*pOplock & REQ_OPLOCK) + if (*oplock & REQ_OPLOCK) pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK); - else if (*pOplock & REQ_BATCHOPLOCK) + else if (*oplock & REQ_BATCHOPLOCK) pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); - pSMB->DesiredAccess = cpu_to_le32(access_flags); + pSMB->DesiredAccess = cpu_to_le32(desired_access); pSMB->AllocationSize = 0; - /* set file as system file if special file such - as fifo and server expecting SFU style and - no Unix extensions */ + /* + * set file as system file if special file such as fifo and server + * expecting SFU style and no Unix extensions + */ if (create_options & CREATE_OPTION_SPECIAL) pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); else @@ -1347,8 +1351,8 @@ openRetry: if (create_options & CREATE_OPTION_READONLY) pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY); - pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); - pSMB->CreateDisposition = cpu_to_le32(openDisposition); + pSMB->ShareAccess = cpu_to_le32(share_access); + pSMB->CreateDisposition = cpu_to_le32(disposition); pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); /* BB Expirement with various impersonation levels and verify */ pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); @@ -1366,20 +1370,20 @@ openRetry: if (rc) { cFYI(1, "Error in Open = %d", rc); } else { - *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */ + *oplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */ *netfid = pSMBr->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) - *pOplock |= CIFS_CREATE_ACTION; - if (pfile_info) { - memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime, + *oplock |= CIFS_CREATE_ACTION; + if (file_info) { + memcpy((char *)file_info, (char *)&pSMBr->CreationTime, 36 /* CreationTime to Attributes */); /* the file_info buf is endian converted by caller */ - pfile_info->AllocationSize = pSMBr->AllocationSize; - pfile_info->EndOfFile = pSMBr->EndOfFile; - pfile_info->NumberOfLinks = cpu_to_le32(1); - pfile_info->DeletePending = 0; + file_info->AllocationSize = pSMBr->AllocationSize; + file_info->EndOfFile = pSMBr->EndOfFile; + file_info->NumberOfLinks = cpu_to_le32(1); + file_info->DeletePending = 0; } } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 1cd0162..e5f6723 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -203,6 +203,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, FILE_ALL_INFO *buf = NULL; struct inode *newinode = NULL; int disposition; + int share_access = FILE_SHARE_ALL; struct TCP_Server_Info *server = tcon->ses->server; *oplock = 0; @@ -320,8 +321,8 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, create_options |= CREATE_OPEN_BACKUP_INTENT; rc = server->ops->open(xid, tcon, full_path, disposition, - desired_access, create_options, fid, oplock, - buf, cifs_sb); + desired_access, share_access, create_options, + fid, oplock, buf, cifs_sb); if (rc) { cFYI(1, "cifs_create returned 0x%x", rc); goto out; @@ -626,10 +627,9 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode, if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; - rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, - GENERIC_WRITE, create_options, - &fileHandle, &oplock, buf, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, GENERIC_WRITE, + FILE_SHARE_ALL, create_options, &fileHandle, &oplock, + buf, cifs_sb); if (rc) goto mknod_out; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 7a0dd99..7631b0c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -180,6 +180,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, { int rc; int desired_access; + int share_access = FILE_SHARE_ALL; int disposition; int create_options = CREATE_NOT_DIR; FILE_ALL_INFO *buf; @@ -226,8 +227,8 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, create_options |= CREATE_OPEN_BACKUP_INTENT; rc = server->ops->open(xid, tcon, full_path, disposition, - desired_access, create_options, fid, oplock, buf, - cifs_sb); + desired_access, share_access, create_options, + fid, oplock, buf, cifs_sb); if (rc) goto out; @@ -589,6 +590,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) struct inode *inode; char *full_path = NULL; int desired_access; + int share_access = FILE_SHARE_ALL; int disposition = FILE_OPEN; int create_options = CREATE_NOT_DIR; struct cifs_fid fid; @@ -668,8 +670,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) * not dirty locally we could do this. */ rc = server->ops->open(xid, tcon, full_path, disposition, - desired_access, create_options, &fid, &oplock, - NULL, cifs_sb); + desired_access, share_access, create_options, + &fid, &oplock, NULL, cifs_sb); if (rc) { mutex_unlock(&cfile->fh_mutex); cFYI(1, "cifs_reopen returned 0x%x", rc); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index cb88429..039d9a1 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -407,10 +407,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, tcon = tlink_tcon(tlink); rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, GENERIC_READ, - CREATE_NOT_DIR, &netfid, &oplock, NULL, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + FILE_SHARE_ALL, CREATE_NOT_DIR, &netfid, &oplock, + NULL, cifs_sb); if (rc == 0) { int buf_type = CIFS_NO_BUFFER; /* Read header */ @@ -1006,9 +1004,8 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry, } rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, - DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, - &netfid, &oplock, NULL, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + DELETE|FILE_WRITE_ATTRIBUTES, FILE_SHARE_ALL, + CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb); if (rc != 0) goto out; @@ -1526,9 +1523,8 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, /* open the file to be renamed -- we need DELETE perms */ rc = CIFSSMBOpen(xid, tcon, from_path, FILE_OPEN, DELETE, - CREATE_NOT_DIR, &srcfid, &oplock, NULL, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + FILE_SHARE_ALL, CREATE_NOT_DIR, &srcfid, &oplock, NULL, + cifs_sb); if (rc == 0) { rc = CIFSSMBRenameOpenFile(xid, tcon, srcfid, (const char *) to_dentry->d_name.name, diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 9f6c4c4..886f61d 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -187,16 +187,11 @@ CIFSCreateMFSymLink(const unsigned int xid, struct cifs_tcon *tcon, { int rc; int oplock = 0; - int remap; int create_options = CREATE_NOT_DIR; __u16 netfid = 0; u8 *buf; unsigned int bytes_written = 0; struct cifs_io_parms io_parms; - struct nls_table *nls_codepage; - - nls_codepage = cifs_sb->local_nls; - remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); if (!buf) @@ -212,8 +207,8 @@ CIFSCreateMFSymLink(const unsigned int xid, struct cifs_tcon *tcon, create_options |= CREATE_OPEN_BACKUP_INTENT; rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, - create_options, &netfid, &oplock, NULL, - nls_codepage, remap); + FILE_SHARE_ALL, create_options, &netfid, &oplock, NULL, + cifs_sb); if (rc != 0) { kfree(buf); return rc; @@ -240,7 +235,7 @@ CIFSCreateMFSymLink(const unsigned int xid, struct cifs_tcon *tcon, static int CIFSQueryMFSymLink(const unsigned int xid, struct cifs_tcon *tcon, const unsigned char *searchName, char **symlinkinfo, - const struct nls_table *nls_codepage, int remap) + struct cifs_sb_info *cifs_sb) { int rc; int oplock = 0; @@ -254,8 +249,8 @@ CIFSQueryMFSymLink(const unsigned int xid, struct cifs_tcon *tcon, FILE_ALL_INFO file_info; rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ, - CREATE_NOT_DIR, &netfid, &oplock, &file_info, - nls_codepage, remap); + FILE_SHARE_ALL, CREATE_NOT_DIR, &netfid, &oplock, + &file_info, cifs_sb); if (rc != 0) return rc; @@ -332,10 +327,8 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, pTcon = tlink_tcon(tlink); rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, - CREATE_NOT_DIR, &netfid, &oplock, &file_info, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + FILE_SHARE_ALL, CREATE_NOT_DIR, &netfid, &oplock, + &file_info, cifs_sb); if (rc != 0) goto out; @@ -530,9 +523,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_sb); if ((rc != 0) && cap_unix(tcon->ses)) rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index df40cc5..3d6717d 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -222,9 +222,8 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, char *tmpbuffer; rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, - OPEN_REPARSE_POINT, &fid, &oplock, NULL, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + FILE_SHARE_ALL, OPEN_REPARSE_POINT, &fid, &oplock, NULL, + cifs_sb); if (!rc) { tmpbuffer = kmalloc(maxpath); rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path, diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 47bc5a8..a76950a 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -671,9 +671,9 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path, static int cifs_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, - int disposition, int desired_access, int create_options, - struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf, - struct cifs_sb_info *cifs_sb) + int disposition, int desired_access, int share_access, + int create_options, struct cifs_fid *fid, __u32 *oplock, + FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb) { if (!(tcon->ses->capabilities & CAP_NT_SMBS)) return SMBLegacyOpen(xid, tcon, path, disposition, @@ -682,9 +682,8 @@ cifs_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); return CIFSSMBOpen(xid, tcon, path, disposition, desired_access, - create_options, &fid->netfid, oplock, buf, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + share_access, create_options, &fid->netfid, oplock, + buf, cifs_sb); } static void @@ -779,9 +778,8 @@ smb_set_file_info(struct inode *inode, const char *full_path, cFYI(1, "calling SetFileInfo since SetPathInfo for times not supported " "by this server"); rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, - SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, - &netfid, &oplock, NULL, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, FILE_SHARE_ALL, + CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb); if (rc != 0) { if (rc == -EIO) diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 71e6aed..7dfb50c 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -58,9 +58,9 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, - int disposition, int desired_access, int create_options, - struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf, - struct cifs_sb_info *cifs_sb) + int disposition, int desired_access, int share_access, + int create_options, struct cifs_fid *fid, __u32 *oplock, + FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb) { int rc; __le16 *smb2_path; @@ -87,8 +87,8 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid, - &fid->volatile_fid, desired_access, disposition, - 0, 0, smb2_oplock, smb2_data); + &fid->volatile_fid, desired_access, share_access, + disposition, 0, 0, smb2_oplock, smb2_data); if (rc) goto out; diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 7064824..6af174a 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -54,8 +54,8 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, return -ENOMEM; rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, - desired_access, create_disposition, file_attributes, - create_options, &oplock, NULL); + desired_access, FILE_SHARE_ALL, create_disposition, + file_attributes, create_options, &oplock, NULL); if (rc) { kfree(utf16_path); return rc; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index bceffe7..22aef1d 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -222,7 +222,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, return -ENOMEM; rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, - FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL); + FILE_READ_ATTRIBUTES, FILE_SHARE_ALL, FILE_OPEN, 0, 0, + &oplock, NULL); if (rc) { kfree(utf16_path); return rc; @@ -432,8 +433,8 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, return -ENOMEM; rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, - FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0, - &oplock, NULL); + FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_SHARE_ALL, + FILE_OPEN, 0, 0, &oplock, NULL); kfree(utf16_path); if (rc) { cERROR(1, "open dir failed"); @@ -515,7 +516,8 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, u8 oplock = SMB2_OPLOCK_LEVEL_NONE; rc = SMB2_open(xid, tcon, &srch_path, &persistent_fid, &volatile_fid, - FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL); + FILE_READ_ATTRIBUTES, FILE_SHARE_ALL, FILE_OPEN, 0, 0, + &oplock, NULL); if (rc) return rc; buf->f_type = SMB2_MAGIC_NUMBER; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 41d9d07..45fa2dc 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -910,8 +910,8 @@ parse_lease_state(struct smb2_create_rsp *rsp) int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, - __u32 create_disposition, __u32 file_attributes, __u32 create_options, - __u8 *oplock, struct smb2_file_all_info *buf) + __u32 share_access, __u32 create_disposition, __u32 file_attributes, + __u32 create_options, __u8 *oplock, struct smb2_file_all_info *buf) { struct smb2_create_req *req; struct smb2_create_rsp *rsp; @@ -940,7 +940,7 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, req->DesiredAccess = cpu_to_le32(desired_access); /* File attributes ignored on open (used in create though) */ req->FileAttributes = cpu_to_le32(file_attributes); - req->ShareAccess = FILE_SHARE_ALL_LE; + req->ShareAccess = cpu_to_le32(share_access); req->CreateDisposition = cpu_to_le32(create_disposition); req->CreateOptions = cpu_to_le32(create_options); uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 2aa3535..edff8f6 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -86,9 +86,10 @@ extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *full_path, int disposition, - int desired_access, int create_options, - struct cifs_fid *fid, __u32 *oplock, - FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb); + int desired_access, int share_access, + int create_options, struct cifs_fid *fid, + __u32 *oplock, FILE_ALL_INFO *buf, + struct cifs_sb_info *cifs_sb); extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); extern int smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, const unsigned int xid); @@ -108,9 +109,10 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, u64 *persistent_fid, u64 *volatile_fid, - __u32 desired_access, __u32 create_disposition, - __u32 file_attributes, __u32 create_options, - __u8 *oplock, struct smb2_file_all_info *buf); + __u32 desired_access, __u32 share_access, + __u32 create_disposition, __u32 file_attributes, + __u32 create_options, __u8 *oplock, + struct smb2_file_all_info *buf); extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id); extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 4/7] CIFS: Add O_DENY* open flags support 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky ` (2 preceding siblings ...) 2013-07-01 16:49 ` [PATCH v7 3/7] CIFS: Add share_access parm to open request Pavel Shilovsky @ 2013-07-01 16:49 ` Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 5/7] NFSv4: " Pavel Shilovsky ` (2 subsequent siblings) 6 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel Construct share_access value from O_DENY* flags and send it to the server. Use NTCreateAndX command rather than Trans2 all the time we have any of O_DENY* flags regardless of unix extensions support. Also change smb error mapping of NT_STATUS_SHARING_VIOLATION to -ESHAREDENIED. Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- fs/cifs/cifsfs.c | 2 +- fs/cifs/cifsglob.h | 16 +++++++++++++++- fs/cifs/dir.c | 4 ++++ fs/cifs/file.c | 15 +++++++++++++-- fs/cifs/inode.c | 5 +++-- fs/cifs/netmisc.c | 2 +- fs/cifs/smb2maperror.c | 2 +- fs/locks.c | 11 ++++++++++- include/linux/fs.h | 1 + 9 files changed, 49 insertions(+), 9 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 345fc89..92bd685 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -799,7 +799,7 @@ struct file_system_type cifs_fs_type = { .name = "cifs", .mount = cifs_do_mount, .kill_sb = cifs_kill_sb, - /* .fs_flags */ + .fs_flags = FS_DOES_SHARELOCK, }; MODULE_ALIAS_FS("cifs"); const struct inode_operations cifs_dir_inode_ops = { diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 80af61d..85703f6 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -465,7 +465,7 @@ struct smb_vol { CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID) #define CIFS_MS_MASK (MS_RDONLY | MS_MANDLOCK | MS_NOEXEC | MS_NOSUID | \ - MS_NODEV | MS_SYNCHRONOUS) + MS_NODEV | MS_SYNCHRONOUS | MS_SHARELOCK) struct cifs_mnt_data { struct cifs_sb_info *cifs_sb; @@ -947,6 +947,20 @@ struct cifsFileInfo { struct work_struct oplock_break; /* work for oplock breaks */ }; +static inline int +cifs_get_share_flags(unsigned int flags) +{ + int share_access = 0; + + if (!(flags & O_DENYREAD)) + share_access |= FILE_SHARE_READ; + if (!(flags & O_DENYWRITE)) + share_access |= FILE_SHARE_WRITE; + if (!(flags & O_DENYDELETE)) + share_access |= FILE_SHARE_DELETE; + return share_access; +} + struct cifs_io_parms { __u16 netfid; #ifdef CONFIG_CIFS_SMB2 diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index e5f6723..c03ad8c 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -216,7 +216,11 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, goto out; } + if (IS_SHARELOCK(inode)) + share_access = cifs_get_share_flags(oflags); + if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && + (share_access == FILE_SHARE_ALL) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 7631b0c..febf807 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -216,6 +216,8 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, *********************************************************************/ disposition = cifs_get_disposition(f_flags); + if (IS_SHARELOCK(inode)) + share_access = cifs_get_share_flags(f_flags); /* BB pass O_SYNC flag through on file attributes .. BB */ @@ -428,6 +430,7 @@ int cifs_open(struct inode *inode, struct file *file) int rc = -EACCES; unsigned int xid; __u32 oplock; + int share_access = FILE_SHARE_ALL; struct cifs_sb_info *cifs_sb; struct TCP_Server_Info *server; struct cifs_tcon *tcon; @@ -463,8 +466,12 @@ int cifs_open(struct inode *inode, struct file *file) else oplock = 0; - if (!tcon->broken_posix_open && tcon->unix_ext && - cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + if (IS_SHARELOCK(inode)) + share_access = cifs_get_share_flags(file->f_flags); + + if (!tcon->broken_posix_open && tcon->unix_ext && cap_unix(tcon->ses) && + (share_access == FILE_SHARE_ALL) && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { /* can not refresh inode info since size could be stale */ rc = cifs_posix_open(full_path, &inode, inode->i_sb, @@ -631,7 +638,11 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) else oplock = 0; + if (IS_SHARELOCK(inode)) + share_access = cifs_get_share_flags(cfile->f_flags); + if (tcon->unix_ext && cap_unix(tcon->ses) && + (share_access == FILE_SHARE_ALL) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { /* diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 039d9a1..7aed606 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1167,7 +1167,8 @@ psx_del_no_retry: cifs_drop_nlink(inode); } else if (rc == -ENOENT) { d_drop(dentry); - } else if (rc == -EBUSY) { + } else if (rc == -ESHAREDENIED) { + rc = -EBUSY; if (server->ops->rename_pending_delete) { rc = server->ops->rename_pending_delete(full_path, dentry, xid); @@ -1514,7 +1515,7 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, * source. Note that cross directory moves do not work with * rename by filehandle to various Windows servers. */ - if (rc == 0 || rc != -EBUSY) + if (rc == 0 || rc != -ESHAREDENIED) goto do_rename_exit; /* open-file renames don't work across directories */ diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index c0b25b2..a9c8bef 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -62,7 +62,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = { {ERRdiffdevice, -EXDEV}, {ERRnofiles, -ENOENT}, {ERRwriteprot, -EROFS}, - {ERRbadshare, -EBUSY}, + {ERRbadshare, -ESHAREDENIED}, {ERRlock, -EACCES}, {ERRunsup, -EINVAL}, {ERRnosuchshare, -ENXIO}, diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index 494c912..d70b6ca 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c @@ -356,7 +356,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { {STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED, "STATUS_PORT_CONNECTION_REFUSED"}, {STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"}, - {STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"}, + {STATUS_SHARING_VIOLATION, -ESHAREDENIED, "STATUS_SHARING_VIOLATION"}, {STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"}, {STATUS_INVALID_PAGE_PROTECTION, -EIO, "STATUS_INVALID_PAGE_PROTECTION"}, diff --git a/fs/locks.c b/fs/locks.c index c2fa136..09c1d0e 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -847,6 +847,10 @@ sharelock_may_delete(struct dentry *dentry) if (!IS_SHARELOCK(dentry->d_inode)) return rc; + /* Don't check a lock on file systems that do it internally */ + if (dentry->d_inode->i_sb->s_type->fs_flags & FS_DOES_SHARELOCK) + return rc; + lock_flocks(); for_each_lock(dentry->d_inode, before) { struct file_lock *fl = *before; @@ -875,8 +879,13 @@ sharelock_lock_file(struct file *filp) { struct file_lock *lock; int error = 0; + struct inode *inode = filp->f_path.dentry->d_inode; + + if (!IS_SHARELOCK(inode)) + return error; - if (!IS_SHARELOCK(filp->f_path.dentry->d_inode)) + /* Don't set a lock on file systems that do it internally */ + if (inode->i_sb->s_type->fs_flags & FS_DOES_SHARELOCK) return error; error = flock_make_lock(filp, &lock, deny_flags_to_cmd(filp->f_flags)); diff --git a/include/linux/fs.h b/include/linux/fs.h index 3247fb4..f802776b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1821,6 +1821,7 @@ struct file_system_type { #define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */ #define FS_USERNS_DEV_MOUNT 16 /* A userns mount does not imply MNT_NODEV */ #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ +#define FS_DOES_SHARELOCK 65536 /* FS does sharelocks internally */ struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); void (*kill_sb) (struct super_block *); -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 5/7] NFSv4: Add O_DENY* open flags support 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky ` (3 preceding siblings ...) 2013-07-01 16:49 ` [PATCH v7 4/7] CIFS: Add O_DENY* open flags support Pavel Shilovsky @ 2013-07-01 16:49 ` Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 6/7] NFSD: Pass share reservations flags to VFS Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 7/7] locks: Disable LOCK_MAND support for MS_SHARELOCK mounts Pavel Shilovsky 6 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel by passing these flags to NFSv4 open request. Make it return -ESHAREDENIED on share conflicts with other opens and disable O_DENYDELETE support since NFSv4 doesn't support it. Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- fs/nfs/dir.c | 4 ++++ fs/nfs/internal.h | 3 ++- fs/nfs/nfs4file.c | 4 ++++ fs/nfs/nfs4proc.c | 4 +++- fs/nfs/nfs4super.c | 9 ++++++--- fs/nfs/nfs4xdr.c | 21 +++++++++++++++++---- fs/nfs/super.c | 6 +++++- 7 files changed, 41 insertions(+), 10 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index f23f455..fe3215c 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1395,6 +1395,10 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, dfprintk(VFS, "NFS: atomic_open(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + /* No support for O_DENYDELETE */ + if (open_flags & O_DENYDELETE) + return -EINVAL; + /* NFS only supports OPEN on regular files */ if ((open_flags & O_DIRECTORY)) { if (!d_unhashed(dentry)) { diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 541c9eb..70cd1b0 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -6,7 +6,8 @@ #include <linux/mount.h> #include <linux/security.h> -#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) +#define NFS_MS_MASK (MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC | \ + MS_SYNCHRONOUS | MS_SHARELOCK) struct nfs_string; diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index 13e6bb3..f5ec400 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -34,6 +34,10 @@ nfs4_file_open(struct inode *inode, struct file *filp) dentry->d_parent->d_name.name, dentry->d_name.name); + /* No support for O_DENYDELETE */ + if (openflags & O_DENYDELETE) + return -EINVAL; + if ((openflags & O_ACCMODE) == 3) openflags--; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 261e9b9..6eabd2a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -102,7 +102,7 @@ static int nfs4_map_errors(int err) case -NFS4ERR_BADNAME: return -EINVAL; case -NFS4ERR_SHARE_DENIED: - return -EACCES; + return -ESHAREDENIED; case -NFS4ERR_MINOR_VERS_MISMATCH: return -EPROTONOSUPPORT; case -NFS4ERR_ACCESS: @@ -795,6 +795,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, atomic_inc(&sp->so_count); p->o_arg.fh = NFS_FH(dir); p->o_arg.open_flags = flags; + if (!IS_SHARELOCK(dir)) + p->o_arg.open_flags &= ~(O_DENYREAD | O_DENYWRITE); p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE); /* don't put an ACCESS op in OPEN compound if O_EXCL, because ACCESS * will return permission denied for all bits until close */ diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 569b166..a25a929 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -28,7 +28,8 @@ static struct file_system_type nfs4_remote_fs_type = { .name = "nfs4", .mount = nfs4_remote_mount, .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | FS_BINARY_MOUNTDATA | + FS_DOES_SHARELOCK, }; static struct file_system_type nfs4_remote_referral_fs_type = { @@ -36,7 +37,8 @@ static struct file_system_type nfs4_remote_referral_fs_type = { .name = "nfs4", .mount = nfs4_remote_referral_mount, .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | FS_BINARY_MOUNTDATA | + FS_DOES_SHARELOCK, }; struct file_system_type nfs4_referral_fs_type = { @@ -44,7 +46,8 @@ struct file_system_type nfs4_referral_fs_type = { .name = "nfs4", .mount = nfs4_referral_mount, .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | FS_BINARY_MOUNTDATA | + FS_DOES_SHARELOCK, }; static const struct super_operations nfs4_sops = { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index e3edda5..cb0e8de 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1325,7 +1325,8 @@ static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struc encode_string(xdr, name->len, name->name); } -static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode) +static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode, + int open_flags) { __be32 *p; @@ -1343,7 +1344,19 @@ static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode) default: *p++ = cpu_to_be32(0); } - *p = cpu_to_be32(0); /* for linux, share_deny = 0 always */ + switch (open_flags & (O_DENYREAD|O_DENYWRITE)) { + case O_DENYREAD: + *p = cpu_to_be32(NFS4_SHARE_DENY_READ); + break; + case O_DENYWRITE: + *p = cpu_to_be32(NFS4_SHARE_DENY_WRITE); + break; + case O_DENYREAD|O_DENYWRITE: + *p = cpu_to_be32(NFS4_SHARE_DENY_BOTH); + break; + default: + *p = cpu_to_be32(0); + } } static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg) @@ -1354,7 +1367,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena * owner 4 = 32 */ encode_nfs4_seqid(xdr, arg->seqid); - encode_share_access(xdr, arg->fmode); + encode_share_access(xdr, arg->fmode, arg->open_flags); p = reserve_space(xdr, 36); p = xdr_encode_hyper(p, arg->clientid); *p++ = cpu_to_be32(24); @@ -1491,7 +1504,7 @@ static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_close encode_op_hdr(xdr, OP_OPEN_DOWNGRADE, decode_open_downgrade_maxsz, hdr); encode_nfs4_stateid(xdr, arg->stateid); encode_nfs4_seqid(xdr, arg->seqid); - encode_share_access(xdr, arg->fmode); + encode_share_access(xdr, arg->fmode, 0); } static void diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 2f8a29d..3ba828b 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -333,6 +333,8 @@ struct file_system_type nfs4_fs_type = { .mount = nfs_fs_mount, .kill_sb = nfs_kill_super, .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | FS_BINARY_MOUNTDATA | + FS_DOES_SHARELOCK, }; MODULE_ALIAS_FS("nfs4"); MODULE_ALIAS("nfs4"); @@ -2544,6 +2546,8 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags, struct nfs_server *server; struct dentry *mntroot = ERR_PTR(-ENOMEM); struct nfs_subversion *nfs_mod = NFS_SB(data->sb)->nfs_client->cl_nfs_mod; + /* save sharelock option */ + int sharelock = data->sb->s_flags & MS_SHARELOCK; dprintk("--> nfs_xdev_mount()\n"); @@ -2555,7 +2559,7 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags, if (IS_ERR(server)) mntroot = ERR_CAST(server); else - mntroot = nfs_fs_mount_common(server, flags, + mntroot = nfs_fs_mount_common(server, flags | sharelock, dev_name, &mount_info, nfs_mod); dprintk("<-- nfs_xdev_mount() = %ld\n", -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 6/7] NFSD: Pass share reservations flags to VFS 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky ` (4 preceding siblings ...) 2013-07-01 16:49 ` [PATCH v7 5/7] NFSv4: " Pavel Shilovsky @ 2013-07-01 16:49 ` Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 7/7] locks: Disable LOCK_MAND support for MS_SHARELOCK mounts Pavel Shilovsky 6 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel that maps them into O_DENY* flags and make them visible for applications on mounts with sharelock option. Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- fs/locks.c | 1 + fs/nfsd/nfs4state.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- fs/nfsd/nfsproc.c | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/fs/locks.c b/fs/locks.c index 09c1d0e..60f3ce8 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -899,6 +899,7 @@ sharelock_lock_file(struct file *filp) locks_free_lock(lock); return error; } +EXPORT_SYMBOL(sharelock_lock_file); static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) { diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f9a5e62..b19cb3a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -457,6 +457,19 @@ test_deny(u32 access, struct nfs4_ol_stateid *stp) return test_bit(access, &stp->st_deny_bmap); } +static int nfs4_deny_to_odeny(u32 access) +{ + switch (access & NFS4_SHARE_DENY_BOTH) { + case NFS4_SHARE_DENY_READ: + return O_DENYREAD; + case NFS4_SHARE_DENY_WRITE: + return O_DENYWRITE; + case NFS4_SHARE_DENY_BOTH: + return O_DENYREAD | O_DENYWRITE; + } + return 0; +} + static int nfs4_access_to_omode(u32 access) { switch (access & NFS4_SHARE_ACCESS_BOTH) { @@ -2780,6 +2793,21 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, } static __be32 +nfs4_vfs_set_deny(struct nfs4_file *fp, unsigned long share_access, + unsigned long deny_access) +{ + int oflag, rc; + __be32 status = nfs_ok; + + oflag = nfs4_access_to_omode(share_access); + fp->fi_fds[oflag]->f_flags |= nfs4_deny_to_odeny(deny_access); + rc = sharelock_lock_file(fp->fi_fds[oflag]); + if (rc) + status = nfserrno(rc); + return status; +} + +static __be32 nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { u32 op_share_access = open->op_share_access; @@ -2800,6 +2828,14 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c } return status; } + status = nfs4_vfs_set_deny(fp, op_share_access, open->op_share_deny); + if (status) { + if (new_access) { + int oflag = nfs4_access_to_omode(op_share_access); + nfs4_file_put_access(fp, oflag); + } + return status; + } /* remember the open */ set_access(op_share_access, stp); set_deny(open->op_share_deny, stp); @@ -3033,7 +3069,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf /* * OPEN the file, or upgrade an existing OPEN. - * If truncate fails, the OPEN fails. + * If truncate or set_deny fails, the OPEN fails. */ if (stp) { /* Stateid was found, this is an OPEN upgrade */ @@ -3047,6 +3083,10 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf status = nfsd4_truncate(rqstp, current_fh, open); if (status) goto out; + status = nfs4_vfs_set_deny(fp, open->op_share_access, + open->op_share_deny); + if (status) + goto out; stp = open->op_stp; open->op_stp = NULL; init_open_stateid(stp, fp, open); @@ -3745,6 +3785,10 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, } nfs4_stateid_downgrade(stp, od->od_share_access); + status = nfs4_vfs_set_deny(stp->st_file, od->od_share_access, + od->od_share_deny); + if (status) + goto out; reset_union_bmap_deny(od->od_share_deny, stp); update_stateid(&stp->st_stid.sc_stateid); diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 54c6b3d..ff95544 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -743,6 +743,7 @@ nfserrno (int errno) { nfserr_notsupp, -EOPNOTSUPP }, { nfserr_toosmall, -ETOOSMALL }, { nfserr_serverfault, -ESERVERFAULT }, + { nfserr_share_denied, -ESHAREDENIED }, }; int i; -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 7/7] locks: Disable LOCK_MAND support for MS_SHARELOCK mounts 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky ` (5 preceding siblings ...) 2013-07-01 16:49 ` [PATCH v7 6/7] NFSD: Pass share reservations flags to VFS Pavel Shilovsky @ 2013-07-01 16:49 ` Pavel Shilovsky 6 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2013-07-01 16:49 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- fs/locks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/locks.c b/fs/locks.c index 60f3ce8..86f3a93 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1758,6 +1758,12 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd) goto out; } + /* Disable LOCK_MAND support for FS mounted with MS_SHARELOCK */ + if ((cmd & LOCK_MAND) && IS_SHARELOCK(f.file->f_path.dentry->d_inode)) { + error = -EPERM; + goto out; + } + can_sleep = !(cmd & LOCK_NB); cmd &= ~LOCK_NB; unlock = (cmd == LOCK_UN); -- 1.8.1.5 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS @ 2014-01-17 10:07 Pavel Shilovsky 2014-01-17 10:07 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky 0 siblings, 1 reply; 17+ messages in thread From: Pavel Shilovsky @ 2014-01-17 10:07 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel This patchset adds support of O_DENY* flags for Linux fs layer. These flags can be used by any application that needs share reservations to organize a file access. VFS already has some sort of this capability - now it's done through flock/LOCK_MAND mechanis, but that approach is non-atomic. This patchset build new capabilities on top of the existing one but doesn't bring any changes into the flock call semantic. These flags can be used by NFS (built-in-kernel) and CIFS (Samba) servers and Wine applications through VFS (for local filesystems) or CIFS/NFS modules. This will help when e.g. Samba and NFS server share the same directory for Windows and Linux users or Wine applications use Samba/NFS share to access the same data from different clients. According to the previous discussions the most problematic question is how to prevent situations like DoS attacks where e.g /lib/liba.so file can be open with DENYREAD, or smth like this. That's why extra mount option 'sharelock' is added for switching on/off O_DENY* flags processing. It allows to avoid use of these flags on critical places (like /, /lib) and turn them on if we really want it proccessed that way. So, we have 3 new flags: O_DENYREAD - to prevent other opens with read access, O_DENYWRITE - to prevent other opens with write access, O_DENYDELETE - to prevent delete operations The patchset avoids data races problem on newely created files: open with O_CREAT can return the -ESHAREDENIED error for a successfully created file if this files was locked with a sharelock by another task. Also, it turns flock/LOCK_MAND capability off for mounts with 'sharelock' option. This lets not mix one share reservation approach with another. Patches #1, #2 and #3 add O_DENY* flags to fcntl and implement VFS part. A patch #4 is related to CIFS client changes, #5 -- NFSD, #6 and #7 describe NFSv4 client parts. The preliminary patch for Samba that replaces the existing use of flock/LOCK_MAND mechanism with O_DENY* flags: http://git.etersoft.ru/people/piastry/packages/?p=samba.git;a=commitdiff;h=173070262b7f29134ad6b2e49a760017c11aec4a The future part of open man page patch: ----- O_DENYREAD, O_DENYWRIYE, O_DENYDELETE - used to inforce a mandatory share reservation scheme of the file access. If these flag is passed, the open fails with -ESHAREDENIED in following cases: 1) if O_DENYREAD flag is specified and there is another open with READ access to the file; 2) if O_DENYWRITE flag is specified and there is another open with WRITE access to the file; 3) if READ access is requested and there is another open with O_DENYREAD flags; 4) if WRITE access is requested and there is another open with O_DENYWRITE flags. If O_DENYDELETE flag is specified and the open succeded, any further unlink operation will fail with -ESHAREDENIED untill this open is closed. Now this flag is processed by VFS and CIFS filesystem. NFS returns -EINVAL for opens with this flag. This mechanism is done through flocks. If O_DENY* flags are specified, flock syscall is processed after the open. The share modes are translated into flock according the following rules: 1) !O_DENYREAD -> LOCK_READ | LOCK_MAND 2) !O_DENYWRITE -> LOCK_WRITE | LOCK_MAND 3) !O_DENYDELETE -> LOCK_DELETE | LOCK_MAND ----- Pavel Shilovsky (7): VFS: Introduce new O_DENY* open flags VFS: Add O_DENYDELETE support for VFS locks: Disable LOCK_MAND support for MS_SHARELOCK mounts CIFS: Add O_DENY* open flags support NFSD: Pass share reservations flags to VFS NFSv4: Add deny state handling for nfs4_state struct NFSv4: Add O_DENY* open flags support arch/alpha/include/uapi/asm/errno.h | 2 + arch/alpha/include/uapi/asm/fcntl.h | 3 + arch/mips/include/uapi/asm/errno.h | 2 + arch/parisc/include/uapi/asm/errno.h | 2 + arch/parisc/include/uapi/asm/fcntl.h | 3 + arch/sparc/include/uapi/asm/errno.h | 2 + arch/sparc/include/uapi/asm/fcntl.h | 3 + fs/cifs/cifsacl.c | 2 + fs/cifs/cifsfs.c | 2 +- fs/cifs/cifsglob.h | 17 +- fs/cifs/cifssmb.c | 2 +- fs/cifs/dir.c | 6 + fs/cifs/file.c | 20 ++- fs/cifs/inode.c | 12 +- fs/cifs/link.c | 2 + fs/cifs/netmisc.c | 2 +- fs/cifs/smb1ops.c | 3 + fs/cifs/smb2inode.c | 1 + fs/cifs/smb2maperror.c | 2 +- fs/cifs/smb2ops.c | 6 + fs/cifs/smb2pdu.c | 2 +- fs/fcntl.c | 5 +- fs/locks.c | 161 ++++++++++++++++-- fs/namei.c | 56 +++++- fs/nfs/dir.c | 39 ++++- fs/nfs/inode.c | 8 +- fs/nfs/internal.h | 3 +- fs/nfs/nfs4_fs.h | 83 ++++++++- fs/nfs/nfs4file.c | 8 +- fs/nfs/nfs4proc.c | 310 +++++++++++++++++++++++----------- fs/nfs/nfs4state.c | 65 ++++--- fs/nfs/nfs4super.c | 9 +- fs/nfs/nfs4xdr.c | 21 ++- fs/nfs/super.c | 7 +- fs/nfsd/nfs4state.c | 46 ++++- fs/nfsd/nfsproc.c | 1 + fs/proc_namespace.c | 1 + include/linux/fs.h | 15 ++ include/linux/nfs_fs.h | 5 +- include/linux/nfs_xdr.h | 1 + include/uapi/asm-generic/errno.h | 2 + include/uapi/asm-generic/fcntl.h | 12 ++ include/uapi/linux/fs.h | 1 + 43 files changed, 789 insertions(+), 166 deletions(-) -- 1.7.10.4 ^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-01-17 10:07 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky @ 2014-01-17 10:07 ` Pavel Shilovsky 2014-01-17 18:18 ` One Thousand Gnomes 2014-02-01 13:20 ` Jeff Layton 0 siblings, 2 replies; 17+ messages in thread From: Pavel Shilovsky @ 2014-01-17 10:07 UTC (permalink / raw) To: linux-kernel; +Cc: linux-cifs, linux-fsdevel, linux-nfs, wine-devel This patch adds 3 flags: 1) O_DENYREAD that doesn't permit read access, 2) O_DENYWRITE that doesn't permit write access, 3) O_DENYDELETE that doesn't permit delete or rename. Network filesystems CIFS, SMB2.0, SMB3.0 and NFSv4 have such flags - this change can benefit cifs and nfs modules as well as Samba and NFS file servers that export the same directory for Windows clients, or Wine applications that access the same files simultaneously. These flags are only take affect for opens on mounts with new sharelock option. They are translated to flock's flags: !O_DENYREAD -> LOCK_READ | LOCK_MAND !O_DENYWRITE -> LOCK_WRITE | LOCK_MAND and set through flock_lock_file on a file. If the file can't be locked due conflicts with another open with O_DENY* flags, a new -ESHAREDENIED error code is returned. Create codepath is slightly changed to prevent data races on newly created files: when open with O_CREAT can return -ESHAREDENIED error for successfully created files due to a sharelock set by another task. Temporary disable O_DENYDELETE support - will enable it in further patches. Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> --- arch/alpha/include/uapi/asm/errno.h | 2 + arch/alpha/include/uapi/asm/fcntl.h | 3 ++ arch/mips/include/uapi/asm/errno.h | 2 + arch/parisc/include/uapi/asm/errno.h | 2 + arch/parisc/include/uapi/asm/fcntl.h | 3 ++ arch/sparc/include/uapi/asm/errno.h | 2 + arch/sparc/include/uapi/asm/fcntl.h | 3 ++ fs/fcntl.c | 5 +- fs/locks.c | 97 +++++++++++++++++++++++++++++++--- fs/namei.c | 53 ++++++++++++++++++- fs/proc_namespace.c | 1 + include/linux/fs.h | 8 +++ include/uapi/asm-generic/errno.h | 2 + include/uapi/asm-generic/fcntl.h | 11 ++++ include/uapi/linux/fs.h | 1 + 15 files changed, 185 insertions(+), 10 deletions(-) diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h index 17f92aa..953a6d6 100644 --- a/arch/alpha/include/uapi/asm/errno.h +++ b/arch/alpha/include/uapi/asm/errno.h @@ -124,4 +124,6 @@ #define EHWPOISON 139 /* Memory page has hardware error */ +#define ESHAREDENIED 140 /* File is locked with a sharelock */ + #endif diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h index 09f49a6..265344b 100644 --- a/arch/alpha/include/uapi/asm/fcntl.h +++ b/arch/alpha/include/uapi/asm/fcntl.h @@ -33,6 +33,9 @@ #define O_PATH 040000000 #define __O_TMPFILE 0100000000 +#define O_DENYREAD 0200000000 /* Do not permit read access */ +#define O_DENYWRITE 0400000000 /* Do not permit write access */ +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ #define F_GETLK 7 #define F_SETLK 8 diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h index 02d645d..f1a4068 100644 --- a/arch/mips/include/uapi/asm/errno.h +++ b/arch/mips/include/uapi/asm/errno.h @@ -123,6 +123,8 @@ #define EHWPOISON 168 /* Memory page has hardware error */ +#define ESHAREDENIED 169 /* File is locked with a sharelock */ + #define EDQUOT 1133 /* Quota exceeded */ diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h index f3a8aa5..654c232 100644 --- a/arch/parisc/include/uapi/asm/errno.h +++ b/arch/parisc/include/uapi/asm/errno.h @@ -124,4 +124,6 @@ #define EHWPOISON 257 /* Memory page has hardware error */ +#define ESHAREDENIED 258 /* File is locked with a sharelock */ + #endif diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h index 34a46cb..5865964 100644 --- a/arch/parisc/include/uapi/asm/fcntl.h +++ b/arch/parisc/include/uapi/asm/fcntl.h @@ -21,6 +21,9 @@ #define O_PATH 020000000 #define __O_TMPFILE 040000000 +#define O_DENYREAD 0200000000 /* Do not permit read access */ +#define O_DENYWRITE 0400000000 /* Do not permit write access */ +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ #define F_GETLK64 8 #define F_SETLK64 9 diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h index 20423e17..fe339b5 100644 --- a/arch/sparc/include/uapi/asm/errno.h +++ b/arch/sparc/include/uapi/asm/errno.h @@ -114,4 +114,6 @@ #define EHWPOISON 135 /* Memory page has hardware error */ +#define ESHAREDENIED 136 /* File is locked with a sharelock */ + #endif diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h index 7e8ace5..ab68170 100644 --- a/arch/sparc/include/uapi/asm/fcntl.h +++ b/arch/sparc/include/uapi/asm/fcntl.h @@ -36,6 +36,9 @@ #define O_PATH 0x1000000 #define __O_TMPFILE 0x2000000 +#define O_DENYREAD 0x4000000 /* Do not permit read access */ +#define O_DENYWRITE 0x8000000 /* Do not permit write access */ +#define O_DENYDELETE 0x10000000 /* Do not permit delete or rename */ #define F_GETOWN 5 /* for sockets. */ #define F_SETOWN 6 /* for sockets. */ diff --git a/fs/fcntl.c b/fs/fcntl.c index ef68665..3f85887 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -729,14 +729,15 @@ 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(20 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( + BUILD_BUG_ON(23 - 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 | O_PATH | __O_TMPFILE + __FMODE_EXEC | O_PATH | __O_TMPFILE | + O_DENYREAD | O_DENYWRITE | O_DENYDELETE )); fasync_cache = kmem_cache_create("fasync_cache", diff --git a/fs/locks.c b/fs/locks.c index 92a0f0a..ffde4d4 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -708,20 +708,73 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s return (locks_conflict(caller_fl, sys_fl)); } -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific - * checking before calling the locks_conflict(). +static unsigned int +deny_flags_to_cmd(unsigned int flags) +{ + unsigned int cmd = LOCK_MAND; + + if (!(flags & O_DENYREAD)) + cmd |= LOCK_READ; + if (!(flags & O_DENYWRITE)) + cmd |= LOCK_WRITE; + + return cmd; +} + +/* + * locks_mand_conflict - Determine if there's a share reservation conflict + * @caller_fl: lock we're attempting to acquire + * @sys_fl: lock already present on system that we're checking against + * + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE + * tell us whether the reservation allows other readers and writers. + */ +static int +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + unsigned char caller_type = caller_fl->fl_type; + unsigned char sys_type = sys_fl->fl_type; + fmode_t caller_fmode = caller_fl->fl_file->f_mode; + fmode_t sys_fmode = sys_fl->fl_file->f_mode; + + /* they can only conflict if FS is mounted with MS_SHARELOCK */ + if (!IS_SHARELOCK(caller_fl->fl_file->f_path.dentry->d_inode)) + return 0; + + /* they can only conflict if they're both LOCK_MAND */ + if (!(caller_type & LOCK_MAND) || !(sys_type & LOCK_MAND)) + return 0; + + if (!(caller_type & LOCK_READ) && (sys_fmode & FMODE_READ)) + return 1; + if (!(caller_type & LOCK_WRITE) && (sys_fmode & FMODE_WRITE)) + return 1; + if (!(sys_type & LOCK_READ) && (caller_fmode & FMODE_READ)) + return 1; + if (!(sys_type & LOCK_WRITE) && (caller_fmode & FMODE_WRITE)) + return 1; + + return 0; +} + +/* + * Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking + * before calling the locks_conflict(). */ static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - /* FLOCK locks referring to the same filp do not conflict with + if (!IS_FLOCK(sys_fl)) + return 0; + if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) + return locks_mand_conflict(caller_fl, sys_fl); + /* + * FLOCK locks referring to the same filp do not conflict with * each other. */ - if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) - return (0); - if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) + if (caller_fl->fl_file == sys_fl->fl_file) return 0; - return (locks_conflict(caller_fl, sys_fl)); + return locks_conflict(caller_fl, sys_fl); } void @@ -888,6 +941,36 @@ out: return error; } +/* + * Determine if a file is allowed to be opened with specified access and share + * modes. Lock the file and return 0 if checks passed, otherwise return + * -ESHAREDENIED. + */ +int +sharelock_lock_file(struct file *filp) +{ + struct file_lock *lock; + int error = 0; + + if (!IS_SHARELOCK(filp->f_path.dentry->d_inode)) + return error; + + /* Disable O_DENYDELETE support for now */ + if (filp->f_flags & O_DENYDELETE) + return -EINVAL; + + error = flock_make_lock(filp, &lock, deny_flags_to_cmd(filp->f_flags)); + if (error) + return error; + + error = flock_lock_file(filp, lock); + if (error == -EAGAIN) + error = -ESHAREDENIED; + + locks_free_lock(lock); + return error; +} + static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) { struct file_lock *fl; diff --git a/fs/namei.c b/fs/namei.c index 3531dee..2b741a1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2725,9 +2725,14 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, acc_mode = MAY_OPEN; } error = may_open(&file->f_path, acc_mode, open_flag); - if (error) + if (error) { fput(file); + goto out; + } + error = sharelock_lock_file(file); + if (error) + fput(file); out: dput(dentry); return error; @@ -2919,6 +2924,40 @@ retry_lookup: } mutex_lock(&dir->d_inode->i_mutex); error = lookup_open(nd, path, file, op, got_write, opened); + + /* + * For sharelock mounts if a file was created but not opened, we need + * to keep parent i_mutex until we finish the open to prevent races when + * somebody opens newly created by us file and locks it with a sharelock + * before we open it. + */ + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { + /* Don't check for write permission, don't truncate */ + open_flag &= ~O_TRUNC; + will_truncate = false; + acc_mode = MAY_OPEN; + path_to_nameidata(path, nd); + + error = may_open(&nd->path, acc_mode, open_flag); + if (error) { + mutex_unlock(&dir->d_inode->i_mutex); + goto out; + } + file->f_path.mnt = nd->path.mnt; + error = finish_open(file, nd->path.dentry, NULL, opened); + if (error) { + mutex_unlock(&dir->d_inode->i_mutex); + if (error == -EOPENSTALE) + goto stale_open; + goto out; + } + error = sharelock_lock_file(file); + mutex_unlock(&dir->d_inode->i_mutex); + if (error) + goto exit_fput; + goto opened; + } + mutex_unlock(&dir->d_inode->i_mutex); if (error <= 0) { @@ -3034,6 +3073,18 @@ finish_open_created: goto stale_open; goto out; } + + if (IS_SHARELOCK(dir->d_inode)) { + /* + * Lock parent i_mutex to prevent races with sharelocks on + * newly created files. + */ + mutex_lock(&dir->d_inode->i_mutex); + error = sharelock_lock_file(file); + mutex_unlock(&dir->d_inode->i_mutex); + if (error) + goto exit_fput; + } opened: error = open_check_o_direct(file); if (error) diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 439406e..dd374d4 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) { MS_SYNCHRONOUS, ",sync" }, { MS_DIRSYNC, ",dirsync" }, { MS_MANDLOCK, ",mand" }, + { MS_SHARELOCK, ",sharelock" }, { 0, NULL } }; const struct proc_fs_info *fs_infop; diff --git a/include/linux/fs.h b/include/linux/fs.h index 121f11f..aa061ca 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1029,6 +1029,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); +extern int sharelock_lock_file(struct file *); #else /* !CONFIG_FILE_LOCKING */ static inline int fcntl_getlk(struct file *file, struct flock __user *user) { @@ -1169,6 +1170,12 @@ static inline int lock_may_write(struct inode *inode, loff_t start, { return 1; } + +static inline int sharelock_lock_file(struct file *filp) +{ + return 0; +} + #endif /* !CONFIG_FILE_LOCKING */ @@ -1675,6 +1682,7 @@ struct super_operations { #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) #define IS_IMA(inode) ((inode)->i_flags & S_IMA) #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) +#define IS_SHARELOCK(inode) __IS_FLG(inode, MS_SHARELOCK) #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) /* diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h index 1e1ea6e..aff869c 100644 --- a/include/uapi/asm-generic/errno.h +++ b/include/uapi/asm-generic/errno.h @@ -110,4 +110,6 @@ #define EHWPOISON 133 /* Memory page has hardware error */ +#define ESHAREDENIED 134 /* File is locked with a sharelock */ + #endif diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 95e46c8..9881cfe 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -92,6 +92,17 @@ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) +#ifndef O_DENYREAD +#define O_DENYREAD 040000000 /* Do not permit read access */ +#endif +/* FMODE_NONOTIFY 0100000000 */ +#ifndef O_DENYWRITE +#define O_DENYWRITE 0200000000 /* Do not permit write access */ +#endif +#ifndef O_DENYDELETE +#define O_DENYDELETE 0400000000 /* Do not permit delete or rename */ +#endif + #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 6c28b61..11f0ecf 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -86,6 +86,7 @@ struct inodes_stat_t { #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ #define MS_I_VERSION (1<<23) /* Update inode I_version field */ #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#define MS_SHARELOCK (1<<25) /* Allow share locks on an FS */ /* These sb flags are internal to the kernel */ #define MS_NOSEC (1<<28) -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-01-17 10:07 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky @ 2014-01-17 18:18 ` One Thousand Gnomes 2014-01-20 10:45 ` Pavel Shilovsky 2014-02-01 13:57 ` Jeff Layton 2014-02-01 13:20 ` Jeff Layton 1 sibling, 2 replies; 17+ messages in thread From: One Thousand Gnomes @ 2014-01-17 18:18 UTC (permalink / raw) To: Pavel Shilovsky Cc: linux-kernel, linux-cifs, linux-fsdevel, linux-nfs, wine-devel > +#define ESHAREDENIED 258 /* File is locked with a sharelock */ Have you prepared C library patches to match this ? (and why not just use EPERM, it has the meaning you want already) > + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE > + * tell us whether the reservation allows other readers and writers. > + */ > +static int > +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) > +{ Shouldn't this also check for CAP_SYS_DAC or some similar permission so that root can override such a mess (eg to fix full disks in an emergency) ? > + > + /* > + * For sharelock mounts if a file was created but not opened, we need > + * to keep parent i_mutex until we finish the open to prevent races when > + * somebody opens newly created by us file and locks it with a sharelock > + * before we open it. > + */ > + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { > + /* Don't check for write permission, don't truncate */ > + open_flag &= ~O_TRUNC; > + will_truncate = false; > + acc_mode = MAY_OPEN; > + path_to_nameidata(path, nd); > + > + error = may_open(&nd->path, acc_mode, open_flag); > + if (error) { > + mutex_unlock(&dir->d_inode->i_mutex); > + goto out; > + } > + file->f_path.mnt = nd->path.mnt; > + error = finish_open(file, nd->path.dentry, NULL, opened); > + if (error) { > + mutex_unlock(&dir->d_inode->i_mutex); > + if (error == -EOPENSTALE) > + goto stale_open; > + goto out; > + } > + error = sharelock_lock_file(file); > + mutex_unlock(&dir->d_inode->i_mutex); > + if (error) > + goto exit_fput; > + goto opened; > + } > + > mutex_unlock(&dir->d_inode->i_mutex); What stops the file system changing mount flags via a remount between these two ? > > if (error <= 0) { > @@ -3034,6 +3073,18 @@ finish_open_created: > goto stale_open; > goto out; > } > + > + if (IS_SHARELOCK(dir->d_inode)) { > + /* > + * Lock parent i_mutex to prevent races with sharelocks on > + * newly created files. > + */ > + mutex_lock(&dir->d_inode->i_mutex); > + error = sharelock_lock_file(file); > + mutex_unlock(&dir->d_inode->i_mutex); > + if (error) > + goto exit_fput; > + } > opened: ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-01-17 18:18 ` One Thousand Gnomes @ 2014-01-20 10:45 ` Pavel Shilovsky 2014-01-20 13:34 ` One Thousand Gnomes 2014-02-01 13:57 ` Jeff Layton 1 sibling, 1 reply; 17+ messages in thread From: Pavel Shilovsky @ 2014-01-20 10:45 UTC (permalink / raw) To: One Thousand Gnomes Cc: Kernel Mailing List, linux-cifs, linux-fsdevel, Linux NFS Mailing list, wine-devel 2014/1/17 One Thousand Gnomes <gnomes@lxorguk.ukuu.org.uk>: >> +#define ESHAREDENIED 258 /* File is locked with a sharelock */ > > Have you prepared C library patches to match this ? I don't have it for now. > > (and why not just use EPERM, it has the meaning you want already) We need to determine if we have a share reservation error to map it accurately in file servers and wine. > > >> + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE >> + * tell us whether the reservation allows other readers and writers. >> + */ >> +static int >> +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) >> +{ > > Shouldn't this also check for CAP_SYS_DAC or some similar permission so > that root can override such a mess (eg to fix full disks in an > emergency) ? May be it's better to let root an ability to remount the system without sharelock mount option and then fix an emergency? > > >> + >> + /* >> + * For sharelock mounts if a file was created but not opened, we need >> + * to keep parent i_mutex until we finish the open to prevent races when >> + * somebody opens newly created by us file and locks it with a sharelock >> + * before we open it. >> + */ >> + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { >> + /* Don't check for write permission, don't truncate */ >> + open_flag &= ~O_TRUNC; >> + will_truncate = false; >> + acc_mode = MAY_OPEN; >> + path_to_nameidata(path, nd); >> + >> + error = may_open(&nd->path, acc_mode, open_flag); >> + if (error) { >> + mutex_unlock(&dir->d_inode->i_mutex); >> + goto out; >> + } >> + file->f_path.mnt = nd->path.mnt; >> + error = finish_open(file, nd->path.dentry, NULL, opened); >> + if (error) { >> + mutex_unlock(&dir->d_inode->i_mutex); >> + if (error == -EOPENSTALE) >> + goto stale_open; >> + goto out; >> + } >> + error = sharelock_lock_file(file); >> + mutex_unlock(&dir->d_inode->i_mutex); >> + if (error) >> + goto exit_fput; >> + goto opened; >> + } >> + >> mutex_unlock(&dir->d_inode->i_mutex); > > What stops the file system changing mount flags via a remount between > these two ? Nothing, but it doesn't bring problems here: if the first IS_SHARELOCK condition is true, we don't get into the second (see 'goto opened'). > >> >> if (error <= 0) { >> @@ -3034,6 +3073,18 @@ finish_open_created: >> goto stale_open; >> goto out; >> } >> + >> + if (IS_SHARELOCK(dir->d_inode)) { >> + /* >> + * Lock parent i_mutex to prevent races with sharelocks on >> + * newly created files. >> + */ >> + mutex_lock(&dir->d_inode->i_mutex); >> + error = sharelock_lock_file(file); >> + mutex_unlock(&dir->d_inode->i_mutex); >> + if (error) >> + goto exit_fput; >> + } >> opened: > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Best regards, Pavel Shilovsky. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-01-20 10:45 ` Pavel Shilovsky @ 2014-01-20 13:34 ` One Thousand Gnomes 2014-01-21 13:19 ` Pavel Shilovsky 0 siblings, 1 reply; 17+ messages in thread From: One Thousand Gnomes @ 2014-01-20 13:34 UTC (permalink / raw) To: Pavel Shilovsky Cc: Kernel Mailing List, linux-cifs, linux-fsdevel, Linux NFS Mailing list, wine-devel > > (and why not just use EPERM, it has the meaning you want already) > We need to determine if we have a share reservation error to map it > accurately in file servers and wine. Ok > > Shouldn't this also check for CAP_SYS_DAC or some similar permission so > > that root can override such a mess (eg to fix full disks in an > > emergency) ? > > May be it's better to let root an ability to remount the system > without sharelock mount option and then fix an emergency? Doesn't that involve breaking the service for all users of the system relying upon those locks, while root being allowed to ignore the locks does not ? Alan ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-01-20 13:34 ` One Thousand Gnomes @ 2014-01-21 13:19 ` Pavel Shilovsky 0 siblings, 0 replies; 17+ messages in thread From: Pavel Shilovsky @ 2014-01-21 13:19 UTC (permalink / raw) To: One Thousand Gnomes Cc: Kernel Mailing List, linux-cifs, linux-fsdevel, Linux NFS Mailing list, wine-devel 2014/1/20 One Thousand Gnomes <gnomes@lxorguk.ukuu.org.uk>: >> > Shouldn't this also check for CAP_SYS_DAC or some similar permission so >> > that root can override such a mess (eg to fix full disks in an >> > emergency) ? >> >> May be it's better to let root an ability to remount the system >> without sharelock mount option and then fix an emergency? > > Doesn't that involve breaking the service for all users of the system > relying upon those locks, while root being allowed to ignore the locks > does not ? If we allow root to remount without "sharelock" (or bypass conflict checks), it will definitely break the service for all users. Probably it's better to stop the service (that cause all sharelocks to be unlocked), fix the emergency and start the service again. In the current state, the patchset doesn't allow any sort of ignoring those locks for mounts with "sharelock" option (either remount without "sharelock" or set special capabilities). It was done to make sure nothing breaks applications relying upon sharelock behavior. Also, that's why "sharelock" mount option was added: this behavior is dangerous to be on critical system paths like "/" or "/lib" and not suitable for global use. -- Best regards, Pavel Shilovsky. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-01-17 18:18 ` One Thousand Gnomes 2014-01-20 10:45 ` Pavel Shilovsky @ 2014-02-01 13:57 ` Jeff Layton 1 sibling, 0 replies; 17+ messages in thread From: Jeff Layton @ 2014-02-01 13:57 UTC (permalink / raw) To: One Thousand Gnomes Cc: Pavel Shilovsky, linux-kernel, linux-cifs, linux-fsdevel, linux-nfs, wine-devel On Fri, 17 Jan 2014 18:18:47 +0000 One Thousand Gnomes <gnomes@lxorguk.ukuu.org.uk> wrote: > > +#define ESHAREDENIED 258 /* File is locked with a sharelock */ > > Have you prepared C library patches to match this ? > > (and why not just use EPERM, it has the meaning you want already) > Tough call... On the one hand, ESHAREDENIED is a distinct error code so an application has the ability to determine what happened when an open or unlink fails. OTOH, a lot of applications won't understand ESHAREDENIED and may barf on it. Those apps might handle EPERM better. I'm not sure what the right approach is there... > > > + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE > > + * tell us whether the reservation allows other readers and writers. > > + */ > > +static int > > +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) > > +{ > > Shouldn't this also check for CAP_SYS_DAC or some similar permission so > that root can override such a mess (eg to fix full disks in an > emergency) ? > > Agreed. This needs a mechanism that allows you to override it, IMO. CAP_DAC_OVERRIDE doesn't seem quite like the right thing since this isn't dealing with permissions, per-se. A new capability bit may even be warranted. > > + > > + /* > > + * For sharelock mounts if a file was created but not opened, we need > > + * to keep parent i_mutex until we finish the open to prevent races when > > + * somebody opens newly created by us file and locks it with a sharelock > > + * before we open it. > > + */ > > + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { > > + /* Don't check for write permission, don't truncate */ > > + open_flag &= ~O_TRUNC; > > + will_truncate = false; > > + acc_mode = MAY_OPEN; > > + path_to_nameidata(path, nd); > > + > > + error = may_open(&nd->path, acc_mode, open_flag); > > + if (error) { > > + mutex_unlock(&dir->d_inode->i_mutex); > > + goto out; > > + } > > + file->f_path.mnt = nd->path.mnt; > > + error = finish_open(file, nd->path.dentry, NULL, opened); > > + if (error) { > > + mutex_unlock(&dir->d_inode->i_mutex); > > + if (error == -EOPENSTALE) > > + goto stale_open; > > + goto out; > > + } > > + error = sharelock_lock_file(file); > > + mutex_unlock(&dir->d_inode->i_mutex); > > + if (error) > > + goto exit_fput; > > + goto opened; > > + } > > + > > mutex_unlock(&dir->d_inode->i_mutex); > > What stops the file system changing mount flags via a remount between > these two ? > > > > > if (error <= 0) { > > @@ -3034,6 +3073,18 @@ finish_open_created: > > goto stale_open; > > goto out; > > } > > + > > + if (IS_SHARELOCK(dir->d_inode)) { > > + /* > > + * Lock parent i_mutex to prevent races with sharelocks on > > + * newly created files. > > + */ > > + mutex_lock(&dir->d_inode->i_mutex); > > + error = sharelock_lock_file(file); > > + mutex_unlock(&dir->d_inode->i_mutex); > > + if (error) > > + goto exit_fput; > > + } > > opened: > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Jeff Layton <jlayton@redhat.com> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-01-17 10:07 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky 2014-01-17 18:18 ` One Thousand Gnomes @ 2014-02-01 13:20 ` Jeff Layton 2014-02-04 12:03 ` Pavel Shilovsky 1 sibling, 1 reply; 17+ messages in thread From: Jeff Layton @ 2014-02-01 13:20 UTC (permalink / raw) To: Pavel Shilovsky Cc: linux-kernel, linux-cifs, linux-fsdevel, linux-nfs, wine-devel On Fri, 17 Jan 2014 14:07:06 +0400 Pavel Shilovsky <piastry@etersoft.ru> wrote: > This patch adds 3 flags: > 1) O_DENYREAD that doesn't permit read access, > 2) O_DENYWRITE that doesn't permit write access, > 3) O_DENYDELETE that doesn't permit delete or rename. > > Network filesystems CIFS, SMB2.0, SMB3.0 and NFSv4 have such flags - > this change can benefit cifs and nfs modules as well as Samba and > NFS file servers that export the same directory for Windows clients, > or Wine applications that access the same files simultaneously. > > These flags are only take affect for opens on mounts with new sharelock > option. They are translated to flock's flags: > > !O_DENYREAD -> LOCK_READ | LOCK_MAND > !O_DENYWRITE -> LOCK_WRITE | LOCK_MAND > > and set through flock_lock_file on a file. If the file can't be locked > due conflicts with another open with O_DENY* flags, a new -ESHAREDENIED > error code is returned. > > Create codepath is slightly changed to prevent data races on newly > created files: when open with O_CREAT can return -ESHAREDENIED error > for successfully created files due to a sharelock set by another task. > > Temporary disable O_DENYDELETE support - will enable it in further > patches. > > Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> > --- > arch/alpha/include/uapi/asm/errno.h | 2 + > arch/alpha/include/uapi/asm/fcntl.h | 3 ++ > arch/mips/include/uapi/asm/errno.h | 2 + > arch/parisc/include/uapi/asm/errno.h | 2 + > arch/parisc/include/uapi/asm/fcntl.h | 3 ++ > arch/sparc/include/uapi/asm/errno.h | 2 + > arch/sparc/include/uapi/asm/fcntl.h | 3 ++ > fs/fcntl.c | 5 +- > fs/locks.c | 97 +++++++++++++++++++++++++++++++--- > fs/namei.c | 53 ++++++++++++++++++- > fs/proc_namespace.c | 1 + > include/linux/fs.h | 8 +++ > include/uapi/asm-generic/errno.h | 2 + > include/uapi/asm-generic/fcntl.h | 11 ++++ > include/uapi/linux/fs.h | 1 + > 15 files changed, 185 insertions(+), 10 deletions(-) > You might consider breaking this patch into two. One patch that makes LOCK_MAND locks actually work and that adds MS_SHARELOCK, and one patch that hooks that up to open(). Given the locking involved with the i_mutex it would be best to present this as a series of small, incremental changes. > diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h > index 17f92aa..953a6d6 100644 > --- a/arch/alpha/include/uapi/asm/errno.h > +++ b/arch/alpha/include/uapi/asm/errno.h > @@ -124,4 +124,6 @@ > > #define EHWPOISON 139 /* Memory page has hardware error */ > > +#define ESHAREDENIED 140 /* File is locked with a sharelock */ > + > #endif > diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h > index 09f49a6..265344b 100644 > --- a/arch/alpha/include/uapi/asm/fcntl.h > +++ b/arch/alpha/include/uapi/asm/fcntl.h > @@ -33,6 +33,9 @@ > > #define O_PATH 040000000 > #define __O_TMPFILE 0100000000 > +#define O_DENYREAD 0200000000 /* Do not permit read access */ > +#define O_DENYWRITE 0400000000 /* Do not permit write access */ > +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ > > #define F_GETLK 7 > #define F_SETLK 8 > diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h > index 02d645d..f1a4068 100644 > --- a/arch/mips/include/uapi/asm/errno.h > +++ b/arch/mips/include/uapi/asm/errno.h > @@ -123,6 +123,8 @@ > > #define EHWPOISON 168 /* Memory page has hardware error */ > > +#define ESHAREDENIED 169 /* File is locked with a sharelock */ > + > #define EDQUOT 1133 /* Quota exceeded */ > > > diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h > index f3a8aa5..654c232 100644 > --- a/arch/parisc/include/uapi/asm/errno.h > +++ b/arch/parisc/include/uapi/asm/errno.h > @@ -124,4 +124,6 @@ > > #define EHWPOISON 257 /* Memory page has hardware error */ > > +#define ESHAREDENIED 258 /* File is locked with a sharelock */ > + > #endif > diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h > index 34a46cb..5865964 100644 > --- a/arch/parisc/include/uapi/asm/fcntl.h > +++ b/arch/parisc/include/uapi/asm/fcntl.h > @@ -21,6 +21,9 @@ > > #define O_PATH 020000000 > #define __O_TMPFILE 040000000 > +#define O_DENYREAD 0200000000 /* Do not permit read access */ > +#define O_DENYWRITE 0400000000 /* Do not permit write access */ > +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ > > #define F_GETLK64 8 > #define F_SETLK64 9 > diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h > index 20423e17..fe339b5 100644 > --- a/arch/sparc/include/uapi/asm/errno.h > +++ b/arch/sparc/include/uapi/asm/errno.h > @@ -114,4 +114,6 @@ > > #define EHWPOISON 135 /* Memory page has hardware error */ > > +#define ESHAREDENIED 136 /* File is locked with a sharelock */ > + > #endif > diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h > index 7e8ace5..ab68170 100644 > --- a/arch/sparc/include/uapi/asm/fcntl.h > +++ b/arch/sparc/include/uapi/asm/fcntl.h > @@ -36,6 +36,9 @@ > > #define O_PATH 0x1000000 > #define __O_TMPFILE 0x2000000 > +#define O_DENYREAD 0x4000000 /* Do not permit read access */ > +#define O_DENYWRITE 0x8000000 /* Do not permit write access */ > +#define O_DENYDELETE 0x10000000 /* Do not permit delete or rename */ > It'd probably be best to add O_DENYDELETE in a separate patch, rather than disabling it temporarily. > #define F_GETOWN 5 /* for sockets. */ > #define F_SETOWN 6 /* for sockets. */ > diff --git a/fs/fcntl.c b/fs/fcntl.c > index ef68665..3f85887 100644 > --- a/fs/fcntl.c > +++ b/fs/fcntl.c > @@ -729,14 +729,15 @@ 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(20 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( > + BUILD_BUG_ON(23 - 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 | O_PATH | __O_TMPFILE > + __FMODE_EXEC | O_PATH | __O_TMPFILE | > + O_DENYREAD | O_DENYWRITE | O_DENYDELETE > )); > > fasync_cache = kmem_cache_create("fasync_cache", > diff --git a/fs/locks.c b/fs/locks.c > index 92a0f0a..ffde4d4 100644 > --- a/fs/locks.c > +++ b/fs/locks.c > @@ -708,20 +708,73 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s > return (locks_conflict(caller_fl, sys_fl)); > } > > -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific > - * checking before calling the locks_conflict(). > +static unsigned int > +deny_flags_to_cmd(unsigned int flags) > +{ > + unsigned int cmd = LOCK_MAND; > + > + if (!(flags & O_DENYREAD)) > + cmd |= LOCK_READ; > + if (!(flags & O_DENYWRITE)) > + cmd |= LOCK_WRITE; > + > + return cmd; > +} > + > +/* > + * locks_mand_conflict - Determine if there's a share reservation conflict > + * @caller_fl: lock we're attempting to acquire > + * @sys_fl: lock already present on system that we're checking against > + * > + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE > + * tell us whether the reservation allows other readers and writers. > + */ > +static int > +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) > +{ > + unsigned char caller_type = caller_fl->fl_type; > + unsigned char sys_type = sys_fl->fl_type; > + fmode_t caller_fmode = caller_fl->fl_file->f_mode; > + fmode_t sys_fmode = sys_fl->fl_file->f_mode; > + > + /* they can only conflict if FS is mounted with MS_SHARELOCK */ > + if (!IS_SHARELOCK(caller_fl->fl_file->f_path.dentry->d_inode)) > + return 0; > + > + /* they can only conflict if they're both LOCK_MAND */ > + if (!(caller_type & LOCK_MAND) || !(sys_type & LOCK_MAND)) > + return 0; > + > + if (!(caller_type & LOCK_READ) && (sys_fmode & FMODE_READ)) > + return 1; > + if (!(caller_type & LOCK_WRITE) && (sys_fmode & FMODE_WRITE)) > + return 1; > + if (!(sys_type & LOCK_READ) && (caller_fmode & FMODE_READ)) > + return 1; > + if (!(sys_type & LOCK_WRITE) && (caller_fmode & FMODE_WRITE)) > + return 1; > + > + return 0; > +} > + > +/* > + * Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking > + * before calling the locks_conflict(). > */ > static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) > { > - /* FLOCK locks referring to the same filp do not conflict with > + if (!IS_FLOCK(sys_fl)) > + return 0; > + if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) > + return locks_mand_conflict(caller_fl, sys_fl); nit: Seems like the above could be optimized a little. You know that locks_mand_conflict is only relevant if both are LOCK_MAND, and one of the first things that locks_mand_conflict does is to check that both have that set. > + /* > + * FLOCK locks referring to the same filp do not conflict with > * each other. > */ > - if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) > - return (0); > - if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) > + if (caller_fl->fl_file == sys_fl->fl_file) > return 0; > > - return (locks_conflict(caller_fl, sys_fl)); > + return locks_conflict(caller_fl, sys_fl); > } > > void > @@ -888,6 +941,36 @@ out: > return error; > } > > +/* > + * Determine if a file is allowed to be opened with specified access and share > + * modes. Lock the file and return 0 if checks passed, otherwise return > + * -ESHAREDENIED. > + */ > +int > +sharelock_lock_file(struct file *filp) > +{ > + struct file_lock *lock; > + int error = 0; > + > + if (!IS_SHARELOCK(filp->f_path.dentry->d_inode)) > + return error; > + > + /* Disable O_DENYDELETE support for now */ > + if (filp->f_flags & O_DENYDELETE) > + return -EINVAL; > + > + error = flock_make_lock(filp, &lock, deny_flags_to_cmd(filp->f_flags)); > + if (error) > + return error; > + > + error = flock_lock_file(filp, lock); > + if (error == -EAGAIN) > + error = -ESHAREDENIED; > + > + locks_free_lock(lock); > + return error; > +} > + > static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) > { > struct file_lock *fl; > diff --git a/fs/namei.c b/fs/namei.c > index 3531dee..2b741a1 100644 > --- a/fs/namei.c > +++ b/fs/namei.c > @@ -2725,9 +2725,14 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, > acc_mode = MAY_OPEN; > } > error = may_open(&file->f_path, acc_mode, open_flag); > - if (error) > + if (error) { > fput(file); > + goto out; > + } > > + error = sharelock_lock_file(file); > + if (error) > + fput(file); > out: > dput(dentry); > return error; > @@ -2919,6 +2924,40 @@ retry_lookup: > } > mutex_lock(&dir->d_inode->i_mutex); > error = lookup_open(nd, path, file, op, got_write, opened); > + > + /* > + * For sharelock mounts if a file was created but not opened, we need > + * to keep parent i_mutex until we finish the open to prevent races when > + * somebody opens newly created by us file and locks it with a sharelock > + * before we open it. > + */ > + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { > + /* Don't check for write permission, don't truncate */ > + open_flag &= ~O_TRUNC; > + will_truncate = false; > + acc_mode = MAY_OPEN; > + path_to_nameidata(path, nd); > + > + error = may_open(&nd->path, acc_mode, open_flag); > + if (error) { > + mutex_unlock(&dir->d_inode->i_mutex); > + goto out; > + } > + file->f_path.mnt = nd->path.mnt; > + error = finish_open(file, nd->path.dentry, NULL, opened); > + if (error) { > + mutex_unlock(&dir->d_inode->i_mutex); > + if (error == -EOPENSTALE) > + goto stale_open; > + goto out; > + } > + error = sharelock_lock_file(file); > + mutex_unlock(&dir->d_inode->i_mutex); > + if (error) > + goto exit_fput; > + goto opened; > + } > + > mutex_unlock(&dir->d_inode->i_mutex); > > if (error <= 0) { > @@ -3034,6 +3073,18 @@ finish_open_created: > goto stale_open; > goto out; > } > + > + if (IS_SHARELOCK(dir->d_inode)) { > + /* > + * Lock parent i_mutex to prevent races with sharelocks on > + * newly created files. > + */ > + mutex_lock(&dir->d_inode->i_mutex); > + error = sharelock_lock_file(file); > + mutex_unlock(&dir->d_inode->i_mutex); > + if (error) > + goto exit_fput; > + } > opened: > error = open_check_o_direct(file); > if (error) > diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c > index 439406e..dd374d4 100644 > --- a/fs/proc_namespace.c > +++ b/fs/proc_namespace.c > @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) > { MS_SYNCHRONOUS, ",sync" }, > { MS_DIRSYNC, ",dirsync" }, > { MS_MANDLOCK, ",mand" }, > + { MS_SHARELOCK, ",sharelock" }, > { 0, NULL } > }; > const struct proc_fs_info *fs_infop; > diff --git a/include/linux/fs.h b/include/linux/fs.h > index 121f11f..aa061ca 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -1029,6 +1029,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); > extern int lease_modify(struct file_lock **, int); > extern int lock_may_read(struct inode *, loff_t start, unsigned long count); > extern int lock_may_write(struct inode *, loff_t start, unsigned long count); > +extern int sharelock_lock_file(struct file *); > #else /* !CONFIG_FILE_LOCKING */ > static inline int fcntl_getlk(struct file *file, struct flock __user *user) > { > @@ -1169,6 +1170,12 @@ static inline int lock_may_write(struct inode *inode, loff_t start, > { > return 1; > } > + > +static inline int sharelock_lock_file(struct file *filp) > +{ > + return 0; > +} > + > #endif /* !CONFIG_FILE_LOCKING */ > > > @@ -1675,6 +1682,7 @@ struct super_operations { > #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) > #define IS_IMA(inode) ((inode)->i_flags & S_IMA) > #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) > +#define IS_SHARELOCK(inode) __IS_FLG(inode, MS_SHARELOCK) > #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) > > /* > diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h > index 1e1ea6e..aff869c 100644 > --- a/include/uapi/asm-generic/errno.h > +++ b/include/uapi/asm-generic/errno.h > @@ -110,4 +110,6 @@ > > #define EHWPOISON 133 /* Memory page has hardware error */ > > +#define ESHAREDENIED 134 /* File is locked with a sharelock */ > + > #endif > diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h > index 95e46c8..9881cfe 100644 > --- a/include/uapi/asm-generic/fcntl.h > +++ b/include/uapi/asm-generic/fcntl.h > @@ -92,6 +92,17 @@ > #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) > #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) > > +#ifndef O_DENYREAD > +#define O_DENYREAD 040000000 /* Do not permit read access */ > +#endif > +/* FMODE_NONOTIFY 0100000000 */ > +#ifndef O_DENYWRITE > +#define O_DENYWRITE 0200000000 /* Do not permit write access */ > +#endif > +#ifndef O_DENYDELETE > +#define O_DENYDELETE 0400000000 /* Do not permit delete or rename */ > +#endif > + One thing to consider: We found with the addition of O_TMPFILE that the open() api is not particularly helpful when it comes to informing appications when a flag isn't supported: http://lwn.net/Articles/562294/ ...having a plan to cope with that here would be best. How can an application determine at runtime that O_DENY* actually *work*? It may be best to step back and consider a new syscall for this (open2() ?). > #ifndef O_NDELAY > #define O_NDELAY O_NONBLOCK > #endif > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h > index 6c28b61..11f0ecf 100644 > --- a/include/uapi/linux/fs.h > +++ b/include/uapi/linux/fs.h > @@ -86,6 +86,7 @@ struct inodes_stat_t { > #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ > #define MS_I_VERSION (1<<23) /* Update inode I_version field */ > #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ > +#define MS_SHARELOCK (1<<25) /* Allow share locks on an FS */ > > /* These sb flags are internal to the kernel */ > #define MS_NOSEC (1<<28) -- Jeff Layton <jlayton@redhat.com> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-02-01 13:20 ` Jeff Layton @ 2014-02-04 12:03 ` Pavel Shilovsky 2014-02-04 12:21 ` Jeff Layton 0 siblings, 1 reply; 17+ messages in thread From: Pavel Shilovsky @ 2014-02-04 12:03 UTC (permalink / raw) To: Jeff Layton Cc: Kernel Mailing List, linux-cifs, linux-fsdevel, Linux NFS Mailing list, wine-devel 2014-02-01 Jeff Layton <jlayton@redhat.com>: > On Fri, 17 Jan 2014 14:07:06 +0400 > Pavel Shilovsky <piastry@etersoft.ru> wrote: > >> This patch adds 3 flags: >> 1) O_DENYREAD that doesn't permit read access, >> 2) O_DENYWRITE that doesn't permit write access, >> 3) O_DENYDELETE that doesn't permit delete or rename. >> >> Network filesystems CIFS, SMB2.0, SMB3.0 and NFSv4 have such flags - >> this change can benefit cifs and nfs modules as well as Samba and >> NFS file servers that export the same directory for Windows clients, >> or Wine applications that access the same files simultaneously. >> >> These flags are only take affect for opens on mounts with new sharelock >> option. They are translated to flock's flags: >> >> !O_DENYREAD -> LOCK_READ | LOCK_MAND >> !O_DENYWRITE -> LOCK_WRITE | LOCK_MAND >> >> and set through flock_lock_file on a file. If the file can't be locked >> due conflicts with another open with O_DENY* flags, a new -ESHAREDENIED >> error code is returned. >> >> Create codepath is slightly changed to prevent data races on newly >> created files: when open with O_CREAT can return -ESHAREDENIED error >> for successfully created files due to a sharelock set by another task. >> >> Temporary disable O_DENYDELETE support - will enable it in further >> patches. >> >> Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> >> --- >> arch/alpha/include/uapi/asm/errno.h | 2 + >> arch/alpha/include/uapi/asm/fcntl.h | 3 ++ >> arch/mips/include/uapi/asm/errno.h | 2 + >> arch/parisc/include/uapi/asm/errno.h | 2 + >> arch/parisc/include/uapi/asm/fcntl.h | 3 ++ >> arch/sparc/include/uapi/asm/errno.h | 2 + >> arch/sparc/include/uapi/asm/fcntl.h | 3 ++ >> fs/fcntl.c | 5 +- >> fs/locks.c | 97 +++++++++++++++++++++++++++++++--- >> fs/namei.c | 53 ++++++++++++++++++- >> fs/proc_namespace.c | 1 + >> include/linux/fs.h | 8 +++ >> include/uapi/asm-generic/errno.h | 2 + >> include/uapi/asm-generic/fcntl.h | 11 ++++ >> include/uapi/linux/fs.h | 1 + >> 15 files changed, 185 insertions(+), 10 deletions(-) >> > > You might consider breaking this patch into two. One patch that makes > LOCK_MAND locks actually work and that adds MS_SHARELOCK, and one patch > that hooks that up to open(). Given the locking involved with the > i_mutex it would be best to present this as a series of small, > incremental changes. Good point. So, we can break it into 2: 1) make flock actually work with LOCK_MAND on MS_SHARELOCK mounts, 2) replace flock+LOCK_MAND with open+O_DENY* flags. >> diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h >> index 17f92aa..953a6d6 100644 >> --- a/arch/alpha/include/uapi/asm/errno.h >> +++ b/arch/alpha/include/uapi/asm/errno.h >> @@ -124,4 +124,6 @@ >> >> #define EHWPOISON 139 /* Memory page has hardware error */ >> >> +#define ESHAREDENIED 140 /* File is locked with a sharelock */ >> + >> #endif >> diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h >> index 09f49a6..265344b 100644 >> --- a/arch/alpha/include/uapi/asm/fcntl.h >> +++ b/arch/alpha/include/uapi/asm/fcntl.h >> @@ -33,6 +33,9 @@ >> >> #define O_PATH 040000000 >> #define __O_TMPFILE 0100000000 >> +#define O_DENYREAD 0200000000 /* Do not permit read access */ >> +#define O_DENYWRITE 0400000000 /* Do not permit write access */ >> +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ >> >> #define F_GETLK 7 >> #define F_SETLK 8 >> diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h >> index 02d645d..f1a4068 100644 >> --- a/arch/mips/include/uapi/asm/errno.h >> +++ b/arch/mips/include/uapi/asm/errno.h >> @@ -123,6 +123,8 @@ >> >> #define EHWPOISON 168 /* Memory page has hardware error */ >> >> +#define ESHAREDENIED 169 /* File is locked with a sharelock */ >> + >> #define EDQUOT 1133 /* Quota exceeded */ >> >> >> diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h >> index f3a8aa5..654c232 100644 >> --- a/arch/parisc/include/uapi/asm/errno.h >> +++ b/arch/parisc/include/uapi/asm/errno.h >> @@ -124,4 +124,6 @@ >> >> #define EHWPOISON 257 /* Memory page has hardware error */ >> >> +#define ESHAREDENIED 258 /* File is locked with a sharelock */ >> + >> #endif >> diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h >> index 34a46cb..5865964 100644 >> --- a/arch/parisc/include/uapi/asm/fcntl.h >> +++ b/arch/parisc/include/uapi/asm/fcntl.h >> @@ -21,6 +21,9 @@ >> >> #define O_PATH 020000000 >> #define __O_TMPFILE 040000000 >> +#define O_DENYREAD 0200000000 /* Do not permit read access */ >> +#define O_DENYWRITE 0400000000 /* Do not permit write access */ >> +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ >> >> #define F_GETLK64 8 >> #define F_SETLK64 9 >> diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h >> index 20423e17..fe339b5 100644 >> --- a/arch/sparc/include/uapi/asm/errno.h >> +++ b/arch/sparc/include/uapi/asm/errno.h >> @@ -114,4 +114,6 @@ >> >> #define EHWPOISON 135 /* Memory page has hardware error */ >> >> +#define ESHAREDENIED 136 /* File is locked with a sharelock */ >> + >> #endif >> diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h >> index 7e8ace5..ab68170 100644 >> --- a/arch/sparc/include/uapi/asm/fcntl.h >> +++ b/arch/sparc/include/uapi/asm/fcntl.h >> @@ -36,6 +36,9 @@ >> >> #define O_PATH 0x1000000 >> #define __O_TMPFILE 0x2000000 >> +#define O_DENYREAD 0x4000000 /* Do not permit read access */ >> +#define O_DENYWRITE 0x8000000 /* Do not permit write access */ >> +#define O_DENYDELETE 0x10000000 /* Do not permit delete or rename */ >> > > It'd probably be best to add O_DENYDELETE in a separate patch, rather > than disabling it temporarily. Agree. > >> #define F_GETOWN 5 /* for sockets. */ >> #define F_SETOWN 6 /* for sockets. */ >> diff --git a/fs/fcntl.c b/fs/fcntl.c >> index ef68665..3f85887 100644 >> --- a/fs/fcntl.c >> +++ b/fs/fcntl.c >> @@ -729,14 +729,15 @@ 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(20 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( >> + BUILD_BUG_ON(23 - 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 | O_PATH | __O_TMPFILE >> + __FMODE_EXEC | O_PATH | __O_TMPFILE | >> + O_DENYREAD | O_DENYWRITE | O_DENYDELETE >> )); >> >> fasync_cache = kmem_cache_create("fasync_cache", >> diff --git a/fs/locks.c b/fs/locks.c >> index 92a0f0a..ffde4d4 100644 >> --- a/fs/locks.c >> +++ b/fs/locks.c >> @@ -708,20 +708,73 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s >> return (locks_conflict(caller_fl, sys_fl)); >> } >> >> -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific >> - * checking before calling the locks_conflict(). >> +static unsigned int >> +deny_flags_to_cmd(unsigned int flags) >> +{ >> + unsigned int cmd = LOCK_MAND; >> + >> + if (!(flags & O_DENYREAD)) >> + cmd |= LOCK_READ; >> + if (!(flags & O_DENYWRITE)) >> + cmd |= LOCK_WRITE; >> + >> + return cmd; >> +} >> + >> +/* >> + * locks_mand_conflict - Determine if there's a share reservation conflict >> + * @caller_fl: lock we're attempting to acquire >> + * @sys_fl: lock already present on system that we're checking against >> + * >> + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE >> + * tell us whether the reservation allows other readers and writers. >> + */ >> +static int >> +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) >> +{ >> + unsigned char caller_type = caller_fl->fl_type; >> + unsigned char sys_type = sys_fl->fl_type; >> + fmode_t caller_fmode = caller_fl->fl_file->f_mode; >> + fmode_t sys_fmode = sys_fl->fl_file->f_mode; >> + >> + /* they can only conflict if FS is mounted with MS_SHARELOCK */ >> + if (!IS_SHARELOCK(caller_fl->fl_file->f_path.dentry->d_inode)) >> + return 0; >> + >> + /* they can only conflict if they're both LOCK_MAND */ >> + if (!(caller_type & LOCK_MAND) || !(sys_type & LOCK_MAND)) >> + return 0; >> + >> + if (!(caller_type & LOCK_READ) && (sys_fmode & FMODE_READ)) >> + return 1; >> + if (!(caller_type & LOCK_WRITE) && (sys_fmode & FMODE_WRITE)) >> + return 1; >> + if (!(sys_type & LOCK_READ) && (caller_fmode & FMODE_READ)) >> + return 1; >> + if (!(sys_type & LOCK_WRITE) && (caller_fmode & FMODE_WRITE)) >> + return 1; >> + >> + return 0; >> +} >> + >> +/* >> + * Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking >> + * before calling the locks_conflict(). >> */ >> static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) >> { >> - /* FLOCK locks referring to the same filp do not conflict with >> + if (!IS_FLOCK(sys_fl)) >> + return 0; >> + if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) >> + return locks_mand_conflict(caller_fl, sys_fl); > > nit: Seems like the above could be optimized a little. You know that > locks_mand_conflict is only relevant if both are LOCK_MAND, and one of > the first things that locks_mand_conflict does is to check that both > have that set. ok. > >> + /* >> + * FLOCK locks referring to the same filp do not conflict with >> * each other. >> */ >> - if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) >> - return (0); >> - if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) >> + if (caller_fl->fl_file == sys_fl->fl_file) >> return 0; >> >> - return (locks_conflict(caller_fl, sys_fl)); >> + return locks_conflict(caller_fl, sys_fl); >> } >> >> void >> @@ -888,6 +941,36 @@ out: >> return error; >> } >> >> +/* >> + * Determine if a file is allowed to be opened with specified access and share >> + * modes. Lock the file and return 0 if checks passed, otherwise return >> + * -ESHAREDENIED. >> + */ >> +int >> +sharelock_lock_file(struct file *filp) >> +{ >> + struct file_lock *lock; >> + int error = 0; >> + >> + if (!IS_SHARELOCK(filp->f_path.dentry->d_inode)) >> + return error; >> + >> + /* Disable O_DENYDELETE support for now */ >> + if (filp->f_flags & O_DENYDELETE) >> + return -EINVAL; >> + >> + error = flock_make_lock(filp, &lock, deny_flags_to_cmd(filp->f_flags)); >> + if (error) >> + return error; >> + >> + error = flock_lock_file(filp, lock); >> + if (error == -EAGAIN) >> + error = -ESHAREDENIED; >> + >> + locks_free_lock(lock); >> + return error; >> +} >> + >> static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) >> { >> struct file_lock *fl; >> diff --git a/fs/namei.c b/fs/namei.c >> index 3531dee..2b741a1 100644 >> --- a/fs/namei.c >> +++ b/fs/namei.c >> @@ -2725,9 +2725,14 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, >> acc_mode = MAY_OPEN; >> } >> error = may_open(&file->f_path, acc_mode, open_flag); >> - if (error) >> + if (error) { >> fput(file); >> + goto out; >> + } >> >> + error = sharelock_lock_file(file); >> + if (error) >> + fput(file); >> out: >> dput(dentry); >> return error; >> @@ -2919,6 +2924,40 @@ retry_lookup: >> } >> mutex_lock(&dir->d_inode->i_mutex); >> error = lookup_open(nd, path, file, op, got_write, opened); >> + >> + /* >> + * For sharelock mounts if a file was created but not opened, we need >> + * to keep parent i_mutex until we finish the open to prevent races when >> + * somebody opens newly created by us file and locks it with a sharelock >> + * before we open it. >> + */ >> + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { >> + /* Don't check for write permission, don't truncate */ >> + open_flag &= ~O_TRUNC; >> + will_truncate = false; >> + acc_mode = MAY_OPEN; >> + path_to_nameidata(path, nd); >> + >> + error = may_open(&nd->path, acc_mode, open_flag); >> + if (error) { >> + mutex_unlock(&dir->d_inode->i_mutex); >> + goto out; >> + } >> + file->f_path.mnt = nd->path.mnt; >> + error = finish_open(file, nd->path.dentry, NULL, opened); >> + if (error) { >> + mutex_unlock(&dir->d_inode->i_mutex); >> + if (error == -EOPENSTALE) >> + goto stale_open; >> + goto out; >> + } >> + error = sharelock_lock_file(file); >> + mutex_unlock(&dir->d_inode->i_mutex); >> + if (error) >> + goto exit_fput; >> + goto opened; >> + } >> + >> mutex_unlock(&dir->d_inode->i_mutex); >> >> if (error <= 0) { >> @@ -3034,6 +3073,18 @@ finish_open_created: >> goto stale_open; >> goto out; >> } >> + >> + if (IS_SHARELOCK(dir->d_inode)) { >> + /* >> + * Lock parent i_mutex to prevent races with sharelocks on >> + * newly created files. >> + */ >> + mutex_lock(&dir->d_inode->i_mutex); >> + error = sharelock_lock_file(file); >> + mutex_unlock(&dir->d_inode->i_mutex); >> + if (error) >> + goto exit_fput; >> + } >> opened: >> error = open_check_o_direct(file); >> if (error) >> diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c >> index 439406e..dd374d4 100644 >> --- a/fs/proc_namespace.c >> +++ b/fs/proc_namespace.c >> @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) >> { MS_SYNCHRONOUS, ",sync" }, >> { MS_DIRSYNC, ",dirsync" }, >> { MS_MANDLOCK, ",mand" }, >> + { MS_SHARELOCK, ",sharelock" }, >> { 0, NULL } >> }; >> const struct proc_fs_info *fs_infop; >> diff --git a/include/linux/fs.h b/include/linux/fs.h >> index 121f11f..aa061ca 100644 >> --- a/include/linux/fs.h >> +++ b/include/linux/fs.h >> @@ -1029,6 +1029,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); >> extern int lease_modify(struct file_lock **, int); >> extern int lock_may_read(struct inode *, loff_t start, unsigned long count); >> extern int lock_may_write(struct inode *, loff_t start, unsigned long count); >> +extern int sharelock_lock_file(struct file *); >> #else /* !CONFIG_FILE_LOCKING */ >> static inline int fcntl_getlk(struct file *file, struct flock __user *user) >> { >> @@ -1169,6 +1170,12 @@ static inline int lock_may_write(struct inode *inode, loff_t start, >> { >> return 1; >> } >> + >> +static inline int sharelock_lock_file(struct file *filp) >> +{ >> + return 0; >> +} >> + >> #endif /* !CONFIG_FILE_LOCKING */ >> >> >> @@ -1675,6 +1682,7 @@ struct super_operations { >> #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) >> #define IS_IMA(inode) ((inode)->i_flags & S_IMA) >> #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) >> +#define IS_SHARELOCK(inode) __IS_FLG(inode, MS_SHARELOCK) >> #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) >> >> /* >> diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h >> index 1e1ea6e..aff869c 100644 >> --- a/include/uapi/asm-generic/errno.h >> +++ b/include/uapi/asm-generic/errno.h >> @@ -110,4 +110,6 @@ >> >> #define EHWPOISON 133 /* Memory page has hardware error */ >> >> +#define ESHAREDENIED 134 /* File is locked with a sharelock */ >> + >> #endif >> diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h >> index 95e46c8..9881cfe 100644 >> --- a/include/uapi/asm-generic/fcntl.h >> +++ b/include/uapi/asm-generic/fcntl.h >> @@ -92,6 +92,17 @@ >> #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) >> #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) >> >> +#ifndef O_DENYREAD >> +#define O_DENYREAD 040000000 /* Do not permit read access */ >> +#endif >> +/* FMODE_NONOTIFY 0100000000 */ >> +#ifndef O_DENYWRITE >> +#define O_DENYWRITE 0200000000 /* Do not permit write access */ >> +#endif >> +#ifndef O_DENYDELETE >> +#define O_DENYDELETE 0400000000 /* Do not permit delete or rename */ >> +#endif >> + > > One thing to consider: We found with the addition of O_TMPFILE that the > open() api is not particularly helpful when it comes to informing > appications when a flag isn't supported: > > http://lwn.net/Articles/562294/ > > ...having a plan to cope with that here would be best. How can an > application determine at runtime that O_DENY* actually *work*? It may > be best to step back and consider a new syscall for this (open2() ?). > So, consider we added new syscall: opendm(filename, flags, mode, deny_mode) { return open(filename, flags | denymode2openflags(deny_mode), mode) } where deny_mode can be DMODE_NONE (0), DMODE_READ (1), DMODE_WRITE(2) and DMODE_RDWR(3) (similar to FMODE_* values). We have open and opendm that act actually in the same manner for mounts without MS_SHARELOCK. For mounts with MS_SHARELOCK open is like opendm with DMODE_NONE. Open flags O_DENY* are for internal use only. Is it what you suggest? -- Best regards, Pavel Shilovsky. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags 2014-02-04 12:03 ` Pavel Shilovsky @ 2014-02-04 12:21 ` Jeff Layton 0 siblings, 0 replies; 17+ messages in thread From: Jeff Layton @ 2014-02-04 12:21 UTC (permalink / raw) To: Pavel Shilovsky Cc: Kernel Mailing List, linux-cifs, linux-fsdevel, Linux NFS Mailing list, wine-devel On Tue, 4 Feb 2014 16:03:14 +0400 Pavel Shilovsky <piastry@etersoft.ru> wrote: > 2014-02-01 Jeff Layton <jlayton@redhat.com>: > > On Fri, 17 Jan 2014 14:07:06 +0400 > > Pavel Shilovsky <piastry@etersoft.ru> wrote: > > > >> This patch adds 3 flags: > >> 1) O_DENYREAD that doesn't permit read access, > >> 2) O_DENYWRITE that doesn't permit write access, > >> 3) O_DENYDELETE that doesn't permit delete or rename. > >> > >> Network filesystems CIFS, SMB2.0, SMB3.0 and NFSv4 have such flags - > >> this change can benefit cifs and nfs modules as well as Samba and > >> NFS file servers that export the same directory for Windows clients, > >> or Wine applications that access the same files simultaneously. > >> > >> These flags are only take affect for opens on mounts with new sharelock > >> option. They are translated to flock's flags: > >> > >> !O_DENYREAD -> LOCK_READ | LOCK_MAND > >> !O_DENYWRITE -> LOCK_WRITE | LOCK_MAND > >> > >> and set through flock_lock_file on a file. If the file can't be locked > >> due conflicts with another open with O_DENY* flags, a new -ESHAREDENIED > >> error code is returned. > >> > >> Create codepath is slightly changed to prevent data races on newly > >> created files: when open with O_CREAT can return -ESHAREDENIED error > >> for successfully created files due to a sharelock set by another task. > >> > >> Temporary disable O_DENYDELETE support - will enable it in further > >> patches. > >> > >> Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> > >> --- > >> arch/alpha/include/uapi/asm/errno.h | 2 + > >> arch/alpha/include/uapi/asm/fcntl.h | 3 ++ > >> arch/mips/include/uapi/asm/errno.h | 2 + > >> arch/parisc/include/uapi/asm/errno.h | 2 + > >> arch/parisc/include/uapi/asm/fcntl.h | 3 ++ > >> arch/sparc/include/uapi/asm/errno.h | 2 + > >> arch/sparc/include/uapi/asm/fcntl.h | 3 ++ > >> fs/fcntl.c | 5 +- > >> fs/locks.c | 97 +++++++++++++++++++++++++++++++--- > >> fs/namei.c | 53 ++++++++++++++++++- > >> fs/proc_namespace.c | 1 + > >> include/linux/fs.h | 8 +++ > >> include/uapi/asm-generic/errno.h | 2 + > >> include/uapi/asm-generic/fcntl.h | 11 ++++ > >> include/uapi/linux/fs.h | 1 + > >> 15 files changed, 185 insertions(+), 10 deletions(-) > >> > > > > You might consider breaking this patch into two. One patch that makes > > LOCK_MAND locks actually work and that adds MS_SHARELOCK, and one patch > > that hooks that up to open(). Given the locking involved with the > > i_mutex it would be best to present this as a series of small, > > incremental changes. > > Good point. So, we can break it into 2: > 1) make flock actually work with LOCK_MAND on MS_SHARELOCK mounts, > 2) replace flock+LOCK_MAND with open+O_DENY* flags. > > > >> diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h > >> index 17f92aa..953a6d6 100644 > >> --- a/arch/alpha/include/uapi/asm/errno.h > >> +++ b/arch/alpha/include/uapi/asm/errno.h > >> @@ -124,4 +124,6 @@ > >> > >> #define EHWPOISON 139 /* Memory page has hardware error */ > >> > >> +#define ESHAREDENIED 140 /* File is locked with a sharelock */ > >> + > >> #endif > >> diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h > >> index 09f49a6..265344b 100644 > >> --- a/arch/alpha/include/uapi/asm/fcntl.h > >> +++ b/arch/alpha/include/uapi/asm/fcntl.h > >> @@ -33,6 +33,9 @@ > >> > >> #define O_PATH 040000000 > >> #define __O_TMPFILE 0100000000 > >> +#define O_DENYREAD 0200000000 /* Do not permit read access */ > >> +#define O_DENYWRITE 0400000000 /* Do not permit write access */ > >> +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ > >> > >> #define F_GETLK 7 > >> #define F_SETLK 8 > >> diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h > >> index 02d645d..f1a4068 100644 > >> --- a/arch/mips/include/uapi/asm/errno.h > >> +++ b/arch/mips/include/uapi/asm/errno.h > >> @@ -123,6 +123,8 @@ > >> > >> #define EHWPOISON 168 /* Memory page has hardware error */ > >> > >> +#define ESHAREDENIED 169 /* File is locked with a sharelock */ > >> + > >> #define EDQUOT 1133 /* Quota exceeded */ > >> > >> > >> diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h > >> index f3a8aa5..654c232 100644 > >> --- a/arch/parisc/include/uapi/asm/errno.h > >> +++ b/arch/parisc/include/uapi/asm/errno.h > >> @@ -124,4 +124,6 @@ > >> > >> #define EHWPOISON 257 /* Memory page has hardware error */ > >> > >> +#define ESHAREDENIED 258 /* File is locked with a sharelock */ > >> + > >> #endif > >> diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h > >> index 34a46cb..5865964 100644 > >> --- a/arch/parisc/include/uapi/asm/fcntl.h > >> +++ b/arch/parisc/include/uapi/asm/fcntl.h > >> @@ -21,6 +21,9 @@ > >> > >> #define O_PATH 020000000 > >> #define __O_TMPFILE 040000000 > >> +#define O_DENYREAD 0200000000 /* Do not permit read access */ > >> +#define O_DENYWRITE 0400000000 /* Do not permit write access */ > >> +#define O_DENYDELETE 01000000000 /* Do not permit delete or rename */ > >> > >> #define F_GETLK64 8 > >> #define F_SETLK64 9 > >> diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h > >> index 20423e17..fe339b5 100644 > >> --- a/arch/sparc/include/uapi/asm/errno.h > >> +++ b/arch/sparc/include/uapi/asm/errno.h > >> @@ -114,4 +114,6 @@ > >> > >> #define EHWPOISON 135 /* Memory page has hardware error */ > >> > >> +#define ESHAREDENIED 136 /* File is locked with a sharelock */ > >> + > >> #endif > >> diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h > >> index 7e8ace5..ab68170 100644 > >> --- a/arch/sparc/include/uapi/asm/fcntl.h > >> +++ b/arch/sparc/include/uapi/asm/fcntl.h > >> @@ -36,6 +36,9 @@ > >> > >> #define O_PATH 0x1000000 > >> #define __O_TMPFILE 0x2000000 > >> +#define O_DENYREAD 0x4000000 /* Do not permit read access */ > >> +#define O_DENYWRITE 0x8000000 /* Do not permit write access */ > >> +#define O_DENYDELETE 0x10000000 /* Do not permit delete or rename */ > >> > > > > It'd probably be best to add O_DENYDELETE in a separate patch, rather > > than disabling it temporarily. > > Agree. > > > > >> #define F_GETOWN 5 /* for sockets. */ > >> #define F_SETOWN 6 /* for sockets. */ > >> diff --git a/fs/fcntl.c b/fs/fcntl.c > >> index ef68665..3f85887 100644 > >> --- a/fs/fcntl.c > >> +++ b/fs/fcntl.c > >> @@ -729,14 +729,15 @@ 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(20 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( > >> + BUILD_BUG_ON(23 - 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 | O_PATH | __O_TMPFILE > >> + __FMODE_EXEC | O_PATH | __O_TMPFILE | > >> + O_DENYREAD | O_DENYWRITE | O_DENYDELETE > >> )); > >> > >> fasync_cache = kmem_cache_create("fasync_cache", > >> diff --git a/fs/locks.c b/fs/locks.c > >> index 92a0f0a..ffde4d4 100644 > >> --- a/fs/locks.c > >> +++ b/fs/locks.c > >> @@ -708,20 +708,73 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s > >> return (locks_conflict(caller_fl, sys_fl)); > >> } > >> > >> -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific > >> - * checking before calling the locks_conflict(). > >> +static unsigned int > >> +deny_flags_to_cmd(unsigned int flags) > >> +{ > >> + unsigned int cmd = LOCK_MAND; > >> + > >> + if (!(flags & O_DENYREAD)) > >> + cmd |= LOCK_READ; > >> + if (!(flags & O_DENYWRITE)) > >> + cmd |= LOCK_WRITE; > >> + > >> + return cmd; > >> +} > >> + > >> +/* > >> + * locks_mand_conflict - Determine if there's a share reservation conflict > >> + * @caller_fl: lock we're attempting to acquire > >> + * @sys_fl: lock already present on system that we're checking against > >> + * > >> + * Check to see if there's a share_reservation conflict. LOCK_READ/LOCK_WRITE > >> + * tell us whether the reservation allows other readers and writers. > >> + */ > >> +static int > >> +locks_mand_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) > >> +{ > >> + unsigned char caller_type = caller_fl->fl_type; > >> + unsigned char sys_type = sys_fl->fl_type; > >> + fmode_t caller_fmode = caller_fl->fl_file->f_mode; > >> + fmode_t sys_fmode = sys_fl->fl_file->f_mode; > >> + > >> + /* they can only conflict if FS is mounted with MS_SHARELOCK */ > >> + if (!IS_SHARELOCK(caller_fl->fl_file->f_path.dentry->d_inode)) > >> + return 0; > >> + > >> + /* they can only conflict if they're both LOCK_MAND */ > >> + if (!(caller_type & LOCK_MAND) || !(sys_type & LOCK_MAND)) > >> + return 0; > >> + > >> + if (!(caller_type & LOCK_READ) && (sys_fmode & FMODE_READ)) > >> + return 1; > >> + if (!(caller_type & LOCK_WRITE) && (sys_fmode & FMODE_WRITE)) > >> + return 1; > >> + if (!(sys_type & LOCK_READ) && (caller_fmode & FMODE_READ)) > >> + return 1; > >> + if (!(sys_type & LOCK_WRITE) && (caller_fmode & FMODE_WRITE)) > >> + return 1; > >> + > >> + return 0; > >> +} > >> + > >> +/* > >> + * Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking > >> + * before calling the locks_conflict(). > >> */ > >> static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) > >> { > >> - /* FLOCK locks referring to the same filp do not conflict with > >> + if (!IS_FLOCK(sys_fl)) > >> + return 0; > >> + if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) > >> + return locks_mand_conflict(caller_fl, sys_fl); > > > > nit: Seems like the above could be optimized a little. You know that > > locks_mand_conflict is only relevant if both are LOCK_MAND, and one of > > the first things that locks_mand_conflict does is to check that both > > have that set. > > ok. > > > > >> + /* > >> + * FLOCK locks referring to the same filp do not conflict with > >> * each other. > >> */ > >> - if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) > >> - return (0); > >> - if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) > >> + if (caller_fl->fl_file == sys_fl->fl_file) > >> return 0; > >> > >> - return (locks_conflict(caller_fl, sys_fl)); > >> + return locks_conflict(caller_fl, sys_fl); > >> } > >> > >> void > >> @@ -888,6 +941,36 @@ out: > >> return error; > >> } > >> > >> +/* > >> + * Determine if a file is allowed to be opened with specified access and share > >> + * modes. Lock the file and return 0 if checks passed, otherwise return > >> + * -ESHAREDENIED. > >> + */ > >> +int > >> +sharelock_lock_file(struct file *filp) > >> +{ > >> + struct file_lock *lock; > >> + int error = 0; > >> + > >> + if (!IS_SHARELOCK(filp->f_path.dentry->d_inode)) > >> + return error; > >> + > >> + /* Disable O_DENYDELETE support for now */ > >> + if (filp->f_flags & O_DENYDELETE) > >> + return -EINVAL; > >> + > >> + error = flock_make_lock(filp, &lock, deny_flags_to_cmd(filp->f_flags)); > >> + if (error) > >> + return error; > >> + > >> + error = flock_lock_file(filp, lock); > >> + if (error == -EAGAIN) > >> + error = -ESHAREDENIED; > >> + > >> + locks_free_lock(lock); > >> + return error; > >> +} > >> + > >> static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) > >> { > >> struct file_lock *fl; > >> diff --git a/fs/namei.c b/fs/namei.c > >> index 3531dee..2b741a1 100644 > >> --- a/fs/namei.c > >> +++ b/fs/namei.c > >> @@ -2725,9 +2725,14 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, > >> acc_mode = MAY_OPEN; > >> } > >> error = may_open(&file->f_path, acc_mode, open_flag); > >> - if (error) > >> + if (error) { > >> fput(file); > >> + goto out; > >> + } > >> > >> + error = sharelock_lock_file(file); > >> + if (error) > >> + fput(file); > >> out: > >> dput(dentry); > >> return error; > >> @@ -2919,6 +2924,40 @@ retry_lookup: > >> } > >> mutex_lock(&dir->d_inode->i_mutex); > >> error = lookup_open(nd, path, file, op, got_write, opened); > >> + > >> + /* > >> + * For sharelock mounts if a file was created but not opened, we need > >> + * to keep parent i_mutex until we finish the open to prevent races when > >> + * somebody opens newly created by us file and locks it with a sharelock > >> + * before we open it. > >> + */ > >> + if (IS_SHARELOCK(dir->d_inode) && error > 0 && *opened & FILE_CREATED) { > >> + /* Don't check for write permission, don't truncate */ > >> + open_flag &= ~O_TRUNC; > >> + will_truncate = false; > >> + acc_mode = MAY_OPEN; > >> + path_to_nameidata(path, nd); > >> + > >> + error = may_open(&nd->path, acc_mode, open_flag); > >> + if (error) { > >> + mutex_unlock(&dir->d_inode->i_mutex); > >> + goto out; > >> + } > >> + file->f_path.mnt = nd->path.mnt; > >> + error = finish_open(file, nd->path.dentry, NULL, opened); > >> + if (error) { > >> + mutex_unlock(&dir->d_inode->i_mutex); > >> + if (error == -EOPENSTALE) > >> + goto stale_open; > >> + goto out; > >> + } > >> + error = sharelock_lock_file(file); > >> + mutex_unlock(&dir->d_inode->i_mutex); > >> + if (error) > >> + goto exit_fput; > >> + goto opened; > >> + } > >> + > >> mutex_unlock(&dir->d_inode->i_mutex); > >> > >> if (error <= 0) { > >> @@ -3034,6 +3073,18 @@ finish_open_created: > >> goto stale_open; > >> goto out; > >> } > >> + > >> + if (IS_SHARELOCK(dir->d_inode)) { > >> + /* > >> + * Lock parent i_mutex to prevent races with sharelocks on > >> + * newly created files. > >> + */ > >> + mutex_lock(&dir->d_inode->i_mutex); > >> + error = sharelock_lock_file(file); > >> + mutex_unlock(&dir->d_inode->i_mutex); > >> + if (error) > >> + goto exit_fput; > >> + } > >> opened: > >> error = open_check_o_direct(file); > >> if (error) > >> diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c > >> index 439406e..dd374d4 100644 > >> --- a/fs/proc_namespace.c > >> +++ b/fs/proc_namespace.c > >> @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) > >> { MS_SYNCHRONOUS, ",sync" }, > >> { MS_DIRSYNC, ",dirsync" }, > >> { MS_MANDLOCK, ",mand" }, > >> + { MS_SHARELOCK, ",sharelock" }, > >> { 0, NULL } > >> }; > >> const struct proc_fs_info *fs_infop; > >> diff --git a/include/linux/fs.h b/include/linux/fs.h > >> index 121f11f..aa061ca 100644 > >> --- a/include/linux/fs.h > >> +++ b/include/linux/fs.h > >> @@ -1029,6 +1029,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); > >> extern int lease_modify(struct file_lock **, int); > >> extern int lock_may_read(struct inode *, loff_t start, unsigned long count); > >> extern int lock_may_write(struct inode *, loff_t start, unsigned long count); > >> +extern int sharelock_lock_file(struct file *); > >> #else /* !CONFIG_FILE_LOCKING */ > >> static inline int fcntl_getlk(struct file *file, struct flock __user *user) > >> { > >> @@ -1169,6 +1170,12 @@ static inline int lock_may_write(struct inode *inode, loff_t start, > >> { > >> return 1; > >> } > >> + > >> +static inline int sharelock_lock_file(struct file *filp) > >> +{ > >> + return 0; > >> +} > >> + > >> #endif /* !CONFIG_FILE_LOCKING */ > >> > >> > >> @@ -1675,6 +1682,7 @@ struct super_operations { > >> #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) > >> #define IS_IMA(inode) ((inode)->i_flags & S_IMA) > >> #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) > >> +#define IS_SHARELOCK(inode) __IS_FLG(inode, MS_SHARELOCK) > >> #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) > >> > >> /* > >> diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h > >> index 1e1ea6e..aff869c 100644 > >> --- a/include/uapi/asm-generic/errno.h > >> +++ b/include/uapi/asm-generic/errno.h > >> @@ -110,4 +110,6 @@ > >> > >> #define EHWPOISON 133 /* Memory page has hardware error */ > >> > >> +#define ESHAREDENIED 134 /* File is locked with a sharelock */ > >> + > >> #endif > >> diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h > >> index 95e46c8..9881cfe 100644 > >> --- a/include/uapi/asm-generic/fcntl.h > >> +++ b/include/uapi/asm-generic/fcntl.h > >> @@ -92,6 +92,17 @@ > >> #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) > >> #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) > >> > >> +#ifndef O_DENYREAD > >> +#define O_DENYREAD 040000000 /* Do not permit read access */ > >> +#endif > >> +/* FMODE_NONOTIFY 0100000000 */ > >> +#ifndef O_DENYWRITE > >> +#define O_DENYWRITE 0200000000 /* Do not permit write access */ > >> +#endif > >> +#ifndef O_DENYDELETE > >> +#define O_DENYDELETE 0400000000 /* Do not permit delete or rename */ > >> +#endif > >> + > > > > One thing to consider: We found with the addition of O_TMPFILE that the > > open() api is not particularly helpful when it comes to informing > > appications when a flag isn't supported: > > > > http://lwn.net/Articles/562294/ > > > > ...having a plan to cope with that here would be best. How can an > > application determine at runtime that O_DENY* actually *work*? It may > > be best to step back and consider a new syscall for this (open2() ?). > > > > So, consider we added new syscall: > > opendm(filename, flags, mode, deny_mode) > { > return open(filename, flags | denymode2openflags(deny_mode), mode) > } > > where deny_mode can be DMODE_NONE (0), DMODE_READ (1), DMODE_WRITE(2) > and DMODE_RDWR(3) (similar to FMODE_* values). > > We have open and opendm that act actually in the same manner for > mounts without MS_SHARELOCK. For mounts with MS_SHARELOCK open is like > opendm with DMODE_NONE. Open flags O_DENY* are for internal use only. > > Is it what you suggest? > Right, something that like that maybe... ...or possibly consider not making this specific to deny modes or anything, and just consider adding a new generic "openat2()" syscall. This one could have a larger (or maybe extensible) field for flags, and well-defined behavior when presented with a flag that it doesn't understand. I realize that that's a large increase in scope, but it can often be easier to get new features merged if you are simultaneously addressing other problems that exist. ;) -- Jeff Layton <jlayton@redhat.com> ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2014-02-04 12:21 UTC | newest] Thread overview: 17+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-07-01 16:49 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 2/7] VFS: Add O_DENYDELETE support for VFS Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 3/7] CIFS: Add share_access parm to open request Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 4/7] CIFS: Add O_DENY* open flags support Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 5/7] NFSv4: " Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 6/7] NFSD: Pass share reservations flags to VFS Pavel Shilovsky 2013-07-01 16:49 ` [PATCH v7 7/7] locks: Disable LOCK_MAND support for MS_SHARELOCK mounts Pavel Shilovsky -- strict thread matches above, loose matches on Subject: below -- 2014-01-17 10:07 [PATCH v7 0/7] Add O_DENY* support for VFS and CIFS/NFS Pavel Shilovsky 2014-01-17 10:07 ` [PATCH v7 1/7] VFS: Introduce new O_DENY* open flags Pavel Shilovsky 2014-01-17 18:18 ` One Thousand Gnomes 2014-01-20 10:45 ` Pavel Shilovsky 2014-01-20 13:34 ` One Thousand Gnomes 2014-01-21 13:19 ` Pavel Shilovsky 2014-02-01 13:57 ` Jeff Layton 2014-02-01 13:20 ` Jeff Layton 2014-02-04 12:03 ` Pavel Shilovsky 2014-02-04 12:21 ` Jeff Layton
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).