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; }
static void sysfs_kill_sb(struct super_block *sb) { struct sysfs_super_info *info = sysfs_info(sb); /* Remove the superblock from fs_supers/s_instances * so we can't find it, before freeing sysfs_super_info. */ kill_anon_super(sb); free_sysfs_super_info(info); }
static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) { struct sysfs_dirent *sd; int type; if (flags & LOOKUP_RCU) return -ECHILD; sd = dentry->d_fsdata; mutex_lock(&sysfs_mutex); /* The sysfs dirent has been deleted */ if (sd->s_flags & SYSFS_FLAG_REMOVED) goto out_bad; /* The sysfs dirent has been moved? */ if (dentry->d_parent->d_fsdata != sd->s_parent) goto out_bad; /* The sysfs dirent has been renamed */ if (strcmp(dentry->d_name.name, sd->s_name) != 0) goto out_bad; /* The sysfs dirent has been moved to a different namespace */ type = KOBJ_NS_TYPE_NONE; if (sd->s_parent) { type = sysfs_ns_type(sd->s_parent); if (type != KOBJ_NS_TYPE_NONE && sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns) goto out_bad; } mutex_unlock(&sysfs_mutex); out_valid: return 1; out_bad: /* Remove the dentry from the dcache hashes. * If this is a deleted dentry we use d_drop instead of d_delete * so sysfs doesn't need to cope with negative dentries. * * If this is a dentry that has simply been renamed we * use d_drop to remove it from the dcache lookup on its * old parent. If this dentry persists later when a lookup * is performed at its new name the dentry will be readded * to the dcache hashes. */ mutex_unlock(&sysfs_mutex); /* If we have submounts we must allow the vfs caches * to lie about the state of the filesystem to prevent * leaks and other nasty things. */ if (check_submounts_and_drop(dentry) != 0) goto out_valid; return 0; }
static int sysfs_test_super(struct super_block *sb, void *data) { struct sysfs_super_info *sb_info = sysfs_info(sb); struct sysfs_super_info *info = data; enum kobj_ns_type type; int found = 1; for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { if (sb_info->ns[type] != info->ns[type]) found = 0; } return found; }
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; }