Commit 7f851936 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull overlayfs updates from Amir Goldstein:

 - Overlayfs aio cleanups and fixes

   Cleanups and minor fixes in preparation for factoring out of
   read/write passthrough code.

 - Overlayfs lock ordering changes

   Hold mnt_writers only throughout copy up instead of a long lived
   elevated refcount.

 - Add support for nesting overlayfs private xattrs

   There are cases where you want to use an overlayfs mount as a
   lowerdir for another overlayfs mount. For example, if the system
   rootfs is on overlayfs due to composefs, or to make it volatile (via
   tmpfs), then you cannot currently store a lowerdir on the rootfs,
   because the inner overlayfs will eat all the whiteouts and overlay
   xattrs. This means you can't e.g. store on the rootfs a prepared
   container image for use with overlayfs.

   This adds support for nesting of overlayfs mounts by escaping the
   problematic features and unescaping them when exposing to the
   overlayfs user.

 - Add new mount options for appending lowerdirs

* tag 'ovl-update-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: add support for appending lowerdirs one by one
  ovl: refactor layer parsing helpers
  ovl: store and show the user provided lowerdir mount option
  ovl: remove unused code in lowerdir param parsing
  ovl: Add documentation on nesting of overlayfs mounts
  ovl: Add an alternative type of whiteout
  ovl: Support escaped overlay.* xattrs
  ovl: Add OVL_XATTR_TRUSTED/USER_PREFIX_LEN macros
  ovl: Move xattr support to new xattrs.c file
  ovl: do not encode lower fh with upper sb_writers held
  ovl: do not open/llseek lower file with upper sb_writers held
  ovl: reorder ovl_want_write() after ovl_inode_lock()
  ovl: split ovl_want_write() into two helpers
  ovl: add helper ovl_file_modified()
  ovl: protect copying of realinode attributes to ovl inode
  ovl: punt write aio completion to workqueue
  ovl: propagate IOCB_APPEND flag on writes to realfile
  ovl: use simpler function to convert iocb to rw flags
