Commit 81c64b0b authored by Linus Torvalds's avatar Linus Torvalds

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
parents e31736d9 35c6cb41
.. SPDX-License-Identifier: GPL-2.0
Written by: Neil Brown Written by: Neil Brown
Please see MAINTAINERS file for where to send questions. Please see MAINTAINERS file for where to send questions.
...@@ -181,7 +183,7 @@ Kernel config options: ...@@ -181,7 +183,7 @@ Kernel config options:
worried about backward compatibility with kernels that have the redirect_dir worried about backward compatibility with kernels that have the redirect_dir
feature and follow redirects even if turned off. 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": - "redirect_dir=BOOL":
See OVERLAY_FS_REDIRECT_DIR kernel config option above. See OVERLAY_FS_REDIRECT_DIR kernel config option above.
...@@ -263,7 +265,7 @@ top, lower2 the middle and lower3 the bottom layer. ...@@ -263,7 +265,7 @@ top, lower2 the middle and lower3 the bottom layer.
Metadata only copy up Metadata only copy up
-------------------- ---------------------
When metadata only copy up feature is enabled, overlayfs will only copy When metadata only copy up feature is enabled, overlayfs will only copy
up metadata (as opposed to whole file), when a metadata specific operation 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 ...@@ -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 "trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
for untrusted layers like from a pen drive. 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. 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. given.
Sharing and copying layers Sharing and copying layers
......
...@@ -12394,7 +12394,7 @@ L: linux-unionfs@vger.kernel.org ...@@ -12394,7 +12394,7 @@ L: linux-unionfs@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
S: Supported S: Supported
F: fs/overlayfs/ F: fs/overlayfs/
F: Documentation/filesystems/overlayfs.txt F: Documentation/filesystems/overlayfs.rst
P54 WIRELESS DRIVER P54 WIRELESS DRIVER
M: Christian Lamparter <chunkeey@googlemail.com> M: Christian Lamparter <chunkeey@googlemail.com>
......
...@@ -227,13 +227,17 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) ...@@ -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 *ovl_encode_real_fh(struct dentry *real, bool is_upper)
{ {
struct ovl_fh *fh; struct ovl_fh *fh;
int fh_type, fh_len, dwords; int fh_type, dwords;
void *buf;
int buflen = MAX_HANDLE_SZ; int buflen = MAX_HANDLE_SZ;
uuid_t *uuid = &real->d_sb->s_uuid; uuid_t *uuid = &real->d_sb->s_uuid;
int err;
buf = kmalloc(buflen, GFP_KERNEL); /* Make sure the real fid stays 32bit aligned */
if (!buf) 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); return ERR_PTR(-ENOMEM);
/* /*
...@@ -242,27 +246,19 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper) ...@@ -242,27 +246,19 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper)
* the price or reconnecting the dentry. * the price or reconnecting the dentry.
*/ */
dwords = buflen >> 2; 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); buflen = (dwords << 2);
fh = ERR_PTR(-EIO); err = -EIO;
if (WARN_ON(fh_type < 0) || if (WARN_ON(fh_type < 0) ||
WARN_ON(buflen > MAX_HANDLE_SZ) || WARN_ON(buflen > MAX_HANDLE_SZ) ||
WARN_ON(fh_type == FILEID_INVALID)) WARN_ON(fh_type == FILEID_INVALID))
goto out; goto out_err;
BUILD_BUG_ON(MAX_HANDLE_SZ + offsetof(struct ovl_fh, fid) > 255); fh->fb.version = OVL_FH_VERSION;
fh_len = offsetof(struct ovl_fh, fid) + buflen; fh->fb.magic = OVL_FH_MAGIC;
fh = kmalloc(fh_len, GFP_KERNEL); fh->fb.type = fh_type;
if (!fh) { fh->fb.flags = OVL_FH_FLAG_CPU_ENDIAN;
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;
/* /*
* When we will want to decode an overlay dentry from this handle * 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 * 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) ...@@ -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. * it to upperdentry or to lowerstack is by checking this flag.
*/ */
if (is_upper) if (is_upper)
fh->flags |= OVL_FH_FLAG_PATH_UPPER; fh->fb.flags |= OVL_FH_FLAG_PATH_UPPER;
fh->len = fh_len; fh->fb.len = sizeof(fh->fb) + buflen;
fh->uuid = *uuid; fh->fb.uuid = *uuid;
memcpy(fh->fid, buf, buflen);
out:
kfree(buf);
return fh; return fh;
out_err:
kfree(fh);
return ERR_PTR(err);
} }
int ovl_set_origin(struct dentry *dentry, struct dentry *lower, int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
...@@ -300,8 +297,8 @@ 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. * Do not fail when upper doesn't support xattrs.
*/ */
err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh, err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh->buf,
fh ? fh->len : 0, 0); fh ? fh->fb.len : 0, 0);
kfree(fh); kfree(fh);
return err; return err;
...@@ -317,7 +314,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index) ...@@ -317,7 +314,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
if (IS_ERR(fh)) if (IS_ERR(fh))
return PTR_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); kfree(fh);
return err; return err;
......
...@@ -1170,7 +1170,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1170,7 +1170,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
if (newdentry == trap) if (newdentry == trap)
goto out_dput; goto out_dput;
if (WARN_ON(olddentry->d_inode == newdentry->d_inode)) if (olddentry->d_inode == newdentry->d_inode)
goto out_dput; goto out_dput;
err = 0; err = 0;
......
...@@ -211,10 +211,11 @@ static int ovl_check_encode_origin(struct dentry *dentry) ...@@ -211,10 +211,11 @@ static int ovl_check_encode_origin(struct dentry *dentry)
return 1; 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; struct ovl_fh *fh = NULL;
int err, enc_lower; int err, enc_lower;
int len;
/* /*
* Check if we should encode a lower or upper file handle and maybe * 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) ...@@ -231,11 +232,12 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
return PTR_ERR(fh); return PTR_ERR(fh);
err = -EOVERFLOW; err = -EOVERFLOW;
if (fh->len > buflen) len = OVL_FH_LEN(fh);
if (len > buflen)
goto fail; goto fail;
memcpy(buf, (char *)fh, fh->len); memcpy(fid, fh, len);
err = fh->len; err = len;
out: out:
kfree(fh); kfree(fh);
...@@ -243,31 +245,16 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen) ...@@ -243,31 +245,16 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
fail: fail:
pr_warn_ratelimited("overlayfs: failed to encode file handle (%pd2, err=%i, buflen=%d, len=%d, type=%d)\n", 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, dentry, err, buflen, fh ? (int)fh->fb.len : 0,
fh ? fh->type : 0); fh ? fh->fb.type : 0);
goto out; 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, static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
struct inode *parent) struct inode *parent)
{ {
struct dentry *dentry; struct dentry *dentry;
int type; int bytes = *max_len << 2;
/* TODO: encode connectable file handles */ /* TODO: encode connectable file handles */
if (parent) if (parent)
...@@ -277,10 +264,14 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len, ...@@ -277,10 +264,14 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
if (WARN_ON(!dentry)) if (WARN_ON(!dentry))
return FILEID_INVALID; return FILEID_INVALID;
type = ovl_dentry_to_fh(dentry, fid, max_len); bytes = ovl_dentry_to_fid(dentry, fid, bytes);
dput(dentry); dput(dentry);
return type; if (bytes <= 0)
return FILEID_INVALID;
*max_len = bytes >> 2;
return OVL_FILEID_V1;
} }
/* /*
...@@ -777,24 +768,45 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, ...@@ -777,24 +768,45 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
goto out; 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, static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid,
int fh_len, int fh_type) int fh_len, int fh_type)
{ {
struct dentry *dentry = NULL; struct dentry *dentry = NULL;
struct ovl_fh *fh = (struct ovl_fh *) fid; struct ovl_fh *fh = NULL;
int len = fh_len << 2; int len = fh_len << 2;
unsigned int flags = 0; unsigned int flags = 0;
int err; int err;
err = -EINVAL; fh = ovl_fid_to_fh(fid, len, fh_type);
if (fh_type != OVL_FILEID) err = PTR_ERR(fh);
if (IS_ERR(fh))
goto out_err; goto out_err;
err = ovl_check_fh_len(fh, len); err = ovl_check_fh_len(fh, len);
if (err) if (err)
goto out_err; goto out_err;
flags = fh->flags; flags = fh->fb.flags;
dentry = (flags & OVL_FH_FLAG_PATH_UPPER) ? dentry = (flags & OVL_FH_FLAG_PATH_UPPER) ?
ovl_upper_fh_to_d(sb, fh) : ovl_upper_fh_to_d(sb, fh) :
ovl_lower_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, ...@@ -802,12 +814,18 @@ static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid,
if (IS_ERR(dentry) && err != -ESTALE) if (IS_ERR(dentry) && err != -ESTALE)
goto out_err; 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; return dentry;
out_err: out_err:
pr_warn_ratelimited("overlayfs: failed to decode file handle (len=%d, type=%d, flags=%x, err=%i)\n", pr_warn_ratelimited("overlayfs: failed to decode file handle (len=%d, type=%d, flags=%x, err=%i)\n",
len, fh_type, flags, err); fh_len, fh_type, flags, err);
return ERR_PTR(err); dentry = ERR_PTR(err);
goto out;
} }
static struct dentry *ovl_fh_to_parent(struct super_block *sb, struct fid *fid, 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, ...@@ -200,8 +200,14 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) || if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) ||
(!ovl_verify_lower(dentry->d_sb) && (!ovl_verify_lower(dentry->d_sb) &&
(is_dir || lowerstat.nlink == 1))) { (is_dir || lowerstat.nlink == 1))) {
stat->ino = lowerstat.ino;
lower_layer = ovl_layer_lower(dentry); 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) ...@@ -84,21 +84,21 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry)
* Return -ENODATA for "origin unknown". * Return -ENODATA for "origin unknown".
* Return <0 for an invalid file handle. * 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; return -EINVAL;
if (fh->magic != OVL_FH_MAGIC) if (fb->magic != OVL_FH_MAGIC)
return -EINVAL; return -EINVAL;
/* Treat larger version and unknown flags as "origin unknown" */ /* 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; return -ENODATA;
/* Treat endianness mismatch as "origin unknown" */ /* Treat endianness mismatch as "origin unknown" */
if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) && if (!(fb->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
(fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN) (fb->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
return -ENODATA; return -ENODATA;
return 0; return 0;
...@@ -119,15 +119,15 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name) ...@@ -119,15 +119,15 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
if (res == 0) if (res == 0)
return NULL; return NULL;
fh = kzalloc(res, GFP_KERNEL); fh = kzalloc(res + OVL_FH_WIRE_OFFSET, GFP_KERNEL);
if (!fh) if (!fh)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
res = vfs_getxattr(dentry, name, fh, res); res = vfs_getxattr(dentry, name, fh->buf, res);
if (res < 0) if (res < 0)
goto fail; goto fail;
err = ovl_check_fh_len(fh, res); err = ovl_check_fb_len(&fh->fb, res);
if (err < 0) { if (err < 0) {
if (err == -ENODATA) if (err == -ENODATA)
goto out; goto out;
...@@ -158,12 +158,12 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt, ...@@ -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 * Make sure that the stored uuid matches the uuid of the lower
* layer where file handle will be decoded. * 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; return NULL;
bytes = (fh->len - offsetof(struct ovl_fh, fid)); bytes = (fh->fb.len - offsetof(struct ovl_fb, fid));
real = exportfs_decode_fh(mnt, (struct fid *)fh->fid, real = exportfs_decode_fh(mnt, (struct fid *)fh->fb.fid,
bytes >> 2, (int)fh->type, bytes >> 2, (int)fh->fb.type,
connected ? ovl_acceptable : NULL, mnt); connected ? ovl_acceptable : NULL, mnt);
if (IS_ERR(real)) { if (IS_ERR(real)) {
/* /*
...@@ -173,7 +173,7 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt, ...@@ -173,7 +173,7 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
* index entries correctly. * index entries correctly.
*/ */
if (real == ERR_PTR(-ESTALE) && if (real == ERR_PTR(-ESTALE) &&
!(fh->flags & OVL_FH_FLAG_PATH_UPPER)) !(fh->fb.flags & OVL_FH_FLAG_PATH_UPPER))
real = NULL; real = NULL;
return real; return real;
} }
...@@ -323,6 +323,14 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, ...@@ -323,6 +323,14 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
int i; int i;
for (i = 0; i < ofs->numlower; 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, origin = ovl_decode_real_fh(fh, ofs->lower_layers[i].mnt,
connected); connected);
if (origin) if (origin)
...@@ -400,7 +408,7 @@ static int ovl_verify_fh(struct dentry *dentry, const char *name, ...@@ -400,7 +408,7 @@ static int ovl_verify_fh(struct dentry *dentry, const char *name,
if (IS_ERR(ofh)) if (IS_ERR(ofh))
return PTR_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; err = -ESTALE;
kfree(ofh); kfree(ofh);
...@@ -431,7 +439,7 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name, ...@@ -431,7 +439,7 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name,
err = ovl_verify_fh(dentry, name, fh); err = ovl_verify_fh(dentry, name, fh);
if (set && err == -ENODATA) 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) if (err)
goto fail; goto fail;
...@@ -505,20 +513,20 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index) ...@@ -505,20 +513,20 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
goto fail; goto fail;
err = -EINVAL; err = -EINVAL;
if (index->d_name.len < sizeof(struct ovl_fh)*2) if (index->d_name.len < sizeof(struct ovl_fb)*2)
goto fail; goto fail;
err = -ENOMEM; err = -ENOMEM;
len = index->d_name.len / 2; len = index->d_name.len / 2;
fh = kzalloc(len, GFP_KERNEL); fh = kzalloc(len + OVL_FH_WIRE_OFFSET, GFP_KERNEL);
if (!fh) if (!fh)
goto fail; goto fail;
err = -EINVAL; err = -EINVAL;
if (hex2bin((u8 *)fh, index->d_name.name, len)) if (hex2bin(fh->buf, index->d_name.name, len))
goto fail; goto fail;
err = ovl_check_fh_len(fh, len); err = ovl_check_fb_len(&fh->fb, len);
if (err) if (err)
goto fail; goto fail;
...@@ -597,11 +605,11 @@ static int ovl_get_index_name_fh(struct ovl_fh *fh, struct qstr *name) ...@@ -597,11 +605,11 @@ static int ovl_get_index_name_fh(struct ovl_fh *fh, struct qstr *name)
{ {
char *n, *s; char *n, *s;
n = kcalloc(fh->len, 2, GFP_KERNEL); n = kcalloc(fh->fb.len, 2, GFP_KERNEL);
if (!n) if (!n)
return -ENOMEM; return -ENOMEM;
s = bin2hex(n, fh, fh->len); s = bin2hex(n, fh->buf, fh->fb.len);
*name = (struct qstr) QSTR_INIT(n, s - n); *name = (struct qstr) QSTR_INIT(n, s - n);
return 0; return 0;
......
...@@ -71,20 +71,36 @@ enum ovl_entry_flag { ...@@ -71,20 +71,36 @@ enum ovl_entry_flag {
#error Endianness not defined #error Endianness not defined
#endif #endif
/* The type returned by overlay exportfs ops when encoding an ovl_fh handle */ /* The type used to be returned by overlay exportfs for misaligned fid */
#define OVL_FILEID 0xfb #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 */ /* On-disk format for "origin" file handle */
struct ovl_fh { struct ovl_fb {
u8 version; /* 0 */ u8 version; /* 0 */
u8 magic; /* 0xfb */ u8 magic; /* 0xfb */
u8 len; /* size of this header + size of fid */ u8 len; /* size of this header + size of fid */
u8 flags; /* OVL_FH_FLAG_* */ u8 flags; /* OVL_FH_FLAG_* */
u8 type; /* fid_type of fid */ u8 type; /* fid_type of fid */
uuid_t uuid; /* uuid of filesystem */ uuid_t uuid; /* uuid of filesystem */
u8 fid[0]; /* file identifier */ u32 fid[0]; /* file identifier should be 32bit aligned in-memory */
} __packed; } __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) static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{ {
int err = vfs_rmdir(dir, dentry); int err = vfs_rmdir(dir, dentry);
...@@ -302,7 +318,13 @@ static inline void ovl_inode_unlock(struct inode *inode) ...@@ -302,7 +318,13 @@ static inline void ovl_inode_unlock(struct inode *inode)
/* namei.c */ /* 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, struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
bool connected); bool connected);
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
......
...@@ -22,6 +22,8 @@ struct ovl_config { ...@@ -22,6 +22,8 @@ struct ovl_config {
struct ovl_sb { struct ovl_sb {
struct super_block *sb; struct super_block *sb;
dev_t pseudo_dev; dev_t pseudo_dev;
/* Unusable (conflicting) uuid */
bool bad_uuid;
}; };
struct ovl_layer { struct ovl_layer {
......
...@@ -1255,7 +1255,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) ...@@ -1255,7 +1255,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
{ {
unsigned int i; unsigned int i;
if (!ofs->config.nfs_export && !(ofs->config.index && ofs->upper_mnt)) if (!ofs->config.nfs_export && !ofs->upper_mnt)
return true; return true;
for (i = 0; i < ofs->numlowerfs; i++) { for (i = 0; i < ofs->numlowerfs; i++) {
...@@ -1263,10 +1263,14 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) ...@@ -1263,10 +1263,14 @@ 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 * 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 * 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. * 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 false;
} }
}
return true; return true;
} }
...@@ -1277,6 +1281,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) ...@@ -1277,6 +1281,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
unsigned int i; unsigned int i;
dev_t dev; dev_t dev;
int err; int err;
bool bad_uuid = false;
/* fsid 0 is reserved for upper fs even with non upper overlay */ /* fsid 0 is reserved for upper fs even with non upper overlay */
if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb) if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb)
...@@ -1288,12 +1293,16 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) ...@@ -1288,12 +1293,16 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
} }
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) { if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) {
bad_uuid = true;
if (ofs->config.index || ofs->config.nfs_export) {
ofs->config.index = false; ofs->config.index = false;
ofs->config.nfs_export = 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", 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", uuid_is_null(&sb->s_uuid) ? "null" :
"conflicting",
path->dentry); path->dentry);
} }
}
err = get_anon_bdev(&dev); err = get_anon_bdev(&dev);
if (err) { if (err) {
...@@ -1303,6 +1312,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) ...@@ -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].sb = sb;
ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev; ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev;
ofs->lower_fs[ofs->numlowerfs].bad_uuid = bad_uuid;
ofs->numlowerfs++; ofs->numlowerfs++;
return ofs->numlowerfs; return ofs->numlowerfs;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment