1b8441ed2STejun Heo /* 2b8441ed2STejun Heo * fs/kernfs/dir.c - kernfs directory implementation 3b8441ed2STejun Heo * 4b8441ed2STejun Heo * Copyright (c) 2001-3 Patrick Mochel 5b8441ed2STejun Heo * Copyright (c) 2007 SUSE Linux Products GmbH 6b8441ed2STejun Heo * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org> 7b8441ed2STejun Heo * 8b8441ed2STejun Heo * This file is released under the GPLv2. 9b8441ed2STejun Heo */ 10fd7b9f7bSTejun Heo 11fd7b9f7bSTejun Heo #include <linux/fs.h> 12fd7b9f7bSTejun Heo #include <linux/namei.h> 13fd7b9f7bSTejun Heo #include <linux/idr.h> 14fd7b9f7bSTejun Heo #include <linux/slab.h> 15fd7b9f7bSTejun Heo #include <linux/security.h> 16fd7b9f7bSTejun Heo #include <linux/hash.h> 17fd7b9f7bSTejun Heo 18fd7b9f7bSTejun Heo #include "kernfs-internal.h" 19fd7b9f7bSTejun Heo 20fd7b9f7bSTejun Heo DEFINE_MUTEX(sysfs_mutex); 21fd7b9f7bSTejun Heo 22fd7b9f7bSTejun Heo #define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) 23fd7b9f7bSTejun Heo 24fd7b9f7bSTejun Heo /** 25fd7b9f7bSTejun Heo * sysfs_name_hash 26fd7b9f7bSTejun Heo * @name: Null terminated string to hash 27fd7b9f7bSTejun Heo * @ns: Namespace tag to hash 28fd7b9f7bSTejun Heo * 29fd7b9f7bSTejun Heo * Returns 31 bit hash of ns + name (so it fits in an off_t ) 30fd7b9f7bSTejun Heo */ 31fd7b9f7bSTejun Heo static unsigned int sysfs_name_hash(const char *name, const void *ns) 32fd7b9f7bSTejun Heo { 33fd7b9f7bSTejun Heo unsigned long hash = init_name_hash(); 34fd7b9f7bSTejun Heo unsigned int len = strlen(name); 35fd7b9f7bSTejun Heo while (len--) 36fd7b9f7bSTejun Heo hash = partial_name_hash(*name++, hash); 37fd7b9f7bSTejun Heo hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31)); 38fd7b9f7bSTejun Heo hash &= 0x7fffffffU; 39fd7b9f7bSTejun Heo /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ 40fd7b9f7bSTejun Heo if (hash < 1) 41fd7b9f7bSTejun Heo hash += 2; 42fd7b9f7bSTejun Heo if (hash >= INT_MAX) 43fd7b9f7bSTejun Heo hash = INT_MAX - 1; 44fd7b9f7bSTejun Heo return hash; 45fd7b9f7bSTejun Heo } 46fd7b9f7bSTejun Heo 47fd7b9f7bSTejun Heo static int sysfs_name_compare(unsigned int hash, const char *name, 48fd7b9f7bSTejun Heo const void *ns, const struct sysfs_dirent *sd) 49fd7b9f7bSTejun Heo { 50fd7b9f7bSTejun Heo if (hash != sd->s_hash) 51fd7b9f7bSTejun Heo return hash - sd->s_hash; 52fd7b9f7bSTejun Heo if (ns != sd->s_ns) 53fd7b9f7bSTejun Heo return ns - sd->s_ns; 54fd7b9f7bSTejun Heo return strcmp(name, sd->s_name); 55fd7b9f7bSTejun Heo } 56fd7b9f7bSTejun Heo 57fd7b9f7bSTejun Heo static int sysfs_sd_compare(const struct sysfs_dirent *left, 58fd7b9f7bSTejun Heo const struct sysfs_dirent *right) 59fd7b9f7bSTejun Heo { 60fd7b9f7bSTejun Heo return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns, 61fd7b9f7bSTejun Heo right); 62fd7b9f7bSTejun Heo } 63fd7b9f7bSTejun Heo 64fd7b9f7bSTejun Heo /** 65fd7b9f7bSTejun Heo * sysfs_link_sibling - link sysfs_dirent into sibling rbtree 66fd7b9f7bSTejun Heo * @sd: sysfs_dirent of interest 67fd7b9f7bSTejun Heo * 68fd7b9f7bSTejun Heo * Link @sd into its sibling rbtree which starts from 69fd7b9f7bSTejun Heo * sd->s_parent->s_dir.children. 70fd7b9f7bSTejun Heo * 71fd7b9f7bSTejun Heo * Locking: 72fd7b9f7bSTejun Heo * mutex_lock(sysfs_mutex) 73fd7b9f7bSTejun Heo * 74fd7b9f7bSTejun Heo * RETURNS: 75fd7b9f7bSTejun Heo * 0 on susccess -EEXIST on failure. 76fd7b9f7bSTejun Heo */ 77fd7b9f7bSTejun Heo static int sysfs_link_sibling(struct sysfs_dirent *sd) 78fd7b9f7bSTejun Heo { 79fd7b9f7bSTejun Heo struct rb_node **node = &sd->s_parent->s_dir.children.rb_node; 80fd7b9f7bSTejun Heo struct rb_node *parent = NULL; 81fd7b9f7bSTejun Heo 82fd7b9f7bSTejun Heo if (sysfs_type(sd) == SYSFS_DIR) 83fd7b9f7bSTejun Heo sd->s_parent->s_dir.subdirs++; 84fd7b9f7bSTejun Heo 85fd7b9f7bSTejun Heo while (*node) { 86fd7b9f7bSTejun Heo struct sysfs_dirent *pos; 87fd7b9f7bSTejun Heo int result; 88fd7b9f7bSTejun Heo 89fd7b9f7bSTejun Heo pos = to_sysfs_dirent(*node); 90fd7b9f7bSTejun Heo parent = *node; 91fd7b9f7bSTejun Heo result = sysfs_sd_compare(sd, pos); 92fd7b9f7bSTejun Heo if (result < 0) 93fd7b9f7bSTejun Heo node = &pos->s_rb.rb_left; 94fd7b9f7bSTejun Heo else if (result > 0) 95fd7b9f7bSTejun Heo node = &pos->s_rb.rb_right; 96fd7b9f7bSTejun Heo else 97fd7b9f7bSTejun Heo return -EEXIST; 98fd7b9f7bSTejun Heo } 99fd7b9f7bSTejun Heo /* add new node and rebalance the tree */ 100fd7b9f7bSTejun Heo rb_link_node(&sd->s_rb, parent, node); 101fd7b9f7bSTejun Heo rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children); 102fd7b9f7bSTejun Heo return 0; 103fd7b9f7bSTejun Heo } 104fd7b9f7bSTejun Heo 105fd7b9f7bSTejun Heo /** 106fd7b9f7bSTejun Heo * sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree 107fd7b9f7bSTejun Heo * @sd: sysfs_dirent of interest 108fd7b9f7bSTejun Heo * 109fd7b9f7bSTejun Heo * Unlink @sd from its sibling rbtree which starts from 110fd7b9f7bSTejun Heo * sd->s_parent->s_dir.children. 111fd7b9f7bSTejun Heo * 112fd7b9f7bSTejun Heo * Locking: 113fd7b9f7bSTejun Heo * mutex_lock(sysfs_mutex) 114fd7b9f7bSTejun Heo */ 115fd7b9f7bSTejun Heo static void sysfs_unlink_sibling(struct sysfs_dirent *sd) 116fd7b9f7bSTejun Heo { 117fd7b9f7bSTejun Heo if (sysfs_type(sd) == SYSFS_DIR) 118fd7b9f7bSTejun Heo sd->s_parent->s_dir.subdirs--; 119fd7b9f7bSTejun Heo 120fd7b9f7bSTejun Heo rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); 121fd7b9f7bSTejun Heo } 122fd7b9f7bSTejun Heo 123fd7b9f7bSTejun Heo /** 124fd7b9f7bSTejun Heo * sysfs_get_active - get an active reference to sysfs_dirent 125fd7b9f7bSTejun Heo * @sd: sysfs_dirent to get an active reference to 126fd7b9f7bSTejun Heo * 127fd7b9f7bSTejun Heo * Get an active reference of @sd. This function is noop if @sd 128fd7b9f7bSTejun Heo * is NULL. 129fd7b9f7bSTejun Heo * 130fd7b9f7bSTejun Heo * RETURNS: 131fd7b9f7bSTejun Heo * Pointer to @sd on success, NULL on failure. 132fd7b9f7bSTejun Heo */ 133fd7b9f7bSTejun Heo struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) 134fd7b9f7bSTejun Heo { 135fd7b9f7bSTejun Heo if (unlikely(!sd)) 136fd7b9f7bSTejun Heo return NULL; 137fd7b9f7bSTejun Heo 138fd7b9f7bSTejun Heo if (!atomic_inc_unless_negative(&sd->s_active)) 139fd7b9f7bSTejun Heo return NULL; 140fd7b9f7bSTejun Heo 141fd7b9f7bSTejun Heo if (sd->s_flags & SYSFS_FLAG_LOCKDEP) 142fd7b9f7bSTejun Heo rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); 143fd7b9f7bSTejun Heo return sd; 144fd7b9f7bSTejun Heo } 145fd7b9f7bSTejun Heo 146fd7b9f7bSTejun Heo /** 147fd7b9f7bSTejun Heo * sysfs_put_active - put an active reference to sysfs_dirent 148fd7b9f7bSTejun Heo * @sd: sysfs_dirent to put an active reference to 149fd7b9f7bSTejun Heo * 150fd7b9f7bSTejun Heo * Put an active reference to @sd. This function is noop if @sd 151fd7b9f7bSTejun Heo * is NULL. 152fd7b9f7bSTejun Heo */ 153fd7b9f7bSTejun Heo void sysfs_put_active(struct sysfs_dirent *sd) 154fd7b9f7bSTejun Heo { 155fd7b9f7bSTejun Heo int v; 156fd7b9f7bSTejun Heo 157fd7b9f7bSTejun Heo if (unlikely(!sd)) 158fd7b9f7bSTejun Heo return; 159fd7b9f7bSTejun Heo 160fd7b9f7bSTejun Heo if (sd->s_flags & SYSFS_FLAG_LOCKDEP) 161fd7b9f7bSTejun Heo rwsem_release(&sd->dep_map, 1, _RET_IP_); 162fd7b9f7bSTejun Heo v = atomic_dec_return(&sd->s_active); 163fd7b9f7bSTejun Heo if (likely(v != SD_DEACTIVATED_BIAS)) 164fd7b9f7bSTejun Heo return; 165fd7b9f7bSTejun Heo 166fd7b9f7bSTejun Heo /* atomic_dec_return() is a mb(), we'll always see the updated 167fd7b9f7bSTejun Heo * sd->u.completion. 168fd7b9f7bSTejun Heo */ 169fd7b9f7bSTejun Heo complete(sd->u.completion); 170fd7b9f7bSTejun Heo } 171fd7b9f7bSTejun Heo 172fd7b9f7bSTejun Heo /** 173fd7b9f7bSTejun Heo * sysfs_deactivate - deactivate sysfs_dirent 174fd7b9f7bSTejun Heo * @sd: sysfs_dirent to deactivate 175fd7b9f7bSTejun Heo * 176fd7b9f7bSTejun Heo * Deny new active references and drain existing ones. 177fd7b9f7bSTejun Heo */ 178fd7b9f7bSTejun Heo static void sysfs_deactivate(struct sysfs_dirent *sd) 179fd7b9f7bSTejun Heo { 180fd7b9f7bSTejun Heo DECLARE_COMPLETION_ONSTACK(wait); 181fd7b9f7bSTejun Heo int v; 182fd7b9f7bSTejun Heo 183fd7b9f7bSTejun Heo BUG_ON(!(sd->s_flags & SYSFS_FLAG_REMOVED)); 184fd7b9f7bSTejun Heo 185fd7b9f7bSTejun Heo if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF)) 186fd7b9f7bSTejun Heo return; 187fd7b9f7bSTejun Heo 188fd7b9f7bSTejun Heo sd->u.completion = (void *)&wait; 189fd7b9f7bSTejun Heo 190fd7b9f7bSTejun Heo rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_); 191fd7b9f7bSTejun Heo /* atomic_add_return() is a mb(), put_active() will always see 192fd7b9f7bSTejun Heo * the updated sd->u.completion. 193fd7b9f7bSTejun Heo */ 194fd7b9f7bSTejun Heo v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); 195fd7b9f7bSTejun Heo 196fd7b9f7bSTejun Heo if (v != SD_DEACTIVATED_BIAS) { 197fd7b9f7bSTejun Heo lock_contended(&sd->dep_map, _RET_IP_); 198fd7b9f7bSTejun Heo wait_for_completion(&wait); 199fd7b9f7bSTejun Heo } 200fd7b9f7bSTejun Heo 201fd7b9f7bSTejun Heo lock_acquired(&sd->dep_map, _RET_IP_); 202fd7b9f7bSTejun Heo rwsem_release(&sd->dep_map, 1, _RET_IP_); 203fd7b9f7bSTejun Heo } 204fd7b9f7bSTejun Heo 205fd7b9f7bSTejun Heo /** 206fd7b9f7bSTejun Heo * kernfs_get - get a reference count on a sysfs_dirent 207fd7b9f7bSTejun Heo * @sd: the target sysfs_dirent 208fd7b9f7bSTejun Heo */ 209fd7b9f7bSTejun Heo void kernfs_get(struct sysfs_dirent *sd) 210fd7b9f7bSTejun Heo { 211fd7b9f7bSTejun Heo if (sd) { 212fd7b9f7bSTejun Heo WARN_ON(!atomic_read(&sd->s_count)); 213fd7b9f7bSTejun Heo atomic_inc(&sd->s_count); 214fd7b9f7bSTejun Heo } 215fd7b9f7bSTejun Heo } 216fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_get); 217fd7b9f7bSTejun Heo 218fd7b9f7bSTejun Heo /** 219fd7b9f7bSTejun Heo * kernfs_put - put a reference count on a sysfs_dirent 220fd7b9f7bSTejun Heo * @sd: the target sysfs_dirent 221fd7b9f7bSTejun Heo * 222fd7b9f7bSTejun Heo * Put a reference count of @sd and destroy it if it reached zero. 223fd7b9f7bSTejun Heo */ 224fd7b9f7bSTejun Heo void kernfs_put(struct sysfs_dirent *sd) 225fd7b9f7bSTejun Heo { 226fd7b9f7bSTejun Heo struct sysfs_dirent *parent_sd; 227ba7443bcSTejun Heo struct kernfs_root *root; 228fd7b9f7bSTejun Heo 229fd7b9f7bSTejun Heo if (!sd || !atomic_dec_and_test(&sd->s_count)) 230fd7b9f7bSTejun Heo return; 231ba7443bcSTejun Heo root = kernfs_root(sd); 232fd7b9f7bSTejun Heo repeat: 233fd7b9f7bSTejun Heo /* Moving/renaming is always done while holding reference. 234fd7b9f7bSTejun Heo * sd->s_parent won't change beneath us. 235fd7b9f7bSTejun Heo */ 236fd7b9f7bSTejun Heo parent_sd = sd->s_parent; 237fd7b9f7bSTejun Heo 238fd7b9f7bSTejun Heo WARN(!(sd->s_flags & SYSFS_FLAG_REMOVED), 239fd7b9f7bSTejun Heo "sysfs: free using entry: %s/%s\n", 240fd7b9f7bSTejun Heo parent_sd ? parent_sd->s_name : "", sd->s_name); 241fd7b9f7bSTejun Heo 242fd7b9f7bSTejun Heo if (sysfs_type(sd) == SYSFS_KOBJ_LINK) 243fd7b9f7bSTejun Heo kernfs_put(sd->s_symlink.target_sd); 244fd7b9f7bSTejun Heo if (sysfs_type(sd) & SYSFS_COPY_NAME) 245fd7b9f7bSTejun Heo kfree(sd->s_name); 246fd7b9f7bSTejun Heo if (sd->s_iattr && sd->s_iattr->ia_secdata) 247fd7b9f7bSTejun Heo security_release_secctx(sd->s_iattr->ia_secdata, 248fd7b9f7bSTejun Heo sd->s_iattr->ia_secdata_len); 249fd7b9f7bSTejun Heo kfree(sd->s_iattr); 250bc755553STejun Heo ida_simple_remove(&root->ino_ida, sd->s_ino); 251fd7b9f7bSTejun Heo kmem_cache_free(sysfs_dir_cachep, sd); 252fd7b9f7bSTejun Heo 253fd7b9f7bSTejun Heo sd = parent_sd; 254ba7443bcSTejun Heo if (sd) { 255ba7443bcSTejun Heo if (atomic_dec_and_test(&sd->s_count)) 256fd7b9f7bSTejun Heo goto repeat; 257ba7443bcSTejun Heo } else { 258ba7443bcSTejun Heo /* just released the root sd, free @root too */ 259bc755553STejun Heo ida_destroy(&root->ino_ida); 260ba7443bcSTejun Heo kfree(root); 261ba7443bcSTejun Heo } 262fd7b9f7bSTejun Heo } 263fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_put); 264fd7b9f7bSTejun Heo 265fd7b9f7bSTejun Heo static int sysfs_dentry_delete(const struct dentry *dentry) 266fd7b9f7bSTejun Heo { 267fd7b9f7bSTejun Heo struct sysfs_dirent *sd = dentry->d_fsdata; 268fd7b9f7bSTejun Heo return !(sd && !(sd->s_flags & SYSFS_FLAG_REMOVED)); 269fd7b9f7bSTejun Heo } 270fd7b9f7bSTejun Heo 271fd7b9f7bSTejun Heo static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) 272fd7b9f7bSTejun Heo { 273fd7b9f7bSTejun Heo struct sysfs_dirent *sd; 274fd7b9f7bSTejun Heo 275fd7b9f7bSTejun Heo if (flags & LOOKUP_RCU) 276fd7b9f7bSTejun Heo return -ECHILD; 277fd7b9f7bSTejun Heo 278fd7b9f7bSTejun Heo sd = dentry->d_fsdata; 279fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 280fd7b9f7bSTejun Heo 281fd7b9f7bSTejun Heo /* The sysfs dirent has been deleted */ 282fd7b9f7bSTejun Heo if (sd->s_flags & SYSFS_FLAG_REMOVED) 283fd7b9f7bSTejun Heo goto out_bad; 284fd7b9f7bSTejun Heo 285fd7b9f7bSTejun Heo /* The sysfs dirent has been moved? */ 286fd7b9f7bSTejun Heo if (dentry->d_parent->d_fsdata != sd->s_parent) 287fd7b9f7bSTejun Heo goto out_bad; 288fd7b9f7bSTejun Heo 289fd7b9f7bSTejun Heo /* The sysfs dirent has been renamed */ 290fd7b9f7bSTejun Heo if (strcmp(dentry->d_name.name, sd->s_name) != 0) 291fd7b9f7bSTejun Heo goto out_bad; 292fd7b9f7bSTejun Heo 293fd7b9f7bSTejun Heo /* The sysfs dirent has been moved to a different namespace */ 294ac9bba03STejun Heo if (sd->s_parent && kernfs_ns_enabled(sd->s_parent) && 295fd7b9f7bSTejun Heo sysfs_info(dentry->d_sb)->ns != sd->s_ns) 296fd7b9f7bSTejun Heo goto out_bad; 297fd7b9f7bSTejun Heo 298fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 299fd7b9f7bSTejun Heo out_valid: 300fd7b9f7bSTejun Heo return 1; 301fd7b9f7bSTejun Heo out_bad: 302fd7b9f7bSTejun Heo /* Remove the dentry from the dcache hashes. 303fd7b9f7bSTejun Heo * If this is a deleted dentry we use d_drop instead of d_delete 304fd7b9f7bSTejun Heo * so sysfs doesn't need to cope with negative dentries. 305fd7b9f7bSTejun Heo * 306fd7b9f7bSTejun Heo * If this is a dentry that has simply been renamed we 307fd7b9f7bSTejun Heo * use d_drop to remove it from the dcache lookup on its 308fd7b9f7bSTejun Heo * old parent. If this dentry persists later when a lookup 309fd7b9f7bSTejun Heo * is performed at its new name the dentry will be readded 310fd7b9f7bSTejun Heo * to the dcache hashes. 311fd7b9f7bSTejun Heo */ 312fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 313fd7b9f7bSTejun Heo 314fd7b9f7bSTejun Heo /* If we have submounts we must allow the vfs caches 315fd7b9f7bSTejun Heo * to lie about the state of the filesystem to prevent 316fd7b9f7bSTejun Heo * leaks and other nasty things. 317fd7b9f7bSTejun Heo */ 318fd7b9f7bSTejun Heo if (check_submounts_and_drop(dentry) != 0) 319fd7b9f7bSTejun Heo goto out_valid; 320fd7b9f7bSTejun Heo 321fd7b9f7bSTejun Heo return 0; 322fd7b9f7bSTejun Heo } 323fd7b9f7bSTejun Heo 324fd7b9f7bSTejun Heo static void sysfs_dentry_release(struct dentry *dentry) 325fd7b9f7bSTejun Heo { 326fd7b9f7bSTejun Heo kernfs_put(dentry->d_fsdata); 327fd7b9f7bSTejun Heo } 328fd7b9f7bSTejun Heo 329fd7b9f7bSTejun Heo const struct dentry_operations sysfs_dentry_ops = { 330fd7b9f7bSTejun Heo .d_revalidate = sysfs_dentry_revalidate, 331fd7b9f7bSTejun Heo .d_delete = sysfs_dentry_delete, 332fd7b9f7bSTejun Heo .d_release = sysfs_dentry_release, 333fd7b9f7bSTejun Heo }; 334fd7b9f7bSTejun Heo 335bc755553STejun Heo struct sysfs_dirent *sysfs_new_dirent(struct kernfs_root *root, 336bc755553STejun Heo const char *name, umode_t mode, int type) 337fd7b9f7bSTejun Heo { 338fd7b9f7bSTejun Heo char *dup_name = NULL; 339fd7b9f7bSTejun Heo struct sysfs_dirent *sd; 340bc755553STejun Heo int ret; 341fd7b9f7bSTejun Heo 342fd7b9f7bSTejun Heo if (type & SYSFS_COPY_NAME) { 343fd7b9f7bSTejun Heo name = dup_name = kstrdup(name, GFP_KERNEL); 344fd7b9f7bSTejun Heo if (!name) 345fd7b9f7bSTejun Heo return NULL; 346fd7b9f7bSTejun Heo } 347fd7b9f7bSTejun Heo 348fd7b9f7bSTejun Heo sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); 349fd7b9f7bSTejun Heo if (!sd) 350fd7b9f7bSTejun Heo goto err_out1; 351fd7b9f7bSTejun Heo 352bc755553STejun Heo ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL); 353bc755553STejun Heo if (ret < 0) 354fd7b9f7bSTejun Heo goto err_out2; 355bc755553STejun Heo sd->s_ino = ret; 356fd7b9f7bSTejun Heo 357fd7b9f7bSTejun Heo atomic_set(&sd->s_count, 1); 358fd7b9f7bSTejun Heo atomic_set(&sd->s_active, 0); 359fd7b9f7bSTejun Heo 360fd7b9f7bSTejun Heo sd->s_name = name; 361fd7b9f7bSTejun Heo sd->s_mode = mode; 362fd7b9f7bSTejun Heo sd->s_flags = type | SYSFS_FLAG_REMOVED; 363fd7b9f7bSTejun Heo 364fd7b9f7bSTejun Heo return sd; 365fd7b9f7bSTejun Heo 366fd7b9f7bSTejun Heo err_out2: 367fd7b9f7bSTejun Heo kmem_cache_free(sysfs_dir_cachep, sd); 368fd7b9f7bSTejun Heo err_out1: 369fd7b9f7bSTejun Heo kfree(dup_name); 370fd7b9f7bSTejun Heo return NULL; 371fd7b9f7bSTejun Heo } 372fd7b9f7bSTejun Heo 373fd7b9f7bSTejun Heo /** 374fd7b9f7bSTejun Heo * sysfs_addrm_start - prepare for sysfs_dirent add/remove 375fd7b9f7bSTejun Heo * @acxt: pointer to sysfs_addrm_cxt to be used 376fd7b9f7bSTejun Heo * 377fd7b9f7bSTejun Heo * This function is called when the caller is about to add or remove 378fd7b9f7bSTejun Heo * sysfs_dirent. This function acquires sysfs_mutex. @acxt is used 379fd7b9f7bSTejun Heo * to keep and pass context to other addrm functions. 380fd7b9f7bSTejun Heo * 381fd7b9f7bSTejun Heo * LOCKING: 382fd7b9f7bSTejun Heo * Kernel thread context (may sleep). sysfs_mutex is locked on 383fd7b9f7bSTejun Heo * return. 384fd7b9f7bSTejun Heo */ 385fd7b9f7bSTejun Heo void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) 386fd7b9f7bSTejun Heo __acquires(sysfs_mutex) 387fd7b9f7bSTejun Heo { 388fd7b9f7bSTejun Heo memset(acxt, 0, sizeof(*acxt)); 389fd7b9f7bSTejun Heo 390fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 391fd7b9f7bSTejun Heo } 392fd7b9f7bSTejun Heo 393fd7b9f7bSTejun Heo /** 394fd7b9f7bSTejun Heo * sysfs_add_one - add sysfs_dirent to parent without warning 395fd7b9f7bSTejun Heo * @acxt: addrm context to use 396fd7b9f7bSTejun Heo * @sd: sysfs_dirent to be added 397fd7b9f7bSTejun Heo * @parent_sd: the parent sysfs_dirent to add @sd to 398fd7b9f7bSTejun Heo * 399fd7b9f7bSTejun Heo * Get @parent_sd and set @sd->s_parent to it and increment nlink of 400fd7b9f7bSTejun Heo * the parent inode if @sd is a directory and link into the children 401fd7b9f7bSTejun Heo * list of the parent. 402fd7b9f7bSTejun Heo * 403fd7b9f7bSTejun Heo * This function should be called between calls to 404fd7b9f7bSTejun Heo * sysfs_addrm_start() and sysfs_addrm_finish() and should be 405fd7b9f7bSTejun Heo * passed the same @acxt as passed to sysfs_addrm_start(). 406fd7b9f7bSTejun Heo * 407fd7b9f7bSTejun Heo * LOCKING: 408fd7b9f7bSTejun Heo * Determined by sysfs_addrm_start(). 409fd7b9f7bSTejun Heo * 410fd7b9f7bSTejun Heo * RETURNS: 411fd7b9f7bSTejun Heo * 0 on success, -EEXIST if entry with the given name already 412fd7b9f7bSTejun Heo * exists. 413fd7b9f7bSTejun Heo */ 414fd7b9f7bSTejun Heo int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, 415fd7b9f7bSTejun Heo struct sysfs_dirent *parent_sd) 416fd7b9f7bSTejun Heo { 417ac9bba03STejun Heo bool has_ns = kernfs_ns_enabled(parent_sd); 418fd7b9f7bSTejun Heo struct sysfs_inode_attrs *ps_iattr; 419fd7b9f7bSTejun Heo int ret; 420fd7b9f7bSTejun Heo 421fd7b9f7bSTejun Heo if (has_ns != (bool)sd->s_ns) { 422fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 423fd7b9f7bSTejun Heo has_ns ? "required" : "invalid", 424fd7b9f7bSTejun Heo parent_sd->s_name, sd->s_name); 425fd7b9f7bSTejun Heo return -EINVAL; 426fd7b9f7bSTejun Heo } 427fd7b9f7bSTejun Heo 428fd7b9f7bSTejun Heo if (sysfs_type(parent_sd) != SYSFS_DIR) 429fd7b9f7bSTejun Heo return -EINVAL; 430fd7b9f7bSTejun Heo 431fd7b9f7bSTejun Heo sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); 432fd7b9f7bSTejun Heo sd->s_parent = parent_sd; 433fd7b9f7bSTejun Heo kernfs_get(parent_sd); 434fd7b9f7bSTejun Heo 435fd7b9f7bSTejun Heo ret = sysfs_link_sibling(sd); 436fd7b9f7bSTejun Heo if (ret) 437fd7b9f7bSTejun Heo return ret; 438fd7b9f7bSTejun Heo 439fd7b9f7bSTejun Heo /* Update timestamps on the parent */ 440fd7b9f7bSTejun Heo ps_iattr = parent_sd->s_iattr; 441fd7b9f7bSTejun Heo if (ps_iattr) { 442fd7b9f7bSTejun Heo struct iattr *ps_iattrs = &ps_iattr->ia_iattr; 443fd7b9f7bSTejun Heo ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; 444fd7b9f7bSTejun Heo } 445fd7b9f7bSTejun Heo 446fd7b9f7bSTejun Heo /* Mark the entry added into directory tree */ 447fd7b9f7bSTejun Heo sd->s_flags &= ~SYSFS_FLAG_REMOVED; 448fd7b9f7bSTejun Heo 449fd7b9f7bSTejun Heo return 0; 450fd7b9f7bSTejun Heo } 451fd7b9f7bSTejun Heo 452fd7b9f7bSTejun Heo /** 453fd7b9f7bSTejun Heo * sysfs_remove_one - remove sysfs_dirent from parent 454fd7b9f7bSTejun Heo * @acxt: addrm context to use 455fd7b9f7bSTejun Heo * @sd: sysfs_dirent to be removed 456fd7b9f7bSTejun Heo * 457fd7b9f7bSTejun Heo * Mark @sd removed and drop nlink of parent inode if @sd is a 458fd7b9f7bSTejun Heo * directory. @sd is unlinked from the children list. 459fd7b9f7bSTejun Heo * 460fd7b9f7bSTejun Heo * This function should be called between calls to 461fd7b9f7bSTejun Heo * sysfs_addrm_start() and sysfs_addrm_finish() and should be 462fd7b9f7bSTejun Heo * passed the same @acxt as passed to sysfs_addrm_start(). 463fd7b9f7bSTejun Heo * 464fd7b9f7bSTejun Heo * LOCKING: 465fd7b9f7bSTejun Heo * Determined by sysfs_addrm_start(). 466fd7b9f7bSTejun Heo */ 467fd7b9f7bSTejun Heo static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, 468fd7b9f7bSTejun Heo struct sysfs_dirent *sd) 469fd7b9f7bSTejun Heo { 470fd7b9f7bSTejun Heo struct sysfs_inode_attrs *ps_iattr; 471fd7b9f7bSTejun Heo 472fd7b9f7bSTejun Heo /* 473fd7b9f7bSTejun Heo * Removal can be called multiple times on the same node. Only the 474fd7b9f7bSTejun Heo * first invocation is effective and puts the base ref. 475fd7b9f7bSTejun Heo */ 476fd7b9f7bSTejun Heo if (sd->s_flags & SYSFS_FLAG_REMOVED) 477fd7b9f7bSTejun Heo return; 478fd7b9f7bSTejun Heo 479ba7443bcSTejun Heo if (sd->s_parent) { 480fd7b9f7bSTejun Heo sysfs_unlink_sibling(sd); 481fd7b9f7bSTejun Heo 482fd7b9f7bSTejun Heo /* Update timestamps on the parent */ 483fd7b9f7bSTejun Heo ps_iattr = sd->s_parent->s_iattr; 484fd7b9f7bSTejun Heo if (ps_iattr) { 485ba7443bcSTejun Heo ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; 486ba7443bcSTejun Heo ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; 487ba7443bcSTejun Heo } 488fd7b9f7bSTejun Heo } 489fd7b9f7bSTejun Heo 490fd7b9f7bSTejun Heo sd->s_flags |= SYSFS_FLAG_REMOVED; 491fd7b9f7bSTejun Heo sd->u.removed_list = acxt->removed; 492fd7b9f7bSTejun Heo acxt->removed = sd; 493fd7b9f7bSTejun Heo } 494fd7b9f7bSTejun Heo 495fd7b9f7bSTejun Heo /** 496fd7b9f7bSTejun Heo * sysfs_addrm_finish - finish up sysfs_dirent add/remove 497fd7b9f7bSTejun Heo * @acxt: addrm context to finish up 498fd7b9f7bSTejun Heo * 499fd7b9f7bSTejun Heo * Finish up sysfs_dirent add/remove. Resources acquired by 500fd7b9f7bSTejun Heo * sysfs_addrm_start() are released and removed sysfs_dirents are 501fd7b9f7bSTejun Heo * cleaned up. 502fd7b9f7bSTejun Heo * 503fd7b9f7bSTejun Heo * LOCKING: 504fd7b9f7bSTejun Heo * sysfs_mutex is released. 505fd7b9f7bSTejun Heo */ 506fd7b9f7bSTejun Heo void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) 507fd7b9f7bSTejun Heo __releases(sysfs_mutex) 508fd7b9f7bSTejun Heo { 509fd7b9f7bSTejun Heo /* release resources acquired by sysfs_addrm_start() */ 510fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 511fd7b9f7bSTejun Heo 512fd7b9f7bSTejun Heo /* kill removed sysfs_dirents */ 513fd7b9f7bSTejun Heo while (acxt->removed) { 514fd7b9f7bSTejun Heo struct sysfs_dirent *sd = acxt->removed; 515fd7b9f7bSTejun Heo 516fd7b9f7bSTejun Heo acxt->removed = sd->u.removed_list; 517fd7b9f7bSTejun Heo 518fd7b9f7bSTejun Heo sysfs_deactivate(sd); 519fd7b9f7bSTejun Heo sysfs_unmap_bin_file(sd); 520fd7b9f7bSTejun Heo kernfs_put(sd); 521fd7b9f7bSTejun Heo } 522fd7b9f7bSTejun Heo } 523fd7b9f7bSTejun Heo 524fd7b9f7bSTejun Heo /** 525fd7b9f7bSTejun Heo * kernfs_find_ns - find sysfs_dirent with the given name 526fd7b9f7bSTejun Heo * @parent: sysfs_dirent to search under 527fd7b9f7bSTejun Heo * @name: name to look for 528fd7b9f7bSTejun Heo * @ns: the namespace tag to use 529fd7b9f7bSTejun Heo * 530fd7b9f7bSTejun Heo * Look for sysfs_dirent with name @name under @parent. Returns pointer to 531fd7b9f7bSTejun Heo * the found sysfs_dirent on success, %NULL on failure. 532fd7b9f7bSTejun Heo */ 533fd7b9f7bSTejun Heo static struct sysfs_dirent *kernfs_find_ns(struct sysfs_dirent *parent, 534fd7b9f7bSTejun Heo const unsigned char *name, 535fd7b9f7bSTejun Heo const void *ns) 536fd7b9f7bSTejun Heo { 537fd7b9f7bSTejun Heo struct rb_node *node = parent->s_dir.children.rb_node; 538ac9bba03STejun Heo bool has_ns = kernfs_ns_enabled(parent); 539fd7b9f7bSTejun Heo unsigned int hash; 540fd7b9f7bSTejun Heo 541fd7b9f7bSTejun Heo lockdep_assert_held(&sysfs_mutex); 542fd7b9f7bSTejun Heo 543fd7b9f7bSTejun Heo if (has_ns != (bool)ns) { 544fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 545fd7b9f7bSTejun Heo has_ns ? "required" : "invalid", 546fd7b9f7bSTejun Heo parent->s_name, name); 547fd7b9f7bSTejun Heo return NULL; 548fd7b9f7bSTejun Heo } 549fd7b9f7bSTejun Heo 550fd7b9f7bSTejun Heo hash = sysfs_name_hash(name, ns); 551fd7b9f7bSTejun Heo while (node) { 552fd7b9f7bSTejun Heo struct sysfs_dirent *sd; 553fd7b9f7bSTejun Heo int result; 554fd7b9f7bSTejun Heo 555fd7b9f7bSTejun Heo sd = to_sysfs_dirent(node); 556fd7b9f7bSTejun Heo result = sysfs_name_compare(hash, name, ns, sd); 557fd7b9f7bSTejun Heo if (result < 0) 558fd7b9f7bSTejun Heo node = node->rb_left; 559fd7b9f7bSTejun Heo else if (result > 0) 560fd7b9f7bSTejun Heo node = node->rb_right; 561fd7b9f7bSTejun Heo else 562fd7b9f7bSTejun Heo return sd; 563fd7b9f7bSTejun Heo } 564fd7b9f7bSTejun Heo return NULL; 565fd7b9f7bSTejun Heo } 566fd7b9f7bSTejun Heo 567fd7b9f7bSTejun Heo /** 568fd7b9f7bSTejun Heo * kernfs_find_and_get_ns - find and get sysfs_dirent with the given name 569fd7b9f7bSTejun Heo * @parent: sysfs_dirent to search under 570fd7b9f7bSTejun Heo * @name: name to look for 571fd7b9f7bSTejun Heo * @ns: the namespace tag to use 572fd7b9f7bSTejun Heo * 573fd7b9f7bSTejun Heo * Look for sysfs_dirent with name @name under @parent and get a reference 574fd7b9f7bSTejun Heo * if found. This function may sleep and returns pointer to the found 575fd7b9f7bSTejun Heo * sysfs_dirent on success, %NULL on failure. 576fd7b9f7bSTejun Heo */ 577fd7b9f7bSTejun Heo struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, 578fd7b9f7bSTejun Heo const char *name, const void *ns) 579fd7b9f7bSTejun Heo { 580fd7b9f7bSTejun Heo struct sysfs_dirent *sd; 581fd7b9f7bSTejun Heo 582fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 583fd7b9f7bSTejun Heo sd = kernfs_find_ns(parent, name, ns); 584fd7b9f7bSTejun Heo kernfs_get(sd); 585fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 586fd7b9f7bSTejun Heo 587fd7b9f7bSTejun Heo return sd; 588fd7b9f7bSTejun Heo } 589fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); 590fd7b9f7bSTejun Heo 591fd7b9f7bSTejun Heo /** 592ba7443bcSTejun Heo * kernfs_create_root - create a new kernfs hierarchy 593ba7443bcSTejun Heo * @priv: opaque data associated with the new directory 594ba7443bcSTejun Heo * 595ba7443bcSTejun Heo * Returns the root of the new hierarchy on success, ERR_PTR() value on 596ba7443bcSTejun Heo * failure. 597ba7443bcSTejun Heo */ 598ba7443bcSTejun Heo struct kernfs_root *kernfs_create_root(void *priv) 599ba7443bcSTejun Heo { 600ba7443bcSTejun Heo struct kernfs_root *root; 601ba7443bcSTejun Heo struct sysfs_dirent *sd; 602ba7443bcSTejun Heo 603ba7443bcSTejun Heo root = kzalloc(sizeof(*root), GFP_KERNEL); 604ba7443bcSTejun Heo if (!root) 605ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 606ba7443bcSTejun Heo 607bc755553STejun Heo ida_init(&root->ino_ida); 608bc755553STejun Heo 609bc755553STejun Heo sd = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); 610ba7443bcSTejun Heo if (!sd) { 611bc755553STejun Heo ida_destroy(&root->ino_ida); 612ba7443bcSTejun Heo kfree(root); 613ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 614ba7443bcSTejun Heo } 615ba7443bcSTejun Heo 616ba7443bcSTejun Heo sd->s_flags &= ~SYSFS_FLAG_REMOVED; 617ba7443bcSTejun Heo sd->priv = priv; 618ba7443bcSTejun Heo sd->s_dir.root = root; 619ba7443bcSTejun Heo 620ba7443bcSTejun Heo root->sd = sd; 621ba7443bcSTejun Heo 622ba7443bcSTejun Heo return root; 623ba7443bcSTejun Heo } 624ba7443bcSTejun Heo 625ba7443bcSTejun Heo /** 626ba7443bcSTejun Heo * kernfs_destroy_root - destroy a kernfs hierarchy 627ba7443bcSTejun Heo * @root: root of the hierarchy to destroy 628ba7443bcSTejun Heo * 629ba7443bcSTejun Heo * Destroy the hierarchy anchored at @root by removing all existing 630ba7443bcSTejun Heo * directories and destroying @root. 631ba7443bcSTejun Heo */ 632ba7443bcSTejun Heo void kernfs_destroy_root(struct kernfs_root *root) 633ba7443bcSTejun Heo { 634ba7443bcSTejun Heo kernfs_remove(root->sd); /* will also free @root */ 635ba7443bcSTejun Heo } 636ba7443bcSTejun Heo 637ba7443bcSTejun Heo /** 638fd7b9f7bSTejun Heo * kernfs_create_dir_ns - create a directory 639fd7b9f7bSTejun Heo * @parent: parent in which to create a new directory 640fd7b9f7bSTejun Heo * @name: name of the new directory 641fd7b9f7bSTejun Heo * @priv: opaque data associated with the new directory 642fd7b9f7bSTejun Heo * @ns: optional namespace tag of the directory 643fd7b9f7bSTejun Heo * 644fd7b9f7bSTejun Heo * Returns the created node on success, ERR_PTR() value on failure. 645fd7b9f7bSTejun Heo */ 646fd7b9f7bSTejun Heo struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, 647fd7b9f7bSTejun Heo const char *name, void *priv, 648fd7b9f7bSTejun Heo const void *ns) 649fd7b9f7bSTejun Heo { 650fd7b9f7bSTejun Heo umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; 651fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 652fd7b9f7bSTejun Heo struct sysfs_dirent *sd; 653fd7b9f7bSTejun Heo int rc; 654fd7b9f7bSTejun Heo 655fd7b9f7bSTejun Heo /* allocate */ 656bc755553STejun Heo sd = sysfs_new_dirent(kernfs_root(parent), name, mode, SYSFS_DIR); 657fd7b9f7bSTejun Heo if (!sd) 658fd7b9f7bSTejun Heo return ERR_PTR(-ENOMEM); 659fd7b9f7bSTejun Heo 660ba7443bcSTejun Heo sd->s_dir.root = parent->s_dir.root; 661fd7b9f7bSTejun Heo sd->s_ns = ns; 662fd7b9f7bSTejun Heo sd->priv = priv; 663fd7b9f7bSTejun Heo 664fd7b9f7bSTejun Heo /* link in */ 665fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 666fd7b9f7bSTejun Heo rc = sysfs_add_one(&acxt, sd, parent); 667fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 668fd7b9f7bSTejun Heo 669fd7b9f7bSTejun Heo if (!rc) 670fd7b9f7bSTejun Heo return sd; 671fd7b9f7bSTejun Heo 672fd7b9f7bSTejun Heo kernfs_put(sd); 673fd7b9f7bSTejun Heo return ERR_PTR(rc); 674fd7b9f7bSTejun Heo } 675fd7b9f7bSTejun Heo 676fd7b9f7bSTejun Heo static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, 677fd7b9f7bSTejun Heo unsigned int flags) 678fd7b9f7bSTejun Heo { 679fd7b9f7bSTejun Heo struct dentry *ret = NULL; 680fd7b9f7bSTejun Heo struct dentry *parent = dentry->d_parent; 681fd7b9f7bSTejun Heo struct sysfs_dirent *parent_sd = parent->d_fsdata; 682fd7b9f7bSTejun Heo struct sysfs_dirent *sd; 683fd7b9f7bSTejun Heo struct inode *inode; 684fd7b9f7bSTejun Heo const void *ns = NULL; 685fd7b9f7bSTejun Heo 686fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 687fd7b9f7bSTejun Heo 688ac9bba03STejun Heo if (kernfs_ns_enabled(parent_sd)) 689fd7b9f7bSTejun Heo ns = sysfs_info(dir->i_sb)->ns; 690fd7b9f7bSTejun Heo 691fd7b9f7bSTejun Heo sd = kernfs_find_ns(parent_sd, dentry->d_name.name, ns); 692fd7b9f7bSTejun Heo 693fd7b9f7bSTejun Heo /* no such entry */ 694fd7b9f7bSTejun Heo if (!sd) { 695fd7b9f7bSTejun Heo ret = ERR_PTR(-ENOENT); 696fd7b9f7bSTejun Heo goto out_unlock; 697fd7b9f7bSTejun Heo } 698fd7b9f7bSTejun Heo kernfs_get(sd); 699fd7b9f7bSTejun Heo dentry->d_fsdata = sd; 700fd7b9f7bSTejun Heo 701fd7b9f7bSTejun Heo /* attach dentry and inode */ 702fd7b9f7bSTejun Heo inode = sysfs_get_inode(dir->i_sb, sd); 703fd7b9f7bSTejun Heo if (!inode) { 704fd7b9f7bSTejun Heo ret = ERR_PTR(-ENOMEM); 705fd7b9f7bSTejun Heo goto out_unlock; 706fd7b9f7bSTejun Heo } 707fd7b9f7bSTejun Heo 708fd7b9f7bSTejun Heo /* instantiate and hash dentry */ 709fd7b9f7bSTejun Heo ret = d_materialise_unique(dentry, inode); 710fd7b9f7bSTejun Heo out_unlock: 711fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 712fd7b9f7bSTejun Heo return ret; 713fd7b9f7bSTejun Heo } 714fd7b9f7bSTejun Heo 715fd7b9f7bSTejun Heo const struct inode_operations sysfs_dir_inode_operations = { 716fd7b9f7bSTejun Heo .lookup = sysfs_lookup, 717fd7b9f7bSTejun Heo .permission = sysfs_permission, 718fd7b9f7bSTejun Heo .setattr = sysfs_setattr, 719fd7b9f7bSTejun Heo .getattr = sysfs_getattr, 720fd7b9f7bSTejun Heo .setxattr = sysfs_setxattr, 721fd7b9f7bSTejun Heo }; 722fd7b9f7bSTejun Heo 723fd7b9f7bSTejun Heo static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) 724fd7b9f7bSTejun Heo { 725fd7b9f7bSTejun Heo struct sysfs_dirent *last; 726fd7b9f7bSTejun Heo 727fd7b9f7bSTejun Heo while (true) { 728fd7b9f7bSTejun Heo struct rb_node *rbn; 729fd7b9f7bSTejun Heo 730fd7b9f7bSTejun Heo last = pos; 731fd7b9f7bSTejun Heo 732fd7b9f7bSTejun Heo if (sysfs_type(pos) != SYSFS_DIR) 733fd7b9f7bSTejun Heo break; 734fd7b9f7bSTejun Heo 735fd7b9f7bSTejun Heo rbn = rb_first(&pos->s_dir.children); 736fd7b9f7bSTejun Heo if (!rbn) 737fd7b9f7bSTejun Heo break; 738fd7b9f7bSTejun Heo 739fd7b9f7bSTejun Heo pos = to_sysfs_dirent(rbn); 740fd7b9f7bSTejun Heo } 741fd7b9f7bSTejun Heo 742fd7b9f7bSTejun Heo return last; 743fd7b9f7bSTejun Heo } 744fd7b9f7bSTejun Heo 745fd7b9f7bSTejun Heo /** 746fd7b9f7bSTejun Heo * sysfs_next_descendant_post - find the next descendant for post-order walk 747fd7b9f7bSTejun Heo * @pos: the current position (%NULL to initiate traversal) 748fd7b9f7bSTejun Heo * @root: sysfs_dirent whose descendants to walk 749fd7b9f7bSTejun Heo * 750fd7b9f7bSTejun Heo * Find the next descendant to visit for post-order traversal of @root's 751fd7b9f7bSTejun Heo * descendants. @root is included in the iteration and the last node to be 752fd7b9f7bSTejun Heo * visited. 753fd7b9f7bSTejun Heo */ 754fd7b9f7bSTejun Heo static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, 755fd7b9f7bSTejun Heo struct sysfs_dirent *root) 756fd7b9f7bSTejun Heo { 757fd7b9f7bSTejun Heo struct rb_node *rbn; 758fd7b9f7bSTejun Heo 759fd7b9f7bSTejun Heo lockdep_assert_held(&sysfs_mutex); 760fd7b9f7bSTejun Heo 761fd7b9f7bSTejun Heo /* if first iteration, visit leftmost descendant which may be root */ 762fd7b9f7bSTejun Heo if (!pos) 763fd7b9f7bSTejun Heo return sysfs_leftmost_descendant(root); 764fd7b9f7bSTejun Heo 765fd7b9f7bSTejun Heo /* if we visited @root, we're done */ 766fd7b9f7bSTejun Heo if (pos == root) 767fd7b9f7bSTejun Heo return NULL; 768fd7b9f7bSTejun Heo 769fd7b9f7bSTejun Heo /* if there's an unvisited sibling, visit its leftmost descendant */ 770fd7b9f7bSTejun Heo rbn = rb_next(&pos->s_rb); 771fd7b9f7bSTejun Heo if (rbn) 772fd7b9f7bSTejun Heo return sysfs_leftmost_descendant(to_sysfs_dirent(rbn)); 773fd7b9f7bSTejun Heo 774fd7b9f7bSTejun Heo /* no sibling left, visit parent */ 775fd7b9f7bSTejun Heo return pos->s_parent; 776fd7b9f7bSTejun Heo } 777fd7b9f7bSTejun Heo 778fd7b9f7bSTejun Heo static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, 779fd7b9f7bSTejun Heo struct sysfs_dirent *sd) 780fd7b9f7bSTejun Heo { 781fd7b9f7bSTejun Heo struct sysfs_dirent *pos, *next; 782fd7b9f7bSTejun Heo 783fd7b9f7bSTejun Heo if (!sd) 784fd7b9f7bSTejun Heo return; 785fd7b9f7bSTejun Heo 786fd7b9f7bSTejun Heo pr_debug("sysfs %s: removing\n", sd->s_name); 787fd7b9f7bSTejun Heo 788fd7b9f7bSTejun Heo next = NULL; 789fd7b9f7bSTejun Heo do { 790fd7b9f7bSTejun Heo pos = next; 791fd7b9f7bSTejun Heo next = sysfs_next_descendant_post(pos, sd); 792fd7b9f7bSTejun Heo if (pos) 793fd7b9f7bSTejun Heo sysfs_remove_one(acxt, pos); 794fd7b9f7bSTejun Heo } while (next); 795fd7b9f7bSTejun Heo } 796fd7b9f7bSTejun Heo 797fd7b9f7bSTejun Heo /** 798fd7b9f7bSTejun Heo * kernfs_remove - remove a sysfs_dirent recursively 799fd7b9f7bSTejun Heo * @sd: the sysfs_dirent to remove 800fd7b9f7bSTejun Heo * 801fd7b9f7bSTejun Heo * Remove @sd along with all its subdirectories and files. 802fd7b9f7bSTejun Heo */ 803fd7b9f7bSTejun Heo void kernfs_remove(struct sysfs_dirent *sd) 804fd7b9f7bSTejun Heo { 805fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 806fd7b9f7bSTejun Heo 807fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 808fd7b9f7bSTejun Heo __kernfs_remove(&acxt, sd); 809fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 810fd7b9f7bSTejun Heo } 811fd7b9f7bSTejun Heo 812fd7b9f7bSTejun Heo /** 813fd7b9f7bSTejun Heo * kernfs_remove_by_name_ns - find a sysfs_dirent by name and remove it 814fd7b9f7bSTejun Heo * @dir_sd: parent of the target 815fd7b9f7bSTejun Heo * @name: name of the sysfs_dirent to remove 816fd7b9f7bSTejun Heo * @ns: namespace tag of the sysfs_dirent to remove 817fd7b9f7bSTejun Heo * 818fd7b9f7bSTejun Heo * Look for the sysfs_dirent with @name and @ns under @dir_sd and remove 819fd7b9f7bSTejun Heo * it. Returns 0 on success, -ENOENT if such entry doesn't exist. 820fd7b9f7bSTejun Heo */ 821fd7b9f7bSTejun Heo int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, 822fd7b9f7bSTejun Heo const void *ns) 823fd7b9f7bSTejun Heo { 824fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 825fd7b9f7bSTejun Heo struct sysfs_dirent *sd; 826fd7b9f7bSTejun Heo 827fd7b9f7bSTejun Heo if (!dir_sd) { 828fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", 829fd7b9f7bSTejun Heo name); 830fd7b9f7bSTejun Heo return -ENOENT; 831fd7b9f7bSTejun Heo } 832fd7b9f7bSTejun Heo 833fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 834fd7b9f7bSTejun Heo 835fd7b9f7bSTejun Heo sd = kernfs_find_ns(dir_sd, name, ns); 836fd7b9f7bSTejun Heo if (sd) 837fd7b9f7bSTejun Heo __kernfs_remove(&acxt, sd); 838fd7b9f7bSTejun Heo 839fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 840fd7b9f7bSTejun Heo 841fd7b9f7bSTejun Heo if (sd) 842fd7b9f7bSTejun Heo return 0; 843fd7b9f7bSTejun Heo else 844fd7b9f7bSTejun Heo return -ENOENT; 845fd7b9f7bSTejun Heo } 846fd7b9f7bSTejun Heo 847fd7b9f7bSTejun Heo /** 848fd7b9f7bSTejun Heo * kernfs_rename_ns - move and rename a kernfs_node 849fd7b9f7bSTejun Heo * @sd: target node 850fd7b9f7bSTejun Heo * @new_parent: new parent to put @sd under 851fd7b9f7bSTejun Heo * @new_name: new name 852fd7b9f7bSTejun Heo * @new_ns: new namespace tag 853fd7b9f7bSTejun Heo */ 854fd7b9f7bSTejun Heo int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, 855fd7b9f7bSTejun Heo const char *new_name, const void *new_ns) 856fd7b9f7bSTejun Heo { 857fd7b9f7bSTejun Heo int error; 858fd7b9f7bSTejun Heo 859fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 860fd7b9f7bSTejun Heo 861fd7b9f7bSTejun Heo error = 0; 862fd7b9f7bSTejun Heo if ((sd->s_parent == new_parent) && (sd->s_ns == new_ns) && 863fd7b9f7bSTejun Heo (strcmp(sd->s_name, new_name) == 0)) 864fd7b9f7bSTejun Heo goto out; /* nothing to rename */ 865fd7b9f7bSTejun Heo 866fd7b9f7bSTejun Heo error = -EEXIST; 867fd7b9f7bSTejun Heo if (kernfs_find_ns(new_parent, new_name, new_ns)) 868fd7b9f7bSTejun Heo goto out; 869fd7b9f7bSTejun Heo 870fd7b9f7bSTejun Heo /* rename sysfs_dirent */ 871fd7b9f7bSTejun Heo if (strcmp(sd->s_name, new_name) != 0) { 872fd7b9f7bSTejun Heo error = -ENOMEM; 873fd7b9f7bSTejun Heo new_name = kstrdup(new_name, GFP_KERNEL); 874fd7b9f7bSTejun Heo if (!new_name) 875fd7b9f7bSTejun Heo goto out; 876fd7b9f7bSTejun Heo 877fd7b9f7bSTejun Heo kfree(sd->s_name); 878fd7b9f7bSTejun Heo sd->s_name = new_name; 879fd7b9f7bSTejun Heo } 880fd7b9f7bSTejun Heo 881fd7b9f7bSTejun Heo /* 882fd7b9f7bSTejun Heo * Move to the appropriate place in the appropriate directories rbtree. 883fd7b9f7bSTejun Heo */ 884fd7b9f7bSTejun Heo sysfs_unlink_sibling(sd); 885fd7b9f7bSTejun Heo kernfs_get(new_parent); 886fd7b9f7bSTejun Heo kernfs_put(sd->s_parent); 887fd7b9f7bSTejun Heo sd->s_ns = new_ns; 888fd7b9f7bSTejun Heo sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); 889fd7b9f7bSTejun Heo sd->s_parent = new_parent; 890fd7b9f7bSTejun Heo sysfs_link_sibling(sd); 891fd7b9f7bSTejun Heo 892fd7b9f7bSTejun Heo error = 0; 893fd7b9f7bSTejun Heo out: 894fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 895fd7b9f7bSTejun Heo return error; 896fd7b9f7bSTejun Heo } 897fd7b9f7bSTejun Heo 898fd7b9f7bSTejun Heo /* Relationship between s_mode and the DT_xxx types */ 899fd7b9f7bSTejun Heo static inline unsigned char dt_type(struct sysfs_dirent *sd) 900fd7b9f7bSTejun Heo { 901fd7b9f7bSTejun Heo return (sd->s_mode >> 12) & 15; 902fd7b9f7bSTejun Heo } 903fd7b9f7bSTejun Heo 904fd7b9f7bSTejun Heo static int sysfs_dir_release(struct inode *inode, struct file *filp) 905fd7b9f7bSTejun Heo { 906fd7b9f7bSTejun Heo kernfs_put(filp->private_data); 907fd7b9f7bSTejun Heo return 0; 908fd7b9f7bSTejun Heo } 909fd7b9f7bSTejun Heo 910fd7b9f7bSTejun Heo static struct sysfs_dirent *sysfs_dir_pos(const void *ns, 911fd7b9f7bSTejun Heo struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos) 912fd7b9f7bSTejun Heo { 913fd7b9f7bSTejun Heo if (pos) { 914fd7b9f7bSTejun Heo int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && 915fd7b9f7bSTejun Heo pos->s_parent == parent_sd && 916fd7b9f7bSTejun Heo hash == pos->s_hash; 917fd7b9f7bSTejun Heo kernfs_put(pos); 918fd7b9f7bSTejun Heo if (!valid) 919fd7b9f7bSTejun Heo pos = NULL; 920fd7b9f7bSTejun Heo } 921fd7b9f7bSTejun Heo if (!pos && (hash > 1) && (hash < INT_MAX)) { 922fd7b9f7bSTejun Heo struct rb_node *node = parent_sd->s_dir.children.rb_node; 923fd7b9f7bSTejun Heo while (node) { 924fd7b9f7bSTejun Heo pos = to_sysfs_dirent(node); 925fd7b9f7bSTejun Heo 926fd7b9f7bSTejun Heo if (hash < pos->s_hash) 927fd7b9f7bSTejun Heo node = node->rb_left; 928fd7b9f7bSTejun Heo else if (hash > pos->s_hash) 929fd7b9f7bSTejun Heo node = node->rb_right; 930fd7b9f7bSTejun Heo else 931fd7b9f7bSTejun Heo break; 932fd7b9f7bSTejun Heo } 933fd7b9f7bSTejun Heo } 934fd7b9f7bSTejun Heo /* Skip over entries in the wrong namespace */ 935fd7b9f7bSTejun Heo while (pos && pos->s_ns != ns) { 936fd7b9f7bSTejun Heo struct rb_node *node = rb_next(&pos->s_rb); 937fd7b9f7bSTejun Heo if (!node) 938fd7b9f7bSTejun Heo pos = NULL; 939fd7b9f7bSTejun Heo else 940fd7b9f7bSTejun Heo pos = to_sysfs_dirent(node); 941fd7b9f7bSTejun Heo } 942fd7b9f7bSTejun Heo return pos; 943fd7b9f7bSTejun Heo } 944fd7b9f7bSTejun Heo 945fd7b9f7bSTejun Heo static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, 946fd7b9f7bSTejun Heo struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) 947fd7b9f7bSTejun Heo { 948fd7b9f7bSTejun Heo pos = sysfs_dir_pos(ns, parent_sd, ino, pos); 949fd7b9f7bSTejun Heo if (pos) 950fd7b9f7bSTejun Heo do { 951fd7b9f7bSTejun Heo struct rb_node *node = rb_next(&pos->s_rb); 952fd7b9f7bSTejun Heo if (!node) 953fd7b9f7bSTejun Heo pos = NULL; 954fd7b9f7bSTejun Heo else 955fd7b9f7bSTejun Heo pos = to_sysfs_dirent(node); 956fd7b9f7bSTejun Heo } while (pos && pos->s_ns != ns); 957fd7b9f7bSTejun Heo return pos; 958fd7b9f7bSTejun Heo } 959fd7b9f7bSTejun Heo 960fd7b9f7bSTejun Heo static int sysfs_readdir(struct file *file, struct dir_context *ctx) 961fd7b9f7bSTejun Heo { 962fd7b9f7bSTejun Heo struct dentry *dentry = file->f_path.dentry; 963fd7b9f7bSTejun Heo struct sysfs_dirent *parent_sd = dentry->d_fsdata; 964fd7b9f7bSTejun Heo struct sysfs_dirent *pos = file->private_data; 965fd7b9f7bSTejun Heo const void *ns = NULL; 966fd7b9f7bSTejun Heo 967fd7b9f7bSTejun Heo if (!dir_emit_dots(file, ctx)) 968fd7b9f7bSTejun Heo return 0; 969fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 970fd7b9f7bSTejun Heo 971ac9bba03STejun Heo if (kernfs_ns_enabled(parent_sd)) 972fd7b9f7bSTejun Heo ns = sysfs_info(dentry->d_sb)->ns; 973fd7b9f7bSTejun Heo 974fd7b9f7bSTejun Heo for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); 975fd7b9f7bSTejun Heo pos; 976fd7b9f7bSTejun Heo pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { 977fd7b9f7bSTejun Heo const char *name = pos->s_name; 978fd7b9f7bSTejun Heo unsigned int type = dt_type(pos); 979fd7b9f7bSTejun Heo int len = strlen(name); 980fd7b9f7bSTejun Heo ino_t ino = pos->s_ino; 981fd7b9f7bSTejun Heo 982fd7b9f7bSTejun Heo ctx->pos = pos->s_hash; 983fd7b9f7bSTejun Heo file->private_data = pos; 984fd7b9f7bSTejun Heo kernfs_get(pos); 985fd7b9f7bSTejun Heo 986fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 987fd7b9f7bSTejun Heo if (!dir_emit(ctx, name, len, ino, type)) 988fd7b9f7bSTejun Heo return 0; 989fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 990fd7b9f7bSTejun Heo } 991fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 992fd7b9f7bSTejun Heo file->private_data = NULL; 993fd7b9f7bSTejun Heo ctx->pos = INT_MAX; 994fd7b9f7bSTejun Heo return 0; 995fd7b9f7bSTejun Heo } 996fd7b9f7bSTejun Heo 997fd7b9f7bSTejun Heo static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) 998fd7b9f7bSTejun Heo { 999fd7b9f7bSTejun Heo struct inode *inode = file_inode(file); 1000fd7b9f7bSTejun Heo loff_t ret; 1001fd7b9f7bSTejun Heo 1002fd7b9f7bSTejun Heo mutex_lock(&inode->i_mutex); 1003fd7b9f7bSTejun Heo ret = generic_file_llseek(file, offset, whence); 1004fd7b9f7bSTejun Heo mutex_unlock(&inode->i_mutex); 1005fd7b9f7bSTejun Heo 1006fd7b9f7bSTejun Heo return ret; 1007fd7b9f7bSTejun Heo } 1008fd7b9f7bSTejun Heo 1009fd7b9f7bSTejun Heo const struct file_operations sysfs_dir_operations = { 1010fd7b9f7bSTejun Heo .read = generic_read_dir, 1011fd7b9f7bSTejun Heo .iterate = sysfs_readdir, 1012fd7b9f7bSTejun Heo .release = sysfs_dir_release, 1013fd7b9f7bSTejun Heo .llseek = sysfs_dir_llseek, 1014fd7b9f7bSTejun Heo }; 1015