/** * sysfs_update_file - update the modified timestamp on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { struct sysfs_dirent *victim_sd = NULL; struct dentry *victim = NULL; int rc; rc = -ENOENT; victim_sd = sysfs_get_dirent(kobj->sd, attr->name); if (!victim_sd) goto out; victim = sysfs_get_dentry(victim_sd); if (IS_ERR(victim)) { rc = PTR_ERR(victim); victim = NULL; goto out; } mutex_lock(&victim->d_inode->i_mutex); victim->d_inode->i_mtime = CURRENT_TIME; fsnotify_modify(victim); mutex_unlock(&victim->d_inode->i_mutex); rc = 0; out: dput(victim); sysfs_put(victim_sd); return rc; }
/** * sysfs_chmod_file - update the modified mode value on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * @mode: file permissions. * */ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) { struct sysfs_dirent *victim_sd = NULL; struct dentry *victim = NULL; struct inode * inode; struct iattr newattrs; int rc; rc = -ENOENT; victim_sd = sysfs_get_dirent(kobj->sd, attr->name); if (!victim_sd) goto out; victim = sysfs_get_dentry(victim_sd); if (IS_ERR(victim)) { rc = PTR_ERR(victim); victim = NULL; goto out; } inode = victim->d_inode; mutex_lock(&inode->i_mutex); newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; rc = notify_change(victim, &newattrs); mutex_unlock(&inode->i_mutex); out: dput(victim); sysfs_put(victim_sd); return rc; }
int sysfs_make_shadowed_dir(struct kobject *kobj, void * (*follow_link)(struct dentry *, struct nameidata *)) { struct dentry *dentry; struct inode *inode; struct inode_operations *i_op; /* get dentry for @kobj->sd, dentry of a shadowed dir is pinned */ dentry = sysfs_get_dentry(kobj->sd); if (IS_ERR(dentry)) return PTR_ERR(dentry); inode = dentry->d_inode; if (inode->i_op != &sysfs_dir_inode_operations) { dput(dentry); return -EINVAL; } i_op = kmalloc(sizeof(*i_op), GFP_KERNEL); if (!i_op) return -ENOMEM; memcpy(i_op, &sysfs_dir_inode_operations, sizeof(*i_op)); i_op->follow_link = follow_link; /* Locking of inode->i_op? * Since setting i_op is a single word write and they * are atomic we should be ok here. */ inode->i_op = i_op; return 0; }
/** * sysfs_chmod_file - update the modified mode value on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * @mode: file permissions. * */ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) { struct dentry *dir = kobj->dentry; struct dentry *victim; struct sysfs_dirent *sd; umode_t umode = (mode & S_IALLUGO) | S_IFREG; int res = -ENOENT; down(&dir->d_inode->i_sem); victim = sysfs_get_dentry(dir, attr->name); if (!IS_ERR(victim)) { if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { sd = victim->d_fsdata; attr->mode = mode; sd->s_mode = umode; victim->d_inode->i_mode = umode; dput(victim); res = 0; } } up(&dir->d_inode->i_sem); return res; }
/** * sysfs_update_file - update the modified timestamp on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * * Also call dnotify for the dentry, which lots of userspace programs * use. */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { struct dentry * dir = kobj->dentry; struct dentry * victim; int res = -ENOENT; down(&dir->d_inode->i_sem); victim = sysfs_get_dentry(dir, attr->name); if (!IS_ERR(victim)) { /* make sure dentry is really there */ if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { victim->d_inode->i_mtime = CURRENT_TIME; dnotify_parent(victim, DN_MODIFY); /** * Drop reference from initial sysfs_get_dentry(). */ dput(victim); res = 0; } /** * Drop the reference acquired from sysfs_get_dentry() above. */ dput(victim); } up(&dir->d_inode->i_sem); return res; }
void sysfs_hash_and_remove(struct dentry * dir, const char * name) { struct dentry * victim; down(&dir->d_inode->i_sem); victim = sysfs_get_dentry(dir,name); if (!IS_ERR(victim)) { /* make sure dentry is really there */ if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name, atomic_read(&victim->d_count)); d_drop(victim); /* release the target kobject in case of * a symlink */ if (S_ISLNK(victim->d_inode->i_mode)) kobject_put(victim->d_fsdata); simple_unlink(dir->d_inode,victim); } /* * Drop reference from sysfs_get_dentry() above. */ dput(victim); } up(&dir->d_inode->i_sem); }
struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) { struct sysfs_dirent *parent_sd = kobj->sd->s_parent; struct dentry *dir, *parent, *shadow; struct inode *inode; struct sysfs_dirent *sd; struct sysfs_addrm_cxt acxt; dir = sysfs_get_dentry(kobj->sd); if (IS_ERR(dir)) { sd = (void *)dir; goto out; } parent = dir->d_parent; inode = dir->d_inode; sd = ERR_PTR(-EINVAL); if (!sysfs_is_shadowed_inode(inode)) goto out_dput; shadow = d_alloc(parent, &dir->d_name); if (!shadow) goto nomem; sd = sysfs_new_dirent("_SHADOW_", inode->i_mode, SYSFS_DIR); if (!sd) goto nomem; sd->s_elem.dir.kobj = kobj; sysfs_addrm_start(&acxt, parent_sd); /* add but don't link into children list */ sysfs_add_one(&acxt, sd); /* attach and instantiate dentry */ sysfs_attach_dentry(sd, shadow); d_instantiate(shadow, igrab(inode)); inc_nlink(inode); /* tj: synchronization? */ sysfs_addrm_finish(&acxt); dget(shadow); /* Extra count - pin the dentry in core */ goto out_dput; nomem: dput(shadow); sd = ERR_PTR(-ENOMEM); out_dput: dput(dir); out: return sd; }
int sysfs_add_file(struct dentry * dir, const struct attribute * attr) { struct dentry * dentry; int error; down(&dir->d_inode->i_sem); dentry = sysfs_get_dentry(dir,attr->name); if (!IS_ERR(dentry)) { error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init_file); if (!error) dentry->d_fsdata = (void *)attr; dput(dentry); } else error = PTR_ERR(dentry); up(&dir->d_inode->i_sem); return error; }
void sysfs_rename_dir(struct kobject * kobj, const char *new_name) { struct dentry * new_dentry, * parent; if (!strcmp(kobject_name(kobj), new_name)) return; if (!kobj->parent) return; parent = kobj->parent->dentry; down(&parent->d_inode->i_sem); new_dentry = sysfs_get_dentry(parent, new_name); d_move(kobj->dentry, new_dentry); kobject_set_name(kobj,new_name); up(&parent->d_inode->i_sem); }
/** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. * @target: object we're pointing to. * @name: name of the symlink. */ int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name) { struct dentry * dentry = kobj->dentry; struct dentry * d; int error = 0; down(&dentry->d_inode->i_sem); d = sysfs_get_dentry(dentry,name); if (!IS_ERR(d)) { error = sysfs_create(d, S_IFLNK|S_IRWXUGO, init_symlink); if (!error) /* * associate the link dentry with the target kobject */ d->d_fsdata = kobject_get(target); dput(d); } else error = PTR_ERR(d); up(&dentry->d_inode->i_sem); return error; }
static int create_dir(struct kobject * k, struct dentry * p, const char * n, struct dentry ** d) { int error; down(&p->d_inode->i_sem); *d = sysfs_get_dentry(p,n); if (!IS_ERR(*d)) { error = sysfs_create(*d, S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO, init_dir); if (!error) { (*d)->d_fsdata = k; p->d_inode->i_nlink++; } dput(*d); } else error = PTR_ERR(*d); up(&p->d_inode->i_sem); 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; }