summaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/kernfs')
-rw-r--r--fs/kernfs/dir.c6
-rw-r--r--fs/kernfs/file.c63
-rw-r--r--fs/kernfs/kernfs-internal.h1
-rw-r--r--fs/kernfs/mount.c10
4 files changed, 44 insertions, 36 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 939684e..78f3403 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 ddcb471..8034706 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 a91d7a1..8be13b2 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 e5b28b0..6a5f04a 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) {
OpenPOWER on IntegriCloud