parents c9d01179 24e16e38
...@@ -344,10 +344,11 @@ escaping the colons with a single backslash. For example: ...@@ -344,10 +344,11 @@ escaping the colons with a single backslash. For example:
mount -t overlay overlay -olowerdir=/a\:lower\:\:dir /merged mount -t overlay overlay -olowerdir=/a\:lower\:\:dir /merged
Since kernel version v6.5, directory names containing colons can also Since kernel version v6.8, directory names containing colons can also
be provided as lower layer using the fsconfig syscall from new mount api: be configured as lower layer using the "lowerdir+" mount options and the
fsconfig syscall from new mount api. For example:
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir", "/a:lower::dir", 0); fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/a:lower::dir", 0);
In the latter case, colons in lower layer directory names will be escaped In the latter case, colons in lower layer directory names will be escaped
as an octal characters (\072) when displayed in /proc/self/mountinfo. as an octal characters (\072) when displayed in /proc/self/mountinfo.
...@@ -416,6 +417,16 @@ Only the data of the files in the "data-only" lower layers may be visible ...@@ -416,6 +417,16 @@ Only the data of the files in the "data-only" lower layers may be visible
when a "metacopy" file in one of the lower layers above it, has a "redirect" when a "metacopy" file in one of the lower layers above it, has a "redirect"
to the absolute path of the "lower data" file in the "data-only" lower layer. to the absolute path of the "lower data" file in the "data-only" lower layer.
Since kernel version v6.8, "data-only" lower layers can also be added using
the "datadir+" mount options and the fsconfig syscall from new mount api.
For example:
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/l1", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/l2", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/l3", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "datadir+", "/do1", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "datadir+", "/do2", 0);
fs-verity support fs-verity support
---------------------- ----------------------
...@@ -504,6 +515,29 @@ directory tree on the same or different underlying filesystem, and even ...@@ -504,6 +515,29 @@ directory tree on the same or different underlying filesystem, and even
to a different machine. With the "inodes index" feature, trying to mount to a different machine. With the "inodes index" feature, trying to mount
the copied layers will fail the verification of the lower root file handle. the copied layers will fail the verification of the lower root file handle.
Nesting overlayfs mounts
------------------------
It is possible to use a lower directory that is stored on an overlayfs
mount. For regular files this does not need any special care. However, files
that have overlayfs attributes, such as whiteouts or "overlay.*" xattrs will be
interpreted by the underlying overlayfs mount and stripped out. In order to
allow the second overlayfs mount to see the attributes they must be escaped.
Overlayfs specific xattrs are escaped by using a special prefix of
"overlay.overlay.". So, a file with a "trusted.overlay.overlay.metacopy" xattr
in the lower dir will be exposed as a regular file with a
"trusted.overlay.metacopy" xattr in the overlayfs mount. This can be nested by
repeating the prefix multiple time, as each instance only removes one prefix.
A lower dir with a regular whiteout will always be handled by the overlayfs
mount, so to support storing an effective whiteout file in an overlayfs mount an
alternative form of whiteout is supported. This form is a regular, zero-size
file with the "overlay.whiteout" xattr set, inside a directory with the
"overlay.whiteouts" xattr set. Such whiteouts are never created by overlayfs,
but can be used by userspace tools (like containers) that generate lower layers.
These alternative whiteouts can be escaped using the standard xattr escape
mechanism in order to properly nest to any depth.
Non-standard behavior Non-standard behavior
--------------------- ---------------------
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
obj-$(CONFIG_OVERLAY_FS) += overlay.o obj-$(CONFIG_OVERLAY_FS) += overlay.o
overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \ overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \
copy_up.o export.o params.o copy_up.o export.o params.o xattrs.o
...@@ -252,7 +252,9 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -252,7 +252,9 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
return PTR_ERR(old_file); return PTR_ERR(old_file);
/* Try to use clone_file_range to clone up within the same fs */ /* Try to use clone_file_range to clone up within the same fs */
ovl_start_write(dentry);
cloned = do_clone_file_range(old_file, 0, new_file, 0, len, 0); cloned = do_clone_file_range(old_file, 0, new_file, 0, len, 0);
ovl_end_write(dentry);
if (cloned == len) if (cloned == len)
goto out_fput; goto out_fput;
/* Couldn't clone, so now we try to copy the data */ /* Couldn't clone, so now we try to copy the data */
...@@ -287,8 +289,12 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -287,8 +289,12 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
* it may not recognize all kind of holes and sometimes * it may not recognize all kind of holes and sometimes
* only skips partial of hole area. However, it will be * only skips partial of hole area. However, it will be
* enough for most of the use cases. * enough for most of the use cases.
*
* We do not hold upper sb_writers throughout the loop to avert
* lockdep warning with llseek of lower file in nested overlay:
* - upper sb_writers
* -- lower ovl_inode_lock (ovl_llseek)
*/ */
if (skip_hole && data_pos < old_pos) { if (skip_hole && data_pos < old_pos) {
data_pos = vfs_llseek(old_file, old_pos, SEEK_DATA); data_pos = vfs_llseek(old_file, old_pos, SEEK_DATA);
if (data_pos > old_pos) { if (data_pos > old_pos) {
...@@ -303,9 +309,11 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -303,9 +309,11 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
} }
} }
ovl_start_write(dentry);
bytes = do_splice_direct(old_file, &old_pos, bytes = do_splice_direct(old_file, &old_pos,
new_file, &new_pos, new_file, &new_pos,
this_len, SPLICE_F_MOVE); this_len, SPLICE_F_MOVE);
ovl_end_write(dentry);
if (bytes <= 0) { if (bytes <= 0) {
error = bytes; error = bytes;
break; break;
...@@ -426,29 +434,29 @@ struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real, ...@@ -426,29 +434,29 @@ struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
return ERR_PTR(err); return ERR_PTR(err);
} }
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower, struct ovl_fh *ovl_get_origin_fh(struct ovl_fs *ofs, struct dentry *origin)
struct dentry *upper)
{ {
const struct ovl_fh *fh = NULL;
int err;
/* /*
* When lower layer doesn't support export operations store a 'null' fh, * When lower layer doesn't support export operations store a 'null' fh,
* so we can use the overlay.origin xattr to distignuish between a copy * so we can use the overlay.origin xattr to distignuish between a copy
* up and a pure upper inode. * up and a pure upper inode.
*/ */
if (ovl_can_decode_fh(lower->d_sb)) { if (!ovl_can_decode_fh(origin->d_sb))
fh = ovl_encode_real_fh(ofs, lower, false); return NULL;
if (IS_ERR(fh))
return PTR_ERR(fh); return ovl_encode_real_fh(ofs, origin, false);
} }
int ovl_set_origin_fh(struct ovl_fs *ofs, const struct ovl_fh *fh,
struct dentry *upper)
{
int err;
/* /*
* Do not fail when upper doesn't support xattrs. * Do not fail when upper doesn't support xattrs.
*/ */
err = ovl_check_setxattr(ofs, upper, OVL_XATTR_ORIGIN, fh->buf, err = ovl_check_setxattr(ofs, upper, OVL_XATTR_ORIGIN, fh->buf,
fh ? fh->fb.len : 0, 0); fh ? fh->fb.len : 0, 0);
kfree(fh);
/* Ignore -EPERM from setting "user.*" on symlink/special */ /* Ignore -EPERM from setting "user.*" on symlink/special */
return err == -EPERM ? 0 : err; return err == -EPERM ? 0 : err;
...@@ -476,7 +484,7 @@ static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper, ...@@ -476,7 +484,7 @@ static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper,
* *
* Caller must hold i_mutex on indexdir. * Caller must hold i_mutex on indexdir.
*/ */
static int ovl_create_index(struct dentry *dentry, struct dentry *origin, static int ovl_create_index(struct dentry *dentry, const struct ovl_fh *fh,
struct dentry *upper) struct dentry *upper)
{ {
struct ovl_fs *ofs = OVL_FS(dentry->d_sb); struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
...@@ -502,7 +510,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin, ...@@ -502,7 +510,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
if (WARN_ON(ovl_test_flag(OVL_INDEX, d_inode(dentry)))) if (WARN_ON(ovl_test_flag(OVL_INDEX, d_inode(dentry))))
return -EIO; return -EIO;
err = ovl_get_index_name(ofs, origin, &name); err = ovl_get_index_name_fh(fh, &name);
if (err) if (err)
return err; return err;
...@@ -541,6 +549,7 @@ struct ovl_copy_up_ctx { ...@@ -541,6 +549,7 @@ struct ovl_copy_up_ctx {
struct dentry *destdir; struct dentry *destdir;
struct qstr destname; struct qstr destname;
struct dentry *workdir; struct dentry *workdir;
const struct ovl_fh *origin_fh;
bool origin; bool origin;
bool indexed; bool indexed;
bool metacopy; bool metacopy;
...@@ -555,14 +564,16 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -555,14 +564,16 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb); struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *udir = d_inode(upperdir); struct inode *udir = d_inode(upperdir);
ovl_start_write(c->dentry);
/* Mark parent "impure" because it may now contain non-pure upper */ /* Mark parent "impure" because it may now contain non-pure upper */
err = ovl_set_impure(c->parent, upperdir); err = ovl_set_impure(c->parent, upperdir);
if (err) if (err)
return err; goto out;
err = ovl_set_nlink_lower(c->dentry); err = ovl_set_nlink_lower(c->dentry);
if (err) if (err)
return err; goto out;
inode_lock_nested(udir, I_MUTEX_PARENT); inode_lock_nested(udir, I_MUTEX_PARENT);
upper = ovl_lookup_upper(ofs, c->dentry->d_name.name, upperdir, upper = ovl_lookup_upper(ofs, c->dentry->d_name.name, upperdir,
...@@ -581,10 +592,12 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -581,10 +592,12 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
} }
inode_unlock(udir); inode_unlock(udir);
if (err) if (err)
return err; goto out;
err = ovl_set_nlink_upper(c->dentry); err = ovl_set_nlink_upper(c->dentry);
out:
ovl_end_write(c->dentry);
return err; return err;
} }
...@@ -637,7 +650,7 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp) ...@@ -637,7 +650,7 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
* hard link. * hard link.
*/ */
if (c->origin) { if (c->origin) {
err = ovl_set_origin(ofs, c->lowerpath.dentry, temp); err = ovl_set_origin_fh(ofs, c->origin_fh, temp);
if (err) if (err)
return err; return err;
} }
...@@ -719,21 +732,19 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -719,21 +732,19 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
.link = c->link .link = c->link
}; };
/* workdir and destdir could be the same when copying up to indexdir */
err = -EIO;
if (lock_rename(c->workdir, c->destdir) != NULL)
goto unlock;
err = ovl_prep_cu_creds(c->dentry, &cc); err = ovl_prep_cu_creds(c->dentry, &cc);
if (err) if (err)
goto unlock; return err;
ovl_start_write(c->dentry);
inode_lock(wdir);
temp = ovl_create_temp(ofs, c->workdir, &cattr); temp = ovl_create_temp(ofs, c->workdir, &cattr);
inode_unlock(wdir);
ovl_end_write(c->dentry);
ovl_revert_cu_creds(&cc); ovl_revert_cu_creds(&cc);
err = PTR_ERR(temp);
if (IS_ERR(temp)) if (IS_ERR(temp))
goto unlock; return PTR_ERR(temp);
/* /*
* Copy up data first and then xattrs. Writing data after * Copy up data first and then xattrs. Writing data after
...@@ -741,15 +752,28 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -741,15 +752,28 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
*/ */
path.dentry = temp; path.dentry = temp;
err = ovl_copy_up_data(c, &path); err = ovl_copy_up_data(c, &path);
if (err) /*
* We cannot hold lock_rename() throughout this helper, because or
* lock ordering with sb_writers, which shouldn't be held when calling
* ovl_copy_up_data(), so lock workdir and destdir and make sure that
* temp wasn't moved before copy up completion or cleanup.
* If temp was moved, abort without the cleanup.
*/
ovl_start_write(c->dentry);
if (lock_rename(c->workdir, c->destdir) != NULL ||
temp->d_parent != c->workdir) {
err = -EIO;
goto unlock;
} else if (err) {
goto cleanup; goto cleanup;
}
err = ovl_copy_up_metadata(c, temp); err = ovl_copy_up_metadata(c, temp);
if (err) if (err)
goto cleanup; goto cleanup;
if (S_ISDIR(c->stat.mode) && c->indexed) { if (S_ISDIR(c->stat.mode) && c->indexed) {
err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp); err = ovl_create_index(c->dentry, c->origin_fh, temp);
if (err) if (err)
goto cleanup; goto cleanup;
} }
...@@ -779,6 +803,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -779,6 +803,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
ovl_set_flag(OVL_WHITEOUTS, inode); ovl_set_flag(OVL_WHITEOUTS, inode);
unlock: unlock:
unlock_rename(c->workdir, c->destdir); unlock_rename(c->workdir, c->destdir);
ovl_end_write(c->dentry);
return err; return err;
...@@ -802,9 +827,10 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -802,9 +827,10 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
if (err) if (err)
return err; return err;
ovl_start_write(c->dentry);
tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode); tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
ovl_end_write(c->dentry);
ovl_revert_cu_creds(&cc); ovl_revert_cu_creds(&cc);
if (IS_ERR(tmpfile)) if (IS_ERR(tmpfile))
return PTR_ERR(tmpfile); return PTR_ERR(tmpfile);
...@@ -815,9 +841,11 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -815,9 +841,11 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
goto out_fput; goto out_fput;
} }
ovl_start_write(c->dentry);
err = ovl_copy_up_metadata(c, temp); err = ovl_copy_up_metadata(c, temp);
if (err) if (err)
goto out_fput; goto out;
inode_lock_nested(udir, I_MUTEX_PARENT); inode_lock_nested(udir, I_MUTEX_PARENT);
...@@ -831,7 +859,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -831,7 +859,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
inode_unlock(udir); inode_unlock(udir);
if (err) if (err)
goto out_fput; goto out;
if (c->metacopy_digest) if (c->metacopy_digest)
ovl_set_flag(OVL_HAS_DIGEST, d_inode(c->dentry)); ovl_set_flag(OVL_HAS_DIGEST, d_inode(c->dentry));
...@@ -843,6 +871,8 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -843,6 +871,8 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
ovl_set_upperdata(d_inode(c->dentry)); ovl_set_upperdata(d_inode(c->dentry));
ovl_inode_update(d_inode(c->dentry), dget(temp)); ovl_inode_update(d_inode(c->dentry), dget(temp));
out:
ovl_end_write(c->dentry);
out_fput: out_fput:
fput(tmpfile); fput(tmpfile);
return err; return err;
...@@ -861,6 +891,8 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -861,6 +891,8 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
{ {
int err; int err;
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb); struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct dentry *origin = c->lowerpath.dentry;
struct ovl_fh *fh = NULL;
bool to_index = false; bool to_index = false;
/* /*
...@@ -877,25 +909,35 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -877,25 +909,35 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
to_index = true; to_index = true;
} }
if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1 || to_index) if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1 || to_index) {
fh = ovl_get_origin_fh(ofs, origin);
if (IS_ERR(fh))
return PTR_ERR(fh);
/* origin_fh may be NULL */
c->origin_fh = fh;
c->origin = true; c->origin = true;
}
if (to_index) { if (to_index) {
c->destdir = ovl_indexdir(c->dentry->d_sb); c->destdir = ovl_indexdir(c->dentry->d_sb);
err = ovl_get_index_name(ofs, c->lowerpath.dentry, &c->destname); err = ovl_get_index_name(ofs, origin, &c->destname);
if (err) if (err)
return err; goto out_free_fh;
} else if (WARN_ON(!c->parent)) { } else if (WARN_ON(!c->parent)) {
/* Disconnected dentry must be copied up to index dir */ /* Disconnected dentry must be copied up to index dir */
return -EIO; err = -EIO;
goto out_free_fh;
} else { } else {
/* /*
* Mark parent "impure" because it may now contain non-pure * Mark parent "impure" because it may now contain non-pure
* upper * upper
*/ */
ovl_start_write(c->dentry);
err = ovl_set_impure(c->parent, c->destdir); err = ovl_set_impure(c->parent, c->destdir);
ovl_end_write(c->dentry);
if (err) if (err)
return err; goto out_free_fh;
} }
/* Should we copyup with O_TMPFILE or with workdir? */ /* Should we copyup with O_TMPFILE or with workdir? */
...@@ -909,6 +951,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -909,6 +951,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
if (c->indexed) if (c->indexed)
ovl_set_flag(OVL_INDEX, d_inode(c->dentry)); ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
ovl_start_write(c->dentry);
if (to_index) { if (to_index) {
/* Initialize nlink for copy up of disconnected dentry */ /* Initialize nlink for copy up of disconnected dentry */
err = ovl_set_nlink_upper(c->dentry); err = ovl_set_nlink_upper(c->dentry);
...@@ -923,10 +966,13 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -923,10 +966,13 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_set_upper_alias(c->dentry);
ovl_dentry_update_reval(c->dentry, ovl_dentry_upper(c->dentry)); ovl_dentry_update_reval(c->dentry, ovl_dentry_upper(c->dentry));
} }
ovl_end_write(c->dentry);
out: out:
if (to_index) if (to_index)
kfree(c->destname.name); kfree(c->destname.name);
out_free_fh:
kfree(fh);
return err; return err;
} }
...@@ -1011,15 +1057,16 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) ...@@ -1011,15 +1057,16 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
* Writing to upper file will clear security.capability xattr. We * Writing to upper file will clear security.capability xattr. We
* don't want that to happen for normal copy-up operation. * don't want that to happen for normal copy-up operation.
*/ */
ovl_start_write(c->dentry);
if (capability) { if (capability) {
err = ovl_do_setxattr(ofs, upperpath.dentry, XATTR_NAME_CAPS, err = ovl_do_setxattr(ofs, upperpath.dentry, XATTR_NAME_CAPS,
capability, cap_size, 0); capability, cap_size, 0);
if (err)
goto out_free;
} }
if (!err) {
err = ovl_removexattr(ofs, upperpath.dentry,
err = ovl_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY); OVL_XATTR_METACOPY);
}
ovl_end_write(c->dentry);
if (err) if (err)
goto out_free; goto out_free;
...@@ -1170,17 +1217,10 @@ static bool ovl_open_need_copy_up(struct dentry *dentry, int flags) ...@@ -1170,17 +1217,10 @@ static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
int ovl_maybe_copy_up(struct dentry *dentry, int flags) int ovl_maybe_copy_up(struct dentry *dentry, int flags)
{ {
int err = 0; if (!ovl_open_need_copy_up(dentry, flags))
return 0;
if (ovl_open_need_copy_up(dentry, flags)) {
err = ovl_want_write(dentry);
if (!err) {
err = ovl_copy_up_flags(dentry, flags);
ovl_drop_write(dentry);
}
}
return err; return ovl_copy_up_flags(dentry, flags);
} }
int ovl_copy_up_with_data(struct dentry *dentry) int ovl_copy_up_with_data(struct dentry *dentry)
......
...@@ -477,7 +477,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -477,7 +477,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
goto out_unlock; goto out_unlock;
err = -ESTALE; err = -ESTALE;
if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper))) if (d_is_negative(upper) || !ovl_upper_is_whiteout(ofs, upper))
goto out_dput; goto out_dput;
newdentry = ovl_create_temp(ofs, workdir, cattr); newdentry = ovl_create_temp(ofs, workdir, cattr);
...@@ -559,10 +559,6 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, ...@@ -559,10 +559,6 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
struct cred *override_cred; struct cred *override_cred;
struct dentry *parent = dentry->d_parent; struct dentry *parent = dentry->d_parent;
err = ovl_copy_up(parent);
if (err)
return err;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
/* /*
...@@ -626,6 +622,10 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, ...@@ -626,6 +622,10 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
.link = link, .link = link,
}; };
err = ovl_copy_up(dentry->d_parent);
if (err)
return err;
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
if (err) if (err)
goto out; goto out;
...@@ -700,28 +700,24 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -700,28 +700,24 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
int err; int err;
struct inode *inode; struct inode *inode;
err = ovl_want_write(old); err = ovl_copy_up(old);
if (err) if (err)
goto out; goto out;
err = ovl_copy_up(old); err = ovl_copy_up(new->d_parent);
if (err) if (err)
goto out_drop_write; goto out;
err = ovl_copy_up(new->d_parent); err = ovl_nlink_start(old);
if (err) if (err)
goto out_drop_write; goto out;
if (ovl_is_metacopy_dentry(old)) { if (ovl_is_metacopy_dentry(old)) {
err = ovl_set_link_redirect(old); err = ovl_set_link_redirect(old);
if (err) if (err)
goto out_drop_write; goto out_nlink_end;
} }
err = ovl_nlink_start(old);
if (err)
goto out_drop_write;
inode = d_inode(old); inode = d_inode(old);
ihold(inode); ihold(inode);
...@@ -731,9 +727,8 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -731,9 +727,8 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
if (err) if (err)
iput(inode); iput(inode);
out_nlink_end:
ovl_nlink_end(old); ovl_nlink_end(old);
out_drop_write:
ovl_drop_write(old);
out: out:
return err; return err;
} }
...@@ -891,17 +886,13 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -891,17 +886,13 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
goto out; goto out;
} }
err = ovl_want_write(dentry);
if (err)
goto out;
err = ovl_copy_up(dentry->d_parent); err = ovl_copy_up(dentry->d_parent);
if (err) if (err)
goto out_drop_write; goto out;
err = ovl_nlink_start(dentry); err = ovl_nlink_start(dentry);
if (err) if (err)
goto out_drop_write; goto out;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
if (!lower_positive) if (!lower_positive)
...@@ -926,8 +917,6 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -926,8 +917,6 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
if (ovl_dentry_upper(dentry)) if (ovl_dentry_upper(dentry))
ovl_copyattr(d_inode(dentry)); ovl_copyattr(d_inode(dentry));
out_drop_write:
ovl_drop_write(dentry);
out: out:
ovl_cache_free(&list); ovl_cache_free(&list);
return err; return err;
...@@ -1131,29 +1120,32 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, ...@@ -1131,29 +1120,32 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
} }
} }
err = ovl_want_write(old);
if (err)
goto out;
err = ovl_copy_up(old); err = ovl_copy_up(old);
if (err) if (err)
goto out_drop_write; goto out;
err = ovl_copy_up(new->d_parent); err = ovl_copy_up(new->d_parent);
if (err) if (err)
goto out_drop_write; goto out;
if (!overwrite) { if (!overwrite) {
err = ovl_copy_up(new); err = ovl_copy_up(new);
if (err) if (err)
goto out_drop_write; goto out;
} else if (d_inode(new)) { } else if (d_inode(new)) {
err = ovl_nlink_start(new); err = ovl_nlink_start(new);
if (err) if (err)
goto out_drop_write; goto out;
update_nlink = true; update_nlink = true;
} }
if (!update_nlink) {
/* ovl_nlink_start() took ovl_want_write() */
err = ovl_want_write(old);
if (err)
goto out;
}
old_cred = ovl_override_creds(old->d_sb); old_cred = ovl_override_creds(old->d_sb);
if (!list_empty(&list)) { if (!list_empty(&list)) {
...@@ -1219,7 +1211,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, ...@@ -1219,7 +1211,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
} }
} else { } else {
if (!d_is_negative(newdentry)) { if (!d_is_negative(newdentry)) {
if (!new_opaque || !ovl_is_whiteout(newdentry)) if (!new_opaque || !ovl_upper_is_whiteout(ofs, newdentry))
goto out_dput; goto out_dput;
} else { } else {
if (flags & RENAME_EXCHANGE) if (flags & RENAME_EXCHANGE)
...@@ -1286,7 +1278,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, ...@@ -1286,7 +1278,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
revert_creds(old_cred); revert_creds(old_cred);
if (update_nlink) if (update_nlink)
ovl_nlink_end(new); ovl_nlink_end(new);
out_drop_write: else
ovl_drop_write(old); ovl_drop_write(old);
out: out:
dput(opaquedir); dput(opaquedir);
......
...@@ -23,12 +23,7 @@ static int ovl_encode_maybe_copy_up(struct dentry *dentry) ...@@ -23,12 +23,7 @@ static int ovl_encode_maybe_copy_up(struct dentry *dentry)
if (ovl_dentry_upper(dentry)) if (ovl_dentry_upper(dentry))
return 0; return 0;
err = ovl_want_write(dentry);
if (!err) {
err = ovl_copy_up(dentry); err = ovl_copy_up(dentry);
ovl_drop_write(dentry);
}
if (err) { if (err) {
pr_warn_ratelimited("failed to copy up on encode (%pd2, err=%i)\n", pr_warn_ratelimited("failed to copy up on encode (%pd2, err=%i)\n",
dentry, err); dentry, err);
......
...@@ -15,10 +15,15 @@ ...@@ -15,10 +15,15 @@
#include <linux/fs.h> #include <linux/fs.h>
#include "overlayfs.h" #include "overlayfs.h"
#include "../internal.h" /* for sb_init_dio_done_wq */
struct ovl_aio_req { struct ovl_aio_req {
struct kiocb iocb; struct kiocb iocb;
refcount_t ref; refcount_t ref;
struct kiocb *orig_iocb; struct kiocb *orig_iocb;
/* used for aio completion */
struct work_struct work;
long res;
}; };
static struct kmem_cache *ovl_aio_request_cachep; static struct kmem_cache *ovl_aio_request_cachep;
...@@ -235,6 +240,12 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence) ...@@ -235,6 +240,12 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
return ret; return ret;
} }
static void ovl_file_modified(struct file *file)
{
/* Update size/mtime */
ovl_copyattr(file_inode(file));
}
static void ovl_file_accessed(struct file *file) static void ovl_file_accessed(struct file *file)
{ {
struct inode *inode, *upperinode; struct inode *inode, *upperinode;
...@@ -263,20 +274,12 @@ static void ovl_file_accessed(struct file *file) ...@@ -263,20 +274,12 @@ static void ovl_file_accessed(struct file *file)
touch_atime(&file->f_path); touch_atime(&file->f_path);
} }
static rwf_t ovl_iocb_to_rwf(int ifl) #define OVL_IOCB_MASK \
(IOCB_NOWAIT | IOCB_HIPRI | IOCB_DSYNC | IOCB_SYNC | IOCB_APPEND)
static rwf_t iocb_to_rw_flags(int flags)
{ {
rwf_t flags = 0; return (__force rwf_t)(flags & OVL_IOCB_MASK);
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 inline void ovl_aio_put(struct ovl_aio_req *aio_req) static inline void ovl_aio_put(struct ovl_aio_req *aio_req)
...@@ -293,10 +296,8 @@ static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req) ...@@ -293,10 +296,8 @@ static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)
struct kiocb *orig_iocb = aio_req->orig_iocb; struct kiocb *orig_iocb = aio_req->orig_iocb;
if (iocb->ki_flags & IOCB_WRITE) { if (iocb->ki_flags & IOCB_WRITE) {
struct inode *inode = file_inode(orig_iocb->ki_filp);
kiocb_end_write(iocb); kiocb_end_write(iocb);
ovl_copyattr(inode); ovl_file_modified(orig_iocb->ki_filp);
} }
orig_iocb->ki_pos = iocb->ki_pos; orig_iocb->ki_pos = iocb->ki_pos;
...@@ -313,6 +314,37 @@ static void ovl_aio_rw_complete(struct kiocb *iocb, long res) ...@@ -313,6 +314,37 @@ static void ovl_aio_rw_complete(struct kiocb *iocb, long res)
orig_iocb->ki_complete(orig_iocb, res); orig_iocb->ki_complete(orig_iocb, res);
} }
static void ovl_aio_complete_work(struct work_struct *work)
{
struct ovl_aio_req *aio_req = container_of(work,
struct ovl_aio_req, work);
ovl_aio_rw_complete(&aio_req->iocb, aio_req->res);
}
static void ovl_aio_queue_completion(struct kiocb *iocb, long res)
{
struct ovl_aio_req *aio_req = container_of(iocb,
struct ovl_aio_req, iocb);
struct kiocb *orig_iocb = aio_req->orig_iocb;
/*
* Punt to a work queue to serialize updates of mtime/size.
*/
aio_req->res = res;
INIT_WORK(&aio_req->work, ovl_aio_complete_work);
queue_work(file_inode(orig_iocb->ki_filp)->i_sb->s_dio_done_wq,
&aio_req->work);
}
static int ovl_init_aio_done_wq(struct super_block *sb)
{
if (sb->s_dio_done_wq)
return 0;
return sb_init_dio_done_wq(sb);
}
static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{ {
struct file *file = iocb->ki_filp; struct file *file = iocb->ki_filp;
...@@ -334,8 +366,9 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -334,8 +366,9 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
old_cred = ovl_override_creds(file_inode(file)->i_sb); old_cred = ovl_override_creds(file_inode(file)->i_sb);
if (is_sync_kiocb(iocb)) { if (is_sync_kiocb(iocb)) {
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, rwf_t rwf = iocb_to_rw_flags(iocb->ki_flags);
ovl_iocb_to_rwf(iocb->ki_flags));
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, rwf);
} else { } else {
struct ovl_aio_req *aio_req; struct ovl_aio_req *aio_req;
...@@ -401,15 +434,20 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -401,15 +434,20 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
old_cred = ovl_override_creds(file_inode(file)->i_sb); old_cred = ovl_override_creds(file_inode(file)->i_sb);
if (is_sync_kiocb(iocb)) { if (is_sync_kiocb(iocb)) {
rwf_t rwf = iocb_to_rw_flags(ifl);
file_start_write(real.file); file_start_write(real.file);
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, rwf);
ovl_iocb_to_rwf(ifl));
file_end_write(real.file); file_end_write(real.file);
/* Update size */ /* Update size */
ovl_copyattr(inode); ovl_file_modified(file);
} else { } else {
struct ovl_aio_req *aio_req; struct ovl_aio_req *aio_req;
ret = ovl_init_aio_done_wq(inode->i_sb);
if (ret)
goto out;
ret = -ENOMEM; ret = -ENOMEM;
aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL);
if (!aio_req) if (!aio_req)
...@@ -418,7 +456,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -418,7 +456,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
aio_req->orig_iocb = iocb; aio_req->orig_iocb = iocb;
kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); kiocb_clone(&aio_req->iocb, iocb, get_file(real.file));
aio_req->iocb.ki_flags = ifl; aio_req->iocb.ki_flags = ifl;
aio_req->iocb.ki_complete = ovl_aio_rw_complete; aio_req->iocb.ki_complete = ovl_aio_queue_completion;
refcount_set(&aio_req->ref, 2); refcount_set(&aio_req->ref, 2);
kiocb_start_write(&aio_req->iocb); kiocb_start_write(&aio_req->iocb);
ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter); ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
...@@ -492,7 +530,7 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out, ...@@ -492,7 +530,7 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
file_end_write(real.file); file_end_write(real.file);
/* Update size */ /* Update size */
ovl_copyattr(inode); ovl_file_modified(out);
revert_creds(old_cred); revert_creds(old_cred);
fdput(real); fdput(real);
...@@ -573,7 +611,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len ...@@ -573,7 +611,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
revert_creds(old_cred); revert_creds(old_cred);
/* Update size */ /* Update size */
ovl_copyattr(inode); ovl_file_modified(file);
fdput(real); fdput(real);
...@@ -657,7 +695,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, ...@@ -657,7 +695,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
revert_creds(old_cred); revert_creds(old_cred);
/* Update size */ /* Update size */
ovl_copyattr(inode_out); ovl_file_modified(file_out);
fdput(real_in); fdput(real_in);
fdput(real_out); fdput(real_out);
......
...@@ -32,10 +32,6 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -32,10 +32,6 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (err) if (err)
return err; return err;
err = ovl_want_write(dentry);
if (err)
goto out;
if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_valid & ATTR_SIZE) {
/* Truncate should trigger data copy up as well */ /* Truncate should trigger data copy up as well */
full_copy_up = true; full_copy_up = true;
...@@ -54,7 +50,7 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -54,7 +50,7 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
winode = d_inode(upperdentry); winode = d_inode(upperdentry);
err = get_write_access(winode); err = get_write_access(winode);
if (err) if (err)
goto out_drop_write; goto out;
} }
if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
...@@ -78,6 +74,10 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -78,6 +74,10 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
*/ */
attr->ia_valid &= ~ATTR_OPEN; attr->ia_valid &= ~ATTR_OPEN;
err = ovl_want_write(dentry);
if (err)
goto out_put_write;
inode_lock(upperdentry->d_inode); inode_lock(upperdentry->d_inode);
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_do_notify_change(ofs, upperdentry, attr); err = ovl_do_notify_change(ofs, upperdentry, attr);
...@@ -85,12 +85,12 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -85,12 +85,12 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (!err) if (!err)
ovl_copyattr(dentry->d_inode); ovl_copyattr(dentry->d_inode);
inode_unlock(upperdentry->d_inode); inode_unlock(upperdentry->d_inode);
ovl_drop_write(dentry);
out_put_write:
if (winode) if (winode)
put_write_access(winode); put_write_access(winode);
} }
out_drop_write:
ovl_drop_write(dentry);
out: out:
return err; return err;
} }
...@@ -339,130 +339,6 @@ static const char *ovl_get_link(struct dentry *dentry, ...@@ -339,130 +339,6 @@ static const char *ovl_get_link(struct dentry *dentry,
return p; return p;
} }
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
{
struct ovl_fs *ofs = OVL_FS(sb);
if (ofs->config.userxattr)
return strncmp(name, OVL_XATTR_USER_PREFIX,
sizeof(OVL_XATTR_USER_PREFIX) - 1) == 0;
else
return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
sizeof(OVL_XATTR_TRUSTED_PREFIX) - 1) == 0;
}
int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
int err;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdentry = ovl_i_dentry_upper(inode);
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
struct path realpath;
const struct cred *old_cred;
err = ovl_want_write(dentry);
if (err)
goto out;
if (!value && !upperdentry) {
ovl_path_lower(dentry, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
revert_creds(old_cred);
if (err < 0)
goto out_drop_write;
}
if (!upperdentry) {
err = ovl_copy_up(dentry);
if (err)
goto out_drop_write;
realdentry = ovl_dentry_upper(dentry);
}
old_cred = ovl_override_creds(dentry->d_sb);
if (value) {
err = ovl_do_setxattr(ofs, realdentry, name, value, size,
flags);
} else {
WARN_ON(flags != XATTR_REPLACE);
err = ovl_do_removexattr(ofs, realdentry, name);
}
revert_creds(old_cred);
/* copy c/mtime */
ovl_copyattr(inode);
out_drop_write:
ovl_drop_write(dentry);
out:
return err;
}
int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
void *value, size_t size)
{
ssize_t res;
const struct cred *old_cred;
struct path realpath;
ovl_i_path_real(inode, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
revert_creds(old_cred);
return res;
}
static bool ovl_can_list(struct super_block *sb, const char *s)
{
/* Never list private (.overlay) */
if (ovl_is_private_xattr(sb, s))
return false;
/* List all non-trusted xattrs */
if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
return true;
/* list other trusted for superuser only */
return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
}
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
struct dentry *realdentry = ovl_dentry_real(dentry);
ssize_t res;
size_t len;
char *s;
const struct cred *old_cred;
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_listxattr(realdentry, list, size);
revert_creds(old_cred);
if (res <= 0 || size == 0)
return res;
/* filter out private xattrs */
for (s = list, len = res; len;) {
size_t slen = strnlen(s, len) + 1;
/* underlying fs providing us with an broken xattr list? */
if (WARN_ON(slen > len))
return -EIO;
len -= slen;
if (!ovl_can_list(dentry->d_sb, s)) {
res -= slen;
memmove(s, s + slen, len);
} else {
s += slen;
}
}
return res;
}
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
/* /*
* Apply the idmapping of the layer to POSIX ACLs. The caller must pass a clone * Apply the idmapping of the layer to POSIX ACLs. The caller must pass a clone
...@@ -611,10 +487,6 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode, ...@@ -611,10 +487,6 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
struct dentry *upperdentry = ovl_dentry_upper(dentry); struct dentry *upperdentry = ovl_dentry_upper(dentry);
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
err = ovl_want_write(dentry);
if (err)
return err;
/* /*
* If ACL is to be removed from a lower file, check if it exists in * If ACL is to be removed from a lower file, check if it exists in
* the first place before copying it up. * the first place before copying it up.
...@@ -630,7 +502,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode, ...@@ -630,7 +502,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
revert_creds(old_cred); revert_creds(old_cred);
if (IS_ERR(real_acl)) { if (IS_ERR(real_acl)) {
err = PTR_ERR(real_acl); err = PTR_ERR(real_acl);
goto out_drop_write; goto out;
} }
posix_acl_release(real_acl); posix_acl_release(real_acl);
} }
...@@ -638,23 +510,26 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode, ...@@ -638,23 +510,26 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
if (!upperdentry) { if (!upperdentry) {
err = ovl_copy_up(dentry); err = ovl_copy_up(dentry);
if (err) if (err)
goto out_drop_write; goto out;
realdentry = ovl_dentry_upper(dentry); realdentry = ovl_dentry_upper(dentry);
} }
err = ovl_want_write(dentry);
if (err)
goto out;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
if (acl) if (acl)
err = ovl_do_set_acl(ofs, realdentry, acl_name, acl); err = ovl_do_set_acl(ofs, realdentry, acl_name, acl);
else else
err = ovl_do_remove_acl(ofs, realdentry, acl_name); err = ovl_do_remove_acl(ofs, realdentry, acl_name);
revert_creds(old_cred); revert_creds(old_cred);
ovl_drop_write(dentry);
/* copy c/mtime */ /* copy c/mtime */
ovl_copyattr(inode); ovl_copyattr(inode);
out:
out_drop_write:
ovl_drop_write(dentry);
return err; return err;
} }
...@@ -778,14 +653,14 @@ int ovl_fileattr_set(struct mnt_idmap *idmap, ...@@ -778,14 +653,14 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
unsigned int flags; unsigned int flags;
int err; int err;
err = ovl_want_write(dentry);
if (err)
goto out;
err = ovl_copy_up(dentry); err = ovl_copy_up(dentry);
if (!err) { if (!err) {
ovl_path_real(dentry, &upperpath); ovl_path_real(dentry, &upperpath);
err = ovl_want_write(dentry);
if (err)
goto out;
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
/* /*
* Store immutable/append-only flags in xattr and clear them * Store immutable/append-only flags in xattr and clear them
...@@ -798,6 +673,7 @@ int ovl_fileattr_set(struct mnt_idmap *idmap, ...@@ -798,6 +673,7 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
if (!err) if (!err)
err = ovl_real_fileattr_set(&upperpath, fa); err = ovl_real_fileattr_set(&upperpath, fa);
revert_creds(old_cred); revert_creds(old_cred);
ovl_drop_write(dentry);
/* /*
* Merge real inode flags with inode flags read from * Merge real inode flags with inode flags read from
...@@ -812,7 +688,6 @@ int ovl_fileattr_set(struct mnt_idmap *idmap, ...@@ -812,7 +688,6 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
/* Update ctime */ /* Update ctime */
ovl_copyattr(inode); ovl_copyattr(inode);
} }
ovl_drop_write(dentry);
out: out:
return err; return err;
} }
......
...@@ -251,7 +251,10 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -251,7 +251,10 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
err = -EREMOTE; err = -EREMOTE;
goto out_err; goto out_err;
} }
if (ovl_is_whiteout(this)) {
path.dentry = this;
path.mnt = d->mnt;
if (ovl_path_is_whiteout(OVL_FS(d->sb), &path)) {
d->stop = d->opaque = true; d->stop = d->opaque = true;
goto put_and_out; goto put_and_out;
} }
...@@ -264,8 +267,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -264,8 +267,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
goto put_and_out; goto put_and_out;
} }
path.dentry = this;
path.mnt = d->mnt;
if (!d_can_lookup(this)) { if (!d_can_lookup(this)) {
if (d->is_dir || !last_element) { if (d->is_dir || !last_element) {
d->stop = true; d->stop = true;
...@@ -438,7 +439,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, ...@@ -438,7 +439,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
else if (IS_ERR(origin)) else if (IS_ERR(origin))
return PTR_ERR(origin); return PTR_ERR(origin);
if (upperdentry && !ovl_is_whiteout(upperdentry) && if (upperdentry && !ovl_upper_is_whiteout(ofs, upperdentry) &&
inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode)) inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode))
goto invalid; goto invalid;
...@@ -507,6 +508,19 @@ static int ovl_verify_fh(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -507,6 +508,19 @@ static int ovl_verify_fh(struct ovl_fs *ofs, struct dentry *dentry,
return err; return err;
} }
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, const struct ovl_fh *fh,
bool is_upper, bool set)
{
int err;
err = ovl_verify_fh(ofs, dentry, ox, fh);
if (set && err == -ENODATA)
err = ovl_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
return err;
}
/* /*
* Verify that @real dentry matches the file handle stored in xattr @name. * Verify that @real dentry matches the file handle stored in xattr @name.
* *
...@@ -515,9 +529,9 @@ static int ovl_verify_fh(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -515,9 +529,9 @@ static int ovl_verify_fh(struct ovl_fs *ofs, struct dentry *dentry,
* *
* Return 0 on match, -ESTALE on mismatch, -ENODATA on no xattr, < 0 on error. * Return 0 on match, -ESTALE on mismatch, -ENODATA on no xattr, < 0 on error.
*/ */
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry, int ovl_verify_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real, bool is_upper, enum ovl_xattr ox, struct dentry *real,
bool set) bool is_upper, bool set)
{ {
struct inode *inode; struct inode *inode;
struct ovl_fh *fh; struct ovl_fh *fh;
...@@ -530,9 +544,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -530,9 +544,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
goto fail; goto fail;
} }
err = ovl_verify_fh(ofs, dentry, ox, fh); err = ovl_verify_set_fh(ofs, dentry, ox, fh, is_upper, set);
if (set && err == -ENODATA)
err = ovl_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
if (err) if (err)
goto fail; goto fail;
...@@ -548,6 +560,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -548,6 +560,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
goto out; goto out;
} }
/* Get upper dentry from index */ /* Get upper dentry from index */
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index, struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index,
bool connected) bool connected)
...@@ -684,7 +697,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index) ...@@ -684,7 +697,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
goto out; goto out;
} }
static int ovl_get_index_name_fh(struct ovl_fh *fh, struct qstr *name) int ovl_get_index_name_fh(const struct ovl_fh *fh, struct qstr *name)
{ {
char *n, *s; char *n, *s;
...@@ -873,20 +886,27 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path) ...@@ -873,20 +886,27 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry, static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
struct dentry *lower, struct dentry *upper) struct dentry *lower, struct dentry *upper)
{ {
const struct ovl_fh *fh;
int err; int err;
if (ovl_check_origin_xattr(ofs, upper)) if (ovl_check_origin_xattr(ofs, upper))
return 0; return 0;
fh = ovl_get_origin_fh(ofs, lower);
if (IS_ERR(fh))
return PTR_ERR(fh);
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
if (err) if (err)
return err; goto out;
err = ovl_set_origin(ofs, lower, upper); err = ovl_set_origin_fh(ofs, fh, upper);
if (!err) if (!err)
err = ovl_set_impure(dentry->d_parent, upper->d_parent); err = ovl_set_impure(dentry->d_parent, upper->d_parent);
ovl_drop_write(dentry); ovl_drop_write(dentry);
out:
kfree(fh);
return err; return err;
} }
...@@ -1383,7 +1403,11 @@ bool ovl_lower_positive(struct dentry *dentry) ...@@ -1383,7 +1403,11 @@ bool ovl_lower_positive(struct dentry *dentry)
break; break;
} }
} else { } else {
positive = !ovl_is_whiteout(this); struct path path = {
.dentry = this,
.mnt = parentpath->layer->mnt,
};
positive = !ovl_path_is_whiteout(OVL_FS(dentry->d_sb), &path);
done = true; done = true;
dput(this); dput(this);
} }
......
...@@ -28,7 +28,16 @@ enum ovl_path_type { ...@@ -28,7 +28,16 @@ enum ovl_path_type {
#define OVL_XATTR_NAMESPACE "overlay." #define OVL_XATTR_NAMESPACE "overlay."
#define OVL_XATTR_TRUSTED_PREFIX XATTR_TRUSTED_PREFIX OVL_XATTR_NAMESPACE #define OVL_XATTR_TRUSTED_PREFIX XATTR_TRUSTED_PREFIX OVL_XATTR_NAMESPACE
#define OVL_XATTR_TRUSTED_PREFIX_LEN (sizeof(OVL_XATTR_TRUSTED_PREFIX) - 1)
#define OVL_XATTR_USER_PREFIX XATTR_USER_PREFIX OVL_XATTR_NAMESPACE #define OVL_XATTR_USER_PREFIX XATTR_USER_PREFIX OVL_XATTR_NAMESPACE
#define OVL_XATTR_USER_PREFIX_LEN (sizeof(OVL_XATTR_USER_PREFIX) - 1)
#define OVL_XATTR_ESCAPE_PREFIX OVL_XATTR_NAMESPACE
#define OVL_XATTR_ESCAPE_PREFIX_LEN (sizeof(OVL_XATTR_ESCAPE_PREFIX) - 1)
#define OVL_XATTR_ESCAPE_TRUSTED_PREFIX OVL_XATTR_TRUSTED_PREFIX OVL_XATTR_ESCAPE_PREFIX
#define OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN (sizeof(OVL_XATTR_ESCAPE_TRUSTED_PREFIX) - 1)
#define OVL_XATTR_ESCAPE_USER_PREFIX OVL_XATTR_USER_PREFIX OVL_XATTR_ESCAPE_PREFIX
#define OVL_XATTR_ESCAPE_USER_PREFIX_LEN (sizeof(OVL_XATTR_ESCAPE_USER_PREFIX) - 1)
enum ovl_xattr { enum ovl_xattr {
OVL_XATTR_OPAQUE, OVL_XATTR_OPAQUE,
...@@ -40,6 +49,8 @@ enum ovl_xattr { ...@@ -40,6 +49,8 @@ enum ovl_xattr {
OVL_XATTR_UUID, OVL_XATTR_UUID,
OVL_XATTR_METACOPY, OVL_XATTR_METACOPY,
OVL_XATTR_PROTATTR, OVL_XATTR_PROTATTR,
OVL_XATTR_XWHITEOUT,
OVL_XATTR_XWHITEOUTS,
}; };
enum ovl_inode_flag { enum ovl_inode_flag {
...@@ -398,6 +409,10 @@ static inline bool ovl_open_flags_need_copy_up(int flags) ...@@ -398,6 +409,10 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
} }
/* util.c */ /* util.c */
int ovl_get_write_access(struct dentry *dentry);
void ovl_put_write_access(struct dentry *dentry);
void ovl_start_write(struct dentry *dentry);
void ovl_end_write(struct dentry *dentry);
int ovl_want_write(struct dentry *dentry); int ovl_want_write(struct dentry *dentry);
void ovl_drop_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry);
struct dentry *ovl_workdir(struct dentry *dentry); struct dentry *ovl_workdir(struct dentry *dentry);
...@@ -460,6 +475,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry); ...@@ -460,6 +475,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
void ovl_dir_modified(struct dentry *dentry, bool impurity); void ovl_dir_modified(struct dentry *dentry, bool impurity);
u64 ovl_inode_version_get(struct inode *inode); u64 ovl_inode_version_get(struct inode *inode);
bool ovl_is_whiteout(struct dentry *dentry); bool ovl_is_whiteout(struct dentry *dentry);
bool ovl_path_is_whiteout(struct ovl_fs *ofs, const struct path *path);
struct file *ovl_path_open(const struct path *path, int flags); struct file *ovl_path_open(const struct path *path, int flags);
int ovl_copy_up_start(struct dentry *dentry, int flags); int ovl_copy_up_start(struct dentry *dentry, int flags);
void ovl_copy_up_end(struct dentry *dentry); void ovl_copy_up_end(struct dentry *dentry);
...@@ -467,9 +483,21 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags); ...@@ -467,9 +483,21 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags);
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path, bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
enum ovl_xattr ox); enum ovl_xattr ox);
bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path); bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path);
bool ovl_path_check_xwhiteout_xattr(struct ovl_fs *ofs, const struct path *path);
bool ovl_path_check_xwhiteouts_xattr(struct ovl_fs *ofs, const struct path *path);
bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs, bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
const struct path *upperpath); const struct path *upperpath);
static inline bool ovl_upper_is_whiteout(struct ovl_fs *ofs,
struct dentry *upperdentry)
{
struct path upperpath = {
.dentry = upperdentry,
.mnt = ovl_upper_mnt(ofs),
};
return ovl_path_is_whiteout(ofs, &upperpath);
}
static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs, static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs,
struct dentry *upperdentry) struct dentry *upperdentry)
{ {
...@@ -624,11 +652,15 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh, ...@@ -624,11 +652,15 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
struct dentry *upperdentry, struct ovl_path **stackp); struct dentry *upperdentry, struct ovl_path **stackp);
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry, int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real, bool is_upper, enum ovl_xattr ox, const struct ovl_fh *fh,
bool set); bool is_upper, bool set);
int ovl_verify_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real,
bool is_upper, bool set);
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index, struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index,
bool connected); bool connected);
int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index); int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index);
int ovl_get_index_name_fh(const struct ovl_fh *fh, struct qstr *name);
int ovl_get_index_name(struct ovl_fs *ofs, struct dentry *origin, int ovl_get_index_name(struct ovl_fs *ofs, struct dentry *origin,
struct qstr *name); struct qstr *name);
struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh); struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh);
...@@ -640,17 +672,24 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -640,17 +672,24 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags); unsigned int flags);
bool ovl_lower_positive(struct dentry *dentry); bool ovl_lower_positive(struct dentry *dentry);
static inline int ovl_verify_origin_fh(struct ovl_fs *ofs, struct dentry *upper,
const struct ovl_fh *fh, bool set)
{
return ovl_verify_set_fh(ofs, upper, OVL_XATTR_ORIGIN, fh, false, set);
}
static inline int ovl_verify_origin(struct ovl_fs *ofs, struct dentry *upper, static inline int ovl_verify_origin(struct ovl_fs *ofs, struct dentry *upper,
struct dentry *origin, bool set) struct dentry *origin, bool set)
{ {
return ovl_verify_set_fh(ofs, upper, OVL_XATTR_ORIGIN, origin, return ovl_verify_origin_xattr(ofs, upper, OVL_XATTR_ORIGIN, origin,
false, set); false, set);
} }
static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index, static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index,
struct dentry *upper, bool set) struct dentry *upper, bool set)
{ {
return ovl_verify_set_fh(ofs, index, OVL_XATTR_UPPER, upper, true, set); return ovl_verify_origin_xattr(ofs, index, OVL_XATTR_UPPER, upper,
true, set);
} }
/* readdir.c */ /* readdir.c */
...@@ -684,17 +723,8 @@ int ovl_set_nlink_lower(struct dentry *dentry); ...@@ -684,17 +723,8 @@ int ovl_set_nlink_lower(struct dentry *dentry);
unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry, unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
struct dentry *upperdentry, struct dentry *upperdentry,
unsigned int fallback); unsigned int fallback);
int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr);
int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
struct kstat *stat, u32 request_mask, unsigned int flags);
int ovl_permission(struct mnt_idmap *idmap, struct inode *inode, int ovl_permission(struct mnt_idmap *idmap, struct inode *inode,
int mask); int mask);
int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
const void *value, size_t size, int flags);
int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
void *value, size_t size);
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap, struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
...@@ -815,7 +845,8 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *path, struct dentr ...@@ -815,7 +845,8 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *path, struct dentr
int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upper, struct kstat *stat); int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upper, struct kstat *stat);
struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real, struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
bool is_upper); bool is_upper);
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower, struct ovl_fh *ovl_get_origin_fh(struct ovl_fs *ofs, struct dentry *origin);
int ovl_set_origin_fh(struct ovl_fs *ofs, const struct ovl_fh *fh,
struct dentry *upper); struct dentry *upper);
/* export.c */ /* export.c */
...@@ -830,3 +861,12 @@ static inline bool ovl_force_readonly(struct ovl_fs *ofs) ...@@ -830,3 +861,12 @@ static inline bool ovl_force_readonly(struct ovl_fs *ofs)
{ {
return (!ovl_upper_mnt(ofs) || !ofs->workdir); return (!ovl_upper_mnt(ofs) || !ofs->workdir);
} }
/* xattr.c */
const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs);
int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr);
int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
struct kstat *stat, u32 request_mask, unsigned int flags);
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
...@@ -43,8 +43,10 @@ module_param_named(metacopy, ovl_metacopy_def, bool, 0644); ...@@ -43,8 +43,10 @@ module_param_named(metacopy, ovl_metacopy_def, bool, 0644);
MODULE_PARM_DESC(metacopy, MODULE_PARM_DESC(metacopy,
"Default to on or off for the metadata only copy up feature"); "Default to on or off for the metadata only copy up feature");
enum { enum ovl_opt {
Opt_lowerdir, Opt_lowerdir,
Opt_lowerdir_add,
Opt_datadir_add,
Opt_upperdir, Opt_upperdir,
Opt_workdir, Opt_workdir,
Opt_default_permissions, Opt_default_permissions,
...@@ -140,8 +142,11 @@ static int ovl_verity_mode_def(void) ...@@ -140,8 +142,11 @@ static int ovl_verity_mode_def(void)
#define fsparam_string_empty(NAME, OPT) \ #define fsparam_string_empty(NAME, OPT) \
__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL) __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
const struct fs_parameter_spec ovl_parameter_spec[] = { const struct fs_parameter_spec ovl_parameter_spec[] = {
fsparam_string_empty("lowerdir", Opt_lowerdir), fsparam_string_empty("lowerdir", Opt_lowerdir),
fsparam_string("lowerdir+", Opt_lowerdir_add),
fsparam_string("datadir+", Opt_datadir_add),
fsparam_string("upperdir", Opt_upperdir), fsparam_string("upperdir", Opt_upperdir),
fsparam_string("workdir", Opt_workdir), fsparam_string("workdir", Opt_workdir),
fsparam_flag("default_permissions", Opt_default_permissions), fsparam_flag("default_permissions", Opt_default_permissions),
...@@ -238,19 +243,8 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path) ...@@ -238,19 +243,8 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
pr_err("failed to resolve '%s': %i\n", name, err); pr_err("failed to resolve '%s': %i\n", name, err);
goto out; goto out;
} }
err = -EINVAL;
if (ovl_dentry_weird(path->dentry)) {
pr_err("filesystem on '%s' not supported\n", name);
goto out_put;
}
if (!d_is_dir(path->dentry)) {
pr_err("'%s' not a directory\n", name);
goto out_put;
}
return 0; return 0;
out_put:
path_put_init(path);
out: out:
return err; return err;
} }
...@@ -268,7 +262,7 @@ static void ovl_unescape(char *s) ...@@ -268,7 +262,7 @@ static void ovl_unescape(char *s)
} }
} }
static int ovl_mount_dir(const char *name, struct path *path, bool upper) static int ovl_mount_dir(const char *name, struct path *path)
{ {
int err = -ENOMEM; int err = -ENOMEM;
char *tmp = kstrdup(name, GFP_KERNEL); char *tmp = kstrdup(name, GFP_KERNEL);
...@@ -276,68 +270,147 @@ static int ovl_mount_dir(const char *name, struct path *path, bool upper) ...@@ -276,68 +270,147 @@ static int ovl_mount_dir(const char *name, struct path *path, bool upper)
if (tmp) { if (tmp) {
ovl_unescape(tmp); ovl_unescape(tmp);
err = ovl_mount_dir_noesc(tmp, path); err = ovl_mount_dir_noesc(tmp, path);
if (!err && upper && path->dentry->d_flags & DCACHE_OP_REAL) {
pr_err("filesystem on '%s' not supported as upperdir\n",
tmp);
path_put_init(path);
err = -EINVAL;
}
kfree(tmp); kfree(tmp);
} }
return err; return err;
} }
static int ovl_parse_param_upperdir(const char *name, struct fs_context *fc, static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path,
bool workdir) enum ovl_opt layer, const char *name, bool upper)
{ {
int err;
struct ovl_fs *ofs = fc->s_fs_info;
struct ovl_config *config = &ofs->config;
struct ovl_fs_context *ctx = fc->fs_private; struct ovl_fs_context *ctx = fc->fs_private;
struct path path;
char *dup;
err = ovl_mount_dir(name, &path, true); if (ovl_dentry_weird(path->dentry))
if (err) return invalfc(fc, "filesystem on %s not supported", name);
return err;
if (!d_is_dir(path->dentry))
return invalfc(fc, "%s is not a directory", name);
/* /*
* Check whether upper path is read-only here to report failures * Check whether upper path is read-only here to report failures
* early. Don't forget to recheck when the superblock is created * early. Don't forget to recheck when the superblock is created
* as the mount attributes could change. * as the mount attributes could change.
*/ */
if (__mnt_is_readonly(path.mnt)) { if (upper) {
path_put(&path); if (path->dentry->d_flags & DCACHE_OP_REAL)
return -EINVAL; return invalfc(fc, "filesystem on %s not supported as upperdir", name);
if (__mnt_is_readonly(path->mnt))
return invalfc(fc, "filesystem on %s is read-only", name);
} else {
if (ctx->lowerdir_all && layer != Opt_lowerdir)
return invalfc(fc, "lowerdir+ and datadir+ cannot follow lowerdir");
if (ctx->nr_data && layer == Opt_lowerdir_add)
return invalfc(fc, "regular lower layers cannot follow data layers");
if (ctx->nr == OVL_MAX_STACK)
return invalfc(fc, "too many lower directories, limit is %d",
OVL_MAX_STACK);
} }
return 0;
}
dup = kstrdup(name, GFP_KERNEL); static int ovl_ctx_realloc_lower(struct fs_context *fc)
if (!dup) { {
path_put(&path); struct ovl_fs_context *ctx = fc->fs_private;
struct ovl_fs_context_layer *l;
size_t nr;
if (ctx->nr < ctx->capacity)
return 0;
nr = min_t(size_t, max(4096 / sizeof(*l), ctx->capacity * 2),
OVL_MAX_STACK);
l = krealloc_array(ctx->lower, nr, sizeof(*l), GFP_KERNEL_ACCOUNT);
if (!l)
return -ENOMEM; return -ENOMEM;
ctx->lower = l;
ctx->capacity = nr;
return 0;
}
static void ovl_add_layer(struct fs_context *fc, enum ovl_opt layer,
struct path *path, char **pname)
{
struct ovl_fs *ofs = fc->s_fs_info;
struct ovl_config *config = &ofs->config;
struct ovl_fs_context *ctx = fc->fs_private;
struct ovl_fs_context_layer *l;
switch (layer) {
case Opt_workdir:
swap(config->workdir, *pname);
swap(ctx->work, *path);
break;
case Opt_upperdir:
swap(config->upperdir, *pname);
swap(ctx->upper, *path);
break;
case Opt_datadir_add:
ctx->nr_data++;
fallthrough;
case Opt_lowerdir_add:
WARN_ON(ctx->nr >= ctx->capacity);
l = &ctx->lower[ctx->nr++];
memset(l, 0, sizeof(*l));
swap(l->name, *pname);
swap(l->path, *path);
break;
default:
WARN_ON(1);
} }
}
if (workdir) { static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
kfree(config->workdir); enum ovl_opt layer)
config->workdir = dup; {
path_put(&ctx->work); char *name = kstrdup(param->string, GFP_KERNEL);
ctx->work = path; bool upper = (layer == Opt_upperdir || layer == Opt_workdir);
} else { struct path path;
kfree(config->upperdir); int err;
config->upperdir = dup;
path_put(&ctx->upper); if (!name)
ctx->upper = path; return -ENOMEM;
if (upper)
err = ovl_mount_dir(name, &path);
else
err = ovl_mount_dir_noesc(name, &path);
if (err)
goto out_free;
err = ovl_mount_dir_check(fc, &path, layer, name, upper);
if (err)
goto out_put;
if (!upper) {
err = ovl_ctx_realloc_lower(fc);
if (err)
goto out_put;
} }
return 0;
/* Store the user provided path string in ctx to show in mountinfo */
ovl_add_layer(fc, layer, &path, &name);
out_put:
path_put(&path);
out_free:
kfree(name);
return err;
} }
static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx) static void ovl_reset_lowerdirs(struct ovl_fs_context *ctx)
{ {
for (size_t nr = 0; nr < ctx->nr; nr++) { struct ovl_fs_context_layer *l = ctx->lower;
path_put(&ctx->lower[nr].path);
kfree(ctx->lower[nr].name); // Reset old user provided lowerdir string
ctx->lower[nr].name = NULL; kfree(ctx->lowerdir_all);
ctx->lowerdir_all = NULL;
for (size_t nr = 0; nr < ctx->nr; nr++, l++) {
path_put(&l->path);
kfree(l->name);
l->name = NULL;
} }
ctx->nr = 0; ctx->nr = 0;
ctx->nr_data = 0; ctx->nr_data = 0;
...@@ -346,7 +419,7 @@ static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx) ...@@ -346,7 +419,7 @@ static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx)
/* /*
* Parse lowerdir= mount option: * Parse lowerdir= mount option:
* *
* (1) lowerdir=/lower1:/lower2:/lower3::/data1::/data2 * e.g.: lowerdir=/lower1:/lower2:/lower3::/data1::/data2
* Set "/lower1", "/lower2", and "/lower3" as lower layers and * Set "/lower1", "/lower2", and "/lower3" as lower layers and
* "/data1" and "/data2" as data lower layers. Any existing lower * "/data1" and "/data2" as data lower layers. Any existing lower
* layers are replaced. * layers are replaced.
...@@ -356,9 +429,9 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -356,9 +429,9 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
int err; int err;
struct ovl_fs_context *ctx = fc->fs_private; struct ovl_fs_context *ctx = fc->fs_private;
struct ovl_fs_context_layer *l; struct ovl_fs_context_layer *l;
char *dup = NULL, *dup_iter; char *dup = NULL, *iter;
ssize_t nr_lower = 0, nr = 0, nr_data = 0; ssize_t nr_lower = 0, nr = 0, nr_data = 0;
bool append = false, data_layer = false; bool data_layer = false;
/* /*
* Ensure we're backwards compatible with mount(2) * Ensure we're backwards compatible with mount(2)
...@@ -366,16 +439,21 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -366,16 +439,21 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
*/ */
/* drop all existing lower layers */ /* drop all existing lower layers */
if (!*name) { ovl_reset_lowerdirs(ctx);
ovl_parse_param_drop_lowerdir(ctx);
if (!*name)
return 0; return 0;
}
if (*name == ':') { if (*name == ':') {
pr_err("cannot append lower layer"); pr_err("cannot append lower layer");
return -EINVAL; return -EINVAL;
} }
// Store user provided lowerdir string to show in mount options
ctx->lowerdir_all = kstrdup(name, GFP_KERNEL);
if (!ctx->lowerdir_all)
return -ENOMEM;
dup = kstrdup(name, GFP_KERNEL); dup = kstrdup(name, GFP_KERNEL);
if (!dup) if (!dup)
return -ENOMEM; return -ENOMEM;
...@@ -385,36 +463,11 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -385,36 +463,11 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
if (nr_lower < 0) if (nr_lower < 0)
goto out_err; goto out_err;
if ((nr_lower > OVL_MAX_STACK) || if (nr_lower > OVL_MAX_STACK) {
(append && (size_add(ctx->nr, nr_lower) > OVL_MAX_STACK))) {
pr_err("too many lower directories, limit is %d\n", OVL_MAX_STACK); pr_err("too many lower directories, limit is %d\n", OVL_MAX_STACK);
goto out_err; goto out_err;
} }
if (!append)
ovl_parse_param_drop_lowerdir(ctx);
/*
* (1) append
*
* We want nr <= nr_lower <= capacity We know nr > 0 and nr <=
* capacity. If nr == 0 this wouldn't be append. If nr +
* nr_lower is <= capacity then nr <= nr_lower <= capacity
* already holds. If nr + nr_lower exceeds capacity, we realloc.
*
* (2) replace
*
* Ensure we're backwards compatible with mount(2) which allows
* "lowerdir=/a:/b:/c,lowerdir=/d:/e:/f" causing the last
* specified lowerdir mount option to win.
*
* We want nr <= nr_lower <= capacity We know either (i) nr == 0
* or (ii) nr > 0. We also know nr_lower > 0. The capacity
* could've been changed multiple times already so we only know
* nr <= capacity. If nr + nr_lower > capacity we realloc,
* otherwise nr <= nr_lower <= capacity holds already.
*/
nr_lower += ctx->nr;
if (nr_lower > ctx->capacity) { if (nr_lower > ctx->capacity) {
err = -ENOMEM; err = -ENOMEM;
l = krealloc_array(ctx->lower, nr_lower, sizeof(*ctx->lower), l = krealloc_array(ctx->lower, nr_lower, sizeof(*ctx->lower),
...@@ -426,41 +479,21 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -426,41 +479,21 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
ctx->capacity = nr_lower; ctx->capacity = nr_lower;
} }
/* iter = dup;
* (3) By (1) and (2) we know nr <= nr_lower <= capacity. l = ctx->lower;
* (4) If ctx->nr == 0 => replace for (nr = 0; nr < nr_lower; nr++, l++) {
* We have verified above that the lowerdir mount option
* isn't an append, i.e., the lowerdir mount option
* doesn't start with ":" or "::".
* (4.1) The lowerdir mount options only contains regular lower
* layers ":".
* => Nothing to verify.
* (4.2) The lowerdir mount options contains regular ":" and
* data "::" layers.
* => We need to verify that data lower layers "::" aren't
* followed by regular ":" lower layers
* (5) If ctx->nr > 0 => append
* We know that there's at least one regular layer
* otherwise we would've failed when parsing the previous
* lowerdir mount option.
* (5.1) The lowerdir mount option is a regular layer ":" append
* => We need to verify that no data layers have been
* specified before.
* (5.2) The lowerdir mount option is a data layer "::" append
* We know that there's at least one regular layer or
* other data layers. => There's nothing to verify.
*/
dup_iter = dup;
for (nr = ctx->nr; nr < nr_lower; nr++) {
l = &ctx->lower[nr];
memset(l, 0, sizeof(*l)); memset(l, 0, sizeof(*l));
err = ovl_mount_dir(dup_iter, &l->path, false); err = ovl_mount_dir(iter, &l->path);
if (err)
goto out_put;
err = ovl_mount_dir_check(fc, &l->path, Opt_lowerdir, iter, false);
if (err) if (err)
goto out_put; goto out_put;
err = -ENOMEM; err = -ENOMEM;
l->name = kstrdup(dup_iter, GFP_KERNEL_ACCOUNT); l->name = kstrdup(iter, GFP_KERNEL_ACCOUNT);
if (!l->name) if (!l->name)
goto out_put; goto out_put;
...@@ -472,8 +505,8 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -472,8 +505,8 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
break; break;
err = -EINVAL; err = -EINVAL;
dup_iter = strchr(dup_iter, '\0') + 1; iter = strchr(iter, '\0') + 1;
if (*dup_iter) { if (*iter) {
/* /*
* This is a regular layer so we require that * This is a regular layer so we require that
* there are no data layers. * there are no data layers.
...@@ -489,7 +522,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -489,7 +522,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
/* This is a data lower layer. */ /* This is a data lower layer. */
data_layer = true; data_layer = true;
dup_iter++; iter++;
} }
ctx->nr = nr_lower; ctx->nr = nr_lower;
ctx->nr_data += nr_data; ctx->nr_data += nr_data;
...@@ -497,21 +530,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -497,21 +530,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
return 0; return 0;
out_put: out_put:
/* ovl_reset_lowerdirs(ctx);
* We know nr >= ctx->nr < nr_lower. If we failed somewhere
* we want to undo until nr == ctx->nr. This is correct for
* both ctx->nr == 0 and ctx->nr > 0.
*/
for (; nr >= ctx->nr; nr--) {
l = &ctx->lower[nr];
kfree(l->name);
l->name = NULL;
path_put(&l->path);
/* don't overflow */
if (nr == 0)
break;
}
out_err: out_err:
kfree(dup); kfree(dup);
...@@ -556,11 +575,11 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -556,11 +575,11 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
case Opt_lowerdir: case Opt_lowerdir:
err = ovl_parse_param_lowerdir(param->string, fc); err = ovl_parse_param_lowerdir(param->string, fc);
break; break;
case Opt_lowerdir_add:
case Opt_datadir_add:
case Opt_upperdir: case Opt_upperdir:
fallthrough;
case Opt_workdir: case Opt_workdir:
err = ovl_parse_param_upperdir(param->string, fc, err = ovl_parse_layer(fc, param, opt);
(Opt_workdir == opt));
break; break;
case Opt_default_permissions: case Opt_default_permissions:
config->default_permissions = true; config->default_permissions = true;
...@@ -617,7 +636,7 @@ static int ovl_get_tree(struct fs_context *fc) ...@@ -617,7 +636,7 @@ static int ovl_get_tree(struct fs_context *fc)
static inline void ovl_fs_context_free(struct ovl_fs_context *ctx) static inline void ovl_fs_context_free(struct ovl_fs_context *ctx)
{ {
ovl_parse_param_drop_lowerdir(ctx); ovl_reset_lowerdirs(ctx);
path_put(&ctx->upper); path_put(&ctx->upper);
path_put(&ctx->work); path_put(&ctx->work);
kfree(ctx->lower); kfree(ctx->lower);
...@@ -933,23 +952,28 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -933,23 +952,28 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
{ {
struct super_block *sb = dentry->d_sb; struct super_block *sb = dentry->d_sb;
struct ovl_fs *ofs = OVL_FS(sb); struct ovl_fs *ofs = OVL_FS(sb);
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer; size_t nr, nr_merged_lower, nr_lower = 0;
char **lowerdirs = ofs->config.lowerdirs;
/* /*
* lowerdirs[] starts from offset 1, then * lowerdirs[0] holds the colon separated list that user provided
* >= 0 regular lower layers prefixed with : and * with lowerdir mount option.
* >= 0 data-only lower layers prefixed with :: * lowerdirs[1..numlayer] hold the lowerdir paths that were added
* * using the lowerdir+ and datadir+ mount options.
* we need to escase comma and space like seq_show_option() does and * For now, we do not allow mixing the legacy lowerdir mount option
* we also need to escape the colon separator from lowerdir paths. * with the new lowerdir+ and datadir+ mount options.
*/ */
seq_puts(m, ",lowerdir="); if (lowerdirs[0]) {
for (nr = 1; nr < ofs->numlayer; nr++) { seq_show_option(m, "lowerdir", lowerdirs[0]);
if (nr > 1) } else {
seq_putc(m, ':'); nr_lower = ofs->numlayer;
if (nr >= nr_merged_lower) nr_merged_lower = nr_lower - ofs->numdatalayer;
seq_putc(m, ':'); }
seq_escape(m, ofs->config.lowerdirs[nr], ":, \t\n\\"); for (nr = 1; nr < nr_lower; nr++) {
if (nr < nr_merged_lower)
seq_show_option(m, "lowerdir+", lowerdirs[nr]);
else
seq_show_option(m, "datadir+", lowerdirs[nr]);
} }
if (ofs->config.upperdir) { if (ofs->config.upperdir) {
seq_show_option(m, "upperdir", ofs->config.upperdir); seq_show_option(m, "upperdir", ofs->config.upperdir);
......
...@@ -32,6 +32,7 @@ struct ovl_fs_context { ...@@ -32,6 +32,7 @@ struct ovl_fs_context {
size_t nr_data; size_t nr_data;
struct ovl_opt_set set; struct ovl_opt_set set;
struct ovl_fs_context_layer *lower; struct ovl_fs_context_layer *lower;
char *lowerdir_all; /* user provided lowerdir string */
}; };
int ovl_init_fs_context(struct fs_context *fc); int ovl_init_fs_context(struct fs_context *fc);
......
...@@ -25,6 +25,7 @@ struct ovl_cache_entry { ...@@ -25,6 +25,7 @@ struct ovl_cache_entry {
struct ovl_cache_entry *next_maybe_whiteout; struct ovl_cache_entry *next_maybe_whiteout;
bool is_upper; bool is_upper;
bool is_whiteout; bool is_whiteout;
bool check_xwhiteout;
char name[]; char name[];
}; };
...@@ -47,6 +48,7 @@ struct ovl_readdir_data { ...@@ -47,6 +48,7 @@ struct ovl_readdir_data {
int err; int err;
bool is_upper; bool is_upper;
bool d_type_supported; bool d_type_supported;
bool in_xwhiteouts_dir;
}; };
struct ovl_dir_file { struct ovl_dir_file {
...@@ -162,6 +164,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd, ...@@ -162,6 +164,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
p->ino = 0; p->ino = 0;
p->is_upper = rdd->is_upper; p->is_upper = rdd->is_upper;
p->is_whiteout = false; p->is_whiteout = false;
/* Defer check for overlay.whiteout to ovl_iterate() */
p->check_xwhiteout = rdd->in_xwhiteouts_dir && d_type == DT_REG;
if (d_type == DT_CHR) { if (d_type == DT_CHR) {
p->next_maybe_whiteout = rdd->first_maybe_whiteout; p->next_maybe_whiteout = rdd->first_maybe_whiteout;
...@@ -301,6 +305,8 @@ static inline int ovl_dir_read(const struct path *realpath, ...@@ -301,6 +305,8 @@ static inline int ovl_dir_read(const struct path *realpath,
if (IS_ERR(realfile)) if (IS_ERR(realfile))
return PTR_ERR(realfile); return PTR_ERR(realfile);
rdd->in_xwhiteouts_dir = rdd->dentry &&
ovl_path_check_xwhiteouts_xattr(OVL_FS(rdd->dentry->d_sb), realpath);
rdd->first_maybe_whiteout = NULL; rdd->first_maybe_whiteout = NULL;
rdd->ctx.pos = 0; rdd->ctx.pos = 0;
do { do {
...@@ -447,7 +453,7 @@ static u64 ovl_remap_lower_ino(u64 ino, int xinobits, int fsid, ...@@ -447,7 +453,7 @@ static u64 ovl_remap_lower_ino(u64 ino, int xinobits, int fsid,
} }
/* /*
* Set d_ino for upper entries. Non-upper entries should always report * Set d_ino for upper entries if needed. Non-upper entries should always report
* the uppermost real inode ino and should not call this function. * the uppermost real inode ino and should not call this function.
* *
* When not all layer are on same fs, report real ino also for upper. * When not all layer are on same fs, report real ino also for upper.
...@@ -455,8 +461,11 @@ static u64 ovl_remap_lower_ino(u64 ino, int xinobits, int fsid, ...@@ -455,8 +461,11 @@ static u64 ovl_remap_lower_ino(u64 ino, int xinobits, int fsid,
* When all layers are on the same fs, and upper has a reference to * When all layers are on the same fs, and upper has a reference to
* copy up origin, call vfs_getattr() on the overlay entry to make * copy up origin, call vfs_getattr() on the overlay entry to make
* sure that d_ino will be consistent with st_ino from stat(2). * sure that d_ino will be consistent with st_ino from stat(2).
*
* Also checks the overlay.whiteout xattr by doing a full lookup which will return
* negative in this case.
*/ */
static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry *p) static int ovl_cache_update(const struct path *path, struct ovl_cache_entry *p, bool update_ino)
{ {
struct dentry *dir = path->dentry; struct dentry *dir = path->dentry;
...@@ -467,7 +476,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry ...@@ -467,7 +476,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
int xinobits = ovl_xino_bits(ofs); int xinobits = ovl_xino_bits(ofs);
int err = 0; int err = 0;
if (!ovl_same_dev(ofs)) if (!ovl_same_dev(ofs) && !p->check_xwhiteout)
goto out; goto out;
if (p->name[0] == '.') { if (p->name[0] == '.') {
...@@ -481,6 +490,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry ...@@ -481,6 +490,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
goto get; goto get;
} }
} }
/* This checks also for xwhiteouts */
this = lookup_one(mnt_idmap(path->mnt), p->name, dir, p->len); this = lookup_one(mnt_idmap(path->mnt), p->name, dir, p->len);
if (IS_ERR_OR_NULL(this) || !this->d_inode) { if (IS_ERR_OR_NULL(this) || !this->d_inode) {
/* Mark a stale entry */ /* Mark a stale entry */
...@@ -494,6 +504,9 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry ...@@ -494,6 +504,9 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
} }
get: get:
if (!ovl_same_dev(ofs) || !update_ino)
goto out;
type = ovl_path_type(this); type = ovl_path_type(this);
if (OVL_TYPE_ORIGIN(type)) { if (OVL_TYPE_ORIGIN(type)) {
struct kstat stat; struct kstat stat;
...@@ -572,7 +585,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list, ...@@ -572,7 +585,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
list_for_each_entry_safe(p, n, list, l_node) { list_for_each_entry_safe(p, n, list, l_node) {
if (strcmp(p->name, ".") != 0 && if (strcmp(p->name, ".") != 0 &&
strcmp(p->name, "..") != 0) { strcmp(p->name, "..") != 0) {
err = ovl_cache_update_ino(path, p); err = ovl_cache_update(path, p, true);
if (err) if (err)
return err; return err;
} }
...@@ -778,13 +791,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -778,13 +791,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
while (od->cursor != &od->cache->entries) { while (od->cursor != &od->cache->entries) {
p = list_entry(od->cursor, struct ovl_cache_entry, l_node); p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
if (!p->is_whiteout) { if (!p->is_whiteout) {
if (!p->ino) { if (!p->ino || p->check_xwhiteout) {
err = ovl_cache_update_ino(&file->f_path, p); err = ovl_cache_update(&file->f_path, p, !p->ino);
if (err) if (err)
goto out; goto out;
} }
} }
/* ovl_cache_update_ino() sets is_whiteout on stale entry */ /* ovl_cache_update() sets is_whiteout on stale entry */
if (!p->is_whiteout) { if (!p->is_whiteout) {
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type)) if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
break; break;
......
...@@ -445,68 +445,6 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir) ...@@ -445,68 +445,6 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
return ok; return ok;
} }
static int ovl_own_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
return -EOPNOTSUPP;
}
static int ovl_own_xattr_set(const struct xattr_handler *handler,
struct mnt_idmap *idmap,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value,
size_t size, int flags)
{
return -EOPNOTSUPP;
}
static int ovl_other_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
return ovl_xattr_get(dentry, inode, name, buffer, size);
}
static int ovl_other_xattr_set(const struct xattr_handler *handler,
struct mnt_idmap *idmap,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value,
size_t size, int flags)
{
return ovl_xattr_set(dentry, inode, name, value, size, flags);
}
static const struct xattr_handler ovl_own_trusted_xattr_handler = {
.prefix = OVL_XATTR_TRUSTED_PREFIX,
.get = ovl_own_xattr_get,
.set = ovl_own_xattr_set,
};
static const struct xattr_handler ovl_own_user_xattr_handler = {
.prefix = OVL_XATTR_USER_PREFIX,
.get = ovl_own_xattr_get,
.set = ovl_own_xattr_set,
};
static const struct xattr_handler ovl_other_xattr_handler = {
.prefix = "", /* catch all */
.get = ovl_other_xattr_get,
.set = ovl_other_xattr_set,
};
static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
&ovl_own_trusted_xattr_handler,
&ovl_other_xattr_handler,
NULL
};
static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
&ovl_own_user_xattr_handler,
&ovl_other_xattr_handler,
NULL
};
static int ovl_setup_trap(struct super_block *sb, struct dentry *dir, static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
struct inode **ptrap, const char *name) struct inode **ptrap, const char *name)
{ {
...@@ -647,7 +585,7 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs) ...@@ -647,7 +585,7 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
if (IS_ERR(whiteout)) if (IS_ERR(whiteout))
goto cleanup_temp; goto cleanup_temp;
err = ovl_is_whiteout(whiteout); err = ovl_upper_is_whiteout(ofs, whiteout);
/* Best effort cleanup of whiteout and temp file */ /* Best effort cleanup of whiteout and temp file */
if (err) if (err)
...@@ -887,15 +825,20 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -887,15 +825,20 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
{ {
struct vfsmount *mnt = ovl_upper_mnt(ofs); struct vfsmount *mnt = ovl_upper_mnt(ofs);
struct dentry *indexdir; struct dentry *indexdir;
struct dentry *origin = ovl_lowerstack(oe)->dentry;
const struct ovl_fh *fh;
int err; int err;
fh = ovl_get_origin_fh(ofs, origin);
if (IS_ERR(fh))
return PTR_ERR(fh);
err = mnt_want_write(mnt); err = mnt_want_write(mnt);
if (err) if (err)
return err; goto out_free_fh;
/* Verify lower root is upper root origin */ /* Verify lower root is upper root origin */
err = ovl_verify_origin(ofs, upperpath->dentry, err = ovl_verify_origin_fh(ofs, upperpath->dentry, fh, true);
ovl_lowerstack(oe)->dentry, true);
if (err) { if (err) {
pr_err("failed to verify upper root origin\n"); pr_err("failed to verify upper root origin\n");
goto out; goto out;
...@@ -927,9 +870,10 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -927,9 +870,10 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
* directory entries. * directory entries.
*/ */
if (ovl_check_origin_xattr(ofs, ofs->indexdir)) { if (ovl_check_origin_xattr(ofs, ofs->indexdir)) {
err = ovl_verify_set_fh(ofs, ofs->indexdir, err = ovl_verify_origin_xattr(ofs, ofs->indexdir,
OVL_XATTR_ORIGIN, OVL_XATTR_ORIGIN,
upperpath->dentry, true, false); upperpath->dentry, true,
false);
if (err) if (err)
pr_err("failed to verify index dir 'origin' xattr\n"); pr_err("failed to verify index dir 'origin' xattr\n");
} }
...@@ -947,6 +891,8 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -947,6 +891,8 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
out: out:
mnt_drop_write(mnt); mnt_drop_write(mnt);
out_free_fh:
kfree(fh);
return err; return err;
} }
...@@ -1382,8 +1328,11 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) ...@@ -1382,8 +1328,11 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
ofs->layers = layers; ofs->layers = layers;
/* /*
* Layer 0 is reserved for upper even if there's no upper. * Layer 0 is reserved for upper even if there's no upper.
* For consistency, config.lowerdirs[0] is NULL. * config.lowerdirs[0] is used for storing the user provided colon
* separated lowerdir string.
*/ */
ofs->config.lowerdirs[0] = ctx->lowerdir_all;
ctx->lowerdir_all = NULL;
ofs->numlayer = 1; ofs->numlayer = 1;
sb->s_stack_depth = 0; sb->s_stack_depth = 0;
...@@ -1493,8 +1442,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) ...@@ -1493,8 +1442,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_magic = OVERLAYFS_SUPER_MAGIC;
sb->s_xattr = ofs->config.userxattr ? ovl_user_xattr_handlers : sb->s_xattr = ovl_xattr_handlers(ofs);
ovl_trusted_xattr_handlers;
sb->s_fs_info = ofs; sb->s_fs_info = ofs;
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
sb->s_flags |= SB_POSIXACL; sb->s_flags |= SB_POSIXACL;
......
...@@ -17,12 +17,38 @@ ...@@ -17,12 +17,38 @@
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include "overlayfs.h" #include "overlayfs.h"
/* Get write access to upper mnt - may fail if upper sb was remounted ro */
int ovl_get_write_access(struct dentry *dentry)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
return mnt_get_write_access(ovl_upper_mnt(ofs));
}
/* Get write access to upper sb - may block if upper sb is frozen */
void ovl_start_write(struct dentry *dentry)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
sb_start_write(ovl_upper_mnt(ofs)->mnt_sb);
}
int ovl_want_write(struct dentry *dentry) int ovl_want_write(struct dentry *dentry)
{ {
struct ovl_fs *ofs = OVL_FS(dentry->d_sb); struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
return mnt_want_write(ovl_upper_mnt(ofs)); return mnt_want_write(ovl_upper_mnt(ofs));
} }
void ovl_put_write_access(struct dentry *dentry)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
mnt_put_write_access(ovl_upper_mnt(ofs));
}
void ovl_end_write(struct dentry *dentry)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
sb_end_write(ovl_upper_mnt(ofs)->mnt_sb);
}
void ovl_drop_write(struct dentry *dentry) void ovl_drop_write(struct dentry *dentry)
{ {
struct ovl_fs *ofs = OVL_FS(dentry->d_sb); struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
...@@ -575,6 +601,16 @@ bool ovl_is_whiteout(struct dentry *dentry) ...@@ -575,6 +601,16 @@ bool ovl_is_whiteout(struct dentry *dentry)
return inode && IS_WHITEOUT(inode); return inode && IS_WHITEOUT(inode);
} }
/*
* Use this over ovl_is_whiteout for upper and lower files, as it also
* handles overlay.whiteout xattr whiteout files.
*/
bool ovl_path_is_whiteout(struct ovl_fs *ofs, const struct path *path)
{
return ovl_is_whiteout(path->dentry) ||
ovl_path_check_xwhiteout_xattr(ofs, path);
}
struct file *ovl_path_open(const struct path *path, int flags) struct file *ovl_path_open(const struct path *path, int flags)
{ {
struct inode *inode = d_inode(path->dentry); struct inode *inode = d_inode(path->dentry);
...@@ -644,22 +680,36 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags) ...@@ -644,22 +680,36 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags)
return false; return false;
} }
/*
* The copy up "transaction" keeps an elevated mnt write count on upper mnt,
* but leaves taking freeze protection on upper sb to lower level helpers.
*/
int ovl_copy_up_start(struct dentry *dentry, int flags) int ovl_copy_up_start(struct dentry *dentry, int flags)
{ {
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
int err; int err;
err = ovl_inode_lock_interruptible(inode); err = ovl_inode_lock_interruptible(inode);
if (!err && ovl_already_copied_up_locked(dentry, flags)) { if (err)
return err;
if (ovl_already_copied_up_locked(dentry, flags))
err = 1; /* Already copied up */ err = 1; /* Already copied up */
ovl_inode_unlock(inode); else
} err = ovl_get_write_access(dentry);
if (err)
goto out_unlock;
return 0;
out_unlock:
ovl_inode_unlock(inode);
return err; return err;
} }
void ovl_copy_up_end(struct dentry *dentry) void ovl_copy_up_end(struct dentry *dentry)
{ {
ovl_put_write_access(dentry);
ovl_inode_unlock(d_inode(dentry)); ovl_inode_unlock(d_inode(dentry));
} }
...@@ -676,6 +726,32 @@ bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path) ...@@ -676,6 +726,32 @@ bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path)
return false; return false;
} }
bool ovl_path_check_xwhiteout_xattr(struct ovl_fs *ofs, const struct path *path)
{
struct dentry *dentry = path->dentry;
int res;
/* xattr.whiteout must be a zero size regular file */
if (!d_is_reg(dentry) || i_size_read(d_inode(dentry)) != 0)
return false;
res = ovl_path_getxattr(ofs, path, OVL_XATTR_XWHITEOUT, NULL, 0);
return res >= 0;
}
bool ovl_path_check_xwhiteouts_xattr(struct ovl_fs *ofs, const struct path *path)
{
struct dentry *dentry = path->dentry;
int res;
/* xattr.whiteouts must be a directory */
if (!d_is_dir(dentry))
return false;
res = ovl_path_getxattr(ofs, path, OVL_XATTR_XWHITEOUTS, NULL, 0);
return res >= 0;
}
/* /*
* Load persistent uuid from xattr into s_uuid if found, or store a new * Load persistent uuid from xattr into s_uuid if found, or store a new
* random generated value in s_uuid and in xattr. * random generated value in s_uuid and in xattr.
...@@ -760,6 +836,8 @@ bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path, ...@@ -760,6 +836,8 @@ bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
#define OVL_XATTR_UUID_POSTFIX "uuid" #define OVL_XATTR_UUID_POSTFIX "uuid"
#define OVL_XATTR_METACOPY_POSTFIX "metacopy" #define OVL_XATTR_METACOPY_POSTFIX "metacopy"
#define OVL_XATTR_PROTATTR_POSTFIX "protattr" #define OVL_XATTR_PROTATTR_POSTFIX "protattr"
#define OVL_XATTR_XWHITEOUT_POSTFIX "whiteout"
#define OVL_XATTR_XWHITEOUTS_POSTFIX "whiteouts"
#define OVL_XATTR_TAB_ENTRY(x) \ #define OVL_XATTR_TAB_ENTRY(x) \
[x] = { [false] = OVL_XATTR_TRUSTED_PREFIX x ## _POSTFIX, \ [x] = { [false] = OVL_XATTR_TRUSTED_PREFIX x ## _POSTFIX, \
...@@ -775,6 +853,8 @@ const char *const ovl_xattr_table[][2] = { ...@@ -775,6 +853,8 @@ const char *const ovl_xattr_table[][2] = {
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UUID), OVL_XATTR_TAB_ENTRY(OVL_XATTR_UUID),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY), OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR), OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_XWHITEOUT),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_XWHITEOUTS),
}; };
int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry, int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
...@@ -973,12 +1053,18 @@ static void ovl_cleanup_index(struct dentry *dentry) ...@@ -973,12 +1053,18 @@ static void ovl_cleanup_index(struct dentry *dentry)
struct dentry *index = NULL; struct dentry *index = NULL;
struct inode *inode; struct inode *inode;
struct qstr name = { }; struct qstr name = { };
bool got_write = false;
int err; int err;
err = ovl_get_index_name(ofs, lowerdentry, &name); err = ovl_get_index_name(ofs, lowerdentry, &name);
if (err) if (err)
goto fail; goto fail;
err = ovl_want_write(dentry);
if (err)
goto fail;
got_write = true;
inode = d_inode(upperdentry); inode = d_inode(upperdentry);
if (!S_ISDIR(inode->i_mode) && inode->i_nlink != 1) { if (!S_ISDIR(inode->i_mode) && inode->i_nlink != 1) {
pr_warn_ratelimited("cleanup linked index (%pd2, ino=%lu, nlink=%u)\n", pr_warn_ratelimited("cleanup linked index (%pd2, ino=%lu, nlink=%u)\n",
...@@ -1016,6 +1102,8 @@ static void ovl_cleanup_index(struct dentry *dentry) ...@@ -1016,6 +1102,8 @@ static void ovl_cleanup_index(struct dentry *dentry)
goto fail; goto fail;
out: out:
if (got_write)
ovl_drop_write(dentry);
kfree(name.name); kfree(name.name);
dput(index); dput(index);
return; return;
...@@ -1062,8 +1150,12 @@ int ovl_nlink_start(struct dentry *dentry) ...@@ -1062,8 +1150,12 @@ int ovl_nlink_start(struct dentry *dentry)
if (err) if (err)
return err; return err;
err = ovl_want_write(dentry);
if (err)
goto out_unlock;
if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode)) if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode))
goto out; return 0;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
/* /*
...@@ -1074,9 +1166,14 @@ int ovl_nlink_start(struct dentry *dentry) ...@@ -1074,9 +1166,14 @@ int ovl_nlink_start(struct dentry *dentry)
*/ */
err = ovl_set_nlink_upper(dentry); err = ovl_set_nlink_upper(dentry);
revert_creds(old_cred); revert_creds(old_cred);
out:
if (err) if (err)
goto out_drop_write;
return 0;
out_drop_write:
ovl_drop_write(dentry);
out_unlock:
ovl_inode_unlock(inode); ovl_inode_unlock(inode);
return err; return err;
...@@ -1086,6 +1183,8 @@ void ovl_nlink_end(struct dentry *dentry) ...@@ -1086,6 +1183,8 @@ void ovl_nlink_end(struct dentry *dentry)
{ {
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
ovl_drop_write(dentry);
if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) { if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) {
const struct cred *old_cred; const struct cred *old_cred;
...@@ -1403,6 +1502,7 @@ void ovl_copyattr(struct inode *inode) ...@@ -1403,6 +1502,7 @@ void ovl_copyattr(struct inode *inode)
realinode = ovl_i_path_real(inode, &realpath); realinode = ovl_i_path_real(inode, &realpath);
real_idmap = mnt_idmap(realpath.mnt); real_idmap = mnt_idmap(realpath.mnt);
spin_lock(&inode->i_lock);
vfsuid = i_uid_into_vfsuid(real_idmap, realinode); vfsuid = i_uid_into_vfsuid(real_idmap, realinode);
vfsgid = i_gid_into_vfsgid(real_idmap, realinode); vfsgid = i_gid_into_vfsgid(real_idmap, realinode);
...@@ -1413,4 +1513,5 @@ void ovl_copyattr(struct inode *inode) ...@@ -1413,4 +1513,5 @@ void ovl_copyattr(struct inode *inode)
inode_set_mtime_to_ts(inode, inode_get_mtime(realinode)); inode_set_mtime_to_ts(inode, inode_get_mtime(realinode));
inode_set_ctime_to_ts(inode, inode_get_ctime(realinode)); inode_set_ctime_to_ts(inode, inode_get_ctime(realinode));
i_size_write(inode, i_size_read(realinode)); i_size_write(inode, i_size_read(realinode));
spin_unlock(&inode->i_lock);
} }
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/fs.h>
#include <linux/xattr.h>
#include "overlayfs.h"
static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
{
struct ovl_fs *ofs = sb->s_fs_info;
if (ofs->config.userxattr)
return strncmp(name, OVL_XATTR_ESCAPE_USER_PREFIX,
OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
else
return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
}
static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
{
struct ovl_fs *ofs = OVL_FS(sb);
if (ofs->config.userxattr)
return strncmp(name, OVL_XATTR_USER_PREFIX,
OVL_XATTR_USER_PREFIX_LEN) == 0;
else
return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
OVL_XATTR_TRUSTED_PREFIX_LEN) == 0;
}
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
{
return ovl_is_own_xattr(sb, name) && !ovl_is_escaped_xattr(sb, name);
}
static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
int err;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdentry = ovl_i_dentry_upper(inode);
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
struct path realpath;
const struct cred *old_cred;
if (!value && !upperdentry) {
ovl_path_lower(dentry, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
revert_creds(old_cred);
if (err < 0)
goto out;
}
if (!upperdentry) {
err = ovl_copy_up(dentry);
if (err)
goto out;
realdentry = ovl_dentry_upper(dentry);
}
err = ovl_want_write(dentry);
if (err)
goto out;
old_cred = ovl_override_creds(dentry->d_sb);
if (value) {
err = ovl_do_setxattr(ofs, realdentry, name, value, size,
flags);
} else {
WARN_ON(flags != XATTR_REPLACE);
err = ovl_do_removexattr(ofs, realdentry, name);
}
revert_creds(old_cred);
ovl_drop_write(dentry);
/* copy c/mtime */
ovl_copyattr(inode);
out:
return err;
}
static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
void *value, size_t size)
{
ssize_t res;
const struct cred *old_cred;
struct path realpath;
ovl_i_path_real(inode, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
revert_creds(old_cred);
return res;
}
static bool ovl_can_list(struct super_block *sb, const char *s)
{
/* Never list private (.overlay) */
if (ovl_is_private_xattr(sb, s))
return false;
/* List all non-trusted xattrs */
if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
return true;
/* list other trusted for superuser only */
return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
}
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
struct dentry *realdentry = ovl_dentry_real(dentry);
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
ssize_t res;
size_t len;
char *s;
const struct cred *old_cred;
size_t prefix_len, name_len;
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_listxattr(realdentry, list, size);
revert_creds(old_cred);
if (res <= 0 || size == 0)
return res;
prefix_len = ofs->config.userxattr ?
OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN;
/* filter out private xattrs */
for (s = list, len = res; len;) {
size_t slen = strnlen(s, len) + 1;
/* underlying fs providing us with an broken xattr list? */
if (WARN_ON(slen > len))
return -EIO;
len -= slen;
if (!ovl_can_list(dentry->d_sb, s)) {
res -= slen;
memmove(s, s + slen, len);
} else if (ovl_is_escaped_xattr(dentry->d_sb, s)) {
res -= OVL_XATTR_ESCAPE_PREFIX_LEN;
name_len = slen - prefix_len - OVL_XATTR_ESCAPE_PREFIX_LEN;
s += prefix_len;
memmove(s, s + OVL_XATTR_ESCAPE_PREFIX_LEN, name_len + len);
s += name_len;
} else {
s += slen;
}
}
return res;
}
static char *ovl_xattr_escape_name(const char *prefix, const char *name)
{
size_t prefix_len = strlen(prefix);
size_t name_len = strlen(name);
size_t escaped_len;
char *escaped, *s;
escaped_len = prefix_len + OVL_XATTR_ESCAPE_PREFIX_LEN + name_len;
if (escaped_len > XATTR_NAME_MAX)
return ERR_PTR(-EOPNOTSUPP);
escaped = kmalloc(escaped_len + 1, GFP_KERNEL);
if (escaped == NULL)
return ERR_PTR(-ENOMEM);
s = escaped;
memcpy(s, prefix, prefix_len);
s += prefix_len;
memcpy(s, OVL_XATTR_ESCAPE_PREFIX, OVL_XATTR_ESCAPE_PREFIX_LEN);
s += OVL_XATTR_ESCAPE_PREFIX_LEN;
memcpy(s, name, name_len + 1);
return escaped;
}
static int ovl_own_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
char *escaped;
int r;
escaped = ovl_xattr_escape_name(handler->prefix, name);
if (IS_ERR(escaped))
return PTR_ERR(escaped);
r = ovl_xattr_get(dentry, inode, escaped, buffer, size);
kfree(escaped);
return r;
}
static int ovl_own_xattr_set(const struct xattr_handler *handler,
struct mnt_idmap *idmap,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value,
size_t size, int flags)
{
char *escaped;
int r;
escaped = ovl_xattr_escape_name(handler->prefix, name);
if (IS_ERR(escaped))
return PTR_ERR(escaped);
r = ovl_xattr_set(dentry, inode, escaped, value, size, flags);
kfree(escaped);
return r;
}
static int ovl_other_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
return ovl_xattr_get(dentry, inode, name, buffer, size);
}
static int ovl_other_xattr_set(const struct xattr_handler *handler,
struct mnt_idmap *idmap,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value,
size_t size, int flags)
{
return ovl_xattr_set(dentry, inode, name, value, size, flags);
}
static const struct xattr_handler ovl_own_trusted_xattr_handler = {
.prefix = OVL_XATTR_TRUSTED_PREFIX,
.get = ovl_own_xattr_get,
.set = ovl_own_xattr_set,
};
static const struct xattr_handler ovl_own_user_xattr_handler = {
.prefix = OVL_XATTR_USER_PREFIX,
.get = ovl_own_xattr_get,
.set = ovl_own_xattr_set,
};
static const struct xattr_handler ovl_other_xattr_handler = {
.prefix = "", /* catch all */
.get = ovl_other_xattr_get,
.set = ovl_other_xattr_set,
};
static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
&ovl_own_trusted_xattr_handler,
&ovl_other_xattr_handler,
NULL
};
static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
&ovl_own_user_xattr_handler,
&ovl_other_xattr_handler,
NULL
};
const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs)
{
return ofs->config.userxattr ? ovl_user_xattr_handlers :
ovl_trusted_xattr_handlers;
}
...@@ -2160,3 +2160,4 @@ int sb_init_dio_done_wq(struct super_block *sb) ...@@ -2160,3 +2160,4 @@ int sb_init_dio_done_wq(struct super_block *sb)
destroy_workqueue(wq); destroy_workqueue(wq);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(sb_init_dio_done_wq);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment