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 11abd54f02STejun Heo #include <linux/sched.h> 12fd7b9f7bSTejun Heo #include <linux/fs.h> 13fd7b9f7bSTejun Heo #include <linux/namei.h> 14fd7b9f7bSTejun Heo #include <linux/idr.h> 15fd7b9f7bSTejun Heo #include <linux/slab.h> 16fd7b9f7bSTejun Heo #include <linux/security.h> 17fd7b9f7bSTejun Heo #include <linux/hash.h> 18fd7b9f7bSTejun Heo 19fd7b9f7bSTejun Heo #include "kernfs-internal.h" 20fd7b9f7bSTejun Heo 21a797bfc3STejun Heo DEFINE_MUTEX(kernfs_mutex); 22fd7b9f7bSTejun Heo 23adc5e8b5STejun Heo #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) 24fd7b9f7bSTejun Heo 25fd7b9f7bSTejun Heo /** 26c637b8acSTejun Heo * kernfs_name_hash 27fd7b9f7bSTejun Heo * @name: Null terminated string to hash 28fd7b9f7bSTejun Heo * @ns: Namespace tag to hash 29fd7b9f7bSTejun Heo * 30fd7b9f7bSTejun Heo * Returns 31 bit hash of ns + name (so it fits in an off_t ) 31fd7b9f7bSTejun Heo */ 32c637b8acSTejun Heo static unsigned int kernfs_name_hash(const char *name, const void *ns) 33fd7b9f7bSTejun Heo { 34fd7b9f7bSTejun Heo unsigned long hash = init_name_hash(); 35fd7b9f7bSTejun Heo unsigned int len = strlen(name); 36fd7b9f7bSTejun Heo while (len--) 37fd7b9f7bSTejun Heo hash = partial_name_hash(*name++, hash); 38fd7b9f7bSTejun Heo hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31)); 39fd7b9f7bSTejun Heo hash &= 0x7fffffffU; 40fd7b9f7bSTejun Heo /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ 41fd7b9f7bSTejun Heo if (hash < 1) 42fd7b9f7bSTejun Heo hash += 2; 43fd7b9f7bSTejun Heo if (hash >= INT_MAX) 44fd7b9f7bSTejun Heo hash = INT_MAX - 1; 45fd7b9f7bSTejun Heo return hash; 46fd7b9f7bSTejun Heo } 47fd7b9f7bSTejun Heo 48c637b8acSTejun Heo static int kernfs_name_compare(unsigned int hash, const char *name, 49324a56e1STejun Heo const void *ns, const struct kernfs_node *kn) 50fd7b9f7bSTejun Heo { 51adc5e8b5STejun Heo if (hash != kn->hash) 52adc5e8b5STejun Heo return hash - kn->hash; 53adc5e8b5STejun Heo if (ns != kn->ns) 54adc5e8b5STejun Heo return ns - kn->ns; 55adc5e8b5STejun Heo return strcmp(name, kn->name); 56fd7b9f7bSTejun Heo } 57fd7b9f7bSTejun Heo 58c637b8acSTejun Heo static int kernfs_sd_compare(const struct kernfs_node *left, 59324a56e1STejun Heo const struct kernfs_node *right) 60fd7b9f7bSTejun Heo { 61c637b8acSTejun Heo return kernfs_name_compare(left->hash, left->name, left->ns, right); 62fd7b9f7bSTejun Heo } 63fd7b9f7bSTejun Heo 64fd7b9f7bSTejun Heo /** 65c637b8acSTejun Heo * kernfs_link_sibling - link kernfs_node into sibling rbtree 66324a56e1STejun Heo * @kn: kernfs_node of interest 67fd7b9f7bSTejun Heo * 68324a56e1STejun Heo * Link @kn into its sibling rbtree which starts from 69adc5e8b5STejun Heo * @kn->parent->dir.children. 70fd7b9f7bSTejun Heo * 71fd7b9f7bSTejun Heo * Locking: 72a797bfc3STejun Heo * mutex_lock(kernfs_mutex) 73fd7b9f7bSTejun Heo * 74fd7b9f7bSTejun Heo * RETURNS: 75fd7b9f7bSTejun Heo * 0 on susccess -EEXIST on failure. 76fd7b9f7bSTejun Heo */ 77c637b8acSTejun Heo static int kernfs_link_sibling(struct kernfs_node *kn) 78fd7b9f7bSTejun Heo { 79adc5e8b5STejun Heo struct rb_node **node = &kn->parent->dir.children.rb_node; 80fd7b9f7bSTejun Heo struct rb_node *parent = NULL; 81fd7b9f7bSTejun Heo 82df23fc39STejun Heo if (kernfs_type(kn) == KERNFS_DIR) 83adc5e8b5STejun Heo kn->parent->dir.subdirs++; 84fd7b9f7bSTejun Heo 85fd7b9f7bSTejun Heo while (*node) { 86324a56e1STejun Heo struct kernfs_node *pos; 87fd7b9f7bSTejun Heo int result; 88fd7b9f7bSTejun Heo 89324a56e1STejun Heo pos = rb_to_kn(*node); 90fd7b9f7bSTejun Heo parent = *node; 91c637b8acSTejun Heo result = kernfs_sd_compare(kn, pos); 92fd7b9f7bSTejun Heo if (result < 0) 93adc5e8b5STejun Heo node = &pos->rb.rb_left; 94fd7b9f7bSTejun Heo else if (result > 0) 95adc5e8b5STejun Heo node = &pos->rb.rb_right; 96fd7b9f7bSTejun Heo else 97fd7b9f7bSTejun Heo return -EEXIST; 98fd7b9f7bSTejun Heo } 99fd7b9f7bSTejun Heo /* add new node and rebalance the tree */ 100adc5e8b5STejun Heo rb_link_node(&kn->rb, parent, node); 101adc5e8b5STejun Heo rb_insert_color(&kn->rb, &kn->parent->dir.children); 102fd7b9f7bSTejun Heo return 0; 103fd7b9f7bSTejun Heo } 104fd7b9f7bSTejun Heo 105fd7b9f7bSTejun Heo /** 106c637b8acSTejun Heo * kernfs_unlink_sibling - unlink kernfs_node from sibling rbtree 107324a56e1STejun Heo * @kn: kernfs_node of interest 108fd7b9f7bSTejun Heo * 10935beab06STejun Heo * Try to unlink @kn from its sibling rbtree which starts from 11035beab06STejun Heo * kn->parent->dir.children. Returns %true if @kn was actually 11135beab06STejun Heo * removed, %false if @kn wasn't on the rbtree. 112fd7b9f7bSTejun Heo * 113fd7b9f7bSTejun Heo * Locking: 114a797bfc3STejun Heo * mutex_lock(kernfs_mutex) 115fd7b9f7bSTejun Heo */ 11635beab06STejun Heo static bool kernfs_unlink_sibling(struct kernfs_node *kn) 117fd7b9f7bSTejun Heo { 11835beab06STejun Heo if (RB_EMPTY_NODE(&kn->rb)) 11935beab06STejun Heo return false; 12035beab06STejun Heo 121df23fc39STejun Heo if (kernfs_type(kn) == KERNFS_DIR) 122adc5e8b5STejun Heo kn->parent->dir.subdirs--; 123fd7b9f7bSTejun Heo 124adc5e8b5STejun Heo rb_erase(&kn->rb, &kn->parent->dir.children); 12535beab06STejun Heo RB_CLEAR_NODE(&kn->rb); 12635beab06STejun Heo return true; 127fd7b9f7bSTejun Heo } 128fd7b9f7bSTejun Heo 129fd7b9f7bSTejun Heo /** 130c637b8acSTejun Heo * kernfs_get_active - get an active reference to kernfs_node 131324a56e1STejun Heo * @kn: kernfs_node to get an active reference to 132fd7b9f7bSTejun Heo * 133324a56e1STejun Heo * Get an active reference of @kn. This function is noop if @kn 134fd7b9f7bSTejun Heo * is NULL. 135fd7b9f7bSTejun Heo * 136fd7b9f7bSTejun Heo * RETURNS: 137324a56e1STejun Heo * Pointer to @kn on success, NULL on failure. 138fd7b9f7bSTejun Heo */ 139c637b8acSTejun Heo struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) 140fd7b9f7bSTejun Heo { 141324a56e1STejun Heo if (unlikely(!kn)) 142fd7b9f7bSTejun Heo return NULL; 143fd7b9f7bSTejun Heo 144f4b3e631SGreg Kroah-Hartman if (!atomic_inc_unless_negative(&kn->active)) 145f4b3e631SGreg Kroah-Hartman return NULL; 146f4b3e631SGreg Kroah-Hartman 1470890147fSGreg Kroah-Hartman if (kn->flags & KERNFS_LOCKDEP) 148324a56e1STejun Heo rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); 149324a56e1STejun Heo return kn; 150fd7b9f7bSTejun Heo } 151fd7b9f7bSTejun Heo 152fd7b9f7bSTejun Heo /** 153c637b8acSTejun Heo * kernfs_put_active - put an active reference to kernfs_node 154324a56e1STejun Heo * @kn: kernfs_node to put an active reference to 155fd7b9f7bSTejun Heo * 156324a56e1STejun Heo * Put an active reference to @kn. This function is noop if @kn 157fd7b9f7bSTejun Heo * is NULL. 158fd7b9f7bSTejun Heo */ 159c637b8acSTejun Heo void kernfs_put_active(struct kernfs_node *kn) 160fd7b9f7bSTejun Heo { 161abd54f02STejun Heo struct kernfs_root *root = kernfs_root(kn); 162fd7b9f7bSTejun Heo int v; 163fd7b9f7bSTejun Heo 164324a56e1STejun Heo if (unlikely(!kn)) 165fd7b9f7bSTejun Heo return; 166fd7b9f7bSTejun Heo 1670890147fSGreg Kroah-Hartman if (kn->flags & KERNFS_LOCKDEP) 168324a56e1STejun Heo rwsem_release(&kn->dep_map, 1, _RET_IP_); 169adc5e8b5STejun Heo v = atomic_dec_return(&kn->active); 170df23fc39STejun Heo if (likely(v != KN_DEACTIVATED_BIAS)) 171fd7b9f7bSTejun Heo return; 172fd7b9f7bSTejun Heo 173abd54f02STejun Heo wake_up_all(&root->deactivate_waitq); 174fd7b9f7bSTejun Heo } 175fd7b9f7bSTejun Heo 176fd7b9f7bSTejun Heo /** 177798c75a0SGreg Kroah-Hartman * kernfs_deactivate - deactivate kernfs_node 178798c75a0SGreg Kroah-Hartman * @kn: kernfs_node to deactivate 179fd7b9f7bSTejun Heo * 18035beab06STejun Heo * Deny new active references and drain existing ones. Mutiple 18135beab06STejun Heo * removers may invoke this function concurrently on @kn and all will 18235beab06STejun Heo * return after deactivation and draining are complete. 183fd7b9f7bSTejun Heo */ 184798c75a0SGreg Kroah-Hartman static void kernfs_deactivate(struct kernfs_node *kn) 18535beab06STejun Heo __releases(&kernfs_mutex) __acquires(&kernfs_mutex) 186fd7b9f7bSTejun Heo { 187abd54f02STejun Heo struct kernfs_root *root = kernfs_root(kn); 188fd7b9f7bSTejun Heo 18935beab06STejun Heo lockdep_assert_held(&kernfs_mutex); 190798c75a0SGreg Kroah-Hartman BUG_ON(!(kn->flags & KERNFS_REMOVED)); 191798c75a0SGreg Kroah-Hartman 1920890147fSGreg Kroah-Hartman if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) 1930890147fSGreg Kroah-Hartman return; 1940890147fSGreg Kroah-Hartman 19535beab06STejun Heo /* only the first invocation on @kn should deactivate it */ 19635beab06STejun Heo if (atomic_read(&kn->active) >= 0) 197abd54f02STejun Heo atomic_add(KN_DEACTIVATED_BIAS, &kn->active); 198abd54f02STejun Heo 19935beab06STejun Heo mutex_unlock(&kernfs_mutex); 200abd54f02STejun Heo 20135beab06STejun Heo if (kn->flags & KERNFS_LOCKDEP) { 20235beab06STejun Heo rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); 20335beab06STejun Heo if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) 20435beab06STejun Heo lock_contended(&kn->dep_map, _RET_IP_); 20535beab06STejun Heo } 20635beab06STejun Heo 20735beab06STejun Heo /* but everyone should wait for draining */ 208abd54f02STejun Heo wait_event(root->deactivate_waitq, 209abd54f02STejun Heo atomic_read(&kn->active) == KN_DEACTIVATED_BIAS); 210fd7b9f7bSTejun Heo 211a6607930STejun Heo if (kn->flags & KERNFS_LOCKDEP) { 212324a56e1STejun Heo lock_acquired(&kn->dep_map, _RET_IP_); 213324a56e1STejun Heo rwsem_release(&kn->dep_map, 1, _RET_IP_); 214fd7b9f7bSTejun Heo } 21535beab06STejun Heo 21635beab06STejun Heo mutex_lock(&kernfs_mutex); 217a6607930STejun Heo } 218fd7b9f7bSTejun Heo 219fd7b9f7bSTejun Heo /** 220324a56e1STejun Heo * kernfs_get - get a reference count on a kernfs_node 221324a56e1STejun Heo * @kn: the target kernfs_node 222fd7b9f7bSTejun Heo */ 223324a56e1STejun Heo void kernfs_get(struct kernfs_node *kn) 224fd7b9f7bSTejun Heo { 225324a56e1STejun Heo if (kn) { 226adc5e8b5STejun Heo WARN_ON(!atomic_read(&kn->count)); 227adc5e8b5STejun Heo atomic_inc(&kn->count); 228fd7b9f7bSTejun Heo } 229fd7b9f7bSTejun Heo } 230fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_get); 231fd7b9f7bSTejun Heo 232fd7b9f7bSTejun Heo /** 233324a56e1STejun Heo * kernfs_put - put a reference count on a kernfs_node 234324a56e1STejun Heo * @kn: the target kernfs_node 235fd7b9f7bSTejun Heo * 236324a56e1STejun Heo * Put a reference count of @kn and destroy it if it reached zero. 237fd7b9f7bSTejun Heo */ 238324a56e1STejun Heo void kernfs_put(struct kernfs_node *kn) 239fd7b9f7bSTejun Heo { 240324a56e1STejun Heo struct kernfs_node *parent; 241ba7443bcSTejun Heo struct kernfs_root *root; 242fd7b9f7bSTejun Heo 243adc5e8b5STejun Heo if (!kn || !atomic_dec_and_test(&kn->count)) 244fd7b9f7bSTejun Heo return; 245324a56e1STejun Heo root = kernfs_root(kn); 246fd7b9f7bSTejun Heo repeat: 247798c75a0SGreg Kroah-Hartman /* Moving/renaming is always done while holding reference. 248adc5e8b5STejun Heo * kn->parent won't change beneath us. 249fd7b9f7bSTejun Heo */ 250adc5e8b5STejun Heo parent = kn->parent; 251fd7b9f7bSTejun Heo 252798c75a0SGreg Kroah-Hartman WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", 253798c75a0SGreg Kroah-Hartman parent ? parent->name : "", kn->name); 254fd7b9f7bSTejun Heo 255df23fc39STejun Heo if (kernfs_type(kn) == KERNFS_LINK) 256adc5e8b5STejun Heo kernfs_put(kn->symlink.target_kn); 2572063d608STejun Heo if (!(kn->flags & KERNFS_STATIC_NAME)) 258adc5e8b5STejun Heo kfree(kn->name); 259adc5e8b5STejun Heo if (kn->iattr) { 260adc5e8b5STejun Heo if (kn->iattr->ia_secdata) 261adc5e8b5STejun Heo security_release_secctx(kn->iattr->ia_secdata, 262adc5e8b5STejun Heo kn->iattr->ia_secdata_len); 263adc5e8b5STejun Heo simple_xattrs_free(&kn->iattr->xattrs); 2642322392bSTejun Heo } 265adc5e8b5STejun Heo kfree(kn->iattr); 266adc5e8b5STejun Heo ida_simple_remove(&root->ino_ida, kn->ino); 267a797bfc3STejun Heo kmem_cache_free(kernfs_node_cache, kn); 268fd7b9f7bSTejun Heo 269324a56e1STejun Heo kn = parent; 270324a56e1STejun Heo if (kn) { 271adc5e8b5STejun Heo if (atomic_dec_and_test(&kn->count)) 272fd7b9f7bSTejun Heo goto repeat; 273ba7443bcSTejun Heo } else { 274324a56e1STejun Heo /* just released the root kn, free @root too */ 275bc755553STejun Heo ida_destroy(&root->ino_ida); 276ba7443bcSTejun Heo kfree(root); 277ba7443bcSTejun Heo } 278fd7b9f7bSTejun Heo } 279fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_put); 280fd7b9f7bSTejun Heo 281c637b8acSTejun Heo static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) 282fd7b9f7bSTejun Heo { 283324a56e1STejun Heo struct kernfs_node *kn; 284fd7b9f7bSTejun Heo 285fd7b9f7bSTejun Heo if (flags & LOOKUP_RCU) 286fd7b9f7bSTejun Heo return -ECHILD; 287fd7b9f7bSTejun Heo 28819bbb926STejun Heo /* Always perform fresh lookup for negatives */ 28919bbb926STejun Heo if (!dentry->d_inode) 29019bbb926STejun Heo goto out_bad_unlocked; 29119bbb926STejun Heo 292324a56e1STejun Heo kn = dentry->d_fsdata; 293a797bfc3STejun Heo mutex_lock(&kernfs_mutex); 294fd7b9f7bSTejun Heo 295798c75a0SGreg Kroah-Hartman /* The kernfs node has been deleted */ 296798c75a0SGreg Kroah-Hartman if (kn->flags & KERNFS_REMOVED) 297fd7b9f7bSTejun Heo goto out_bad; 298fd7b9f7bSTejun Heo 299c637b8acSTejun Heo /* The kernfs node has been moved? */ 300adc5e8b5STejun Heo if (dentry->d_parent->d_fsdata != kn->parent) 301fd7b9f7bSTejun Heo goto out_bad; 302fd7b9f7bSTejun Heo 303c637b8acSTejun Heo /* The kernfs node has been renamed */ 304adc5e8b5STejun Heo if (strcmp(dentry->d_name.name, kn->name) != 0) 305fd7b9f7bSTejun Heo goto out_bad; 306fd7b9f7bSTejun Heo 307c637b8acSTejun Heo /* The kernfs node has been moved to a different namespace */ 308adc5e8b5STejun Heo if (kn->parent && kernfs_ns_enabled(kn->parent) && 309c525aaddSTejun Heo kernfs_info(dentry->d_sb)->ns != kn->ns) 310fd7b9f7bSTejun Heo goto out_bad; 311fd7b9f7bSTejun Heo 312a797bfc3STejun Heo mutex_unlock(&kernfs_mutex); 313fd7b9f7bSTejun Heo out_valid: 314fd7b9f7bSTejun Heo return 1; 315fd7b9f7bSTejun Heo out_bad: 316a797bfc3STejun Heo mutex_unlock(&kernfs_mutex); 31719bbb926STejun Heo out_bad_unlocked: 31819bbb926STejun Heo /* 31919bbb926STejun Heo * @dentry doesn't match the underlying kernfs node, drop the 32019bbb926STejun Heo * dentry and force lookup. If we have submounts we must allow the 32119bbb926STejun Heo * vfs caches to lie about the state of the filesystem to prevent 32219bbb926STejun Heo * leaks and other nasty things, so use check_submounts_and_drop() 32319bbb926STejun Heo * instead of d_drop(). 324fd7b9f7bSTejun Heo */ 325fd7b9f7bSTejun Heo if (check_submounts_and_drop(dentry) != 0) 326fd7b9f7bSTejun Heo goto out_valid; 327fd7b9f7bSTejun Heo 328fd7b9f7bSTejun Heo return 0; 329fd7b9f7bSTejun Heo } 330fd7b9f7bSTejun Heo 331c637b8acSTejun Heo static void kernfs_dop_release(struct dentry *dentry) 332fd7b9f7bSTejun Heo { 333fd7b9f7bSTejun Heo kernfs_put(dentry->d_fsdata); 334fd7b9f7bSTejun Heo } 335fd7b9f7bSTejun Heo 336a797bfc3STejun Heo const struct dentry_operations kernfs_dops = { 337c637b8acSTejun Heo .d_revalidate = kernfs_dop_revalidate, 338c637b8acSTejun Heo .d_release = kernfs_dop_release, 339fd7b9f7bSTejun Heo }; 340fd7b9f7bSTejun Heo 341db4aad20STejun Heo static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, 342db4aad20STejun Heo const char *name, umode_t mode, 343db4aad20STejun Heo unsigned flags) 344fd7b9f7bSTejun Heo { 345fd7b9f7bSTejun Heo char *dup_name = NULL; 346324a56e1STejun Heo struct kernfs_node *kn; 347bc755553STejun Heo int ret; 348fd7b9f7bSTejun Heo 3492063d608STejun Heo if (!(flags & KERNFS_STATIC_NAME)) { 350fd7b9f7bSTejun Heo name = dup_name = kstrdup(name, GFP_KERNEL); 351fd7b9f7bSTejun Heo if (!name) 352fd7b9f7bSTejun Heo return NULL; 353fd7b9f7bSTejun Heo } 354fd7b9f7bSTejun Heo 355a797bfc3STejun Heo kn = kmem_cache_zalloc(kernfs_node_cache, GFP_KERNEL); 356324a56e1STejun Heo if (!kn) 357fd7b9f7bSTejun Heo goto err_out1; 358fd7b9f7bSTejun Heo 359bc755553STejun Heo ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL); 360bc755553STejun Heo if (ret < 0) 361fd7b9f7bSTejun Heo goto err_out2; 362adc5e8b5STejun Heo kn->ino = ret; 363fd7b9f7bSTejun Heo 364adc5e8b5STejun Heo atomic_set(&kn->count, 1); 365798c75a0SGreg Kroah-Hartman atomic_set(&kn->active, 0); 36635beab06STejun Heo RB_CLEAR_NODE(&kn->rb); 367fd7b9f7bSTejun Heo 368adc5e8b5STejun Heo kn->name = name; 369adc5e8b5STejun Heo kn->mode = mode; 370798c75a0SGreg Kroah-Hartman kn->flags = flags | KERNFS_REMOVED; 371fd7b9f7bSTejun Heo 372324a56e1STejun Heo return kn; 373fd7b9f7bSTejun Heo 374fd7b9f7bSTejun Heo err_out2: 375a797bfc3STejun Heo kmem_cache_free(kernfs_node_cache, kn); 376fd7b9f7bSTejun Heo err_out1: 377fd7b9f7bSTejun Heo kfree(dup_name); 378fd7b9f7bSTejun Heo return NULL; 379fd7b9f7bSTejun Heo } 380fd7b9f7bSTejun Heo 381db4aad20STejun Heo struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, 382db4aad20STejun Heo const char *name, umode_t mode, 383db4aad20STejun Heo unsigned flags) 384db4aad20STejun Heo { 385db4aad20STejun Heo struct kernfs_node *kn; 386db4aad20STejun Heo 387db4aad20STejun Heo kn = __kernfs_new_node(kernfs_root(parent), name, mode, flags); 388db4aad20STejun Heo if (kn) { 389db4aad20STejun Heo kernfs_get(parent); 390db4aad20STejun Heo kn->parent = parent; 391db4aad20STejun Heo } 392db4aad20STejun Heo return kn; 393db4aad20STejun Heo } 394db4aad20STejun Heo 395fd7b9f7bSTejun Heo /** 3967653fe9dSGreg Kroah-Hartman * kernfs_addrm_start - prepare for kernfs_node add/remove 3977653fe9dSGreg Kroah-Hartman * @acxt: pointer to kernfs_addrm_cxt to be used 3987653fe9dSGreg Kroah-Hartman * 3997653fe9dSGreg Kroah-Hartman * This function is called when the caller is about to add or remove 4007653fe9dSGreg Kroah-Hartman * kernfs_node. This function acquires kernfs_mutex. @acxt is used 4017653fe9dSGreg Kroah-Hartman * to keep and pass context to other addrm functions. 4027653fe9dSGreg Kroah-Hartman * 4037653fe9dSGreg Kroah-Hartman * LOCKING: 4047653fe9dSGreg Kroah-Hartman * Kernel thread context (may sleep). kernfs_mutex is locked on 4057653fe9dSGreg Kroah-Hartman * return. 4067653fe9dSGreg Kroah-Hartman */ 4077653fe9dSGreg Kroah-Hartman void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) 4087653fe9dSGreg Kroah-Hartman __acquires(kernfs_mutex) 4097653fe9dSGreg Kroah-Hartman { 4107653fe9dSGreg Kroah-Hartman memset(acxt, 0, sizeof(*acxt)); 4117653fe9dSGreg Kroah-Hartman 4127653fe9dSGreg Kroah-Hartman mutex_lock(&kernfs_mutex); 4137653fe9dSGreg Kroah-Hartman } 4147653fe9dSGreg Kroah-Hartman 4157653fe9dSGreg Kroah-Hartman /** 416c637b8acSTejun Heo * kernfs_add_one - add kernfs_node to parent without warning 4177653fe9dSGreg Kroah-Hartman * @acxt: addrm context to use 418324a56e1STejun Heo * @kn: kernfs_node to be added 419fd7b9f7bSTejun Heo * 420db4aad20STejun Heo * The caller must already have initialized @kn->parent. This 421db4aad20STejun Heo * function increments nlink of the parent's inode if @kn is a 422db4aad20STejun Heo * directory and link into the children list of the parent. 423fd7b9f7bSTejun Heo * 4247653fe9dSGreg Kroah-Hartman * This function should be called between calls to 4257653fe9dSGreg Kroah-Hartman * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed 4267653fe9dSGreg Kroah-Hartman * the same @acxt as passed to kernfs_addrm_start(). 4277653fe9dSGreg Kroah-Hartman * 4287653fe9dSGreg Kroah-Hartman * LOCKING: 4297653fe9dSGreg Kroah-Hartman * Determined by kernfs_addrm_start(). 4307653fe9dSGreg Kroah-Hartman * 431fd7b9f7bSTejun Heo * RETURNS: 432fd7b9f7bSTejun Heo * 0 on success, -EEXIST if entry with the given name already 433fd7b9f7bSTejun Heo * exists. 434fd7b9f7bSTejun Heo */ 435db4aad20STejun Heo int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn) 436fd7b9f7bSTejun Heo { 437db4aad20STejun Heo struct kernfs_node *parent = kn->parent; 4387653fe9dSGreg Kroah-Hartman bool has_ns = kernfs_ns_enabled(parent); 439c525aaddSTejun Heo struct kernfs_iattrs *ps_iattr; 440fd7b9f7bSTejun Heo int ret; 441fd7b9f7bSTejun Heo 4427653fe9dSGreg Kroah-Hartman if (has_ns != (bool)kn->ns) { 4437653fe9dSGreg Kroah-Hartman WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", 4447653fe9dSGreg Kroah-Hartman has_ns ? "required" : "invalid", parent->name, kn->name); 4457653fe9dSGreg Kroah-Hartman return -EINVAL; 4467653fe9dSGreg Kroah-Hartman } 447fd7b9f7bSTejun Heo 448df23fc39STejun Heo if (kernfs_type(parent) != KERNFS_DIR) 4497653fe9dSGreg Kroah-Hartman return -EINVAL; 450fd7b9f7bSTejun Heo 451798c75a0SGreg Kroah-Hartman if (parent->flags & KERNFS_REMOVED) 452798c75a0SGreg Kroah-Hartman return -ENOENT; 453798c75a0SGreg Kroah-Hartman 454c637b8acSTejun Heo kn->hash = kernfs_name_hash(kn->name, kn->ns); 455fd7b9f7bSTejun Heo 456c637b8acSTejun Heo ret = kernfs_link_sibling(kn); 457fd7b9f7bSTejun Heo if (ret) 4587653fe9dSGreg Kroah-Hartman return ret; 459fd7b9f7bSTejun Heo 460fd7b9f7bSTejun Heo /* Update timestamps on the parent */ 461adc5e8b5STejun Heo ps_iattr = parent->iattr; 462fd7b9f7bSTejun Heo if (ps_iattr) { 463fd7b9f7bSTejun Heo struct iattr *ps_iattrs = &ps_iattr->ia_iattr; 464fd7b9f7bSTejun Heo ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; 465fd7b9f7bSTejun Heo } 466fd7b9f7bSTejun Heo 467fd7b9f7bSTejun Heo /* Mark the entry added into directory tree */ 468798c75a0SGreg Kroah-Hartman kn->flags &= ~KERNFS_REMOVED; 469798c75a0SGreg Kroah-Hartman 4707653fe9dSGreg Kroah-Hartman return 0; 4717653fe9dSGreg Kroah-Hartman } 4727653fe9dSGreg Kroah-Hartman 4737653fe9dSGreg Kroah-Hartman /** 4747653fe9dSGreg Kroah-Hartman * kernfs_addrm_finish - finish up kernfs_node add/remove 4757653fe9dSGreg Kroah-Hartman * @acxt: addrm context to finish up 4767653fe9dSGreg Kroah-Hartman * 4777653fe9dSGreg Kroah-Hartman * Finish up kernfs_node add/remove. Resources acquired by 4787653fe9dSGreg Kroah-Hartman * kernfs_addrm_start() are released and removed kernfs_nodes are 4797653fe9dSGreg Kroah-Hartman * cleaned up. 4807653fe9dSGreg Kroah-Hartman * 4817653fe9dSGreg Kroah-Hartman * LOCKING: 4827653fe9dSGreg Kroah-Hartman * kernfs_mutex is released. 4837653fe9dSGreg Kroah-Hartman */ 4847653fe9dSGreg Kroah-Hartman void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) 4857653fe9dSGreg Kroah-Hartman __releases(kernfs_mutex) 4867653fe9dSGreg Kroah-Hartman { 4877653fe9dSGreg Kroah-Hartman /* release resources acquired by kernfs_addrm_start() */ 488a797bfc3STejun Heo mutex_unlock(&kernfs_mutex); 4897653fe9dSGreg Kroah-Hartman 4907653fe9dSGreg Kroah-Hartman /* kill removed kernfs_nodes */ 4917653fe9dSGreg Kroah-Hartman while (acxt->removed) { 4927653fe9dSGreg Kroah-Hartman struct kernfs_node *kn = acxt->removed; 4937653fe9dSGreg Kroah-Hartman 4947653fe9dSGreg Kroah-Hartman acxt->removed = kn->u.removed_list; 4957653fe9dSGreg Kroah-Hartman 49655f6e30dSGreg Kroah-Hartman kernfs_unmap_bin_file(kn); 4977653fe9dSGreg Kroah-Hartman kernfs_put(kn); 4987653fe9dSGreg Kroah-Hartman } 499fd7b9f7bSTejun Heo } 500fd7b9f7bSTejun Heo 501fd7b9f7bSTejun Heo /** 502324a56e1STejun Heo * kernfs_find_ns - find kernfs_node with the given name 503324a56e1STejun Heo * @parent: kernfs_node to search under 504fd7b9f7bSTejun Heo * @name: name to look for 505fd7b9f7bSTejun Heo * @ns: the namespace tag to use 506fd7b9f7bSTejun Heo * 507324a56e1STejun Heo * Look for kernfs_node with name @name under @parent. Returns pointer to 508324a56e1STejun Heo * the found kernfs_node on success, %NULL on failure. 509fd7b9f7bSTejun Heo */ 510324a56e1STejun Heo static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, 511fd7b9f7bSTejun Heo const unsigned char *name, 512fd7b9f7bSTejun Heo const void *ns) 513fd7b9f7bSTejun Heo { 514adc5e8b5STejun Heo struct rb_node *node = parent->dir.children.rb_node; 515ac9bba03STejun Heo bool has_ns = kernfs_ns_enabled(parent); 516fd7b9f7bSTejun Heo unsigned int hash; 517fd7b9f7bSTejun Heo 518a797bfc3STejun Heo lockdep_assert_held(&kernfs_mutex); 519fd7b9f7bSTejun Heo 520fd7b9f7bSTejun Heo if (has_ns != (bool)ns) { 521c637b8acSTejun Heo WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", 522adc5e8b5STejun Heo has_ns ? "required" : "invalid", parent->name, name); 523fd7b9f7bSTejun Heo return NULL; 524fd7b9f7bSTejun Heo } 525fd7b9f7bSTejun Heo 526c637b8acSTejun Heo hash = kernfs_name_hash(name, ns); 527fd7b9f7bSTejun Heo while (node) { 528324a56e1STejun Heo struct kernfs_node *kn; 529fd7b9f7bSTejun Heo int result; 530fd7b9f7bSTejun Heo 531324a56e1STejun Heo kn = rb_to_kn(node); 532c637b8acSTejun Heo result = kernfs_name_compare(hash, name, ns, kn); 533fd7b9f7bSTejun Heo if (result < 0) 534fd7b9f7bSTejun Heo node = node->rb_left; 535fd7b9f7bSTejun Heo else if (result > 0) 536fd7b9f7bSTejun Heo node = node->rb_right; 537fd7b9f7bSTejun Heo else 538324a56e1STejun Heo return kn; 539fd7b9f7bSTejun Heo } 540fd7b9f7bSTejun Heo return NULL; 541fd7b9f7bSTejun Heo } 542fd7b9f7bSTejun Heo 543fd7b9f7bSTejun Heo /** 544324a56e1STejun Heo * kernfs_find_and_get_ns - find and get kernfs_node with the given name 545324a56e1STejun Heo * @parent: kernfs_node to search under 546fd7b9f7bSTejun Heo * @name: name to look for 547fd7b9f7bSTejun Heo * @ns: the namespace tag to use 548fd7b9f7bSTejun Heo * 549324a56e1STejun Heo * Look for kernfs_node with name @name under @parent and get a reference 550fd7b9f7bSTejun Heo * if found. This function may sleep and returns pointer to the found 551324a56e1STejun Heo * kernfs_node on success, %NULL on failure. 552fd7b9f7bSTejun Heo */ 553324a56e1STejun Heo struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, 554fd7b9f7bSTejun Heo const char *name, const void *ns) 555fd7b9f7bSTejun Heo { 556324a56e1STejun Heo struct kernfs_node *kn; 557fd7b9f7bSTejun Heo 558a797bfc3STejun Heo mutex_lock(&kernfs_mutex); 559324a56e1STejun Heo kn = kernfs_find_ns(parent, name, ns); 560324a56e1STejun Heo kernfs_get(kn); 561a797bfc3STejun Heo mutex_unlock(&kernfs_mutex); 562fd7b9f7bSTejun Heo 563324a56e1STejun Heo return kn; 564fd7b9f7bSTejun Heo } 565fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); 566fd7b9f7bSTejun Heo 567fd7b9f7bSTejun Heo /** 568ba7443bcSTejun Heo * kernfs_create_root - create a new kernfs hierarchy 56980b9bbefSTejun Heo * @kdops: optional directory syscall operations for the hierarchy 570ba7443bcSTejun Heo * @priv: opaque data associated with the new directory 571ba7443bcSTejun Heo * 572ba7443bcSTejun Heo * Returns the root of the new hierarchy on success, ERR_PTR() value on 573ba7443bcSTejun Heo * failure. 574ba7443bcSTejun Heo */ 57580b9bbefSTejun Heo struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) 576ba7443bcSTejun Heo { 577ba7443bcSTejun Heo struct kernfs_root *root; 578324a56e1STejun Heo struct kernfs_node *kn; 579ba7443bcSTejun Heo 580ba7443bcSTejun Heo root = kzalloc(sizeof(*root), GFP_KERNEL); 581ba7443bcSTejun Heo if (!root) 582ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 583ba7443bcSTejun Heo 584bc755553STejun Heo ida_init(&root->ino_ida); 585bc755553STejun Heo 586db4aad20STejun Heo kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO, 587db4aad20STejun Heo KERNFS_DIR); 588324a56e1STejun Heo if (!kn) { 589bc755553STejun Heo ida_destroy(&root->ino_ida); 590ba7443bcSTejun Heo kfree(root); 591ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 592ba7443bcSTejun Heo } 593ba7443bcSTejun Heo 594798c75a0SGreg Kroah-Hartman kn->flags &= ~KERNFS_REMOVED; 595324a56e1STejun Heo kn->priv = priv; 596adc5e8b5STejun Heo kn->dir.root = root; 597ba7443bcSTejun Heo 59880b9bbefSTejun Heo root->dir_ops = kdops; 599324a56e1STejun Heo root->kn = kn; 600abd54f02STejun Heo init_waitqueue_head(&root->deactivate_waitq); 601ba7443bcSTejun Heo 602ba7443bcSTejun Heo return root; 603ba7443bcSTejun Heo } 604ba7443bcSTejun Heo 605ba7443bcSTejun Heo /** 606ba7443bcSTejun Heo * kernfs_destroy_root - destroy a kernfs hierarchy 607ba7443bcSTejun Heo * @root: root of the hierarchy to destroy 608ba7443bcSTejun Heo * 609ba7443bcSTejun Heo * Destroy the hierarchy anchored at @root by removing all existing 610ba7443bcSTejun Heo * directories and destroying @root. 611ba7443bcSTejun Heo */ 612ba7443bcSTejun Heo void kernfs_destroy_root(struct kernfs_root *root) 613ba7443bcSTejun Heo { 614324a56e1STejun Heo kernfs_remove(root->kn); /* will also free @root */ 615ba7443bcSTejun Heo } 616ba7443bcSTejun Heo 617ba7443bcSTejun Heo /** 618fd7b9f7bSTejun Heo * kernfs_create_dir_ns - create a directory 619fd7b9f7bSTejun Heo * @parent: parent in which to create a new directory 620fd7b9f7bSTejun Heo * @name: name of the new directory 621bb8b9d09STejun Heo * @mode: mode of the new directory 622fd7b9f7bSTejun Heo * @priv: opaque data associated with the new directory 623fd7b9f7bSTejun Heo * @ns: optional namespace tag of the directory 624fd7b9f7bSTejun Heo * 625fd7b9f7bSTejun Heo * Returns the created node on success, ERR_PTR() value on failure. 626fd7b9f7bSTejun Heo */ 627324a56e1STejun Heo struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, 628bb8b9d09STejun Heo const char *name, umode_t mode, 629bb8b9d09STejun Heo void *priv, const void *ns) 630fd7b9f7bSTejun Heo { 6317653fe9dSGreg Kroah-Hartman struct kernfs_addrm_cxt acxt; 632324a56e1STejun Heo struct kernfs_node *kn; 633fd7b9f7bSTejun Heo int rc; 634fd7b9f7bSTejun Heo 635fd7b9f7bSTejun Heo /* allocate */ 636db4aad20STejun Heo kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR); 637324a56e1STejun Heo if (!kn) 638fd7b9f7bSTejun Heo return ERR_PTR(-ENOMEM); 639fd7b9f7bSTejun Heo 640adc5e8b5STejun Heo kn->dir.root = parent->dir.root; 641adc5e8b5STejun Heo kn->ns = ns; 642324a56e1STejun Heo kn->priv = priv; 643fd7b9f7bSTejun Heo 644fd7b9f7bSTejun Heo /* link in */ 6457653fe9dSGreg Kroah-Hartman kernfs_addrm_start(&acxt); 646db4aad20STejun Heo rc = kernfs_add_one(&acxt, kn); 6477653fe9dSGreg Kroah-Hartman kernfs_addrm_finish(&acxt); 6487653fe9dSGreg Kroah-Hartman 649fd7b9f7bSTejun Heo if (!rc) 650324a56e1STejun Heo return kn; 651fd7b9f7bSTejun Heo 652324a56e1STejun Heo kernfs_put(kn); 653fd7b9f7bSTejun Heo return ERR_PTR(rc); 654fd7b9f7bSTejun Heo } 655fd7b9f7bSTejun Heo 656c637b8acSTejun Heo static struct dentry *kernfs_iop_lookup(struct inode *dir, 657c637b8acSTejun Heo struct dentry *dentry, 658fd7b9f7bSTejun Heo unsigned int flags) 659fd7b9f7bSTejun Heo { 66019bbb926STejun Heo struct dentry *ret; 661324a56e1STejun Heo struct kernfs_node *parent = dentry->d_parent->d_fsdata; 662324a56e1STejun Heo struct kernfs_node *kn; 663fd7b9f7bSTejun Heo struct inode *inode; 664fd7b9f7bSTejun Heo const void *ns = NULL; 665fd7b9f7bSTejun Heo 666a797bfc3STejun Heo mutex_lock(&kernfs_mutex); 667fd7b9f7bSTejun Heo 668324a56e1STejun Heo if (kernfs_ns_enabled(parent)) 669c525aaddSTejun Heo ns = kernfs_info(dir->i_sb)->ns; 670fd7b9f7bSTejun Heo 671324a56e1STejun Heo kn = kernfs_find_ns(parent, dentry->d_name.name, ns); 672fd7b9f7bSTejun Heo 673fd7b9f7bSTejun Heo /* no such entry */ 674324a56e1STejun Heo if (!kn) { 67519bbb926STejun Heo ret = NULL; 676fd7b9f7bSTejun Heo goto out_unlock; 677fd7b9f7bSTejun Heo } 678324a56e1STejun Heo kernfs_get(kn); 679324a56e1STejun Heo dentry->d_fsdata = kn; 680fd7b9f7bSTejun Heo 681fd7b9f7bSTejun Heo /* attach dentry and inode */ 682c637b8acSTejun Heo inode = kernfs_get_inode(dir->i_sb, kn); 683fd7b9f7bSTejun Heo if (!inode) { 684fd7b9f7bSTejun Heo ret = ERR_PTR(-ENOMEM); 685fd7b9f7bSTejun Heo goto out_unlock; 686fd7b9f7bSTejun Heo } 687fd7b9f7bSTejun Heo 688fd7b9f7bSTejun Heo /* instantiate and hash dentry */ 689fd7b9f7bSTejun Heo ret = d_materialise_unique(dentry, inode); 690fd7b9f7bSTejun Heo out_unlock: 691a797bfc3STejun Heo mutex_unlock(&kernfs_mutex); 692fd7b9f7bSTejun Heo return ret; 693fd7b9f7bSTejun Heo } 694fd7b9f7bSTejun Heo 69580b9bbefSTejun Heo static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry, 69680b9bbefSTejun Heo umode_t mode) 69780b9bbefSTejun Heo { 69880b9bbefSTejun Heo struct kernfs_node *parent = dir->i_private; 69980b9bbefSTejun Heo struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops; 70080b9bbefSTejun Heo 70180b9bbefSTejun Heo if (!kdops || !kdops->mkdir) 70280b9bbefSTejun Heo return -EPERM; 70380b9bbefSTejun Heo 70480b9bbefSTejun Heo return kdops->mkdir(parent, dentry->d_name.name, mode); 70580b9bbefSTejun Heo } 70680b9bbefSTejun Heo 70780b9bbefSTejun Heo static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry) 70880b9bbefSTejun Heo { 70980b9bbefSTejun Heo struct kernfs_node *kn = dentry->d_fsdata; 71080b9bbefSTejun Heo struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; 71180b9bbefSTejun Heo 71280b9bbefSTejun Heo if (!kdops || !kdops->rmdir) 71380b9bbefSTejun Heo return -EPERM; 71480b9bbefSTejun Heo 71580b9bbefSTejun Heo return kdops->rmdir(kn); 71680b9bbefSTejun Heo } 71780b9bbefSTejun Heo 71880b9bbefSTejun Heo static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry, 71980b9bbefSTejun Heo struct inode *new_dir, struct dentry *new_dentry) 72080b9bbefSTejun Heo { 72180b9bbefSTejun Heo struct kernfs_node *kn = old_dentry->d_fsdata; 72280b9bbefSTejun Heo struct kernfs_node *new_parent = new_dir->i_private; 72380b9bbefSTejun Heo struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; 72480b9bbefSTejun Heo 72580b9bbefSTejun Heo if (!kdops || !kdops->rename) 72680b9bbefSTejun Heo return -EPERM; 72780b9bbefSTejun Heo 72880b9bbefSTejun Heo return kdops->rename(kn, new_parent, new_dentry->d_name.name); 72980b9bbefSTejun Heo } 73080b9bbefSTejun Heo 731a797bfc3STejun Heo const struct inode_operations kernfs_dir_iops = { 732c637b8acSTejun Heo .lookup = kernfs_iop_lookup, 733c637b8acSTejun Heo .permission = kernfs_iop_permission, 734c637b8acSTejun Heo .setattr = kernfs_iop_setattr, 735c637b8acSTejun Heo .getattr = kernfs_iop_getattr, 736c637b8acSTejun Heo .setxattr = kernfs_iop_setxattr, 737c637b8acSTejun Heo .removexattr = kernfs_iop_removexattr, 738c637b8acSTejun Heo .getxattr = kernfs_iop_getxattr, 739c637b8acSTejun Heo .listxattr = kernfs_iop_listxattr, 74080b9bbefSTejun Heo 74180b9bbefSTejun Heo .mkdir = kernfs_iop_mkdir, 74280b9bbefSTejun Heo .rmdir = kernfs_iop_rmdir, 74380b9bbefSTejun Heo .rename = kernfs_iop_rename, 744fd7b9f7bSTejun Heo }; 745fd7b9f7bSTejun Heo 746c637b8acSTejun Heo static struct kernfs_node *kernfs_leftmost_descendant(struct kernfs_node *pos) 747fd7b9f7bSTejun Heo { 748324a56e1STejun Heo struct kernfs_node *last; 749fd7b9f7bSTejun Heo 750fd7b9f7bSTejun Heo while (true) { 751fd7b9f7bSTejun Heo struct rb_node *rbn; 752fd7b9f7bSTejun Heo 753fd7b9f7bSTejun Heo last = pos; 754fd7b9f7bSTejun Heo 755df23fc39STejun Heo if (kernfs_type(pos) != KERNFS_DIR) 756fd7b9f7bSTejun Heo break; 757fd7b9f7bSTejun Heo 758adc5e8b5STejun Heo rbn = rb_first(&pos->dir.children); 759fd7b9f7bSTejun Heo if (!rbn) 760fd7b9f7bSTejun Heo break; 761fd7b9f7bSTejun Heo 762324a56e1STejun Heo pos = rb_to_kn(rbn); 763fd7b9f7bSTejun Heo } 764fd7b9f7bSTejun Heo 765fd7b9f7bSTejun Heo return last; 766fd7b9f7bSTejun Heo } 767fd7b9f7bSTejun Heo 768fd7b9f7bSTejun Heo /** 769c637b8acSTejun Heo * kernfs_next_descendant_post - find the next descendant for post-order walk 770fd7b9f7bSTejun Heo * @pos: the current position (%NULL to initiate traversal) 771324a56e1STejun Heo * @root: kernfs_node whose descendants to walk 772fd7b9f7bSTejun Heo * 773fd7b9f7bSTejun Heo * Find the next descendant to visit for post-order traversal of @root's 774fd7b9f7bSTejun Heo * descendants. @root is included in the iteration and the last node to be 775fd7b9f7bSTejun Heo * visited. 776fd7b9f7bSTejun Heo */ 777c637b8acSTejun Heo static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, 778324a56e1STejun Heo struct kernfs_node *root) 779fd7b9f7bSTejun Heo { 780fd7b9f7bSTejun Heo struct rb_node *rbn; 781fd7b9f7bSTejun Heo 782a797bfc3STejun Heo lockdep_assert_held(&kernfs_mutex); 783fd7b9f7bSTejun Heo 784fd7b9f7bSTejun Heo /* if first iteration, visit leftmost descendant which may be root */ 785fd7b9f7bSTejun Heo if (!pos) 786c637b8acSTejun Heo return kernfs_leftmost_descendant(root); 787fd7b9f7bSTejun Heo 788fd7b9f7bSTejun Heo /* if we visited @root, we're done */ 789fd7b9f7bSTejun Heo if (pos == root) 790fd7b9f7bSTejun Heo return NULL; 791fd7b9f7bSTejun Heo 792fd7b9f7bSTejun Heo /* if there's an unvisited sibling, visit its leftmost descendant */ 793adc5e8b5STejun Heo rbn = rb_next(&pos->rb); 794fd7b9f7bSTejun Heo if (rbn) 795c637b8acSTejun Heo return kernfs_leftmost_descendant(rb_to_kn(rbn)); 796fd7b9f7bSTejun Heo 797fd7b9f7bSTejun Heo /* no sibling left, visit parent */ 798adc5e8b5STejun Heo return pos->parent; 799fd7b9f7bSTejun Heo } 800fd7b9f7bSTejun Heo 8017653fe9dSGreg Kroah-Hartman static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, 8027653fe9dSGreg Kroah-Hartman struct kernfs_node *kn) 803fd7b9f7bSTejun Heo { 80435beab06STejun Heo struct kernfs_node *pos; 80535beab06STejun Heo 80635beab06STejun Heo lockdep_assert_held(&kernfs_mutex); 807fd7b9f7bSTejun Heo 808ce9b499cSGreg Kroah-Hartman if (!kn) 809ce9b499cSGreg Kroah-Hartman return; 810ce9b499cSGreg Kroah-Hartman 811c637b8acSTejun Heo pr_debug("kernfs %s: removing\n", kn->name); 812fd7b9f7bSTejun Heo 81335beab06STejun Heo /* disable lookup and node creation under @kn */ 81435beab06STejun Heo pos = NULL; 81535beab06STejun Heo while ((pos = kernfs_next_descendant_post(pos, kn))) 81635beab06STejun Heo pos->flags |= KERNFS_REMOVED; 81735beab06STejun Heo 81835beab06STejun Heo /* deactivate and unlink the subtree node-by-node */ 819fd7b9f7bSTejun Heo do { 82035beab06STejun Heo pos = kernfs_leftmost_descendant(kn); 82135beab06STejun Heo 82235beab06STejun Heo /* 82335beab06STejun Heo * kernfs_deactivate() drops kernfs_mutex temporarily and 82435beab06STejun Heo * @pos's base ref could have been put by someone else by 82535beab06STejun Heo * the time the function returns. Make sure it doesn't go 82635beab06STejun Heo * away underneath us. 82735beab06STejun Heo */ 82835beab06STejun Heo kernfs_get(pos); 82935beab06STejun Heo 83035beab06STejun Heo kernfs_deactivate(pos); 83135beab06STejun Heo 83235beab06STejun Heo /* 83335beab06STejun Heo * kernfs_unlink_sibling() succeeds once per node. Use it 83435beab06STejun Heo * to decide who's responsible for cleanups. 83535beab06STejun Heo */ 83635beab06STejun Heo if (!pos->parent || kernfs_unlink_sibling(pos)) { 83735beab06STejun Heo struct kernfs_iattrs *ps_iattr = 83835beab06STejun Heo pos->parent ? pos->parent->iattr : NULL; 83935beab06STejun Heo 84035beab06STejun Heo /* update timestamps on the parent */ 84135beab06STejun Heo if (ps_iattr) { 84235beab06STejun Heo ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; 84335beab06STejun Heo ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; 84435beab06STejun Heo } 84535beab06STejun Heo 84635beab06STejun Heo pos->u.removed_list = acxt->removed; 84735beab06STejun Heo acxt->removed = pos; 84835beab06STejun Heo } 84935beab06STejun Heo 85035beab06STejun Heo kernfs_put(pos); 85135beab06STejun Heo } while (pos != kn); 852fd7b9f7bSTejun Heo } 853fd7b9f7bSTejun Heo 854fd7b9f7bSTejun Heo /** 855324a56e1STejun Heo * kernfs_remove - remove a kernfs_node recursively 856324a56e1STejun Heo * @kn: the kernfs_node to remove 857fd7b9f7bSTejun Heo * 858324a56e1STejun Heo * Remove @kn along with all its subdirectories and files. 859fd7b9f7bSTejun Heo */ 860324a56e1STejun Heo void kernfs_remove(struct kernfs_node *kn) 861fd7b9f7bSTejun Heo { 8627653fe9dSGreg Kroah-Hartman struct kernfs_addrm_cxt acxt; 8637653fe9dSGreg Kroah-Hartman 8647653fe9dSGreg Kroah-Hartman kernfs_addrm_start(&acxt); 8657653fe9dSGreg Kroah-Hartman __kernfs_remove(&acxt, kn); 8667653fe9dSGreg Kroah-Hartman kernfs_addrm_finish(&acxt); 867fd7b9f7bSTejun Heo } 868fd7b9f7bSTejun Heo 869fd7b9f7bSTejun Heo /** 870324a56e1STejun Heo * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it 871324a56e1STejun Heo * @parent: parent of the target 872324a56e1STejun Heo * @name: name of the kernfs_node to remove 873324a56e1STejun Heo * @ns: namespace tag of the kernfs_node to remove 874fd7b9f7bSTejun Heo * 875324a56e1STejun Heo * Look for the kernfs_node with @name and @ns under @parent and remove it. 876324a56e1STejun Heo * Returns 0 on success, -ENOENT if such entry doesn't exist. 877fd7b9f7bSTejun Heo */ 878324a56e1STejun Heo int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, 879fd7b9f7bSTejun Heo const void *ns) 880fd7b9f7bSTejun Heo { 8817653fe9dSGreg Kroah-Hartman struct kernfs_addrm_cxt acxt; 882324a56e1STejun Heo struct kernfs_node *kn; 883fd7b9f7bSTejun Heo 884324a56e1STejun Heo if (!parent) { 885c637b8acSTejun Heo WARN(1, KERN_WARNING "kernfs: can not remove '%s', no directory\n", 886fd7b9f7bSTejun Heo name); 887fd7b9f7bSTejun Heo return -ENOENT; 888fd7b9f7bSTejun Heo } 889fd7b9f7bSTejun Heo 8907653fe9dSGreg Kroah-Hartman kernfs_addrm_start(&acxt); 891fd7b9f7bSTejun Heo 892324a56e1STejun Heo kn = kernfs_find_ns(parent, name, ns); 893324a56e1STejun Heo if (kn) 8947653fe9dSGreg Kroah-Hartman __kernfs_remove(&acxt, kn); 895fd7b9f7bSTejun Heo 8967653fe9dSGreg Kroah-Hartman kernfs_addrm_finish(&acxt); 897fd7b9f7bSTejun Heo 898324a56e1STejun Heo if (kn) 899fd7b9f7bSTejun Heo return 0; 900fd7b9f7bSTejun Heo else 901fd7b9f7bSTejun Heo return -ENOENT; 902fd7b9f7bSTejun Heo } 903fd7b9f7bSTejun Heo 904fd7b9f7bSTejun Heo /** 905fd7b9f7bSTejun Heo * kernfs_rename_ns - move and rename a kernfs_node 906324a56e1STejun Heo * @kn: target node 907fd7b9f7bSTejun Heo * @new_parent: new parent to put @sd under 908fd7b9f7bSTejun Heo * @new_name: new name 909fd7b9f7bSTejun Heo * @new_ns: new namespace tag 910fd7b9f7bSTejun Heo */ 911324a56e1STejun Heo int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, 912fd7b9f7bSTejun Heo const char *new_name, const void *new_ns) 913fd7b9f7bSTejun Heo { 914fd7b9f7bSTejun Heo int error; 915fd7b9f7bSTejun Heo 916ae34372eSTejun Heo mutex_lock(&kernfs_mutex); 917d0ae3d43STejun Heo 918798c75a0SGreg Kroah-Hartman error = -ENOENT; 919798c75a0SGreg Kroah-Hartman if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) 920798c75a0SGreg Kroah-Hartman goto out; 921798c75a0SGreg Kroah-Hartman 922fd7b9f7bSTejun Heo error = 0; 923adc5e8b5STejun Heo if ((kn->parent == new_parent) && (kn->ns == new_ns) && 924adc5e8b5STejun Heo (strcmp(kn->name, new_name) == 0)) 925798c75a0SGreg Kroah-Hartman goto out; /* nothing to rename */ 926fd7b9f7bSTejun Heo 927fd7b9f7bSTejun Heo error = -EEXIST; 928fd7b9f7bSTejun Heo if (kernfs_find_ns(new_parent, new_name, new_ns)) 929798c75a0SGreg Kroah-Hartman goto out; 930fd7b9f7bSTejun Heo 931324a56e1STejun Heo /* rename kernfs_node */ 932adc5e8b5STejun Heo if (strcmp(kn->name, new_name) != 0) { 933fd7b9f7bSTejun Heo error = -ENOMEM; 934fd7b9f7bSTejun Heo new_name = kstrdup(new_name, GFP_KERNEL); 935fd7b9f7bSTejun Heo if (!new_name) 936798c75a0SGreg Kroah-Hartman goto out; 937fd7b9f7bSTejun Heo 93847a52e91STejun Heo if (kn->flags & KERNFS_STATIC_NAME) 93947a52e91STejun Heo kn->flags &= ~KERNFS_STATIC_NAME; 94047a52e91STejun Heo else 941adc5e8b5STejun Heo kfree(kn->name); 94247a52e91STejun Heo 943adc5e8b5STejun Heo kn->name = new_name; 944fd7b9f7bSTejun Heo } 945fd7b9f7bSTejun Heo 946fd7b9f7bSTejun Heo /* 947fd7b9f7bSTejun Heo * Move to the appropriate place in the appropriate directories rbtree. 948fd7b9f7bSTejun Heo */ 949c637b8acSTejun Heo kernfs_unlink_sibling(kn); 950fd7b9f7bSTejun Heo kernfs_get(new_parent); 951adc5e8b5STejun Heo kernfs_put(kn->parent); 952adc5e8b5STejun Heo kn->ns = new_ns; 953c637b8acSTejun Heo kn->hash = kernfs_name_hash(kn->name, kn->ns); 954adc5e8b5STejun Heo kn->parent = new_parent; 955c637b8acSTejun Heo kernfs_link_sibling(kn); 956fd7b9f7bSTejun Heo 957fd7b9f7bSTejun Heo error = 0; 958ae34372eSTejun Heo out: 959798c75a0SGreg Kroah-Hartman mutex_unlock(&kernfs_mutex); 960fd7b9f7bSTejun Heo return error; 961fd7b9f7bSTejun Heo } 962fd7b9f7bSTejun Heo 963fd7b9f7bSTejun Heo /* Relationship between s_mode and the DT_xxx types */ 964324a56e1STejun Heo static inline unsigned char dt_type(struct kernfs_node *kn) 965fd7b9f7bSTejun Heo { 966adc5e8b5STejun Heo return (kn->mode >> 12) & 15; 967fd7b9f7bSTejun Heo } 968fd7b9f7bSTejun Heo 969c637b8acSTejun Heo static int kernfs_dir_fop_release(struct inode *inode, struct file *filp) 970fd7b9f7bSTejun Heo { 971fd7b9f7bSTejun Heo kernfs_put(filp->private_data); 972fd7b9f7bSTejun Heo return 0; 973fd7b9f7bSTejun Heo } 974fd7b9f7bSTejun Heo 975c637b8acSTejun Heo static struct kernfs_node *kernfs_dir_pos(const void *ns, 976324a56e1STejun Heo struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) 977fd7b9f7bSTejun Heo { 978fd7b9f7bSTejun Heo if (pos) { 979798c75a0SGreg Kroah-Hartman int valid = !(pos->flags & KERNFS_REMOVED) && 980798c75a0SGreg Kroah-Hartman pos->parent == parent && hash == pos->hash; 981fd7b9f7bSTejun Heo kernfs_put(pos); 982fd7b9f7bSTejun Heo if (!valid) 983fd7b9f7bSTejun Heo pos = NULL; 984fd7b9f7bSTejun Heo } 985fd7b9f7bSTejun Heo if (!pos && (hash > 1) && (hash < INT_MAX)) { 986adc5e8b5STejun Heo struct rb_node *node = parent->dir.children.rb_node; 987fd7b9f7bSTejun Heo while (node) { 988324a56e1STejun Heo pos = rb_to_kn(node); 989fd7b9f7bSTejun Heo 990adc5e8b5STejun Heo if (hash < pos->hash) 991fd7b9f7bSTejun Heo node = node->rb_left; 992adc5e8b5STejun Heo else if (hash > pos->hash) 993fd7b9f7bSTejun Heo node = node->rb_right; 994fd7b9f7bSTejun Heo else 995fd7b9f7bSTejun Heo break; 996fd7b9f7bSTejun Heo } 997fd7b9f7bSTejun Heo } 998fd7b9f7bSTejun Heo /* Skip over entries in the wrong namespace */ 999adc5e8b5STejun Heo while (pos && pos->ns != ns) { 1000adc5e8b5STejun Heo struct rb_node *node = rb_next(&pos->rb); 1001fd7b9f7bSTejun Heo if (!node) 1002fd7b9f7bSTejun Heo pos = NULL; 1003fd7b9f7bSTejun Heo else 1004324a56e1STejun Heo pos = rb_to_kn(node); 1005fd7b9f7bSTejun Heo } 1006fd7b9f7bSTejun Heo return pos; 1007fd7b9f7bSTejun Heo } 1008fd7b9f7bSTejun Heo 1009c637b8acSTejun Heo static struct kernfs_node *kernfs_dir_next_pos(const void *ns, 1010324a56e1STejun Heo struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos) 1011fd7b9f7bSTejun Heo { 1012c637b8acSTejun Heo pos = kernfs_dir_pos(ns, parent, ino, pos); 1013fd7b9f7bSTejun Heo if (pos) 1014fd7b9f7bSTejun Heo do { 1015adc5e8b5STejun Heo struct rb_node *node = rb_next(&pos->rb); 1016fd7b9f7bSTejun Heo if (!node) 1017fd7b9f7bSTejun Heo pos = NULL; 1018fd7b9f7bSTejun Heo else 1019324a56e1STejun Heo pos = rb_to_kn(node); 1020adc5e8b5STejun Heo } while (pos && pos->ns != ns); 1021fd7b9f7bSTejun Heo return pos; 1022fd7b9f7bSTejun Heo } 1023fd7b9f7bSTejun Heo 1024c637b8acSTejun Heo static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx) 1025fd7b9f7bSTejun Heo { 1026fd7b9f7bSTejun Heo struct dentry *dentry = file->f_path.dentry; 1027324a56e1STejun Heo struct kernfs_node *parent = dentry->d_fsdata; 1028324a56e1STejun Heo struct kernfs_node *pos = file->private_data; 1029fd7b9f7bSTejun Heo const void *ns = NULL; 1030fd7b9f7bSTejun Heo 1031fd7b9f7bSTejun Heo if (!dir_emit_dots(file, ctx)) 1032fd7b9f7bSTejun Heo return 0; 1033a797bfc3STejun Heo mutex_lock(&kernfs_mutex); 1034fd7b9f7bSTejun Heo 1035324a56e1STejun Heo if (kernfs_ns_enabled(parent)) 1036c525aaddSTejun Heo ns = kernfs_info(dentry->d_sb)->ns; 1037fd7b9f7bSTejun Heo 1038c637b8acSTejun Heo for (pos = kernfs_dir_pos(ns, parent, ctx->pos, pos); 1039fd7b9f7bSTejun Heo pos; 1040c637b8acSTejun Heo pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) { 1041adc5e8b5STejun Heo const char *name = pos->name; 1042fd7b9f7bSTejun Heo unsigned int type = dt_type(pos); 1043fd7b9f7bSTejun Heo int len = strlen(name); 1044adc5e8b5STejun Heo ino_t ino = pos->ino; 1045fd7b9f7bSTejun Heo 1046adc5e8b5STejun Heo ctx->pos = pos->hash; 1047fd7b9f7bSTejun Heo file->private_data = pos; 1048fd7b9f7bSTejun Heo kernfs_get(pos); 1049fd7b9f7bSTejun Heo 1050a797bfc3STejun Heo mutex_unlock(&kernfs_mutex); 1051fd7b9f7bSTejun Heo if (!dir_emit(ctx, name, len, ino, type)) 1052fd7b9f7bSTejun Heo return 0; 1053a797bfc3STejun Heo mutex_lock(&kernfs_mutex); 1054fd7b9f7bSTejun Heo } 1055a797bfc3STejun Heo mutex_unlock(&kernfs_mutex); 1056fd7b9f7bSTejun Heo file->private_data = NULL; 1057fd7b9f7bSTejun Heo ctx->pos = INT_MAX; 1058fd7b9f7bSTejun Heo return 0; 1059fd7b9f7bSTejun Heo } 1060fd7b9f7bSTejun Heo 1061c637b8acSTejun Heo static loff_t kernfs_dir_fop_llseek(struct file *file, loff_t offset, 1062c637b8acSTejun Heo int whence) 1063fd7b9f7bSTejun Heo { 1064fd7b9f7bSTejun Heo struct inode *inode = file_inode(file); 1065fd7b9f7bSTejun Heo loff_t ret; 1066fd7b9f7bSTejun Heo 1067fd7b9f7bSTejun Heo mutex_lock(&inode->i_mutex); 1068fd7b9f7bSTejun Heo ret = generic_file_llseek(file, offset, whence); 1069fd7b9f7bSTejun Heo mutex_unlock(&inode->i_mutex); 1070fd7b9f7bSTejun Heo 1071fd7b9f7bSTejun Heo return ret; 1072fd7b9f7bSTejun Heo } 1073fd7b9f7bSTejun Heo 1074a797bfc3STejun Heo const struct file_operations kernfs_dir_fops = { 1075fd7b9f7bSTejun Heo .read = generic_read_dir, 1076c637b8acSTejun Heo .iterate = kernfs_fop_readdir, 1077c637b8acSTejun Heo .release = kernfs_dir_fop_release, 1078c637b8acSTejun Heo .llseek = kernfs_dir_fop_llseek, 1079fd7b9f7bSTejun Heo }; 1080