overlayfs update for 6.7-rc1

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE9zuTYTs0RXF+Ke33EVvVyTe/1WoFAmVIg7wACgkQEVvVyTe/
 1WpPtw//WiQc+gwGvLEyi9YFZWTuAOO8ZGSxF7t+CU2SYy9d91s3K8dNRx2kWBh0
 ycsFIYhTyq6BrHqlg1JpI4WW8S9SW7BkXbE4f5Lm/kiiGqlJn+eCA+aR8AqPMpVx
 KemCHJQj2WwcjlHRonAYIyQOApNePwy7EPPDlk+TlVEgMRtDHW+CY1ftChqi8bVf
 /aYoFdJIbliIUWNKzBeje/Hypz1a3aUrMitquoK3A91RgIgQQmnWN6yirT4Z4gKB
 Vd9vnnFPu9LWjCv+RUuP3C0G4zHkP92sfbEIulKqml0Vx68JZyTy5jsUzxaGmXaQ
 im4neCEUV88PZoAJgGFcBHbi4y0bt5WDIf7kdZGheZJv8H/X5TQTxlXle3h0qHEp
 Rx65OjPGzZjfia1nFzNjUCd+jCtdp02H1WxchfsRpwmXqGPYBLsNPWN+BoSaLjtL
 gbEMRMAs4mAJObnhAIOZclzh0boLi4IWz0yNkBoZRxkUOAUDV7UaICQoSneWj36j
 OH75cNrRV7vZeMQy2aLWkxGm2LabhjzDZXwO9zwHZFMKIpGg7zko10Fko9qS+20p
 BHe+aqWgr0qxcY41fpKzu0ftSuNs9Jsn1/I/jqmgwFw+ogRwjuxgl2LkCHOPh3cS
 AMwd1ZahQDQKq6Wgh1WCOi31n1d8+/Gy74IojwSC++XaPMqYB0I=
 =MPCb
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2023-11-07 11:46:31 -08:00
commit 7f851936a0
16 changed files with 934 additions and 537 deletions

View File

@ -344,10 +344,11 @@ escaping the colons with a single backslash. For example:
mount -t overlay overlay -olowerdir=/a\:lower\:\:dir /merged
Since kernel version v6.5, directory names containing colons can also
be provided as lower layer using the fsconfig syscall from new mount api:
Since kernel version v6.8, directory names containing colons can also
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
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
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.
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
----------------------
@ -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
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
---------------------

View File

@ -6,4 +6,4 @@
obj-$(CONFIG_OVERLAY_FS) += overlay.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

View File

