Signed-off-by: Peter Zijlstra (Intel) <pet...@infradead.org> --- fs/debugfs/file.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/debugfs.h | 29 +++++++++ 2 files changed, 173 insertions(+)
--- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -865,6 +865,150 @@ struct dentry *debugfs_create_bool(const } EXPORT_SYMBOL_GPL(debugfs_create_bool); +ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry = F_DENTRY(file); + char *str, *copy = NULL; + int copy_len, len; + ssize_t ret; + + ret = debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + +again: + rcu_read_lock(); + str = rcu_dereference(*(char **)file->private_data); + len = strlen(str) + 1; + + if (!copy || copy_len < len) { + rcu_read_unlock(); + kfree(copy); + copy = kmalloc(len + 1, GFP_KERNEL); + if (!copy) { + debugfs_file_put(dentry); + return -ENOMEM; + } + copy_len = len; + goto again; + } + + strncpy(copy, str, len); + copy[len] = '\n'; + copy[len+1] = '\0'; + rcu_read_unlock(); + + debugfs_file_put(dentry); + + ret = simple_read_from_buffer(user_buf, count, ppos, copy, len + 1); + kfree(copy); + + return ret; +} +EXPORT_SYMBOL_GPL(debugfs_read_file_str); + +ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry = F_DENTRY(file); + char *old, *new = NULL; + int pos = *ppos; + int r; + + r = debugfs_file_get(dentry); + if (unlikely(r)) + return r; + + old = *(char **)file->private_data; + + /* only allow strict concattenation */ + r = -EINVAL; + if (pos && pos != strlen(old)) + goto error; + + r = -ENOMEM; + new = kmalloc(pos + count + 1, GFP_KERNEL); + if (!new) + goto error; + + if (pos) + memcpy(new, old, pos); + + r = -EFAULT; + if (copy_from_user(new + pos, user_buf, count)) + goto error; + + new[pos + count] = '\0'; + strim(new); + + rcu_assign_pointer(*(char **)file->private_data, new); + synchronize_rcu(); + kfree(old); + + debugfs_file_put(dentry); + return count; + +error: + kfree(new); + debugfs_file_put(dentry); + return r; +} +EXPORT_SYMBOL_GPL(debugfs_write_file_str); + +static const struct file_operations fops_str = { + .read = debugfs_read_file_str, + .write = debugfs_write_file_str, + .open = simple_open, + .llseek = default_llseek, +}; + +static const struct file_operations fops_str_ro = { + .read = debugfs_read_file_str, + .open = simple_open, + .llseek = default_llseek, +}; + +static const struct file_operations fops_str_wo = { + .write = debugfs_write_file_str, + .open = simple_open, + .llseek = default_llseek, +}; + +/** + * debugfs_create_str - create a debugfs file that is used to read and write a string value + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is %NULL, then the + * file will be created in the root of the debugfs filesystem. + * @value: a pointer to the variable that the file should read to and write + * from. + * + * This function creates a file in debugfs with the given name that + * contains the value of the variable @value. If the @mode variable is so + * set, it can be read from, and written to. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the debugfs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, ERR_PTR(-ERROR) will be + * returned. + * + * NOTE: when writing is enabled it will replace the string, string lifetime is + * assumed to be RCU managed. + * + * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will + * be returned. + */ +struct dentry *debugfs_create_str(const char *name, umode_t mode, + struct dentry *parent, char **value) +{ + return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_str, + &fops_str_ro, &fops_str_wo); +} +EXPORT_SYMBOL_GPL(debugfs_create_str); + static ssize_t read_file_blob(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -128,6 +128,8 @@ void debugfs_create_atomic_t(const char struct dentry *parent, atomic_t *value); struct dentry *debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, bool *value); +struct dentry *debugfs_create_str(const char *name, umode_t mode, + struct dentry *parent, char **value); struct dentry *debugfs_create_blob(const char *name, umode_t mode, struct dentry *parent, @@ -156,6 +158,12 @@ ssize_t debugfs_read_file_bool(struct fi ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos); +ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); + +ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos); + #else #include <linux/err.h> @@ -297,6 +305,13 @@ static inline struct dentry *debugfs_cre return ERR_PTR(-ENODEV); } +static inline struct dentry *debugfs_create_str(const char *name, umode_t mode, + struct dentry *parent, + char **value) +{ + return ERR_PTR(-ENODEV); +} + static inline struct dentry *debugfs_create_blob(const char *name, umode_t mode, struct dentry *parent, struct debugfs_blob_wrapper *blob) @@ -347,6 +362,20 @@ static inline ssize_t debugfs_write_file { return -ENODEV; } + +static inline ssize_t debugfs_read_file_str(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + return -ENODEV; +} + +static inline ssize_t debugfs_write_file_str(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + return -ENODEV; +} #endif