overlayfs update for 6.2

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCY5b3+gAKCRDh3BK/laaZ
 PIPxAQCPgyV/X/yJFd3wVgKa3/JxcHl5qdPbwHXFuYiJCBd69QEA9LYQEeEoTLCY
 veGiQPkl6Sp8ZqmTbDBxqw5OaBTSMwM=
 =7TiE
 -----END PGP SIGNATURE-----

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

Pull overlayfs update from Miklos Szeredi:

 - Fix a couple of bugs found by syzbot

 - Don't ingore some open flags set by fcntl(F_SETFL)

 - Fix failure to create a hard link in certain cases

 - Use type safe helpers for some mnt_userns transformations

 - Improve performance of mount

 - Misc cleanups

* tag 'ovl-update-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: Kconfig: Fix spelling mistake "undelying" -> "underlying"
  ovl: use inode instead of dentry where possible
  ovl: Add comment on upperredirect reassignment
  ovl: use plain list filler in indexdir and workdir cleanup
  ovl: do not reconnect upper index records in ovl_indexdir_cleanup()
  ovl: fix comment typos
  ovl: port to vfs{g,u}id_t and associated helpers
  ovl: Use ovl mounter's fsuid and fsgid in ovl_link()
  ovl: Use "buf" flexible array for memcpy() destination
  ovl: update ->f_iocb_flags when ovl_change_flags() modifies ->f_flags
  ovl: fix use inode directly in rcu-walk mode
This commit is contained in:
Linus Torvalds 2022-12-12 20:18:26 -08:00
commit 6df7cc2268
9 changed files with 86 additions and 67 deletions

View file

@ -96,7 +96,7 @@ config OVERLAY_FS_XINO_AUTO
depends on 64BIT
help
If this config option is enabled then overlay filesystems will use
unused high bits in undelying filesystem inode numbers to map all
unused high bits in underlying filesystem inode numbers to map all
inodes to a unified address space. The mapped 64bit inode numbers
might not be compatible with applications that expect 32bit inodes.

View file

