diff options
| -rw-r--r-- | Documentation/filesystems/configfs/configfs.txt | 57 | ||||
| -rw-r--r-- | fs/configfs/configfs_internal.h | 14 | ||||
| -rw-r--r-- | fs/configfs/dir.c | 18 | ||||
| -rw-r--r-- | fs/configfs/file.c | 255 | ||||
| -rw-r--r-- | fs/configfs/inode.c | 2 | ||||
| -rw-r--r-- | include/linux/configfs.h | 50 |
6 files changed, 374 insertions, 22 deletions
diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index af68efdbbfad..e5fe521eea1d 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt | |||
| @@ -51,15 +51,27 @@ configfs tree is always there, whether mounted on /config or not. | |||
| 51 | An item is created via mkdir(2). The item's attributes will also | 51 | An item is created via mkdir(2). The item's attributes will also |
| 52 | appear at this time. readdir(3) can determine what the attributes are, | 52 | appear at this time. readdir(3) can determine what the attributes are, |
| 53 | read(2) can query their default values, and write(2) can store new | 53 | read(2) can query their default values, and write(2) can store new |
| 54 | values. Like sysfs, attributes should be ASCII text files, preferably | 54 | values. Don't mix more than one attribute in one attribute file. |
| 55 | with only one value per file. The same efficiency caveats from sysfs | 55 | |
| 56 | apply. Don't mix more than one attribute in one attribute file. | 56 | There are two types of configfs attributes: |
| 57 | 57 | ||
| 58 | Like sysfs, configfs expects write(2) to store the entire buffer at | 58 | * Normal attributes, which similar to sysfs attributes, are small ASCII text |
| 59 | once. When writing to configfs attributes, userspace processes should | 59 | files, with a maximum size of one page (PAGE_SIZE, 4096 on i386). Preferably |
| 60 | first read the entire file, modify the portions they wish to change, and | 60 | only one value per file should be used, and the same caveats from sysfs apply. |
| 61 | then write the entire buffer back. Attribute files have a maximum size | 61 | Configfs expects write(2) to store the entire buffer at once. When writing to |
| 62 | of one page (PAGE_SIZE, 4096 on i386). | 62 | normal configfs attributes, userspace processes should first read the entire |
| 63 | file, modify the portions they wish to change, and then write the entire | ||
| 64 | buffer back. | ||
| 65 | |||
| 66 | * Binary attributes, which are somewhat similar to sysfs binary attributes, | ||
| 67 | but with a few slight changes to semantics. The PAGE_SIZE limitation does not | ||
| 68 | apply, but the whole binary item must fit in single kernel vmalloc'ed buffer. | ||
| 69 | The write(2) calls from user space are buffered, and the attributes' | ||
| 70 | write_bin_attribute method will be invoked on the final close, therefore it is | ||
| 71 | imperative for user-space to check the return code of close(2) in order to | ||
| 72 | verify that the operation finished successfully. | ||
| 73 | To avoid a malicious user OOMing the kernel, there's a per-binary attribute | ||
| 74 | maximum buffer value. | ||
| 63 | 75 | ||
| 64 | When an item needs to be destroyed, remove it with rmdir(2). An | 76 | When an item needs to be destroyed, remove it with rmdir(2). An |
| 65 | item cannot be destroyed if any other item has a link to it (via | 77 | item cannot be destroyed if any other item has a link to it (via |
| @@ -171,6 +183,7 @@ among other things. For that, it needs a type. | |||
| 171 | struct configfs_item_operations *ct_item_ops; | 183 | struct configfs_item_operations *ct_item_ops; |
| 172 | struct configfs_group_operations *ct_group_ops; | 184 | struct configfs_group_operations *ct_group_ops; |
| 173 | struct configfs_attribute **ct_attrs; | 185 | struct configfs_attribute **ct_attrs; |
| 186 | struct configfs_bin_attribute **ct_bin_attrs; | ||
| 174 | }; | 187 | }; |
| 175 | 188 | ||
| 176 | The most basic function of a config_item_type is to define what | 189 | The most basic function of a config_item_type is to define what |
| @@ -201,6 +214,32 @@ be called whenever userspace asks for a read(2) on the attribute. If an | |||
| 201 | attribute is writable and provides a ->store method, that method will be | 214 | attribute is writable and provides a ->store method, that method will be |
| 202 | be called whenever userspace asks for a write(2) on the attribute. | 215 | be called whenever userspace asks for a write(2) on the attribute. |
| 203 | 216 | ||
| 217 | [struct configfs_bin_attribute] | ||
| 218 | |||
| 219 | struct configfs_attribute { | ||
| 220 | struct configfs_attribute cb_attr; | ||
| 221 | void *cb_private; | ||
| 222 | size_t cb_max_size; | ||
| 223 | }; | ||
| 224 | |||
| 225 | The binary attribute is used when the one needs to use binary blob to | ||
| 226 | appear as the contents of a file in the item's configfs directory. | ||
| 227 | To do so add the binary attribute to the NULL-terminated array | ||
| 228 | config_item_type->ct_bin_attrs, and the item appears in configfs, the | ||
| 229 | attribute file will appear with the configfs_bin_attribute->cb_attr.ca_name | ||
| 230 | filename. configfs_bin_attribute->cb_attr.ca_mode specifies the file | ||
| 231 | permissions. | ||
| 232 | The cb_private member is provided for use by the driver, while the | ||
| 233 | cb_max_size member specifies the maximum amount of vmalloc buffer | ||
| 234 | to be used. | ||
| 235 | |||
| 236 | If binary attribute is readable and the config_item provides a | ||
| 237 | ct_item_ops->read_bin_attribute() method, that method will be called | ||
| 238 | whenever userspace asks for a read(2) on the attribute. The converse | ||
| 239 | will happen for write(2). The reads/writes are bufferred so only a | ||
| 240 | single read/write will occur; the attributes' need not concern itself | ||
| 241 | with it. | ||
| 242 | |||
| 204 | [struct config_group] | 243 | [struct config_group] |
| 205 | 244 | ||
| 206 | A config_item cannot live in a vacuum. The only way one can be created | 245 | A config_item cannot live in a vacuum. The only way one can be created |
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index b65d1ef532d5..ccc31fa6f1a7 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h | |||
| @@ -53,13 +53,14 @@ struct configfs_dirent { | |||
| 53 | #define CONFIGFS_ROOT 0x0001 | 53 | #define CONFIGFS_ROOT 0x0001 |
| 54 | #define CONFIGFS_DIR 0x0002 | 54 | #define CONFIGFS_DIR 0x0002 |
| 55 | #define CONFIGFS_ITEM_ATTR 0x0004 | 55 | #define CONFIGFS_ITEM_ATTR 0x0004 |
| 56 | #define CONFIGFS_ITEM_BIN_ATTR 0x0008 | ||
| 56 | #define CONFIGFS_ITEM_LINK 0x0020 | 57 | #define CONFIGFS_ITEM_LINK 0x0020 |
| 57 | #define CONFIGFS_USET_DIR 0x0040 | 58 | #define CONFIGFS_USET_DIR 0x0040 |
| 58 | #define CONFIGFS_USET_DEFAULT 0x0080 | 59 | #define CONFIGFS_USET_DEFAULT 0x0080 |
| 59 | #define CONFIGFS_USET_DROPPING 0x0100 | 60 | #define CONFIGFS_USET_DROPPING 0x0100 |
| 60 | #define CONFIGFS_USET_IN_MKDIR 0x0200 | 61 | #define CONFIGFS_USET_IN_MKDIR 0x0200 |
| 61 | #define CONFIGFS_USET_CREATING 0x0400 | 62 | #define CONFIGFS_USET_CREATING 0x0400 |
| 62 | #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) | 63 | #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR) |
| 63 | 64 | ||
| 64 | extern struct mutex configfs_symlink_mutex; | 65 | extern struct mutex configfs_symlink_mutex; |
| 65 | extern spinlock_t configfs_dirent_lock; | 66 | extern spinlock_t configfs_dirent_lock; |
| @@ -72,6 +73,8 @@ extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *, | |||
| 72 | extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct inode *)); | 73 | extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct inode *)); |
| 73 | 74 | ||
| 74 | extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); | 75 | extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); |
| 76 | extern int configfs_create_bin_file(struct config_item *, | ||
| 77 | const struct configfs_bin_attribute *); | ||
| 75 | extern int configfs_make_dirent(struct configfs_dirent *, | 78 | extern int configfs_make_dirent(struct configfs_dirent *, |
| 76 | struct dentry *, void *, umode_t, int); | 79 | struct dentry *, void *, umode_t, int); |
| 77 | extern int configfs_dirent_is_ready(struct configfs_dirent *); | 80 | extern int configfs_dirent_is_ready(struct configfs_dirent *); |
| @@ -88,7 +91,7 @@ extern void configfs_release_fs(void); | |||
| 88 | extern struct rw_semaphore configfs_rename_sem; | 91 | extern struct rw_semaphore configfs_rename_sem; |
| 89 | extern const struct file_operations configfs_dir_operations; | 92 | extern const struct file_operations configfs_dir_operations; |
| 90 | extern const struct file_operations configfs_file_operations; | 93 | extern const struct file_operations configfs_file_operations; |
| 91 | extern const struct file_operations bin_fops; | 94 | extern const struct file_operations configfs_bin_file_operations; |
| 92 | extern const struct inode_operations configfs_dir_inode_operations; | 95 | extern const struct inode_operations configfs_dir_inode_operations; |
| 93 | extern const struct inode_operations configfs_root_inode_operations; | 96 | extern const struct inode_operations configfs_root_inode_operations; |
| 94 | extern const struct inode_operations configfs_symlink_inode_operations; | 97 | extern const struct inode_operations configfs_symlink_inode_operations; |
| @@ -119,6 +122,13 @@ static inline struct configfs_attribute * to_attr(struct dentry * dentry) | |||
| 119 | return ((struct configfs_attribute *) sd->s_element); | 122 | return ((struct configfs_attribute *) sd->s_element); |
| 120 | } | 123 | } |
| 121 | 124 | ||
| 125 | static inline struct configfs_bin_attribute *to_bin_attr(struct dentry *dentry) | ||
| 126 | { | ||
| 127 | struct configfs_attribute *attr = to_attr(dentry); | ||
| 128 | |||
| 129 | return container_of(attr, struct configfs_bin_attribute, cb_attr); | ||
| 130 | } | ||
| 131 | |||
| 122 | static inline struct config_item *configfs_get_config_item(struct dentry *dentry) | 132 | static inline struct config_item *configfs_get_config_item(struct dentry *dentry) |
| 123 | { | 133 | { |
| 124 | struct config_item * item = NULL; | 134 | struct config_item * item = NULL; |
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index a7a1b218f308..7ae97e83f121 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
| @@ -255,6 +255,12 @@ static void configfs_init_file(struct inode * inode) | |||
| 255 | inode->i_fop = &configfs_file_operations; | 255 | inode->i_fop = &configfs_file_operations; |
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | static void configfs_init_bin_file(struct inode *inode) | ||
| 259 | { | ||
| 260 | inode->i_size = 0; | ||
| 261 | inode->i_fop = &configfs_bin_file_operations; | ||
| 262 | } | ||
| 263 | |||
| 258 | static void init_symlink(struct inode * inode) | 264 | static void init_symlink(struct inode * inode) |
| 259 | { | 265 | { |
| 260 | inode->i_op = &configfs_symlink_inode_operations; | 266 | inode->i_op = &configfs_symlink_inode_operations; |
| @@ -423,7 +429,9 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den | |||
| 423 | spin_unlock(&configfs_dirent_lock); | 429 | spin_unlock(&configfs_dirent_lock); |
| 424 | 430 | ||
| 425 | error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, | 431 | error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, |
| 426 | configfs_init_file); | 432 | (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) ? |
| 433 | configfs_init_bin_file : | ||
| 434 | configfs_init_file); | ||
| 427 | if (error) { | 435 | if (error) { |
| 428 | configfs_put(sd); | 436 | configfs_put(sd); |
| 429 | return error; | 437 | return error; |
| @@ -583,6 +591,7 @@ static int populate_attrs(struct config_item *item) | |||
| 583 | { | 591 | { |
| 584 | struct config_item_type *t = item->ci_type; | 592 | struct config_item_type *t = item->ci_type; |
| 585 | struct configfs_attribute *attr; | 593 | struct configfs_attribute *attr; |
| 594 | struct configfs_bin_attribute *bin_attr; | ||
| 586 | int error = 0; | 595 | int error = 0; |
| 587 | int i; | 596 | int i; |
| 588 | 597 | ||
| @@ -594,6 +603,13 @@ static int populate_attrs(struct config_item *item) | |||
| 594 | break; | 603 | break; |
| 595 | } | 604 | } |
| 596 | } | 605 | } |
| 606 | if (t->ct_bin_attrs) { | ||
| 607 | for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) { | ||
| 608 | error = configfs_create_bin_file(item, bin_attr); | ||
| 609 | if (error) | ||
| 610 | break; | ||
| 611 | } | ||
| 612 | } | ||
| 597 | 613 | ||
| 598 | if (error) | 614 | if (error) |
| 599 | detach_attrs(item); | 615 | detach_attrs(item); |
diff --git a/fs/configfs/file.c b/fs/configfs/file.c index d39099ea7df7..3687187c8ea5 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <linux/module.h> | 28 | #include <linux/module.h> |
| 29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
| 30 | #include <linux/mutex.h> | 30 | #include <linux/mutex.h> |
| 31 | #include <linux/vmalloc.h> | ||
| 31 | #include <asm/uaccess.h> | 32 | #include <asm/uaccess.h> |
| 32 | 33 | ||
| 33 | #include <linux/configfs.h> | 34 | #include <linux/configfs.h> |
| @@ -48,6 +49,10 @@ struct configfs_buffer { | |||
| 48 | struct configfs_item_operations * ops; | 49 | struct configfs_item_operations * ops; |
| 49 | struct mutex mutex; | 50 | struct mutex mutex; |
| 50 | int needs_read_fill; | 51 | int needs_read_fill; |
| 52 | bool read_in_progress; | ||
| 53 | bool write_in_progress; | ||
| 54 | char *bin_buffer; | ||
| 55 | int bin_buffer_size; | ||
| 51 | }; | 56 | }; |
| 52 | 57 | ||
| 53 | 58 | ||
| @@ -123,6 +128,87 @@ out: | |||
| 123 | return retval; | 128 | return retval; |
| 124 | } | 129 | } |
| 125 | 130 | ||
| 131 | /** | ||
| 132 | * configfs_read_bin_file - read a binary attribute. | ||
| 133 | * @file: file pointer. | ||
| 134 | * @buf: buffer to fill. | ||
| 135 | * @count: number of bytes to read. | ||
| 136 | * @ppos: starting offset in file. | ||
| 137 | * | ||
| 138 | * Userspace wants to read a binary attribute file. The attribute | ||
| 139 | * descriptor is in the file's ->d_fsdata. The target item is in the | ||
| 140 | * directory's ->d_fsdata. | ||
| 141 | * | ||
| 142 | * We check whether we need to refill the buffer. If so we will | ||
| 143 | * call the attributes' attr->read() twice. The first time we | ||
| 144 | * will pass a NULL as a buffer pointer, which the attributes' method | ||
| 145 | * will use to return the size of the buffer required. If no error | ||
| 146 | * occurs we will allocate the buffer using vmalloc and call | ||
| 147 | * attr->read() again passing that buffer as an argument. | ||
| 148 | * Then we just copy to user-space using simple_read_from_buffer. | ||
| 149 | */ | ||
| 150 | |||
| 151 | static ssize_t | ||
| 152 | configfs_read_bin_file(struct file *file, char __user *buf, | ||
| 153 | size_t count, loff_t *ppos) | ||
| 154 | { | ||
| 155 | struct configfs_buffer *buffer = file->private_data; | ||
| 156 | struct dentry *dentry = file->f_path.dentry; | ||
| 157 | struct config_item *item = to_item(dentry->d_parent); | ||
| 158 | struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); | ||
| 159 | ssize_t retval = 0; | ||
| 160 | ssize_t len = min_t(size_t, count, PAGE_SIZE); | ||
| 161 | |||
| 162 | mutex_lock(&buffer->mutex); | ||
| 163 | |||
| 164 | /* we don't support switching read/write modes */ | ||
| 165 | if (buffer->write_in_progress) { | ||
| 166 | retval = -ETXTBSY; | ||
| 167 | goto out; | ||
| 168 | } | ||
| 169 | buffer->read_in_progress = 1; | ||
| 170 | |||
| 171 | if (buffer->needs_read_fill) { | ||
| 172 | /* perform first read with buf == NULL to get extent */ | ||
| 173 | len = bin_attr->read(item, NULL, 0); | ||
| 174 | if (len <= 0) { | ||
| 175 | retval = len; | ||
| 176 | goto out; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* do not exceed the maximum value */ | ||
| 180 | if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) { | ||
| 181 | retval = -EFBIG; | ||
| 182 | goto out; | ||
| 183 | } | ||
| 184 | |||
| 185 | buffer->bin_buffer = vmalloc(len); | ||
| 186 | if (buffer->bin_buffer == NULL) { | ||
| 187 | retval = -ENOMEM; | ||
| 188 | goto out; | ||
| 189 | } | ||
| 190 | buffer->bin_buffer_size = len; | ||
| 191 | |||
| 192 | /* perform second read to fill buffer */ | ||
| 193 | len = bin_attr->read(item, buffer->bin_buffer, len); | ||
| 194 | if (len < 0) { | ||
| 195 | retval = len; | ||
| 196 | vfree(buffer->bin_buffer); | ||
| 197 | buffer->bin_buffer_size = 0; | ||
| 198 | buffer->bin_buffer = NULL; | ||
| 199 | goto out; | ||
| 200 | } | ||
| 201 | |||
| 202 | buffer->needs_read_fill = 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | retval = simple_read_from_buffer(buf, count, ppos, buffer->bin_buffer, | ||
| 206 | buffer->bin_buffer_size); | ||
| 207 | out: | ||
| 208 | mutex_unlock(&buffer->mutex); | ||
| 209 | return retval; | ||
| 210 | } | ||
| 211 | |||
| 126 | 212 | ||
| 127 | /** | 213 | /** |
| 128 | * fill_write_buffer - copy buffer from userspace. | 214 | * fill_write_buffer - copy buffer from userspace. |
| @@ -209,10 +295,80 @@ configfs_write_file(struct file *file, const char __user *buf, size_t count, lof | |||
| 209 | return len; | 295 | return len; |
| 210 | } | 296 | } |
| 211 | 297 | ||
| 212 | static int check_perm(struct inode * inode, struct file * file) | 298 | /** |
| 299 | * configfs_write_bin_file - write a binary attribute. | ||
| 300 | * @file: file pointer | ||
| 301 | * @buf: data to write | ||
| 302 | * @count: number of bytes | ||
| 303 | * @ppos: starting offset | ||
| 304 | * | ||
| 305 | * Writing to a binary attribute file is similar to a normal read. | ||
| 306 | * We buffer the consecutive writes (binary attribute files do not | ||
| 307 | * support lseek) in a continuously growing buffer, but we don't | ||
| 308 | * commit until the close of the file. | ||
| 309 | */ | ||
| 310 | |||
| 311 | static ssize_t | ||
| 312 | configfs_write_bin_file(struct file *file, const char __user *buf, | ||
| 313 | size_t count, loff_t *ppos) | ||
| 314 | { | ||
| 315 | struct configfs_buffer *buffer = file->private_data; | ||
| 316 | struct dentry *dentry = file->f_path.dentry; | ||
| 317 | struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); | ||
| 318 | void *tbuf = NULL; | ||
| 319 | ssize_t len; | ||
| 320 | |||
| 321 | mutex_lock(&buffer->mutex); | ||
| 322 | |||
| 323 | /* we don't support switching read/write modes */ | ||
| 324 | if (buffer->read_in_progress) { | ||
| 325 | len = -ETXTBSY; | ||
| 326 | goto out; | ||
| 327 | } | ||
| 328 | buffer->write_in_progress = 1; | ||
| 329 | |||
| 330 | /* buffer grows? */ | ||
| 331 | if (*ppos + count > buffer->bin_buffer_size) { | ||
| 332 | |||
| 333 | if (bin_attr->cb_max_size && | ||
| 334 | *ppos + count > bin_attr->cb_max_size) { | ||
| 335 | len = -EFBIG; | ||
| 336 | } | ||
| 337 | |||
| 338 | tbuf = vmalloc(*ppos + count); | ||
| 339 | if (tbuf == NULL) { | ||
| 340 | len = -ENOMEM; | ||
| 341 | goto out; | ||
| 342 | } | ||
| 343 | |||
| 344 | /* copy old contents */ | ||
| 345 | if (buffer->bin_buffer) { | ||
| 346 | memcpy(tbuf, buffer->bin_buffer, | ||
| 347 | buffer->bin_buffer_size); | ||
| 348 | vfree(buffer->bin_buffer); | ||
| 349 | } | ||
| 350 | |||
| 351 | /* clear the new area */ | ||
| 352 | memset(tbuf + buffer->bin_buffer_size, 0, | ||
| 353 | *ppos + count - buffer->bin_buffer_size); | ||
| 354 | buffer->bin_buffer = tbuf; | ||
| 355 | buffer->bin_buffer_size = *ppos + count; | ||
| 356 | } | ||
| 357 | |||
| 358 | len = simple_write_to_buffer(buffer->bin_buffer, | ||
| 359 | buffer->bin_buffer_size, ppos, buf, count); | ||
| 360 | if (len > 0) | ||
| 361 | *ppos += len; | ||
| 362 | out: | ||
| 363 | mutex_unlock(&buffer->mutex); | ||
| 364 | return len; | ||
| 365 | } | ||
| 366 | |||
| 367 | static int check_perm(struct inode * inode, struct file * file, int type) | ||
| 213 | { | 368 | { |
| 214 | struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); | 369 | struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); |
| 215 | struct configfs_attribute * attr = to_attr(file->f_path.dentry); | 370 | struct configfs_attribute * attr = to_attr(file->f_path.dentry); |
| 371 | struct configfs_bin_attribute *bin_attr = NULL; | ||
| 216 | struct configfs_buffer * buffer; | 372 | struct configfs_buffer * buffer; |
| 217 | struct configfs_item_operations * ops = NULL; | 373 | struct configfs_item_operations * ops = NULL; |
| 218 | int error = 0; | 374 | int error = 0; |
| @@ -220,6 +376,9 @@ static int check_perm(struct inode * inode, struct file * file) | |||
| 220 | if (!item || !attr) | 376 | if (!item || !attr) |
| 221 | goto Einval; | 377 | goto Einval; |
| 222 | 378 | ||
| 379 | if (type & CONFIGFS_ITEM_BIN_ATTR) | ||
| 380 | bin_attr = to_bin_attr(file->f_path.dentry); | ||
| 381 | |||
| 223 | /* Grab the module reference for this attribute if we have one */ | 382 | /* Grab the module reference for this attribute if we have one */ |
| 224 | if (!try_module_get(attr->ca_owner)) { | 383 | if (!try_module_get(attr->ca_owner)) { |
| 225 | error = -ENODEV; | 384 | error = -ENODEV; |
| @@ -236,9 +395,14 @@ static int check_perm(struct inode * inode, struct file * file) | |||
| 236 | * and we must have a store method. | 395 | * and we must have a store method. |
| 237 | */ | 396 | */ |
| 238 | if (file->f_mode & FMODE_WRITE) { | 397 | if (file->f_mode & FMODE_WRITE) { |
| 239 | if (!(inode->i_mode & S_IWUGO) || !attr->store) | 398 | if (!(inode->i_mode & S_IWUGO)) |
| 399 | goto Eaccess; | ||
| 400 | |||
| 401 | if ((type & CONFIGFS_ITEM_ATTR) && !attr->store) | ||
| 240 | goto Eaccess; | 402 | goto Eaccess; |
| 241 | 403 | ||
| 404 | if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write) | ||
| 405 | goto Eaccess; | ||
| 242 | } | 406 | } |
| 243 | 407 | ||
| 244 | /* File needs read support. | 408 | /* File needs read support. |
| @@ -246,7 +410,13 @@ static int check_perm(struct inode * inode, struct file * file) | |||
| 246 | * must be a show method for it. | 410 | * must be a show method for it. |
| 247 | */ | 411 | */ |
| 248 | if (file->f_mode & FMODE_READ) { | 412 | if (file->f_mode & FMODE_READ) { |
| 249 | if (!(inode->i_mode & S_IRUGO) || !attr->show) | 413 | if (!(inode->i_mode & S_IRUGO)) |
| 414 | goto Eaccess; | ||
| 415 | |||
| 416 | if ((type & CONFIGFS_ITEM_ATTR) && !attr->show) | ||
| 417 | goto Eaccess; | ||
| 418 | |||
| 419 | if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read) | ||
| 250 | goto Eaccess; | 420 | goto Eaccess; |
| 251 | } | 421 | } |
| 252 | 422 | ||
| @@ -260,6 +430,8 @@ static int check_perm(struct inode * inode, struct file * file) | |||
| 260 | } | 430 | } |
| 261 | mutex_init(&buffer->mutex); | 431 | mutex_init(&buffer->mutex); |
| 262 | buffer->needs_read_fill = 1; | 432 | buffer->needs_read_fill = 1; |
| 433 | buffer->read_in_progress = 0; | ||
| 434 | buffer->write_in_progress = 0; | ||
| 263 | buffer->ops = ops; | 435 | buffer->ops = ops; |
| 264 | file->private_data = buffer; | 436 | file->private_data = buffer; |
| 265 | goto Done; | 437 | goto Done; |
| @@ -277,12 +449,7 @@ static int check_perm(struct inode * inode, struct file * file) | |||
| 277 | return error; | 449 | return error; |
| 278 | } | 450 | } |
| 279 | 451 | ||
| 280 | static int configfs_open_file(struct inode * inode, struct file * filp) | 452 | static int configfs_release(struct inode *inode, struct file *filp) |
| 281 | { | ||
| 282 | return check_perm(inode,filp); | ||
| 283 | } | ||
| 284 | |||
| 285 | static int configfs_release(struct inode * inode, struct file * filp) | ||
| 286 | { | 453 | { |
| 287 | struct config_item * item = to_item(filp->f_path.dentry->d_parent); | 454 | struct config_item * item = to_item(filp->f_path.dentry->d_parent); |
| 288 | struct configfs_attribute * attr = to_attr(filp->f_path.dentry); | 455 | struct configfs_attribute * attr = to_attr(filp->f_path.dentry); |
| @@ -303,6 +470,47 @@ static int configfs_release(struct inode * inode, struct file * filp) | |||
| 303 | return 0; | 470 | return 0; |
| 304 | } | 471 | } |
| 305 | 472 | ||
| 473 | static int configfs_open_file(struct inode *inode, struct file *filp) | ||
| 474 | { | ||
| 475 | return check_perm(inode, filp, CONFIGFS_ITEM_ATTR); | ||
| 476 | } | ||
| 477 | |||
| 478 | static int configfs_open_bin_file(struct inode *inode, struct file *filp) | ||
| 479 | { | ||
| 480 | return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR); | ||
| 481 | } | ||
| 482 | |||
| 483 | static int configfs_release_bin_file(struct inode *inode, struct file *filp) | ||
| 484 | { | ||
| 485 | struct configfs_buffer *buffer = filp->private_data; | ||
| 486 | struct dentry *dentry = filp->f_path.dentry; | ||
| 487 | struct config_item *item = to_item(dentry->d_parent); | ||
| 488 | struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); | ||
| 489 | ssize_t len = 0; | ||
| 490 | int ret; | ||
| 491 | |||
| 492 | buffer->read_in_progress = 0; | ||
| 493 | |||
| 494 | if (buffer->write_in_progress) { | ||
| 495 | buffer->write_in_progress = 0; | ||
| 496 | |||
| 497 | len = bin_attr->write(item, buffer->bin_buffer, | ||
| 498 | buffer->bin_buffer_size); | ||
| 499 | |||
| 500 | /* vfree on NULL is safe */ | ||
| 501 | vfree(buffer->bin_buffer); | ||
| 502 | buffer->bin_buffer = NULL; | ||
| 503 | buffer->bin_buffer_size = 0; | ||
| 504 | buffer->needs_read_fill = 1; | ||
| 505 | } | ||
| 506 | |||
| 507 | ret = configfs_release(inode, filp); | ||
| 508 | if (len < 0) | ||
| 509 | return len; | ||
| 510 | return ret; | ||
| 511 | } | ||
| 512 | |||
| 513 | |||
| 306 | const struct file_operations configfs_file_operations = { | 514 | const struct file_operations configfs_file_operations = { |
| 307 | .read = configfs_read_file, | 515 | .read = configfs_read_file, |
| 308 | .write = configfs_write_file, | 516 | .write = configfs_write_file, |
| @@ -311,6 +519,14 @@ const struct file_operations configfs_file_operations = { | |||
| 311 | .release = configfs_release, | 519 | .release = configfs_release, |
| 312 | }; | 520 | }; |
| 313 | 521 | ||
| 522 | const struct file_operations configfs_bin_file_operations = { | ||
| 523 | .read = configfs_read_bin_file, | ||
| 524 | .write = configfs_write_bin_file, | ||
| 525 | .llseek = NULL, /* bin file is not seekable */ | ||
| 526 | .open = configfs_open_bin_file, | ||
| 527 | .release = configfs_release_bin_file, | ||
| 528 | }; | ||
| 529 | |||
| 314 | /** | 530 | /** |
| 315 | * configfs_create_file - create an attribute file for an item. | 531 | * configfs_create_file - create an attribute file for an item. |
| 316 | * @item: item we're creating for. | 532 | * @item: item we're creating for. |
| @@ -332,3 +548,24 @@ int configfs_create_file(struct config_item * item, const struct configfs_attrib | |||
| 332 | return error; | 548 | return error; |
| 333 | } | 549 | } |
| 334 | 550 | ||
| 551 | /** | ||
| 552 | * configfs_create_bin_file - create a binary attribute file for an item. | ||
| 553 | * @item: item we're creating for. | ||
| 554 | * @attr: atrribute descriptor. | ||
| 555 | */ | ||
| 556 | |||
| 557 | int configfs_create_bin_file(struct config_item *item, | ||
| 558 | const struct configfs_bin_attribute *bin_attr) | ||
| 559 | { | ||
| 560 | struct dentry *dir = item->ci_dentry; | ||
| 561 | struct configfs_dirent *parent_sd = dir->d_fsdata; | ||
| 562 | umode_t mode = (bin_attr->cb_attr.ca_mode & S_IALLUGO) | S_IFREG; | ||
| 563 | int error = 0; | ||
| 564 | |||
| 565 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_NORMAL); | ||
| 566 | error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode, | ||
| 567 | CONFIGFS_ITEM_BIN_ATTR); | ||
| 568 | mutex_unlock(&dir->d_inode->i_mutex); | ||
| 569 | |||
| 570 | return error; | ||
| 571 | } | ||
diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index eae87575e681..0cc810e9dccc 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c | |||
| @@ -218,7 +218,7 @@ const unsigned char * configfs_get_name(struct configfs_dirent *sd) | |||
| 218 | if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK)) | 218 | if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK)) |
| 219 | return sd->s_dentry->d_name.name; | 219 | return sd->s_dentry->d_name.name; |
| 220 | 220 | ||
| 221 | if (sd->s_type & CONFIGFS_ITEM_ATTR) { | 221 | if (sd->s_type & (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)) { |
| 222 | attr = sd->s_element; | 222 | attr = sd->s_element; |
| 223 | return attr->ca_name; | 223 | return attr->ca_name; |
| 224 | } | 224 | } |
diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 758a029011b1..f7300d023dbe 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h | |||
| @@ -51,6 +51,7 @@ struct module; | |||
| 51 | struct configfs_item_operations; | 51 | struct configfs_item_operations; |
| 52 | struct configfs_group_operations; | 52 | struct configfs_group_operations; |
| 53 | struct configfs_attribute; | 53 | struct configfs_attribute; |
| 54 | struct configfs_bin_attribute; | ||
| 54 | struct configfs_subsystem; | 55 | struct configfs_subsystem; |
| 55 | 56 | ||
| 56 | struct config_item { | 57 | struct config_item { |
| @@ -84,6 +85,7 @@ struct config_item_type { | |||
| 84 | struct configfs_item_operations *ct_item_ops; | 85 | struct configfs_item_operations *ct_item_ops; |
| 85 | struct configfs_group_operations *ct_group_ops; | 86 | struct configfs_group_operations *ct_group_ops; |
| 86 | struct configfs_attribute **ct_attrs; | 87 | struct configfs_attribute **ct_attrs; |
| 88 | struct configfs_bin_attribute **ct_bin_attrs; | ||
| 87 | }; | 89 | }; |
| 88 | 90 | ||
| 89 | /** | 91 | /** |
| @@ -154,6 +156,54 @@ static struct configfs_attribute _pfx##attr_##_name = { \ | |||
| 154 | .store = _pfx##_name##_store, \ | 156 | .store = _pfx##_name##_store, \ |
| 155 | } | 157 | } |
| 156 | 158 | ||
| 159 | struct file; | ||
| 160 | struct vm_area_struct; | ||
| 161 | |||
| 162 | struct configfs_bin_attribute { | ||
| 163 | struct configfs_attribute cb_attr; /* std. attribute */ | ||
| 164 | void *cb_private; /* for user */ | ||
| 165 | size_t cb_max_size; /* max core size */ | ||
| 166 | ssize_t (*read)(struct config_item *, void *, size_t); | ||
| 167 | ssize_t (*write)(struct config_item *, const void *, size_t); | ||
| 168 | }; | ||
| 169 | |||
| 170 | #define CONFIGFS_BIN_ATTR(_pfx, _name, _priv, _maxsz) \ | ||
| 171 | static struct configfs_bin_attribute _pfx##attr_##_name = { \ | ||
| 172 | .cb_attr = { \ | ||
| 173 | .ca_name = __stringify(_name), \ | ||
| 174 | .ca_mode = S_IRUGO | S_IWUSR, \ | ||
| 175 | .ca_owner = THIS_MODULE, \ | ||
| 176 | }, \ | ||
| 177 | .cb_private = _priv, \ | ||
| 178 | .cb_max_size = _maxsz, \ | ||
| 179 | .read = _pfx##_name##_read, \ | ||
| 180 | .write = _pfx##_name##_write, \ | ||
| 181 | } | ||
| 182 | |||
| 183 | #define CONFIGFS_BIN_ATTR_RO(_pfx, _name, _priv, _maxsz) \ | ||
| 184 | static struct configfs_attribute _pfx##attr_##_name = { \ | ||
| 185 | .cb_attr = { \ | ||
| 186 | .ca_name = __stringify(_name), \ | ||
| 187 | .ca_mode = S_IRUGO, \ | ||
| 188 | .ca_owner = THIS_MODULE, \ | ||
| 189 | }, \ | ||
| 190 | .cb_private = _priv, \ | ||
| 191 | .cb_max_size = _maxsz, \ | ||
| 192 | .read = _pfx##_name##_read, \ | ||
| 193 | } | ||
| 194 | |||
| 195 | #define CONFIGFS_BIN_ATTR_WO(_pfx, _name, _priv, _maxsz) \ | ||
| 196 | static struct configfs_attribute _pfx##attr_##_name = { \ | ||
| 197 | .cb_attr = { \ | ||
| 198 | .ca_name = __stringify(_name), \ | ||
| 199 | .ca_mode = S_IWUSR, \ | ||
| 200 | .ca_owner = THIS_MODULE, \ | ||
| 201 | }, \ | ||
| 202 | .cb_private = _priv, \ | ||
| 203 | .cb_max_size = _maxsz, \ | ||
| 204 | .write = _pfx##_name##_write, \ | ||
| 205 | } | ||
| 206 | |||
| 157 | /* | 207 | /* |
| 158 | * If allow_link() exists, the item can symlink(2) out to other | 208 | * If allow_link() exists, the item can symlink(2) out to other |
| 159 | * items. If the item is a group, it may support mkdir(2). | 209 | * items. If the item is a group, it may support mkdir(2). |
