diff options
Diffstat (limited to 'fs/kernfs')
-rw-r--r-- | fs/kernfs/dir.c | 6 | ||||
-rw-r--r-- | fs/kernfs/file.c | 63 | ||||
-rw-r--r-- | fs/kernfs/kernfs-internal.h | 1 | ||||
-rw-r--r-- | fs/kernfs/mount.c | 10 |
4 files changed, 44 insertions, 36 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 939684ebff1e..78f3403300af 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -191,7 +191,7 @@ static unsigned int kernfs_name_hash(const char *name, const void *ns) hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31)); hash &= 0x7fffffffU; /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ - if (hash < 1) + if (hash < 2) hash += 2; if (hash >= INT_MAX) hash = INT_MAX - 1; @@ -501,7 +501,7 @@ const struct dentry_operations kernfs_dops = { */ struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry) { - if (dentry->d_op == &kernfs_dops) + if (dentry->d_sb->s_op == &kernfs_sops) return dentry->d_fsdata; return NULL; } @@ -1296,7 +1296,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, spin_unlock_irq(&kernfs_rename_lock); - kn->hash = kernfs_name_hash(new_name, new_ns); + kn->hash = kernfs_name_hash(kn->name, kn->ns); kernfs_link_sibling(kn); kernfs_put(old_parent); diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index ddcb471b9cc9..8034706a7af8 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -253,55 +253,50 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, { struct kernfs_open_file *of = kernfs_of(file); const struct kernfs_ops *ops; - char *buf = NULL; - ssize_t len; - - /* - * @of->mutex nests outside active ref and is just to ensure that - * the ops aren't called concurrently for the same open file. - */ - mutex_lock(&of->mutex); - if (!kernfs_get_active(of->kn)) { - mutex_unlock(&of->mutex); - return -ENODEV; - } - - ops = kernfs_ops(of->kn); - if (!ops->write) { - len = -EINVAL; - goto out_unlock; - } + size_t len; + char *buf; - if (ops->atomic_write_len) { + if (of->atomic_write_len) { len = count; - if (len > ops->atomic_write_len) { - len = -E2BIG; - goto out_unlock; - } + if (len > of->atomic_write_len) + return -E2BIG; } else { len = min_t(size_t, count, PAGE_SIZE); } buf = kmalloc(len + 1, GFP_KERNEL); - if (!buf) { - len = -ENOMEM; - goto out_unlock; - } + if (!buf) + return -ENOMEM; if (copy_from_user(buf, user_buf, len)) { len = -EFAULT; - goto out_unlock; + goto out_free; } buf[len] = '\0'; /* guarantee string termination */ - len = ops->write(of, buf, len, *ppos); -out_unlock: + /* + * @of->mutex nests outside active ref and is just to ensure that + * the ops aren't called concurrently for the same open file. + */ + mutex_lock(&of->mutex); + if (!kernfs_get_active(of->kn)) { + mutex_unlock(&of->mutex); + len = -ENODEV; + goto out_free; + } + + ops = kernfs_ops(of->kn); + if (ops->write) + len = ops->write(of, buf, len, *ppos); + else + len = -EINVAL; + kernfs_put_active(of->kn); mutex_unlock(&of->mutex); if (len > 0) *ppos += len; - +out_free: kfree(buf); return len; } @@ -666,6 +661,12 @@ static int kernfs_fop_open(struct inode *inode, struct file *file) of->file = file; /* + * Write path needs to atomic_write_len outside active reference. + * Cache it in open_file. See kernfs_fop_write() for details. + */ + of->atomic_write_len = ops->atomic_write_len; + + /* * Always instantiate seq_file even if read access doesn't use * seq_file or is not requested. This unifies private data access * and readable regular files are the vast majority anyway. diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index a91d7a1113d9..8be13b2a079b 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -65,6 +65,7 @@ struct kernfs_super_info { }; #define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info)) +extern const struct super_operations kernfs_sops; extern struct kmem_cache *kernfs_node_cache; /* diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index e5b28b0ebc37..6a5f04ac8704 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -39,7 +39,7 @@ static int kernfs_sop_show_options(struct seq_file *sf, struct dentry *dentry) return 0; } -static const struct super_operations kernfs_sops = { +const struct super_operations kernfs_sops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, .evict_inode = kernfs_evict_inode, @@ -131,6 +131,7 @@ const void *kernfs_super_ns(struct super_block *sb) * @fs_type: file_system_type of the fs being mounted * @flags: mount flags specified for the mount * @root: kernfs_root of the hierarchy being mounted + * @new_sb_created: tell the caller if we allocated a new superblock * @ns: optional namespace tag of the mount * * This is to be called from each kernfs user's file_system_type->mount() @@ -141,7 +142,8 @@ const void *kernfs_super_ns(struct super_block *sb) * The return value can be passed to the vfs layer verbatim. */ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, - struct kernfs_root *root, const void *ns) + struct kernfs_root *root, bool *new_sb_created, + const void *ns) { struct super_block *sb; struct kernfs_super_info *info; @@ -159,6 +161,10 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, kfree(info); if (IS_ERR(sb)) return ERR_CAST(sb); + + if (new_sb_created) + *new_sb_created = !sb->s_root; + if (!sb->s_root) { error = kernfs_fill_super(sb); if (error) { |