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 22adc5e8b5STejun Heo #define rb_to_kn(X) rb_entry((X), struct kernfs_node, 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, 48324a56e1STejun Heo const void *ns, const struct kernfs_node *kn) 49fd7b9f7bSTejun Heo { 50adc5e8b5STejun Heo if (hash != kn->hash) 51adc5e8b5STejun Heo return hash - kn->hash; 52adc5e8b5STejun Heo if (ns != kn->ns) 53adc5e8b5STejun Heo return ns - kn->ns; 54adc5e8b5STejun Heo return strcmp(name, kn->name); 55fd7b9f7bSTejun Heo } 56fd7b9f7bSTejun Heo 57324a56e1STejun Heo static int sysfs_sd_compare(const struct kernfs_node *left, 58324a56e1STejun Heo const struct kernfs_node *right) 59fd7b9f7bSTejun Heo { 60adc5e8b5STejun Heo return sysfs_name_compare(left->hash, left->name, left->ns, right); 61fd7b9f7bSTejun Heo } 62fd7b9f7bSTejun Heo 63fd7b9f7bSTejun Heo /** 64324a56e1STejun Heo * sysfs_link_sibling - link kernfs_node into sibling rbtree 65324a56e1STejun Heo * @kn: kernfs_node of interest 66fd7b9f7bSTejun Heo * 67324a56e1STejun Heo * Link @kn into its sibling rbtree which starts from 68adc5e8b5STejun Heo * @kn->parent->dir.children. 69fd7b9f7bSTejun Heo * 70fd7b9f7bSTejun Heo * Locking: 71fd7b9f7bSTejun Heo * mutex_lock(sysfs_mutex) 72fd7b9f7bSTejun Heo * 73fd7b9f7bSTejun Heo * RETURNS: 74fd7b9f7bSTejun Heo * 0 on susccess -EEXIST on failure. 75fd7b9f7bSTejun Heo */ 76324a56e1STejun Heo static int sysfs_link_sibling(struct kernfs_node *kn) 77fd7b9f7bSTejun Heo { 78adc5e8b5STejun Heo struct rb_node **node = &kn->parent->dir.children.rb_node; 79fd7b9f7bSTejun Heo struct rb_node *parent = NULL; 80fd7b9f7bSTejun Heo 81324a56e1STejun Heo if (sysfs_type(kn) == SYSFS_DIR) 82adc5e8b5STejun Heo kn->parent->dir.subdirs++; 83fd7b9f7bSTejun Heo 84fd7b9f7bSTejun Heo while (*node) { 85324a56e1STejun Heo struct kernfs_node *pos; 86fd7b9f7bSTejun Heo int result; 87fd7b9f7bSTejun Heo 88324a56e1STejun Heo pos = rb_to_kn(*node); 89fd7b9f7bSTejun Heo parent = *node; 90324a56e1STejun Heo result = sysfs_sd_compare(kn, pos); 91fd7b9f7bSTejun Heo if (result < 0) 92adc5e8b5STejun Heo node = &pos->rb.rb_left; 93fd7b9f7bSTejun Heo else if (result > 0) 94adc5e8b5STejun Heo node = &pos->rb.rb_right; 95fd7b9f7bSTejun Heo else 96fd7b9f7bSTejun Heo return -EEXIST; 97fd7b9f7bSTejun Heo } 98fd7b9f7bSTejun Heo /* add new node and rebalance the tree */ 99adc5e8b5STejun Heo rb_link_node(&kn->rb, parent, node); 100adc5e8b5STejun Heo rb_insert_color(&kn->rb, &kn->parent->dir.children); 101fd7b9f7bSTejun Heo return 0; 102fd7b9f7bSTejun Heo } 103fd7b9f7bSTejun Heo 104fd7b9f7bSTejun Heo /** 105324a56e1STejun Heo * sysfs_unlink_sibling - unlink kernfs_node from sibling rbtree 106324a56e1STejun Heo * @kn: kernfs_node of interest 107fd7b9f7bSTejun Heo * 108324a56e1STejun Heo * Unlink @kn from its sibling rbtree which starts from 109adc5e8b5STejun Heo * kn->parent->dir.children. 110fd7b9f7bSTejun Heo * 111fd7b9f7bSTejun Heo * Locking: 112fd7b9f7bSTejun Heo * mutex_lock(sysfs_mutex) 113fd7b9f7bSTejun Heo */ 114324a56e1STejun Heo static void sysfs_unlink_sibling(struct kernfs_node *kn) 115fd7b9f7bSTejun Heo { 116324a56e1STejun Heo if (sysfs_type(kn) == SYSFS_DIR) 117adc5e8b5STejun Heo kn->parent->dir.subdirs--; 118fd7b9f7bSTejun Heo 119adc5e8b5STejun Heo rb_erase(&kn->rb, &kn->parent->dir.children); 120fd7b9f7bSTejun Heo } 121fd7b9f7bSTejun Heo 122fd7b9f7bSTejun Heo /** 123324a56e1STejun Heo * sysfs_get_active - get an active reference to kernfs_node 124324a56e1STejun Heo * @kn: kernfs_node to get an active reference to 125fd7b9f7bSTejun Heo * 126324a56e1STejun Heo * Get an active reference of @kn. This function is noop if @kn 127fd7b9f7bSTejun Heo * is NULL. 128fd7b9f7bSTejun Heo * 129fd7b9f7bSTejun Heo * RETURNS: 130324a56e1STejun Heo * Pointer to @kn on success, NULL on failure. 131fd7b9f7bSTejun Heo */ 132324a56e1STejun Heo struct kernfs_node *sysfs_get_active(struct kernfs_node *kn) 133fd7b9f7bSTejun Heo { 134324a56e1STejun Heo if (unlikely(!kn)) 135fd7b9f7bSTejun Heo return NULL; 136fd7b9f7bSTejun Heo 137adc5e8b5STejun Heo if (!atomic_inc_unless_negative(&kn->active)) 138fd7b9f7bSTejun Heo return NULL; 139fd7b9f7bSTejun Heo 140adc5e8b5STejun Heo if (kn->flags & SYSFS_FLAG_LOCKDEP) 141324a56e1STejun Heo rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); 142324a56e1STejun Heo return kn; 143fd7b9f7bSTejun Heo } 144fd7b9f7bSTejun Heo 145fd7b9f7bSTejun Heo /** 146324a56e1STejun Heo * sysfs_put_active - put an active reference to kernfs_node 147324a56e1STejun Heo * @kn: kernfs_node to put an active reference to 148fd7b9f7bSTejun Heo * 149324a56e1STejun Heo * Put an active reference to @kn. This function is noop if @kn 150fd7b9f7bSTejun Heo * is NULL. 151fd7b9f7bSTejun Heo */ 152324a56e1STejun Heo void sysfs_put_active(struct kernfs_node *kn) 153fd7b9f7bSTejun Heo { 154fd7b9f7bSTejun Heo int v; 155fd7b9f7bSTejun Heo 156324a56e1STejun Heo if (unlikely(!kn)) 157fd7b9f7bSTejun Heo return; 158fd7b9f7bSTejun Heo 159adc5e8b5STejun Heo if (kn->flags & SYSFS_FLAG_LOCKDEP) 160324a56e1STejun Heo rwsem_release(&kn->dep_map, 1, _RET_IP_); 161adc5e8b5STejun Heo v = atomic_dec_return(&kn->active); 162fd7b9f7bSTejun Heo if (likely(v != SD_DEACTIVATED_BIAS)) 163fd7b9f7bSTejun Heo return; 164fd7b9f7bSTejun Heo 165324a56e1STejun Heo /* 166324a56e1STejun Heo * atomic_dec_return() is a mb(), we'll always see the updated 167324a56e1STejun Heo * kn->u.completion. 168fd7b9f7bSTejun Heo */ 169324a56e1STejun Heo complete(kn->u.completion); 170fd7b9f7bSTejun Heo } 171fd7b9f7bSTejun Heo 172fd7b9f7bSTejun Heo /** 173324a56e1STejun Heo * sysfs_deactivate - deactivate kernfs_node 174324a56e1STejun Heo * @kn: kernfs_node to deactivate 175fd7b9f7bSTejun Heo * 176fd7b9f7bSTejun Heo * Deny new active references and drain existing ones. 177fd7b9f7bSTejun Heo */ 178324a56e1STejun Heo static void sysfs_deactivate(struct kernfs_node *kn) 179fd7b9f7bSTejun Heo { 180fd7b9f7bSTejun Heo DECLARE_COMPLETION_ONSTACK(wait); 181fd7b9f7bSTejun Heo int v; 182fd7b9f7bSTejun Heo 183adc5e8b5STejun Heo BUG_ON(!(kn->flags & SYSFS_FLAG_REMOVED)); 184fd7b9f7bSTejun Heo 185324a56e1STejun Heo if (!(sysfs_type(kn) & SYSFS_ACTIVE_REF)) 186fd7b9f7bSTejun Heo return; 187fd7b9f7bSTejun Heo 188324a56e1STejun Heo kn->u.completion = (void *)&wait; 189fd7b9f7bSTejun Heo 190324a56e1STejun Heo rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); 191fd7b9f7bSTejun Heo /* atomic_add_return() is a mb(), put_active() will always see 192324a56e1STejun Heo * the updated kn->u.completion. 193fd7b9f7bSTejun Heo */ 194adc5e8b5STejun Heo v = atomic_add_return(SD_DEACTIVATED_BIAS, &kn->active); 195fd7b9f7bSTejun Heo 196fd7b9f7bSTejun Heo if (v != SD_DEACTIVATED_BIAS) { 197324a56e1STejun Heo lock_contended(&kn->dep_map, _RET_IP_); 198fd7b9f7bSTejun Heo wait_for_completion(&wait); 199fd7b9f7bSTejun Heo } 200fd7b9f7bSTejun Heo 201324a56e1STejun Heo lock_acquired(&kn->dep_map, _RET_IP_); 202324a56e1STejun Heo rwsem_release(&kn->dep_map, 1, _RET_IP_); 203fd7b9f7bSTejun Heo } 204fd7b9f7bSTejun Heo 205fd7b9f7bSTejun Heo /** 206324a56e1STejun Heo * kernfs_get - get a reference count on a kernfs_node 207324a56e1STejun Heo * @kn: the target kernfs_node 208fd7b9f7bSTejun Heo */ 209324a56e1STejun Heo void kernfs_get(struct kernfs_node *kn) 210fd7b9f7bSTejun Heo { 211324a56e1STejun Heo if (kn) { 212adc5e8b5STejun Heo WARN_ON(!atomic_read(&kn->count)); 213adc5e8b5STejun Heo atomic_inc(&kn->count); 214fd7b9f7bSTejun Heo } 215fd7b9f7bSTejun Heo } 216fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_get); 217fd7b9f7bSTejun Heo 218fd7b9f7bSTejun Heo /** 219324a56e1STejun Heo * kernfs_put - put a reference count on a kernfs_node 220324a56e1STejun Heo * @kn: the target kernfs_node 221fd7b9f7bSTejun Heo * 222324a56e1STejun Heo * Put a reference count of @kn and destroy it if it reached zero. 223fd7b9f7bSTejun Heo */ 224324a56e1STejun Heo void kernfs_put(struct kernfs_node *kn) 225fd7b9f7bSTejun Heo { 226324a56e1STejun Heo struct kernfs_node *parent; 227ba7443bcSTejun Heo struct kernfs_root *root; 228fd7b9f7bSTejun Heo 229adc5e8b5STejun Heo if (!kn || !atomic_dec_and_test(&kn->count)) 230fd7b9f7bSTejun Heo return; 231324a56e1STejun Heo root = kernfs_root(kn); 232fd7b9f7bSTejun Heo repeat: 233fd7b9f7bSTejun Heo /* Moving/renaming is always done while holding reference. 234adc5e8b5STejun Heo * kn->parent won't change beneath us. 235fd7b9f7bSTejun Heo */ 236adc5e8b5STejun Heo parent = kn->parent; 237fd7b9f7bSTejun Heo 238adc5e8b5STejun Heo WARN(!(kn->flags & SYSFS_FLAG_REMOVED), 239fd7b9f7bSTejun Heo "sysfs: free using entry: %s/%s\n", 240adc5e8b5STejun Heo parent ? parent->name : "", kn->name); 241fd7b9f7bSTejun Heo 242324a56e1STejun Heo if (sysfs_type(kn) == SYSFS_KOBJ_LINK) 243adc5e8b5STejun Heo kernfs_put(kn->symlink.target_kn); 244324a56e1STejun Heo if (sysfs_type(kn) & SYSFS_COPY_NAME) 245adc5e8b5STejun Heo kfree(kn->name); 246adc5e8b5STejun Heo if (kn->iattr) { 247adc5e8b5STejun Heo if (kn->iattr->ia_secdata) 248adc5e8b5STejun Heo security_release_secctx(kn->iattr->ia_secdata, 249adc5e8b5STejun Heo kn->iattr->ia_secdata_len); 250adc5e8b5STejun Heo simple_xattrs_free(&kn->iattr->xattrs); 2512322392bSTejun Heo } 252adc5e8b5STejun Heo kfree(kn->iattr); 253adc5e8b5STejun Heo ida_simple_remove(&root->ino_ida, kn->ino); 254324a56e1STejun Heo kmem_cache_free(sysfs_dir_cachep, kn); 255fd7b9f7bSTejun Heo 256324a56e1STejun Heo kn = parent; 257324a56e1STejun Heo if (kn) { 258adc5e8b5STejun Heo if (atomic_dec_and_test(&kn->count)) 259fd7b9f7bSTejun Heo goto repeat; 260ba7443bcSTejun Heo } else { 261324a56e1STejun Heo /* just released the root kn, free @root too */ 262bc755553STejun Heo ida_destroy(&root->ino_ida); 263ba7443bcSTejun Heo kfree(root); 264ba7443bcSTejun Heo } 265fd7b9f7bSTejun Heo } 266fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_put); 267fd7b9f7bSTejun Heo 268fd7b9f7bSTejun Heo static int sysfs_dentry_delete(const struct dentry *dentry) 269fd7b9f7bSTejun Heo { 270324a56e1STejun Heo struct kernfs_node *kn = dentry->d_fsdata; 271adc5e8b5STejun Heo return !(kn && !(kn->flags & SYSFS_FLAG_REMOVED)); 272fd7b9f7bSTejun Heo } 273fd7b9f7bSTejun Heo 274fd7b9f7bSTejun Heo static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) 275fd7b9f7bSTejun Heo { 276324a56e1STejun Heo struct kernfs_node *kn; 277fd7b9f7bSTejun Heo 278fd7b9f7bSTejun Heo if (flags & LOOKUP_RCU) 279fd7b9f7bSTejun Heo return -ECHILD; 280fd7b9f7bSTejun Heo 281324a56e1STejun Heo kn = dentry->d_fsdata; 282fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 283fd7b9f7bSTejun Heo 284fd7b9f7bSTejun Heo /* The sysfs dirent has been deleted */ 285adc5e8b5STejun Heo if (kn->flags & SYSFS_FLAG_REMOVED) 286fd7b9f7bSTejun Heo goto out_bad; 287fd7b9f7bSTejun Heo 288fd7b9f7bSTejun Heo /* The sysfs dirent has been moved? */ 289adc5e8b5STejun Heo if (dentry->d_parent->d_fsdata != kn->parent) 290fd7b9f7bSTejun Heo goto out_bad; 291fd7b9f7bSTejun Heo 292fd7b9f7bSTejun Heo /* The sysfs dirent has been renamed */ 293adc5e8b5STejun Heo if (strcmp(dentry->d_name.name, kn->name) != 0) 294fd7b9f7bSTejun Heo goto out_bad; 295fd7b9f7bSTejun Heo 296fd7b9f7bSTejun Heo /* The sysfs dirent has been moved to a different namespace */ 297adc5e8b5STejun Heo if (kn->parent && kernfs_ns_enabled(kn->parent) && 298adc5e8b5STejun Heo sysfs_info(dentry->d_sb)->ns != kn->ns) 299fd7b9f7bSTejun Heo goto out_bad; 300fd7b9f7bSTejun Heo 301fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 302fd7b9f7bSTejun Heo out_valid: 303fd7b9f7bSTejun Heo return 1; 304fd7b9f7bSTejun Heo out_bad: 305fd7b9f7bSTejun Heo /* Remove the dentry from the dcache hashes. 306fd7b9f7bSTejun Heo * If this is a deleted dentry we use d_drop instead of d_delete 307fd7b9f7bSTejun Heo * so sysfs doesn't need to cope with negative dentries. 308fd7b9f7bSTejun Heo * 309fd7b9f7bSTejun Heo * If this is a dentry that has simply been renamed we 310fd7b9f7bSTejun Heo * use d_drop to remove it from the dcache lookup on its 311fd7b9f7bSTejun Heo * old parent. If this dentry persists later when a lookup 312fd7b9f7bSTejun Heo * is performed at its new name the dentry will be readded 313fd7b9f7bSTejun Heo * to the dcache hashes. 314fd7b9f7bSTejun Heo */ 315fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 316fd7b9f7bSTejun Heo 317fd7b9f7bSTejun Heo /* If we have submounts we must allow the vfs caches 318fd7b9f7bSTejun Heo * to lie about the state of the filesystem to prevent 319fd7b9f7bSTejun Heo * leaks and other nasty things. 320fd7b9f7bSTejun Heo */ 321fd7b9f7bSTejun Heo if (check_submounts_and_drop(dentry) != 0) 322fd7b9f7bSTejun Heo goto out_valid; 323fd7b9f7bSTejun Heo 324fd7b9f7bSTejun Heo return 0; 325fd7b9f7bSTejun Heo } 326fd7b9f7bSTejun Heo 327fd7b9f7bSTejun Heo static void sysfs_dentry_release(struct dentry *dentry) 328fd7b9f7bSTejun Heo { 329fd7b9f7bSTejun Heo kernfs_put(dentry->d_fsdata); 330fd7b9f7bSTejun Heo } 331fd7b9f7bSTejun Heo 332fd7b9f7bSTejun Heo const struct dentry_operations sysfs_dentry_ops = { 333fd7b9f7bSTejun Heo .d_revalidate = sysfs_dentry_revalidate, 334fd7b9f7bSTejun Heo .d_delete = sysfs_dentry_delete, 335fd7b9f7bSTejun Heo .d_release = sysfs_dentry_release, 336fd7b9f7bSTejun Heo }; 337fd7b9f7bSTejun Heo 338324a56e1STejun Heo struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, 339bc755553STejun Heo const char *name, umode_t mode, int type) 340fd7b9f7bSTejun Heo { 341fd7b9f7bSTejun Heo char *dup_name = NULL; 342324a56e1STejun Heo struct kernfs_node *kn; 343bc755553STejun Heo int ret; 344fd7b9f7bSTejun Heo 345fd7b9f7bSTejun Heo if (type & SYSFS_COPY_NAME) { 346fd7b9f7bSTejun Heo name = dup_name = kstrdup(name, GFP_KERNEL); 347fd7b9f7bSTejun Heo if (!name) 348fd7b9f7bSTejun Heo return NULL; 349fd7b9f7bSTejun Heo } 350fd7b9f7bSTejun Heo 351324a56e1STejun Heo kn = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); 352324a56e1STejun Heo if (!kn) 353fd7b9f7bSTejun Heo goto err_out1; 354fd7b9f7bSTejun Heo 355bc755553STejun Heo ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL); 356bc755553STejun Heo if (ret < 0) 357fd7b9f7bSTejun Heo goto err_out2; 358adc5e8b5STejun Heo kn->ino = ret; 359fd7b9f7bSTejun Heo 360adc5e8b5STejun Heo atomic_set(&kn->count, 1); 361adc5e8b5STejun Heo atomic_set(&kn->active, 0); 362fd7b9f7bSTejun Heo 363adc5e8b5STejun Heo kn->name = name; 364adc5e8b5STejun Heo kn->mode = mode; 365adc5e8b5STejun Heo kn->flags = type | SYSFS_FLAG_REMOVED; 366fd7b9f7bSTejun Heo 367324a56e1STejun Heo return kn; 368fd7b9f7bSTejun Heo 369fd7b9f7bSTejun Heo err_out2: 370324a56e1STejun Heo kmem_cache_free(sysfs_dir_cachep, kn); 371fd7b9f7bSTejun Heo err_out1: 372fd7b9f7bSTejun Heo kfree(dup_name); 373fd7b9f7bSTejun Heo return NULL; 374fd7b9f7bSTejun Heo } 375fd7b9f7bSTejun Heo 376fd7b9f7bSTejun Heo /** 377324a56e1STejun Heo * sysfs_addrm_start - prepare for kernfs_node add/remove 378fd7b9f7bSTejun Heo * @acxt: pointer to sysfs_addrm_cxt to be used 379fd7b9f7bSTejun Heo * 380fd7b9f7bSTejun Heo * This function is called when the caller is about to add or remove 381324a56e1STejun Heo * kernfs_node. This function acquires sysfs_mutex. @acxt is used to 382324a56e1STejun Heo * keep and pass context to other addrm functions. 383fd7b9f7bSTejun Heo * 384fd7b9f7bSTejun Heo * LOCKING: 385fd7b9f7bSTejun Heo * Kernel thread context (may sleep). sysfs_mutex is locked on 386fd7b9f7bSTejun Heo * return. 387fd7b9f7bSTejun Heo */ 388fd7b9f7bSTejun Heo void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) 389fd7b9f7bSTejun Heo __acquires(sysfs_mutex) 390fd7b9f7bSTejun Heo { 391fd7b9f7bSTejun Heo memset(acxt, 0, sizeof(*acxt)); 392fd7b9f7bSTejun Heo 393fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 394fd7b9f7bSTejun Heo } 395fd7b9f7bSTejun Heo 396fd7b9f7bSTejun Heo /** 397324a56e1STejun Heo * sysfs_add_one - add kernfs_node to parent without warning 398fd7b9f7bSTejun Heo * @acxt: addrm context to use 399324a56e1STejun Heo * @kn: kernfs_node to be added 400324a56e1STejun Heo * @parent: the parent kernfs_node to add @kn to 401fd7b9f7bSTejun Heo * 402adc5e8b5STejun Heo * Get @parent and set @kn->parent to it and increment nlink of the 403adc5e8b5STejun Heo * parent inode if @kn is a directory and link into the children list 404adc5e8b5STejun Heo * of the parent. 405fd7b9f7bSTejun Heo * 406fd7b9f7bSTejun Heo * This function should be called between calls to 407fd7b9f7bSTejun Heo * sysfs_addrm_start() and sysfs_addrm_finish() and should be 408fd7b9f7bSTejun Heo * passed the same @acxt as passed to sysfs_addrm_start(). 409fd7b9f7bSTejun Heo * 410fd7b9f7bSTejun Heo * LOCKING: 411fd7b9f7bSTejun Heo * Determined by sysfs_addrm_start(). 412fd7b9f7bSTejun Heo * 413fd7b9f7bSTejun Heo * RETURNS: 414fd7b9f7bSTejun Heo * 0 on success, -EEXIST if entry with the given name already 415fd7b9f7bSTejun Heo * exists. 416fd7b9f7bSTejun Heo */ 417324a56e1STejun Heo int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, 418324a56e1STejun Heo struct kernfs_node *parent) 419fd7b9f7bSTejun Heo { 420324a56e1STejun Heo bool has_ns = kernfs_ns_enabled(parent); 421fd7b9f7bSTejun Heo struct sysfs_inode_attrs *ps_iattr; 422fd7b9f7bSTejun Heo int ret; 423fd7b9f7bSTejun Heo 424adc5e8b5STejun Heo if (has_ns != (bool)kn->ns) { 425fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 426adc5e8b5STejun Heo has_ns ? "required" : "invalid", parent->name, kn->name); 427fd7b9f7bSTejun Heo return -EINVAL; 428fd7b9f7bSTejun Heo } 429fd7b9f7bSTejun Heo 430324a56e1STejun Heo if (sysfs_type(parent) != SYSFS_DIR) 431fd7b9f7bSTejun Heo return -EINVAL; 432fd7b9f7bSTejun Heo 433adc5e8b5STejun Heo kn->hash = sysfs_name_hash(kn->name, kn->ns); 434adc5e8b5STejun Heo kn->parent = parent; 435324a56e1STejun Heo kernfs_get(parent); 436fd7b9f7bSTejun Heo 437324a56e1STejun Heo ret = sysfs_link_sibling(kn); 438fd7b9f7bSTejun Heo if (ret) 439fd7b9f7bSTejun Heo return ret; 440fd7b9f7bSTejun Heo 441fd7b9f7bSTejun Heo /* Update timestamps on the parent */ 442adc5e8b5STejun Heo ps_iattr = parent->iattr; 443fd7b9f7bSTejun Heo if (ps_iattr) { 444fd7b9f7bSTejun Heo struct iattr *ps_iattrs = &ps_iattr->ia_iattr; 445fd7b9f7bSTejun Heo ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; 446fd7b9f7bSTejun Heo } 447fd7b9f7bSTejun Heo 448fd7b9f7bSTejun Heo /* Mark the entry added into directory tree */ 449adc5e8b5STejun Heo kn->flags &= ~SYSFS_FLAG_REMOVED; 450fd7b9f7bSTejun Heo 451fd7b9f7bSTejun Heo return 0; 452fd7b9f7bSTejun Heo } 453fd7b9f7bSTejun Heo 454fd7b9f7bSTejun Heo /** 455324a56e1STejun Heo * sysfs_remove_one - remove kernfs_node from parent 456fd7b9f7bSTejun Heo * @acxt: addrm context to use 457324a56e1STejun Heo * @kn: kernfs_node to be removed 458fd7b9f7bSTejun Heo * 459324a56e1STejun Heo * Mark @kn removed and drop nlink of parent inode if @kn is a 460324a56e1STejun Heo * directory. @kn is unlinked from the children list. 461fd7b9f7bSTejun Heo * 462fd7b9f7bSTejun Heo * This function should be called between calls to 463fd7b9f7bSTejun Heo * sysfs_addrm_start() and sysfs_addrm_finish() and should be 464fd7b9f7bSTejun Heo * passed the same @acxt as passed to sysfs_addrm_start(). 465fd7b9f7bSTejun Heo * 466fd7b9f7bSTejun Heo * LOCKING: 467fd7b9f7bSTejun Heo * Determined by sysfs_addrm_start(). 468fd7b9f7bSTejun Heo */ 469fd7b9f7bSTejun Heo static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, 470324a56e1STejun Heo struct kernfs_node *kn) 471fd7b9f7bSTejun Heo { 472fd7b9f7bSTejun Heo struct sysfs_inode_attrs *ps_iattr; 473fd7b9f7bSTejun Heo 474fd7b9f7bSTejun Heo /* 475fd7b9f7bSTejun Heo * Removal can be called multiple times on the same node. Only the 476fd7b9f7bSTejun Heo * first invocation is effective and puts the base ref. 477fd7b9f7bSTejun Heo */ 478adc5e8b5STejun Heo if (kn->flags & SYSFS_FLAG_REMOVED) 479fd7b9f7bSTejun Heo return; 480fd7b9f7bSTejun Heo 481adc5e8b5STejun Heo if (kn->parent) { 482324a56e1STejun Heo sysfs_unlink_sibling(kn); 483fd7b9f7bSTejun Heo 484fd7b9f7bSTejun Heo /* Update timestamps on the parent */ 485adc5e8b5STejun Heo ps_iattr = kn->parent->iattr; 486fd7b9f7bSTejun Heo if (ps_iattr) { 487ba7443bcSTejun Heo ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; 488ba7443bcSTejun Heo ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; 489ba7443bcSTejun Heo } 490fd7b9f7bSTejun Heo } 491fd7b9f7bSTejun Heo 492adc5e8b5STejun Heo kn->flags |= SYSFS_FLAG_REMOVED; 493324a56e1STejun Heo kn->u.removed_list = acxt->removed; 494324a56e1STejun Heo acxt->removed = kn; 495fd7b9f7bSTejun Heo } 496fd7b9f7bSTejun Heo 497fd7b9f7bSTejun Heo /** 498324a56e1STejun Heo * sysfs_addrm_finish - finish up kernfs_node add/remove 499fd7b9f7bSTejun Heo * @acxt: addrm context to finish up 500fd7b9f7bSTejun Heo * 501324a56e1STejun Heo * Finish up kernfs_node add/remove. Resources acquired by 502324a56e1STejun Heo * sysfs_addrm_start() are released and removed kernfs_nodes are 503fd7b9f7bSTejun Heo * cleaned up. 504fd7b9f7bSTejun Heo * 505fd7b9f7bSTejun Heo * LOCKING: 506fd7b9f7bSTejun Heo * sysfs_mutex is released. 507fd7b9f7bSTejun Heo */ 508fd7b9f7bSTejun Heo void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) 509fd7b9f7bSTejun Heo __releases(sysfs_mutex) 510fd7b9f7bSTejun Heo { 511fd7b9f7bSTejun Heo /* release resources acquired by sysfs_addrm_start() */ 512fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 513fd7b9f7bSTejun Heo 514324a56e1STejun Heo /* kill removed kernfs_nodes */ 515fd7b9f7bSTejun Heo while (acxt->removed) { 516324a56e1STejun Heo struct kernfs_node *kn = acxt->removed; 517fd7b9f7bSTejun Heo 518324a56e1STejun Heo acxt->removed = kn->u.removed_list; 519fd7b9f7bSTejun Heo 520324a56e1STejun Heo sysfs_deactivate(kn); 521324a56e1STejun Heo sysfs_unmap_bin_file(kn); 522324a56e1STejun Heo kernfs_put(kn); 523fd7b9f7bSTejun Heo } 524fd7b9f7bSTejun Heo } 525fd7b9f7bSTejun Heo 526fd7b9f7bSTejun Heo /** 527324a56e1STejun Heo * kernfs_find_ns - find kernfs_node with the given name 528324a56e1STejun Heo * @parent: kernfs_node to search under 529fd7b9f7bSTejun Heo * @name: name to look for 530fd7b9f7bSTejun Heo * @ns: the namespace tag to use 531fd7b9f7bSTejun Heo * 532324a56e1STejun Heo * Look for kernfs_node with name @name under @parent. Returns pointer to 533324a56e1STejun Heo * the found kernfs_node on success, %NULL on failure. 534fd7b9f7bSTejun Heo */ 535324a56e1STejun Heo static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, 536fd7b9f7bSTejun Heo const unsigned char *name, 537fd7b9f7bSTejun Heo const void *ns) 538fd7b9f7bSTejun Heo { 539adc5e8b5STejun Heo struct rb_node *node = parent->dir.children.rb_node; 540ac9bba03STejun Heo bool has_ns = kernfs_ns_enabled(parent); 541fd7b9f7bSTejun Heo unsigned int hash; 542fd7b9f7bSTejun Heo 543fd7b9f7bSTejun Heo lockdep_assert_held(&sysfs_mutex); 544fd7b9f7bSTejun Heo 545fd7b9f7bSTejun Heo if (has_ns != (bool)ns) { 546fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 547adc5e8b5STejun Heo has_ns ? "required" : "invalid", parent->name, name); 548fd7b9f7bSTejun Heo return NULL; 549fd7b9f7bSTejun Heo } 550fd7b9f7bSTejun Heo 551fd7b9f7bSTejun Heo hash = sysfs_name_hash(name, ns); 552fd7b9f7bSTejun Heo while (node) { 553324a56e1STejun Heo struct kernfs_node *kn; 554fd7b9f7bSTejun Heo int result; 555fd7b9f7bSTejun Heo 556324a56e1STejun Heo kn = rb_to_kn(node); 557324a56e1STejun Heo result = sysfs_name_compare(hash, name, ns, kn); 558fd7b9f7bSTejun Heo if (result < 0) 559fd7b9f7bSTejun Heo node = node->rb_left; 560fd7b9f7bSTejun Heo else if (result > 0) 561fd7b9f7bSTejun Heo node = node->rb_right; 562fd7b9f7bSTejun Heo else 563324a56e1STejun Heo return kn; 564fd7b9f7bSTejun Heo } 565fd7b9f7bSTejun Heo return NULL; 566fd7b9f7bSTejun Heo } 567fd7b9f7bSTejun Heo 568fd7b9f7bSTejun Heo /** 569324a56e1STejun Heo * kernfs_find_and_get_ns - find and get kernfs_node with the given name 570324a56e1STejun Heo * @parent: kernfs_node to search under 571fd7b9f7bSTejun Heo * @name: name to look for 572fd7b9f7bSTejun Heo * @ns: the namespace tag to use 573fd7b9f7bSTejun Heo * 574324a56e1STejun Heo * Look for kernfs_node with name @name under @parent and get a reference 575fd7b9f7bSTejun Heo * if found. This function may sleep and returns pointer to the found 576324a56e1STejun Heo * kernfs_node on success, %NULL on failure. 577fd7b9f7bSTejun Heo */ 578324a56e1STejun Heo struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, 579fd7b9f7bSTejun Heo const char *name, const void *ns) 580fd7b9f7bSTejun Heo { 581324a56e1STejun Heo struct kernfs_node *kn; 582fd7b9f7bSTejun Heo 583fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 584324a56e1STejun Heo kn = kernfs_find_ns(parent, name, ns); 585324a56e1STejun Heo kernfs_get(kn); 586fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 587fd7b9f7bSTejun Heo 588324a56e1STejun Heo return kn; 589fd7b9f7bSTejun Heo } 590fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); 591fd7b9f7bSTejun Heo 592fd7b9f7bSTejun Heo /** 593ba7443bcSTejun Heo * kernfs_create_root - create a new kernfs hierarchy 594ba7443bcSTejun Heo * @priv: opaque data associated with the new directory 595ba7443bcSTejun Heo * 596ba7443bcSTejun Heo * Returns the root of the new hierarchy on success, ERR_PTR() value on 597ba7443bcSTejun Heo * failure. 598ba7443bcSTejun Heo */ 599ba7443bcSTejun Heo struct kernfs_root *kernfs_create_root(void *priv) 600ba7443bcSTejun Heo { 601ba7443bcSTejun Heo struct kernfs_root *root; 602324a56e1STejun Heo struct kernfs_node *kn; 603ba7443bcSTejun Heo 604ba7443bcSTejun Heo root = kzalloc(sizeof(*root), GFP_KERNEL); 605ba7443bcSTejun Heo if (!root) 606ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 607ba7443bcSTejun Heo 608bc755553STejun Heo ida_init(&root->ino_ida); 609bc755553STejun Heo 610324a56e1STejun Heo kn = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); 611324a56e1STejun Heo if (!kn) { 612bc755553STejun Heo ida_destroy(&root->ino_ida); 613ba7443bcSTejun Heo kfree(root); 614ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 615ba7443bcSTejun Heo } 616ba7443bcSTejun Heo 617adc5e8b5STejun Heo kn->flags &= ~SYSFS_FLAG_REMOVED; 618324a56e1STejun Heo kn->priv = priv; 619adc5e8b5STejun Heo kn->dir.root = root; 620ba7443bcSTejun Heo 621324a56e1STejun Heo root->kn = kn; 622ba7443bcSTejun Heo 623ba7443bcSTejun Heo return root; 624ba7443bcSTejun Heo } 625ba7443bcSTejun Heo 626ba7443bcSTejun Heo /** 627ba7443bcSTejun Heo * kernfs_destroy_root - destroy a kernfs hierarchy 628ba7443bcSTejun Heo * @root: root of the hierarchy to destroy 629ba7443bcSTejun Heo * 630ba7443bcSTejun Heo * Destroy the hierarchy anchored at @root by removing all existing 631ba7443bcSTejun Heo * directories and destroying @root. 632ba7443bcSTejun Heo */ 633ba7443bcSTejun Heo void kernfs_destroy_root(struct kernfs_root *root) 634ba7443bcSTejun Heo { 635324a56e1STejun Heo kernfs_remove(root->kn); /* will also free @root */ 636ba7443bcSTejun Heo } 637ba7443bcSTejun Heo 638ba7443bcSTejun Heo /** 639fd7b9f7bSTejun Heo * kernfs_create_dir_ns - create a directory 640fd7b9f7bSTejun Heo * @parent: parent in which to create a new directory 641fd7b9f7bSTejun Heo * @name: name of the new directory 642fd7b9f7bSTejun Heo * @priv: opaque data associated with the new directory 643fd7b9f7bSTejun Heo * @ns: optional namespace tag of the directory 644fd7b9f7bSTejun Heo * 645fd7b9f7bSTejun Heo * Returns the created node on success, ERR_PTR() value on failure. 646fd7b9f7bSTejun Heo */ 647324a56e1STejun Heo struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, 648fd7b9f7bSTejun Heo const char *name, void *priv, 649fd7b9f7bSTejun Heo const void *ns) 650fd7b9f7bSTejun Heo { 651fd7b9f7bSTejun Heo umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; 652fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 653324a56e1STejun Heo struct kernfs_node *kn; 654fd7b9f7bSTejun Heo int rc; 655fd7b9f7bSTejun Heo 656fd7b9f7bSTejun Heo /* allocate */ 657324a56e1STejun Heo kn = sysfs_new_dirent(kernfs_root(parent), name, mode, SYSFS_DIR); 658324a56e1STejun Heo if (!kn) 659fd7b9f7bSTejun Heo return ERR_PTR(-ENOMEM); 660fd7b9f7bSTejun Heo 661adc5e8b5STejun Heo kn->dir.root = parent->dir.root; 662adc5e8b5STejun Heo kn->ns = ns; 663324a56e1STejun Heo kn->priv = priv; 664fd7b9f7bSTejun Heo 665fd7b9f7bSTejun Heo /* link in */ 666fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 667324a56e1STejun Heo rc = sysfs_add_one(&acxt, kn, parent); 668fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 669fd7b9f7bSTejun Heo 670fd7b9f7bSTejun Heo if (!rc) 671324a56e1STejun Heo return kn; 672fd7b9f7bSTejun Heo 673324a56e1STejun Heo kernfs_put(kn); 674fd7b9f7bSTejun Heo return ERR_PTR(rc); 675fd7b9f7bSTejun Heo } 676fd7b9f7bSTejun Heo 677fd7b9f7bSTejun Heo static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, 678fd7b9f7bSTejun Heo unsigned int flags) 679fd7b9f7bSTejun Heo { 680fd7b9f7bSTejun Heo struct dentry *ret = NULL; 681324a56e1STejun Heo struct kernfs_node *parent = dentry->d_parent->d_fsdata; 682324a56e1STejun Heo struct kernfs_node *kn; 683fd7b9f7bSTejun Heo struct inode *inode; 684fd7b9f7bSTejun Heo const void *ns = NULL; 685fd7b9f7bSTejun Heo 686fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 687fd7b9f7bSTejun Heo 688324a56e1STejun Heo if (kernfs_ns_enabled(parent)) 689fd7b9f7bSTejun Heo ns = sysfs_info(dir->i_sb)->ns; 690fd7b9f7bSTejun Heo 691324a56e1STejun Heo kn = kernfs_find_ns(parent, dentry->d_name.name, ns); 692fd7b9f7bSTejun Heo 693fd7b9f7bSTejun Heo /* no such entry */ 694324a56e1STejun Heo if (!kn) { 695fd7b9f7bSTejun Heo ret = ERR_PTR(-ENOENT); 696fd7b9f7bSTejun Heo goto out_unlock; 697fd7b9f7bSTejun Heo } 698324a56e1STejun Heo kernfs_get(kn); 699324a56e1STejun Heo dentry->d_fsdata = kn; 700fd7b9f7bSTejun Heo 701fd7b9f7bSTejun Heo /* attach dentry and inode */ 702324a56e1STejun Heo inode = sysfs_get_inode(dir->i_sb, kn); 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, 7212322392bSTejun Heo .removexattr = sysfs_removexattr, 7222322392bSTejun Heo .getxattr = sysfs_getxattr, 7232322392bSTejun Heo .listxattr = sysfs_listxattr, 724fd7b9f7bSTejun Heo }; 725fd7b9f7bSTejun Heo 726324a56e1STejun Heo static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) 727fd7b9f7bSTejun Heo { 728324a56e1STejun Heo struct kernfs_node *last; 729fd7b9f7bSTejun Heo 730fd7b9f7bSTejun Heo while (true) { 731fd7b9f7bSTejun Heo struct rb_node *rbn; 732fd7b9f7bSTejun Heo 733fd7b9f7bSTejun Heo last = pos; 734fd7b9f7bSTejun Heo 735fd7b9f7bSTejun Heo if (sysfs_type(pos) != SYSFS_DIR) 736fd7b9f7bSTejun Heo break; 737fd7b9f7bSTejun Heo 738adc5e8b5STejun Heo rbn = rb_first(&pos->dir.children); 739fd7b9f7bSTejun Heo if (!rbn) 740fd7b9f7bSTejun Heo break; 741fd7b9f7bSTejun Heo 742324a56e1STejun Heo pos = rb_to_kn(rbn); 743fd7b9f7bSTejun Heo } 744fd7b9f7bSTejun Heo 745fd7b9f7bSTejun Heo return last; 746fd7b9f7bSTejun Heo } 747fd7b9f7bSTejun Heo 748fd7b9f7bSTejun Heo /** 749fd7b9f7bSTejun Heo * sysfs_next_descendant_post - find the next descendant for post-order walk 750fd7b9f7bSTejun Heo * @pos: the current position (%NULL to initiate traversal) 751324a56e1STejun Heo * @root: kernfs_node whose descendants to walk 752fd7b9f7bSTejun Heo * 753fd7b9f7bSTejun Heo * Find the next descendant to visit for post-order traversal of @root's 754fd7b9f7bSTejun Heo * descendants. @root is included in the iteration and the last node to be 755fd7b9f7bSTejun Heo * visited. 756fd7b9f7bSTejun Heo */ 757324a56e1STejun Heo static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, 758324a56e1STejun Heo struct kernfs_node *root) 759fd7b9f7bSTejun Heo { 760fd7b9f7bSTejun Heo struct rb_node *rbn; 761fd7b9f7bSTejun Heo 762fd7b9f7bSTejun Heo lockdep_assert_held(&sysfs_mutex); 763fd7b9f7bSTejun Heo 764fd7b9f7bSTejun Heo /* if first iteration, visit leftmost descendant which may be root */ 765fd7b9f7bSTejun Heo if (!pos) 766fd7b9f7bSTejun Heo return sysfs_leftmost_descendant(root); 767fd7b9f7bSTejun Heo 768fd7b9f7bSTejun Heo /* if we visited @root, we're done */ 769fd7b9f7bSTejun Heo if (pos == root) 770fd7b9f7bSTejun Heo return NULL; 771fd7b9f7bSTejun Heo 772fd7b9f7bSTejun Heo /* if there's an unvisited sibling, visit its leftmost descendant */ 773adc5e8b5STejun Heo rbn = rb_next(&pos->rb); 774fd7b9f7bSTejun Heo if (rbn) 775324a56e1STejun Heo return sysfs_leftmost_descendant(rb_to_kn(rbn)); 776fd7b9f7bSTejun Heo 777fd7b9f7bSTejun Heo /* no sibling left, visit parent */ 778adc5e8b5STejun Heo return pos->parent; 779fd7b9f7bSTejun Heo } 780fd7b9f7bSTejun Heo 781fd7b9f7bSTejun Heo static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, 782324a56e1STejun Heo struct kernfs_node *kn) 783fd7b9f7bSTejun Heo { 784324a56e1STejun Heo struct kernfs_node *pos, *next; 785fd7b9f7bSTejun Heo 786324a56e1STejun Heo if (!kn) 787fd7b9f7bSTejun Heo return; 788fd7b9f7bSTejun Heo 789adc5e8b5STejun Heo pr_debug("sysfs %s: removing\n", kn->name); 790fd7b9f7bSTejun Heo 791fd7b9f7bSTejun Heo next = NULL; 792fd7b9f7bSTejun Heo do { 793fd7b9f7bSTejun Heo pos = next; 794324a56e1STejun Heo next = sysfs_next_descendant_post(pos, kn); 795fd7b9f7bSTejun Heo if (pos) 796fd7b9f7bSTejun Heo sysfs_remove_one(acxt, pos); 797fd7b9f7bSTejun Heo } while (next); 798fd7b9f7bSTejun Heo } 799fd7b9f7bSTejun Heo 800fd7b9f7bSTejun Heo /** 801324a56e1STejun Heo * kernfs_remove - remove a kernfs_node recursively 802324a56e1STejun Heo * @kn: the kernfs_node to remove 803fd7b9f7bSTejun Heo * 804324a56e1STejun Heo * Remove @kn along with all its subdirectories and files. 805fd7b9f7bSTejun Heo */ 806324a56e1STejun Heo void kernfs_remove(struct kernfs_node *kn) 807fd7b9f7bSTejun Heo { 808fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 809fd7b9f7bSTejun Heo 810fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 811324a56e1STejun Heo __kernfs_remove(&acxt, kn); 812fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 813fd7b9f7bSTejun Heo } 814fd7b9f7bSTejun Heo 815fd7b9f7bSTejun Heo /** 816324a56e1STejun Heo * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it 817324a56e1STejun Heo * @parent: parent of the target 818324a56e1STejun Heo * @name: name of the kernfs_node to remove 819324a56e1STejun Heo * @ns: namespace tag of the kernfs_node to remove 820fd7b9f7bSTejun Heo * 821324a56e1STejun Heo * Look for the kernfs_node with @name and @ns under @parent and remove it. 822324a56e1STejun Heo * Returns 0 on success, -ENOENT if such entry doesn't exist. 823fd7b9f7bSTejun Heo */ 824324a56e1STejun Heo int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, 825fd7b9f7bSTejun Heo const void *ns) 826fd7b9f7bSTejun Heo { 827fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 828324a56e1STejun Heo struct kernfs_node *kn; 829fd7b9f7bSTejun Heo 830324a56e1STejun Heo if (!parent) { 831fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", 832fd7b9f7bSTejun Heo name); 833fd7b9f7bSTejun Heo return -ENOENT; 834fd7b9f7bSTejun Heo } 835fd7b9f7bSTejun Heo 836fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 837fd7b9f7bSTejun Heo 838324a56e1STejun Heo kn = kernfs_find_ns(parent, name, ns); 839324a56e1STejun Heo if (kn) 840324a56e1STejun Heo __kernfs_remove(&acxt, kn); 841fd7b9f7bSTejun Heo 842fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 843fd7b9f7bSTejun Heo 844324a56e1STejun Heo if (kn) 845fd7b9f7bSTejun Heo return 0; 846fd7b9f7bSTejun Heo else 847fd7b9f7bSTejun Heo return -ENOENT; 848fd7b9f7bSTejun Heo } 849fd7b9f7bSTejun Heo 850fd7b9f7bSTejun Heo /** 851fd7b9f7bSTejun Heo * kernfs_rename_ns - move and rename a kernfs_node 852324a56e1STejun Heo * @kn: target node 853fd7b9f7bSTejun Heo * @new_parent: new parent to put @sd under 854fd7b9f7bSTejun Heo * @new_name: new name 855fd7b9f7bSTejun Heo * @new_ns: new namespace tag 856fd7b9f7bSTejun Heo */ 857324a56e1STejun Heo int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, 858fd7b9f7bSTejun Heo const char *new_name, const void *new_ns) 859fd7b9f7bSTejun Heo { 860fd7b9f7bSTejun Heo int error; 861fd7b9f7bSTejun Heo 862fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 863fd7b9f7bSTejun Heo 864fd7b9f7bSTejun Heo error = 0; 865adc5e8b5STejun Heo if ((kn->parent == new_parent) && (kn->ns == new_ns) && 866adc5e8b5STejun Heo (strcmp(kn->name, new_name) == 0)) 867fd7b9f7bSTejun Heo goto out; /* nothing to rename */ 868fd7b9f7bSTejun Heo 869fd7b9f7bSTejun Heo error = -EEXIST; 870fd7b9f7bSTejun Heo if (kernfs_find_ns(new_parent, new_name, new_ns)) 871fd7b9f7bSTejun Heo goto out; 872fd7b9f7bSTejun Heo 873324a56e1STejun Heo /* rename kernfs_node */ 874adc5e8b5STejun Heo if (strcmp(kn->name, new_name) != 0) { 875fd7b9f7bSTejun Heo error = -ENOMEM; 876fd7b9f7bSTejun Heo new_name = kstrdup(new_name, GFP_KERNEL); 877fd7b9f7bSTejun Heo if (!new_name) 878fd7b9f7bSTejun Heo goto out; 879fd7b9f7bSTejun Heo 880adc5e8b5STejun Heo kfree(kn->name); 881adc5e8b5STejun Heo kn->name = new_name; 882fd7b9f7bSTejun Heo } 883fd7b9f7bSTejun Heo 884fd7b9f7bSTejun Heo /* 885fd7b9f7bSTejun Heo * Move to the appropriate place in the appropriate directories rbtree. 886fd7b9f7bSTejun Heo */ 887324a56e1STejun Heo sysfs_unlink_sibling(kn); 888fd7b9f7bSTejun Heo kernfs_get(new_parent); 889adc5e8b5STejun Heo kernfs_put(kn->parent); 890adc5e8b5STejun Heo kn->ns = new_ns; 891adc5e8b5STejun Heo kn->hash = sysfs_name_hash(kn->name, kn->ns); 892adc5e8b5STejun Heo kn->parent = new_parent; 893324a56e1STejun Heo sysfs_link_sibling(kn); 894fd7b9f7bSTejun Heo 895fd7b9f7bSTejun Heo error = 0; 896fd7b9f7bSTejun Heo out: 897fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 898fd7b9f7bSTejun Heo return error; 899fd7b9f7bSTejun Heo } 900fd7b9f7bSTejun Heo 901fd7b9f7bSTejun Heo /* Relationship between s_mode and the DT_xxx types */ 902324a56e1STejun Heo static inline unsigned char dt_type(struct kernfs_node *kn) 903fd7b9f7bSTejun Heo { 904adc5e8b5STejun Heo return (kn->mode >> 12) & 15; 905fd7b9f7bSTejun Heo } 906fd7b9f7bSTejun Heo 907fd7b9f7bSTejun Heo static int sysfs_dir_release(struct inode *inode, struct file *filp) 908fd7b9f7bSTejun Heo { 909fd7b9f7bSTejun Heo kernfs_put(filp->private_data); 910fd7b9f7bSTejun Heo return 0; 911fd7b9f7bSTejun Heo } 912fd7b9f7bSTejun Heo 913324a56e1STejun Heo static struct kernfs_node *sysfs_dir_pos(const void *ns, 914324a56e1STejun Heo struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) 915fd7b9f7bSTejun Heo { 916fd7b9f7bSTejun Heo if (pos) { 917adc5e8b5STejun Heo int valid = !(pos->flags & SYSFS_FLAG_REMOVED) && 918adc5e8b5STejun Heo pos->parent == parent && hash == pos->hash; 919fd7b9f7bSTejun Heo kernfs_put(pos); 920fd7b9f7bSTejun Heo if (!valid) 921fd7b9f7bSTejun Heo pos = NULL; 922fd7b9f7bSTejun Heo } 923fd7b9f7bSTejun Heo if (!pos && (hash > 1) && (hash < INT_MAX)) { 924adc5e8b5STejun Heo struct rb_node *node = parent->dir.children.rb_node; 925fd7b9f7bSTejun Heo while (node) { 926324a56e1STejun Heo pos = rb_to_kn(node); 927fd7b9f7bSTejun Heo 928adc5e8b5STejun Heo if (hash < pos->hash) 929fd7b9f7bSTejun Heo node = node->rb_left; 930adc5e8b5STejun Heo else if (hash > pos->hash) 931fd7b9f7bSTejun Heo node = node->rb_right; 932fd7b9f7bSTejun Heo else 933fd7b9f7bSTejun Heo break; 934fd7b9f7bSTejun Heo } 935fd7b9f7bSTejun Heo } 936fd7b9f7bSTejun Heo /* Skip over entries in the wrong namespace */ 937adc5e8b5STejun Heo while (pos && pos->ns != ns) { 938adc5e8b5STejun Heo struct rb_node *node = rb_next(&pos->rb); 939fd7b9f7bSTejun Heo if (!node) 940fd7b9f7bSTejun Heo pos = NULL; 941fd7b9f7bSTejun Heo else 942324a56e1STejun Heo pos = rb_to_kn(node); 943fd7b9f7bSTejun Heo } 944fd7b9f7bSTejun Heo return pos; 945fd7b9f7bSTejun Heo } 946fd7b9f7bSTejun Heo 947324a56e1STejun Heo static struct kernfs_node *sysfs_dir_next_pos(const void *ns, 948324a56e1STejun Heo struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos) 949fd7b9f7bSTejun Heo { 950324a56e1STejun Heo pos = sysfs_dir_pos(ns, parent, ino, pos); 951fd7b9f7bSTejun Heo if (pos) 952fd7b9f7bSTejun Heo do { 953adc5e8b5STejun Heo struct rb_node *node = rb_next(&pos->rb); 954fd7b9f7bSTejun Heo if (!node) 955fd7b9f7bSTejun Heo pos = NULL; 956fd7b9f7bSTejun Heo else 957324a56e1STejun Heo pos = rb_to_kn(node); 958adc5e8b5STejun Heo } while (pos && pos->ns != ns); 959fd7b9f7bSTejun Heo return pos; 960fd7b9f7bSTejun Heo } 961fd7b9f7bSTejun Heo 962fd7b9f7bSTejun Heo static int sysfs_readdir(struct file *file, struct dir_context *ctx) 963fd7b9f7bSTejun Heo { 964fd7b9f7bSTejun Heo struct dentry *dentry = file->f_path.dentry; 965324a56e1STejun Heo struct kernfs_node *parent = dentry->d_fsdata; 966324a56e1STejun Heo struct kernfs_node *pos = file->private_data; 967fd7b9f7bSTejun Heo const void *ns = NULL; 968fd7b9f7bSTejun Heo 969fd7b9f7bSTejun Heo if (!dir_emit_dots(file, ctx)) 970fd7b9f7bSTejun Heo return 0; 971fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 972fd7b9f7bSTejun Heo 973324a56e1STejun Heo if (kernfs_ns_enabled(parent)) 974fd7b9f7bSTejun Heo ns = sysfs_info(dentry->d_sb)->ns; 975fd7b9f7bSTejun Heo 976324a56e1STejun Heo for (pos = sysfs_dir_pos(ns, parent, ctx->pos, pos); 977fd7b9f7bSTejun Heo pos; 978324a56e1STejun Heo pos = sysfs_dir_next_pos(ns, parent, ctx->pos, pos)) { 979adc5e8b5STejun Heo const char *name = pos->name; 980fd7b9f7bSTejun Heo unsigned int type = dt_type(pos); 981fd7b9f7bSTejun Heo int len = strlen(name); 982adc5e8b5STejun Heo ino_t ino = pos->ino; 983fd7b9f7bSTejun Heo 984adc5e8b5STejun Heo ctx->pos = pos->hash; 985fd7b9f7bSTejun Heo file->private_data = pos; 986fd7b9f7bSTejun Heo kernfs_get(pos); 987fd7b9f7bSTejun Heo 988fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 989fd7b9f7bSTejun Heo if (!dir_emit(ctx, name, len, ino, type)) 990fd7b9f7bSTejun Heo return 0; 991fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 992fd7b9f7bSTejun Heo } 993fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 994fd7b9f7bSTejun Heo file->private_data = NULL; 995fd7b9f7bSTejun Heo ctx->pos = INT_MAX; 996fd7b9f7bSTejun Heo return 0; 997fd7b9f7bSTejun Heo } 998fd7b9f7bSTejun Heo 999fd7b9f7bSTejun Heo static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) 1000fd7b9f7bSTejun Heo { 1001fd7b9f7bSTejun Heo struct inode *inode = file_inode(file); 1002fd7b9f7bSTejun Heo loff_t ret; 1003fd7b9f7bSTejun Heo 1004fd7b9f7bSTejun Heo mutex_lock(&inode->i_mutex); 1005fd7b9f7bSTejun Heo ret = generic_file_llseek(file, offset, whence); 1006fd7b9f7bSTejun Heo mutex_unlock(&inode->i_mutex); 1007fd7b9f7bSTejun Heo 1008fd7b9f7bSTejun Heo return ret; 1009fd7b9f7bSTejun Heo } 1010fd7b9f7bSTejun Heo 1011fd7b9f7bSTejun Heo const struct file_operations sysfs_dir_operations = { 1012fd7b9f7bSTejun Heo .read = generic_read_dir, 1013fd7b9f7bSTejun Heo .iterate = sysfs_readdir, 1014fd7b9f7bSTejun Heo .release = sysfs_dir_release, 1015fd7b9f7bSTejun Heo .llseek = sysfs_dir_llseek, 1016fd7b9f7bSTejun Heo }; 1017