@ -252,7 +252,9 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
return PTR_ERR(old_file);
/* 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);
ovl_end_write(dentry);
if (cloned == len)
goto out_fput;
/* 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,
* it may not recognize all kind of holes and sometimes
* only skips partial of hole area. However, it will be
* 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) {
data_pos = vfs_llseek(old_file, old_pos, SEEK_DATA);
if (data_pos > old_pos) {
@ -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,
new_file, &new_pos,
this_len, SPLICE_F_MOVE);
ovl_end_write(dentry);
if (bytes <= 0) {
error = bytes;
break;
@ -426,29 +434,29 @@ out_err:
return ERR_PTR(err);
}
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
struct dentry *upper)
struct ovl_fh *ovl_get_origin_fh(struct ovl_fs *ofs, struct dentry *origin)
{
const struct ovl_fh *fh = NULL;
int err;
/*
* 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
* up and a pure upper inode.
*/
if (ovl_can_decode_fh(lower->d_sb)) {
fh = ovl_encode_real_fh(ofs, lower, false);
if (IS_ERR(fh))
return PTR_ERR(fh);
}
if (!ovl_can_decode_fh(origin->d_sb))
return NULL;
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.
*/
err = ovl_check_setxattr(ofs, upper, OVL_XATTR_ORIGIN, fh->buf,
fh ? fh->fb.len : 0, 0);
kfree(fh);
/* Ignore -EPERM from setting "user.*" on symlink/special */
return err == -EPERM ? 0 : err;
@ -476,7 +484,7 @@ static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper,
*
* 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 ovl_fs *ofs = OVL_FS(dentry->d_sb);
@ -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))))
return -EIO;
err = ovl_get_index_name(ofs, origin, &name);
err = ovl_get_index_name_fh(fh, &name);
if (err)
return err;
@ -541,6 +549,7 @@ struct ovl_copy_up_ctx {
struct dentry *destdir;
struct qstr destname;
struct dentry *workdir;
const struct ovl_fh *origin_fh;
bool origin;
bool indexed;
bool metacopy;
@ -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 inode *udir = d_inode(upperdir);
ovl_start_write(c->dentry);
/* Mark parent "impure" because it may now contain non-pure upper */
err = ovl_set_impure(c->parent, upperdir);
if (err)
return err;
goto out;
err = ovl_set_nlink_lower(c->dentry);
if (err)
return err;
goto out;
inode_lock_nested(udir, I_MUTEX_PARENT);
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)
}
inode_unlock(udir);
if (err)
return err;
goto out;
err = ovl_set_nlink_upper(c->dentry);
out:
ovl_end_write(c->dentry);
return err;
}
@ -637,7 +650,7 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
* hard link.
*/
if (c->origin) {
err = ovl_set_origin(ofs, c->lowerpath.dentry, temp);
err = ovl_set_origin_fh(ofs, c->origin_fh, temp);
if (err)
return err;
}
@ -719,21 +732,19 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
.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);
if (err)
goto unlock;
return err;
ovl_start_write(c->dentry);
inode_lock(wdir);
temp = ovl_create_temp(ofs, c->workdir, &cattr);
inode_unlock(wdir);
ovl_end_write(c->dentry);
ovl_revert_cu_creds(&cc);
err = PTR_ERR(temp);
if (IS_ERR(temp))
goto unlock;
return PTR_ERR(temp);
/*
* 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)
*/
path.dentry = temp;
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;
}
err = ovl_copy_up_metadata(c, temp);
if (err)
goto cleanup;
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)
goto cleanup;
}
@ -779,6 +803,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
ovl_set_flag(OVL_WHITEOUTS, inode);
unlock:
unlock_rename(c->workdir, c->destdir);
ovl_end_write(c->dentry);
return err;
@ -802,9 +827,10 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
if (err)
return err;
ovl_start_write(c->dentry);
tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
ovl_end_write(c->dentry);
ovl_revert_cu_creds(&cc);
if (IS_ERR(tmpfile))
return PTR_ERR(tmpfile);
@ -815,9 +841,11 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
goto out_fput;
}
ovl_start_write(c->dentry);
err = ovl_copy_up_metadata(c, temp);
if (err)
goto out_fput;
goto out;
inode_lock_nested(udir, I_MUTEX_PARENT);
@ -831,7 +859,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
inode_unlock(udir);
if (err)
goto out_fput;
goto out;
if (c->metacopy_digest)
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)
ovl_set_upperdata(d_inode(c->dentry));
ovl_inode_update(d_inode(c->dentry), dget(temp));
out:
ovl_end_write(c->dentry);
out_fput:
fput(tmpfile);
return err;
@ -861,6 +891,8 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
{
int err;
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;
/*
@ -877,25 +909,35 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
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;
}
if (to_index) {
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)
return err;
goto out_free_fh;
} else if (WARN_ON(!c->parent)) {
/* Disconnected dentry must be copied up to index dir */
return -EIO;
err = -EIO;
goto out_free_fh;
} else {
/*
* Mark parent "impure" because it may now contain non-pure
* upper
*/
ovl_start_write(c->dentry);
err = ovl_set_impure(c->parent, c->destdir);
ovl_end_write(c->dentry);
if (err)
return err;
goto out_free_fh;
}
/* 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)
if (c->indexed)
ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
ovl_start_write(c->dentry);
if (to_index) {
/* Initialize nlink for copy up of disconnected dentry */
err = ovl_set_nlink_upper(c->dentry);
@ -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_update_reval(c->dentry, ovl_dentry_upper(c->dentry));
}
ovl_end_write(c->dentry);
out:
if (to_index)
kfree(c->destname.name);
out_free_fh:
kfree(fh);
return err;
}
@ -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
* don't want that to happen for normal copy-up operation.
*/
ovl_start_write(c->dentry);
if (capability) {
err = ovl_do_setxattr(ofs, upperpath.dentry, XATTR_NAME_CAPS,
capability, cap_size, 0);
if (err)
goto out_free;
}
err = ovl_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY);
if (!err) {
err = ovl_removexattr(ofs, upperpath.dentry,
OVL_XATTR_METACOPY);
}
ovl_end_write(c->dentry);
if (err)
goto out_free;
@ -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 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)

View File

