diff options
Diffstat (limited to 'fs/sysfs/file.c')
| -rw-r--r-- | fs/sysfs/file.c | 797 |
1 files changed, 347 insertions, 450 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index cf3786625bf..e9ef59b3abb 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -1,559 +1,456 @@ /* - * file.c - operations for regular (text) files. + * fs/sysfs/file.c - sysfs regular (text) file implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007 Tejun Heo <teheo@suse.de> + * + * This file is released under the GPLv2. + * + * Please see Documentation/filesystems/sysfs.txt for more information. */ #include <linux/module.h> -#include <linux/fsnotify.h> #include <linux/kobject.h> -#include <linux/namei.h> -#include <linux/poll.h> -#include <asm/uaccess.h> -#include <asm/semaphore.h> +#include <linux/kallsyms.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/seq_file.h> #include "sysfs.h" - -#define to_subsys(k) container_of(k,struct subsystem,kset.kobj) -#define to_sattr(a) container_of(a,struct subsys_attribute,attr) +#include "../kernfs/kernfs-internal.h" /* - * Subsystem file operations. - * These operations allow subsystems to have files that can be - * read/written. + * Determine ktype->sysfs_ops for the given kernfs_node. This function + * must be called while holding an active reference. */ -static ssize_t -subsys_attr_show(struct kobject * kobj, struct attribute * attr, char * page) +static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) { - struct subsystem * s = to_subsys(kobj); - struct subsys_attribute * sattr = to_sattr(attr); - ssize_t ret = -EIO; + struct kobject *kobj = kn->parent->priv; - if (sattr->show) - ret = sattr->show(s,page); - return ret; + if (kn->flags & KERNFS_LOCKDEP) + lockdep_assert_held(kn); + return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; } -static ssize_t -subsys_attr_store(struct kobject * kobj, struct attribute * attr, - const char * page, size_t count) +/* + * Reads on sysfs are handled through seq_file, which takes care of hairy + * details like buffering and seeking. The following function pipes + * sysfs_ops->show() result through seq_file. + */ +static int sysfs_kf_seq_show(struct seq_file *sf, void *v) { - struct subsystem * s = to_subsys(kobj); - struct subsys_attribute * sattr = to_sattr(attr); - ssize_t ret = -EIO; + struct kernfs_open_file *of = sf->private; + struct kobject *kobj = of->kn->parent->priv; + const struct sysfs_ops *ops = sysfs_file_ops(of->kn); + ssize_t count; + char *buf; - if (sattr->store) - ret = sattr->store(s,page,count); - return ret; -} + /* acquire buffer and ensure that it's >= PAGE_SIZE and clear */ + count = seq_get_buf(sf, &buf); + if (count < PAGE_SIZE) { + seq_commit(sf, -1); + return 0; + } + memset(buf, 0, PAGE_SIZE); -static struct sysfs_ops subsys_sysfs_ops = { - .show = subsys_attr_show, - .store = subsys_attr_store, -}; + /* + * Invoke show(). Control may reach here via seq file lseek even + * if @ops->show() isn't implemented. + */ + if (ops->show) { + count = ops->show(kobj, of->kn->priv, buf); + if (count < 0) + return count; + } + /* + * The code works fine with PAGE_SIZE return but it's likely to + * indicate truncated result or overflow in normal use cases. + */ + if (count >= (ssize_t)PAGE_SIZE) { + print_symbol("fill_read_buffer: %s returned bad count\n", + (unsigned long)ops->show); + /* Try to struggle along */ + count = PAGE_SIZE - 1; + } + seq_commit(sf, count); + return 0; +} -struct sysfs_buffer { - size_t count; - loff_t pos; - char * page; - struct sysfs_ops * ops; - struct semaphore sem; - int needs_read_fill; - int event; -}; +static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf, + size_t count, loff_t pos) +{ + struct bin_attribute *battr = of->kn->priv; + struct kobject *kobj = of->kn->parent->priv; + loff_t size = file_inode(of->file)->i_size; + if (!count) + return 0; -/** - * fill_read_buffer - allocate and fill buffer from object. - * @dentry: dentry pointer. - * @buffer: data buffer for file. - * - * Allocate @buffer->page, if it hasn't been already, then call the - * kobject's show() method to fill the buffer with this attribute's - * data. - * This is called only once, on the file's first read. - */ -static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) -{ - struct sysfs_dirent * sd = dentry->d_fsdata; - struct attribute * attr = to_attr(dentry); - struct kobject * kobj = to_kobj(dentry->d_parent); - struct sysfs_ops * ops = buffer->ops; - int ret = 0; - ssize_t count; + if (size) { + if (pos > size) + return 0; + if (pos + count > size) + count = size - pos; + } - if (!buffer->page) - buffer->page = (char *) get_zeroed_page(GFP_KERNEL); - if (!buffer->page) - return -ENOMEM; - - buffer->event = atomic_read(&sd->s_event); - count = ops->show(kobj,attr,buffer->page); - buffer->needs_read_fill = 0; - BUG_ON(count > (ssize_t)PAGE_SIZE); - if (count >= 0) - buffer->count = count; - else - ret = count; - return ret; -} + if (!battr->read) + return -EIO; + return battr->read(of->file, kobj, battr, buf, pos, count); +} -/** - * flush_read_buffer - push buffer to userspace. - * @buffer: data buffer for file. - * @buf: user-passed buffer. - * @count: number of bytes requested. - * @ppos: file position. - * - * Copy the buffer we filled in fill_read_buffer() to userspace. - * This is done at the reader's leisure, copying and advancing - * the amount they specify each time. - * This may be called continuously until the buffer is empty. - */ -static int flush_read_buffer(struct sysfs_buffer * buffer, char __user * buf, - size_t count, loff_t * ppos) +/* kernfs write callback for regular sysfs files */ +static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf, + size_t count, loff_t pos) { - int error; + const struct sysfs_ops *ops = sysfs_file_ops(of->kn); + struct kobject *kobj = of->kn->parent->priv; - if (*ppos > buffer->count) + if (!count) return 0; - if (count > (buffer->count - *ppos)) - count = buffer->count - *ppos; - - error = copy_to_user(buf,buffer->page + *ppos,count); - if (!error) - *ppos += count; - return error ? -EFAULT : count; + return ops->store(kobj, of->kn->priv, buf, count); } -/** - * sysfs_read_file - read an attribute. - * @file: file pointer. - * @buf: buffer to fill. - * @count: number of bytes to read. - * @ppos: starting offset in file. - * - * Userspace wants to read an attribute file. The attribute descriptor - * is in the file's ->d_fsdata. The target object is in the directory's - * ->d_fsdata. - * - * We call fill_read_buffer() to allocate and fill the buffer from the - * object's show() method exactly once (if the read is happening from - * the beginning of the file). That should fill the entire buffer with - * all the data the object has to offer for that attribute. - * We then call flush_read_buffer() to copy the buffer to userspace - * in the increments specified. - */ - -static ssize_t -sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) +/* kernfs write callback for bin sysfs files */ +static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf, + size_t count, loff_t pos) { - struct sysfs_buffer * buffer = file->private_data; - ssize_t retval = 0; - - down(&buffer->sem); - if (buffer->needs_read_fill) { - if ((retval = fill_read_buffer(file->f_dentry,buffer))) - goto out; + struct bin_attribute *battr = of->kn->priv; + struct kobject *kobj = of->kn->parent->priv; + loff_t size = file_inode(of->file)->i_size; + + if (size) { + if (size <= pos) + return 0; + count = min_t(ssize_t, count, size - pos); } - pr_debug("%s: count = %d, ppos = %lld, buf = %s\n", - __FUNCTION__,count,*ppos,buffer->page); - retval = flush_read_buffer(buffer,buf,count,ppos); -out: - up(&buffer->sem); - return retval; -} + if (!count) + return 0; + if (!battr->write) + return -EIO; -/** - * fill_write_buffer - copy buffer from userspace. - * @buffer: data buffer for file. - * @buf: data from user. - * @count: number of bytes in @userbuf. - * - * Allocate @buffer->page if it hasn't been already, then - * copy the user-supplied buffer into it. - */ + return battr->write(of->file, kobj, battr, buf, pos, count); +} -static int -fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t count) +static int sysfs_kf_bin_mmap(struct kernfs_open_file *of, + struct vm_area_struct *vma) { - int error; - - if (!buffer->page) - buffer->page = (char *)get_zeroed_page(GFP_KERNEL); - if (!buffer->page) - return -ENOMEM; + struct bin_attribute *battr = of->kn->priv; + struct kobject *kobj = of->kn->parent->priv; - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; - error = copy_from_user(buffer->page,buf,count); - buffer->needs_read_fill = 1; - return error ? -EFAULT : count; + return battr->mmap(of->file, kobj, battr, vma); } +void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) +{ + struct kernfs_node *kn = kobj->sd, *tmp; -/** - * flush_write_buffer - push buffer to kobject. - * @dentry: dentry to the attribute - * @buffer: data buffer for file. - * @count: number of bytes - * - * Get the correct pointers for the kobject and the attribute we're - * dealing with, then call the store() method for the attribute, - * passing the buffer that we acquired in fill_write_buffer(). - */ + if (kn && dir) + kn = kernfs_find_and_get(kn, dir); + else + kernfs_get(kn); -static int -flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) -{ - struct attribute * attr = to_attr(dentry); - struct kobject * kobj = to_kobj(dentry->d_parent); - struct sysfs_ops * ops = buffer->ops; + if (kn && attr) { + tmp = kernfs_find_and_get(kn, attr); + kernfs_put(kn); + kn = tmp; + } - return ops->store(kobj,attr,buffer->page,count); + if (kn) { + kernfs_notify(kn); + kernfs_put(kn); + } } +EXPORT_SYMBOL_GPL(sysfs_notify); +static const struct kernfs_ops sysfs_file_kfops_empty = { +}; -/** - * sysfs_write_file - write an attribute. - * @file: file pointer - * @buf: data to write - * @count: number of bytes - * @ppos: starting offset - * - * Similar to sysfs_read_file(), though working in the opposite direction. - * We allocate and fill the data from the user in fill_write_buffer(), - * then push it to the kobject in flush_write_buffer(). - * There is no easy way for us to know if userspace is only doing a partial - * write, so we don't support them. We expect the entire buffer to come - * on the first write. - * Hint: if you're writing a value, first read the file, modify only the - * the value you're changing, then write entire buffer back. - */ +static const struct kernfs_ops sysfs_file_kfops_ro = { + .seq_show = sysfs_kf_seq_show, +}; -static ssize_t -sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - struct sysfs_buffer * buffer = file->private_data; - ssize_t len; - - down(&buffer->sem); - len = fill_write_buffer(buffer, buf, count); - if (len > 0) - len = flush_write_buffer(file->f_dentry, buffer, len); - if (len > 0) - *ppos += len; - up(&buffer->sem); - return len; -} +static const struct kernfs_ops sysfs_file_kfops_wo = { + .write = sysfs_kf_write, +}; -static int check_perm(struct inode * inode, struct file * file) -{ - struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); - struct attribute * attr = to_attr(file->f_dentry); - struct sysfs_buffer * buffer; - struct sysfs_ops * ops = NULL; - int error = 0; - - if (!kobj || !attr) - goto Einval; - - /* Grab the module reference for this attribute if we have one */ - if (!try_module_get(attr->owner)) { - error = -ENODEV; - goto Done; - } +static const struct kernfs_ops sysfs_file_kfops_rw = { + .seq_show = sysfs_kf_seq_show, + .write = sysfs_kf_write, +}; - /* if the kobject has no ktype, then we assume that it is a subsystem - * itself, and use ops for it. - */ - if (kobj->kset && kobj->kset->ktype) - ops = kobj->kset->ktype->sysfs_ops; - else if (kobj->ktype) - ops = kobj->ktype->sysfs_ops; - else - ops = &subsys_sysfs_ops; +static const struct kernfs_ops sysfs_bin_kfops_ro = { + .read = sysfs_kf_bin_read, +}; - /* No sysfs operations, either from having no subsystem, - * or the subsystem have no operations. - */ - if (!ops) - goto Eaccess; +static const struct kernfs_ops sysfs_bin_kfops_wo = { + .write = sysfs_kf_bin_write, +}; - /* File needs write support. - * The inode's perms must say it's ok, - * and we must have a store method. - */ - if (file->f_mode & FMODE_WRITE) { +static const struct kernfs_ops sysfs_bin_kfops_rw = { + .read = sysfs_kf_bin_read, + .write = sysfs_kf_bin_write, +}; - if (!(inode->i_mode & S_IWUGO) || !ops->store) - goto Eaccess; +static const struct kernfs_ops sysfs_bin_kfops_mmap = { + .read = sysfs_kf_bin_read, + .write = sysfs_kf_bin_write, + .mmap = sysfs_kf_bin_mmap, +}; +int sysfs_add_file_mode_ns(struct kernfs_node *parent, + const struct attribute *attr, bool is_bin, + umode_t mode, const void *ns) +{ + struct lock_class_key *key = NULL; + const struct kernfs_ops *ops; + struct kernfs_node *kn; + loff_t size; + + if (!is_bin) { + struct kobject *kobj = parent->priv; + const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; + + /* every kobject with an attribute needs a ktype assigned */ + if (WARN(!sysfs_ops, KERN_ERR + "missing sysfs attribute operations for kobject: %s\n", + kobject_name(kobj))) + return -EINVAL; + + if (sysfs_ops->show && sysfs_ops->store) + ops = &sysfs_file_kfops_rw; + else if (sysfs_ops->show) + ops = &sysfs_file_kfops_ro; + else if (sysfs_ops->store) + ops = &sysfs_file_kfops_wo; + else + ops = &sysfs_file_kfops_empty; + + size = PAGE_SIZE; + } else { + struct bin_attribute *battr = (void *)attr; + + if (battr->mmap) + ops = &sysfs_bin_kfops_mmap; + else if (battr->read && battr->write) + ops = &sysfs_bin_kfops_rw; + else if (battr->read) + ops = &sysfs_bin_kfops_ro; + else if (battr->write) + ops = &sysfs_bin_kfops_wo; + else + ops = &sysfs_file_kfops_empty; + + size = battr->size; } - /* File needs read support. - * The inode's perms must say it's ok, and we there - * must be a show method for it. - */ - if (file->f_mode & FMODE_READ) { - if (!(inode->i_mode & S_IRUGO) || !ops->show) - goto Eaccess; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + if (!attr->ignore_lockdep) + key = attr->key ?: (struct lock_class_key *)&attr->skey; +#endif + kn = __kernfs_create_file(parent, attr->name, mode, size, ops, + (void *)attr, ns, true, key); + if (IS_ERR(kn)) { + if (PTR_ERR(kn) == -EEXIST) + sysfs_warn_dup(parent, attr->name); + return PTR_ERR(kn); } + return 0; +} - /* No error? Great, allocate a buffer for the file, and store it - * it in file->private_data for easy access. - */ - buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); - if (buffer) { - init_MUTEX(&buffer->sem); - buffer->needs_read_fill = 1; - buffer->ops = ops; - file->private_data = buffer; - } else - error = -ENOMEM; - goto Done; - - Einval: - error = -EINVAL; - goto Done; - Eaccess: - error = -EACCES; - module_put(attr->owner); - Done: - if (error && kobj) - kobject_put(kobj); - return error; +int sysfs_add_file(struct kernfs_node *parent, const struct attribute *attr, + bool is_bin) +{ + return sysfs_add_file_mode_ns(parent, attr, is_bin, attr->mode, NULL); } -static int sysfs_open_file(struct inode * inode, struct file * filp) +/** + * sysfs_create_file_ns - create an attribute file for an object with custom ns + * @kobj: object we're creating for + * @attr: attribute descriptor + * @ns: namespace the new file should belong to + */ +int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, + const void *ns) { - return check_perm(inode,filp); + BUG_ON(!kobj || !kobj->sd || !attr); + + return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, ns); + } +EXPORT_SYMBOL_GPL(sysfs_create_file_ns); -static int sysfs_release(struct inode * inode, struct file * filp) +int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr) { - struct kobject * kobj = to_kobj(filp->f_dentry->d_parent); - struct attribute * attr = to_attr(filp->f_dentry); - struct module * owner = attr->owner; - struct sysfs_buffer * buffer = filp->private_data; - - if (kobj) - kobject_put(kobj); - /* After this point, attr should not be accessed. */ - module_put(owner); - - if (buffer) { - if (buffer->page) - free_page((unsigned long)buffer->page); - kfree(buffer); - } - return 0; + int err = 0; + int i; + + for (i = 0; ptr[i] && !err; i++) + err = sysfs_create_file(kobj, ptr[i]); + if (err) + while (--i >= 0) + sysfs_remove_file(kobj, ptr[i]); + return err; } +EXPORT_SYMBOL_GPL(sysfs_create_files); -/* Sysfs attribute files are pollable. The idea is that you read - * the content and then you use 'poll' or 'select' to wait for - * the content to change. When the content changes (assuming the - * manager for the kobject supports notification), poll will - * return POLLERR|POLLPRI, and select will return the fd whether - * it is waiting for read, write, or exceptions. - * Once poll/select indicates that the value has changed, you - * need to close and re-open the file, as simply seeking and reading - * again will not get new data, or reset the state of 'poll'. - * Reminder: this only works for attributes which actively support - * it, and it is not possible to test an attribute from userspace - * to see if it supports poll (Nether 'poll' or 'select' return - * an appropriate error code). When in doubt, set a suitable timeout value. +/** + * sysfs_add_file_to_group - add an attribute file to a pre-existing group. + * @kobj: object we're acting for. + * @attr: attribute descriptor. + * @group: group name. */ -static unsigned int sysfs_poll(struct file *filp, poll_table *wait) +int sysfs_add_file_to_group(struct kobject *kobj, + const struct attribute *attr, const char *group) { - struct sysfs_buffer * buffer = filp->private_data; - struct kobject * kobj = to_kobj(filp->f_dentry->d_parent); - struct sysfs_dirent * sd = filp->f_dentry->d_fsdata; - int res = 0; - - poll_wait(filp, &kobj->poll, wait); + struct kernfs_node *parent; + int error; - if (buffer->event != atomic_read(&sd->s_event)) { - res = POLLERR|POLLPRI; - buffer->needs_read_fill = 1; + if (group) { + parent = kernfs_find_and_get(kobj->sd, group); + } else { + parent = kobj->sd; + kernfs_get(parent); } - return res; -} + if (!parent) + return -ENOENT; + error = sysfs_add_file(parent, attr, false); + kernfs_put(parent); -static struct dentry *step_down(struct dentry *dir, const char * name) -{ - struct dentry * de; - - if (dir == NULL || dir->d_inode == NULL) - return NULL; - - mutex_lock(&dir->d_inode->i_mutex); - de = lookup_one_len(name, dir, strlen(name)); - mutex_unlock(&dir->d_inode->i_mutex); - dput(dir); - if (IS_ERR(de)) - return NULL; - if (de->d_inode == NULL) { - dput(de); - return NULL; - } - return de; + return error; } +EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); -void sysfs_notify(struct kobject * k, char *dir, char *attr) +/** + * sysfs_chmod_file - update the modified mode value on an object attribute. + * @kobj: object we're acting for. + * @attr: attribute descriptor. + * @mode: file permissions. + * + */ +int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, + umode_t mode) { - struct dentry *de = k->dentry; - if (de) - dget(de); - if (de && dir) - de = step_down(de, dir); - if (de && attr) - de = step_down(de, attr); - if (de) { - struct sysfs_dirent * sd = de->d_fsdata; - if (sd) - atomic_inc(&sd->s_event); - wake_up_interruptible(&k->poll); - dput(de); - } -} -EXPORT_SYMBOL_GPL(sysfs_notify); - -const struct file_operations sysfs_file_operations = { - .read = sysfs_read_file, - .write = sysfs_write_file, - .llseek = generic_file_llseek, - .open = sysfs_open_file, - .release = sysfs_release, - .poll = sysfs_poll, -}; + struct kernfs_node *kn; + struct iattr newattrs; + int rc; + kn = kernfs_find_and_get(kobj->sd, attr->name); + if (!kn) + return -ENOENT; -int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) -{ - struct sysfs_dirent * parent_sd = dir->d_fsdata; - umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; - int error = -EEXIST; + newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE; - mutex_lock(&dir->d_inode->i_mutex); - if (!sysfs_dirent_exist(parent_sd, attr->name)) - error = sysfs_make_dirent(parent_sd, NULL, (void *)attr, - mode, type); - mutex_unlock(&dir->d_inode->i_mutex); + rc = kernfs_setattr(kn, &newattrs); - return error; + kernfs_put(kn); + return rc; } - +EXPORT_SYMBOL_GPL(sysfs_chmod_file); /** - * sysfs_create_file - create an attribute file for an object. - * @kobj: object we're creating for. - * @attr: atrribute descriptor. + * sysfs_remove_file_ns - remove an object attribute with a custom ns tag + * @kobj: object we're acting for + * @attr: attribute descriptor + * @ns: namespace tag of the file to remove + * + * Hash the attribute name and namespace tag and kill the victim. */ +void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, + const void *ns) +{ + struct kernfs_node *parent = kobj->sd; + + kernfs_remove_by_name_ns(parent, attr->name, ns); +} +EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); -int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) +/** + * sysfs_remove_file_self - remove an object attribute from its own method + * @kobj: object we're acting for + * @attr: attribute descriptor + * + * See kernfs_remove_self() for details. + */ +bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr) { - BUG_ON(!kobj || !kobj->dentry || !attr); + struct kernfs_node *parent = kobj->sd; + struct kernfs_node *kn; + bool ret; + + kn = kernfs_find_and_get(parent, attr->name); + if (WARN_ON_ONCE(!kn)) + return false; - return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR); + ret = kernfs_remove_self(kn); + kernfs_put(kn); + return ret; } +void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr) +{ + int i; + for (i = 0; ptr[i]; i++) + sysfs_remove_file(kobj, ptr[i]); +} +EXPORT_SYMBOL_GPL(sysfs_remove_files); /** - * sysfs_update_file - update the modified timestamp on an object attribute. + * sysfs_remove_file_from_group - remove an attribute file from a group. * @kobj: object we're acting for. * @attr: attribute descriptor. + * @group: group name. */ -int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) +void sysfs_remove_file_from_group(struct kobject *kobj, + const struct attribute *attr, const char *group) { - struct dentry * dir = kobj->dentry; - struct dentry * victim; - int res = -ENOENT; - - mutex_lock(&dir->d_inode->i_mutex); - victim = lookup_one_len(attr->name, dir, strlen(attr->name)); - if (!IS_ERR(victim)) { - /* make sure dentry is really there */ - if (victim->d_inode && - (victim->d_parent->d_inode == dir->d_inode)) { - victim->d_inode->i_mtime = CURRENT_TIME; - fsnotify_modify(victim); - - /** - * Drop reference from initial sysfs_get_dentry(). - */ - dput(victim); - res = 0; - } else - d_drop(victim); - - /** - * Drop the reference acquired from sysfs_get_dentry() above. - */ - dput(victim); + struct kernfs_node *parent; + + if (group) { + parent = kernfs_find_and_get(kobj->sd, group); + } else { + parent = kobj->sd; + kernfs_get(parent); } - mutex_unlock(&dir->d_inode->i_mutex); - return res; + if (parent) { + kernfs_remove_by_name(parent, attr->name); + kernfs_put(parent); + } } - +EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); /** - * sysfs_chmod_file - update the modified mode value on an object attribute. - * @kobj: object we're acting for. - * @attr: attribute descriptor. - * @mode: file permissions. - * + * sysfs_create_bin_file - create binary file for object. + * @kobj: object. + * @attr: attribute descriptor. */ -int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) +int sysfs_create_bin_file(struct kobject *kobj, + const struct bin_attribute *attr) { - struct dentry *dir = kobj->dentry; - struct dentry *victim; - struct inode * inode; - struct iattr newattrs; - int res = -ENOENT; - - mutex_lock(&dir->d_inode->i_mutex); - victim = lookup_one_len(attr->name, dir, strlen(attr->name)); - if (!IS_ERR(victim)) { - if (victim->d_inode && - (victim->d_parent->d_inode == dir->d_inode)) { - inode = victim->d_inode; - mutex_lock(&inode->i_mutex); - newattrs.ia_mode = (mode & S_IALLUGO) | - (inode->i_mode & ~S_IALLUGO); - newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - res = notify_change(victim, &newattrs); - mutex_unlock(&inode->i_mutex); - } - dput(victim); - } - mutex_unlock(&dir->d_inode->i_mutex); + BUG_ON(!kobj || !kobj->sd || !attr); - return res; + return sysfs_add_file(kobj->sd, &attr->attr, true); } -EXPORT_SYMBOL_GPL(sysfs_chmod_file); - +EXPORT_SYMBOL_GPL(sysfs_create_bin_file); /** - * sysfs_remove_file - remove an object attribute. - * @kobj: object we're acting for. + * sysfs_remove_bin_file - remove binary file for object. + * @kobj: object. * @attr: attribute descriptor. - * - * Hash the attribute name and kill the victim. */ - -void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) +void sysfs_remove_bin_file(struct kobject *kobj, + const struct bin_attribute *attr) { - sysfs_hash_and_remove(kobj->dentry,attr->name); + kernfs_remove_by_name(kobj->sd, attr->attr.name); } - - -EXPORT_SYMBOL_GPL(sysfs_create_file); -EXPORT_SYMBOL_GPL(sysfs_remove_file); -EXPORT_SYMBOL_GPL(sysfs_update_file); +EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); |
