Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
config CONFIGFS_FS
tristate "Userspace-driven configuration filesystem"
help
configfs is a RAM-based filesystem that provides the converse
of sysfs's functionality. Where sysfs is a filesystem-based
view of kernel objects, configfs is a filesystem-based manager
of kernel objects, or config_items.
Both sysfs and configfs can and should exist together on the
same system. One is not a replacement for the other.
+8
View File
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the configfs virtual filesystem
#
obj-$(CONFIG_CONFIGFS_FS) += configfs.o
configfs-objs := inode.o file.o dir.o symlink.o mount.o item.o
+158
View File
@@ -0,0 +1,158 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* configfs_internal.h - Internal stuff for configfs
*
* Based on sysfs:
* sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
*
* configfs Copyright (C) 2005 Oracle. All rights reserved.
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/spinlock.h>
struct configfs_fragment {
atomic_t frag_count;
struct rw_semaphore frag_sem;
bool frag_dead;
};
void put_fragment(struct configfs_fragment *);
struct configfs_fragment *get_fragment(struct configfs_fragment *);
struct configfs_dirent {
atomic_t s_count;
int s_dependent_count;
struct list_head s_sibling;
struct list_head s_children;
int s_links;
void * s_element;
int s_type;
umode_t s_mode;
struct dentry * s_dentry;
struct iattr * s_iattr;
#ifdef CONFIG_LOCKDEP
int s_depth;
#endif
struct configfs_fragment *s_frag;
};
#define CONFIGFS_ROOT 0x0001
#define CONFIGFS_DIR 0x0002
#define CONFIGFS_ITEM_ATTR 0x0004
#define CONFIGFS_ITEM_BIN_ATTR 0x0008
#define CONFIGFS_ITEM_LINK 0x0020
#define CONFIGFS_USET_DIR 0x0040
#define CONFIGFS_USET_DEFAULT 0x0080
#define CONFIGFS_USET_DROPPING 0x0100
#define CONFIGFS_USET_IN_MKDIR 0x0200
#define CONFIGFS_USET_CREATING 0x0400
#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)
#define CONFIGFS_PINNED \
(CONFIGFS_ROOT | CONFIGFS_DIR | CONFIGFS_ITEM_LINK)
extern struct mutex configfs_symlink_mutex;
extern spinlock_t configfs_dirent_lock;
extern struct kmem_cache *configfs_dir_cachep;
extern int configfs_is_root(struct config_item *item);
extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *, struct super_block *);
extern struct inode *configfs_create(struct dentry *, umode_t mode);
extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
extern int configfs_create_bin_file(struct config_item *,
const struct configfs_bin_attribute *);
extern int configfs_make_dirent(struct configfs_dirent *, struct dentry *,
void *, umode_t, int, struct configfs_fragment *);
extern int configfs_dirent_is_ready(struct configfs_dirent *);
extern const unsigned char * configfs_get_name(struct configfs_dirent *sd);
extern int configfs_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *iattr);
extern struct dentry *configfs_pin_fs(void);
extern void configfs_release_fs(void);
extern const struct file_operations configfs_dir_operations;
extern const struct file_operations configfs_file_operations;
extern const struct file_operations configfs_bin_file_operations;
extern const struct inode_operations configfs_dir_inode_operations;
extern const struct inode_operations configfs_root_inode_operations;
extern const struct inode_operations configfs_symlink_inode_operations;
extern const struct dentry_operations configfs_dentry_ops;
extern int configfs_symlink(struct mnt_idmap *idmap,
struct inode *dir, struct dentry *dentry,
const char *symname);
extern int configfs_unlink(struct inode *dir, struct dentry *dentry);
int configfs_create_link(struct configfs_dirent *target, struct dentry *parent,
struct dentry *dentry, char *body);
static inline struct config_item * to_item(struct dentry * dentry)
{
struct configfs_dirent * sd = dentry->d_fsdata;
return ((struct config_item *) sd->s_element);
}
static inline struct configfs_attribute * to_attr(struct dentry * dentry)
{
struct configfs_dirent * sd = dentry->d_fsdata;
return ((struct configfs_attribute *) sd->s_element);
}
static inline struct configfs_bin_attribute *to_bin_attr(struct dentry *dentry)
{
struct configfs_attribute *attr = to_attr(dentry);
return container_of(attr, struct configfs_bin_attribute, cb_attr);
}
static inline struct config_item *configfs_get_config_item(struct dentry *dentry)
{
struct config_item * item = NULL;
spin_lock(&dentry->d_lock);
if (!d_unhashed(dentry)) {
struct configfs_dirent * sd = dentry->d_fsdata;
item = config_item_get(sd->s_element);
}
spin_unlock(&dentry->d_lock);
return item;
}
static inline void release_configfs_dirent(struct configfs_dirent * sd)
{
if (!(sd->s_type & CONFIGFS_ROOT)) {
kfree(sd->s_iattr);
put_fragment(sd->s_frag);
kmem_cache_free(configfs_dir_cachep, sd);
}
}
static inline struct configfs_dirent * configfs_get(struct configfs_dirent * sd)
{
if (sd) {
WARN_ON(!atomic_read(&sd->s_count));
atomic_inc(&sd->s_count);
}
return sd;
}
static inline void configfs_put(struct configfs_dirent * sd)
{
WARN_ON(!atomic_read(&sd->s_count));
if (atomic_dec_and_test(&sd->s_count))
release_configfs_dirent(sd);
}
File diff suppressed because it is too large Load Diff
+481
View File
@@ -0,0 +1,481 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* file.c - operations for regular (text) files.
*
* Based on sysfs:
* sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
*
* configfs Copyright (C) 2005 Oracle. All rights reserved.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <linux/configfs.h>
#include "configfs_internal.h"
/*
* A simple attribute can only be 4096 characters. Why 4k? Because the
* original code limited it to PAGE_SIZE. That's a bad idea, though,
* because an attribute of 16k on ia64 won't work on x86. So we limit to
* 4k, our minimum common page size.
*/
#define SIMPLE_ATTR_SIZE 4096
struct configfs_buffer {
size_t count;
loff_t pos;
char * page;
const struct configfs_item_operations *ops;
struct mutex mutex;
int needs_read_fill;
bool read_in_progress;
bool write_in_progress;
char *bin_buffer;
int bin_buffer_size;
int cb_max_size;
struct config_item *item;
struct module *owner;
union {
struct configfs_attribute *attr;
struct configfs_bin_attribute *bin_attr;
};
};
static inline struct configfs_fragment *to_frag(struct file *file)
{
struct configfs_dirent *sd = file->f_path.dentry->d_fsdata;
return sd->s_frag;
}
static int fill_read_buffer(struct file *file, struct configfs_buffer *buffer)
{
struct configfs_fragment *frag = to_frag(file);
ssize_t count = -ENOENT;
if (!buffer->page)
buffer->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
down_read(&frag->frag_sem);
if (!frag->frag_dead)
count = buffer->attr->show(buffer->item, buffer->page);
up_read(&frag->frag_sem);
if (count < 0)
return count;
if (WARN_ON_ONCE(count > (ssize_t)SIMPLE_ATTR_SIZE))
return -EIO;
buffer->needs_read_fill = 0;
buffer->count = count;
return 0;
}
static ssize_t configfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct file *file = iocb->ki_filp;
struct configfs_buffer *buffer = file->private_data;
ssize_t retval = 0;
mutex_lock(&buffer->mutex);
if (buffer->needs_read_fill) {
retval = fill_read_buffer(file, buffer);
if (retval)
goto out;
}
pr_debug("%s: count = %zd, pos = %lld, buf = %s\n",
__func__, iov_iter_count(to), iocb->ki_pos, buffer->page);
if (iocb->ki_pos >= buffer->count)
goto out;
retval = copy_to_iter(buffer->page + iocb->ki_pos,
buffer->count - iocb->ki_pos, to);
iocb->ki_pos += retval;
if (retval == 0)
retval = -EFAULT;
out:
mutex_unlock(&buffer->mutex);
return retval;
}
static ssize_t configfs_bin_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct file *file = iocb->ki_filp;
struct configfs_fragment *frag = to_frag(file);
struct configfs_buffer *buffer = file->private_data;
ssize_t retval = 0;
ssize_t len;
mutex_lock(&buffer->mutex);
/* we don't support switching read/write modes */
if (buffer->write_in_progress) {
retval = -ETXTBSY;
goto out;
}
buffer->read_in_progress = true;
if (buffer->needs_read_fill) {
/* perform first read with buf == NULL to get extent */
down_read(&frag->frag_sem);
if (!frag->frag_dead)
len = buffer->bin_attr->read(buffer->item, NULL, 0);
else
len = -ENOENT;
up_read(&frag->frag_sem);
if (len <= 0) {
retval = len;
goto out;
}
/* do not exceed the maximum value */
if (buffer->cb_max_size && len > buffer->cb_max_size) {
retval = -EFBIG;
goto out;
}
buffer->bin_buffer = vmalloc(len);
if (buffer->bin_buffer == NULL) {
retval = -ENOMEM;
goto out;
}
buffer->bin_buffer_size = len;
/* perform second read to fill buffer */
down_read(&frag->frag_sem);
if (!frag->frag_dead)
len = buffer->bin_attr->read(buffer->item,
buffer->bin_buffer, len);
else
len = -ENOENT;
up_read(&frag->frag_sem);
if (len < 0) {
retval = len;
vfree(buffer->bin_buffer);
buffer->bin_buffer_size = 0;
buffer->bin_buffer = NULL;
goto out;
}
buffer->needs_read_fill = 0;
}
if (iocb->ki_pos >= buffer->bin_buffer_size)
goto out;
retval = copy_to_iter(buffer->bin_buffer + iocb->ki_pos,
buffer->bin_buffer_size - iocb->ki_pos, to);
iocb->ki_pos += retval;
if (retval == 0)
retval = -EFAULT;
out:
mutex_unlock(&buffer->mutex);
return retval;
}
/* Fill @buffer with data coming from @from. */
static int fill_write_buffer(struct configfs_buffer *buffer,
struct iov_iter *from)
{
int copied;
if (!buffer->page)
buffer->page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
copied = copy_from_iter(buffer->page, SIMPLE_ATTR_SIZE - 1, from);
buffer->needs_read_fill = 1;
/* if buf is assumed to contain a string, terminate it by \0,
* so e.g. sscanf() can scan the string easily */
buffer->page[copied] = 0;
return copied ? : -EFAULT;
}
static int
flush_write_buffer(struct file *file, struct configfs_buffer *buffer, size_t count)
{
struct configfs_fragment *frag = to_frag(file);
int res = -ENOENT;
down_read(&frag->frag_sem);
if (!frag->frag_dead)
res = buffer->attr->store(buffer->item, buffer->page, count);
up_read(&frag->frag_sem);
return res;
}
/*
* 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 value
* you're changing, then write entire buffer back.
*/
static ssize_t configfs_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct configfs_buffer *buffer = file->private_data;
int len;
mutex_lock(&buffer->mutex);
len = fill_write_buffer(buffer, from);
if (len > 0)
len = flush_write_buffer(file, buffer, len);
if (len > 0)
iocb->ki_pos += len;
mutex_unlock(&buffer->mutex);
return len;
}
static ssize_t configfs_bin_write_iter(struct kiocb *iocb,
struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct configfs_buffer *buffer = file->private_data;
void *tbuf = NULL;
size_t end_offset;
ssize_t len;
mutex_lock(&buffer->mutex);
/* we don't support switching read/write modes */
if (buffer->read_in_progress) {
len = -ETXTBSY;
goto out;
}
buffer->write_in_progress = true;
/* buffer grows? */
end_offset = iocb->ki_pos + iov_iter_count(from);
if (end_offset > buffer->bin_buffer_size) {
if (buffer->cb_max_size && end_offset > buffer->cb_max_size) {
len = -EFBIG;
goto out;
}
tbuf = vmalloc(end_offset);
if (tbuf == NULL) {
len = -ENOMEM;
goto out;
}
/* copy old contents */
if (buffer->bin_buffer) {
memcpy(tbuf, buffer->bin_buffer,
buffer->bin_buffer_size);
vfree(buffer->bin_buffer);
}
/* clear the new area */
memset(tbuf + buffer->bin_buffer_size, 0,
end_offset - buffer->bin_buffer_size);
buffer->bin_buffer = tbuf;
buffer->bin_buffer_size = end_offset;
}
len = copy_from_iter(buffer->bin_buffer + iocb->ki_pos,
buffer->bin_buffer_size - iocb->ki_pos, from);
iocb->ki_pos += len;
out:
mutex_unlock(&buffer->mutex);
return len ? : -EFAULT;
}
static int __configfs_open_file(struct inode *inode, struct file *file, int type)
{
struct dentry *dentry = file->f_path.dentry;
struct configfs_fragment *frag = to_frag(file);
struct configfs_attribute *attr;
struct configfs_buffer *buffer;
int error;
error = -ENOMEM;
buffer = kzalloc_obj(struct configfs_buffer);
if (!buffer)
goto out;
error = -ENOENT;
down_read(&frag->frag_sem);
if (unlikely(frag->frag_dead))
goto out_free_buffer;
error = -EINVAL;
buffer->item = to_item(dentry->d_parent);
if (!buffer->item)
goto out_free_buffer;
attr = to_attr(dentry);
if (!attr)
goto out_free_buffer;
if (type & CONFIGFS_ITEM_BIN_ATTR) {
buffer->bin_attr = to_bin_attr(dentry);
buffer->cb_max_size = buffer->bin_attr->cb_max_size;
} else {
buffer->attr = attr;
}
buffer->owner = attr->ca_owner;
/* Grab the module reference for this attribute if we have one */
error = -ENODEV;
if (!try_module_get(buffer->owner))
goto out_free_buffer;
error = -EACCES;
if (!buffer->item->ci_type)
goto out_put_module;
buffer->ops = buffer->item->ci_type->ct_item_ops;
/* 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) {
if (!(inode->i_mode & S_IWUGO))
goto out_put_module;
if ((type & CONFIGFS_ITEM_ATTR) && !attr->store)
goto out_put_module;
if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->write)
goto out_put_module;
}
/* 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))
goto out_put_module;
if ((type & CONFIGFS_ITEM_ATTR) && !attr->show)
goto out_put_module;
if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->read)
goto out_put_module;
}
mutex_init(&buffer->mutex);
buffer->needs_read_fill = 1;
buffer->read_in_progress = false;
buffer->write_in_progress = false;
file->private_data = buffer;
up_read(&frag->frag_sem);
return 0;
out_put_module:
module_put(buffer->owner);
out_free_buffer:
up_read(&frag->frag_sem);
kfree(buffer);
out:
return error;
}
static int configfs_release(struct inode *inode, struct file *filp)
{
struct configfs_buffer *buffer = filp->private_data;
module_put(buffer->owner);
kfree(buffer->page);
mutex_destroy(&buffer->mutex);
kfree(buffer);
return 0;
}
static int configfs_open_file(struct inode *inode, struct file *filp)
{
return __configfs_open_file(inode, filp, CONFIGFS_ITEM_ATTR);
}
static int configfs_open_bin_file(struct inode *inode, struct file *filp)
{
return __configfs_open_file(inode, filp, CONFIGFS_ITEM_BIN_ATTR);
}
static int configfs_release_bin_file(struct inode *inode, struct file *file)
{
struct configfs_buffer *buffer = file->private_data;
if (buffer->write_in_progress) {
struct configfs_fragment *frag = to_frag(file);
down_read(&frag->frag_sem);
if (!frag->frag_dead) {
/* result of ->release() is ignored */
buffer->bin_attr->write(buffer->item,
buffer->bin_buffer,
buffer->bin_buffer_size);
}
up_read(&frag->frag_sem);
}
vfree(buffer->bin_buffer);
configfs_release(inode, file);
return 0;
}
const struct file_operations configfs_file_operations = {
.read_iter = configfs_read_iter,
.write_iter = configfs_write_iter,
.llseek = generic_file_llseek,
.open = configfs_open_file,
.release = configfs_release,
};
const struct file_operations configfs_bin_file_operations = {
.read_iter = configfs_bin_read_iter,
.write_iter = configfs_bin_write_iter,
.llseek = NULL, /* bin file is not seekable */
.open = configfs_open_bin_file,
.release = configfs_release_bin_file,
};
/**
* configfs_create_file - create an attribute file for an item.
* @item: item we're creating for.
* @attr: atrribute descriptor.
*/
int configfs_create_file(struct config_item * item, const struct configfs_attribute * attr)
{
struct dentry *dir = item->ci_dentry;
struct configfs_dirent *parent_sd = dir->d_fsdata;
umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG;
int error = 0;
inode_lock_nested(d_inode(dir), I_MUTEX_NORMAL);
error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode,
CONFIGFS_ITEM_ATTR, parent_sd->s_frag);
inode_unlock(d_inode(dir));
return error;
}
/**
* configfs_create_bin_file - create a binary attribute file for an item.
* @item: item we're creating for.
* @bin_attr: atrribute descriptor.
*/
int configfs_create_bin_file(struct config_item *item,
const struct configfs_bin_attribute *bin_attr)
{
struct dentry *dir = item->ci_dentry;
struct configfs_dirent *parent_sd = dir->d_fsdata;
umode_t mode = (bin_attr->cb_attr.ca_mode & S_IALLUGO) | S_IFREG;
int error = 0;
inode_lock_nested(dir->d_inode, I_MUTEX_NORMAL);
error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode,
CONFIGFS_ITEM_BIN_ATTR, parent_sd->s_frag);
inode_unlock(dir->d_inode);
return error;
}
+194
View File
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* inode.c - basic inode and dentry operations.
*
* Based on sysfs:
* sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
*
* configfs Copyright (C) 2005 Oracle. All rights reserved.
*
* Please see Documentation/filesystems/configfs.rst for more
* information.
*/
#undef DEBUG
#include <linux/pagemap.h>
#include <linux/namei.h>
#include <linux/backing-dev.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/lockdep.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include "configfs_internal.h"
#ifdef CONFIG_LOCKDEP
static struct lock_class_key default_group_class[MAX_LOCK_DEPTH];
#endif
static const struct inode_operations configfs_inode_operations ={
.setattr = configfs_setattr,
};
int configfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *iattr)
{
struct inode * inode = d_inode(dentry);
struct configfs_dirent * sd = dentry->d_fsdata;
struct iattr * sd_iattr;
unsigned int ia_valid = iattr->ia_valid;
int error;
if (!sd)
return -EINVAL;
sd_iattr = sd->s_iattr;
if (!sd_iattr) {
/* setting attributes for the first time, allocate now */
sd_iattr = kzalloc_obj(struct iattr);
if (!sd_iattr)
return -ENOMEM;
/* assign default attributes */
sd_iattr->ia_mode = sd->s_mode;
sd_iattr->ia_uid = GLOBAL_ROOT_UID;
sd_iattr->ia_gid = GLOBAL_ROOT_GID;
sd_iattr->ia_atime = sd_iattr->ia_mtime =
sd_iattr->ia_ctime = current_time(inode);
sd->s_iattr = sd_iattr;
}
/* attributes were changed atleast once in past */
error = simple_setattr(idmap, dentry, iattr);
if (error)
return error;
if (ia_valid & ATTR_UID)
sd_iattr->ia_uid = iattr->ia_uid;
if (ia_valid & ATTR_GID)
sd_iattr->ia_gid = iattr->ia_gid;
if (ia_valid & ATTR_ATIME)
sd_iattr->ia_atime = iattr->ia_atime;
if (ia_valid & ATTR_MTIME)
sd_iattr->ia_mtime = iattr->ia_mtime;
if (ia_valid & ATTR_CTIME)
sd_iattr->ia_ctime = iattr->ia_ctime;
if (ia_valid & ATTR_MODE) {
umode_t mode = iattr->ia_mode;
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
mode &= ~S_ISGID;
sd_iattr->ia_mode = sd->s_mode = mode;
}
return error;
}
static inline void set_default_inode_attr(struct inode * inode, umode_t mode)
{
inode->i_mode = mode;
simple_inode_init_ts(inode);
}
static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
{
inode->i_mode = iattr->ia_mode;
inode->i_uid = iattr->ia_uid;
inode->i_gid = iattr->ia_gid;
inode_set_atime_to_ts(inode, iattr->ia_atime);
inode_set_mtime_to_ts(inode, iattr->ia_mtime);
inode_set_ctime_to_ts(inode, iattr->ia_ctime);
}
struct inode *configfs_new_inode(umode_t mode, struct configfs_dirent *sd,
struct super_block *s)
{
struct inode * inode = new_inode(s);
if (inode) {
inode->i_ino = get_next_ino();
inode->i_mapping->a_ops = &ram_aops;
inode->i_op = &configfs_inode_operations;
if (sd->s_iattr) {
/* sysfs_dirent has non-default attributes
* get them for the new inode from persistent copy
* in sysfs_dirent
*/
set_inode_attr(inode, sd->s_iattr);
} else
set_default_inode_attr(inode, mode);
}
return inode;
}
#ifdef CONFIG_LOCKDEP
static void configfs_set_inode_lock_class(struct configfs_dirent *sd,
struct inode *inode)
{
int depth = sd->s_depth;
if (depth > 0) {
if (depth <= ARRAY_SIZE(default_group_class)) {
lockdep_set_class(&inode->i_rwsem,
&default_group_class[depth - 1]);
} else {
/*
* In practice the maximum level of locking depth is
* already reached. Just inform about possible reasons.
*/
pr_info("Too many levels of inodes for the locking correctness validator.\n");
pr_info("Spurious warnings may appear.\n");
}
}
}
#else /* CONFIG_LOCKDEP */
static void configfs_set_inode_lock_class(struct configfs_dirent *sd,
struct inode *inode)
{
}
#endif /* CONFIG_LOCKDEP */
struct inode *configfs_create(struct dentry *dentry, umode_t mode)
{
struct inode *inode = NULL;
struct configfs_dirent *sd;
if (!dentry)
return ERR_PTR(-ENOENT);
if (d_really_is_positive(dentry))
return ERR_PTR(-EEXIST);
sd = dentry->d_fsdata;
inode = configfs_new_inode(mode, sd, dentry->d_sb);
if (!inode)
return ERR_PTR(-ENOMEM);
configfs_set_inode_lock_class(sd, inode);
return inode;
}
/*
* Get the name for corresponding element represented by the given configfs_dirent
*/
const unsigned char * configfs_get_name(struct configfs_dirent *sd)
{
struct configfs_attribute *attr;
BUG_ON(!sd || !sd->s_element);
/* These always have a dentry, so use that */
if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK))
return sd->s_dentry->d_name.name;
if (sd->s_type & (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)) {
attr = sd->s_element;
return attr->ca_name;
}
return NULL;
}
+190
View File
@@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* item.c - library routines for handling generic config items
*
* Based on kobject:
* kobject is Copyright (c) 2002-2003 Patrick Mochel
*
* configfs Copyright (C) 2005 Oracle. All rights reserved.
*
* Please see the file Documentation/filesystems/configfs.rst for
* critical information about using the config_item interface.
*/
#include <linux/string.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/configfs.h>
static inline struct config_item *to_item(struct list_head *entry)
{
return container_of(entry, struct config_item, ci_entry);
}
/* Evil kernel */
static void config_item_release(struct kref *kref);
/**
* config_item_init - initialize item.
* @item: item in question.
*/
static void config_item_init(struct config_item *item)
{
kref_init(&item->ci_kref);
INIT_LIST_HEAD(&item->ci_entry);
}
/**
* config_item_set_name - Set the name of an item
* @item: item.
* @fmt: The vsnprintf()'s format string.
*
* If strlen(name) >= CONFIGFS_ITEM_NAME_LEN, then use a
* dynamically allocated string that @item->ci_name points to.
* Otherwise, use the static @item->ci_namebuf array.
*/
int config_item_set_name(struct config_item *item, const char *fmt, ...)
{
int limit = CONFIGFS_ITEM_NAME_LEN;
int need;
va_list args;
char *name;
/*
* First, try the static array
*/
va_start(args, fmt);
need = vsnprintf(item->ci_namebuf, limit, fmt, args);
va_end(args);
if (need < limit)
name = item->ci_namebuf;
else {
va_start(args, fmt);
name = kvasprintf(GFP_KERNEL, fmt, args);
va_end(args);
if (!name)
return -ENOMEM;
}
/* Free the old name, if necessary. */
if (item->ci_name && item->ci_name != item->ci_namebuf)
kfree(item->ci_name);
/* Now, set the new name */
item->ci_name = name;
return 0;
}
EXPORT_SYMBOL(config_item_set_name);
void config_item_init_type_name(struct config_item *item,
const char *name,
const struct config_item_type *type)
{
config_item_set_name(item, "%s", name);
item->ci_type = type;
config_item_init(item);
}
EXPORT_SYMBOL(config_item_init_type_name);
void config_group_init_type_name(struct config_group *group, const char *name,
const struct config_item_type *type)
{
config_item_set_name(&group->cg_item, "%s", name);
group->cg_item.ci_type = type;
config_group_init(group);
}
EXPORT_SYMBOL(config_group_init_type_name);
struct config_item *config_item_get(struct config_item *item)
{
if (item)
kref_get(&item->ci_kref);
return item;
}
EXPORT_SYMBOL(config_item_get);
struct config_item *config_item_get_unless_zero(struct config_item *item)
{
if (item && kref_get_unless_zero(&item->ci_kref))
return item;
return NULL;
}
EXPORT_SYMBOL(config_item_get_unless_zero);
static void config_item_cleanup(struct config_item *item)
{
const struct config_item_type *t = item->ci_type;
struct config_group *s = item->ci_group;
struct config_item *parent = item->ci_parent;
pr_debug("config_item %s: cleaning up\n", config_item_name(item));
if (item->ci_name != item->ci_namebuf)
kfree(item->ci_name);
item->ci_name = NULL;
if (t && t->ct_item_ops && t->ct_item_ops->release)
t->ct_item_ops->release(item);
if (s)
config_group_put(s);
if (parent)
config_item_put(parent);
}
static void config_item_release(struct kref *kref)
{
config_item_cleanup(container_of(kref, struct config_item, ci_kref));
}
/**
* config_item_put - decrement refcount for item.
* @item: item.
*
* Decrement the refcount, and if 0, call config_item_cleanup().
*/
void config_item_put(struct config_item *item)
{
if (item)
kref_put(&item->ci_kref, config_item_release);
}
EXPORT_SYMBOL(config_item_put);
/**
* config_group_init - initialize a group for use
* @group: config_group
*/
void config_group_init(struct config_group *group)
{
config_item_init(&group->cg_item);
INIT_LIST_HEAD(&group->cg_children);
INIT_LIST_HEAD(&group->default_groups);
}
EXPORT_SYMBOL(config_group_init);
/**
* config_group_find_item - search for item in group.
* @group: group we're looking in.
* @name: item's name.
*
* Iterate over @group->cg_list, looking for a matching config_item.
* If matching item is found take a reference and return the item.
* Caller must have locked group via @group->cg_subsys->su_mtx.
*/
struct config_item *config_group_find_item(struct config_group *group,
const char *name)
{
struct list_head *entry;
struct config_item *ret = NULL;
list_for_each(entry, &group->cg_children) {
struct config_item *item = to_item(entry);
if (config_item_name(item) &&
!strcmp(config_item_name(item), name)) {
ret = config_item_get(item);
break;
}
}
return ret;
}
EXPORT_SYMBOL(config_group_find_item);
+179
View File
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* mount.c - operations for initializing and mounting configfs.
*
* Based on sysfs:
* sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
*
* configfs Copyright (C) 2005 Oracle. All rights reserved.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/fs_context.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include "configfs_internal.h"
/* Random magic number */
#define CONFIGFS_MAGIC 0x62656570
static struct vfsmount *configfs_mount = NULL;
struct kmem_cache *configfs_dir_cachep;
static int configfs_mnt_count = 0;
static void configfs_free_inode(struct inode *inode)
{
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
free_inode_nonrcu(inode);
}
static const struct super_operations configfs_ops = {
.statfs = simple_statfs,
.drop_inode = inode_just_drop,
.free_inode = configfs_free_inode,
};
static struct config_group configfs_root_group = {
.cg_item = {
.ci_namebuf = "root",
.ci_name = configfs_root_group.cg_item.ci_namebuf,
},
};
int configfs_is_root(struct config_item *item)
{
return item == &configfs_root_group.cg_item;
}
static struct configfs_dirent configfs_root = {
.s_sibling = LIST_HEAD_INIT(configfs_root.s_sibling),
.s_children = LIST_HEAD_INIT(configfs_root.s_children),
.s_element = &configfs_root_group.cg_item,
.s_type = CONFIGFS_ROOT,
.s_iattr = NULL,
};
static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct inode *inode;
struct dentry *root;
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_magic = CONFIGFS_MAGIC;
sb->s_op = &configfs_ops;
sb->s_time_gran = 1;
inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
&configfs_root, sb);
if (inode) {
inode->i_op = &configfs_root_inode_operations;
inode->i_fop = &configfs_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
} else {
pr_debug("could not get root inode\n");
return -ENOMEM;
}
root = d_make_root(inode);
if (!root) {
pr_debug("%s: could not get root dentry!\n",__func__);
return -ENOMEM;
}
config_group_init(&configfs_root_group);
configfs_root_group.cg_item.ci_dentry = root;
root->d_fsdata = &configfs_root;
sb->s_root = root;
set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
sb->s_d_flags |= DCACHE_DONTCACHE;
return 0;
}
static int configfs_get_tree(struct fs_context *fc)
{
return get_tree_single(fc, configfs_fill_super);
}
static const struct fs_context_operations configfs_context_ops = {
.get_tree = configfs_get_tree,
};
static int configfs_init_fs_context(struct fs_context *fc)
{
fc->ops = &configfs_context_ops;
return 0;
}
static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE,
.name = "configfs",
.init_fs_context = configfs_init_fs_context,
.kill_sb = kill_anon_super,
};
MODULE_ALIAS_FS("configfs");
struct dentry *configfs_pin_fs(void)
{
int err = simple_pin_fs(&configfs_fs_type, &configfs_mount,
&configfs_mnt_count);
return err ? ERR_PTR(err) : configfs_mount->mnt_root;
}
void configfs_release_fs(void)
{
simple_release_fs(&configfs_mount, &configfs_mnt_count);
}
static int __init configfs_init(void)
{
int err = -ENOMEM;
configfs_dir_cachep = kmem_cache_create("configfs_dir_cache",
sizeof(struct configfs_dirent),
0, 0, NULL);
if (!configfs_dir_cachep)
goto out;
err = sysfs_create_mount_point(kernel_kobj, "config");
if (err)
goto out2;
err = register_filesystem(&configfs_fs_type);
if (err)
goto out3;
return 0;
out3:
pr_err("Unable to register filesystem!\n");
sysfs_remove_mount_point(kernel_kobj, "config");
out2:
kmem_cache_destroy(configfs_dir_cachep);
configfs_dir_cachep = NULL;
out:
return err;
}
static void __exit configfs_exit(void)
{
unregister_filesystem(&configfs_fs_type);
sysfs_remove_mount_point(kernel_kobj, "config");
kmem_cache_destroy(configfs_dir_cachep);
configfs_dir_cachep = NULL;
}
MODULE_AUTHOR("Oracle");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.2");
MODULE_DESCRIPTION("Simple RAM filesystem for user driven kernel subsystem configuration.");
core_initcall(configfs_init);
module_exit(configfs_exit);
+262
View File
@@ -0,0 +1,262 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* symlink.c - operations for configfs symlinks.
*
* Based on sysfs:
* sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
*
* configfs Copyright (C) 2005 Oracle. All rights reserved.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include "configfs_internal.h"
/* Protects attachments of new symlinks */
DEFINE_MUTEX(configfs_symlink_mutex);
static int item_depth(struct config_item * item)
{
struct config_item * p = item;
int depth = 0;
do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p));
return depth;
}
static int item_path_length(struct config_item * item)
{
struct config_item * p = item;
int length = 1;
do {
length += strlen(config_item_name(p)) + 1;
p = p->ci_parent;
} while (p && !configfs_is_root(p));
return length;
}
static void fill_item_path(struct config_item * item, char * buffer, int length)
{
struct config_item * p;
--length;
for (p = item; p && !configfs_is_root(p); p = p->ci_parent) {
int cur = strlen(config_item_name(p));
/* back up enough to print this bus id with '/' */
length -= cur;
memcpy(buffer + length, config_item_name(p), cur);
*(buffer + --length) = '/';
}
}
static int configfs_get_target_path(struct config_item *item,
struct config_item *target, char *path)
{
int depth, size;
char *s;
depth = item_depth(item);
size = item_path_length(target) + depth * 3 - 1;
if (size > PATH_MAX)
return -ENAMETOOLONG;
pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size);
for (s = path; depth--; s += 3)
strcpy(s,"../");
fill_item_path(target, path, size);
pr_debug("%s: path = '%s'\n", __func__, path);
return 0;
}
static int create_link(struct config_item *parent_item,
struct config_item *item,
struct dentry *dentry)
{
struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata;
char *body;
int ret;
if (!configfs_dirent_is_ready(target_sd))
return -ENOENT;
body = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!body)
return -ENOMEM;
configfs_get(target_sd);
spin_lock(&configfs_dirent_lock);
if (target_sd->s_type & CONFIGFS_USET_DROPPING) {
spin_unlock(&configfs_dirent_lock);
configfs_put(target_sd);
kfree(body);
return -ENOENT;
}
target_sd->s_links++;
spin_unlock(&configfs_dirent_lock);
ret = configfs_get_target_path(parent_item, item, body);
if (!ret)
ret = configfs_create_link(target_sd, parent_item->ci_dentry,
dentry, body);
if (ret) {
spin_lock(&configfs_dirent_lock);
target_sd->s_links--;
spin_unlock(&configfs_dirent_lock);
configfs_put(target_sd);
kfree(body);
}
return ret;
}
static int get_target(const char *symname, struct config_item **target,
struct super_block *sb)
{
struct path path __free(path_put) = {};
int ret;
ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &path);
if (ret)
return ret;
if (path.dentry->d_sb != sb)
return -EPERM;
*target = configfs_get_config_item(path.dentry);
if (!*target)
return -ENOENT;
return 0;
}
int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *symname)
{
int ret;
struct configfs_dirent *sd;
struct config_item *parent_item;
struct config_item *target_item = NULL;
const struct config_item_type *type;
sd = dentry->d_parent->d_fsdata;
/*
* Fake invisibility if dir belongs to a group/default groups hierarchy
* being attached
*/
if (!configfs_dirent_is_ready(sd))
return -ENOENT;
parent_item = configfs_get_config_item(dentry->d_parent);
type = parent_item->ci_type;
ret = -EPERM;
if (!type || !type->ct_item_ops ||
!type->ct_item_ops->allow_link)
goto out_put;
/*
* This is really sick. What they wanted was a hybrid of
* link(2) and symlink(2) - they wanted the target resolved
* at syscall time (as link(2) would've done), be a directory
* (which link(2) would've refused to do) *AND* be a deep
* fucking magic, making the target busy from rmdir POV.
* symlink(2) is nothing of that sort, and the locking it
* gets matches the normal symlink(2) semantics. Without
* attempts to resolve the target (which might very well
* not even exist yet) done prior to locking the parent
* directory. This perversion, OTOH, needs to resolve
* the target, which would lead to obvious deadlocks if
* attempted with any directories locked.
*
* Unfortunately, that garbage is userland ABI and we should've
* said "no" back in 2005. Too late now, so we get to
* play very ugly games with locking.
*
* Try *ANYTHING* of that sort in new code, and you will
* really regret it. Just ask yourself - what could a BOFH
* do to me and do I want to find it out first-hand?
*
* AV, a thoroughly annoyed bastard.
*/
inode_unlock(dir);
ret = get_target(symname, &target_item, dentry->d_sb);
inode_lock(dir);
if (ret)
goto out_put;
if (dentry->d_inode || d_unhashed(dentry))
ret = -EEXIST;
else
ret = inode_permission(&nop_mnt_idmap, dir,
MAY_WRITE | MAY_EXEC);
if (!ret)
ret = type->ct_item_ops->allow_link(parent_item, target_item);
if (!ret) {
mutex_lock(&configfs_symlink_mutex);
ret = create_link(parent_item, target_item, dentry);
mutex_unlock(&configfs_symlink_mutex);
if (ret && type->ct_item_ops->drop_link)
type->ct_item_ops->drop_link(parent_item,
target_item);
}
config_item_put(target_item);
out_put:
config_item_put(parent_item);
return ret;
}
int configfs_unlink(struct inode *dir, struct dentry *dentry)
{
struct configfs_dirent *sd = dentry->d_fsdata, *target_sd;
struct config_item *parent_item;
const struct config_item_type *type;
int ret;
ret = -EPERM; /* What lack-of-symlink returns */
if (!(sd->s_type & CONFIGFS_ITEM_LINK))
goto out;
target_sd = sd->s_element;
parent_item = configfs_get_config_item(dentry->d_parent);
type = parent_item->ci_type;
spin_lock(&configfs_dirent_lock);
list_del_init(&sd->s_sibling);
spin_unlock(&configfs_dirent_lock);
configfs_put(sd);
simple_unlink(dir, dentry);
/*
* drop_link() must be called before
* decrementing target's ->s_links, so that the order of
* drop_link(this, target) and drop_item(target) is preserved.
*/
if (type && type->ct_item_ops &&
type->ct_item_ops->drop_link)
type->ct_item_ops->drop_link(parent_item,
target_sd->s_element);
spin_lock(&configfs_dirent_lock);
target_sd->s_links--;
spin_unlock(&configfs_dirent_lock);
configfs_put(target_sd);
config_item_put(parent_item);
ret = 0;
out:
return ret;
}
const struct inode_operations configfs_symlink_inode_operations = {
.get_link = simple_get_link,
.setattr = configfs_setattr,
};