@ -477,7 +477,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
goto out_unlock;
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;
newdentry = ovl_create_temp(ofs, workdir, cattr);
@ -559,10 +559,6 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
struct cred *override_cred;
struct dentry *parent = dentry->d_parent;
err = ovl_copy_up(parent);
if (err)
return err;
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,
.link = link,
};
err = ovl_copy_up(dentry->d_parent);
if (err)
return err;
err = ovl_want_write(dentry);
if (err)
goto out;
@ -700,28 +700,24 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
int err;
struct inode *inode;
err = ovl_want_write(old);
err = ovl_copy_up(old);
if (err)
goto out;
err = ovl_copy_up(old);
if (err)
goto out_drop_write;
err = ovl_copy_up(new->d_parent);
if (err)
goto out_drop_write;
goto out;
err = ovl_nlink_start(old);
if (err)
goto out;
if (ovl_is_metacopy_dentry(old)) {
err = ovl_set_link_redirect(old);
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);
ihold(inode);
@ -731,9 +727,8 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
if (err)
iput(inode);
out_nlink_end:
ovl_nlink_end(old);
out_drop_write:
ovl_drop_write(old);
out:
return err;
}
@ -891,17 +886,13 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
goto out;
}
err = ovl_want_write(dentry);
err = ovl_copy_up(dentry->d_parent);
if (err)
goto out;
err = ovl_copy_up(dentry->d_parent);
if (err)
goto out_drop_write;
err = ovl_nlink_start(dentry);
if (err)
goto out_drop_write;
goto out;
old_cred = ovl_override_creds(dentry->d_sb);
if (!lower_positive)
@ -926,8 +917,6 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
if (ovl_dentry_upper(dentry))
ovl_copyattr(d_inode(dentry));
out_drop_write:
ovl_drop_write(dentry);
out:
ovl_cache_free(&list);
return err;
@ -1131,29 +1120,32 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
}
}
err = ovl_want_write(old);
err = ovl_copy_up(old);
if (err)
goto out;
err = ovl_copy_up(old);
if (err)
goto out_drop_write;
err = ovl_copy_up(new->d_parent);
if (err)
goto out_drop_write;
goto out;
if (!overwrite) {
err = ovl_copy_up(new);
if (err)
goto out_drop_write;
goto out;
} else if (d_inode(new)) {
err = ovl_nlink_start(new);
if (err)
goto out_drop_write;
goto out;
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);
if (!list_empty(&list)) {
@ -1219,7 +1211,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
}
} else {
if (!d_is_negative(newdentry)) {
if (!new_opaque || !ovl_is_whiteout(newdentry))
if (!new_opaque || !ovl_upper_is_whiteout(ofs, newdentry))
goto out_dput;
} else {
if (flags & RENAME_EXCHANGE)
@ -1286,8 +1278,8 @@ out_revert_creds:
revert_creds(old_cred);
if (update_nlink)
ovl_nlink_end(new);
out_drop_write:
ovl_drop_write(old);
else
ovl_drop_write(old);
out:
dput(opaquedir);
ovl_cache_free(&list);

View File

@ -23,12 +23,7 @@ static int ovl_encode_maybe_copy_up(struct dentry *dentry)
if (ovl_dentry_upper(dentry))
return 0;
err = ovl_want_write(dentry);
if (!err) {
err = ovl_copy_up(dentry);
ovl_drop_write(dentry);
}
err = ovl_copy_up(dentry);
if (err) {
pr_warn_ratelimited("failed to copy up on encode (%pd2, err=%i)\n",
dentry, err);

View File

@ -15,10 +15,15 @@
#include <linux/fs.h>
#include "overlayfs.h"
#include "../internal.h" /* for sb_init_dio_done_wq */
struct ovl_aio_req {
struct kiocb iocb;
refcount_t ref;
struct kiocb *orig_iocb;
/* used for aio completion */
struct work_struct work;
long res;
};
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)
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)
{
struct inode *inode, *upperinode;
@ -263,20 +274,12 @@ static void ovl_file_accessed(struct file *file)
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;
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;
return (__force rwf_t)(flags & OVL_IOCB_MASK);
}
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)
struct kiocb *orig_iocb = aio_req->orig_iocb;
if (iocb->ki_flags & IOCB_WRITE) {
struct inode *inode = file_inode(orig_iocb->ki_filp);
kiocb_end_write(iocb);
ovl_copyattr(inode);
ovl_file_modified(orig_iocb->ki_filp);
}
orig_iocb->ki_pos = iocb->ki_pos;
@ -313,6 +314,37 @@ static void ovl_aio_rw_complete(struct kiocb *iocb, long 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)
{
struct file *file = iocb->ki_filp;
@ -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);
if (is_sync_kiocb(iocb)) {
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(iocb->ki_flags));
rwf_t rwf = iocb_to_rw_flags(iocb->ki_flags);
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, rwf);
} else {
struct ovl_aio_req *aio_req;
@ -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);
if (is_sync_kiocb(iocb)) {
rwf_t rwf = iocb_to_rw_flags(ifl);
file_start_write(real.file);
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(ifl));
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, rwf);
file_end_write(real.file);
/* Update size */
ovl_copyattr(inode);
ovl_file_modified(file);
} else {
struct ovl_aio_req *aio_req;
ret = ovl_init_aio_done_wq(inode->i_sb);
if (ret)
goto out;
ret = -ENOMEM;
aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL);
if (!aio_req)
@ -418,7 +456,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
aio_req->orig_iocb = iocb;
kiocb_clone(&aio_req->iocb, iocb, get_file(real.file));
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);
kiocb_start_write(&aio_req->iocb);
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,
file_end_write(real.file);
/* Update size */
ovl_copyattr(inode);
ovl_file_modified(out);
revert_creds(old_cred);
fdput(real);
@ -573,7 +611,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
revert_creds(old_cred);
/* Update size */
ovl_copyattr(inode);
ovl_file_modified(file);
fdput(real);
@ -657,7 +695,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
revert_creds(old_cred);
/* Update size */
ovl_copyattr(inode_out);
ovl_file_modified(file_out);
fdput(real_in);
fdput(real_out);

