📄 dir.c
字号:
inode = ilookup(sysfs_sb, sd->s_ino); if (!inode) return; /* Drop any existing dentries associated with sd. * * For the dentry to be properly freed we need to grab a * reference to the dentry under the dcache lock, unhash it, * and then put it. The playing with the dentry count allows * dput to immediately free the dentry if it is not in use. */repeat: spin_lock(&dcache_lock); list_for_each_entry(dentry, &inode->i_dentry, d_alias) { if (d_unhashed(dentry)) continue; dget_locked(dentry); spin_lock(&dentry->d_lock); __d_drop(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); dput(dentry); goto repeat; } spin_unlock(&dcache_lock); /* adjust nlink and update timestamp */ mutex_lock(&inode->i_mutex); inode->i_ctime = CURRENT_TIME; drop_nlink(inode); if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); mutex_unlock(&inode->i_mutex); iput(inode);}/** * sysfs_addrm_finish - finish up sysfs_dirent add/remove * @acxt: addrm context to finish up * * Finish up sysfs_dirent add/remove. Resources acquired by * sysfs_addrm_start() are released and removed sysfs_dirents are * cleaned up. Timestamps on the parent inode are updated. * * LOCKING: * All mutexes acquired by sysfs_addrm_start() are released. */void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt){ /* release resources acquired by sysfs_addrm_start() */ mutex_unlock(&sysfs_mutex); if (acxt->parent_inode) { struct inode *inode = acxt->parent_inode; /* if added/removed, update timestamps on the parent */ if (acxt->cnt) inode->i_ctime = inode->i_mtime = CURRENT_TIME; mutex_unlock(&inode->i_mutex); iput(inode); } /* kill removed sysfs_dirents */ while (acxt->removed) { struct sysfs_dirent *sd = acxt->removed; acxt->removed = sd->s_sibling; sd->s_sibling = NULL; sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); }}/** * sysfs_find_dirent - find 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. * * LOCKING: * mutex_lock(sysfs_mutex) * * RETURNS: * Pointer to sysfs_dirent if found, NULL if not. */struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name){ struct sysfs_dirent *sd; for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) if (!strcmp(sd->s_name, name)) return sd; return NULL;}/** * 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 int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, const char *name, struct sysfs_dirent **p_sd){ umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; int rc; /* allocate */ sd = sysfs_new_dirent(name, mode, SYSFS_DIR); if (!sd) return -ENOMEM; sd->s_dir.kobj = kobj; /* link in */ sysfs_addrm_start(&acxt, parent_sd); rc = sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (rc == 0) *p_sd = sd; else sysfs_put(sd); return rc;}int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd){ return create_dir(kobj, kobj->sd, name, p_sd);}/** * sysfs_create_dir - create a directory for an object. * @kobj: object we're creating directory for. */int sysfs_create_dir(struct kobject * kobj){ struct sysfs_dirent *parent_sd, *sd; int error = 0; BUG_ON(!kobj); if (kobj->parent) parent_sd = kobj->parent->sd; else parent_sd = &sysfs_root; error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd); if (!error) kobj->sd = sd; return error;}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(sd); if (!inode) { ret = ERR_PTR(-ENOMEM); goto out_unlock; } /* instantiate and hash dentry */ dentry->d_op = &sysfs_dentry_ops; dentry->d_fsdata = sysfs_get(sd); d_instantiate(dentry, inode); d_rehash(dentry); out_unlock: mutex_unlock(&sysfs_mutex); return ret;}const struct inode_operations sysfs_dir_inode_operations = { .lookup = sysfs_lookup, .setattr = sysfs_setattr,};static void remove_dir(struct sysfs_dirent *sd){ struct sysfs_addrm_cxt acxt; sysfs_addrm_start(&acxt, sd->s_parent); sysfs_remove_one(&acxt, sd); sysfs_addrm_finish(&acxt);}void sysfs_remove_subdir(struct sysfs_dirent *sd){ remove_dir(sd);}static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd){ struct sysfs_addrm_cxt acxt; struct sysfs_dirent **pos; if (!dir_sd) return; pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); sysfs_addrm_start(&acxt, dir_sd); pos = &dir_sd->s_dir.children; while (*pos) { struct sysfs_dirent *sd = *pos; if (sysfs_type(sd) != SYSFS_DIR) sysfs_remove_one(&acxt, sd); else pos = &(*pos)->s_sibling; } sysfs_addrm_finish(&acxt); remove_dir(dir_sd);}/** * sysfs_remove_dir - remove an object's directory. * @kobj: object. * * The only thing special about this is that we remove any files in * the directory before we remove the directory, and we've inlined * what used to be sysfs_rmdir() below, instead of calling separately. */void sysfs_remove_dir(struct kobject * kobj){ struct sysfs_dirent *sd = kobj->sd; spin_lock(&sysfs_assoc_lock); kobj->sd = NULL; spin_unlock(&sysfs_assoc_lock); __sysfs_remove_dir(sd);}int sysfs_rename_dir(struct kobject * kobj, const char *new_name){ struct sysfs_dirent *sd = kobj->sd; struct dentry *parent = NULL; struct dentry *old_dentry = NULL, *new_dentry = NULL; const char *dup_name = NULL; int error; mutex_lock(&sysfs_rename_mutex); error = 0; if (strcmp(sd->s_name, new_name) == 0) goto out; /* nothing to rename */ /* get the original dentry */ old_dentry = sysfs_get_dentry(sd); if (IS_ERR(old_dentry)) { error = PTR_ERR(old_dentry); old_dentry = NULL; goto out; } parent = old_dentry->d_parent; /* lock parent and get dentry for new name */ mutex_lock(&parent->d_inode->i_mutex); mutex_lock(&sysfs_mutex); error = -EEXIST; if (sysfs_find_dirent(sd->s_parent, new_name)) goto out_unlock; error = -ENOMEM; new_dentry = d_alloc_name(parent, new_name); if (!new_dentry) goto out_unlock; /* rename kobject and sysfs_dirent */ error = -ENOMEM; new_name = dup_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out_unlock; error = kobject_set_name(kobj, "%s", new_name); if (error) goto out_unlock; dup_name = sd->s_name; sd->s_name = new_name; /* rename */ d_add(new_dentry, NULL); d_move(old_dentry, new_dentry); error = 0; out_unlock: mutex_unlock(&sysfs_mutex); mutex_unlock(&parent->d_inode->i_mutex); kfree(dup_name); dput(old_dentry); dput(new_dentry); out: mutex_unlock(&sysfs_rename_mutex); 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; mutex_lock(&sysfs_rename_mutex); BUG_ON(!sd->s_parent); new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; error = 0; if (sd->s_parent == new_parent_sd) goto out; /* nothing to move */ /* get dentries */ old_dentry = sysfs_get_dentry(sd); if (IS_ERR(old_dentry)) { error = PTR_ERR(old_dentry); old_dentry = NULL; goto out; } old_parent = old_dentry->d_parent; new_parent = sysfs_get_dentry(new_parent_sd); if (IS_ERR(new_parent)) { error = PTR_ERR(new_parent); new_parent = NULL; goto out; }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; } mutex_lock(&sysfs_mutex); error = -EEXIST; if (sysfs_find_dirent(new_parent_sd, sd->s_name)) goto out_unlock; error = -ENOMEM; new_dentry = d_alloc_name(new_parent, sd->s_name); if (!new_dentry) goto out_unlock; error = 0; d_add(new_dentry, NULL); d_move(old_dentry, new_dentry); /* Remove from old parent's list and insert into new parent's list. */ sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); out_unlock: mutex_unlock(&sysfs_mutex); mutex_unlock(&new_parent->d_inode->i_mutex); mutex_unlock(&old_parent->d_inode->i_mutex); out: dput(new_parent); dput(old_dentry); dput(new_dentry); mutex_unlock(&sysfs_rename_mutex); return error;}/* Relationship between s_mode and the DT_xxx types */static inline unsigned char dt_type(struct sysfs_dirent *sd){ return (sd->s_mode >> 12) & 15;}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; 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++; } if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) { mutex_lock(&sysfs_mutex); /* Skip the dentries we have already reported */ pos = parent_sd->s_dir.children; while (pos && (filp->f_pos > pos->s_ino)) pos = pos->s_sibling; for ( ; pos; pos = pos->s_sibling) { const char * name; int len; name = pos->s_name; len = strlen(name); filp->f_pos = ino = pos->s_ino; if (filldir(dirent, name, len, filp->f_pos, ino, dt_type(pos)) < 0) break; } if (!pos) filp->f_pos = INT_MAX; mutex_unlock(&sysfs_mutex); } return 0;}const struct file_operations sysfs_dir_operations = { .read = generic_read_dir, .readdir = sysfs_readdir,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -