Watch
1
Fork
You've already forked kernel_a52xq
0
mirror of https://github.com/a52xq-dev/kernel.git synced 2026-10-18 21:58:11 +07:00

overlayfs update for 4.19

This contains two new features:
 
  1) Stack file operations: this allows removal of several hacks from the
     VFS, proper interaction of read-only open files with copy-up,
     possibility to implement fs modifying ioctls properly, and others.
 
  2) Metadata only copy-up: when file is on lower layer and only metadata is
     modified (except size) then only copy up the metadata and continue to
     use the data from the lower file.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCW3srhAAKCRDh3BK/laaZ
 PC6tAQCP+KklcN+TvNp502f+O/kATahSpgnun4NY1/p4I8JV+AEAzdlkTN3+MiAO
 fn9brN6mBK7h59DO3hqedPLJy2vrgwg=
 =QDXH
 -----END PGP SIGNATURE-----

Merge tag 'ovl-update-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs updates from Miklos Szeredi:
 "This contains two new features:

   - Stack file operations: this allows removal of several hacks from
     the VFS, proper interaction of read-only open files with copy-up,
     possibility to implement fs modifying ioctls properly, and others.

   - Metadata only copy-up: when file is on lower layer and only
     metadata is modified (except size) then only copy up the metadata
     and continue to use the data from the lower file"

* tag 'ovl-update-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: (66 commits)
  ovl: Enable metadata only feature
  ovl: Do not do metacopy only for ioctl modifying file attr
  ovl: Do not do metadata only copy-up for truncate operation
  ovl: add helper to force data copy-up
  ovl: Check redirect on index as well
  ovl: Set redirect on upper inode when it is linked
  ovl: Set redirect on metacopy files upon rename
  ovl: Do not set dentry type ORIGIN for broken hardlinks
  ovl: Add an inode flag OVL_CONST_INO
  ovl: Treat metacopy dentries as type OVL_PATH_MERGE
  ovl: Check redirects for metacopy files
  ovl: Move some dir related ovl_lookup_single() code in else block
  ovl: Do not expose metacopy only dentry from d_real()
  ovl: Open file with data except for the case of fsync
  ovl: Add helper ovl_inode_realdata()
  ovl: Store lower data inode in ovl_inode
  ovl: Fix ovl_getattr() to get number of blocks from lower
  ovl: Add helper ovl_dentry_lowerdata() to get lower data dentry
  ovl: Copy up meta inode data from lowest data inode
  ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
  ...
This commit is contained in:
Linus Torvalds 2018-08-21 18:19:09 -07:00
commit d9a185f8b4
33 changed files with 1619 additions and 595 deletions

View file

@ -21,8 +21,7 @@ prototypes:
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
struct vfsmount *(*d_automount)(struct path *path);
int (*d_manage)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int, unsigned int);
struct dentry *(*d_real)(struct dentry *, const struct inode *);
locking rules:
rename_lock ->d_lock may block rcu-walk

View file

@ -10,10 +10,6 @@ union-filesystems). An overlay-filesystem tries to present a
filesystem which is the result over overlaying one filesystem on top
of the other.
The result will inevitably fail to look exactly like a normal
filesystem for various technical reasons. The expectation is that
many use cases will be able to ignore these differences.
Overlay objects
---------------
@ -266,6 +262,30 @@ rightmost one and going left. In the above example lower1 will be the
top, lower2 the middle and lower3 the bottom layer.
Metadata only copy up
--------------------
When metadata only copy up feature is enabled, overlayfs will only copy
up metadata (as opposed to whole file), when a metadata specific operation
like chown/chmod is performed. Full file will be copied up later when
file is opened for WRITE operation.
In other words, this is delayed data copy up operation and data is copied
up when there is a need to actually modify data.
There are multiple ways to enable/disable this feature. A config option
CONFIG_OVERLAY_FS_METACOPY can be set/unset to enable/disable this feature
by default. Or one can enable/disable it at module load time with module
parameter metacopy=on/off. Lastly, there is also a per mount option
metacopy=on/off to enable/disable this feature per mount.
Do not use metacopy=on with untrusted upper/lower directories. Otherwise
it is possible that an attacker can create a handcrafted file with
appropriate REDIRECT and METACOPY xattrs, and gain access to file on lower
pointed by REDIRECT. This should not be possible on local system as setting
"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
for untrusted layers like from a pen drive.
Sharing and copying layers
--------------------------
@ -284,7 +304,7 @@ though it will not result in a crash or deadlock.
Mounting an overlay using an upper layer path, where the upper layer path
was previously used by another mounted overlay in combination with a
different lower layer path, is allowed, unless the "inodes index" feature
is enabled.
or "metadata only copy up" feature is enabled.
With the "inodes index" feature, on the first time mount, an NFS file
handle of the lower layer root directory, along with the UUID of the lower
@ -297,6 +317,10 @@ lower root origin, mount will fail with ESTALE. An overlayfs mount with
does not support NFS export, lower filesystem does not have a valid UUID or
if the upper filesystem does not support extended attributes.
For "metadata only copy up" feature there is no verification mechanism at
mount time. So if same upper is mounted with different set of lower, mount
probably will succeed but expect the unexpected later on. So don't do it.
It is quite a common practice to copy overlay layers to a different
directory tree on the same or different underlying filesystem, and even
to a different machine. With the "inodes index" feature, trying to mount
@ -306,27 +330,40 @@ the copied layers will fail the verification of the lower root file handle.
Non-standard behavior
---------------------
The copy_up operation essentially creates a new, identical file and
moves it over to the old name. Any open files referring to this inode
will access the old data.
Overlayfs can now act as a POSIX compliant filesystem with the following
features turned on:
The new file may be on a different filesystem, so both st_dev and st_ino
of the real file may change. The values of st_dev and st_ino returned by
stat(2) on an overlay object are often not the same as the real file
stat(2) values to prevent the values from changing on copy_up.
1) "redirect_dir"
Unless "xino" feature is enabled, when overlay layers are not all on the
same underlying filesystem, the value of st_dev may be different for two
non-directory objects in the same overlay filesystem and the value of
st_ino for directory objects may be non persistent and could change even
while the overlay filesystem is still mounted.
Enabled with the mount option or module option: "redirect_dir=on" or with
the kernel config option CONFIG_OVERLAY_FS_REDIRECT_DIR=y.
Unless "inode index" feature is enabled, if a file with multiple hard
links is copied up, then this will "break" the link. Changes will not be
propagated to other names referring to the same inode.
If this feature is disabled, then rename(2) on a lower or merged directory
will fail with EXDEV ("Invalid cross-device link").
Unless "redirect_dir" feature is enabled, rename(2) on a lower or merged
directory will fail with EXDEV.
2) "inode index"
Enabled with the mount option or module option "index=on" or with the
kernel config option CONFIG_OVERLAY_FS_INDEX=y.
If this feature is disabled and a file with multiple hard links is copied
up, then this will "break" the link. Changes will not be propagated to
other names referring to the same inode.
3) "xino"
Enabled with the mount option "xino=auto" or "xino=on", with the module
option "xino_auto=on" or with the kernel config option
CONFIG_OVERLAY_FS_XINO_AUTO=y. Also implicitly enabled by using the same
underlying filesystem for all layers making up the overlay.
If this feature is disabled or the underlying filesystem doesn't have
enough free bits in the inode number, then overlayfs will not be able to
guarantee that the values of st_ino and st_dev returned by stat(2) and the
value of d_ino returned by readdir(3) will act like on a normal filesystem.
E.g. the value of st_dev may be different for two objects in the same
overlay filesystem and the value of st_ino for directory objects may not be
persistent and could change even while the overlay filesystem is mounted.
Changes to underlying filesystems