View File

@ -32,10 +32,6 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (err)
return err;
err = ovl_want_write(dentry);
if (err)
goto out;
if (attr->ia_valid & ATTR_SIZE) {
/* Truncate should trigger data copy up as well */
full_copy_up = true;
@ -54,7 +50,7 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
winode = d_inode(upperdentry);
err = get_write_access(winode);
if (err)
goto out_drop_write;
goto out;
}
if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
@ -78,6 +74,10 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
*/
attr->ia_valid &= ~ATTR_OPEN;
err = ovl_want_write(dentry);
if (err)
goto out_put_write;
inode_lock(upperdentry->d_inode);
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_do_notify_change(ofs, upperdentry, attr);
@ -85,12 +85,12 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (!err)
ovl_copyattr(dentry->d_inode);
inode_unlock(upperdentry->d_inode);
ovl_drop_write(dentry);
out_put_write:
if (winode)
put_write_access(winode);
}
out_drop_write:
ovl_drop_write(dentry);
out:
return err;
}
@ -339,130 +339,6 @@ static const char *ovl_get_link(struct dentry *dentry,
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
/*
* 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,
struct dentry *upperdentry = ovl_dentry_upper(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
* the first place before copying it up.
@ -630,7 +502,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
revert_creds(old_cred);
if (IS_ERR(real_acl)) {
err = PTR_ERR(real_acl);
goto out_drop_write;
goto out;
}
posix_acl_release(real_acl);
}
@ -638,23 +510,26 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
if (!upperdentry) {
err = ovl_copy_up(dentry);
if (err)
goto out_drop_write;
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 (acl)
err = ovl_do_set_acl(ofs, realdentry, acl_name, acl);
else
err = ovl_do_remove_acl(ofs, realdentry, acl_name);
revert_creds(old_cred);
ovl_drop_write(dentry);
/* copy c/mtime */
ovl_copyattr(inode);
out_drop_write:
ovl_drop_write(dentry);
out:
return err;
}
@ -778,14 +653,14 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
unsigned int flags;
int err;
err = ovl_want_write(dentry);
if (err)
goto out;
err = ovl_copy_up(dentry);
if (!err) {
ovl_path_real(dentry, &upperpath);
err = ovl_want_write(dentry);
if (err)
goto out;
old_cred = ovl_override_creds(inode->i_sb);
/*
* Store immutable/append-only flags in xattr and clear them
@ -798,6 +673,7 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
if (!err)
err = ovl_real_fileattr_set(&upperpath, fa);
revert_creds(old_cred);
ovl_drop_write(dentry);
/*
* Merge real inode flags with inode flags read from
@ -812,7 +688,6 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
/* Update ctime */
ovl_copyattr(inode);
}
ovl_drop_write(dentry);
out:
return err;
}

View File

@ -251,7 +251,10 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
err = -EREMOTE;
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;
goto put_and_out;
}
@ -264,8 +267,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
goto put_and_out;
}
path.dentry = this;
path.mnt = d->mnt;
if (!d_can_lookup(this)) {
if (d->is_dir || !last_element) {
d->stop = true;
@ -438,7 +439,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
else if (IS_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))
goto invalid;
@ -507,6 +508,19 @@ static int ovl_verify_fh(struct ovl_fs *ofs, struct dentry *dentry,
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.
*
@ -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.
*/
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real, 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 inode *inode;
struct ovl_fh *fh;
@ -530,9 +544,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
goto fail;
}
err = ovl_verify_fh(ofs, dentry, ox, fh);
if (set && err == -ENODATA)
err = ovl_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
err = ovl_verify_set_fh(ofs, dentry, ox, fh, is_upper, set);
if (err)
goto fail;
@ -548,6 +560,7 @@ fail:
goto out;
}
/* Get upper dentry from index */
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index,
bool connected)
@ -684,7 +697,7 @@ orphan:
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;
@ -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,
struct dentry *lower, struct dentry *upper)
{
const struct ovl_fh *fh;
int err;
if (ovl_check_origin_xattr(ofs, upper))
return 0;
fh = ovl_get_origin_fh(ofs, lower);
if (IS_ERR(fh))
return PTR_ERR(fh);
err = ovl_want_write(dentry);
if (err)
return err;
goto out;
err = ovl_set_origin(ofs, lower, upper);
err = ovl_set_origin_fh(ofs, fh, upper);
if (!err)
err = ovl_set_impure(dentry->d_parent, upper->d_parent);
ovl_drop_write(dentry);
out:
kfree(fh);
return err;
}
@ -1383,7 +1403,11 @@ bool ovl_lower_positive(struct dentry *dentry)
break;
}
} 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;
dput(this);
}

