Skip to content

Commit

Permalink
Merge tag 'ovl-update-6.2' of git://git.kernel.org/pub/scm/linux/kern…
Browse files Browse the repository at this point in the history
…el/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
  • Loading branch information
Linus Torvalds committed Dec 13, 2022
2 parents 4a6bff1 + 637d13b commit 6df7cc2
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 67 deletions.
2 changes: 1 addition & 1 deletion fs/overlayfs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
46 changes: 30 additions & 16 deletions fs/overlayfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions fs/overlayfs/export.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 2 additions & 1 deletion fs/overlayfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 9 additions & 3 deletions fs/overlayfs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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))
Expand Down Expand Up @@ -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);
/*
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 6 additions & 5 deletions fs/overlayfs/overlayfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down
58 changes: 26 additions & 32 deletions fs/overlayfs/readdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion fs/overlayfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading

0 comments on commit 6df7cc2

Please sign in to comment.