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
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;
|
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,
|
static inline void __d_set_inode_and_type(struct dentry *dentry,
|
||||||
struct inode *inode,
|
struct inode *inode,
|
||||||
unsigned type_flags)
|
unsigned type_flags)
|
||||||
|
@ -3598,6 +3625,11 @@ EXPORT_SYMBOL(d_genocide);
|
||||||
|
|
||||||
void __init vfs_caches_init_early(void)
|
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();
|
dcache_init_early();
|
||||||
inode_init_early();
|
inode_init_early();
|
||||||
}
|
}
|
||||||
|
|
|
@ -766,7 +766,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
struct dentry *dentry = NULL, *trap;
|
struct dentry *dentry = NULL, *trap;
|
||||||
const char *old_name;
|
struct name_snapshot old_name;
|
||||||
|
|
||||||
trap = lock_rename(new_dir, old_dir);
|
trap = lock_rename(new_dir, old_dir);
|
||||||
/* Source or destination directories don't exist? */
|
/* 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))
|
if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
|
||||||
goto exit;
|
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),
|
error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
|
||||||
dentry, 0);
|
dentry, 0);
|
||||||
if (error) {
|
if (error) {
|
||||||
fsnotify_oldname_free(old_name);
|
release_dentry_name_snapshot(&old_name);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
d_move(old_dentry, dentry);
|
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),
|
d_is_dir(old_dentry),
|
||||||
NULL, old_dentry);
|
NULL, old_dentry);
|
||||||
fsnotify_oldname_free(old_name);
|
release_dentry_name_snapshot(&old_name);
|
||||||
unlock_rename(new_dir, old_dir);
|
unlock_rename(new_dir, old_dir);
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
return old_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);
|
int len = dot ? dot - name : strlen(name);
|
||||||
|
|
||||||
fs = __get_fs_type(name, len);
|
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);
|
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)) {
|
if (dot && fs && !(fs->fs_flags & FS_HAS_SUBTYPE)) {
|
||||||
put_filesystem(fs);
|
put_filesystem(fs);
|
||||||
|
|
|
@ -2014,7 +2014,7 @@ bool inode_owner_or_capable(const struct inode *inode)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ns = current_user_ns();
|
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 true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ static inline int splice_branch(struct inode *inode,
|
||||||
return -EAGAIN;
|
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)
|
struct buffer_head *bh, int create)
|
||||||
{
|
{
|
||||||
int err = -EIO;
|
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,
|
/* Source inode owner (or CAP_FOWNER) can hardlink all they like,
|
||||||
* otherwise, it must be a safe source.
|
* 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;
|
return 0;
|
||||||
|
|
||||||
audit_log_link_denied("linkat", link);
|
audit_log_link_denied("linkat", link);
|
||||||
|
@ -4363,11 +4363,11 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
bool is_dir = d_is_dir(old_dentry);
|
bool is_dir = d_is_dir(old_dentry);
|
||||||
const unsigned char *old_name;
|
|
||||||
struct inode *source = old_dentry->d_inode;
|
struct inode *source = old_dentry->d_inode;
|
||||||
struct inode *target = new_dentry->d_inode;
|
struct inode *target = new_dentry->d_inode;
|
||||||
bool new_is_dir = false;
|
bool new_is_dir = false;
|
||||||
unsigned max_links = new_dir->i_sb->s_max_links;
|
unsigned max_links = new_dir->i_sb->s_max_links;
|
||||||
|
struct name_snapshot old_name;
|
||||||
|
|
||||||
if (source == target)
|
if (source == target)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4414,7 +4414,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
|
take_dentry_name_snapshot(&old_name, old_dentry);
|
||||||
dget(new_dentry);
|
dget(new_dentry);
|
||||||
if (!is_dir || (flags & RENAME_EXCHANGE))
|
if (!is_dir || (flags & RENAME_EXCHANGE))
|
||||||
lock_two_nondirectories(source, target);
|
lock_two_nondirectories(source, target);
|
||||||
|
@ -4469,14 +4469,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
inode_unlock(target);
|
inode_unlock(target);
|
||||||
dput(new_dentry);
|
dput(new_dentry);
|
||||||
if (!error) {
|
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);
|
!(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
|
||||||
if (flags & RENAME_EXCHANGE) {
|
if (flags & RENAME_EXCHANGE) {
|
||||||
fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
|
fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
|
||||||
new_is_dir, NULL, new_dentry);
|
new_is_dir, NULL, new_dentry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fsnotify_oldname_free(old_name);
|
release_dentry_name_snapshot(&old_name);
|
||||||
|
|
||||||
return error;
|
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)))
|
if (unlikely(!fsnotify_inode_watches_children(p_inode)))
|
||||||
__fsnotify_update_child_dentry_flags(p_inode);
|
__fsnotify_update_child_dentry_flags(p_inode);
|
||||||
else if (p_inode->i_fsnotify_mask & mask) {
|
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
|
/* we are notifying a parent so come up with the new mask which
|
||||||
* specifies these are events which came from a child. */
|
* specifies these are events which came from a child. */
|
||||||
mask |= FS_EVENT_ON_CHILD;
|
mask |= FS_EVENT_ON_CHILD;
|
||||||
|
|
||||||
|
take_dentry_name_snapshot(&name, dentry);
|
||||||
if (path)
|
if (path)
|
||||||
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
|
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
|
||||||
dentry->d_name.name, 0);
|
name.name, 0);
|
||||||
else
|
else
|
||||||
ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
|
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);
|
dput(parent);
|
||||||
|
|
|
@ -38,6 +38,8 @@ static int flags_by_sb(int s_flags)
|
||||||
flags |= ST_SYNCHRONOUS;
|
flags |= ST_SYNCHRONOUS;
|
||||||
if (s_flags & MS_MANDLOCK)
|
if (s_flags & MS_MANDLOCK)
|
||||||
flags |= ST_MANDLOCK;
|
flags |= ST_MANDLOCK;
|
||||||
|
if (s_flags & MS_RDONLY)
|
||||||
|
flags |= ST_RDONLY;
|
||||||
return flags;
|
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));
|
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 */
|
#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 */
|
#endif /* _LINUX_FS_NOTIFY_H */
|
||||||
|
|
Loading…
Reference in New Issue