View File

@ -28,7 +28,16 @@ enum ovl_path_type {
#define OVL_XATTR_NAMESPACE "overlay."
#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_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 {
OVL_XATTR_OPAQUE,
@ -40,6 +49,8 @@ enum ovl_xattr {
OVL_XATTR_UUID,
OVL_XATTR_METACOPY,
OVL_XATTR_PROTATTR,
OVL_XATTR_XWHITEOUT,
OVL_XATTR_XWHITEOUTS,
};
enum ovl_inode_flag {
@ -398,6 +409,10 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
}
/* 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);
void ovl_drop_write(struct dentry *dentry);
struct dentry *ovl_workdir(struct dentry *dentry);
@ -460,6 +475,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
void ovl_dir_modified(struct dentry *dentry, bool impurity);
u64 ovl_inode_version_get(struct inode *inode);
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);
int ovl_copy_up_start(struct dentry *dentry, int flags);
void ovl_copy_up_end(struct dentry *dentry);
@ -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,
enum ovl_xattr ox);
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,
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,
struct dentry *upperdentry)
{
@ -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,
struct dentry *upperdentry, struct ovl_path **stackp);
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real, bool is_upper,
bool set);
enum ovl_xattr ox, const struct ovl_fh *fh,
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,
bool connected);
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,
struct qstr *name);
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,
unsigned int flags);
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,
struct dentry *origin, bool set)
{
return ovl_verify_set_fh(ofs, upper, OVL_XATTR_ORIGIN, origin,
false, set);
return ovl_verify_origin_xattr(ofs, upper, OVL_XATTR_ORIGIN, origin,
false, set);
}
static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index,
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 */
@ -684,17 +723,8 @@ int ovl_set_nlink_lower(struct dentry *dentry);
unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
struct dentry *upperdentry,
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 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
struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
@ -815,8 +845,9 @@ 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);
struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
bool is_upper);
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
struct dentry *upper);
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);
/* export.c */
extern const struct export_operations ovl_export_operations;
@ -830,3 +861,12 @@ static inline bool ovl_force_readonly(struct ovl_fs *ofs)
{
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);

View File

