Message ID | 1392741464-20029-1-git-send-email-linkinjeon@gmail.com |
---|---|
State | Superseded, archived |
Headers | show |
On Wed, Feb 19, 2014 at 01:37:43AM +0900, Namjae Jeon wrote: > + /* > + * There is no need to overlap collapse range with EOF, in which case > + * it is effectively a truncate operation > + */ > + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && > + (offset + len >= i_size_read(inode))) > + return -EINVAL; > + I wonder if we should just translate a collapse range that is equivalent to a truncate operation to, in fact, be a truncate operation? - Ted -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Sat, Feb 22, 2014 at 09:06:25AM -0500, Theodore Ts'o wrote: > On Wed, Feb 19, 2014 at 01:37:43AM +0900, Namjae Jeon wrote: > > + /* > > + * There is no need to overlap collapse range with EOF, in which case > > + * it is effectively a truncate operation > > + */ > > + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && > > + (offset + len >= i_size_read(inode))) > > + return -EINVAL; > > + > > I wonder if we should just translate a collapse range that is > equivalent to a truncate operation to, in fact, be a truncate > operation? Trying to collapse a range that extends beyond EOF, IMO, is likely to only happen if the DVR/NLE application is buggy. Hence I think that telling the application it is doing something that is likely to be wrong is better than silently truncating the file.... Cheers, Dave.
On Mon, 24 Feb 2014, Dave Chinner wrote: > On Sat, Feb 22, 2014 at 09:06:25AM -0500, Theodore Ts'o wrote: > > On Wed, Feb 19, 2014 at 01:37:43AM +0900, Namjae Jeon wrote: > > > + /* > > > + * There is no need to overlap collapse range with EOF, in which case > > > + * it is effectively a truncate operation > > > + */ > > > + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && > > > + (offset + len >= i_size_read(inode))) > > > + return -EINVAL; > > > + > > > > I wonder if we should just translate a collapse range that is > > equivalent to a truncate operation to, in fact, be a truncate > > operation? > > Trying to collapse a range that extends beyond EOF, IMO, is likely > to only happen if the DVR/NLE application is buggy. Hence I think > that telling the application it is doing something that is likely to > be wrong is better than silently truncating the file.... I do agree with Ted on this point. This is not an xfs ioctl added for one DVR/NLE application, it's a mode of a Linux system call. We do not usually reject with an error when one system call happens to ask for something which can already be accomplished another way; nor nanny our callers. It seems natural to me that COLLAPSE_RANGE should support beyond EOF; unless that adds significantly to implementation difficulties? Actually, is it even correct to fail at EOF? What if fallocation with FALLOC_FL_KEEP_SIZE was used earlier, to allocate beyond EOF: shouldn't it be possible to shift that allocation down, along with the EOF, rather than leave it behind as a stranded island? Hugh -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Feb 25, 2014 at 03:41:20PM -0800, Hugh Dickins wrote: > On Mon, 24 Feb 2014, Dave Chinner wrote: > > On Sat, Feb 22, 2014 at 09:06:25AM -0500, Theodore Ts'o wrote: > > > On Wed, Feb 19, 2014 at 01:37:43AM +0900, Namjae Jeon wrote: > > > > + /* > > > > + * There is no need to overlap collapse range with EOF, in which case > > > > + * it is effectively a truncate operation > > > > + */ > > > > + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && > > > > + (offset + len >= i_size_read(inode))) > > > > + return -EINVAL; > > > > + > > > > > > I wonder if we should just translate a collapse range that is > > > equivalent to a truncate operation to, in fact, be a truncate > > > operation? > > > > Trying to collapse a range that extends beyond EOF, IMO, is likely > > to only happen if the DVR/NLE application is buggy. Hence I think > > that telling the application it is doing something that is likely to > > be wrong is better than silently truncating the file.... > > I do agree with Ted on this point. This is not an xfs ioctl added > for one DVR/NLE application, it's a mode of a Linux system call. > > We do not usually reject with an error when one system call happens > to ask for something which can already be accomplished another way; > nor nanny our callers. > > It seems natural to me that COLLAPSE_RANGE should support beyond EOF; > unless that adds significantly to implementation difficulties? Yes, it does add to the implementation complexity significantly - it adds data security issues that don't exist with the current API. That is, Filesystems can have uninitialised blocks beyond EOF so if we allow COLLAPSE_RANGE to shift them down within EOF, we now have to ensure they are properly zeroed or marked as unwritten. It also makes implementations more difficult. For example, XFS can also have in-memory delayed allocation extents beyond EOF, and they can't be brought into the range < EOF without either: a) inserting zeroed pages with appropriately set up and mapped bufferheads into the page cache for the range that sits within EOF; or b) truncating the delalloc extents beyond EOF before the move So, really, the moment you go beyond EOF filesystems have to do quite a bit more validation and IO in the context of the system call. It no longer becomes a pure extent manipulation offload - it becomes a data security problem. And, indeed, the specification that we are working to is that the applications that want to collapse the range of a file are using this function instead of read/memcpy/write/truncate, which by definition means they cannot shift ranges of the file beyond EOF into the new file. So IMO the API defines the functionality as required by the applications that require it and *no more*. If you need some different behaviour - we can add it via additional flags in future when you have an application that requires it. > Actually, is it even correct to fail at EOF? What if fallocation > with FALLOC_FL_KEEP_SIZE was used earlier, to allocate beyond EOF: > shouldn't it be possible to shift that allocation down, along with > the EOF, rather than leave it behind as a stranded island? It does get shifted down - it just remains beyond EOF, just like it was before the operation. And that is part of the specification of COLLAPSE_RANGE - it was done so that preallocation (physical or speculative delayed allocation) beyond EOF to avoid fragmentation as the DVR continues to write is not screwed up by chopping out earlier parts of the file. Cheers, Dave.
On Wed, 26 Feb 2014, Dave Chinner wrote: > On Tue, Feb 25, 2014 at 03:41:20PM -0800, Hugh Dickins wrote: > > On Mon, 24 Feb 2014, Dave Chinner wrote: > > > On Sat, Feb 22, 2014 at 09:06:25AM -0500, Theodore Ts'o wrote: > > > > On Wed, Feb 19, 2014 at 01:37:43AM +0900, Namjae Jeon wrote: > > > > > + /* > > > > > + * There is no need to overlap collapse range with EOF, in which case > > > > > + * it is effectively a truncate operation > > > > > + */ > > > > > + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && > > > > > + (offset + len >= i_size_read(inode))) > > > > > + return -EINVAL; > > > > > + > > > > > > > > I wonder if we should just translate a collapse range that is > > > > equivalent to a truncate operation to, in fact, be a truncate > > > > operation? > > > > > > Trying to collapse a range that extends beyond EOF, IMO, is likely > > > to only happen if the DVR/NLE application is buggy. Hence I think > > > that telling the application it is doing something that is likely to > > > be wrong is better than silently truncating the file.... > > > > I do agree with Ted on this point. This is not an xfs ioctl added > > for one DVR/NLE application, it's a mode of a Linux system call. > > > > We do not usually reject with an error when one system call happens > > to ask for something which can already be accomplished another way; > > nor nanny our callers. > > > > It seems natural to me that COLLAPSE_RANGE should support beyond EOF; > > unless that adds significantly to implementation difficulties? > > Yes, it does add to the implementation complexity significantly - it > adds data security issues that don't exist with the current API. > > That is, Filesystems can have uninitialised blocks beyond EOF so > if we allow COLLAPSE_RANGE to shift them down within EOF, we now > have to ensure they are properly zeroed or marked as unwritten. > > It also makes implementations more difficult. For example, XFS can > also have in-memory delayed allocation extents beyond EOF, and they > can't be brought into the range < EOF without either: > > a) inserting zeroed pages with appropriately set up > and mapped bufferheads into the page cache for the range > that sits within EOF; or > b) truncating the delalloc extents beyond EOF before the > move > > So, really, the moment you go beyond EOF filesystems have to do > quite a bit more validation and IO in the context of the system > call. It no longer becomes a pure extent manipulation offload - it > becomes a data security problem. Those sound like problems you would already have solved for a simple extending truncate. But I wasn't really thinking of the offset > i_size case, just the offset + len >= i_size case: which would end with i_size at offset, and the areas you're worried about still beyond EOF - or am I confused? > > And, indeed, the specification that we are working to is that the > applications that want to collapse the range of a file are using > this function instead of read/memcpy/write/truncate, which by > definition means they cannot shift ranges of the file beyond EOF > into the new file. > > So IMO the API defines the functionality as required by the > applications that require it and *no more*. If you need some > different behaviour - we can add it via additional flags in future > when you have an application that requires it. You still seem to be thinking in terms of xfs ioctl hacks, rather than fully scoped Linux system calls. But it probably doesn't matter too much, if we start with an error, and later correct that to a full implementation - an xfstest or LTP test which expected failure will fail once it sees success, but no users harmed in the making of this change. > > > Actually, is it even correct to fail at EOF? What if fallocation > > with FALLOC_FL_KEEP_SIZE was used earlier, to allocate beyond EOF: > > shouldn't it be possible to shift that allocation down, along with > > the EOF, rather than leave it behind as a stranded island? > > It does get shifted down - it just remains beyond EOF, just like it > was before the operation. And that is part of the specification of > COLLAPSE_RANGE - it was done so that preallocation (physical or > speculative delayed allocation) beyond EOF to avoid fragmentation as > the DVR continues to write is not screwed up by chopping out earlier > parts of the file. Yes, I was confused when I pictured a stranded island there. Hugh -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Feb 25, 2014 at 09:25:40PM -0800, Hugh Dickins wrote: > On Wed, 26 Feb 2014, Dave Chinner wrote: > > On Tue, Feb 25, 2014 at 03:41:20PM -0800, Hugh Dickins wrote: > > > On Mon, 24 Feb 2014, Dave Chinner wrote: > > > > On Sat, Feb 22, 2014 at 09:06:25AM -0500, Theodore Ts'o wrote: > > > > > On Wed, Feb 19, 2014 at 01:37:43AM +0900, Namjae Jeon wrote: > > > > > > + /* > > > > > > + * There is no need to overlap collapse range with EOF, in which case > > > > > > + * it is effectively a truncate operation > > > > > > + */ > > > > > > + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && > > > > > > + (offset + len >= i_size_read(inode))) > > > > > > + return -EINVAL; > > > > > > + > > > > > > > > > > I wonder if we should just translate a collapse range that is > > > > > equivalent to a truncate operation to, in fact, be a truncate > > > > > operation? > > > > > > > > Trying to collapse a range that extends beyond EOF, IMO, is likely > > > > to only happen if the DVR/NLE application is buggy. Hence I think > > > > that telling the application it is doing something that is likely to > > > > be wrong is better than silently truncating the file.... > > > > > > I do agree with Ted on this point. This is not an xfs ioctl added > > > for one DVR/NLE application, it's a mode of a Linux system call. > > > > > > We do not usually reject with an error when one system call happens > > > to ask for something which can already be accomplished another way; > > > nor nanny our callers. > > > > > > It seems natural to me that COLLAPSE_RANGE should support beyond EOF; > > > unless that adds significantly to implementation difficulties? > > > > Yes, it does add to the implementation complexity significantly - it > > adds data security issues that don't exist with the current API. > > > > That is, Filesystems can have uninitialised blocks beyond EOF so > > if we allow COLLAPSE_RANGE to shift them down within EOF, we now > > have to ensure they are properly zeroed or marked as unwritten. > > > > It also makes implementations more difficult. For example, XFS can > > also have in-memory delayed allocation extents beyond EOF, and they > > can't be brought into the range < EOF without either: > > > > a) inserting zeroed pages with appropriately set up > > and mapped bufferheads into the page cache for the range > > that sits within EOF; or > > b) truncating the delalloc extents beyond EOF before the > > move > > > > So, really, the moment you go beyond EOF filesystems have to do > > quite a bit more validation and IO in the context of the system > > call. It no longer becomes a pure extent manipulation offload - it > > becomes a data security problem. > > Those sound like problems you would already have solved for a > simple extending truncate. Yes, they have because of what truncate defines - that the region between the old EOF and the new EOF must contain zeroes. truncate does not move blocks around, it merely changes the EOF, and so the solution is simple. > But I wasn't really thinking of the offset > i_size case, just the > offset + len >= i_size case: which would end with i_size at offset, > and the areas you're worried about still beyond EOF - or am I confused? Right, offset beyond EOF is just plain daft. But you're not thinking of the entire picture. What happens when a system crashes half way through a collapse operation? On tmpfs you don't care - everything is lost, but on real filesystems we have to care about. offset + len beyond EOF is just truncate(offset). From the point of view of an application offloading a data movement operation via collapse range, any range that overlaps EOF is wrong - data beyond EOF is not accessible and is not available for the application to move. Hence EINVAL - it's an invalid set of parameters. If we do allow it and implement it by block shifting (which, technically, is the correct interpretation of the collapse range behaviour because it preserves preallocation beyond the collapsed region beyond EOF), then we have thr problem of moving data blocks below EOF by extent shifting before we change the EOF. That exposes blocks of undefined content to the user if we crash and recover up to that point of the operation. It's just plain dangerous, and if we allow this possibility via the API, someone is going to make that mistake in a filesystem because it's difficult to test and hard to get right. > > And, indeed, the specification that we are working to is that the > > applications that want to collapse the range of a file are using > > this function instead of read/memcpy/write/truncate, which by > > definition means they cannot shift ranges of the file beyond EOF > > into the new file. > > > > So IMO the API defines the functionality as required by the > > applications that require it and *no more*. If you need some > > different behaviour - we can add it via additional flags in future > > when you have an application that requires it. > > You still seem to be thinking in terms of xfs ioctl hacks, > rather than fully scoped Linux system calls. Low blow, referee! :/ To set the record straight, this fallocate interface was originally scoped and implemented for ext4, then extended to XFS by request. it's never been near an XFS ioctl interface in it's life. Besides, you're completely off the ball here. This isn't a fully scoped Linux system call - that's what fallocate() is. fallocate() is designed to be as extensible as possible within the constraints of it's three control parameters (flags, offset, length). This was done so we could add new allocation primitives for filesystem offloads in the future. That's *exactly* what collapse range is - it is using fallocate in the way it was intended to be used. FWIW, consider the the "can't change EOF with the hole punch primitive" rule we defined. We did that because hole punch needs to work beyond EOF without changing EOF because having a hole punch that removes preallocation beyond EOF extend the file is just plain daft. Hence if you want to punch a hole that crosses EOF and set EOF to the start of the hole a the same time *you have to use truncate*. Because, by definition, that's a *truncate operation*, not a *hole punch operation*. Now, consider the collapse range primitive and apply the same logic: Applications cannot access data beyond EOF. Allowing them to collapse a range past EOF is effectively saying "bring data beyond EOF down into the file, then set EOF so you can't access the data that was beyond the old EOF. It has potential for stale data exposure, especially if the operation is fatally interrupted (e.g. by a system crash or filesystem shutdown). Hence if you want to collapse a range that crosses EOF and set EOF to the start of the range a the same time *you have to use truncate*. Because, by definition, that's a *truncate operation*, not a *collapse range operation*. > But it probably doesn't matter too much, if we start with an error, > and later correct that to a full implementation - an xfstest or LTP > test which expected failure will fail once it sees success, but no > users harmed in the making of this change. If we want different behaviour in future, then we define a new control flag to indicate that we want the different behaviour. Yet another reason we designed the fallocate syscall to be extensible.... Cheers, Dave.
On Wed, 26 Feb 2014, Dave Chinner wrote: > On Tue, Feb 25, 2014 at 09:25:40PM -0800, Hugh Dickins wrote: > > > But I wasn't really thinking of the offset > i_size case, just the > > offset + len >= i_size case: which would end with i_size at offset, > > and the areas you're worried about still beyond EOF - or am I confused? > > Right, offset beyond EOF is just plain daft. But you're not thinking > of the entire picture. What happens when a system crashes half way > through a collapse operation? On tmpfs you don't care - everything > is lost, but on real filesystems we have to care about. > > offset + len beyond EOF is just truncate(offset). > > From the point of view of an application offloading a data movement > operation via collapse range, any range that overlaps EOF is wrong - > data beyond EOF is not accessible and is not available for the > application to move. Hence EINVAL - it's an invalid set of > parameters. > > If we do allow it and implement it by block shifting (which, > technically, is the correct interpretation of the collapse range > behaviour because it preserves preallocation beyond > the collapsed region beyond EOF), then we have > thr problem of moving data blocks below EOF by extent shifting > before we change the EOF. That exposes blocks of undefined content > to the user if we crash and recover up to that point of the > operation. It's just plain dangerous, and if we allow this > possibility via the API, someone is going to make that mistake in a > filesystem because it's difficult to test and hard to get right. Again, I would have thought that this is a problem you are already having to solve in the case when offset + len is below EOF, with blocks of undefined content preallocated beyond EOF. But I don't know xfs, you do: so I accept there may be subtle reasons why the offset + len below EOF case is easier for you to handle - and please don't spend your time trying to hammer those into my head! I think I've been somewhat unreasonable: I insisted in the other thread that "Collapse is significantly more challenging than either hole-punch or truncation", so I should give you a break, not demand that you provide a perfect smooth implementation in all circumstances. None of our filesystems were designed with this operation in mind, each may have its own sound reasons to reject those peculiare cases which would pose more trouble and risk than they are worth. Whether that should be enforced at the VFS level is anther matter: if it turns out that the xfs and ext4 limitations match up, okay. I think we have different preferences, for whether to return error or success, when there is nothing to be done; but I notice now that fallocate fails on len 0, so you are being consistent with that. Reject "offset + len >= i_size" or "offset + len > i_size"? Hugh -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/fs/open.c b/fs/open.c index 4b3e1ed..4a923a5 100644 --- a/fs/open.c +++ b/fs/open.c @@ -231,7 +231,8 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) return -EINVAL; /* Return error if mode is not supported */ - if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_COLLAPSE_RANGE)) return -EOPNOTSUPP; /* Punch hole must have keep size set */ @@ -239,11 +240,20 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) !(mode & FALLOC_FL_KEEP_SIZE)) return -EOPNOTSUPP; + /* Collapse range should only be used exclusively. */ + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && + (mode & ~FALLOC_FL_COLLAPSE_RANGE)) + return -EINVAL; + if (!(file->f_mode & FMODE_WRITE)) return -EBADF; - /* It's not possible punch hole on append only file */ - if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode)) + /* + * It's not possible to punch hole or perform collapse range + * on append only file + */ + if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE) + && IS_APPEND(inode)) return -EPERM; if (IS_IMMUTABLE(inode)) @@ -271,6 +281,14 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) return -EFBIG; + /* + * There is no need to overlap collapse range with EOF, in which case + * it is effectively a truncate operation + */ + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && + (offset + len >= i_size_read(inode))) + return -EINVAL; + if (!file->f_op->fallocate) return -EOPNOTSUPP; diff --git a/include/uapi/linux/falloc.h b/include/uapi/linux/falloc.h index 990c4cc..5ff562d 100644 --- a/include/uapi/linux/falloc.h +++ b/include/uapi/linux/falloc.h @@ -5,5 +5,26 @@ #define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */ #define FALLOC_FL_NO_HIDE_STALE 0x04 /* reserved codepoint */ +/* + * FALLOC_FL_COLLAPSE_RANGE is used to remove a range of a file + * without leaving a hole in the file. The contents of the file beyond + * the range being removed is appended to the start offset of the range + * being removed (i.e. the hole that was punched is "collapsed"), + * resulting in a file layout that looks like the range that was + * removed never existed. As such collapsing a range of a file changes + * the size of the file, reducing it by the same length of the range + * that has been removed by the operation. + * + * Different filesystems may implement different limitations on the + * granularity of the operation. Most will limit operations to + * filesystem block size boundaries, but this boundary may be larger or + * smaller depending on the filesystem and/or the configuration of the + * filesystem or file. + * + * Attempting to collapse a range that crosses the end of the file is + * considered an illegal operation - just use ftruncate(2) if you need + * to collapse a range that crosses EOF. + */ +#define FALLOC_FL_COLLAPSE_RANGE 0x08 #endif /* _UAPI_FALLOC_H_ */