Merge branch 'work.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull misc filesystem updates from Al Viro: "Assorted normal VFS / filesystems stuff..." * 'work.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: dentry name snapshots Make statfs properly return read-only state after emergency remount fs/dcache: init in_lookup_hashtable minix: Deinline get_block, save 2691 bytes fs: Reorder inode_owner_or_capable() to avoid needless fs: warn in case userspace lied about modprobe return
This commit is contained in:
commit
b8d4c1f9f4
10 changed files with 61 additions and 46 deletions
32
fs/dcache.c
32
fs/dcache.c
|
@ -277,6 +277,33 @@ static inline int dname_external(const struct dentry *dentry)
|
|||
return dentry->d_name.name != dentry->d_iname;
|
||||
}
|
||||
|
||||
void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (unlikely(dname_external(dentry))) {
|
||||
struct external_name *p = external_name(dentry);
|
||||
atomic_inc(&p->u.count);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
name->name = p->name;
|
||||
} else {
|
||||
memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
name->name = name->inline_name;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(take_dentry_name_snapshot);
|
||||
|
||||
void release_dentry_name_snapshot(struct name_snapshot *name)
|
||||
{
|
||||
if (unlikely(name->name != name->inline_name)) {
|
||||
struct external_name *p;
|
||||
p = container_of(name->name, struct external_name, name[0]);
|
||||
if (unlikely(atomic_dec_and_test(&p->u.count)))
|
||||
kfree_rcu(p, u.head);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(release_dentry_name_snapshot);
|
||||
|
||||
static inline void __d_set_inode_and_type(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
unsigned type_flags)
|
||||
|
@ -3598,6 +3625,11 @@ EXPORT_SYMBOL(d_genocide);
|
|||
|
||||
void __init vfs_caches_init_early(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(in_lookup_hashtable); i++)
|
||||
INIT_HLIST_BL_HEAD(&in_lookup_hashtable[i]);
|
||||
|
||||
dcache_init_early();
|
||||
inode_init_early();
|
||||
}
|
||||
|
|
|
@ -766,7 +766,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
|||
{
|
||||
int error;
|
||||
struct dentry *dentry = NULL, *trap;
|
||||
const char *old_name;
|
||||
struct name_snapshot old_name;
|
||||
|
||||
trap = lock_rename(new_dir, old_dir);
|
||||
/* Source or destination directories don't exist? */
|
||||
|
@ -781,19 +781,19 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
|||
if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
|
||||
goto exit;
|
||||
|
||||
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
|
||||
take_dentry_name_snapshot(&old_name, old_dentry);
|
||||
|
||||
error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
|
||||
dentry, 0);
|
||||
if (error) {
|
||||
fsnotify_oldname_free(old_name);
|
||||
release_dentry_name_snapshot(&old_name);
|
||||
goto exit;
|
||||
}
|
||||
d_move(old_dentry, dentry);
|
||||
fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name,
|
||||
fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
|
||||
d_is_dir(old_dentry),
|
||||
NULL, old_dentry);
|
||||
fsnotify_oldname_free(old_name);
|
||||
release_dentry_name_snapshot(&old_name);
|
||||
unlock_rename(new_dir, old_dir);
|
||||
dput(dentry);
|
||||
return old_dentry;
|
||||
|
|
|
@ -275,8 +275,10 @@ struct file_system_type *get_fs_type(const char *name)
|
|||
int len = dot ? dot - name : strlen(name);
|
||||
|
||||
fs = __get_fs_type(name, len);
|
||||
if (!fs && (request_module("fs-%.*s", len, name) == 0))
|
||||
if (!fs && (request_module("fs-%.*s", len, name) == 0)) {
|
||||
fs = __get_fs_type(name, len);
|
||||
WARN_ONCE(!fs, "request_module fs-%.*s succeeded, but still no fs?\n", len, name);
|
||||
}
|
||||
|
||||
if (dot && fs && !(fs->fs_flags & FS_HAS_SUBTYPE)) {
|
||||
put_filesystem(fs);
|
||||
|
|
|
@ -2014,7 +2014,7 @@ bool inode_owner_or_capable(const struct inode *inode)
|
|||
return true;
|
||||
|
||||
ns = current_user_ns();
|
||||
if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid))
|
||||
if (kuid_has_mapping(ns, inode->i_uid) && ns_capable(ns, CAP_FOWNER))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ static inline int splice_branch(struct inode *inode,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static inline int get_block(struct inode * inode, sector_t block,
|
||||
static int get_block(struct inode * inode, sector_t block,
|
||||
struct buffer_head *bh, int create)
|
||||
{
|
||||
int err = -EIO;
|
||||
|
|
10
fs/namei.c
10
fs/namei.c
|
@ -1008,7 +1008,7 @@ static int may_linkat(struct path *link)
|
|||
/* Source inode owner (or CAP_FOWNER) can hardlink all they like,
|
||||
* otherwise, it must be a safe source.
|
||||
*/
|
||||
if (inode_owner_or_capable(inode) || safe_hardlink_source(inode))
|
||||
if (safe_hardlink_source(inode) || inode_owner_or_capable(inode))
|
||||
return 0;
|
||||
|
||||
audit_log_link_denied("linkat", link);
|
||||
|
@ -4363,11 +4363,11 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
{
|
||||
int error;
|
||||
bool is_dir = d_is_dir(old_dentry);
|
||||
const unsigned char *old_name;
|
||||
struct inode *source = old_dentry->d_inode;
|
||||
struct inode *target = new_dentry->d_inode;
|
||||
bool new_is_dir = false;
|
||||
unsigned max_links = new_dir->i_sb->s_max_links;
|
||||
struct name_snapshot old_name;
|
||||
|
||||
if (source == target)
|
||||
return 0;
|
||||
|
@ -4414,7 +4414,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
|
||||
take_dentry_name_snapshot(&old_name, old_dentry);
|
||||
dget(new_dentry);
|
||||
if (!is_dir || (flags & RENAME_EXCHANGE))
|
||||
lock_two_nondirectories(source, target);
|
||||
|
@ -4469,14 +4469,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
inode_unlock(target);
|
||||
dput(new_dentry);
|
||||
if (!error) {
|
||||
fsnotify_move(old_dir, new_dir, old_name, is_dir,
|
||||
fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
|
||||
!(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
|
||||
if (flags & RENAME_EXCHANGE) {
|
||||
fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
|
||||
new_is_dir, NULL, new_dentry);
|
||||
}
|
||||
}
|
||||
fsnotify_oldname_free(old_name);
|
||||
release_dentry_name_snapshot(&old_name);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -161,16 +161,20 @@ int __fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask
|
|||
if (unlikely(!fsnotify_inode_watches_children(p_inode)))
|
||||
__fsnotify_update_child_dentry_flags(p_inode);
|
||||
else if (p_inode->i_fsnotify_mask & mask) {
|
||||
struct name_snapshot name;
|
||||
|
||||
/* we are notifying a parent so come up with the new mask which
|
||||
* specifies these are events which came from a child. */
|
||||
mask |= FS_EVENT_ON_CHILD;
|
||||
|
||||
take_dentry_name_snapshot(&name, dentry);
|
||||
if (path)
|
||||
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
|
||||
dentry->d_name.name, 0);
|
||||
name.name, 0);
|
||||
else
|
||||
ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
|
||||
dentry->d_name.name, 0);
|
||||
name.name, 0);
|
||||
release_dentry_name_snapshot(&name);
|
||||
}
|
||||
|
||||
dput(parent);
|
||||
|
|
|
@ -38,6 +38,8 @@ static int flags_by_sb(int s_flags)
|
|||
flags |= ST_SYNCHRONOUS;
|
||||
if (s_flags & MS_MANDLOCK)
|
||||
flags |= ST_MANDLOCK;
|
||||
if (s_flags & MS_RDONLY)
|
||||
flags |= ST_RDONLY;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
|
@ -591,5 +591,11 @@ static inline struct inode *d_real_inode(const struct dentry *dentry)
|
|||
return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0));
|
||||
}
|
||||
|
||||
struct name_snapshot {
|
||||
const char *name;
|
||||
char inline_name[DNAME_INLINE_LEN];
|
||||
};
|
||||
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
||||
void release_dentry_name_snapshot(struct name_snapshot *);
|
||||
|
||||
#endif /* __LINUX_DCACHE_H */
|
||||
|
|
|
@ -293,35 +293,4 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FSNOTIFY) /* notify helpers */
|
||||
|
||||
/*
|
||||
* fsnotify_oldname_init - save off the old filename before we change it
|
||||
*/
|
||||
static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name)
|
||||
{
|
||||
return kstrdup(name, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/*
|
||||
* fsnotify_oldname_free - free the name we got from fsnotify_oldname_init
|
||||
*/
|
||||
static inline void fsnotify_oldname_free(const unsigned char *old_name)
|
||||
{
|
||||
kfree(old_name);
|
||||
}
|
||||
|
||||
#else /* CONFIG_FSNOTIFY */
|
||||
|
||||
static inline const char *fsnotify_oldname_init(const unsigned char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void fsnotify_oldname_free(const unsigned char *old_name)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FSNOTIFY */
|
||||
|
||||
#endif /* _LINUX_FS_NOTIFY_H */
|
||||
|
|
Loading…
Reference in a new issue