Skip to content

Commit

Permalink
btrfs: make reflinks respect O_SYNC O_DSYNC and S_SYNC flags
Browse files Browse the repository at this point in the history
If we reflink to or from a file opened with O_SYNC/O_DSYNC or to/from a
file that has the S_SYNC attribute set, we totally ignore that and do not
durably persist the reflink changes. Since a reflink can change the data
readable from a file (and mtime/ctime, or a file size), it makes sense to
durably persist (fsync) the source and destination files/ranges.

This was previously discussed at:

https://lore.kernel.org/linux-btrfs/20200903035225.GJ6090@magnolia/

The recently introduced test case generic/628, from fstests, exercises
these scenarios and currently fails without this change.

So make sure we fsync the source and destination files/ranges when either
of them was opened with O_SYNC/O_DSYNC or has the S_SYNC attribute set,
just like XFS already does.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
Filipe Manana authored and David Sterba committed Apr 19, 2021
1 parent bb05b29 commit b7a7a83
Showing 1 changed file with 25 additions and 0 deletions.
25 changes: 25 additions & 0 deletions fs/btrfs/reflink.c
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,16 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in,
len, remap_flags);
}

static bool file_sync_write(const struct file *file)
{
if (file->f_flags & (__O_SYNC | O_DSYNC))
return true;
if (IS_SYNC(file_inode(file)))
return true;

return false;
}

loff_t btrfs_remap_file_range(struct file *src_file, loff_t off,
struct file *dst_file, loff_t destoff, loff_t len,
unsigned int remap_flags)
Expand Down Expand Up @@ -871,5 +881,20 @@ loff_t btrfs_remap_file_range(struct file *src_file, loff_t off,
unlock_two_nondirectories(src_inode, dst_inode);
}

/*
* If either the source or the destination file was opened with O_SYNC,
* O_DSYNC or has the S_SYNC attribute, fsync both the destination and
* source files/ranges, so that after a successful return (0) followed
* by a power failure results in the reflinked data to be readable from
* both files/ranges.
*/
if (ret == 0 && len > 0 &&
(file_sync_write(src_file) || file_sync_write(dst_file))) {
ret = btrfs_sync_file(src_file, off, off + len - 1, 0);
if (ret == 0)
ret = btrfs_sync_file(dst_file, destoff,
destoff + len - 1, 0);
}

return ret < 0 ? ret : len;
}

0 comments on commit b7a7a83

Please sign in to comment.