@ -576,28 +576,42 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
goto out_revert_creds;
}
err = -ENOMEM;
override_cred = prepare_creds();
if (override_cred) {
if (!attr->hardlink) {
err = -ENOMEM;
override_cred = prepare_creds();
if (!override_cred)
goto out_revert_creds;
/*
* In the creation cases(create, mkdir, mknod, symlink),
* ovl should transfer current's fs{u,g}id to underlying
* fs. Because underlying fs want to initialize its new
* inode owner using current's fs{u,g}id. And in this
* case, the @inode is a new inode that is initialized
* in inode_init_owner() to current's fs{u,g}id. So use
* the inode's i_{u,g}id to override the cred's fs{u,g}id.
*
* But in the other hardlink case, ovl_link() does not
* create a new inode, so just use the ovl mounter's
* fs{u,g}id.
*/
override_cred->fsuid = inode->i_uid;
override_cred->fsgid = inode->i_gid;
if (!attr->hardlink) {
err = security_dentry_create_files_as(dentry,
attr->mode, &dentry->d_name, old_cred,
override_cred);
if (err) {
put_cred(override_cred);
goto out_revert_creds;
}
err = security_dentry_create_files_as(dentry,
attr->mode, &dentry->d_name, old_cred,
override_cred);
if (err) {
put_cred(override_cred);
goto out_revert_creds;
}
put_cred(override_creds(override_cred));
put_cred(override_cred);
if (!ovl_dentry_is_whiteout(dentry))
err = ovl_create_upper(dentry, inode, attr);
else
err = ovl_create_over_whiteout(dentry, inode, attr);
}
if (!ovl_dentry_is_whiteout(dentry))
err = ovl_create_upper(dentry, inode, attr);
else
err = ovl_create_over_whiteout(dentry, inode, attr);
out_revert_creds:
revert_creds(old_cred);
return err;

View file

@ -339,7 +339,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
return dentry;
}
/* Get the upper or lower dentry in stach whose on layer @idx */
/* Get the upper or lower dentry in stack whose on layer @idx */
static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx)
{
struct ovl_entry *oe = dentry->d_fsdata;
@ -463,7 +463,7 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb,
/* Get connected upper overlay dir from index */
if (index) {
struct dentry *upper = ovl_index_upper(ofs, index);
struct dentry *upper = ovl_index_upper(ofs, index, true);
dput(index);
if (IS_ERR_OR_NULL(upper))
@ -739,7 +739,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
/* Then try to get a connected upper dir by index */
if (index && d_is_dir(index)) {
struct dentry *upper = ovl_index_upper(ofs, index);
struct dentry *upper = ovl_index_upper(ofs, index, true);
err = PTR_ERR(upper);
if (IS_ERR_OR_NULL(upper))
@ -796,7 +796,7 @@ static struct ovl_fh *ovl_fid_to_fh(struct fid *fid, int buflen, int fh_type)
return ERR_PTR(-ENOMEM);
/* Copy unaligned inner fh into aligned buffer */
memcpy(&fh->fb, fid, buflen - OVL_FH_WIRE_OFFSET);
memcpy(fh->buf, fid, buflen - OVL_FH_WIRE_OFFSET);
return fh;
}

View file

@ -34,7 +34,7 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode)
return 'm';
}
/* No atime modificaton nor notify on underlying */
/* No atime modification nor notify on underlying */
#define OVL_OPEN_FLAGS (O_NOATIME | FMODE_NONOTIFY)
static struct file *ovl_open_realfile(const struct file *file,
@ -96,6 +96,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
spin_lock(&file->f_lock);
file->f_flags = (file->f_flags & ~OVL_SETFL_MASK) | flags;
file->f_iocb_flags = iocb_flags(file);
spin_unlock(&file->f_lock);
return 0;

View file

@ -487,7 +487,8 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
}
/* Get upper dentry from index */
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index,
bool connected)
{
struct ovl_fh *fh;
struct dentry *upper;
@ -499,7 +500,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
if (IS_ERR_OR_NULL(fh))
return ERR_CAST(fh);
upper = ovl_decode_real_fh(ofs, fh, ovl_upper_mnt(ofs), true);
upper = ovl_decode_real_fh(ofs, fh, ovl_upper_mnt(ofs), connected);
kfree(fh);
if (IS_ERR_OR_NULL(upper))
@ -572,7 +573,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
* directly from the index dentry, but for dir index we first need to
* decode the upper directory.
*/
upper = ovl_index_upper(ofs, index);
upper = ovl_index_upper(ofs, index, false);
if (IS_ERR_OR_NULL(upper)) {
err = PTR_ERR(upper);
/*
@ -1085,6 +1086,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
.mnt = ovl_upper_mnt(ofs),
};
/*
* It's safe to assign upperredirect here: the previous
* assignment of happens only if upperdentry is non-NULL, and
* this one only if upperdentry is NULL.
*/
upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
if (IS_ERR(upperredirect)) {
err = PTR_ERR(upperredirect);

View file

@ -110,7 +110,7 @@ struct ovl_fh {
u8 padding[3]; /* make sure fb.fid is 32bit aligned */
union {
struct ovl_fb fb;
u8 buf[0];
DECLARE_FLEX_ARRAY(u8, buf);
};
} __packed;
@ -415,7 +415,7 @@ const char *ovl_dentry_get_redirect(struct dentry *dentry);
void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
void ovl_dir_modified(struct dentry *dentry, bool impurity);
u64 ovl_dentry_version_get(struct dentry *dentry);
u64 ovl_inode_version_get(struct inode *inode);
bool ovl_is_whiteout(struct dentry *dentry);
struct file *ovl_path_open(const struct path *path, int flags);
int ovl_copy_up_start(struct dentry *dentry, int flags);
@ -539,7 +539,8 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real, bool is_upper,
bool set);
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index);
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index,
bool connected);
int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index);
int ovl_get_index_name(struct ovl_fs *ofs, struct dentry *origin,
struct qstr *name);
@ -584,9 +585,9 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs);
* lower dir was removed under it and possibly before it was rotated from upper
* to lower layer.
*/
static inline bool ovl_dir_is_real(struct dentry *dir)
static inline bool ovl_dir_is_real(struct inode *dir)
{
return !ovl_test_flag(OVL_WHITEOUTS, d_inode(dir));
return !ovl_test_flag(OVL_WHITEOUTS, dir);
}
/* inode.c */

