static int sysfs_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; struct sysfs_dirent *parent_sd = dentry->d_fsdata; struct sysfs_dirent *pos = file->private_data; enum kobj_ns_type type; const void *ns; type = sysfs_ns_type(parent_sd); ns = sysfs_info(dentry->d_sb)->ns[type]; if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&sysfs_mutex); for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); pos; pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { const char *name = pos->s_name; unsigned int type = dt_type(pos); int len = strlen(name); ino_t ino = pos->s_ino; ctx->pos = pos->s_hash; file->private_data = sysfs_get(pos); mutex_unlock(&sysfs_mutex); if (!dir_emit(ctx, name, len, ino, type)) return 0; mutex_lock(&sysfs_mutex); } mutex_unlock(&sysfs_mutex); file->private_data = NULL; ctx->pos = INT_MAX; return 0; }
/** * __sysfs_add_one - add sysfs_dirent to parent without warning * @acxt: addrm context to use * @sd: sysfs_dirent to be added * @parent_sd: the parent sysfs_dirent to add @sd to * * Get @parent_sd and set @sd->s_parent to it and increment nlink of * the parent inode if @sd is a directory and link into the children * list of the parent. * * This function should be called between calls to * sysfs_addrm_start() and sysfs_addrm_finish() and should be * passed the same @acxt as passed to sysfs_addrm_start(). * * LOCKING: * Determined by sysfs_addrm_start(). * * RETURNS: * 0 on success, -EEXIST if entry with the given name already * exists. */ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd) { struct sysfs_inode_attrs *ps_iattr; int ret; if (!!sysfs_ns_type(parent_sd) != !!sd->s_ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", sysfs_ns_type(parent_sd) ? "required" : "invalid", parent_sd->s_name, sd->s_name); return -EINVAL; } sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); sd->s_parent = sysfs_get(parent_sd); ret = sysfs_link_sibling(sd); if (ret) return ret; /* Update timestamps on the parent */ ps_iattr = parent_sd->s_iattr; if (ps_iattr) { struct iattr *ps_iattrs = &ps_iattr->ia_iattr; ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; } /* Mark the entry added into directory tree */ sd->s_flags &= ~SYSFS_FLAG_REMOVED; return 0; }
static int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp) { struct sysfs_dirent *sd; int error; BUG_ON(!kobj || (!update && !kobj->sd)); /* Updates may happen before the object has been instantiated */ if (unlikely(update && !kobj->sd)) return -EINVAL; if (!grp->attrs && !grp->bin_attrs) { WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n", kobj->name, grp->name ? "" : grp->name); return -EINVAL; } if (grp->name) { error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) return error; } else sd = kobj->sd; sysfs_get(sd); error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) sysfs_remove_subdir(sd); } sysfs_put(sd); return error; }
static int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp) { struct sysfs_dirent *sd; int error; BUG_ON(!kobj || (!update && !kobj->sd)); /* Updates may happen before the object has been instantiated */ if (unlikely(update && !kobj->sd)) return -EINVAL; if (grp->name) { error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) return error; } else sd = kobj->sd; sysfs_get(sd); error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) sysfs_remove_subdir(sd); } sysfs_put(sd); return error; }
/** * sysfs_add_one - add sysfs_dirent to parent * @acxt: addrm context to use * @sd: sysfs_dirent to be added * * Get @acxt->parent_sd and set sd->s_parent to it and increment * nlink of parent inode if @sd is a directory. @sd is NOT * linked into the children list of the parent. The caller * should invoke sysfs_link_sibling() after this function * completes if @sd needs to be on the children list. * * This function should be called between calls to * sysfs_addrm_start() and sysfs_addrm_finish() and should be * passed the same @acxt as passed to sysfs_addrm_start(). * * LOCKING: * Determined by sysfs_addrm_start(). */ void sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { sd->s_parent = sysfs_get(acxt->parent_sd); if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode) inc_nlink(acxt->parent_inode); acxt->cnt++; }
static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, const char *name, int warn) { struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; struct sysfs_dirent *sd = NULL; struct sysfs_addrm_cxt acxt; int error; BUG_ON(!name); if (!kobj) parent_sd = &sysfs_root; else parent_sd = kobj->sd; error = -EFAULT; if (!parent_sd) goto out_put; /* target->sd can go away beneath us but is protected with * sysfs_assoc_lock. Fetch target_sd from it. */ spin_lock(&sysfs_assoc_lock); if (target->sd) target_sd = sysfs_get(target->sd); spin_unlock(&sysfs_assoc_lock); error = -ENOENT; if (!target_sd) goto out_put; error = -ENOMEM; sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) goto out_put; sd->s_symlink.target_sd = target_sd; target_sd = NULL; /* reference is now owned by the symlink */ sysfs_addrm_start(&acxt, parent_sd); if (warn) error = sysfs_add_one(&acxt, sd); else error = __sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (error) goto out_put; return 0; out_put: sysfs_put(target_sd); sysfs_put(sd); return error; }
/** * sysfs_attach_dentry - associate sysfs_dirent with dentry * @sd: target sysfs_dirent * @dentry: dentry to associate * * Associate @sd with @dentry. This is protected by * sysfs_assoc_lock to avoid race with sysfs_d_iput(). * * LOCKING: * mutex_lock(sysfs_mutex) */ static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry) { dentry->d_op = &sysfs_dentry_ops; dentry->d_fsdata = sysfs_get(sd); /* protect sd->s_dentry against sysfs_d_iput */ spin_lock(&sysfs_assoc_lock); sd->s_dentry = dentry; spin_unlock(&sysfs_assoc_lock); d_rehash(dentry); }
/** * sysfs_get_dirent - find and get sysfs_dirent with the given name * @parent_sd: sysfs_dirent to search under * @name: name to look for * * Look for sysfs_dirent with name @name under @parent_sd and get * it if found. * * LOCKING: * Kernel thread context (may sleep). Grabs sysfs_mutex. * * RETURNS: * Pointer to sysfs_dirent if found, NULL if not. */ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name) { struct sysfs_dirent *sd; mutex_lock(&sysfs_mutex); sd = sysfs_find_dirent(parent_sd, name); sysfs_get(sd); mutex_unlock(&sysfs_mutex); return sd; }
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { struct bin_attribute *bin_attr; struct sysfs_inode_attrs *iattrs; inode->i_private = sysfs_get(sd); inode->i_mapping->a_ops = &sysfs_aops; inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; inode->i_op = &sysfs_inode_operations; inode->i_ino = sd->s_ino; lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); iattrs = sd->s_iattr; if (iattrs) { /* sysfs_dirent has non-default attributes * get them for the new inode from persistent copy * in sysfs_dirent */ set_inode_attr(inode, &iattrs->ia_iattr); if (iattrs->ia_secdata) security_inode_notifysecctx(inode, iattrs->ia_secdata, iattrs->ia_secdata_len); } else set_default_inode_attr(inode, sd->s_mode); /* initialize inode according to type */ switch (sysfs_type(sd)) { case SYSFS_DIR: inode->i_op = &sysfs_dir_inode_operations; inode->i_fop = &sysfs_dir_operations; inode->i_nlink = sysfs_count_nlink(sd); break; case SYSFS_KOBJ_ATTR: inode->i_size = PAGE_SIZE; inode->i_fop = &sysfs_file_operations; break; case SYSFS_KOBJ_BIN_ATTR: bin_attr = sd->s_bin_attr.bin_attr; inode->i_size = bin_attr->size; inode->i_fop = &bin_fops; break; case SYSFS_KOBJ_LINK: inode->i_op = &sysfs_symlink_inode_operations; break; default: BUG(); } unlock_new_inode(inode); }
/** * sysfs_remove_file_from_group - remove an attribute file from a group. * @kobj: object we're acting for. * @attr: attribute descriptor. * @group: group name. */ void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group) { struct sysfs_dirent *dir_sd; if (group) dir_sd = sysfs_get_dirent(kobj->sd, NULL, group); else dir_sd = sysfs_get(kobj->sd); if (dir_sd) { sysfs_hash_and_remove(dir_sd, NULL, attr->name); sysfs_put(dir_sd); } }
static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct dentry *dentry = filp->f_path.dentry; struct sysfs_dirent * parent_sd = dentry->d_fsdata; struct sysfs_dirent *pos = filp->private_data; ino_t ino; if (filp->f_pos == 0) { ino = parent_sd->s_ino; if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0) filp->f_pos++; } if (filp->f_pos == 1) { if (parent_sd->s_parent) ino = parent_sd->s_parent->s_ino; else ino = parent_sd->s_ino; if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0) filp->f_pos++; } mutex_lock(&sysfs_mutex); for (pos = sysfs_dir_pos(parent_sd, filp->f_pos, pos); pos; pos = sysfs_dir_next_pos(parent_sd, filp->f_pos, pos)) { const char * name; unsigned int type; int len, ret; name = pos->s_name; len = strlen(name); ino = pos->s_ino; type = dt_type(pos); filp->f_pos = ino; filp->private_data = sysfs_get(pos); mutex_unlock(&sysfs_mutex); ret = filldir(dirent, name, len, filp->f_pos, ino, type); mutex_lock(&sysfs_mutex); if (ret < 0) break; } mutex_unlock(&sysfs_mutex); if ((filp->f_pos > 1) && !pos) { /* EOF */ filp->f_pos = INT_MAX; filp->private_data = NULL; } return 0; }
static int sysfs_dir_open(struct inode *inode, struct file *file) { struct dentry * dentry = file->f_path.dentry; struct sysfs_dirent * parent_sd = dentry->d_fsdata; struct sysfs_dirent * sd; sd = sysfs_new_dirent("_DIR_", 0, 0); if (sd) { mutex_lock(&sysfs_mutex); sd->s_parent = sysfs_get(parent_sd); sysfs_link_sibling(sd); mutex_unlock(&sysfs_mutex); } file->private_data = sd; return sd ? 0 : -ENOMEM; }
void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp) { struct sysfs_dirent *dir_sd = kobj->sd; struct sysfs_dirent *sd; if (grp->name) { sd = sysfs_get_dirent(dir_sd, grp->name); BUG_ON(!sd); } else sd = sysfs_get(dir_sd); remove_files(sd, grp); if (grp->name) sysfs_remove_subdir(sd); sysfs_put(sd); }
/** * 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. */ int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group) { struct sysfs_dirent *dir_sd; int error; if (group) dir_sd = sysfs_get_dirent(kobj->sd, NULL, group); else dir_sd = sysfs_get(kobj->sd); if (!dir_sd) return -ENOENT; error = sysfs_add_file(dir_sd, attr, SYSFS_KOBJ_ATTR); sysfs_put(dir_sd); return error; }
int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, const char *new_name, const void *new_ns) { int error; mutex_lock(&sysfs_mutex); error = 0; if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) && (strcmp(sd->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; if (sysfs_find_dirent(new_parent_sd, new_name, new_ns)) goto out; /* rename sysfs_dirent */ if (strcmp(sd->s_name, new_name) != 0) { error = -ENOMEM; new_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out; kfree(sd->s_name); sd->s_name = new_name; } /* * Move to the appropriate place in the appropriate directories rbtree. */ sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_ns = new_ns; sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); error = 0; out: mutex_unlock(&sysfs_mutex); return error; }
int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, const char *new_name) { const char *dup_name = NULL; int error; mutex_lock(&sysfs_mutex); error = 0; if ((sd->s_parent == new_parent_sd) && (strcmp(sd->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; if (sysfs_find_dirent(new_parent_sd, new_name)) goto out; /* rename sysfs_dirent */ if (strcmp(sd->s_name, new_name) != 0) { error = -ENOMEM; new_name = dup_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out; dup_name = sd->s_name; sd->s_name = new_name; } /* Remove from old parent's list and insert into new parent's list. */ if (sd->s_parent != new_parent_sd) { sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); } error = 0; out: mutex_unlock(&sysfs_mutex); kfree(dup_name); return error; }
/** * __sysfs_add_one - add sysfs_dirent to parent without warning * @acxt: addrm context to use * @sd: sysfs_dirent to be added * * Get @acxt->parent_sd and set sd->s_parent to it and increment * nlink of parent inode if @sd is a directory and link into the * children list of the parent. * * This function should be called between calls to * sysfs_addrm_start() and sysfs_addrm_finish() and should be * passed the same @acxt as passed to sysfs_addrm_start(). * * LOCKING: * Determined by sysfs_addrm_start(). * * RETURNS: * 0 on success, -EEXIST if entry with the given name already * exists. */ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { struct sysfs_inode_attrs *ps_iattr; if (sysfs_find_dirent(acxt->parent_sd, sd->s_name)) return -EEXIST; sd->s_parent = sysfs_get(acxt->parent_sd); sysfs_link_sibling(sd); /* Update timestamps on the parent */ ps_iattr = acxt->parent_sd->s_iattr; if (ps_iattr) { struct iattr *ps_iattrs = &ps_iattr->ia_iattr; ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; } return 0; }
static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret = NULL; struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata; struct sysfs_dirent *sd; struct inode *inode; mutex_lock(&sysfs_mutex); sd = sysfs_find_dirent(parent_sd, dentry->d_name.name); /* no such entry */ if (!sd) { ret = ERR_PTR(-ENOENT); goto out_unlock; } /* attach dentry and inode */ inode = sysfs_get_inode(dir->i_sb, sd); if (!inode) { ret = ERR_PTR(-ENOMEM); goto out_unlock; } /* instantiate and hash dentry */ ret = d_find_alias(inode); if (!ret) { dentry->d_op = &sysfs_dentry_ops; dentry->d_fsdata = sysfs_get(sd); d_add(dentry, inode); } else { d_move(ret, dentry); iput(inode); } out_unlock: mutex_unlock(&sysfs_mutex); return ret; }
void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp) { struct sysfs_dirent *dir_sd = kobj->sd; struct sysfs_dirent *sd; if (grp->name) { sd = sysfs_get_dirent(dir_sd, NULL, grp->name); if (!sd) { WARN(!sd, KERN_WARNING "sysfs group %p not found for " "kobject '%s'\n", grp, kobject_name(kobj)); return; } } else sd = sysfs_get(dir_sd); remove_files(sd, kobj, grp); if (grp->name) sysfs_remove_subdir(sd); sysfs_put(sd); }
static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret = NULL; struct dentry *parent = dentry->d_parent; struct sysfs_dirent *parent_sd = parent->d_fsdata; struct sysfs_dirent *sd; struct inode *inode; enum kobj_ns_type type; const void *ns; mutex_lock(&sysfs_mutex); type = sysfs_ns_type(parent_sd); ns = sysfs_info(dir->i_sb)->ns[type]; sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns); /* no such entry */ if (!sd) { ret = ERR_PTR(-ENOENT); goto out_unlock; } dentry->d_fsdata = sysfs_get(sd); /* attach dentry and inode */ inode = sysfs_get_inode(dir->i_sb, sd); if (!inode) { ret = ERR_PTR(-ENOMEM); goto out_unlock; } /* instantiate and hash dentry */ ret = d_materialise_unique(dentry, inode); out_unlock: mutex_unlock(&sysfs_mutex); return ret; }
int sysfs_create_group(struct kobject * kobj, const struct attribute_group * grp) { struct sysfs_dirent *sd; int error; BUG_ON(!kobj || !kobj->sd); if (grp->name) { error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) return error; } else sd = kobj->sd; sysfs_get(sd); error = create_files(sd, grp); if (error) { if (grp->name) sysfs_remove_subdir(sd); } sysfs_put(sd); return error; }
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { struct bin_attribute *bin_attr; inode->i_private = sysfs_get(sd); inode->i_mapping->a_ops = &sysfs_aops; inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; inode->i_op = &sysfs_inode_operations; set_default_inode_attr(inode, sd->s_mode); sysfs_refresh_inode(sd, inode); /* initialize inode according to type */ switch (sysfs_type(sd)) { case SYSFS_DIR: inode->i_op = &sysfs_dir_inode_operations; inode->i_fop = &sysfs_dir_operations; break; case SYSFS_KOBJ_ATTR: inode->i_size = PAGE_SIZE; inode->i_fop = &sysfs_file_operations; break; case SYSFS_KOBJ_BIN_ATTR: bin_attr = sd->s_bin_attr.bin_attr; inode->i_size = bin_attr->size; inode->i_fop = &bin_fops; break; case SYSFS_KOBJ_LINK: inode->i_op = &sysfs_symlink_inode_operations; break; default: BUG(); } unlock_new_inode(inode); }
static int sysfs_open_file(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; int error; /* need attr_sd for attr and ops, its parent for kobj */ if (!sysfs_get_active_two(attr_sd)) return -ENODEV; /* 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; error = -EACCES; /* No sysfs operations, either from having no subsystem, * or the subsystem have no operations. */ if (!ops) goto err_out; /* 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) || !ops->store) goto err_out; } /* 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 err_out; } /* No error? Great, allocate a buffer for the file, and store it * it in file->private_data for easy access. */ error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); if (!buffer) goto err_out; init_MUTEX(&buffer->sem); buffer->needs_read_fill = 1; buffer->ops = ops; file->private_data = buffer; /* open succeeded, put active references and pin attr_sd */ sysfs_put_active_two(attr_sd); sysfs_get(attr_sd); return 0; err_out: sysfs_put_active_two(attr_sd); return error; }
int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) { struct sysfs_dirent *sd = kobj->sd; struct sysfs_dirent *new_parent_sd; struct dentry *old_parent, *new_parent = NULL; struct dentry *old_dentry = NULL, *new_dentry = NULL; int error; BUG_ON(!sd->s_parent); new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; /* get dentries */ old_dentry = sysfs_get_dentry(sd); if (IS_ERR(old_dentry)) { error = PTR_ERR(old_dentry); goto out_dput; } old_parent = sd->s_parent->s_dentry; new_parent = sysfs_get_dentry(new_parent_sd); if (IS_ERR(new_parent)) { error = PTR_ERR(new_parent); goto out_dput; } if (old_parent->d_inode == new_parent->d_inode) { error = 0; goto out_dput; /* nothing to move */ } again: mutex_lock(&old_parent->d_inode->i_mutex); if (!mutex_trylock(&new_parent->d_inode->i_mutex)) { mutex_unlock(&old_parent->d_inode->i_mutex); goto again; } new_dentry = lookup_one_len(kobj->name, new_parent, strlen(kobj->name)); if (IS_ERR(new_dentry)) { error = PTR_ERR(new_dentry); goto out_unlock; } else error = 0; d_add(new_dentry, NULL); d_move(sd->s_dentry, new_dentry); dput(new_dentry); /* Remove from old parent's list and insert into new parent's list. */ mutex_lock(&sysfs_mutex); sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); mutex_unlock(&sysfs_mutex); out_unlock: mutex_unlock(&new_parent->d_inode->i_mutex); mutex_unlock(&old_parent->d_inode->i_mutex); out_dput: dput(new_parent); dput(old_dentry); dput(new_dentry); return error; }
int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, const char *new_name) { struct sysfs_dirent *sd = kobj->sd; struct dentry *new_parent = NULL; struct dentry *old_dentry = NULL, *new_dentry = NULL; const char *dup_name = NULL; int error; /* get dentries */ old_dentry = sysfs_get_dentry(sd); if (IS_ERR(old_dentry)) { error = PTR_ERR(old_dentry); goto out_dput; } new_parent = sysfs_get_dentry(new_parent_sd); if (IS_ERR(new_parent)) { error = PTR_ERR(new_parent); goto out_dput; } /* lock new_parent and get dentry for new name */ mutex_lock(&new_parent->d_inode->i_mutex); new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); if (IS_ERR(new_dentry)) { error = PTR_ERR(new_dentry); goto out_unlock; } /* By allowing two different directories with the same * d_parent we allow this routine to move between different * shadows of the same directory */ error = -EINVAL; if (old_dentry->d_parent->d_inode != new_parent->d_inode || new_dentry->d_parent->d_inode != new_parent->d_inode || old_dentry == new_dentry) goto out_unlock; error = -EEXIST; if (new_dentry->d_inode) goto out_unlock; /* rename kobject and sysfs_dirent */ error = -ENOMEM; new_name = dup_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out_drop; error = kobject_set_name(kobj, "%s", new_name); if (error) goto out_drop; mutex_lock(&sysfs_mutex); dup_name = sd->s_name; sd->s_name = new_name; /* move under the new parent */ d_add(new_dentry, NULL); d_move(sd->s_dentry, new_dentry); sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); mutex_unlock(&sysfs_mutex); error = 0; goto out_unlock; out_drop: d_drop(new_dentry); out_unlock: mutex_unlock(&new_parent->d_inode->i_mutex); out_dput: kfree(dup_name); dput(new_parent); dput(old_dentry); dput(new_dentry); return error; }
/** * sysfs_get_dentry - get dentry for the given sysfs_dirent * @sd: sysfs_dirent of interest * * Get dentry for @sd. Dentry is looked up if currently not * present. This function climbs sysfs_dirent tree till it * reaches a sysfs_dirent with valid dentry attached and descends * down from there looking up dentry for each step. * * LOCKING: * Kernel thread context (may sleep) * * RETURNS: * Pointer to found dentry on success, ERR_PTR() value on error. */ struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd) { struct sysfs_dirent *cur; struct dentry *parent_dentry, *dentry; int i, depth; /* Find the first parent which has valid s_dentry and get the * dentry. */ mutex_lock(&sysfs_mutex); restart0: spin_lock(&sysfs_assoc_lock); restart1: spin_lock(&dcache_lock); dentry = NULL; depth = 0; cur = sd; while (!cur->s_dentry || !cur->s_dentry->d_inode) { if (cur->s_flags & SYSFS_FLAG_REMOVED) { dentry = ERR_PTR(-ENOENT); depth = 0; break; } cur = cur->s_parent; depth++; } if (!IS_ERR(dentry)) dentry = dget_locked(cur->s_dentry); spin_unlock(&dcache_lock); spin_unlock(&sysfs_assoc_lock); /* from the found dentry, look up depth times */ while (depth--) { /* find and get depth'th ancestor */ for (cur = sd, i = 0; cur && i < depth; i++) cur = cur->s_parent; /* This can happen if tree structure was modified due * to move/rename. Restart. */ if (i != depth) { dput(dentry); goto restart0; } sysfs_get(cur); mutex_unlock(&sysfs_mutex); /* look it up */ parent_dentry = dentry; dentry = lookup_one_len_kern(cur->s_name, parent_dentry, strlen(cur->s_name)); dput(parent_dentry); if (IS_ERR(dentry)) { sysfs_put(cur); return dentry; } mutex_lock(&sysfs_mutex); spin_lock(&sysfs_assoc_lock); /* This, again, can happen if tree structure has * changed and we looked up the wrong thing. Restart. */ if (cur->s_dentry != dentry) { dput(dentry); sysfs_put(cur); goto restart1; } spin_unlock(&sysfs_assoc_lock); sysfs_put(cur); } mutex_unlock(&sysfs_mutex); return dentry; }