locks: implement "filp-private" (aka UNPOSIX) locks
v3 cover locks: implement "filp-private" (aka UNPOSIX) locks
This patchset is the third posting of this set. Behavior between this set and the last should be more or less the same. Here is a summary of changes:
v3:
- more consolidation of common code between flock_to_posix_lock and flock_to_posix_lock64
- better bisectability by reordering changes, such that partial implementation won't be exposed
- s/filp/file/ s/FILP/FILE/ in symbol names
v2:
- inheritance semantics have been change to be more BSD-lock like
- patchset size has been reduced by changing how lock ownership is handled
- new F_UNLCKP l_type value has been added
Note too that I've gone ahead and opened a request for the POSIX folks to consider adding this to the POSIX spec once we have something mergeable. They seem amenable to the idea but don't want to enshrine it into the standard until there's a real implementation of it:
http://austingroupbugs.net/view.php?id=768
I also owe them a better writeup of the semantics for these new locks, but have been holding off on doing that until they're a little more settled.
As a side note, I've also had a few other userland developers reach out to me as to the status of this work. There seems to be a lot of interest since classic POSIX locks are such a pain to work with in threaded programs. Hopefully some will chime in on this posting...
Original cover letter from v1 posting follows. Comments and suggestions welcome.
-------------------------------[snip]------------------------------
At LSF this year, there was a discussion about the "wishlist" for userland file servers. One of the things brought up was the goofy and problematic behavior of POSIX locks when a file is closed. Boaz started a thread on it here:
http://permalink.gmane.org/gmane.linux.file-systems/73364
Userland fileservers often need to maintain more than one open file descriptor on a file. The POSIX spec says:
"All locks associated with a file for a given process shall be removed
when a file descriptor for that file is closed by that process or the process holding that file descriptor terminates."
This is problematic since you can't close any file descriptor without dropping all your POSIX locks. Most userland file servers therefore end up opening the file with more access than is really necessary, and keeping fd's open for longer than is necessary to work around this.
This patchset is a first stab at an approach to address this problem by adding two new l_type values -- F_RDLCKP and F_WRLCKP (the 'P' is short for "private" -- I'm open to changing that if you have a better mnemonic).
For all intents and purposes these lock types act just like their "non-P" counterpart. The difference is that they are only implicitly released when the fd against which they were acquired is closed. As a side effect, these locks cannot be merged with "non-P" locks since they have different semantics on close.
I've given this patchset some very basic smoke testing and it seems to do the right thing, but it is still pretty rough. If this looks reasonable I'll plan to do some documentation updates and will take a stab at trying to get these new lock types added to the POSIX spec (as HCH recommended).
At this point, my main questions are:
1) does this look useful, particularly for fileserver implementors?
2) does this look OK API-wise? We could consider different "cmd" values or even different syscalls, but I figured this makes it clearer that "P" and "non-P" locks will still conflict with one another.
Jeff Layton (6):
locks: consolidate common code in the flock_to_posix_lock routines
locks: consolidate checks for compatible filp->f_mode values in setlk
handlers
locks: rename locks_remove_flock to locks_remove_file
locks: show private lock types in /proc/locks
locks: report l_pid as -1 for FL_FILE_PVT locks
locks: add new "private" lock type that is owned by the filp
fs/file_table.c | 2 +- fs/locks.c | 122 +++++++++++++++++++++------------------ include/linux/fs.h | 5 +- include/uapi/asm-generic/fcntl.h | 16 +++++ 4 files changed, 85 insertions(+), 60 deletions(-)
Signature · 1 line
1.8.4.2
PATCH v3 1/6 locks: consolidate common code in the flock_to_posix_lock routines
Currently, there's a lot of copy and paste between the two. Add some functions to do the initialization of the file_lock from values passed in, and turn the flock/flock64 variants of those functions into wrappers around them.
Unfortunately it's harder to consolidate the fl_start/fl_end calculations due to the differently sized types involved so I've left them separate for now.
--- fs/locks.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-)
fs/locks.c +15 -16
@@ -344,6 +344,19 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } +static int +flock_to_posix_lock_common (struct file_lock *fl,struct file *filp,short type) +{ + fl->fl_owner = current->files; + fl->fl_pid = current->tgid; + fl->fl_file = filp; + fl->fl_flags =FL_POSIX ; + fl->fl_ops =NULL ; + fl->fl_lmops =NULL ; + +return assign_type (fl, type); +} +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX * style lock. */
@@ -386,14 +399,7 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,if (fl->fl_end < fl->fl_start)return -EOVERFLOW ; - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (fl, filp, l->l_type); }#if BITS_PER_LONG ==32
@@ -431,15 +437,8 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_start = start;/* we record the absolute position */ if (fl->fl_end < fl->fl_start)return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - return assign_type(fl, l->l_type); + return flock_to_posix_lock_common(fl, filp, l->l_type); }#endif
Signature · 1 line
1.8.4.2
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 8 lines · Jeff Layton
Currently, there's a lot of copy and paste between the two. Add some functions to do the initialization of the
file_lockfrom values passed in, and turn the flock/flock64 variants of those functions into wrappers around them.Unfortunately it's harder to consolidate the fl_start/fl_end calculations due to the differently sized types involved so I've left them separate for now.
I'd think you could assign everything to the flock64 type and do the common work there or something.
But I'm confused about what the current code is actually trying to do:
if I'm chasing down the definitions right, these quantities are all signed, and when start is defined as an off_t it can overflow in the SEEK_CUR and SEEK_END cases. And
if (fl->fl_end < fl->fl_start) return -EOVERFLOW
is counting on overlow wrapping around, which I thought wasn't guaranteed in the case of signed arithmetic?
--b.
Quoted 71 lines
Signed-off-by Jeff Layton <[email protected]>--- fs/locks.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-)fs/locks.c +15
-16@@ -344,6 +344,19 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } +static int +flock_to_posix_lock_common (struct file_lock *fl,struct file *filp,short type) +{ + fl->fl_owner = current->files; + fl->fl_pid = current->tgid; + fl->fl_file = filp; + fl->fl_flags =FL_POSIX ; + fl->fl_ops =NULL ; + fl->fl_lmops =NULL ; + +return assign_type (fl, type); +} +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX * style lock. */ @@ -386,14 +399,7 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,if (fl->fl_end < fl->fl_start)return -EOVERFLOW ; - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (fl, filp, l->l_type); }#if BITS_PER_LONG ==32 @@ -431,15 +437,8 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_start = start;/* we record the absolute position */ if (fl->fl_end < fl->fl_start)return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - return assign_type(fl, l->l_type); + return flock_to_posix_lock_common(fl, filp, l->l_type); }#endif Signature · 7 lines
1.8.4.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 18 lines · bfields
Quoted 8 lines · Jeff Layton
Currently, there's a lot of copy and paste between the two. Add some functions to do the initialization of the
file_lockfrom values passed in, and turn the flock/flock64 variants of those functions into wrappers around them.Unfortunately it's harder to consolidate the fl_start/fl_end calculations due to the differently sized types involved so I've left them separate for now.
I'd think you could assign everything to the flock64 type and do the common work there or something.
But I'm confused about what the current code is actually trying to do:
if I'm chasing down the definitions right, these quantities are all signed, and when start is defined as anoff_tit can overflow in theSEEK_CURandSEEK_ENDcases. And
Quoted 5 lines
if (fl->fl_end < fl->fl_start) return -EOVERFLOWis counting on overlow wrapping around, which I thought wasn't guaranteed in the case of signed arithmetic?
E.g. the following (untested) removes the duplication and should return -EOVERFLOW in the cases we currently do a random conversion from 64- to 32-bit and back. Susv3 says:
[EOVERFLOW]
The cmd argument is F_GETLK, F_SETLK, or F_SETLKW and the smallest or, if l_len is non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of type off_t.
so that's what I tried to do.
--b.
fs/locks.c +40 -70
@@ -344,48 +344,41 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; -break ; + fl->fl_start =0 ;case SEEK_CUR : - start = filp->f_pos; -break ; + fl->fl_start = filp->f_pos;case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; + fl->fl_start =i_size_read (file_inode (filp));default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;
@@ -396,50 +389,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif
include/uapi/asm-generic/fcntl.h +0 -3
@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD
@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 197 lines · On Tue, 10 Dec 2013 18:22:04 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 18 lines · bfields
Quoted 8 lines · Jeff Layton
Currently, there's a lot of copy and paste between the two. Add some functions to do the initialization of the
file_lockfrom values passed in, and turn the flock/flock64 variants of those functions into wrappers around them.Unfortunately it's harder to consolidate the fl_start/fl_end calculations due to the differently sized types involved so I've left them separate for now.
I'd think you could assign everything to the flock64 type and do the common work there or something.
But I'm confused about what the current code is actually trying to do:
if I'm chasing down the definitions right, these quantities are all signed, and when start is defined as anoff_tit can overflow in theSEEK_CURandSEEK_ENDcases. AndQuoted 5 lines
if (fl->fl_end < fl->fl_start) return -EOVERFLOWis counting on overlow wrapping around, which I thought wasn't guaranteed in the case of signed arithmetic?
E.g. the following (untested) removes the duplication and should return
-EOVERFLOWin the cases we currently do a random conversion from 64- to 32-bit and back. Susv3 says:[EOVERFLOW]
The cmd argument isF_GETLK,F_SETLK, orF_SETLKWand the smallest or, ifl_lenis non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of typeoff_t.so that's what I tried to do.
--b.
fs/locks.c +40
-70@@ -344,48 +344,41 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; -break ; + fl->fl_start =0 ;case SEEK_CUR : - start = filp->f_pos; -break ; + fl->fl_start = filp->f_pos;case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; + fl->fl_start =i_size_read (file_inode (filp));default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;@@ -396,50 +389,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif include/uapi/asm-generic/fcntl.h +0
-2@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD
include/uapi/asm-generic/fcntl.h struct flock64 { · line 204
Quoted 6 lines
include/uapi/asm-generic/fcntl.h +0
-1@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Nice. I had started to consolidate them, but then figured out that there are so many edge cases and I didn't have a good way to test them all.
As far as I can tell though, this looks correct. I'll plan to drop my patch and base the rest of the set on top of yours.
Thanks,
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 205 lines · Jeff Layton
Quoted 202 lines · On Tue, 10 Dec 2013 18:22:04 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 18 lines · bfields
Quoted 8 lines · Jeff Layton
Currently, there's a lot of copy and paste between the two. Add some functions to do the initialization of the
file_lockfrom values passed in, and turn the flock/flock64 variants of those functions into wrappers around them.Unfortunately it's harder to consolidate the fl_start/fl_end calculations due to the differently sized types involved so I've left them separate for now.
I'd think you could assign everything to the flock64 type and do the common work there or something.
But I'm confused about what the current code is actually trying to do:
if I'm chasing down the definitions right, these quantities are all signed, and when start is defined as anoff_tit can overflow in theSEEK_CURandSEEK_ENDcases. AndQuoted 5 lines
if (fl->fl_end < fl->fl_start) return -EOVERFLOWis counting on overlow wrapping around, which I thought wasn't guaranteed in the case of signed arithmetic?
E.g. the following (untested) removes the duplication and should return
-EOVERFLOWin the cases we currently do a random conversion from 64- to 32-bit and back. Susv3 says:[EOVERFLOW]
The cmd argument isF_GETLK,F_SETLK, orF_SETLKWand the smallest or, ifl_lenis non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of typeoff_t.so that's what I tried to do.
--b.
fs/locks.c +40
-70@@ -344,48 +344,41 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; -break ; + fl->fl_start =0 ;case SEEK_CUR : - start = filp->f_pos; -break ; + fl->fl_start = filp->f_pos;case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; + fl->fl_start =i_size_read (file_inode (filp));default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;@@ -396,50 +389,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif include/uapi/asm-generic/fcntl.h +0
-3@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */
Quoted 8 lines
include/uapi/asm-generic/fcntl.h struct flock64 { · line 204
Quoted 1 line
include/uapi/asm-generic/fcntl.h +0
-0#endif /* _ASM_GENERIC_FCNTL_H */ Nice. I had started to consolidate them, but then figured out that there are so many edge cases and I didn't have a good way to test them all.
As far as I can tell though, this looks correct. I'll plan to drop my patch and base the rest of the set on top of yours.
Well, it'd be weird if I didn't screw up something somewhere. If we've both looked at it and not found anything then maybe it's OK. But I'll see if I can do some basic tests for the edge cases.
--b.
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 1 line · J. Bruce Fields
Well, it'd be weird if I didn't screw up something somewhere.
Yes, a classic: I forgot the breaks after each switch case.
Here's a version that at least doesn't return -EINVAL on every lock attempt.
--b.
commit 70b7b9a442d06a71306736eb01cedba5dd6d86cf
Author: J. Bruce Fields <[email protected]> Date: Tue Dec 10 18:14:28 2013 -0500
locks: fix posix lock range overflow handling
In the 32-bit case fcntl is converting signed 64-bit to signed 32-bit quantities in a couple places, with probably incorrect results.
So instead let's return -EOVERFLOW as described in SUSv3, whenever "the smallest or, if l_len is non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of type off_t."
While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
fs/locks.c +40 -67
@@ -344,48 +344,44 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;
@@ -396,50 +392,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif
include/uapi/asm-generic/fcntl.h +0 -3
@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD
@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 187 lines · On Wed, 11 Dec 2013 10:19:31 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 1 line · J. Bruce Fields
Well, it'd be weird if I didn't screw up something somewhere.
Yes, a classic: I forgot the breaks after each switch case.
Here's a version that at least doesn't return
-EINVALon every lock attempt.--b.
commit
70b7b9a442d06a71306736eb01cedba5dd6d86cfAuthor: J. Bruce Fields <[email protected]> Date: Tue Dec 10 18:14:28 2013 -0500locks: fix posix lock range overflow handling
In the 32-bit case fcntl is converting signed 64-bit to signed 32-bit quantities in a couple places, with probably incorrect results.
So instead let's return
-EOVERFLOWas described in SUSv3, whenever "the smallest or, ifl_lenis non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of typeoff_t."While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
Signed-off-by J. Bruce Fields <[email protected]>fs/locks.c +40
-67@@ -344,48 +344,44 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;@@ -396,50 +392,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif include/uapi/asm-generic/fcntl.h +0
-2@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD
include/uapi/asm-generic/fcntl.h struct flock64 { · line 204
Quoted 6 lines
include/uapi/asm-generic/fcntl.h +0
-1@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Thanks -- looks like this is hitting the same discrepancy that I was getting with my consolidation attempt. Setting a lock like this on x86_64 fails with the current kernel:
struct flock lck1 = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = LONG_MAX,
};...but succeeds with your patch in place. I've not sat down yet to figure out whether you broke or fixed something though :).
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 201 lines · Jeff Layton
Quoted 187 lines · On Wed, 11 Dec 2013 10:19:31 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 1 line · J. Bruce Fields
Well, it'd be weird if I didn't screw up something somewhere.
Yes, a classic: I forgot the breaks after each switch case.
Here's a version that at least doesn't return
-EINVALon every lock attempt.--b.
commit
70b7b9a442d06a71306736eb01cedba5dd6d86cfAuthor: J. Bruce Fields <[email protected]> Date: Tue Dec 10 18:14:28 2013 -0500locks: fix posix lock range overflow handling
In the 32-bit case fcntl is converting signed 64-bit to signed 32-bit quantities in a couple places, with probably incorrect results.
So instead let's return
-EOVERFLOWas described in SUSv3, whenever "the smallest or, ifl_lenis non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of typeoff_t."While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
Signed-off-by J. Bruce Fields <[email protected]>fs/locks.c +40
-67@@ -344,48 +344,44 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;@@ -396,50 +392,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif include/uapi/asm-generic/fcntl.h +0
-2@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PADinclude/uapi/asm-generic/fcntl.h struct flock64 { · line 204
Quoted 6 lines
include/uapi/asm-generic/fcntl.h +0
-1@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */ Thanks -- looks like this is hitting the same discrepancy that I was getting with my consolidation attempt. Setting a lock like this on
x86_64fails with the current kernel:
Quoted 6 lines
struct flock lck1 = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = LONG_MAX,
};Are you sure? I've tried but can't reproduce that. I'm on 3.13-rc3 plus a couple DRC patches from you.
--b.
Quoted 7 lines
...but succeeds with your patch in place. I've not sat down yet to figure out whether you broke or fixed something though :).
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 209 lines · On Wed, 11 Dec 2013 11:59:18 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 201 lines · Jeff Layton
Quoted 187 lines · On Wed, 11 Dec 2013 10:19:31 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 1 line · J. Bruce Fields
Well, it'd be weird if I didn't screw up something somewhere.
Yes, a classic: I forgot the breaks after each switch case.
Here's a version that at least doesn't return
-EINVALon every lock attempt.--b.
commit
70b7b9a442d06a71306736eb01cedba5dd6d86cfAuthor: J. Bruce Fields <[email protected]> Date: Tue Dec 10 18:14:28 2013 -0500locks: fix posix lock range overflow handling
In the 32-bit case fcntl is converting signed 64-bit to signed 32-bit quantities in a couple places, with probably incorrect results.
So instead let's return
-EOVERFLOWas described in SUSv3, whenever "the smallest or, ifl_lenis non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of typeoff_t."While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
Signed-off-by J. Bruce Fields <[email protected]>fs/locks.c +40
-67@@ -344,48 +344,44 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;@@ -396,50 +392,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif include/uapi/asm-generic/fcntl.h +0
-2@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PADinclude/uapi/asm-generic/fcntl.h struct flock64 { · line 204
Quoted 6 lines
include/uapi/asm-generic/fcntl.h +0
-1@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */ Thanks -- looks like this is hitting the same discrepancy that I was getting with my consolidation attempt. Setting a lock like this on
x86_64fails with the current kernel:Quoted 6 lines
struct flock lck1 = { .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = LONG_MAX, };
Quoted 5 lines
Are you sure? I've tried but can't reproduce that. I'm on 3.13-rc3 plus a couple DRC patches from you.
--b.
Nope, my bad -- stupid mistake on my part. My test program had F_WRLCKP, which of course *should* get an EINVAL on the older kernel. Patch seems to be fine so far in all of the edge cases I've tested (which admittedly, isn't many).
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
In reply to J. Bruce FieldsQuoted 88 lines · On Wed, 11 Dec 2013 10:19:31 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 1 line · J. Bruce Fields
Well, it'd be weird if I didn't screw up something somewhere.
Yes, a classic: I forgot the breaks after each switch case.
Here's a version that at least doesn't return
-EINVALon every lock attempt.--b.
commit
70b7b9a442d06a71306736eb01cedba5dd6d86cfAuthor: J. Bruce Fields <[email protected]> Date: Tue Dec 10 18:14:28 2013 -0500locks: fix posix lock range overflow handling
In the 32-bit case fcntl is converting signed 64-bit to signed 32-bit quantities in a couple places, with probably incorrect results.
So instead let's return
-EOVERFLOWas described in SUSv3, whenever "the smallest or, ifl_lenis non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of typeoff_t."While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
Signed-off-by J. Bruce Fields <[email protected]>fs/locks.c +15
-21@@ -344,48 +344,44 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; +loff_t start;switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; } +if (l->l_start <0 ) +return -EINVAL ; +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 )
fs/locks.c static int assign_type(struct file_lock *fl, long type) · line 380
Quoted 10 lines
fs/locks.c +4
-6-return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ;
Erm... I think this is not quite right...
"start" is uninitialized here. I think this should be:
fl->fl_end = fl->fl_start - 1
With that too, we can get rid of the local "start" variable. I think this may explain why I'm tripping over the BUG() in locks_remove_file.
While we're in here, I'm going to see what can be done to get rid of that BUG() call too. That problem doesn't seem like something we ought to be bringing down the box over...
Quoted 95 lines
fs/locks.c +21
-40+ fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;@@ -396,50 +392,27 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif include/uapi/asm-generic/fcntl.h +0
-3@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 2 lines · Jeff Layton
On Wed, 11 Dec 2013 10:19:31 -0500
"J. Bruce Fields" <[email protected]> wrote:
...
Quoted 7 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ if (l->l_len > 0) + fl->fl_end = fl->fl_start + l->l_len - 1; + else if (l->l_len < 0) { + fl->fl_end = start - 1;Erm... I think this is not quite right...
Quoted 6 lines
"start" is uninitialized here. I think this should be:
fl->fl_end = fl->fl_start - 1With that too, we can get rid of the local "start" variable. I think this may explain why I'm tripping over the
BUG()inlocks_remove_file.
Yep.
One other bug: I think l_start < 0 is actually fine in the SEEK_CUR/SEEK_END cases.
With that fixed and another comment (though I don't know how much it helps), it looks like the below.
--b.
commit 052f395880af17919d4c13037deed581f367b2a8
Author: J. Bruce Fields <[email protected]> Date: Tue Dec 10 18:14:28 2013 -0500
locks: fix posix lock range overflow handling
In the 32-bit case fcntl assigns f_pos and i_size to a 32-bit off_t, with probably incorrect results.
So instead let's return -EOVERFLOW as described in SUSv3, whenever "the smallest or, if l_len is non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of type off_t."
While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
fs/locks.c +44 -68
@@ -344,48 +344,42 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; -switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; } +if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (fl->fl_start <0 ) +return -EINVAL ; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = fl->fl_start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;
@@ -396,50 +390,32 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +/* +* The use of OFFT_OFFSET_MAX here ensures we return -EOVERFLOW +* if the start or end of the lock could not be represented as +* an off_t, following SUSv3. +*/ +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif
include/uapi/asm-generic/fcntl.h +0 -3
@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD
@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 18 lines · J. Bruce Fields
Quoted 2 lines · Jeff Layton
On Wed, 11 Dec 2013 10:19:31 -0500
"J. Bruce Fields" <[email protected]> wrote:...
Quoted 7 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ if (l->l_len > 0) + fl->fl_end = fl->fl_start + l->l_len - 1; + else if (l->l_len < 0) { + fl->fl_end = start - 1;Erm... I think this is not quite right...
Quoted 6 lines
"start" is uninitialized here. I think this should be:
fl->fl_end = fl->fl_start - 1With that too, we can get rid of the local "start" variable. I think this may explain why I'm tripping over the
BUG()inlocks_remove_file.
Quoted 7 lines
Yep.
One other bug: I think
l_start< 0 is actually fine in the SEEK_CUR/SEEK_END cases.With that fixed and another comment (though I don't know how much it helps), it looks like the below.
Alternatively, maybe we could simplify? (On top of the previous):
commit d4bf5cb021a3ac1ec07530ebda904e262cc89d11
Author: J. Bruce Fields <[email protected]> Date: Wed Dec 11 17:42:32 2013 -0500
locks: simplify overflow checking
Or maybe we don't actually care about indicating overflow in the 32-bit
case: sure we could fail if e.g. f_pos+start or f_pos+start+len would exceed 32-bits, but do we really need to?
fs/locks.c +5 -18
@@ -344,8 +344,8 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, -struct flock64 *l,loff_t offset_max) +static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock64 *l) {switch (l->l_whence) {case SEEK_SET :
@@ -360,12 +360,12 @@ static int flock_to_posix_lock_common(struct file *filp, struct file_lock *fl,default :return -EINVAL ; } -if (l->l_start > offset_max - fl->fl_start) +if (l->l_start >OFFSET_MAX - fl->fl_start)return -EOVERFLOW ; fl->fl_start += l->l_start;if (fl->fl_start <0 )return -EINVAL ; -if (l->l_len > offset_max - fl->fl_start) +if (l->l_len >OFFSET_MAX - fl->fl_start)return -EOVERFLOW ;if (fl->fl_start + l->l_len <0 )return -EINVAL ;
@@ -403,22 +403,9 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl, .l_len = l->l_len, }; -/* -* The use of OFFT_OFFSET_MAX here ensures we return -EOVERFLOW -* if the start or end of the lock could not be represented as -* an off_t, following SUSv3. -*/ -return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +return flock64_to_posix_lock (filp, fl, &ll); } -#if BITS_PER_LONG ==32 -static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock64 *l) -{ -return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); -} -#endif -/* default lease lock manager operations */ static void lease_break_callback (struct file_lock *fl) {
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Quoted 85 lines · On Wed, 11 Dec 2013 17:57:24 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 18 lines · J. Bruce Fields
Quoted 2 lines · Jeff Layton
On Wed, 11 Dec 2013 10:19:31 -0500
"J. Bruce Fields" <[email protected]> wrote:...
Quoted 7 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ if (l->l_len > 0) + fl->fl_end = fl->fl_start + l->l_len - 1; + else if (l->l_len < 0) { + fl->fl_end = start - 1;Erm... I think this is not quite right...
Quoted 6 lines
"start" is uninitialized here. I think this should be:
fl->fl_end = fl->fl_start - 1With that too, we can get rid of the local "start" variable. I think this may explain why I'm tripping over the
BUG()inlocks_remove_file.Quoted 7 lines
Yep.
One other bug: I think
l_start< 0 is actually fine in the SEEK_CUR/SEEK_END cases.With that fixed and another comment (though I don't know how much it helps), it looks like the below.
Alternatively, maybe we could simplify? (On top of the previous):
commit
d4bf5cb021a3ac1ec07530ebda904e262cc89d11Author: J. Bruce Fields <[email protected]> Date: Wed Dec 11 17:42:32 2013 -0500locks: simplify overflow checking
Or maybe we don't actually care about indicating overflow in the 32-bit
case: sure we could fail if e.g. f_pos+start or f_pos+start+len would exceed 32-bits, but do we really need to?Signed-off-by J. Bruce Fields <[email protected]>fs/locks.c +5
-11@@ -344,8 +344,8 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, -struct flock64 *l,loff_t offset_max) +static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock64 *l) {switch (l->l_whence) {case SEEK_SET :@@ -360,12 +360,12 @@ static int flock_to_posix_lock_common(struct file *filp, struct file_lock *fl,default :return -EINVAL ; } -if (l->l_start > offset_max - fl->fl_start) +if (l->l_start >OFFSET_MAX - fl->fl_start)return -EOVERFLOW ; fl->fl_start += l->l_start;if (fl->fl_start <0 )return -EINVAL ; -if (l->l_len > offset_max - fl->fl_start) +if (l->l_len >OFFSET_MAX - fl->fl_start)return -EOVERFLOW ;if (fl->fl_start + l->l_len <0 )return -EINVAL ;@@ -403,22 +403,9 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl, .l_len = l->l_len, }; -/* -* The use of OFFT_OFFSET_MAX here ensures we return -EOVERFLOW -* if the start or end of the lock could not be represented as -* an off_t, following SUSv3. -*/ -return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +return flock64_to_posix_lock (filp, fl, &ll); } -#if BITS_PER_LONG ==32
fs/locks.c static int flock_to_posix_lock(struct file *filp, struct file_lock *fl, · line 411
Quoted 10 lines
fs/locks.c +0
-7-static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock64 *l) -{ -return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); -} -#endif -/* default lease lock manager operations */ static void lease_break_callback (struct file_lock *fl) {
Makes sense to me -- be liberal in what you accept and all that...
It might be problematic for 32-bit apps that then later try to do F_GETLK against such a lock, I doubt that'll be any worse than the current situation. I'll toss this one onto the pile...
Thanks!
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
In reply to J. Bruce FieldsQuoted 81 lines · On Wed, 11 Dec 2013 17:56:16 -0500 "J. Bruce Fields" <[email protected]>…
Quoted 2 lines · Jeff Layton
On Wed, 11 Dec 2013 10:19:31 -0500
"J. Bruce Fields" <[email protected]> wrote:...
Quoted 7 lines
fs/locks.c static int assign_type(struct file_lock *fl, long type)
Quoted 4 lines
fs/locks.c +4
-0+if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = start -1 ;Erm... I think this is not quite right...
Quoted 6 lines
"start" is uninitialized here. I think this should be:
fl->fl_end = fl->fl_start - 1With that too, we can get rid of the local "start" variable. I think this may explain why I'm tripping over the
BUG()inlocks_remove_file.Yep.
One other bug: I think
l_start< 0 is actually fine in the SEEK_CUR/SEEK_END cases.With that fixed and another comment (though I don't know how much it helps), it looks like the below.
--b.
commit
052f395880af17919d4c13037deed581f367b2a8Author: J. Bruce Fields <[email protected]> Date: Tue Dec 10 18:14:28 2013 -0500locks: fix posix lock range overflow handling
In the 32-bit case fcntl assigns
f_posandi_sizeto a 32-bitoff_t, with probably incorrect results.So instead let's return
-EOVERFLOWas described in SUSv3, whenever "the smallest or, ifl_lenis non-zero, the largest offset of any byte in the requested segment cannot be represented correctly in an object of typeoff_t."While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
Signed-off-by J. Bruce Fields <[email protected]>fs/locks.c +5
-10@@ -344,48 +344,42 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock_to_posix_lock_common (struct file *filp,struct file_lock *fl, +struct flock64 *l,loff_t offset_max) { -off_t start, end; -switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; }
fs/locks.c static int assign_type(struct file_lock *fl, long type) · line 372
Quoted 10 lines
fs/locks.c +9
-0+if (l->l_start > offset_max - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (fl->fl_start <0 ) +return -EINVAL ; +if (l->l_len > offset_max - fl->fl_start) +return -EOVERFLOW ; +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ;
Well spotted. The change looks correct to me.
Quoted 124 lines
fs/locks.c +30
-58/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; - }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - +if (l->l_len >0 ) + fl->fl_end = fl->fl_start + l->l_len -1 ; +else if (l->l_len <0 ) { + fl->fl_end = fl->fl_start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;@@ -396,50 +390,32 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) +{ +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; + +/* +* The use of OFFT_OFFSET_MAX here ensures we return -EOVERFLOW +* if the start or end of the lock could not be represented as +* an off_t, following SUSv3. +*/ +return flock_to_posix_lock_common (filp, fl, &ll,OFFT_OFFSET_MAX ); +} +#if BITS_PER_LONG ==32 static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl,struct flock64 *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } - - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock_to_posix_lock_common (filp, fl, l,OFFSET_MAX ); }#endif include/uapi/asm-generic/fcntl.h +0
-3@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 1/6] locks: consolidate common code in the flock_to_posix_lock routines
Ugh, I screwed up one more when rewriting flock{64}_to_posix_lock, an off-by-one error caused by not noticing that the "end" offset of a lock is at start + len - 1, not start + len.
(So for example, a 1-byte lock starting at offset 5 is recorded as (fl_start, fl_end) == (5, 5), not (5,6)....)
This actually causes "cthon -l" fails as it attempts a lock with (start, len) == (1, OFFSET_MAX).
--b.
fs/locks.c +7 -6
@@ -365,16 +365,17 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_start += l->l_start;if (fl->fl_start <0 )return -EINVAL ; -if (l->l_len >0 && l->l_len -1 >OFFSET_MAX - fl->fl_start) -return -EOVERFLOW ; -if (fl->fl_start + l->l_len <0 ) -return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ -if (l->l_len >0 ) +if (l->l_len >0 ) { +if (l->l_len -1 >OFFSET_MAX - fl->fl_start) +return -EOVERFLOW ; fl->fl_end = fl->fl_start + l->l_len -1 ; -else if (l->l_len <0 ) { + + }else if (l->l_len <0 ) { +if (fl->fl_start + l->l_len <0 ) +return -EINVAL ; fl->fl_end = fl->fl_start -1 ; fl->fl_start += l->l_len; }else
PATCH 1/1 locks: fix posix lock range overflow handling
From: "J. Bruce Fields" <[email protected]>
In the 32-bit case fcntl assigns the 64-bit f_pos and i_size to a 32-bit off_t.
The existing range checks also seem to depend on signed arithmetic wrapping when it overflows. In practice maybe that works, but we can be more careful. That also allows us to make a more reliable distinction between -EINVAL and -EOVERFLOW.
Note that in the 32-bit case SEEK_CUR or SEEK_END might allow the caller to set a lock with starting point no longer representable as a 32-bit value. We could return -EOVERFLOW in such cases, but the locks code is capable of handling such ranges, so we choose to be lenient here. The only problem is that subsequent GETLK calls on such a lock will fail with EOVERFLOW.
While we're here, do some cleanup including consolidating code for the flock and flock64 cases.
--- fs/locks.c | 100 +++++++++++++-------------------------- include/uapi/asm-generic/fcntl.h | 3 -- 2 files changed, 32 insertions(+), 71 deletions(-)
Here's the updated patch. Could probably use another read-through, I'm tired of looking at it....
--b.
fs/locks.c +32 -68
@@ -344,48 +344,43 @@ static int assign_type(struct file_lock *fl, long type)return 0 ; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX -* style lock. -*/ -static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock *l) +static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock64 *l) { -off_t start, end; -switch (l->l_whence) {case SEEK_SET : - start =0 ; + fl->fl_start =0 ;break ;case SEEK_CUR : - start = filp->f_pos; + fl->fl_start = filp->f_pos;break ;case SEEK_END : - start =i_size_read (file_inode (filp)); + fl->fl_start =i_size_read (file_inode (filp));break ;default :return -EINVAL ; } +if (l->l_start >OFFSET_MAX - fl->fl_start) +return -EOVERFLOW ; + fl->fl_start += l->l_start; +if (fl->fl_start <0 ) +return -EINVAL ;/* POSIX-1996 leaves the case l->l_len < 0 undefined; POSIX-2001 defines it. */ - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ;if (l->l_len >0 ) { - end = start + l->l_len -1 ; - fl->fl_end = end; +if (l->l_len -1 >OFFSET_MAX - fl->fl_start) +return -EOVERFLOW ; + fl->fl_end = fl->fl_start + l->l_len -1 ; + }else if (l->l_len <0 ) { - end = start -1 ; - fl->fl_end = end; - start += l->l_len; -if (start <0 ) +if (fl->fl_start + l->l_len <0 )return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - + fl->fl_end = fl->fl_start -1 ; + fl->fl_start += l->l_len; + }else + fl->fl_end =OFFSET_MAX ; + fl->fl_owner = current->files; fl->fl_pid = current->tgid; fl->fl_file = filp;
@@ -396,52 +391,21 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,return assign_type (fl, l->l_type); } -#if BITS_PER_LONG ==32 -static int flock64_to_posix_lock (struct file *filp,struct file_lock *fl, -struct flock64 *l) +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX +* style lock. +*/ +static int flock_to_posix_lock (struct file *filp,struct file_lock *fl, +struct flock *l) { -loff_t start; - -switch (l->l_whence) { -case SEEK_SET : - start =0 ; -break ; -case SEEK_CUR : - start = filp->f_pos; -break ; -case SEEK_END : - start =i_size_read (file_inode (filp)); -break ; -default : -return -EINVAL ; - } +struct flock64 ll = { + .l_type = l->l_type, + .l_whence = l->l_whence, + .l_start = l->l_start, + .l_len = l->l_len, + }; - start += l->l_start; -if (start <0 ) -return -EINVAL ; - fl->fl_end =OFFSET_MAX ; -if (l->l_len >0 ) { - fl->fl_end = start + l->l_len -1 ; - }else if (l->l_len <0 ) { - fl->fl_end = start -1 ; - start += l->l_len; -if (start <0 ) -return -EINVAL ; - } - fl->fl_start = start;/* we record the absolute position */ -if (fl->fl_end < fl->fl_start) -return -EOVERFLOW ; - - fl->fl_owner = current->files; - fl->fl_pid = current->tgid; - fl->fl_file = filp; - fl->fl_flags =FL_POSIX ; - fl->fl_ops =NULL ; - fl->fl_lmops =NULL ; - -return assign_type (fl, l->l_type); +return flock64_to_posix_lock (filp, fl, &ll); } -#endif /* default lease lock manager operations */ static void lease_break_callback (struct file_lock *fl)
include/uapi/asm-generic/fcntl.h +0 -3
@@ -186,8 +186,6 @@ struct flock { };#endif -#ifndef CONFIG_64BIT -#ifndef HAVE_ARCH_STRUCT_FLOCK64 #ifndef __ARCH_FLOCK64_PAD#define __ARCH_FLOCK64_PAD
@@ -202,6 +200,5 @@ struct flock64 {__ARCH_FLOCK64_PAD };#endif -#endif /* !CONFIG_64BIT */ #endif /* _ASM_GENERIC_FCNTL_H */
Signature · 1 line
1.8.3.1
PATCH v3 2/6 locks: consolidate checks for compatible filp->f_mode values in setlk handlers
In reply to Jeff LaytonMove this check into the posix_lock_setup_common handler instead of duplicating this check in two places. This also fixes a minor wart in the code where we continue referring to the struct flock after converting it to struct file_lock.
--- fs/locks.c | 46 ++++++++++++---------------------------------- 1 file changed, 12 insertions(+), 34 deletions(-)
fs/locks.c +12 -34
@@ -354,6 +354,18 @@ flock_to_posix_lock_common(struct file_lock *fl, struct file *filp, short type) fl->fl_ops =NULL ; fl->fl_lmops =NULL ; +/* Ensure that fl->fl_filp has compatible f_mode */ +switch (type) { +case F_RDLCK : +if (!(filp->f_mode &FMODE_READ )) +return -EBADF ; +break ; +case F_WRLCK : +if (!(filp->f_mode &FMODE_WRITE )) +return -EBADF ; +break ; + } +return assign_type (fl, type); }
@@ -2060,23 +2072,6 @@ again: file_lock->fl_flags |=FL_SLEEP ; } - error = -EBADF ; -switch (flock.l_type) { -case F_RDLCK : -if (!(filp->f_mode &FMODE_READ )) - goto out; -break ; -case F_WRLCK : -if (!(filp->f_mode &FMODE_WRITE )) - goto out; -break ; -case F_UNLCK : -break ; -default : - error = -EINVAL ; - goto out; - } - error =do_lock_file_wait (filp, cmd, file_lock);/*
@@ -2178,23 +2173,6 @@ again: file_lock->fl_flags |=FL_SLEEP ; } - error = -EBADF ; -switch (flock.l_type) { -case F_RDLCK : -if (!(filp->f_mode &FMODE_READ )) - goto out; -break ; -case F_WRLCK : -if (!(filp->f_mode &FMODE_WRITE )) - goto out; -break ; -case F_UNLCK : -break ; -default : - error = -EINVAL ; - goto out; - } - error =do_lock_file_wait (filp, cmd, file_lock);/*
Signature · 1 line
1.8.4.2
PATCH v3 3/6 locks: rename locks_remove_flock to locks_remove_file
In reply to Jeff LaytonThis function currently removes leases in addition to flock locks, so the name is misleading. Rename it to locks_remove_file to indicate that it removes locks that are associated with a particular struct file.
--- fs/file_table.c | 2 +- fs/locks.c | 2 +- include/linux/fs.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-)
fs/file_table.c +1 -1
@@ -234,7 +234,7 @@ static void __fput(struct file *file)* in the file cleanup chain. */ eventpoll_release (file); -locks_remove_flock (file); +locks_remove_file (file);if (unlikely (file->f_flags &FASYNC )) {if (file->f_op->fasync)
fs/locks.c +1 -1
@@ -2231,7 +2231,7 @@ EXPORT_SYMBOL(locks_remove_posix);/* * This function is called on the last close of an open file. */ -void locks_remove_flock (struct file *filp) +void locks_remove_file (struct file *filp) {struct inode * inode =file_inode (filp);struct file_lock *fl;
include/linux/fs.h +2 -2
@@ -1012,7 +1012,7 @@ extern struct file_lock * locks_alloc_lock(void);extern void locks_copy_lock (struct file_lock *,struct file_lock *);extern void __locks_copy_lock (struct file_lock *,const struct file_lock *);extern void locks_remove_posix (struct file *,fl_owner_t ); -extern void locks_remove_flock (struct file *); +extern void locks_remove_file (struct file *);extern void locks_release_private (struct file_lock *);extern void posix_test_lock (struct file *,struct file_lock *);extern int posix_lock_file (struct file *,struct file_lock *,struct file_lock *);
@@ -1083,7 +1083,7 @@ static inline void locks_remove_posix(struct file *filp, fl_owner_t owner)return ; } -static inline void locks_remove_flock (struct file *filp) +static inline void locks_remove_file (struct file *filp) {return ; }
Signature · 1 line
1.8.4.2
PATCH v3 4/6 locks: show private lock types in /proc/locks
In reply to Jeff LaytonIn a later patch, we'll be adding a new type of lock that's owned by the struct file instead of the files_struct. Those sorts of locks will be flagged with a new IS_FILE_PVT flag.
Show "private" locks in /proc/locks with a 'P' suffix. This does mean that we need to widen the column by one character.
--- fs/locks.c | 9 ++++++--- include/linux/fs.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-)
fs/locks.c +6 -3
@@ -135,6 +135,7 @@#define IS_POSIX (fl) (fl->fl_flags & FL_POSIX)#define IS_FLOCK (fl) (fl->fl_flags & FL_FLOCK)#define IS_LEASE (fl) (fl->fl_flags & (FL_LEASE|FL_DELEG)) +#define IS_FILE_PVT (fl) (fl->fl_flags & FL_FILE_PVT)static bool lease_breaking (struct file_lock *fl) {
@@ -2358,13 +2359,15 @@ 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 " , +seq_printf (f,"%s " , (fl->fl_type &LOCK_READ ) ? (fl->fl_type &LOCK_WRITE ) ?"RW " :"READ " : (fl->fl_type &LOCK_WRITE ) ?"WRITE" :"NONE " ); + }else if (IS_FILE_PVT (fl)) { +seq_printf (f,"%s " , fl->fl_type ==F_WRLCK ?"WRITEP" :"READP " ); }else { -seq_printf (f,"%s " , - (lease_breaking (fl)) +seq_printf (f,"%s " , +lease_breaking (fl) ? (fl->fl_type ==F_UNLCK ) ?"UNLCK" :"READ " : (fl->fl_type ==F_WRLCK ) ?"WRITE" :"READ " ); }
include/linux/fs.h +1 -0
@@ -888,6 +888,7 @@ static inline int file_check_writeable(struct file *filp)#define FL_SLEEP 128/* A blocking lock */ #define FL_DOWNGRADE_PENDING 256/* Lease is being downgraded */ #define FL_UNLOCK_PENDING 512/* Lease is being broken */ +#define FL_FILE_PVT 1024/* lock is private to the file */ /* * Special return value from posix_lock_file() and vfs_lock_file() for
Signature · 1 line
1.8.4.2
PATCH v3 5/6 locks: report l_pid as -1 for FL_FILE_PVT locks
In reply to Jeff LaytonFL_FILE_PVT locks are no longer tied to a particular pid, and are instead inheritable by child processes. Report a l_pid of '-1' for these sorts of locks since the pid is somewhat meaningless for them.
This precedent comes from FreeBSD. There, POSIX and flock() locks can conflict with one another. If fcntl(F_GETLK, ...) returns a lock set with flock() then the l_pid member cannot be a process ID because the lock is not held by a process as such.
--- fs/locks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/locks.c +2 -2
@@ -1899,7 +1899,7 @@ EXPORT_SYMBOL_GPL(vfs_test_lock);static int posix_lock_to_flock (struct flock *flock,struct file_lock *fl) { - flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid;#if BITS_PER_LONG ==32 /* * Make sure we can represent the posix lock via
@@ -1921,7 +1921,7 @@ static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)#if BITS_PER_LONG ==32 static void posix_lock_to_flock64 (struct flock64 *flock,struct file_lock *fl) { - flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid; flock->l_start = fl->fl_start; flock->l_len = fl->fl_end ==OFFSET_MAX ?0 : fl->fl_end - fl->fl_start +1 ;
Signature · 1 line
1.8.4.2
Re: [PATCH v3 5/6] locks: report l_pid as -1 for FL_FILE_PVT locks
Quoted 28 lines · On Tue, 10 Dec 2013 14:17:34 -0500 Jeff Layton <[email protected]> wrote:
FL_FILE_PVTlocks are no longer tied to a particular pid, and are instead inheritable by child processes. Report al_pidof '-1' for these sorts of locks since the pid is somewhat meaningless for them.This precedent comes from FreeBSD. There, POSIX and
flock()locks can conflict with one another. Iffcntl(F_GETLK, ...)returns a lock set withflock()then thel_pidmember cannot be a process ID because the lock is not held by a process as such.Signed-off-by Jeff Layton <[email protected]>--- fs/locks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)fs/locks.c +1
-1@@ -1899,7 +1899,7 @@ EXPORT_SYMBOL_GPL(vfs_test_lock);static int posix_lock_to_flock (struct flock *flock,struct file_lock *fl) { - flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid;#if BITS_PER_LONG ==32 /* * Make sure we can represent the posix lock via
fs/locks.c static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl) · line 1927
Quoted 8 lines
fs/locks.c +1
-1@@ -1921,7 +1921,7 @@ static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)#if BITS_PER_LONG ==32 static void posix_lock_to_flock64 (struct flock64 *flock,struct file_lock *fl) { - flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid; flock->l_start = fl->fl_start; flock->l_len = fl->fl_end ==OFFSET_MAX ?0 : fl->fl_end - fl->fl_start +1 ;
While I think this behavior is reasonable, I do wonder if we ought to consider more changes to how F_GETLK works. Currently the F_GETLK code won't handle the new l_type values, but maybe it should...
For instance, if there is a conflicting lock, and the F_GETLK caller specified F_RDLCKP or F_WRLCKP, might it make sense to report the l_type on the conflicting lock as F_RDLCKP or F_WRLCKP if that conflicting lock is also a *P type?
...or maybe we should consider a new F_GETLKP cmd value, and a new expanded struct flock that gives more info. The pid is already somewhat meaningless with this sort of lock. Perhaps we could obfuscate the fl_owner value and report that instead? What other sorts of info would be useful to programs that intend to use these new interfaces?
Signature · 1 line
Jeff Layton <[email protected]>
RE: [Nfs-ganesha-devel] [PATCH v3 5/6] locks: report l_pid as -1 for FL_FILE_PVT locks
Quoted 32 lines
Quoted 29 lines · On Tue, 10 Dec 2013 14:17:34 -0500 Jeff Layton <[email protected]> wrote:
FL_FILE_PVTlocks are no longer tied to a particular pid, and are instead inheritable by child processes. Report al_pidof '-1' for these sorts of locks since the pid is somewhat meaningless for them.This precedent comes from FreeBSD. There, POSIX and
flock()locks can conflict with one another. Iffcntl(F_GETLK, ...)returns a lock set withflock()then thel_pidmember cannot be a process ID because the lock is not held by a process as such.Signed-off-by Jeff Layton <[email protected]>--- fs/locks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)fs/locks.c +1
-1@@ -1899,7 +1899,7 @@ EXPORT_SYMBOL_GPL(vfs_test_lock);static int posix_lock_to_flock (struct flock *flock,struct file_lock *fl) { - flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid;#if BITS_PER_LONG ==32 /* * Make sure we can represent the posix lock via @@ -1921,7 +1921,7 @@ static int
posix_lock_to_flock(struct flock *flock, struct file_lock *fl)#ifBITS_PER_LONG== 32 static void
Quoted 8 lines
fs/locks.c static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl) · line 1927
Quoted 6 lines
posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl){fs/locks.c +1
-1- flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid; flock->l_start = fl->fl_start; flock->l_len = fl->fl_end ==OFFSET_MAX ?0 : fl->fl_end - fl->fl_start +1 ;While I think this behavior is reasonable, I do wonder if we ought to
consider
Quoted 4 lines
more changes to how
F_GETLKworks. Currently theF_GETLKcode won't handle the newl_typevalues, but maybe it should...For instance, if there is a conflicting lock, and the
F_GETLKcaller
specified
Quoted 2 lines
F_RDLCKPorF_WRLCKP, might it make sense to report thel_typeon the conflicting lock asF_RDLCKPorF_WRLCKPif that conflicting lock is also
a *P
Quoted 5 lines
type?
...or maybe we should consider a new
F_GETLKPcmd value, and a new expanded struct flock that gives more info. The pid is already somewhat meaningless with this sort of lock. Perhaps we could obfuscate the
fl_owner
Quoted 2 lines
value and report that instead? What other sorts of info would be useful to programs that intend to use these new interfaces?
I always wonder if anyone actually uses the conflicting lock information...
I think returning the lock type as F_RDLCK or F_WRLCK is fine, but maybe an extended F_GETLKP could report the extended type. For Ganesha, we wouldn't use the extended type since the NFS protocol has no concept of separate private and non-private locks (all NFS locks are "private" to the specified owner in the request). The pid is also not that useful to Ganesha.
Frank
Re: [Nfs-ganesha-devel] [PATCH v3 5/6] locks: report l_pid as -1 for FL_FILE_PVT locks
Quoted 58 lines · On Tue, 10 Dec 2013 11:41:04 -0800 "Frank Filz" <[email protected]> wrote:
Quoted 32 lines
Quoted 29 lines · On Tue, 10 Dec 2013 14:17:34 -0500 Jeff Layton <[email protected]> wrote:
FL_FILE_PVTlocks are no longer tied to a particular pid, and are instead inheritable by child processes. Report al_pidof '-1' for these sorts of locks since the pid is somewhat meaningless for them.This precedent comes from FreeBSD. There, POSIX and
flock()locks can conflict with one another. Iffcntl(F_GETLK, ...)returns a lock set withflock()then thel_pidmember cannot be a process ID because the lock is not held by a process as such.Signed-off-by Jeff Layton <[email protected]>--- fs/locks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)fs/locks.c +1
-1@@ -1899,7 +1899,7 @@ EXPORT_SYMBOL_GPL(vfs_test_lock);static int posix_lock_to_flock (struct flock *flock,struct file_lock *fl) { - flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid;#if BITS_PER_LONG ==32 /* * Make sure we can represent the posix lock via @@ -1921,7 +1921,7 @@ static int
posix_lock_to_flock(struct flock *flock, struct file_lock *fl)#ifBITS_PER_LONG== 32 static voidQuoted 8 lines
fs/locks.c static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl) · line 1927
Quoted 6 lines
posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl){fs/locks.c +1
-1- flock->l_pid = fl->fl_pid; + flock->l_pid =IS_FILE_PVT (fl) ?-1 : fl->fl_pid; flock->l_start = fl->fl_start; flock->l_len = fl->fl_end ==OFFSET_MAX ?0 : fl->fl_end - fl->fl_start +1 ;While I think this behavior is reasonable, I do wonder if we ought to
consider
Quoted 4 lines
more changes to how
F_GETLKworks. Currently theF_GETLKcode won't handle the newl_typevalues, but maybe it should...For instance, if there is a conflicting lock, and the
F_GETLKcallerspecified
Quoted 2 lines
F_RDLCKPorF_WRLCKP, might it make sense to report thel_typeon the conflicting lock asF_RDLCKPorF_WRLCKPif that conflicting lock is alsoa *P
Quoted 5 lines
type?
...or maybe we should consider a new
F_GETLKPcmd value, and a new expanded struct flock that gives more info. The pid is already somewhat meaningless with this sort of lock. Perhaps we could obfuscate the
fl_ownerQuoted 2 lines
value and report that instead? What other sorts of info would be useful to programs that intend to use these new interfaces?
Quoted 8 lines
I always wonder if anyone actually uses the conflicting lock information...
I think returning the lock type as
F_RDLCKorF_WRLCKis fine, but maybe an extendedF_GETLKPcould report the extended type. For Ganesha, we wouldn't use the extended type since the NFS protocol has no concept of separate private and non-private locks (all NFS locks are "private" to the specified owner in the request). The pid is also not that useful to Ganesha.
Yeah, the value of that has always been a little questionable to me too...
FWIW, we don't actually need a new cmd value to get that. What we could do is simply report the new lock types in there iff the F_GETLK request had a F_RDLCKP or F_WRLCKP l_type value. If a classic l_type value was specified though, then I think we'd need to report only classic l_type values for the conflicting lock.
That said, since the utility of that isn't completely clear, it's probably best not to try to do anything too fancy here. We could always add that later if we need it.
Thanks...
Signature · 1 line
Jeff Layton <[email protected]>
PATCH v3 6/6 locks: add new "private" lock type that is owned by the filp
In reply to Jeff LaytonDue to some unfortunate history, POSIX locks have very strange and unhelpful semantics. The thing that usually catches people by surprise is that they are dropped whenever the process closes any file descriptor associated with the inode.
This is extremely problematic for people developing file servers that need to implement byte-range locks. Developers often need a "lock management" facility to ensure that file descriptors are not closed until all of the locks associated with the inode are finished.
Additionally, "classic" POSIX locks are owned by the process. Locks taken between threads within the same process won't conflict with one another, which renders classic POSIX locks useless for synchronization between threads.
This patchset adds a new type of lock that attempts to address these issues. These locks work just like classic POSIX read/write locks, but have semantics that are more like BSD locks with respect to inheritance and behavior on close.
This is implemented primarily by changing how fl_owner field is set for these locks. Instead of having them owned by the files_struct of the process, they are instead owned by the filp on which they were acquired. Thus, they are inherited across fork() and are only released when the last reference to a filp is put.
These new semantics prevent them from being merged with classic POSIX locks, even if they are acquired by the same process. These locks will also conflict with classic POSIX locks even if they are acquired by the same process or on the same file descriptor.
--- fs/locks.c | 32 ++++++++++++++++++++++++++++++-- include/uapi/asm-generic/fcntl.h | 16 ++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-)
fs/locks.c +30 -2
@@ -367,7 +367,34 @@ flock_to_posix_lock_common(struct file_lock *fl, struct file *filp, short type)break ; } -return assign_type (fl, type); +/* +* FL_FILE_PVT locks are "owned" by the filp upon which they were +* acquired, regardless of what task is dealing with them. Set the +* fl_owner appropriately and flag them as private. +*/ +switch (type) { +case F_RDLCKP : + fl->fl_owner = (fl_owner_t )fl->fl_file; + fl->fl_type =F_RDLCK ; + fl->fl_flags |=FL_FILE_PVT ; +break ; +case F_WRLCKP : + fl->fl_owner = (fl_owner_t )fl->fl_file; + fl->fl_type =F_WRLCK ; + fl->fl_flags |=FL_FILE_PVT ; +break ; +case F_UNLCKP : + fl->fl_owner = (fl_owner_t )fl->fl_file; + fl->fl_type =F_UNLCK ; + fl->fl_flags |=FL_FILE_PVT ; +break ; +default : +/* Any other POSIX lock is owned by the file_struct */ + fl->fl_owner = current->files; +return assign_type (fl, type); + } + +return 0 ; }/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
@@ -2259,7 +2286,8 @@ void locks_remove_file(struct file *filp)while ((fl = *before) !=NULL ) {if (fl->fl_file == filp) { -if (IS_FLOCK (fl)) { +if (IS_FLOCK (fl) || + (IS_POSIX (fl) &&IS_FILE_PVT (fl))) {locks_delete_lock (before);continue ; }
include/uapi/asm-generic/fcntl.h +16 -0
@@ -151,6 +151,22 @@ struct f_owner_ex {#define F_UNLCK 2#endif +/* +* fd "private" POSIX locks. +* +* Usually POSIX locks held by a process are released on *any* close and are +* not inherited across a fork(). +* +* These lock types will conflict with normal POSIX locks, but are "owned" +* by the opened file, not the process. This means that they are inherited +* across fork() like BSD (flock) locks, and they are only released +* automatically when the last reference to the the open file against which +* they were acquired is put. +*/ +#define F_RDLCKP 5 +#define F_WRLCKP 6 +#define F_UNLCKP 7 +/* for old implementation of bsd flock () */ #ifndef F_EXLCK #define F_EXLCK 4/* or 3 */
Signature · 1 line
1.8.4.2
Re: [PATCH v3 6/6] locks: add new "private" lock type that is owned by the filp
Quoted 104 lines · On Tue, 10 Dec 2013 14:17:35 -0500 Jeff Layton <[email protected]> wrote:
Due to some unfortunate history, POSIX locks have very strange and unhelpful semantics. The thing that usually catches people by surprise is that they are dropped whenever the process closes any file descriptor associated with the inode.
This is extremely problematic for people developing file servers that need to implement byte-range locks. Developers often need a "lock management" facility to ensure that file descriptors are not closed until all of the locks associated with the inode are finished.
Additionally, "classic" POSIX locks are owned by the process. Locks taken between threads within the same process won't conflict with one another, which renders classic POSIX locks useless for synchronization between threads.
This patchset adds a new type of lock that attempts to address these issues. These locks work just like classic POSIX read/write locks, but have semantics that are more like BSD locks with respect to inheritance and behavior on close.
This is implemented primarily by changing how
fl_ownerfield is set for these locks. Instead of having them owned by thefiles_structof the process, they are instead owned by the filp on which they were acquired. Thus, they are inherited acrossfork()and are only released when the last reference to a filp is put.These new semantics prevent them from being merged with classic POSIX locks, even if they are acquired by the same process. These locks will also conflict with classic POSIX locks even if they are acquired by the same process or on the same file descriptor.
Signed-off-by Jeff Layton <[email protected]>--- fs/locks.c | 32 ++++++++++++++++++++++++++++++-- include/uapi/asm-generic/fcntl.h | 16 ++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-)fs/locks.c +30
-2@@ -367,7 +367,34 @@ flock_to_posix_lock_common(struct file_lock *fl, struct file *filp, short type)break ; } -return assign_type (fl, type); +/* +* FL_FILE_PVT locks are "owned" by the filp upon which they were +* acquired, regardless of what task is dealing with them. Set the +* fl_owner appropriately and flag them as private. +*/ +switch (type) { +case F_RDLCKP : + fl->fl_owner = (fl_owner_t )fl->fl_file; + fl->fl_type =F_RDLCK ; + fl->fl_flags |=FL_FILE_PVT ; +break ; +case F_WRLCKP : + fl->fl_owner = (fl_owner_t )fl->fl_file; + fl->fl_type =F_WRLCK ; + fl->fl_flags |=FL_FILE_PVT ; +break ; +case F_UNLCKP : + fl->fl_owner = (fl_owner_t )fl->fl_file; + fl->fl_type =F_UNLCK ; + fl->fl_flags |=FL_FILE_PVT ; +break ; +default : +/* Any other POSIX lock is owned by the file_struct */ + fl->fl_owner = current->files; +return assign_type (fl, type); + } + +return 0 ; }/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX @@ -2259,7 +2286,8 @@ void locks_remove_file(struct file *filp)while ((fl = *before) !=NULL ) {if (fl->fl_file == filp) { -if (IS_FLOCK (fl)) { +if (IS_FLOCK (fl) || + (IS_POSIX (fl) &&IS_FILE_PVT (fl))) {locks_delete_lock (before);continue ; }include/uapi/asm-generic/fcntl.h +9
-0@@ -151,6 +151,22 @@ struct f_owner_ex {#define F_UNLCK 2#endif +/* +* fd "private" POSIX locks. +* +* Usually POSIX locks held by a process are released on *any* close and are +* not inherited across a fork(). +* +* These lock types will conflict with normal POSIX locks, but are "owned" +* by the opened file, not the process. This means that they are inherited +* across fork() like BSD (flock) locks, and they are only released
include/uapi/asm-generic/fcntl.h struct f_owner_ex { · line 172
Quoted 10 lines
include/uapi/asm-generic/fcntl.h +7
-0+* automatically when the last reference to the the open file against which +* they were acquired is put. +*/ +#define F_RDLCKP 5 +#define F_WRLCKP 6 +#define F_UNLCKP 7 +/* for old implementation of bsd flock () */ #ifndef F_EXLCK #define F_EXLCK 4/* or 3 */
So, I think the above semantics are pretty clear, but now that I've had a go at sitting down to document this stuff for the POSIX spec and manpages, it's clear how convoluted the text in there is becoming.
That makes me wonder...would we be better off with a new set of cmd values here instead of new l_type values? IOW, we could add new:
F_GETLKPF_SETLKPF_SETLKPW
...and then just reuse the same F_RDLCK/F_WRLCK/F_UNLCK values? With that too, we could create a new equivalent to struct flock that has fixed length types instead of dealing with the off_t mess.
I think doing that would make it a little easier to document, at the expense of being a little trickier to code up for all of the different arches. What would be the most intuitive interface from the standpoint of a userland developer?
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v3 6/6] locks: add new "private" lock type that is owned by the filp
Quoted 10 lines · Jeff Layton
So, I think the above semantics are pretty clear, but now that I've had a go at sitting down to document this stuff for the POSIX spec and manpages, it's clear how convoluted the text in there is becoming.
That makes me wonder...would we be better off with a new set of cmd values here instead of new
l_typevalues? IOW, we could add new:
F_GETLKPF_SETLKPF_SETLKPW
That seems a tad cleaner to me indeed.
Quoted 3 lines
...and then just reuse the same F_RDLCK/F_WRLCK/F_UNLCK values? With that too, we could create a new equivalent to struct flock that has fixed length types instead of dealing with the
off_tmess.
For the Posix interface you'd need an off_t as that's what the whole API uses for file offsets. We could make sure to always use a off64_t for the kernel interface though.
What is the API you propose to posix? An new posix_lockf?
Re: [PATCH v3 6/6] locks: add new "private" lock type that is owned by the filp
Quoted 14 lines · On Tue, 17 Dec 2013 05:37:21 -0800 Christoph Hellwig <[email protected]> wrote:
Quoted 10 lines · Jeff Layton
So, I think the above semantics are pretty clear, but now that I've had a go at sitting down to document this stuff for the POSIX spec and manpages, it's clear how convoluted the text in there is becoming.
That makes me wonder...would we be better off with a new set of cmd values here instead of new
l_typevalues? IOW, we could add new:
F_GETLKPF_SETLKPF_SETLKPWThat seems a tad cleaner to me indeed.
Quoted 8 lines
Quoted 3 lines
...and then just reuse the same F_RDLCK/F_WRLCK/F_UNLCK values? With that too, we could create a new equivalent to struct flock that has fixed length types instead of dealing with the
off_tmess.For the Posix interface you'd need an
off_tas that's what the whole API uses for file offsets. We could make sure to always use aoff64_tfor the kernel interface though.
Ok.
Quoted 2 lines
What is the API you propose to posix? An new
posix_lockf?
I haven't proposed anything concrete to POSIX just yet. I'm trying to get the Linux patches done and then I'll do that. I don't think we want a new syscall when fcntl() will work.
If we use new cmd values however, then we don't necessarily need to use struct flock/flock64. I think the question is -- should we stick with struct flock/flock64, or would we be better served with something new for this?
Thanks,
Signature · 1 line
Jeff Layton <[email protected]>
Quoted 1 line
This patchset is the third posting of this set. Behavior between this set
and
Quoted 21 lines
the last should be more or less the same. Here is a summary of changes:
v3:
- more consolidation of common code betweenflock_to_posix_lockandflock_to_posix_lock64
- better bisectability by reordering changes, such that partial implementation won't be exposed
- s/filp/file/ s/FILP/FILE/ in symbol namesv2:
- inheritance semantics have been change to be more BSD-lock like
- patchset size has been reduced by changing how lock ownership is handled
- newF_UNLCKPl_typevalue has been addedNote too that I've gone ahead and opened a request for the POSIX folks to consider adding this to the POSIX spec once we have something mergeable. They seem amenable to the idea but don't want to enshrine it into the standard until there's a real implementation of it:
Quoted 6 lines
http://austingroupbugs.net/view.php?id=768I also owe them a better writeup of the semantics for these new locks, but have been holding off on doing that until they're a little more settled.
As a side note, I've also had a few other userland developers reach out to
me
Quoted 1 line
as to the status of this work. There seems to be a lot of interest since
classic
Quoted 2 lines
POSIX locks are such a pain to work with in threaded programs. Hopefully some will chime in on this posting...
This is looking really good to me. I will be excited to utilize this capability in Ganesha.
Thanks
Frank
Quoted 6 lines
Original cover letter from v1 posting follows. Comments and suggestions welcome.
-------------------------------[snip]------------------------------
At LSF this year, there was a discussion about the "wishlist" for userland
file
Quoted 2 lines
servers. One of the things brought up was the goofy and problematic behavior of POSIX locks when a file is closed. Boaz started a thread on it
here:
Quoted 7 lines
http://permalink.gmane.org/gmane.linux.file-systems/73364Userland fileservers often need to maintain more than one open file descriptor on a file. The POSIX spec says:
"All locks associated with a file for a given process shall be removed
when a
Quoted 1 line
file descriptor for that file is closed by that process or the process
holding
Quoted 3 lines
that file descriptor terminates."
This is problematic since you can't close any file descriptor without
dropping
Quoted 1 line
all your POSIX locks. Most userland file servers therefore end up opening
the
Quoted 4 lines
file with more access than is really necessary, and keeping fd's open for longer than is necessary to work around this.
This patchset is a first stab at an approach to address this problem by
adding
Quoted 5 lines
two new
l_typevalues --F_RDLCKPandF_WRLCKP(the 'P' is short for "private" -- I'm open to changing that if you have a better mnemonic).For all intents and purposes these lock types act just like their "non-P" counterpart. The difference is that they are only implicitly released when
the
Quoted 1 line
fd against which they were acquired is closed. As a side effect, these
locks
Quoted 4 lines
cannot be merged with "non-P" locks since they have different semantics on close.
I've given this patchset some very basic smoke testing and it seems to do
the
Quoted 1 line
right thing, but it is still pretty rough. If this looks reasonable I'll
plan to do
Quoted 20 lines
some documentation updates and will take a stab at trying to get these new lock types added to the POSIX spec (as HCH recommended).
At this point, my main questions are:
1) does this look useful, particularly for fileserver implementors?
2) does this look OK API-wise? We could consider different "cmd" values or even different syscalls, but I figured this makes it clearer that "P" and "non-P" locks will still conflict with one another.
Jeff Layton (6):
locks: consolidate common code in theflock_to_posix_lockroutines
locks: consolidate checks for compatiblefilp->f_modevalues in setlk
handlers
locks: renamelocks_remove_flocktolocks_remove_file
locks: show private lock types in/proc/locks
locks: reportl_pidas -1 forFL_FILE_PVTlocks
locks: add new "private" lock type that is owned by the filp
Quoted 2 lines
fs/file_table.c | 2 +- fs/locks.c | 122
+++++++++++++++++++++------------------
Quoted 9 lines
include/linux/fs.h | 5 +- include/uapi/asm-generic/fcntl.h | 16 +++++ 4 files changed, 85 insertions(+), 60 deletions(-)--
1.8.4.2
----------------------------------------------------------------------------
--
Quoted 11 lines
Rapidly troubleshoot problems before they affect your business. Most IT organizations don't have a clear picture of how application performance affects their revenue. With AppDynamics, you get 100% visibility into your Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clk
trk
_______________________________________________
Nfs-ganesha-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs-ganesha-devel