overlayfs fixes for 5.5-rc2
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCXfNhGQAKCRDh3BK/laaZ PGSEAP9Nyv3XCN2wdqMLdrgn07B3Pk9w2Unf3Y5amKOxNXqyQwEAy2/E6DCiGjSa WRheJoTgDSeqUQNY6GFHsCIgLWOCHgs= =WH5O -----END PGP SIGNATURE----- Merge tag 'ovl-fixes-5.5-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs Pull overlayfs fixes from Miklos Szeredi: "Fix some bugs and documentation" * tag 'ovl-fixes-5.5-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: docs: filesystems: overlayfs: Fix restview warnings docs: filesystems: overlayfs: Rename overlayfs.txt to .rst ovl: relax WARN_ON() on rename to self ovl: fix corner case of non-unique st_dev;st_ino ovl: don't use a temp buf for encoding real fh ovl: make sure that real fid is 32bit aligned in memory ovl: fix lookup failure on multi lower squashfs
This commit is contained in:
commit
81c64b0bd0
@ -1,3 +1,5 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Written by: Neil Brown
|
||||
Please see MAINTAINERS file for where to send questions.
|
||||
|
||||
@ -181,7 +183,7 @@ Kernel config options:
|
||||
worried about backward compatibility with kernels that have the redirect_dir
|
||||
feature and follow redirects even if turned off.
|
||||
|
||||
Module options (can also be changed through /sys/module/overlay/parameters/*):
|
||||
Module options (can also be changed through /sys/module/overlay/parameters/):
|
||||
|
||||
- "redirect_dir=BOOL":
|
||||
See OVERLAY_FS_REDIRECT_DIR kernel config option above.
|
||||
@ -263,7 +265,7 @@ top, lower2 the middle and lower3 the bottom layer.
|
||||
|
||||
|
||||
Metadata only copy up
|
||||
--------------------
|
||||
---------------------
|
||||
|
||||
When metadata only copy up feature is enabled, overlayfs will only copy
|
||||
up metadata (as opposed to whole file), when a metadata specific operation
|
||||
@ -286,10 +288,10 @@ pointed by REDIRECT. This should not be possible on local system as setting
|
||||
"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
|
||||
for untrusted layers like from a pen drive.
|
||||
|
||||
Note: redirect_dir={off|nofollow|follow(*)} conflicts with metacopy=on, and
|
||||
Note: redirect_dir={off|nofollow|follow[*]} conflicts with metacopy=on, and
|
||||
results in an error.
|
||||
|
||||
(*) redirect_dir=follow only conflicts with metacopy=on if upperdir=... is
|
||||
[*] redirect_dir=follow only conflicts with metacopy=on if upperdir=... is
|
||||
given.
|
||||
|
||||
Sharing and copying layers
|
@ -12394,7 +12394,7 @@ L: linux-unionfs@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
|
||||
S: Supported
|
||||
F: fs/overlayfs/
|
||||
F: Documentation/filesystems/overlayfs.txt
|
||||
F: Documentation/filesystems/overlayfs.rst
|
||||
|
||||
P54 WIRELESS DRIVER
|
||||
M: Christian Lamparter <chunkeey@googlemail.com>
|
||||
|
@ -227,13 +227,17 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
|
||||
struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper)
|
||||
{
|
||||
struct ovl_fh *fh;
|
||||
int fh_type, fh_len, dwords;
|
||||
void *buf;
|
||||
int fh_type, dwords;
|
||||
int buflen = MAX_HANDLE_SZ;
|
||||
uuid_t *uuid = &real->d_sb->s_uuid;
|
||||
int err;
|
||||
|
||||
buf = kmalloc(buflen, GFP_KERNEL);
|
||||
if (!buf)
|
||||
/* Make sure the real fid stays 32bit aligned */
|
||||
BUILD_BUG_ON(OVL_FH_FID_OFFSET % 4);
|
||||
BUILD_BUG_ON(MAX_HANDLE_SZ + OVL_FH_FID_OFFSET > 255);
|
||||
|
||||
fh = kzalloc(buflen + OVL_FH_FID_OFFSET, GFP_KERNEL);
|
||||
if (!fh)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
@ -242,27 +246,19 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper)
|
||||
* the price or reconnecting the dentry.
|
||||
*/
|
||||
dwords = buflen >> 2;
|
||||
fh_type = exportfs_encode_fh(real, buf, &dwords, 0);
|
||||
fh_type = exportfs_encode_fh(real, (void *)fh->fb.fid, &dwords, 0);
|
||||
buflen = (dwords << 2);
|
||||
|
||||
fh = ERR_PTR(-EIO);
|
||||
err = -EIO;
|
||||
if (WARN_ON(fh_type < 0) ||
|
||||
WARN_ON(buflen > MAX_HANDLE_SZ) ||
|
||||
WARN_ON(fh_type == FILEID_INVALID))
|
||||
goto out;
|
||||
goto out_err;
|
||||
|
||||
BUILD_BUG_ON(MAX_HANDLE_SZ + offsetof(struct ovl_fh, fid) > 255);
|
||||
fh_len = offsetof(struct ovl_fh, fid) + buflen;
|
||||
fh = kmalloc(fh_len, GFP_KERNEL);
|
||||
if (!fh) {
|
||||
fh = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fh->version = OVL_FH_VERSION;
|
||||
fh->magic = OVL_FH_MAGIC;
|
||||
fh->type = fh_type;
|
||||
fh->flags = OVL_FH_FLAG_CPU_ENDIAN;
|
||||
fh->fb.version = OVL_FH_VERSION;
|
||||
fh->fb.magic = OVL_FH_MAGIC;
|
||||
fh->fb.type = fh_type;
|
||||
fh->fb.flags = OVL_FH_FLAG_CPU_ENDIAN;
|
||||
/*
|
||||
* When we will want to decode an overlay dentry from this handle
|
||||
* and all layers are on the same fs, if we get a disconncted real
|
||||
@ -270,14 +266,15 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper)
|
||||
* it to upperdentry or to lowerstack is by checking this flag.
|
||||
*/
|
||||
if (is_upper)
|
||||
fh->flags |= OVL_FH_FLAG_PATH_UPPER;
|
||||
fh->len = fh_len;
|
||||
fh->uuid = *uuid;
|
||||
memcpy(fh->fid, buf, buflen);
|
||||
fh->fb.flags |= OVL_FH_FLAG_PATH_UPPER;
|
||||
fh->fb.len = sizeof(fh->fb) + buflen;
|
||||
fh->fb.uuid = *uuid;
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
return fh;
|
||||
|
||||
out_err:
|
||||
kfree(fh);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
|
||||
@ -300,8 +297,8 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
|
||||
/*
|
||||
* Do not fail when upper doesn't support xattrs.
|
||||
*/
|
||||
err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh,
|
||||
fh ? fh->len : 0, 0);
|
||||
err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh->buf,
|
||||
fh ? fh->fb.len : 0, 0);
|
||||
kfree(fh);
|
||||
|
||||
return err;
|
||||
@ -317,7 +314,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
|
||||
if (IS_ERR(fh))
|
||||
return PTR_ERR(fh);
|
||||
|
||||
err = ovl_do_setxattr(index, OVL_XATTR_UPPER, fh, fh->len, 0);
|
||||
err = ovl_do_setxattr(index, OVL_XATTR_UPPER, fh->buf, fh->fb.len, 0);
|
||||
|
||||
kfree(fh);
|
||||
return err;
|
||||
|
@ -1170,7 +1170,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
||||
if (newdentry == trap)
|
||||
goto out_dput;
|
||||
|
||||
if (WARN_ON(olddentry->d_inode == newdentry->d_inode))
|
||||
if (olddentry->d_inode == newdentry->d_inode)
|
||||
goto out_dput;
|
||||
|
||||
err = 0;
|
||||
|
@ -211,10 +211,11 @@ static int ovl_check_encode_origin(struct dentry *dentry)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
|
||||
static int ovl_dentry_to_fid(struct dentry *dentry, u32 *fid, int buflen)
|
||||
{
|
||||
struct ovl_fh *fh = NULL;
|
||||
int err, enc_lower;
|
||||
int len;
|
||||
|
||||
/*
|
||||
* Check if we should encode a lower or upper file handle and maybe
|
||||
@ -231,11 +232,12 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
|
||||
return PTR_ERR(fh);
|
||||
|
||||
err = -EOVERFLOW;
|
||||
if (fh->len > buflen)
|
||||
len = OVL_FH_LEN(fh);
|
||||
if (len > buflen)
|
||||
goto fail;
|
||||
|
||||
memcpy(buf, (char *)fh, fh->len);
|
||||
err = fh->len;
|
||||
memcpy(fid, fh, len);
|
||||
err = len;
|
||||
|
||||
out:
|
||||
kfree(fh);
|
||||
@ -243,31 +245,16 @@ out:
|
||||
|
||||
fail:
|
||||
pr_warn_ratelimited("overlayfs: failed to encode file handle (%pd2, err=%i, buflen=%d, len=%d, type=%d)\n",
|
||||
dentry, err, buflen, fh ? (int)fh->len : 0,
|
||||
fh ? fh->type : 0);
|
||||
dentry, err, buflen, fh ? (int)fh->fb.len : 0,
|
||||
fh ? fh->fb.type : 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int ovl_dentry_to_fh(struct dentry *dentry, u32 *fid, int *max_len)
|
||||
{
|
||||
int res, len = *max_len << 2;
|
||||
|
||||
res = ovl_d_to_fh(dentry, (char *)fid, len);
|
||||
if (res <= 0)
|
||||
return FILEID_INVALID;
|
||||
|
||||
len = res;
|
||||
|
||||
/* Round up to dwords */
|
||||
*max_len = (len + 3) >> 2;
|
||||
return OVL_FILEID;
|
||||
}
|
||||
|
||||
static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
|
||||
struct inode *parent)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int type;
|
||||
int bytes = *max_len << 2;
|
||||
|
||||
/* TODO: encode connectable file handles */
|
||||
if (parent)
|
||||
@ -277,10 +264,14 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
|
||||
if (WARN_ON(!dentry))
|
||||
return FILEID_INVALID;
|
||||
|
||||
type = ovl_dentry_to_fh(dentry, fid, max_len);
|
||||
|
||||
bytes = ovl_dentry_to_fid(dentry, fid, bytes);
|
||||
dput(dentry);
|
||||
return type;
|
||||
if (bytes <= 0)
|
||||
return FILEID_INVALID;
|
||||
|
||||
*max_len = bytes >> 2;
|
||||
|
||||
return OVL_FILEID_V1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -777,24 +768,45 @@ out_err:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct ovl_fh *ovl_fid_to_fh(struct fid *fid, int buflen, int fh_type)
|
||||
{
|
||||
struct ovl_fh *fh;
|
||||
|
||||
/* If on-wire inner fid is aligned - nothing to do */
|
||||
if (fh_type == OVL_FILEID_V1)
|
||||
return (struct ovl_fh *)fid;
|
||||
|
||||
if (fh_type != OVL_FILEID_V0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
fh = kzalloc(buflen, GFP_KERNEL);
|
||||
if (!fh)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Copy unaligned inner fh into aligned buffer */
|
||||
memcpy(&fh->fb, fid, buflen - OVL_FH_WIRE_OFFSET);
|
||||
return fh;
|
||||
}
|
||||
|
||||
static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid,
|
||||
int fh_len, int fh_type)
|
||||
{
|
||||
struct dentry *dentry = NULL;
|
||||
struct ovl_fh *fh = (struct ovl_fh *) fid;
|
||||
struct ovl_fh *fh = NULL;
|
||||
int len = fh_len << 2;
|
||||
unsigned int flags = 0;
|
||||
int err;
|
||||
|
||||
err = -EINVAL;
|
||||
if (fh_type != OVL_FILEID)
|
||||
fh = ovl_fid_to_fh(fid, len, fh_type);
|
||||
err = PTR_ERR(fh);
|
||||
if (IS_ERR(fh))
|
||||
goto out_err;
|
||||
|
||||
err = ovl_check_fh_len(fh, len);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
flags = fh->flags;
|
||||
flags = fh->fb.flags;
|
||||
dentry = (flags & OVL_FH_FLAG_PATH_UPPER) ?
|
||||
ovl_upper_fh_to_d(sb, fh) :
|
||||
ovl_lower_fh_to_d(sb, fh);
|
||||
@ -802,12 +814,18 @@ static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid,
|
||||
if (IS_ERR(dentry) && err != -ESTALE)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
/* We may have needed to re-align OVL_FILEID_V0 */
|
||||
if (!IS_ERR_OR_NULL(fh) && fh != (void *)fid)
|
||||
kfree(fh);
|
||||
|
||||
return dentry;
|
||||
|
||||
out_err:
|
||||
pr_warn_ratelimited("overlayfs: failed to decode file handle (len=%d, type=%d, flags=%x, err=%i)\n",
|
||||
len, fh_type, flags, err);
|
||||
return ERR_PTR(err);
|
||||
fh_len, fh_type, flags, err);
|
||||
dentry = ERR_PTR(err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct dentry *ovl_fh_to_parent(struct super_block *sb, struct fid *fid,
|
||||
|
@ -200,8 +200,14 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
|
||||
if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) ||
|
||||
(!ovl_verify_lower(dentry->d_sb) &&
|
||||
(is_dir || lowerstat.nlink == 1))) {
|
||||
stat->ino = lowerstat.ino;
|
||||
lower_layer = ovl_layer_lower(dentry);
|
||||
/*
|
||||
* Cannot use origin st_dev;st_ino because
|
||||
* origin inode content may differ from overlay
|
||||
* inode content.
|
||||
*/
|
||||
if (samefs || lower_layer->fsid)
|
||||
stat->ino = lowerstat.ino;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -84,21 +84,21 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry)
|
||||
* Return -ENODATA for "origin unknown".
|
||||
* Return <0 for an invalid file handle.
|
||||
*/
|
||||
int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
|
||||
int ovl_check_fb_len(struct ovl_fb *fb, int fb_len)
|
||||
{
|
||||
if (fh_len < sizeof(struct ovl_fh) || fh_len < fh->len)
|
||||
if (fb_len < sizeof(struct ovl_fb) || fb_len < fb->len)
|
||||
return -EINVAL;
|
||||
|
||||
if (fh->magic != OVL_FH_MAGIC)
|
||||
if (fb->magic != OVL_FH_MAGIC)
|
||||
return -EINVAL;
|
||||
|
||||
/* Treat larger version and unknown flags as "origin unknown" */
|
||||
if (fh->version > OVL_FH_VERSION || fh->flags & ~OVL_FH_FLAG_ALL)
|
||||
if (fb->version > OVL_FH_VERSION || fb->flags & ~OVL_FH_FLAG_ALL)
|
||||
return -ENODATA;
|
||||
|
||||
/* Treat endianness mismatch as "origin unknown" */
|
||||
if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
|
||||
(fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
|
||||
if (!(fb->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
|
||||
(fb->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
|
||||
return -ENODATA;
|
||||
|
||||
return 0;
|
||||
@ -119,15 +119,15 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
|
||||
if (res == 0)
|
||||
return NULL;
|
||||
|
||||
fh = kzalloc(res, GFP_KERNEL);
|
||||
fh = kzalloc(res + OVL_FH_WIRE_OFFSET, GFP_KERNEL);
|
||||
if (!fh)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
res = vfs_getxattr(dentry, name, fh, res);
|
||||
res = vfs_getxattr(dentry, name, fh->buf, res);
|
||||
if (res < 0)
|
||||
goto fail;
|
||||
|
||||
err = ovl_check_fh_len(fh, res);
|
||||
err = ovl_check_fb_len(&fh->fb, res);
|
||||
if (err < 0) {
|
||||
if (err == -ENODATA)
|
||||
goto out;
|
||||
@ -158,12 +158,12 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
|
||||
* Make sure that the stored uuid matches the uuid of the lower
|
||||
* layer where file handle will be decoded.
|
||||
*/
|
||||
if (!uuid_equal(&fh->uuid, &mnt->mnt_sb->s_uuid))
|
||||
if (!uuid_equal(&fh->fb.uuid, &mnt->mnt_sb->s_uuid))
|
||||
return NULL;
|
||||
|
||||
bytes = (fh->len - offsetof(struct ovl_fh, fid));
|
||||
real = exportfs_decode_fh(mnt, (struct fid *)fh->fid,
|
||||
bytes >> 2, (int)fh->type,
|
||||
bytes = (fh->fb.len - offsetof(struct ovl_fb, fid));
|
||||
real = exportfs_decode_fh(mnt, (struct fid *)fh->fb.fid,
|
||||
bytes >> 2, (int)fh->fb.type,
|
||||
connected ? ovl_acceptable : NULL, mnt);
|
||||
if (IS_ERR(real)) {
|
||||
/*
|
||||
@ -173,7 +173,7 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
|
||||
* index entries correctly.
|
||||
*/
|
||||
if (real == ERR_PTR(-ESTALE) &&
|
||||
!(fh->flags & OVL_FH_FLAG_PATH_UPPER))
|
||||
!(fh->fb.flags & OVL_FH_FLAG_PATH_UPPER))
|
||||
real = NULL;
|
||||
return real;
|
||||
}
|
||||
@ -323,6 +323,14 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ofs->numlower; i++) {
|
||||
/*
|
||||
* If lower fs uuid is not unique among lower fs we cannot match
|
||||
* fh->uuid to layer.
|
||||
*/
|
||||
if (ofs->lower_layers[i].fsid &&
|
||||
ofs->lower_layers[i].fs->bad_uuid)
|
||||
continue;
|
||||
|
||||
origin = ovl_decode_real_fh(fh, ofs->lower_layers[i].mnt,
|
||||
connected);
|
||||
if (origin)
|
||||
@ -400,7 +408,7 @@ static int ovl_verify_fh(struct dentry *dentry, const char *name,
|
||||
if (IS_ERR(ofh))
|
||||
return PTR_ERR(ofh);
|
||||
|
||||
if (fh->len != ofh->len || memcmp(fh, ofh, fh->len))
|
||||
if (fh->fb.len != ofh->fb.len || memcmp(&fh->fb, &ofh->fb, fh->fb.len))
|
||||
err = -ESTALE;
|
||||
|
||||
kfree(ofh);
|
||||
@ -431,7 +439,7 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name,
|
||||
|
||||
err = ovl_verify_fh(dentry, name, fh);
|
||||
if (set && err == -ENODATA)
|
||||
err = ovl_do_setxattr(dentry, name, fh, fh->len, 0);
|
||||
err = ovl_do_setxattr(dentry, name, fh->buf, fh->fb.len, 0);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
@ -505,20 +513,20 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
|
||||
goto fail;
|
||||
|
||||
err = -EINVAL;
|
||||
if (index->d_name.len < sizeof(struct ovl_fh)*2)
|
||||
if (index->d_name.len < sizeof(struct ovl_fb)*2)
|
||||
goto fail;
|
||||
|
||||
err = -ENOMEM;
|
||||
len = index->d_name.len / 2;
|
||||
fh = kzalloc(len, GFP_KERNEL);
|
||||
fh = kzalloc(len + OVL_FH_WIRE_OFFSET, GFP_KERNEL);
|
||||
if (!fh)
|
||||
goto fail;
|
||||
|
||||
err = -EINVAL;
|
||||
if (hex2bin((u8 *)fh, index->d_name.name, len))
|
||||
if (hex2bin(fh->buf, index->d_name.name, len))
|
||||
goto fail;
|
||||
|
||||
err = ovl_check_fh_len(fh, len);
|
||||
err = ovl_check_fb_len(&fh->fb, len);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
@ -597,11 +605,11 @@ static int ovl_get_index_name_fh(struct ovl_fh *fh, struct qstr *name)
|
||||
{
|
||||
char *n, *s;
|
||||
|
||||
n = kcalloc(fh->len, 2, GFP_KERNEL);
|
||||
n = kcalloc(fh->fb.len, 2, GFP_KERNEL);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
s = bin2hex(n, fh, fh->len);
|
||||
s = bin2hex(n, fh->buf, fh->fb.len);
|
||||
*name = (struct qstr) QSTR_INIT(n, s - n);
|
||||
|
||||
return 0;
|
||||
|
@ -71,20 +71,36 @@ enum ovl_entry_flag {
|
||||
#error Endianness not defined
|
||||
#endif
|
||||
|
||||
/* The type returned by overlay exportfs ops when encoding an ovl_fh handle */
|
||||
#define OVL_FILEID 0xfb
|
||||
/* The type used to be returned by overlay exportfs for misaligned fid */
|
||||
#define OVL_FILEID_V0 0xfb
|
||||
/* The type returned by overlay exportfs for 32bit aligned fid */
|
||||
#define OVL_FILEID_V1 0xf8
|
||||
|
||||
/* On-disk and in-memeory format for redirect by file handle */
|
||||
struct ovl_fh {
|
||||
/* On-disk format for "origin" file handle */
|
||||
struct ovl_fb {
|
||||
u8 version; /* 0 */
|
||||
u8 magic; /* 0xfb */
|
||||
u8 len; /* size of this header + size of fid */
|
||||
u8 flags; /* OVL_FH_FLAG_* */
|
||||
u8 type; /* fid_type of fid */
|
||||
uuid_t uuid; /* uuid of filesystem */
|
||||
u8 fid[0]; /* file identifier */
|
||||
u32 fid[0]; /* file identifier should be 32bit aligned in-memory */
|
||||
} __packed;
|
||||
|
||||
/* In-memory and on-wire format for overlay file handle */
|
||||
struct ovl_fh {
|
||||
u8 padding[3]; /* make sure fb.fid is 32bit aligned */
|
||||
union {
|
||||
struct ovl_fb fb;
|
||||
u8 buf[0];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define OVL_FH_WIRE_OFFSET offsetof(struct ovl_fh, fb)
|
||||
#define OVL_FH_LEN(fh) (OVL_FH_WIRE_OFFSET + (fh)->fb.len)
|
||||
#define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \
|
||||
offsetof(struct ovl_fb, fid))
|
||||
|
||||
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int err = vfs_rmdir(dir, dentry);
|
||||
@ -302,7 +318,13 @@ static inline void ovl_inode_unlock(struct inode *inode)
|
||||
|
||||
|
||||
/* namei.c */
|
||||
int ovl_check_fh_len(struct ovl_fh *fh, int fh_len);
|
||||
int ovl_check_fb_len(struct ovl_fb *fb, int fb_len);
|
||||
|
||||
static inline int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
|
||||
{
|
||||
return ovl_check_fb_len(&fh->fb, fh_len - OVL_FH_WIRE_OFFSET);
|
||||
}
|
||||
|
||||
struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
|
||||
bool connected);
|
||||
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
|
||||
|
@ -22,6 +22,8 @@ struct ovl_config {
|
||||
struct ovl_sb {
|
||||
struct super_block *sb;
|
||||
dev_t pseudo_dev;
|
||||
/* Unusable (conflicting) uuid */
|
||||
bool bad_uuid;
|
||||
};
|
||||
|
||||
struct ovl_layer {
|
||||
|
@ -1255,7 +1255,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!ofs->config.nfs_export && !(ofs->config.index && ofs->upper_mnt))
|
||||
if (!ofs->config.nfs_export && !ofs->upper_mnt)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < ofs->numlowerfs; i++) {
|
||||
@ -1263,9 +1263,13 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
|
||||
* We use uuid to associate an overlay lower file handle with a
|
||||
* lower layer, so we can accept lower fs with null uuid as long
|
||||
* as all lower layers with null uuid are on the same fs.
|
||||
* if we detect multiple lower fs with the same uuid, we
|
||||
* disable lower file handle decoding on all of them.
|
||||
*/
|
||||
if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid))
|
||||
if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid)) {
|
||||
ofs->lower_fs[i].bad_uuid = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1277,6 +1281,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
|
||||
unsigned int i;
|
||||
dev_t dev;
|
||||
int err;
|
||||
bool bad_uuid = false;
|
||||
|
||||
/* fsid 0 is reserved for upper fs even with non upper overlay */
|
||||
if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb)
|
||||
@ -1288,11 +1293,15 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
|
||||
}
|
||||
|
||||
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) {
|
||||
ofs->config.index = false;
|
||||
ofs->config.nfs_export = false;
|
||||
pr_warn("overlayfs: %s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n",
|
||||
uuid_is_null(&sb->s_uuid) ? "null" : "conflicting",
|
||||
path->dentry);
|
||||
bad_uuid = true;
|
||||
if (ofs->config.index || ofs->config.nfs_export) {
|
||||
ofs->config.index = false;
|
||||
ofs->config.nfs_export = false;
|
||||
pr_warn("overlayfs: %s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n",
|
||||
uuid_is_null(&sb->s_uuid) ? "null" :
|
||||
"conflicting",
|
||||
path->dentry);
|
||||
}
|
||||
}
|
||||
|
||||
err = get_anon_bdev(&dev);
|
||||
@ -1303,6 +1312,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
|
||||
|
||||
ofs->lower_fs[ofs->numlowerfs].sb = sb;
|
||||
ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev;
|
||||
ofs->lower_fs[ofs->numlowerfs].bad_uuid = bad_uuid;
|
||||
ofs->numlowerfs++;
|
||||
|
||||
return ofs->numlowerfs;
|
||||
|
Loading…
Reference in New Issue
Block a user