View file

@ -989,8 +989,7 @@ struct dentry_operations {
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int, unsigned int);
struct dentry *(*d_real)(struct dentry *, const struct inode *);
};
d_revalidate: called when the VFS needs to revalidate a dentry. This
@ -1124,22 +1123,15 @@ struct dentry_operations {
dentry being transited from.
d_real: overlay/union type filesystems implement this method to return one of
the underlying dentries hidden by the overlay. It is used in three
the underlying dentries hidden by the overlay. It is used in two
different modes:
Called from open it may need to copy-up the file depending on the
supplied open flags. This mode is selected with a non-zero flags
argument. In this mode the d_real method can return an error.
Called from file_dentry() it returns the real dentry matching the inode
argument. The real dentry may be from a lower layer already copied up,
but still referenced from the file. This mode is selected with a
non-NULL inode argument. This will always succeed.
non-NULL inode argument.
With NULL inode and zero flags the topmost real underlying dentry is
returned. This will always succeed.
This method is never called with both non-NULL inode and non-zero flags.
With NULL inode the topmost real underlying dentry is returned.
Each dentry has a pointer to its parent dentry, as well as a hash list
of child dentries. Child dentries are basically like files in a

View file

@ -3217,8 +3217,9 @@ void btrfs_get_block_group_info(struct list_head *groups_list,
struct btrfs_ioctl_space_info *space);
void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
struct btrfs_ioctl_balance_args *bargs);
ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
struct file *dst_file, u64 dst_loff);
int btrfs_dedupe_file_range(struct file *src_file, loff_t src_loff,
struct file *dst_file, loff_t dst_loff,
u64 olen);
/* file.c */
int __init btrfs_auto_defrag_init(void);

View file

