Commit b8d4c1f9 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 090a81d8 49d31c2f
...@@ -277,6 +277,33 @@ static inline int dname_external(const struct dentry *dentry) ...@@ -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); ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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;
......
...@@ -1008,7 +1008,7 @@ static int may_linkat(struct path *link) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 */
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