/** * flush_write_buffer - push buffer to kobject * @of: open file * @buf: data buffer for file * @off: file offset to write to * @count: number of bytes * * Get the correct pointers for the kobject and the attribute we're dealing * with, then call the store() method for it with @buf. */ static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off, size_t count) { struct kobject *kobj = of->sd->s_parent->s_dir.kobj; int rc = 0; /* * Need @of->sd for attr and ops, its parent for kobj. @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 (!sysfs_get_active(of->sd)) { mutex_unlock(&of->mutex); return -ENODEV; } if (sysfs_is_bin(of->sd)) { struct bin_attribute *battr = of->sd->s_attr.bin_attr; rc = -EIO; if (battr->write) rc = battr->write(of->file, kobj, battr, buf, off, count); } else { const struct sysfs_ops *ops = sysfs_file_ops(of->sd); rc = ops->store(kobj, of->sd->s_attr.attr, buf, count); } sysfs_put_active(of->sd); mutex_unlock(&of->mutex); return rc; }
/** * sysfs_write_file - write an attribute * @file: file pointer * @user_buf: data to write * @count: number of bytes * @ppos: starting offset * * Copy data in from userland and pass it to the matching * sysfs_ops->store() by invoking 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 ssize_t sysfs_write_file(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct sysfs_open_file *of = sysfs_of(file); ssize_t len = min_t(size_t, count, PAGE_SIZE); loff_t size = file_inode(file)->i_size; char *buf; if (sysfs_is_bin(of->sd) && size) { if (size <= *ppos) return 0; len = min_t(ssize_t, len, size - *ppos); } if (!len) return 0; buf = kmalloc(len + 1, GFP_KERNEL); if (!buf) return -ENOMEM; if (copy_from_user(buf, user_buf, len)) { len = -EFAULT; goto out_free; } buf[len] = '\0'; /* guarantee string termination */ len = flush_write_buffer(of, buf, *ppos, len); if (len > 0) *ppos += len; out_free: kfree(buf); return len; }