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

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
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 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
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 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
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;
......
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