@ -3592,13 +3592,13 @@ out_unlock:
return ret;
}
ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
struct file *dst_file, u64 dst_loff)
int btrfs_dedupe_file_range(struct file *src_file, loff_t src_loff,
struct file *dst_file, loff_t dst_loff,
u64 olen)
{
struct inode *src = file_inode(src_file);
struct inode *dst = file_inode(dst_file);
u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
ssize_t res;
if (WARN_ON_ONCE(bs < PAGE_SIZE)) {
/*
@ -3609,10 +3609,7 @@ ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
return -EINVAL;
}
res = btrfs_extent_same(src, loff, olen, dst, dst_loff);
if (res)
return res;
return olen;
return btrfs_extent_same(src, src_loff, olen, dst, dst_loff);
}
static int clone_finish_inode_update(struct btrfs_trans_handle *trans,

View file

@ -52,7 +52,8 @@ static void file_free_rcu(struct rcu_head *head)
static inline void file_free(struct file *f)
{
security_file_free(f);
percpu_counter_dec(&nr_files);
if (!(f->f_mode & FMODE_NOACCOUNT))
percpu_counter_dec(&nr_files);
call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
}
@ -91,34 +92,11 @@ int proc_nr_files(struct ctl_table *table, int write,
}
#endif
/* Find an unused file structure and return a pointer to it.
* Returns an error pointer if some error happend e.g. we over file
* structures limit, run out of memory or operation is not permitted.
*
* Be very careful using this. You are responsible for
* getting write access to any mount that you might assign
* to this filp, if it is opened for write. If this is not
* done, you will imbalance int the mount's writer count
* and a warning at __fput() time.
*/
struct file *alloc_empty_file(int flags, const struct cred *cred)
static struct file *__alloc_file(int flags, const struct cred *cred)
{
static long old_max;
struct file *f;
int error;
/*
* Privileged users can go above max_files
*/
if (get_nr_files() >= files_stat.max_files && !capable(CAP_SYS_ADMIN)) {
/*
* percpu_counters are inaccurate. Do an expensive check before
* we go and fail.
*/
if (percpu_counter_sum_positive(&nr_files) >= files_stat.max_files)
goto over;
}
f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
if (unlikely(!f))
return ERR_PTR(-ENOMEM);
@ -138,7 +116,41 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
f->f_flags = flags;
f->f_mode = OPEN_FMODE(flags);
/* f->f_version: 0 */
percpu_counter_inc(&nr_files);
return f;
}
/* Find an unused file structure and return a pointer to it.
* Returns an error pointer if some error happend e.g. we over file
* structures limit, run out of memory or operation is not permitted.
*
* Be very careful using this. You are responsible for
* getting write access to any mount that you might assign
* to this filp, if it is opened for write. If this is not
* done, you will imbalance int the mount's writer count
* and a warning at __fput() time.
*/
struct file *alloc_empty_file(int flags, const struct cred *cred)
{
static long old_max;
struct file *f;
/*
* Privileged users can go above max_files
*/
if (get_nr_files() >= files_stat.max_files && !capable(CAP_SYS_ADMIN)) {
/*
* percpu_counters are inaccurate. Do an expensive check before
* we go and fail.
*/
if (percpu_counter_sum_positive(&nr_files) >= files_stat.max_files)
goto over;
}
f = __alloc_file(flags, cred);
if (!IS_ERR(f))
percpu_counter_inc(&nr_files);
return f;
over:
@ -150,6 +162,21 @@ over:
return ERR_PTR(-ENFILE);
}
/*
* Variant of alloc_empty_file() that doesn't check and modify nr_files.
*
* Should not be used unless there's a very good reason to do so.
*/
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
{
struct file *f = __alloc_file(flags, cred);
if (!IS_ERR(f))
f->f_mode |= FMODE_NOACCOUNT;
return f;
}
/**
* alloc_file - allocate and initialize a 'struct file'
*

View file

@ -1595,50 +1595,17 @@ sector_t bmap(struct inode *inode, sector_t block)
}
EXPORT_SYMBOL(bmap);
/*
* Update times in overlayed inode from underlying real inode
*/
static void update_ovl_inode_times(struct dentry *dentry, struct inode *inode,
bool rcu)
{
struct dentry *upperdentry;
/*
* Nothing to do if in rcu or if non-overlayfs
*/
if (rcu || likely(!(dentry->d_flags & DCACHE_OP_REAL)))
return;
upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER);
/*
* If file is on lower then we can't update atime, so no worries about
* stale mtime/ctime.
*/
if (upperdentry) {
struct inode *realinode = d_inode(upperdentry);
if ((!timespec64_equal(&inode->i_mtime, &realinode->i_mtime) ||
!timespec64_equal(&inode->i_ctime, &realinode->i_ctime))) {
inode->i_mtime = realinode->i_mtime;
inode->i_ctime = realinode->i_ctime;
}
}
}
/*
* With relative atime, only update atime if the previous atime is
* earlier than either the ctime or mtime or if at least a day has
* passed since the last atime update.
*/
static int relatime_need_update(const struct path *path, struct inode *inode,
struct timespec now, bool rcu)
static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
struct timespec now)
{
if (!(path->mnt->mnt_flags & MNT_RELATIME))
if (!(mnt->mnt_flags & MNT_RELATIME))
return 1;
update_ovl_inode_times(path->dentry, inode, rcu);
/*
* Is mtime younger than atime? If yes, update atime:
*/
@ -1709,8 +1676,7 @@ static int update_time(struct inode *inode, struct timespec64 *time, int flags)
* This function automatically handles read only file systems and media,
* as well as the "noatime" flag and inode specific "noatime" markers.
*/
bool __atime_needs_update(const struct path *path, struct inode *inode,
bool rcu)
bool atime_needs_update(const struct path *path, struct inode *inode)
{
struct vfsmount *mnt = path->mnt;
struct timespec64 now;
@ -1736,7 +1702,7 @@ bool __atime_needs_update(const struct path *path, struct inode *inode,
now = current_time(inode);
if (!relatime_need_update(path, inode, timespec64_to_timespec(now), rcu))
if (!relatime_need_update(mnt, inode, timespec64_to_timespec(now)))
return false;
if (timespec64_equal(&inode->i_atime, &now))
@ -1751,7 +1717,7 @@ void touch_atime(const struct path *path)
struct inode *inode = d_inode(path->dentry);
struct timespec64 now;
if (!__atime_needs_update(path, inode, false))
if (!atime_needs_update(path, inode))
return;
if (!sb_start_write_trylock(inode->i_sb))

View file

@ -82,10 +82,8 @@ extern void __init mnt_init(void);
extern int __mnt_want_write(struct vfsmount *);
extern int __mnt_want_write_file(struct file *);
extern int mnt_want_write_file_path(struct file *);
extern void __mnt_drop_write(struct vfsmount *);
extern void __mnt_drop_write_file(struct file *);
extern void mnt_drop_write_file_path(struct file *);
/*
* fs_struct.c
@ -96,6 +94,7 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
* file_table.c
*/
extern struct file *alloc_empty_file(int, const struct cred *);
extern struct file *alloc_empty_file_noaccount(int, const struct cred *);
/*
* super.c
@ -136,13 +135,6 @@ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc);
extern void inode_add_lru(struct inode *inode);
extern int dentry_needs_remove_privs(struct dentry *dentry);
extern bool __atime_needs_update(const struct path *, struct inode *, bool);
static inline bool atime_needs_update_rcu(const struct path *path,
struct inode *inode)
{
return __atime_needs_update(path, inode, true);
}
/*
* fs-writeback.c
*/
@ -185,7 +177,6 @@ extern const struct dentry_operations ns_dentry_operations;
*/
extern int do_vfs_ioctl(struct file *file, unsigned int fd, unsigned int cmd,
unsigned long arg);
extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/*
* iomap support:

View file

@ -49,6 +49,7 @@ long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
out:
return error;
}
EXPORT_SYMBOL(vfs_ioctl);
static int ioctl_fibmap(struct file *filp, int __user *p)
{

View file

@ -139,11 +139,6 @@
#define IS_OFDLCK(fl) (fl->fl_flags & FL_OFDLCK)
#define IS_REMOTELCK(fl) (fl->fl_pid <= 0)
static inline bool is_remote_lock(struct file *filp)
{
return likely(!(filp->f_path.dentry->d_sb->s_flags & SB_NOREMOTELOCK));
}
static bool lease_breaking(struct file_lock *fl)
{
return fl->fl_flags & (FL_UNLOCK_PENDING | FL_DOWNGRADE_PENDING);
@ -1651,8 +1646,7 @@ check_conflicting_open(const struct dentry *dentry, const long arg, int flags)
if (flags & FL_LAYOUT)
return 0;
if ((arg == F_RDLCK) &&
(atomic_read(&d_real_inode(dentry)->i_writecount) > 0))
if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
return -EAGAIN;
if ((arg == F_WRLCK) && ((d_count(dentry) > 1) ||
@ -1873,7 +1867,7 @@ EXPORT_SYMBOL(generic_setlease);
int
vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **priv)
{
if (filp->f_op->setlease && is_remote_lock(filp))
if (filp->f_op->setlease)
return filp->f_op->setlease(filp, arg, lease, priv);
else
return generic_setlease(filp, arg, lease, priv);
@ -2020,7 +2014,7 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
if (error)
goto out_free;
if (f.file->f_op->flock && is_remote_lock(f.file))
if (f.file->f_op->flock)
error = f.file->f_op->flock(f.file,
(can_sleep) ? F_SETLKW : F_SETLK,
lock);
@ -2046,7 +2040,7 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
*/
int vfs_test_lock(struct file *filp, struct file_lock *fl)
{
if (filp->f_op->lock && is_remote_lock(filp))
if (filp->f_op->lock)
return filp->f_op->lock(filp, F_GETLK, fl);
posix_test_lock(filp, fl);
return 0;
@ -2196,7 +2190,7 @@ out:
*/
int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf)
{
if (filp->f_op->lock && is_remote_lock(filp))
if (filp->f_op->lock)
return filp->f_op->lock(filp, cmd, fl);
else
return posix_lock_file(filp, fl, conf);
@ -2518,7 +2512,7 @@ locks_remove_flock(struct file *filp, struct file_lock_context *flctx)
if (list_empty(&flctx->flc_flock))
return;
if (filp->f_op->flock && is_remote_lock(filp))
if (filp->f_op->flock)
filp->f_op->flock(filp, F_SETLKW, &fl);
else
flock_lock_inode(inode, &fl);
@ -2605,7 +2599,7 @@ EXPORT_SYMBOL(posix_unblock_lock);
*/
int vfs_cancel_lock(struct file *filp, struct file_lock *fl)
{
if (filp->f_op->lock && is_remote_lock(filp))
if (filp->f_op->lock)
return filp->f_op->lock(filp, F_CANCELLK, fl);
return 0;
}

View file

@ -1015,7 +1015,7 @@ const char *get_link(struct nameidata *nd)
if (!(nd->flags & LOOKUP_RCU)) {
touch_atime(&last->link);
cond_resched();
} else if (atime_needs_update_rcu(&last->link, inode)) {
} else if (atime_needs_update(&last->link, inode)) {
if (unlikely(unlazy_walk(nd)))
return ERR_PTR(-ECHILD);
touch_atime(&last->link);

View file

@ -430,75 +430,21 @@ int __mnt_want_write_file(struct file *file)
return mnt_clone_write(file->f_path.mnt);
}
/**
* mnt_want_write_file_path - get write access to a file's mount
* @file: the file who's mount on which to take a write
*
* This is like mnt_want_write, but it takes a file and can
* do some optimisations if the file is open for write already
*
* Called by the vfs for cases when we have an open file at hand, but will do an
* inode operation on it (important distinction for files opened on overlayfs,
* since the file operations will come from the real underlying file, while
* inode operations come from the overlay).
*/
int mnt_want_write_file_path(struct file *file)
{
int ret;
sb_start_write(file->f_path.mnt->mnt_sb);
ret = __mnt_want_write_file(file);
if (ret)
sb_end_write(file->f_path.mnt->mnt_sb);
return ret;
}
static inline int may_write_real(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct dentry *upperdentry;
/* Writable file? */
if (file->f_mode & FMODE_WRITER)
return 0;
/* Not overlayfs? */
if (likely(!(dentry->d_flags & DCACHE_OP_REAL)))
return 0;
/* File refers to upper, writable layer? */
upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER);
if (upperdentry &&
(file_inode(file) == d_inode(upperdentry) ||
file_inode(file) == d_inode(dentry)))
return 0;
/* Lower layer: can't write to real file, sorry... */
return -EPERM;
}
/**
* mnt_want_write_file - get write access to a file's mount
* @file: the file who's mount on which to take a write
*
* This is like mnt_want_write, but it takes a file and can
* do some optimisations if the file is open for write already
*
* Mostly called by filesystems from their ioctl operation before performing
* modification. On overlayfs this needs to check if the file is on a read-only
* lower layer and deny access in that case.
*/
int mnt_want_write_file(struct file *file)
{
int ret;
ret = may_write_real(file);
if (!ret) {
sb_start_write(file_inode(file)->i_sb);
ret = __mnt_want_write_file(file);
if (ret)
sb_end_write(file_inode(file)->i_sb);
}
sb_start_write(file_inode(file)->i_sb);
ret = __mnt_want_write_file(file);
if (ret)
sb_end_write(file_inode(file)->i_sb);
return ret;
}
EXPORT_SYMBOL_GPL(mnt_want_write_file);
@ -538,14 +484,9 @@ void __mnt_drop_write_file(struct file *file)
__mnt_drop_write(file->f_path.mnt);
}
void mnt_drop_write_file_path(struct file *file)
{
mnt_drop_write(file->f_path.mnt);
}
void mnt_drop_write_file(struct file *file)
{
__mnt_drop_write(file->f_path.mnt);
__mnt_drop_write_file(file);
sb_end_write(file_inode(file)->i_sb);
}
EXPORT_SYMBOL(mnt_drop_write_file);

View file

@ -2537,19 +2537,14 @@ static int ocfs2_file_clone_range(struct file *file_in,
len, false);
}
static ssize_t ocfs2_file_dedupe_range(struct file *src_file,
u64 loff,
u64 len,
struct file *dst_file,
u64 dst_loff)
static int ocfs2_file_dedupe_range(struct file *file_in,
loff_t pos_in,
struct file *file_out,
loff_t pos_out,
u64 len)
{
int error;
error = ocfs2_reflink_remap_range(src_file, loff, dst_file, dst_loff,
return ocfs2_reflink_remap_range(file_in, pos_in, file_out, pos_out,
len, true);
if (error)
return error;
return len;
}
const struct inode_operations ocfs2_file_iops = {

View file

@ -68,7 +68,6 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
long vfs_truncate(const struct path *path, loff_t length)
{
struct inode *inode;
struct dentry *upperdentry;
long error;
inode = path->dentry->d_inode;
@ -91,17 +90,7 @@ long vfs_truncate(const struct path *path, loff_t length)
if (IS_APPEND(inode))
goto mnt_drop_write_and_out;
/*
* If this is an overlayfs then do as if opening the file so we get
* write access on the upper inode, not on the overlay inode. For
* non-overlay filesystems d_real() is an identity function.
*/
upperdentry = d_real(path->dentry, NULL, O_WRONLY, 0);
error = PTR_ERR(upperdentry);
if (IS_ERR(upperdentry))
goto mnt_drop_write_and_out;
error = get_write_access(upperdentry->d_inode);
error = get_write_access(inode);
if (error)
goto mnt_drop_write_and_out;
@ -120,7 +109,7 @@ long vfs_truncate(const struct path *path, loff_t length)
error = do_truncate(path->dentry, length, 0, NULL);
put_write_and_out:
put_write_access(upperdentry->d_inode);
put_write_access(inode);
mnt_drop_write_and_out:
mnt_drop_write(path->mnt);
out:
@ -707,12 +696,12 @@ int ksys_fchown(unsigned int fd, uid_t user, gid_t group)
if (!f.file)
goto out;
error = mnt_want_write_file_path(f.file);
error = mnt_want_write_file(f.file);
if (error)
goto out_fput;
audit_file(f.file);
error = chown_common(&f.file->f_path, user, group);
mnt_drop_write_file_path(f.file);
mnt_drop_write_file(f.file);
out_fput:
fdput(f);
out:
@ -887,13 +876,8 @@ EXPORT_SYMBOL(file_path);
*/
int vfs_open(const struct path *path, struct file *file)
{
struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags, 0);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
file->f_path = *path;
return do_dentry_open(file, d_backing_inode(dentry), NULL);
return do_dentry_open(file, d_backing_inode(path->dentry), NULL);
}
struct file *dentry_open(const struct path *path, int flags,
@ -919,6 +903,24 @@ struct file *dentry_open(const struct path *path, int flags,
}
EXPORT_SYMBOL(dentry_open);
struct file *open_with_fake_path(const struct path *path, int flags,
struct inode *inode, const struct cred *cred)
{
struct file *f = alloc_empty_file_noaccount(flags, cred);
if (!IS_ERR(f)) {
int error;
f->f_path = *path;
error = do_dentry_open(f, inode, NULL);
if (error) {
fput(f);
f = ERR_PTR(error);
}
}
return f;
}
EXPORT_SYMBOL(open_with_fake_path);
static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
{
int lookup_flags = 0;

View file

@ -64,6 +64,7 @@ config OVERLAY_FS_NFS_EXPORT
bool "Overlayfs: turn on NFS export feature by default"
depends on OVERLAY_FS
depends on OVERLAY_FS_INDEX
depends on !OVERLAY_FS_METACOPY
help
If this config option is enabled then overlay filesystems will use
the index directory to decode overlay NFS file handles by default.
@ -103,3 +104,21 @@ config OVERLAY_FS_XINO_AUTO
For more information, see Documentation/filesystems/overlayfs.txt
If unsure, say N.
config OVERLAY_FS_METACOPY
bool "Overlayfs: turn on metadata only copy up feature by default"
depends on OVERLAY_FS
select OVERLAY_FS_REDIRECT_DIR
help
If this config option is enabled then overlay filesystems will
copy up only metadata where appropriate and data copy up will
happen when a file is opened for WRITE operation. It is still
possible to turn off this feature globally with the "metacopy=off"
module option or on a filesystem instance basis with the
"metacopy=off" mount option.
Note, that this feature is not backward compatible. That is,
mounting an overlay which has metacopy only inodes on a kernel
that doesn't support this feature will have unexpected results.
If unsure, say N.

View file

@ -4,5 +4,5 @@
obj-$(CONFIG_OVERLAY_FS) += overlay.o
overlay-objs := super.o namei.o util.o inode.o dir.o readdir.o copy_up.o \
export.o
overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \
copy_up.o export.o

View file

@ -25,35 +25,20 @@
#define OVL_COPY_UP_CHUNK_SIZE (1 << 20)
static bool __read_mostly ovl_check_copy_up;
module_param_named(check_copy_up, ovl_check_copy_up, bool,
S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(ovl_check_copy_up,
"Warn on copy-up when causing process also has a R/O fd open");
static int ovl_check_fd(const void *data, struct file *f, unsigned int fd)
static int ovl_ccup_set(const char *buf, const struct kernel_param *param)
{
const struct dentry *dentry = data;
if (file_inode(f) == d_inode(dentry))
pr_warn_ratelimited("overlayfs: Warning: Copying up %pD, but open R/O on fd %u which will cease to be coherent [pid=%d %s]\n",
f, fd, current->pid, current->comm);
pr_warn("overlayfs: \"check_copy_up\" module option is obsolete\n");
return 0;
}
/*
* Check the fds open by this process and warn if something like the following
* scenario is about to occur:
*
* fd1 = open("foo", O_RDONLY);
* fd2 = open("foo", O_RDWR);
*/
static void ovl_do_check_copy_up(struct dentry *dentry)
static int ovl_ccup_get(char *buf, const struct kernel_param *param)
{
if (ovl_check_copy_up)
iterate_fd(current->files, 0, ovl_check_fd, dentry);
return sprintf(buf, "N\n");
}
module_param_call(check_copy_up, ovl_ccup_set, ovl_ccup_get, NULL, 0644);
MODULE_PARM_DESC(ovl_check_copy_up, "Obsolete; does nothing");
int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{
ssize_t list_size, size, value_size = 0;
@ -195,6 +180,16 @@ out_fput:
return error;
}
static int ovl_set_size(struct dentry *upperdentry, struct kstat *stat)
{
struct iattr attr = {
.ia_valid = ATTR_SIZE,
.ia_size = stat->size,
};
return notify_change(upperdentry, &attr, NULL);
}
static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
{
struct iattr attr = {
@ -403,6 +398,7 @@ struct ovl_copy_up_ctx {
bool tmpfile;
bool origin;
bool indexed;
bool metacopy;
};
static int ovl_link_up(struct ovl_copy_up_ctx *c)
@ -505,28 +501,10 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
{
int err;
if (S_ISREG(c->stat.mode)) {
struct path upperpath;
ovl_path_upper(c->dentry, &upperpath);
BUG_ON(upperpath.dentry != NULL);
upperpath.dentry = temp;
err = ovl_copy_up_data(&c->lowerpath, &upperpath, c->stat.size);
if (err)
return err;
}
err = ovl_copy_xattr(c->lowerpath.dentry, temp);
if (err)
return err;
inode_lock(temp->d_inode);
err = ovl_set_attr(temp, &c->stat);
inode_unlock(temp->d_inode);
if (err)
return err;
/*
* Store identifier of lower inode in upper inode xattr to
* allow lookup of the copy up origin inode.
@ -540,7 +518,34 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
return err;
}
return 0;
if (S_ISREG(c->stat.mode) && !c->metacopy) {
struct path upperpath, datapath;
ovl_path_upper(c->dentry, &upperpath);
BUG_ON(upperpath.dentry != NULL);
upperpath.dentry = temp;
ovl_path_lowerdata(c->dentry, &datapath);
err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
if (err)
return err;
}
if (c->metacopy) {
err = ovl_check_setxattr(c->dentry, temp, OVL_XATTR_METACOPY,
NULL, 0, -EOPNOTSUPP);
if (err)
return err;
}
inode_lock(temp->d_inode);
if (c->metacopy)
err = ovl_set_size(temp, &c->stat);
if (!err)
err = ovl_set_attr(temp, &c->stat);
inode_unlock(temp->d_inode);
return err;
}
static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
@ -575,6 +580,8 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
if (err)
goto out;
if (!c->metacopy)
ovl_set_upperdata(d_inode(c->dentry));
inode = d_inode(c->dentry);
ovl_inode_update(inode, newdentry);
if (S_ISDIR(inode->i_mode))
@ -677,6 +684,49 @@ out:
return err;
}
static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
int flags)
{
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
if (!ofs->config.metacopy)
return false;
if (!S_ISREG(mode))
return false;
if (flags && ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)))
return false;
return true;
}
/* Copy up data of an inode which was copied up metadata only in the past. */
static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
{
struct path upperpath, datapath;
int err;
ovl_path_upper(c->dentry, &upperpath);
if (WARN_ON(upperpath.dentry == NULL))
return -EIO;
ovl_path_lowerdata(c->dentry, &datapath);
if (WARN_ON(datapath.dentry == NULL))
return -EIO;
err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
if (err)
return err;
err = vfs_removexattr(upperpath.dentry, OVL_XATTR_METACOPY);
if (err)
return err;
ovl_set_upperdata(d_inode(c->dentry));
return err;
}
static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
int flags)
{
@ -698,6 +748,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
if (err)
return err;
ctx.metacopy = ovl_need_meta_copy_up(dentry, ctx.stat.mode, flags);
if (parent) {
ovl_path_upper(parent, &parentpath);
ctx.destdir = parentpath.dentry;
@ -719,9 +771,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
if (IS_ERR(ctx.link))
return PTR_ERR(ctx.link);
}
ovl_do_check_copy_up(ctx.lowerpath.dentry);
err = ovl_copy_up_start(dentry);
err = ovl_copy_up_start(dentry, flags);
/* err < 0: interrupted, err > 0: raced with another copy-up */
if (unlikely(err)) {
if (err > 0)
@ -731,6 +782,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
err = ovl_do_copy_up(&ctx);
if (!err && parent && !ovl_dentry_has_upper_alias(dentry))
err = ovl_link_up(&ctx);
if (!err && ovl_dentry_needs_data_copy_up_locked(dentry, flags))
err = ovl_copy_up_meta_inode_data(&ctx);
ovl_copy_up_end(dentry);
}
do_delayed_call(&done);
@ -756,21 +809,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
struct dentry *next;
struct dentry *parent = NULL;
/*
* Check if copy-up has happened as well as for upper alias (in
* case of hard links) is there.
*
* Both checks are lockless:
* - false negatives: will recheck under oi->lock
* - false positives:
* + ovl_dentry_upper() uses memory barriers to ensure the
* upper dentry is up-to-date
* + ovl_dentry_has_upper_alias() relies on locking of
* upper parent i_rwsem to prevent reordering copy-up
* with rename.
*/
if (ovl_dentry_upper(dentry) &&
(ovl_dentry_has_upper_alias(dentry) || disconnected))
if (ovl_already_copied_up(dentry, flags))
break;
next = dget(dentry);
@ -795,6 +834,41 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
return err;
}
static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
{
/* Copy up of disconnected dentry does not set upper alias */
if (ovl_already_copied_up(dentry, flags))
return false;
if (special_file(d_inode(dentry)->i_mode))
return false;
if (!ovl_open_flags_need_copy_up(flags))
return false;
return true;
}
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
{
int err = 0;
if (ovl_open_need_copy_up(dentry, file_flags)) {
err = ovl_want_write(dentry);
if (!err) {
err = ovl_copy_up_flags(dentry, file_flags);
ovl_drop_write(dentry);
}
}
return err;
}
int ovl_copy_up_with_data(struct dentry *dentry)
{
return ovl_copy_up_flags(dentry, O_WRONLY);
}
int ovl_copy_up(struct dentry *dentry)
{
return ovl_copy_up_flags(dentry, 0);

View file

@ -24,6 +24,8 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);
MODULE_PARM_DESC(ovl_redirect_max,
"Maximum length of absolute redirect xattr value");
static int ovl_set_redirect(struct dentry *dentry, bool samedir);
int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
{
int err;
@ -242,7 +244,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
.newinode = inode,
};
ovl_dentry_version_inc(dentry->d_parent, false);
ovl_dir_modified(dentry->d_parent, false);
ovl_dentry_set_upper_alias(dentry);
if (!hardlink) {
/*
@ -657,6 +659,12 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
if (err)
goto out_drop_write;
if (ovl_is_metacopy_dentry(old)) {
err = ovl_set_redirect(old, false);
if (err)
goto out_drop_write;
}
err = ovl_nlink_start(old, &locked);
if (err)
goto out_drop_write;
@ -722,7 +730,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
if (err)
goto out_d_drop;
ovl_dentry_version_inc(dentry->d_parent, true);
ovl_dir_modified(dentry->d_parent, true);
out_d_drop:
d_drop(dentry);
out_dput_upper:
@ -767,7 +775,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
err = vfs_rmdir(dir, upper);
else
err = vfs_unlink(dir, upper, NULL);
ovl_dentry_version_inc(dentry->d_parent, ovl_type_origin(dentry));
ovl_dir_modified(dentry->d_parent, ovl_type_origin(dentry));
/*
* Keeping this dentry hashed would mean having to release
@ -797,6 +805,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
int err;
bool locked = false;
const struct cred *old_cred;
struct dentry *upperdentry;
bool lower_positive = ovl_lower_positive(dentry);
LIST_HEAD(list);
@ -832,6 +841,17 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
drop_nlink(dentry->d_inode);
}
ovl_nlink_end(dentry, locked);
/*
* Copy ctime
*
* Note: we fail to update ctime if there was no copy-up, only a
* whiteout
*/
upperdentry = ovl_dentry_upper(dentry);
if (upperdentry)
ovl_copyattr(d_inode(upperdentry), d_inode(dentry));
out_drop_write:
ovl_drop_write(dentry);
out:
@ -862,13 +882,13 @@ static bool ovl_can_move(struct dentry *dentry)
!d_is_dir(dentry) || !ovl_type_merge_or_lower(dentry);
}
static char *ovl_get_redirect(struct dentry *dentry, bool samedir)
static char *ovl_get_redirect(struct dentry *dentry, bool abs_redirect)
{
char *buf, *ret;
struct dentry *d, *tmp;
int buflen = ovl_redirect_max + 1;
if (samedir) {
if (!abs_redirect) {
ret = kstrndup(dentry->d_name.name, dentry->d_name.len,
GFP_KERNEL);
goto out;
@ -922,15 +942,43 @@ out:
return ret ? ret : ERR_PTR(-ENOMEM);
}
static bool ovl_need_absolute_redirect(struct dentry *dentry, bool samedir)
{
struct dentry *lowerdentry;
if (!samedir)
return true;
if (d_is_dir(dentry))
return false;
/*
* For non-dir hardlinked files, we need absolute redirects
* in general as two upper hardlinks could be in different
* dirs. We could put a relative redirect now and convert
* it to absolute redirect later. But when nlink > 1 and
* indexing is on, that means relative redirect needs to be
* converted to absolute during copy up of another lower
* hardllink as well.
*
* So without optimizing too much, just check if lower is
* a hard link or not. If lower is hard link, put absolute
* redirect.
*/
lowerdentry = ovl_dentry_lower(dentry);
return (d_inode(lowerdentry)->i_nlink > 1);
}
static int ovl_set_redirect(struct dentry *dentry, bool samedir)
{
int err;
const char *redirect = ovl_dentry_get_redirect(dentry);
bool absolute_redirect = ovl_need_absolute_redirect(dentry, samedir);
if (redirect && (samedir || redirect[0] == '/'))
if (redirect && (!absolute_redirect || redirect[0] == '/'))
return 0;
redirect = ovl_get_redirect(dentry, samedir);
redirect = ovl_get_redirect(dentry, absolute_redirect);
if (IS_ERR(redirect))
return PTR_ERR(redirect);
@ -1106,22 +1154,20 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
goto out_dput;
err = 0;
if (is_dir) {
if (ovl_type_merge_or_lower(old))
err = ovl_set_redirect(old, samedir);
else if (!old_opaque && ovl_type_merge(new->d_parent))
err = ovl_set_opaque_xerr(old, olddentry, -EXDEV);
if (err)
goto out_dput;
}
if (!overwrite && new_is_dir) {
if (ovl_type_merge_or_lower(new))
err = ovl_set_redirect(new, samedir);
else if (!new_opaque && ovl_type_merge(old->d_parent))
err = ovl_set_opaque_xerr(new, newdentry, -EXDEV);
if (err)
goto out_dput;
}
if (ovl_type_merge_or_lower(old))
err = ovl_set_redirect(old, samedir);
else if (is_dir && !old_opaque && ovl_type_merge(new->d_parent))
err = ovl_set_opaque_xerr(old, olddentry, -EXDEV);
if (err)
goto out_dput;
if (!overwrite && ovl_type_merge_or_lower(new))
err = ovl_set_redirect(new, samedir);
else if (!overwrite && new_is_dir && !new_opaque &&
ovl_type_merge(old->d_parent))
err = ovl_set_opaque_xerr(new, newdentry, -EXDEV);
if (err)
goto out_dput;
err = ovl_do_rename(old_upperdir->d_inode, olddentry,
new_upperdir->d_inode, newdentry, flags);
@ -1138,10 +1184,15 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
drop_nlink(d_inode(new));
}
ovl_dentry_version_inc(old->d_parent, ovl_type_origin(old) ||
(!overwrite && ovl_type_origin(new)));
ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old) ||
(d_inode(new) && ovl_type_origin(new)));
ovl_dir_modified(old->d_parent, ovl_type_origin(old) ||
(!overwrite && ovl_type_origin(new)));
ovl_dir_modified(new->d_parent, ovl_type_origin(old) ||
(d_inode(new) && ovl_type_origin(new)));
/* copy ctime: */
ovl_copyattr(d_inode(olddentry), d_inode(old));
if (d_inode(new) && ovl_dentry_upper(new))
ovl_copyattr(d_inode(newdentry), d_inode(new));
out_dput:
dput(newdentry);

View file

@ -317,6 +317,9 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
return ERR_CAST(inode);
}
if (upper)
ovl_set_flag(OVL_UPPERDATA, inode);
dentry = d_find_any_alias(inode);
if (!dentry) {
dentry = d_alloc_anon(inode->i_sb);

511
fs/overlayfs/file.c Normal file
View file

@ -0,0 +1,511 @@
/*
* Copyright (C) 2017 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/cred.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/xattr.h>
#include <linux/uio.h>
#include "overlayfs.h"
static char ovl_whatisit(struct inode *inode, struct inode *realinode)
{
if (realinode != ovl_inode_upper(inode))
return 'l';
if (ovl_has_upperdata(inode))
return 'u';
else
return 'm';
}
static struct file *ovl_open_realfile(const struct file *file,
struct inode *realinode)
{
struct inode *inode = file_inode(file);
struct file *realfile;
const struct cred *old_cred;
old_cred = ovl_override_creds(inode->i_sb);
realfile = open_with_fake_path(&file->f_path, file->f_flags | O_NOATIME,
realinode, current_cred());
revert_creds(old_cred);
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
file, file, ovl_whatisit(inode, realinode), file->f_flags,
realfile, IS_ERR(realfile) ? 0 : realfile->f_flags);
return realfile;
}
#define OVL_SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT)
static int ovl_change_flags(struct file *file, unsigned int flags)
{
struct inode *inode = file_inode(file);
int err;
/* No atime modificaton on underlying */
flags |= O_NOATIME;
/* If some flag changed that cannot be changed then something's amiss */
if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
return -EIO;
flags &= OVL_SETFL_MASK;
if (((flags ^ file->f_flags) & O_APPEND) && IS_APPEND(inode))
return -EPERM;
if (flags & O_DIRECT) {
if (!file->f_mapping->a_ops ||
!file->f_mapping->a_ops->direct_IO)
return -EINVAL;
}
if (file->f_op->check_flags) {
err = file->f_op->check_flags(flags);
if (err)
return err;
}
spin_lock(&file->f_lock);
file->f_flags = (file->f_flags & ~OVL_SETFL_MASK) | flags;
spin_unlock(&file->f_lock);
return 0;
}
static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
bool allow_meta)
{
struct inode *inode = file_inode(file);
struct inode *realinode;
real->flags = 0;
real->file = file->private_data;
if (allow_meta)
realinode = ovl_inode_real(inode);
else
realinode = ovl_inode_realdata(inode);
/* Has it been copied up since we'd opened it? */
if (unlikely(file_inode(real->file) != realinode)) {
real->flags = FDPUT_FPUT;
real->file = ovl_open_realfile(file, realinode);
return PTR_ERR_OR_ZERO(real->file);
}
/* Did the flags change since open? */
if (unlikely((file->f_flags ^ real->file->f_flags) & ~O_NOATIME))
return ovl_change_flags(real->file, file->f_flags);
return 0;
}
static int ovl_real_fdget(const struct file *file, struct fd *real)
{
return ovl_real_fdget_meta(file, real, false);
}
static int ovl_open(struct inode *inode, struct file *file)
{
struct dentry *dentry = file_dentry(file);
struct file *realfile;
int err;
err = ovl_open_maybe_copy_up(dentry, file->f_flags);
if (err)
return err;
/* No longer need these flags, so don't pass them on to underlying fs */
file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
realfile = ovl_open_realfile(file, ovl_inode_realdata(inode));
if (IS_ERR(realfile))
return PTR_ERR(realfile);
/* For O_DIRECT dentry_open() checks f_mapping->a_ops->direct_IO */
file->f_mapping = realfile->f_mapping;
file->private_data = realfile;
return 0;
}
static int ovl_release(struct inode *inode, struct file *file)
{
fput(file->private_data);
return 0;
}
static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *realinode = ovl_inode_real(file_inode(file));
return generic_file_llseek_size(file, offset, whence,
realinode->i_sb->s_maxbytes,
i_size_read(realinode));
}
static void ovl_file_accessed(struct file *file)
{
struct inode *inode, *upperinode;
if (file->f_flags & O_NOATIME)
return;
inode = file_inode(file);
upperinode = ovl_inode_upper(inode);
if (!upperinode)
return;
if ((!timespec64_equal(&inode->i_mtime, &upperinode->i_mtime) ||
!timespec64_equal(&inode->i_ctime, &upperinode->i_ctime))) {
inode->i_mtime = upperinode->i_mtime;
inode->i_ctime = upperinode->i_ctime;
}
touch_atime(&file->f_path);
}
static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)
{
int ifl = iocb->ki_flags;
rwf_t flags = 0;
if (ifl & IOCB_NOWAIT)
flags |= RWF_NOWAIT;
if (ifl & IOCB_HIPRI)
flags |= RWF_HIPRI;
if (ifl & IOCB_DSYNC)
flags |= RWF_DSYNC;
if (ifl & IOCB_SYNC)
flags |= RWF_SYNC;
return flags;
}
static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
struct fd real;
const struct cred *old_cred;
ssize_t ret;
if (!iov_iter_count(iter))
return 0;
ret = ovl_real_fdget(file, &real);
if (ret)
return ret;
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(iocb));
revert_creds(old_cred);
ovl_file_accessed(file);
fdput(real);
return ret;
}
static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct fd real;
const struct cred *old_cred;
ssize_t ret;
if (!iov_iter_count(iter))
return 0;
inode_lock(inode);
/* Update mode */
ovl_copyattr(ovl_inode_real(inode), inode);
ret = file_remove_privs(file);
if (ret)
goto out_unlock;
ret = ovl_real_fdget(file, &real);
if (ret)
goto out_unlock;
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(iocb));
revert_creds(old_cred);
/* Update size */
ovl_copyattr(ovl_inode_real(inode), inode);
fdput(real);
out_unlock:
inode_unlock(inode);
return ret;
}
static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct fd real;
const struct cred *old_cred;
int ret;
ret = ovl_real_fdget_meta(file, &real, !datasync);
if (ret)
return ret;
/* Don't sync lower file for fear of receiving EROFS error */
if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) {
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_fsync_range(real.file, start, end, datasync);
revert_creds(old_cred);
}
fdput(real);
return ret;
}
static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
{
struct file *realfile = file->private_data;
const struct cred *old_cred;
int ret;
if (!realfile->f_op->mmap)
return -ENODEV;
if (WARN_ON(file != vma->vm_file))
return -EIO;
vma->vm_file = get_file(realfile);
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = call_mmap(vma->vm_file, vma);
revert_creds(old_cred);
if (ret) {
/* Drop reference count from new vm_file value */
fput(realfile);
} else {
/* Drop reference count from previous vm_file value */
fput(file);
}
ovl_file_accessed(file);
return ret;
}
static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{
struct inode *inode = file_inode(file);
struct fd real;
const struct cred *old_cred;
int ret;
ret = ovl_real_fdget(file, &real);
if (ret)
return ret;
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_fallocate(real.file, mode, offset, len);
revert_creds(old_cred);
/* Update size */
ovl_copyattr(ovl_inode_real(inode), inode);
fdput(real);
return ret;
}
static long ovl_real_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct fd real;
const struct cred *old_cred;
long ret;
ret = ovl_real_fdget(file, &real);
if (ret)
return ret;
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_ioctl(real.file, cmd, arg);
revert_creds(old_cred);
fdput(real);
return ret;
}
static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret;
struct inode *inode = file_inode(file);
switch (cmd) {
case FS_IOC_GETFLAGS:
ret = ovl_real_ioctl(file, cmd, arg);
break;
case FS_IOC_SETFLAGS:
if (!inode_owner_or_capable(inode))
return -EACCES;
ret = mnt_want_write_file(file);
if (ret)
return ret;
ret = ovl_copy_up_with_data(file_dentry(file));
if (!ret) {
ret = ovl_real_ioctl(file, cmd, arg);
inode_lock(inode);
ovl_copyflags(ovl_inode_real(inode), inode);
inode_unlock(inode);
}
mnt_drop_write_file(file);
break;
default:
ret = -ENOTTY;
}
return ret;
}
static long ovl_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case FS_IOC32_GETFLAGS:
cmd = FS_IOC_GETFLAGS;
break;
case FS_IOC32_SETFLAGS:
cmd = FS_IOC_SETFLAGS;
break;
default:
return -ENOIOCTLCMD;
}
return ovl_ioctl(file, cmd, arg);
}
enum ovl_copyop {
OVL_COPY,
OVL_CLONE,
OVL_DEDUPE,
};
static ssize_t ovl_copyfile(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
u64 len, unsigned int flags, enum ovl_copyop op)
{
struct inode *inode_out = file_inode(file_out);
struct fd real_in, real_out;
const struct cred *old_cred;
ssize_t ret;
ret = ovl_real_fdget(file_out, &real_out);
if (ret)
return ret;
ret = ovl_real_fdget(file_in, &real_in);
if (ret) {
fdput(real_out);
return ret;
}
old_cred = ovl_override_creds(file_inode(file_out)->i_sb);
switch (op) {
case OVL_COPY:
ret = vfs_copy_file_range(real_in.file, pos_in,
real_out.file, pos_out, len, flags);
break;
case OVL_CLONE:
ret = vfs_clone_file_range(real_in.file, pos_in,
real_out.file, pos_out, len);
break;
case OVL_DEDUPE:
ret = vfs_dedupe_file_range_one(real_in.file, pos_in,
real_out.file, pos_out, len);
break;
}
revert_creds(old_cred);
/* Update size */
ovl_copyattr(ovl_inode_real(inode_out), inode_out);
fdput(real_in);
fdput(real_out);
return ret;
}
static ssize_t ovl_copy_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
size_t len, unsigned int flags)
{
return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, flags,
OVL_COPY);
}
static int ovl_clone_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out, u64 len)
{
return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0,
OVL_CLONE);
}
static int ovl_dedupe_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out, u64 len)
{
/*
* Don't copy up because of a dedupe request, this wouldn't make sense
* most of the time (data would be duplicated instead of deduplicated).
*/
if (!ovl_inode_upper(file_inode(file_in)) ||
!ovl_inode_upper(file_inode(file_out)))
return -EPERM;
return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0,
OVL_DEDUPE);
}
const struct file_operations ovl_file_operations = {
.open = ovl_open,
.release = ovl_release,
.llseek = ovl_llseek,
.read_iter = ovl_read_iter,
.write_iter = ovl_write_iter,
.fsync = ovl_fsync,
.mmap = ovl_mmap,
.fallocate = ovl_fallocate,
.unlocked_ioctl = ovl_ioctl,
.compat_ioctl = ovl_compat_ioctl,
.copy_file_range = ovl_copy_file_range,
.clone_file_range = ovl_clone_file_range,
.dedupe_file_range = ovl_dedupe_file_range,
};

Some files were not shown because too many files have changed in this diff Show more