@ -43,8 +43,10 @@ module_param_named(metacopy, ovl_metacopy_def, bool, 0644);
MODULE_PARM_DESC(metacopy,
"Default to on or off for the metadata only copy up feature");
enum {
enum ovl_opt {
Opt_lowerdir,
Opt_lowerdir_add,
Opt_datadir_add,
Opt_upperdir,
Opt_workdir,
Opt_default_permissions,
@ -140,8 +142,11 @@ static int ovl_verity_mode_def(void)
#define fsparam_string_empty(NAME, OPT) \
__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
const struct fs_parameter_spec ovl_parameter_spec[] = {
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("workdir", Opt_workdir),
fsparam_flag("default_permissions", Opt_default_permissions),
@ -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);
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;
out_put:
path_put_init(path);
out:
return err;
}
@ -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;
char *tmp = kstrdup(name, GFP_KERNEL);
@ -276,68 +270,147 @@ static int ovl_mount_dir(const char *name, struct path *path, bool upper)
if (tmp) {
ovl_unescape(tmp);
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);
}
return err;
}
static int ovl_parse_param_upperdir(const char *name, struct fs_context *fc,
bool workdir)
static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path,
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 path path;
char *dup;
err = ovl_mount_dir(name, &path, true);
if (err)
return err;
if (ovl_dentry_weird(path->dentry))
return invalfc(fc, "filesystem on %s not supported", name);
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
* early. Don't forget to recheck when the superblock is created
* as the mount attributes could change.
*/
if (__mnt_is_readonly(path.mnt)) {
path_put(&path);
return -EINVAL;
}
dup = kstrdup(name, GFP_KERNEL);
if (!dup) {
path_put(&path);
return -ENOMEM;
}
if (workdir) {
kfree(config->workdir);
config->workdir = dup;
path_put(&ctx->work);
ctx->work = path;
if (upper) {
if (path->dentry->d_flags & DCACHE_OP_REAL)
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 {
kfree(config->upperdir);
config->upperdir = dup;
path_put(&ctx->upper);
ctx->upper = path;
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;
}
static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx)
static int ovl_ctx_realloc_lower(struct fs_context *fc)
{
for (size_t nr = 0; nr < ctx->nr; nr++) {
path_put(&ctx->lower[nr].path);
kfree(ctx->lower[nr].name);
ctx->lower[nr].name = NULL;
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;
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);
}
}
static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
enum ovl_opt layer)
{
char *name = kstrdup(param->string, GFP_KERNEL);
bool upper = (layer == Opt_upperdir || layer == Opt_workdir);
struct path path;
int err;
if (!name)
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;
}
/* 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_reset_lowerdirs(struct ovl_fs_context *ctx)
{
struct ovl_fs_context_layer *l = ctx->lower;
// Reset old user provided lowerdir string
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_data = 0;
@ -346,7 +419,7 @@ static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx)
/*
* 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
* "/data1" and "/data2" as data lower layers. Any existing lower
* layers are replaced.
@ -356,9 +429,9 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
int err;
struct ovl_fs_context *ctx = fc->fs_private;
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;
bool append = false, data_layer = false;
bool data_layer = false;
/*
* 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)
*/
/* drop all existing lower layers */
if (!*name) {
ovl_parse_param_drop_lowerdir(ctx);
ovl_reset_lowerdirs(ctx);
if (!*name)
return 0;
}
if (*name == ':') {
pr_err("cannot append lower layer");
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);
if (!dup)
return -ENOMEM;
@ -385,36 +463,11 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
if (nr_lower < 0)
goto out_err;
if ((nr_lower > OVL_MAX_STACK) ||
(append && (size_add(ctx->nr, nr_lower) > OVL_MAX_STACK))) {
if (nr_lower > OVL_MAX_STACK) {
pr_err("too many lower directories, limit is %d\n", OVL_MAX_STACK);
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) {
err = -ENOMEM;
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)
ctx->capacity = nr_lower;
}
/*
* (3) By (1) and (2) we know nr <= nr_lower <= capacity.
* (4) If ctx->nr == 0 => replace
* 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];
iter = dup;
l = ctx->lower;
for (nr = 0; nr < nr_lower; nr++, 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)
goto out_put;
err = -ENOMEM;
l->name = kstrdup(dup_iter, GFP_KERNEL_ACCOUNT);
l->name = kstrdup(iter, GFP_KERNEL_ACCOUNT);
if (!l->name)
goto out_put;
@ -472,8 +505,8 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
break;
err = -EINVAL;
dup_iter = strchr(dup_iter, '\0') + 1;
if (*dup_iter) {
iter = strchr(iter, '\0') + 1;
if (*iter) {
/*
* This is a regular layer so we require that
* there are no data layers.
@ -489,7 +522,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
/* This is a data lower layer. */
data_layer = true;
dup_iter++;
iter++;
}
ctx->nr = nr_lower;
ctx->nr_data += nr_data;
@ -497,21 +530,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
return 0;
out_put:
/*
* 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;
}
ovl_reset_lowerdirs(ctx);
out_err:
kfree(dup);
@ -556,11 +575,11 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
case Opt_lowerdir:
err = ovl_parse_param_lowerdir(param->string, fc);
break;
case Opt_lowerdir_add:
case Opt_datadir_add:
case Opt_upperdir:
fallthrough;
case Opt_workdir:
err = ovl_parse_param_upperdir(param->string, fc,
(Opt_workdir == opt));
err = ovl_parse_layer(fc, param, opt);
break;
case Opt_default_permissions:
config->default_permissions = true;
@ -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)
{
ovl_parse_param_drop_lowerdir(ctx);
ovl_reset_lowerdirs(ctx);
path_put(&ctx->upper);
path_put(&ctx->work);
kfree(ctx->lower);
@ -933,23 +952,28 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
{
struct super_block *sb = dentry->d_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
* >= 0 regular lower layers prefixed with : and
* >= 0 data-only lower layers prefixed with ::
*
* we need to escase comma and space like seq_show_option() does and
* we also need to escape the colon separator from lowerdir paths.
* lowerdirs[0] holds the colon separated list that user provided
* with lowerdir mount option.
* lowerdirs[1..numlayer] hold the lowerdir paths that were added
* using the lowerdir+ and datadir+ mount options.
* For now, we do not allow mixing the legacy lowerdir mount option
* with the new lowerdir+ and datadir+ mount options.
*/
seq_puts(m, ",lowerdir=");
for (nr = 1; nr < ofs->numlayer; nr++) {
if (nr > 1)
seq_putc(m, ':');
if (nr >= nr_merged_lower)
seq_putc(m, ':');
seq_escape(m, ofs->config.lowerdirs[nr], ":, \t\n\\");
if (lowerdirs[0]) {
seq_show_option(m, "lowerdir", lowerdirs[0]);
} else {
nr_lower = ofs->numlayer;
nr_merged_lower = nr_lower - ofs->numdatalayer;
}
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) {
seq_show_option(m, "upperdir", ofs->config.upperdir);

View File

@ -32,6 +32,7 @@ struct ovl_fs_context {
size_t nr_data;
struct ovl_opt_set set;
struct ovl_fs_context_layer *lower;
char *lowerdir_all; /* user provided lowerdir string */
};
int ovl_init_fs_context(struct fs_context *fc);

View File

@ -25,6 +25,7 @@ struct ovl_cache_entry {
struct ovl_cache_entry *next_maybe_whiteout;
bool is_upper;
bool is_whiteout;
bool check_xwhiteout;
char name[];
};
@ -47,6 +48,7 @@ struct ovl_readdir_data {
int err;
bool is_upper;
bool d_type_supported;
bool in_xwhiteouts_dir;
};
struct ovl_dir_file {
@ -162,6 +164,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
p->ino = 0;
p->is_upper = rdd->is_upper;
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) {
p->next_maybe_whiteout = rdd->first_maybe_whiteout;
@ -301,6 +305,8 @@ static inline int ovl_dir_read(const struct path *realpath,
if (IS_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->ctx.pos = 0;
do {
@ -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.
*
* 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,
* 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
* 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;
@ -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 err = 0;
if (!ovl_same_dev(ofs))
if (!ovl_same_dev(ofs) && !p->check_xwhiteout)
goto out;
if (p->name[0] == '.') {
@ -481,6 +490,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
goto get;
}
}
/* This checks also for xwhiteouts */
this = lookup_one(mnt_idmap(path->mnt), p->name, dir, p->len);
if (IS_ERR_OR_NULL(this) || !this->d_inode) {
/* Mark a stale entry */
@ -494,6 +504,9 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
}
get:
if (!ovl_same_dev(ofs) || !update_ino)
goto out;
type = ovl_path_type(this);
if (OVL_TYPE_ORIGIN(type)) {
struct kstat stat;
@ -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) {
if (strcmp(p->name, ".") != 0 &&
strcmp(p->name, "..") != 0) {
err = ovl_cache_update_ino(path, p);
err = ovl_cache_update(path, p, true);
if (err)
return err;
}
@ -778,13 +791,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
while (od->cursor != &od->cache->entries) {
p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
if (!p->is_whiteout) {
if (!p->ino) {
err = ovl_cache_update_ino(&file->f_path, p);
if (!p->ino || p->check_xwhiteout) {
err = ovl_cache_update(&file->f_path, p, !p->ino);
if (err)
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 (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
break;

View File

@ -445,68 +445,6 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
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,
struct inode **ptrap, const char *name)
{
@ -647,7 +585,7 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
if (IS_ERR(whiteout))
goto cleanup_temp;
err = ovl_is_whiteout(whiteout);
err = ovl_upper_is_whiteout(ofs, whiteout);
/* Best effort cleanup of whiteout and temp file */
if (err)
@ -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 dentry *indexdir;
struct dentry *origin = ovl_lowerstack(oe)->dentry;
const struct ovl_fh *fh;
int err;
fh = ovl_get_origin_fh(ofs, origin);
if (IS_ERR(fh))
return PTR_ERR(fh);
err = mnt_want_write(mnt);
if (err)
return err;
goto out_free_fh;
/* Verify lower root is upper root origin */
err = ovl_verify_origin(ofs, upperpath->dentry,
ovl_lowerstack(oe)->dentry, true);
err = ovl_verify_origin_fh(ofs, upperpath->dentry, fh, true);
if (err) {
pr_err("failed to verify upper root origin\n");
goto out;
@ -927,9 +870,10 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
* directory entries.
*/
if (ovl_check_origin_xattr(ofs, ofs->indexdir)) {
err = ovl_verify_set_fh(ofs, ofs->indexdir,
OVL_XATTR_ORIGIN,
upperpath->dentry, true, false);
err = ovl_verify_origin_xattr(ofs, ofs->indexdir,
OVL_XATTR_ORIGIN,
upperpath->dentry, true,
false);
if (err)
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,
out:
mnt_drop_write(mnt);
out_free_fh:
kfree(fh);
return err;
}
@ -1382,8 +1328,11 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
ofs->layers = layers;
/*
* 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;
sb->s_stack_depth = 0;
@ -1493,8 +1442,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
sb->s_magic = OVERLAYFS_SUPER_MAGIC;
sb->s_xattr = ofs->config.userxattr ? ovl_user_xattr_handlers :
ovl_trusted_xattr_handlers;
sb->s_xattr = ovl_xattr_handlers(ofs);
sb->s_fs_info = ofs;
#ifdef CONFIG_FS_POSIX_ACL
sb->s_flags |= SB_POSIXACL;

View File

@ -17,12 +17,38 @@
#include <linux/ratelimit.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)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
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)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
@ -575,6 +601,16 @@ bool ovl_is_whiteout(struct dentry *dentry)
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 inode *inode = d_inode(path->dentry);
@ -644,22 +680,36 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags)
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)
{
struct inode *inode = d_inode(dentry);
int err;
err = ovl_inode_lock_interruptible(inode);
if (!err && ovl_already_copied_up_locked(dentry, flags)) {
err = 1; /* Already copied up */
ovl_inode_unlock(inode);
}
if (err)
return err;
if (ovl_already_copied_up_locked(dentry, flags))
err = 1; /* Already copied up */
else
err = ovl_get_write_access(dentry);
if (err)
goto out_unlock;
return 0;
out_unlock:
ovl_inode_unlock(inode);
return err;
}
void ovl_copy_up_end(struct dentry *dentry)
{
ovl_put_write_access(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)
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
* 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,
#define OVL_XATTR_UUID_POSTFIX "uuid"
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
#define OVL_XATTR_PROTATTR_POSTFIX "protattr"
#define OVL_XATTR_XWHITEOUT_POSTFIX "whiteout"
#define OVL_XATTR_XWHITEOUTS_POSTFIX "whiteouts"
#define OVL_XATTR_TAB_ENTRY(x) \
[x] = { [false] = OVL_XATTR_TRUSTED_PREFIX x ## _POSTFIX, \
@ -775,6 +853,8 @@ const char *const ovl_xattr_table[][2] = {
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UUID),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
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,
@ -973,12 +1053,18 @@ static void ovl_cleanup_index(struct dentry *dentry)
struct dentry *index = NULL;
struct inode *inode;
struct qstr name = { };
bool got_write = false;
int err;
err = ovl_get_index_name(ofs, lowerdentry, &name);
if (err)
goto fail;
err = ovl_want_write(dentry);
if (err)
goto fail;
got_write = true;
inode = d_inode(upperdentry);
if (!S_ISDIR(inode->i_mode) && inode->i_nlink != 1) {
pr_warn_ratelimited("cleanup linked index (%pd2, ino=%lu, nlink=%u)\n",
@ -1016,6 +1102,8 @@ static void ovl_cleanup_index(struct dentry *dentry)
goto fail;
out:
if (got_write)
ovl_drop_write(dentry);
kfree(name.name);
dput(index);
return;
@ -1062,8 +1150,12 @@ int ovl_nlink_start(struct dentry *dentry)
if (err)
return err;
err = ovl_want_write(dentry);
if (err)
goto out_unlock;
if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode))
goto out;
return 0;
old_cred = ovl_override_creds(dentry->d_sb);
/*
@ -1074,10 +1166,15 @@ int ovl_nlink_start(struct dentry *dentry)
*/
err = ovl_set_nlink_upper(dentry);
revert_creds(old_cred);
out:
if (err)
ovl_inode_unlock(inode);
goto out_drop_write;
return 0;
out_drop_write:
ovl_drop_write(dentry);
out_unlock:
ovl_inode_unlock(inode);
return err;
}
@ -1086,6 +1183,8 @@ void ovl_nlink_end(struct dentry *dentry)
{
struct inode *inode = d_inode(dentry);
ovl_drop_write(dentry);
if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) {
const struct cred *old_cred;
@ -1403,6 +1502,7 @@ void ovl_copyattr(struct inode *inode)
realinode = ovl_i_path_real(inode, &realpath);
real_idmap = mnt_idmap(realpath.mnt);
spin_lock(&inode->i_lock);
vfsuid = i_uid_into_vfsuid(real_idmap, realinode);
vfsgid = i_gid_into_vfsgid(real_idmap, realinode);
@ -1413,4 +1513,5 @@ void ovl_copyattr(struct inode *inode)
inode_set_mtime_to_ts(inode, inode_get_mtime(realinode));
inode_set_ctime_to_ts(inode, inode_get_ctime(realinode));
i_size_write(inode, i_size_read(realinode));
spin_unlock(&inode->i_lock);
}

271
fs/overlayfs/xattrs.c Normal file
View File

@ -0,0 +1,271 @@
// 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;
}

View File

@ -2160,3 +2160,4 @@ int sb_init_dio_done_wq(struct super_block *sb)
destroy_workqueue(wq);
return 0;
}
EXPORT_SYMBOL_GPL(sb_init_dio_done_wq);