View file

@ -235,15 +235,15 @@ void ovl_dir_cache_free(struct inode *inode)
}
}
static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry)
static void ovl_cache_put(struct ovl_dir_file *od, struct inode *inode)
{
struct ovl_dir_cache *cache = od->cache;
WARN_ON(cache->refcount <= 0);
cache->refcount--;
if (!cache->refcount) {
if (ovl_dir_cache(d_inode(dentry)) == cache)
ovl_set_dir_cache(d_inode(dentry), NULL);
if (ovl_dir_cache(inode) == cache)
ovl_set_dir_cache(inode, NULL);
ovl_cache_free(&cache->entries);
kfree(cache);
@ -323,15 +323,15 @@ static void ovl_dir_reset(struct file *file)
{
struct ovl_dir_file *od = file->private_data;
struct ovl_dir_cache *cache = od->cache;
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = file_inode(file);
bool is_real;
if (cache && ovl_dentry_version_get(dentry) != cache->version) {
ovl_cache_put(od, dentry);
if (cache && ovl_inode_version_get(inode) != cache->version) {
ovl_cache_put(od, inode);
od->cache = NULL;
od->cursor = NULL;
}
is_real = ovl_dir_is_real(dentry);
is_real = ovl_dir_is_real(inode);
if (od->is_real != is_real) {
/* is_real can only become false when dir is copied up */
if (WARN_ON(is_real))
@ -394,9 +394,10 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
{
int res;
struct ovl_dir_cache *cache;
struct inode *inode = d_inode(dentry);
cache = ovl_dir_cache(d_inode(dentry));
if (cache && ovl_dentry_version_get(dentry) == cache->version) {
cache = ovl_dir_cache(inode);
if (cache && ovl_inode_version_get(inode) == cache->version) {
WARN_ON(!cache->refcount);
cache->refcount++;
return cache;
@ -418,8 +419,8 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
return ERR_PTR(res);
}
cache->version = ovl_dentry_version_get(dentry);
ovl_set_dir_cache(d_inode(dentry), cache);
cache->version = ovl_inode_version_get(inode);
ovl_set_dir_cache(inode, cache);
return cache;
}
@ -596,16 +597,17 @@ static struct ovl_dir_cache *ovl_cache_get_impure(const struct path *path)
{
int res;
struct dentry *dentry = path->dentry;
struct inode *inode = d_inode(dentry);
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct ovl_dir_cache *cache;
cache = ovl_dir_cache(d_inode(dentry));
if (cache && ovl_dentry_version_get(dentry) == cache->version)
cache = ovl_dir_cache(inode);
if (cache && ovl_inode_version_get(inode) == cache->version)
return cache;
/* Impure cache is not refcounted, free it here */
ovl_dir_cache_free(d_inode(dentry));
ovl_set_dir_cache(d_inode(dentry), NULL);
ovl_dir_cache_free(inode);
ovl_set_dir_cache(inode, NULL);
cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);
if (!cache)
@ -627,13 +629,13 @@ static struct ovl_dir_cache *ovl_cache_get_impure(const struct path *path)
OVL_XATTR_IMPURE);
ovl_drop_write(dentry);
}
ovl_clear_flag(OVL_IMPURE, d_inode(dentry));
ovl_clear_flag(OVL_IMPURE, inode);
kfree(cache);
return NULL;
}
cache->version = ovl_dentry_version_get(dentry);
ovl_set_dir_cache(d_inode(dentry), cache);
cache->version = ovl_inode_version_get(inode);
ovl_set_dir_cache(inode, cache);
return cache;
}
@ -675,7 +677,7 @@ static bool ovl_fill_real(struct dir_context *ctx, const char *name,
static bool ovl_is_impure_dir(struct file *file)
{
struct ovl_dir_file *od = file->private_data;
struct inode *dir = d_inode(file->f_path.dentry);
struct inode *dir = file_inode(file);
/*
* Only upper dir can be impure, but if we are in the middle of
@ -893,7 +895,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
struct file *realfile;
int err;
err = ovl_sync_status(OVL_FS(file->f_path.dentry->d_sb));
err = ovl_sync_status(OVL_FS(file_inode(file)->i_sb));
if (err <= 0)
return err;
@ -913,7 +915,7 @@ static int ovl_dir_release(struct inode *inode, struct file *file)
if (od->cache) {
inode_lock(inode);
ovl_cache_put(od, file->f_path.dentry);
ovl_cache_put(od, inode);
inode_unlock(inode);
}
fput(od->realfile);
@ -942,7 +944,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
return PTR_ERR(realfile);
}
od->realfile = realfile;
od->is_real = ovl_dir_is_real(file->f_path.dentry);
od->is_real = ovl_dir_is_real(inode);
od->is_upper = OVL_TYPE_UPPER(type);
file->private_data = od;
@ -1071,14 +1073,10 @@ static int ovl_workdir_cleanup_recurse(struct ovl_fs *ofs, const struct path *pa
int err;
struct inode *dir = path->dentry->d_inode;
LIST_HEAD(list);
struct rb_root root = RB_ROOT;
struct ovl_cache_entry *p;
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge,
.dentry = NULL,
.ctx.actor = ovl_fill_plain,
.list = &list,
.root = &root,
.is_lowest = false,
};
bool incompat = false;
@ -1159,14 +1157,10 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
struct inode *dir = indexdir->d_inode;
struct path path = { .mnt = ovl_upper_mnt(ofs), .dentry = indexdir };
LIST_HEAD(list);
struct rb_root root = RB_ROOT;
struct ovl_cache_entry *p;
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge,
.dentry = NULL,
.ctx.actor = ovl_fill_plain,
.list = &list,
.root = &root,
.is_lowest = false,
};
err = ovl_dir_read(&path, &rdd);

View file

@ -139,11 +139,16 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry,
unsigned int flags, bool weak)
{
struct ovl_entry *oe = dentry->d_fsdata;
struct inode *inode = d_inode_rcu(dentry);
struct dentry *upper;
unsigned int i;
int ret = 1;
upper = ovl_dentry_upper(dentry);
/* Careful in RCU mode */
if (!inode)
return -ECHILD;
upper = ovl_i_dentry_upper(inode);
if (upper)
ret = ovl_revalidate_real(upper, flags, weak);

View file

@ -463,7 +463,7 @@ static void ovl_dir_version_inc(struct dentry *dentry, bool impurity)
* which have been copied up and have origins), so only need to note
* changes to impure entries.
*/
if (!ovl_dir_is_real(dentry) || impurity)
if (!ovl_dir_is_real(inode) || impurity)
OVL_I(inode)->version++;
}
@ -475,10 +475,8 @@ void ovl_dir_modified(struct dentry *dentry, bool impurity)
ovl_dir_version_inc(dentry, impurity);
}
u64 ovl_dentry_version_get(struct dentry *dentry)
u64 ovl_inode_version_get(struct inode *inode)
{
struct inode *inode = d_inode(dentry);
WARN_ON(!inode_is_locked(inode));
return OVL_I(inode)->version;
}