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 22324a56e1STejun Heo #define rb_to_kn(X) rb_entry((X), struct kernfs_node, s_rb) 23fd7b9f7bSTejun Heo 24fd7b9f7bSTejun Heo /** 25fd7b9f7bSTejun Heo * sysfs_name_hash 26fd7b9f7bSTejun Heo * @name: Null terminated string to hash 27fd7b9f7bSTejun Heo * @ns: Namespace tag to hash 28fd7b9f7bSTejun Heo * 29fd7b9f7bSTejun Heo * Returns 31 bit hash of ns + name (so it fits in an off_t ) 30fd7b9f7bSTejun Heo */ 31fd7b9f7bSTejun Heo static unsigned int sysfs_name_hash(const char *name, const void *ns) 32fd7b9f7bSTejun Heo { 33fd7b9f7bSTejun Heo unsigned long hash = init_name_hash(); 34fd7b9f7bSTejun Heo unsigned int len = strlen(name); 35fd7b9f7bSTejun Heo while (len--) 36fd7b9f7bSTejun Heo hash = partial_name_hash(*name++, hash); 37fd7b9f7bSTejun Heo hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31)); 38fd7b9f7bSTejun Heo hash &= 0x7fffffffU; 39fd7b9f7bSTejun Heo /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ 40fd7b9f7bSTejun Heo if (hash < 1) 41fd7b9f7bSTejun Heo hash += 2; 42fd7b9f7bSTejun Heo if (hash >= INT_MAX) 43fd7b9f7bSTejun Heo hash = INT_MAX - 1; 44fd7b9f7bSTejun Heo return hash; 45fd7b9f7bSTejun Heo } 46fd7b9f7bSTejun Heo 47fd7b9f7bSTejun Heo static int sysfs_name_compare(unsigned int hash, const char *name, 48324a56e1STejun Heo const void *ns, const struct kernfs_node *kn) 49fd7b9f7bSTejun Heo { 50324a56e1STejun Heo if (hash != kn->s_hash) 51324a56e1STejun Heo return hash - kn->s_hash; 52324a56e1STejun Heo if (ns != kn->s_ns) 53324a56e1STejun Heo return ns - kn->s_ns; 54324a56e1STejun Heo return strcmp(name, kn->s_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 { 60fd7b9f7bSTejun Heo return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns, 61fd7b9f7bSTejun Heo right); 62fd7b9f7bSTejun Heo } 63fd7b9f7bSTejun Heo 64fd7b9f7bSTejun Heo /** 65324a56e1STejun Heo * sysfs_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 69324a56e1STejun Heo * @kn->s_parent->s_dir.children. 70fd7b9f7bSTejun Heo * 71fd7b9f7bSTejun Heo * Locking: 72fd7b9f7bSTejun Heo * mutex_lock(sysfs_mutex) 73fd7b9f7bSTejun Heo * 74fd7b9f7bSTejun Heo * RETURNS: 75fd7b9f7bSTejun Heo * 0 on susccess -EEXIST on failure. 76fd7b9f7bSTejun Heo */ 77324a56e1STejun Heo static int sysfs_link_sibling(struct kernfs_node *kn) 78fd7b9f7bSTejun Heo { 79324a56e1STejun Heo struct rb_node **node = &kn->s_parent->s_dir.children.rb_node; 80fd7b9f7bSTejun Heo struct rb_node *parent = NULL; 81fd7b9f7bSTejun Heo 82324a56e1STejun Heo if (sysfs_type(kn) == SYSFS_DIR) 83324a56e1STejun Heo kn->s_parent->s_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; 91324a56e1STejun Heo result = sysfs_sd_compare(kn, pos); 92fd7b9f7bSTejun Heo if (result < 0) 93fd7b9f7bSTejun Heo node = &pos->s_rb.rb_left; 94fd7b9f7bSTejun Heo else if (result > 0) 95fd7b9f7bSTejun Heo node = &pos->s_rb.rb_right; 96fd7b9f7bSTejun Heo else 97fd7b9f7bSTejun Heo return -EEXIST; 98fd7b9f7bSTejun Heo } 99fd7b9f7bSTejun Heo /* add new node and rebalance the tree */ 100324a56e1STejun Heo rb_link_node(&kn->s_rb, parent, node); 101324a56e1STejun Heo rb_insert_color(&kn->s_rb, &kn->s_parent->s_dir.children); 102fd7b9f7bSTejun Heo return 0; 103fd7b9f7bSTejun Heo } 104fd7b9f7bSTejun Heo 105fd7b9f7bSTejun Heo /** 106324a56e1STejun Heo * sysfs_unlink_sibling - unlink kernfs_node from sibling rbtree 107324a56e1STejun Heo * @kn: kernfs_node of interest 108fd7b9f7bSTejun Heo * 109324a56e1STejun Heo * Unlink @kn from its sibling rbtree which starts from 110324a56e1STejun Heo * kn->s_parent->s_dir.children. 111fd7b9f7bSTejun Heo * 112fd7b9f7bSTejun Heo * Locking: 113fd7b9f7bSTejun Heo * mutex_lock(sysfs_mutex) 114fd7b9f7bSTejun Heo */ 115324a56e1STejun Heo static void sysfs_unlink_sibling(struct kernfs_node *kn) 116fd7b9f7bSTejun Heo { 117324a56e1STejun Heo if (sysfs_type(kn) == SYSFS_DIR) 118324a56e1STejun Heo kn->s_parent->s_dir.subdirs--; 119fd7b9f7bSTejun Heo 120324a56e1STejun Heo rb_erase(&kn->s_rb, &kn->s_parent->s_dir.children); 121fd7b9f7bSTejun Heo } 122fd7b9f7bSTejun Heo 123fd7b9f7bSTejun Heo /** 124324a56e1STejun Heo * sysfs_get_active - get an active reference to kernfs_node 125324a56e1STejun Heo * @kn: kernfs_node to get an active reference to 126fd7b9f7bSTejun Heo * 127324a56e1STejun Heo * Get an active reference of @kn. This function is noop if @kn 128fd7b9f7bSTejun Heo * is NULL. 129fd7b9f7bSTejun Heo * 130fd7b9f7bSTejun Heo * RETURNS: 131324a56e1STejun Heo * Pointer to @kn on success, NULL on failure. 132fd7b9f7bSTejun Heo */ 133324a56e1STejun Heo struct kernfs_node *sysfs_get_active(struct kernfs_node *kn) 134fd7b9f7bSTejun Heo { 135324a56e1STejun Heo if (unlikely(!kn)) 136fd7b9f7bSTejun Heo return NULL; 137fd7b9f7bSTejun Heo 138324a56e1STejun Heo if (!atomic_inc_unless_negative(&kn->s_active)) 139fd7b9f7bSTejun Heo return NULL; 140fd7b9f7bSTejun Heo 141324a56e1STejun Heo if (kn->s_flags & SYSFS_FLAG_LOCKDEP) 142324a56e1STejun Heo rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); 143324a56e1STejun Heo return kn; 144fd7b9f7bSTejun Heo } 145fd7b9f7bSTejun Heo 146fd7b9f7bSTejun Heo /** 147324a56e1STejun Heo * sysfs_put_active - put an active reference to kernfs_node 148324a56e1STejun Heo * @kn: kernfs_node to put an active reference to 149fd7b9f7bSTejun Heo * 150324a56e1STejun Heo * Put an active reference to @kn. This function is noop if @kn 151fd7b9f7bSTejun Heo * is NULL. 152fd7b9f7bSTejun Heo */ 153324a56e1STejun Heo void sysfs_put_active(struct kernfs_node *kn) 154fd7b9f7bSTejun Heo { 155fd7b9f7bSTejun Heo int v; 156fd7b9f7bSTejun Heo 157324a56e1STejun Heo if (unlikely(!kn)) 158fd7b9f7bSTejun Heo return; 159fd7b9f7bSTejun Heo 160324a56e1STejun Heo if (kn->s_flags & SYSFS_FLAG_LOCKDEP) 161324a56e1STejun Heo rwsem_release(&kn->dep_map, 1, _RET_IP_); 162324a56e1STejun Heo v = atomic_dec_return(&kn->s_active); 163fd7b9f7bSTejun Heo if (likely(v != SD_DEACTIVATED_BIAS)) 164fd7b9f7bSTejun Heo return; 165fd7b9f7bSTejun Heo 166324a56e1STejun Heo /* 167324a56e1STejun Heo * atomic_dec_return() is a mb(), we'll always see the updated 168324a56e1STejun Heo * kn->u.completion. 169fd7b9f7bSTejun Heo */ 170324a56e1STejun Heo complete(kn->u.completion); 171fd7b9f7bSTejun Heo } 172fd7b9f7bSTejun Heo 173fd7b9f7bSTejun Heo /** 174324a56e1STejun Heo * sysfs_deactivate - deactivate kernfs_node 175324a56e1STejun Heo * @kn: kernfs_node to deactivate 176fd7b9f7bSTejun Heo * 177fd7b9f7bSTejun Heo * Deny new active references and drain existing ones. 178fd7b9f7bSTejun Heo */ 179324a56e1STejun Heo static void sysfs_deactivate(struct kernfs_node *kn) 180fd7b9f7bSTejun Heo { 181fd7b9f7bSTejun Heo DECLARE_COMPLETION_ONSTACK(wait); 182fd7b9f7bSTejun Heo int v; 183fd7b9f7bSTejun Heo 184324a56e1STejun Heo BUG_ON(!(kn->s_flags & SYSFS_FLAG_REMOVED)); 185fd7b9f7bSTejun Heo 186324a56e1STejun Heo if (!(sysfs_type(kn) & SYSFS_ACTIVE_REF)) 187fd7b9f7bSTejun Heo return; 188fd7b9f7bSTejun Heo 189324a56e1STejun Heo kn->u.completion = (void *)&wait; 190fd7b9f7bSTejun Heo 191324a56e1STejun Heo rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); 192fd7b9f7bSTejun Heo /* atomic_add_return() is a mb(), put_active() will always see 193324a56e1STejun Heo * the updated kn->u.completion. 194fd7b9f7bSTejun Heo */ 195324a56e1STejun Heo v = atomic_add_return(SD_DEACTIVATED_BIAS, &kn->s_active); 196fd7b9f7bSTejun Heo 197fd7b9f7bSTejun Heo if (v != SD_DEACTIVATED_BIAS) { 198324a56e1STejun Heo lock_contended(&kn->dep_map, _RET_IP_); 199fd7b9f7bSTejun Heo wait_for_completion(&wait); 200fd7b9f7bSTejun Heo } 201fd7b9f7bSTejun Heo 202324a56e1STejun Heo lock_acquired(&kn->dep_map, _RET_IP_); 203324a56e1STejun Heo rwsem_release(&kn->dep_map, 1, _RET_IP_); 204fd7b9f7bSTejun Heo } 205fd7b9f7bSTejun Heo 206fd7b9f7bSTejun Heo /** 207324a56e1STejun Heo * kernfs_get - get a reference count on a kernfs_node 208324a56e1STejun Heo * @kn: the target kernfs_node 209fd7b9f7bSTejun Heo */ 210324a56e1STejun Heo void kernfs_get(struct kernfs_node *kn) 211fd7b9f7bSTejun Heo { 212324a56e1STejun Heo if (kn) { 213324a56e1STejun Heo WARN_ON(!atomic_read(&kn->s_count)); 214324a56e1STejun Heo atomic_inc(&kn->s_count); 215fd7b9f7bSTejun Heo } 216fd7b9f7bSTejun Heo } 217fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_get); 218fd7b9f7bSTejun Heo 219fd7b9f7bSTejun Heo /** 220324a56e1STejun Heo * kernfs_put - put a reference count on a kernfs_node 221324a56e1STejun Heo * @kn: the target kernfs_node 222fd7b9f7bSTejun Heo * 223324a56e1STejun Heo * Put a reference count of @kn and destroy it if it reached zero. 224fd7b9f7bSTejun Heo */ 225324a56e1STejun Heo void kernfs_put(struct kernfs_node *kn) 226fd7b9f7bSTejun Heo { 227324a56e1STejun Heo struct kernfs_node *parent; 228ba7443bcSTejun Heo struct kernfs_root *root; 229fd7b9f7bSTejun Heo 230324a56e1STejun Heo if (!kn || !atomic_dec_and_test(&kn->s_count)) 231fd7b9f7bSTejun Heo return; 232324a56e1STejun Heo root = kernfs_root(kn); 233fd7b9f7bSTejun Heo repeat: 234fd7b9f7bSTejun Heo /* Moving/renaming is always done while holding reference. 235324a56e1STejun Heo * kn->s_parent won't change beneath us. 236fd7b9f7bSTejun Heo */ 237324a56e1STejun Heo parent = kn->s_parent; 238fd7b9f7bSTejun Heo 239324a56e1STejun Heo WARN(!(kn->s_flags & SYSFS_FLAG_REMOVED), 240fd7b9f7bSTejun Heo "sysfs: free using entry: %s/%s\n", 241324a56e1STejun Heo parent ? parent->s_name : "", kn->s_name); 242fd7b9f7bSTejun Heo 243324a56e1STejun Heo if (sysfs_type(kn) == SYSFS_KOBJ_LINK) 244324a56e1STejun Heo kernfs_put(kn->s_symlink.target_kn); 245324a56e1STejun Heo if (sysfs_type(kn) & SYSFS_COPY_NAME) 246324a56e1STejun Heo kfree(kn->s_name); 247324a56e1STejun Heo if (kn->s_iattr) { 248324a56e1STejun Heo if (kn->s_iattr->ia_secdata) 249324a56e1STejun Heo security_release_secctx(kn->s_iattr->ia_secdata, 250324a56e1STejun Heo kn->s_iattr->ia_secdata_len); 251324a56e1STejun Heo simple_xattrs_free(&kn->s_iattr->xattrs); 2522322392bSTejun Heo } 253324a56e1STejun Heo kfree(kn->s_iattr); 254324a56e1STejun Heo ida_simple_remove(&root->ino_ida, kn->s_ino); 255324a56e1STejun Heo kmem_cache_free(sysfs_dir_cachep, kn); 256fd7b9f7bSTejun Heo 257324a56e1STejun Heo kn = parent; 258324a56e1STejun Heo if (kn) { 259324a56e1STejun Heo if (atomic_dec_and_test(&kn->s_count)) 260fd7b9f7bSTejun Heo goto repeat; 261ba7443bcSTejun Heo } else { 262324a56e1STejun Heo /* just released the root kn, free @root too */ 263bc755553STejun Heo ida_destroy(&root->ino_ida); 264ba7443bcSTejun Heo kfree(root); 265ba7443bcSTejun Heo } 266fd7b9f7bSTejun Heo } 267fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_put); 268fd7b9f7bSTejun Heo 269fd7b9f7bSTejun Heo static int sysfs_dentry_delete(const struct dentry *dentry) 270fd7b9f7bSTejun Heo { 271324a56e1STejun Heo struct kernfs_node *kn = dentry->d_fsdata; 272324a56e1STejun Heo return !(kn && !(kn->s_flags & SYSFS_FLAG_REMOVED)); 273fd7b9f7bSTejun Heo } 274fd7b9f7bSTejun Heo 275fd7b9f7bSTejun Heo static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) 276fd7b9f7bSTejun Heo { 277324a56e1STejun Heo struct kernfs_node *kn; 278fd7b9f7bSTejun Heo 279fd7b9f7bSTejun Heo if (flags & LOOKUP_RCU) 280fd7b9f7bSTejun Heo return -ECHILD; 281fd7b9f7bSTejun Heo 282324a56e1STejun Heo kn = dentry->d_fsdata; 283fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 284fd7b9f7bSTejun Heo 285fd7b9f7bSTejun Heo /* The sysfs dirent has been deleted */ 286324a56e1STejun Heo if (kn->s_flags & SYSFS_FLAG_REMOVED) 287fd7b9f7bSTejun Heo goto out_bad; 288fd7b9f7bSTejun Heo 289fd7b9f7bSTejun Heo /* The sysfs dirent has been moved? */ 290324a56e1STejun Heo if (dentry->d_parent->d_fsdata != kn->s_parent) 291fd7b9f7bSTejun Heo goto out_bad; 292fd7b9f7bSTejun Heo 293fd7b9f7bSTejun Heo /* The sysfs dirent has been renamed */ 294324a56e1STejun Heo if (strcmp(dentry->d_name.name, kn->s_name) != 0) 295fd7b9f7bSTejun Heo goto out_bad; 296fd7b9f7bSTejun Heo 297fd7b9f7bSTejun Heo /* The sysfs dirent has been moved to a different namespace */ 298324a56e1STejun Heo if (kn->s_parent && kernfs_ns_enabled(kn->s_parent) && 299324a56e1STejun Heo sysfs_info(dentry->d_sb)->ns != kn->s_ns) 300fd7b9f7bSTejun Heo goto out_bad; 301fd7b9f7bSTejun Heo 302fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 303fd7b9f7bSTejun Heo out_valid: 304fd7b9f7bSTejun Heo return 1; 305fd7b9f7bSTejun Heo out_bad: 306fd7b9f7bSTejun Heo /* Remove the dentry from the dcache hashes. 307fd7b9f7bSTejun Heo * If this is a deleted dentry we use d_drop instead of d_delete 308fd7b9f7bSTejun Heo * so sysfs doesn't need to cope with negative dentries. 309fd7b9f7bSTejun Heo * 310fd7b9f7bSTejun Heo * If this is a dentry that has simply been renamed we 311fd7b9f7bSTejun Heo * use d_drop to remove it from the dcache lookup on its 312fd7b9f7bSTejun Heo * old parent. If this dentry persists later when a lookup 313fd7b9f7bSTejun Heo * is performed at its new name the dentry will be readded 314fd7b9f7bSTejun Heo * to the dcache hashes. 315fd7b9f7bSTejun Heo */ 316fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 317fd7b9f7bSTejun Heo 318fd7b9f7bSTejun Heo /* If we have submounts we must allow the vfs caches 319fd7b9f7bSTejun Heo * to lie about the state of the filesystem to prevent 320fd7b9f7bSTejun Heo * leaks and other nasty things. 321fd7b9f7bSTejun Heo */ 322fd7b9f7bSTejun Heo if (check_submounts_and_drop(dentry) != 0) 323fd7b9f7bSTejun Heo goto out_valid; 324fd7b9f7bSTejun Heo 325fd7b9f7bSTejun Heo return 0; 326fd7b9f7bSTejun Heo } 327fd7b9f7bSTejun Heo 328fd7b9f7bSTejun Heo static void sysfs_dentry_release(struct dentry *dentry) 329fd7b9f7bSTejun Heo { 330fd7b9f7bSTejun Heo kernfs_put(dentry->d_fsdata); 331fd7b9f7bSTejun Heo } 332fd7b9f7bSTejun Heo 333fd7b9f7bSTejun Heo const struct dentry_operations sysfs_dentry_ops = { 334fd7b9f7bSTejun Heo .d_revalidate = sysfs_dentry_revalidate, 335fd7b9f7bSTejun Heo .d_delete = sysfs_dentry_delete, 336fd7b9f7bSTejun Heo .d_release = sysfs_dentry_release, 337fd7b9f7bSTejun Heo }; 338fd7b9f7bSTejun Heo 339324a56e1STejun Heo struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, 340bc755553STejun Heo const char *name, umode_t mode, int type) 341fd7b9f7bSTejun Heo { 342fd7b9f7bSTejun Heo char *dup_name = NULL; 343324a56e1STejun Heo struct kernfs_node *kn; 344bc755553STejun Heo int ret; 345fd7b9f7bSTejun Heo 346fd7b9f7bSTejun Heo if (type & SYSFS_COPY_NAME) { 347fd7b9f7bSTejun Heo name = dup_name = kstrdup(name, GFP_KERNEL); 348fd7b9f7bSTejun Heo if (!name) 349fd7b9f7bSTejun Heo return NULL; 350fd7b9f7bSTejun Heo } 351fd7b9f7bSTejun Heo 352324a56e1STejun Heo kn = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); 353324a56e1STejun Heo if (!kn) 354fd7b9f7bSTejun Heo goto err_out1; 355fd7b9f7bSTejun Heo 356bc755553STejun Heo ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL); 357bc755553STejun Heo if (ret < 0) 358fd7b9f7bSTejun Heo goto err_out2; 359324a56e1STejun Heo kn->s_ino = ret; 360fd7b9f7bSTejun Heo 361324a56e1STejun Heo atomic_set(&kn->s_count, 1); 362324a56e1STejun Heo atomic_set(&kn->s_active, 0); 363fd7b9f7bSTejun Heo 364324a56e1STejun Heo kn->s_name = name; 365324a56e1STejun Heo kn->s_mode = mode; 366324a56e1STejun Heo kn->s_flags = type | SYSFS_FLAG_REMOVED; 367fd7b9f7bSTejun Heo 368324a56e1STejun Heo return kn; 369fd7b9f7bSTejun Heo 370fd7b9f7bSTejun Heo err_out2: 371324a56e1STejun Heo kmem_cache_free(sysfs_dir_cachep, kn); 372fd7b9f7bSTejun Heo err_out1: 373fd7b9f7bSTejun Heo kfree(dup_name); 374fd7b9f7bSTejun Heo return NULL; 375fd7b9f7bSTejun Heo } 376fd7b9f7bSTejun Heo 377fd7b9f7bSTejun Heo /** 378324a56e1STejun Heo * sysfs_addrm_start - prepare for kernfs_node add/remove 379fd7b9f7bSTejun Heo * @acxt: pointer to sysfs_addrm_cxt to be used 380fd7b9f7bSTejun Heo * 381fd7b9f7bSTejun Heo * This function is called when the caller is about to add or remove 382324a56e1STejun Heo * kernfs_node. This function acquires sysfs_mutex. @acxt is used to 383324a56e1STejun Heo * keep and pass context to other addrm functions. 384fd7b9f7bSTejun Heo * 385fd7b9f7bSTejun Heo * LOCKING: 386fd7b9f7bSTejun Heo * Kernel thread context (may sleep). sysfs_mutex is locked on 387fd7b9f7bSTejun Heo * return. 388fd7b9f7bSTejun Heo */ 389fd7b9f7bSTejun Heo void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) 390fd7b9f7bSTejun Heo __acquires(sysfs_mutex) 391fd7b9f7bSTejun Heo { 392fd7b9f7bSTejun Heo memset(acxt, 0, sizeof(*acxt)); 393fd7b9f7bSTejun Heo 394fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 395fd7b9f7bSTejun Heo } 396fd7b9f7bSTejun Heo 397fd7b9f7bSTejun Heo /** 398324a56e1STejun Heo * sysfs_add_one - add kernfs_node to parent without warning 399fd7b9f7bSTejun Heo * @acxt: addrm context to use 400324a56e1STejun Heo * @kn: kernfs_node to be added 401324a56e1STejun Heo * @parent: the parent kernfs_node to add @kn to 402fd7b9f7bSTejun Heo * 403324a56e1STejun Heo * Get @parent and set @kn->s_parent to it and increment nlink of 404324a56e1STejun Heo * the parent inode if @kn is a directory and link into the children 405fd7b9f7bSTejun Heo * list of the parent. 406fd7b9f7bSTejun Heo * 407fd7b9f7bSTejun Heo * This function should be called between calls to 408fd7b9f7bSTejun Heo * sysfs_addrm_start() and sysfs_addrm_finish() and should be 409fd7b9f7bSTejun Heo * passed the same @acxt as passed to sysfs_addrm_start(). 410fd7b9f7bSTejun Heo * 411fd7b9f7bSTejun Heo * LOCKING: 412fd7b9f7bSTejun Heo * Determined by sysfs_addrm_start(). 413fd7b9f7bSTejun Heo * 414fd7b9f7bSTejun Heo * RETURNS: 415fd7b9f7bSTejun Heo * 0 on success, -EEXIST if entry with the given name already 416fd7b9f7bSTejun Heo * exists. 417fd7b9f7bSTejun Heo */ 418324a56e1STejun Heo int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, 419324a56e1STejun Heo struct kernfs_node *parent) 420fd7b9f7bSTejun Heo { 421324a56e1STejun Heo bool has_ns = kernfs_ns_enabled(parent); 422fd7b9f7bSTejun Heo struct sysfs_inode_attrs *ps_iattr; 423fd7b9f7bSTejun Heo int ret; 424fd7b9f7bSTejun Heo 425324a56e1STejun Heo if (has_ns != (bool)kn->s_ns) { 426fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 427fd7b9f7bSTejun Heo has_ns ? "required" : "invalid", 428324a56e1STejun Heo parent->s_name, kn->s_name); 429fd7b9f7bSTejun Heo return -EINVAL; 430fd7b9f7bSTejun Heo } 431fd7b9f7bSTejun Heo 432324a56e1STejun Heo if (sysfs_type(parent) != SYSFS_DIR) 433fd7b9f7bSTejun Heo return -EINVAL; 434fd7b9f7bSTejun Heo 435324a56e1STejun Heo kn->s_hash = sysfs_name_hash(kn->s_name, kn->s_ns); 436324a56e1STejun Heo kn->s_parent = parent; 437324a56e1STejun Heo kernfs_get(parent); 438fd7b9f7bSTejun Heo 439324a56e1STejun Heo ret = sysfs_link_sibling(kn); 440fd7b9f7bSTejun Heo if (ret) 441fd7b9f7bSTejun Heo return ret; 442fd7b9f7bSTejun Heo 443fd7b9f7bSTejun Heo /* Update timestamps on the parent */ 444324a56e1STejun Heo ps_iattr = parent->s_iattr; 445fd7b9f7bSTejun Heo if (ps_iattr) { 446fd7b9f7bSTejun Heo struct iattr *ps_iattrs = &ps_iattr->ia_iattr; 447fd7b9f7bSTejun Heo ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; 448fd7b9f7bSTejun Heo } 449fd7b9f7bSTejun Heo 450fd7b9f7bSTejun Heo /* Mark the entry added into directory tree */ 451324a56e1STejun Heo kn->s_flags &= ~SYSFS_FLAG_REMOVED; 452fd7b9f7bSTejun Heo 453fd7b9f7bSTejun Heo return 0; 454fd7b9f7bSTejun Heo } 455fd7b9f7bSTejun Heo 456fd7b9f7bSTejun Heo /** 457324a56e1STejun Heo * sysfs_remove_one - remove kernfs_node from parent 458fd7b9f7bSTejun Heo * @acxt: addrm context to use 459324a56e1STejun Heo * @kn: kernfs_node to be removed 460fd7b9f7bSTejun Heo * 461324a56e1STejun Heo * Mark @kn removed and drop nlink of parent inode if @kn is a 462324a56e1STejun Heo * directory. @kn is unlinked from the children list. 463fd7b9f7bSTejun Heo * 464fd7b9f7bSTejun Heo * This function should be called between calls to 465fd7b9f7bSTejun Heo * sysfs_addrm_start() and sysfs_addrm_finish() and should be 466fd7b9f7bSTejun Heo * passed the same @acxt as passed to sysfs_addrm_start(). 467fd7b9f7bSTejun Heo * 468fd7b9f7bSTejun Heo * LOCKING: 469fd7b9f7bSTejun Heo * Determined by sysfs_addrm_start(). 470fd7b9f7bSTejun Heo */ 471fd7b9f7bSTejun Heo static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, 472324a56e1STejun Heo struct kernfs_node *kn) 473fd7b9f7bSTejun Heo { 474fd7b9f7bSTejun Heo struct sysfs_inode_attrs *ps_iattr; 475fd7b9f7bSTejun Heo 476fd7b9f7bSTejun Heo /* 477fd7b9f7bSTejun Heo * Removal can be called multiple times on the same node. Only the 478fd7b9f7bSTejun Heo * first invocation is effective and puts the base ref. 479fd7b9f7bSTejun Heo */ 480324a56e1STejun Heo if (kn->s_flags & SYSFS_FLAG_REMOVED) 481fd7b9f7bSTejun Heo return; 482fd7b9f7bSTejun Heo 483324a56e1STejun Heo if (kn->s_parent) { 484324a56e1STejun Heo sysfs_unlink_sibling(kn); 485fd7b9f7bSTejun Heo 486fd7b9f7bSTejun Heo /* Update timestamps on the parent */ 487324a56e1STejun Heo ps_iattr = kn->s_parent->s_iattr; 488fd7b9f7bSTejun Heo if (ps_iattr) { 489ba7443bcSTejun Heo ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; 490ba7443bcSTejun Heo ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; 491ba7443bcSTejun Heo } 492fd7b9f7bSTejun Heo } 493fd7b9f7bSTejun Heo 494324a56e1STejun Heo kn->s_flags |= SYSFS_FLAG_REMOVED; 495324a56e1STejun Heo kn->u.removed_list = acxt->removed; 496324a56e1STejun Heo acxt->removed = kn; 497fd7b9f7bSTejun Heo } 498fd7b9f7bSTejun Heo 499fd7b9f7bSTejun Heo /** 500324a56e1STejun Heo * sysfs_addrm_finish - finish up kernfs_node add/remove 501fd7b9f7bSTejun Heo * @acxt: addrm context to finish up 502fd7b9f7bSTejun Heo * 503324a56e1STejun Heo * Finish up kernfs_node add/remove. Resources acquired by 504324a56e1STejun Heo * sysfs_addrm_start() are released and removed kernfs_nodes are 505fd7b9f7bSTejun Heo * cleaned up. 506fd7b9f7bSTejun Heo * 507fd7b9f7bSTejun Heo * LOCKING: 508fd7b9f7bSTejun Heo * sysfs_mutex is released. 509fd7b9f7bSTejun Heo */ 510fd7b9f7bSTejun Heo void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) 511fd7b9f7bSTejun Heo __releases(sysfs_mutex) 512fd7b9f7bSTejun Heo { 513fd7b9f7bSTejun Heo /* release resources acquired by sysfs_addrm_start() */ 514fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 515fd7b9f7bSTejun Heo 516324a56e1STejun Heo /* kill removed kernfs_nodes */ 517fd7b9f7bSTejun Heo while (acxt->removed) { 518324a56e1STejun Heo struct kernfs_node *kn = acxt->removed; 519fd7b9f7bSTejun Heo 520324a56e1STejun Heo acxt->removed = kn->u.removed_list; 521fd7b9f7bSTejun Heo 522324a56e1STejun Heo sysfs_deactivate(kn); 523324a56e1STejun Heo sysfs_unmap_bin_file(kn); 524324a56e1STejun Heo kernfs_put(kn); 525fd7b9f7bSTejun Heo } 526fd7b9f7bSTejun Heo } 527fd7b9f7bSTejun Heo 528fd7b9f7bSTejun Heo /** 529324a56e1STejun Heo * kernfs_find_ns - find kernfs_node with the given name 530324a56e1STejun Heo * @parent: kernfs_node to search under 531fd7b9f7bSTejun Heo * @name: name to look for 532fd7b9f7bSTejun Heo * @ns: the namespace tag to use 533fd7b9f7bSTejun Heo * 534324a56e1STejun Heo * Look for kernfs_node with name @name under @parent. Returns pointer to 535324a56e1STejun Heo * the found kernfs_node on success, %NULL on failure. 536fd7b9f7bSTejun Heo */ 537324a56e1STejun Heo static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, 538fd7b9f7bSTejun Heo const unsigned char *name, 539fd7b9f7bSTejun Heo const void *ns) 540fd7b9f7bSTejun Heo { 541fd7b9f7bSTejun Heo struct rb_node *node = parent->s_dir.children.rb_node; 542ac9bba03STejun Heo bool has_ns = kernfs_ns_enabled(parent); 543fd7b9f7bSTejun Heo unsigned int hash; 544fd7b9f7bSTejun Heo 545fd7b9f7bSTejun Heo lockdep_assert_held(&sysfs_mutex); 546fd7b9f7bSTejun Heo 547fd7b9f7bSTejun Heo if (has_ns != (bool)ns) { 548fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 549fd7b9f7bSTejun Heo has_ns ? "required" : "invalid", 550fd7b9f7bSTejun Heo parent->s_name, name); 551fd7b9f7bSTejun Heo return NULL; 552fd7b9f7bSTejun Heo } 553fd7b9f7bSTejun Heo 554fd7b9f7bSTejun Heo hash = sysfs_name_hash(name, ns); 555fd7b9f7bSTejun Heo while (node) { 556324a56e1STejun Heo struct kernfs_node *kn; 557fd7b9f7bSTejun Heo int result; 558fd7b9f7bSTejun Heo 559324a56e1STejun Heo kn = rb_to_kn(node); 560324a56e1STejun Heo result = sysfs_name_compare(hash, name, ns, kn); 561fd7b9f7bSTejun Heo if (result < 0) 562fd7b9f7bSTejun Heo node = node->rb_left; 563fd7b9f7bSTejun Heo else if (result > 0) 564fd7b9f7bSTejun Heo node = node->rb_right; 565fd7b9f7bSTejun Heo else 566324a56e1STejun Heo return kn; 567fd7b9f7bSTejun Heo } 568fd7b9f7bSTejun Heo return NULL; 569fd7b9f7bSTejun Heo } 570fd7b9f7bSTejun Heo 571fd7b9f7bSTejun Heo /** 572324a56e1STejun Heo * kernfs_find_and_get_ns - find and get kernfs_node with the given name 573324a56e1STejun Heo * @parent: kernfs_node to search under 574fd7b9f7bSTejun Heo * @name: name to look for 575fd7b9f7bSTejun Heo * @ns: the namespace tag to use 576fd7b9f7bSTejun Heo * 577324a56e1STejun Heo * Look for kernfs_node with name @name under @parent and get a reference 578fd7b9f7bSTejun Heo * if found. This function may sleep and returns pointer to the found 579324a56e1STejun Heo * kernfs_node on success, %NULL on failure. 580fd7b9f7bSTejun Heo */ 581324a56e1STejun Heo struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, 582fd7b9f7bSTejun Heo const char *name, const void *ns) 583fd7b9f7bSTejun Heo { 584324a56e1STejun Heo struct kernfs_node *kn; 585fd7b9f7bSTejun Heo 586fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 587324a56e1STejun Heo kn = kernfs_find_ns(parent, name, ns); 588324a56e1STejun Heo kernfs_get(kn); 589fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 590fd7b9f7bSTejun Heo 591324a56e1STejun Heo return kn; 592fd7b9f7bSTejun Heo } 593fd7b9f7bSTejun Heo EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); 594fd7b9f7bSTejun Heo 595fd7b9f7bSTejun Heo /** 596ba7443bcSTejun Heo * kernfs_create_root - create a new kernfs hierarchy 597ba7443bcSTejun Heo * @priv: opaque data associated with the new directory 598ba7443bcSTejun Heo * 599ba7443bcSTejun Heo * Returns the root of the new hierarchy on success, ERR_PTR() value on 600ba7443bcSTejun Heo * failure. 601ba7443bcSTejun Heo */ 602ba7443bcSTejun Heo struct kernfs_root *kernfs_create_root(void *priv) 603ba7443bcSTejun Heo { 604ba7443bcSTejun Heo struct kernfs_root *root; 605324a56e1STejun Heo struct kernfs_node *kn; 606ba7443bcSTejun Heo 607ba7443bcSTejun Heo root = kzalloc(sizeof(*root), GFP_KERNEL); 608ba7443bcSTejun Heo if (!root) 609ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 610ba7443bcSTejun Heo 611bc755553STejun Heo ida_init(&root->ino_ida); 612bc755553STejun Heo 613324a56e1STejun Heo kn = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); 614324a56e1STejun Heo if (!kn) { 615bc755553STejun Heo ida_destroy(&root->ino_ida); 616ba7443bcSTejun Heo kfree(root); 617ba7443bcSTejun Heo return ERR_PTR(-ENOMEM); 618ba7443bcSTejun Heo } 619ba7443bcSTejun Heo 620324a56e1STejun Heo kn->s_flags &= ~SYSFS_FLAG_REMOVED; 621324a56e1STejun Heo kn->priv = priv; 622324a56e1STejun Heo kn->s_dir.root = root; 623ba7443bcSTejun Heo 624324a56e1STejun Heo root->kn = kn; 625ba7443bcSTejun Heo 626ba7443bcSTejun Heo return root; 627ba7443bcSTejun Heo } 628ba7443bcSTejun Heo 629ba7443bcSTejun Heo /** 630ba7443bcSTejun Heo * kernfs_destroy_root - destroy a kernfs hierarchy 631ba7443bcSTejun Heo * @root: root of the hierarchy to destroy 632ba7443bcSTejun Heo * 633ba7443bcSTejun Heo * Destroy the hierarchy anchored at @root by removing all existing 634ba7443bcSTejun Heo * directories and destroying @root. 635ba7443bcSTejun Heo */ 636ba7443bcSTejun Heo void kernfs_destroy_root(struct kernfs_root *root) 637ba7443bcSTejun Heo { 638324a56e1STejun Heo kernfs_remove(root->kn); /* will also free @root */ 639ba7443bcSTejun Heo } 640ba7443bcSTejun Heo 641ba7443bcSTejun Heo /** 642fd7b9f7bSTejun Heo * kernfs_create_dir_ns - create a directory 643fd7b9f7bSTejun Heo * @parent: parent in which to create a new directory 644fd7b9f7bSTejun Heo * @name: name of the new directory 645fd7b9f7bSTejun Heo * @priv: opaque data associated with the new directory 646fd7b9f7bSTejun Heo * @ns: optional namespace tag of the directory 647fd7b9f7bSTejun Heo * 648fd7b9f7bSTejun Heo * Returns the created node on success, ERR_PTR() value on failure. 649fd7b9f7bSTejun Heo */ 650324a56e1STejun Heo struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, 651fd7b9f7bSTejun Heo const char *name, void *priv, 652fd7b9f7bSTejun Heo const void *ns) 653fd7b9f7bSTejun Heo { 654fd7b9f7bSTejun Heo umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; 655fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 656324a56e1STejun Heo struct kernfs_node *kn; 657fd7b9f7bSTejun Heo int rc; 658fd7b9f7bSTejun Heo 659fd7b9f7bSTejun Heo /* allocate */ 660324a56e1STejun Heo kn = sysfs_new_dirent(kernfs_root(parent), name, mode, SYSFS_DIR); 661324a56e1STejun Heo if (!kn) 662fd7b9f7bSTejun Heo return ERR_PTR(-ENOMEM); 663fd7b9f7bSTejun Heo 664324a56e1STejun Heo kn->s_dir.root = parent->s_dir.root; 665324a56e1STejun Heo kn->s_ns = ns; 666324a56e1STejun Heo kn->priv = priv; 667fd7b9f7bSTejun Heo 668fd7b9f7bSTejun Heo /* link in */ 669fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 670324a56e1STejun Heo rc = sysfs_add_one(&acxt, kn, parent); 671fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 672fd7b9f7bSTejun Heo 673fd7b9f7bSTejun Heo if (!rc) 674324a56e1STejun Heo return kn; 675fd7b9f7bSTejun Heo 676324a56e1STejun Heo kernfs_put(kn); 677fd7b9f7bSTejun Heo return ERR_PTR(rc); 678fd7b9f7bSTejun Heo } 679fd7b9f7bSTejun Heo 680fd7b9f7bSTejun Heo static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, 681fd7b9f7bSTejun Heo unsigned int flags) 682fd7b9f7bSTejun Heo { 683fd7b9f7bSTejun Heo struct dentry *ret = NULL; 684324a56e1STejun Heo struct kernfs_node *parent = dentry->d_parent->d_fsdata; 685324a56e1STejun Heo struct kernfs_node *kn; 686fd7b9f7bSTejun Heo struct inode *inode; 687fd7b9f7bSTejun Heo const void *ns = NULL; 688fd7b9f7bSTejun Heo 689fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 690fd7b9f7bSTejun Heo 691324a56e1STejun Heo if (kernfs_ns_enabled(parent)) 692fd7b9f7bSTejun Heo ns = sysfs_info(dir->i_sb)->ns; 693fd7b9f7bSTejun Heo 694324a56e1STejun Heo kn = kernfs_find_ns(parent, dentry->d_name.name, ns); 695fd7b9f7bSTejun Heo 696fd7b9f7bSTejun Heo /* no such entry */ 697324a56e1STejun Heo if (!kn) { 698fd7b9f7bSTejun Heo ret = ERR_PTR(-ENOENT); 699fd7b9f7bSTejun Heo goto out_unlock; 700fd7b9f7bSTejun Heo } 701324a56e1STejun Heo kernfs_get(kn); 702324a56e1STejun Heo dentry->d_fsdata = kn; 703fd7b9f7bSTejun Heo 704fd7b9f7bSTejun Heo /* attach dentry and inode */ 705324a56e1STejun Heo inode = sysfs_get_inode(dir->i_sb, kn); 706fd7b9f7bSTejun Heo if (!inode) { 707fd7b9f7bSTejun Heo ret = ERR_PTR(-ENOMEM); 708fd7b9f7bSTejun Heo goto out_unlock; 709fd7b9f7bSTejun Heo } 710fd7b9f7bSTejun Heo 711fd7b9f7bSTejun Heo /* instantiate and hash dentry */ 712fd7b9f7bSTejun Heo ret = d_materialise_unique(dentry, inode); 713fd7b9f7bSTejun Heo out_unlock: 714fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 715fd7b9f7bSTejun Heo return ret; 716fd7b9f7bSTejun Heo } 717fd7b9f7bSTejun Heo 718fd7b9f7bSTejun Heo const struct inode_operations sysfs_dir_inode_operations = { 719fd7b9f7bSTejun Heo .lookup = sysfs_lookup, 720fd7b9f7bSTejun Heo .permission = sysfs_permission, 721fd7b9f7bSTejun Heo .setattr = sysfs_setattr, 722fd7b9f7bSTejun Heo .getattr = sysfs_getattr, 723fd7b9f7bSTejun Heo .setxattr = sysfs_setxattr, 7242322392bSTejun Heo .removexattr = sysfs_removexattr, 7252322392bSTejun Heo .getxattr = sysfs_getxattr, 7262322392bSTejun Heo .listxattr = sysfs_listxattr, 727fd7b9f7bSTejun Heo }; 728fd7b9f7bSTejun Heo 729324a56e1STejun Heo static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) 730fd7b9f7bSTejun Heo { 731324a56e1STejun Heo struct kernfs_node *last; 732fd7b9f7bSTejun Heo 733fd7b9f7bSTejun Heo while (true) { 734fd7b9f7bSTejun Heo struct rb_node *rbn; 735fd7b9f7bSTejun Heo 736fd7b9f7bSTejun Heo last = pos; 737fd7b9f7bSTejun Heo 738fd7b9f7bSTejun Heo if (sysfs_type(pos) != SYSFS_DIR) 739fd7b9f7bSTejun Heo break; 740fd7b9f7bSTejun Heo 741fd7b9f7bSTejun Heo rbn = rb_first(&pos->s_dir.children); 742fd7b9f7bSTejun Heo if (!rbn) 743fd7b9f7bSTejun Heo break; 744fd7b9f7bSTejun Heo 745324a56e1STejun Heo pos = rb_to_kn(rbn); 746fd7b9f7bSTejun Heo } 747fd7b9f7bSTejun Heo 748fd7b9f7bSTejun Heo return last; 749fd7b9f7bSTejun Heo } 750fd7b9f7bSTejun Heo 751fd7b9f7bSTejun Heo /** 752fd7b9f7bSTejun Heo * sysfs_next_descendant_post - find the next descendant for post-order walk 753fd7b9f7bSTejun Heo * @pos: the current position (%NULL to initiate traversal) 754324a56e1STejun Heo * @root: kernfs_node whose descendants to walk 755fd7b9f7bSTejun Heo * 756fd7b9f7bSTejun Heo * Find the next descendant to visit for post-order traversal of @root's 757fd7b9f7bSTejun Heo * descendants. @root is included in the iteration and the last node to be 758fd7b9f7bSTejun Heo * visited. 759fd7b9f7bSTejun Heo */ 760324a56e1STejun Heo static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, 761324a56e1STejun Heo struct kernfs_node *root) 762fd7b9f7bSTejun Heo { 763fd7b9f7bSTejun Heo struct rb_node *rbn; 764fd7b9f7bSTejun Heo 765fd7b9f7bSTejun Heo lockdep_assert_held(&sysfs_mutex); 766fd7b9f7bSTejun Heo 767fd7b9f7bSTejun Heo /* if first iteration, visit leftmost descendant which may be root */ 768fd7b9f7bSTejun Heo if (!pos) 769fd7b9f7bSTejun Heo return sysfs_leftmost_descendant(root); 770fd7b9f7bSTejun Heo 771fd7b9f7bSTejun Heo /* if we visited @root, we're done */ 772fd7b9f7bSTejun Heo if (pos == root) 773fd7b9f7bSTejun Heo return NULL; 774fd7b9f7bSTejun Heo 775fd7b9f7bSTejun Heo /* if there's an unvisited sibling, visit its leftmost descendant */ 776fd7b9f7bSTejun Heo rbn = rb_next(&pos->s_rb); 777fd7b9f7bSTejun Heo if (rbn) 778324a56e1STejun Heo return sysfs_leftmost_descendant(rb_to_kn(rbn)); 779fd7b9f7bSTejun Heo 780fd7b9f7bSTejun Heo /* no sibling left, visit parent */ 781fd7b9f7bSTejun Heo return pos->s_parent; 782fd7b9f7bSTejun Heo } 783fd7b9f7bSTejun Heo 784fd7b9f7bSTejun Heo static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, 785324a56e1STejun Heo struct kernfs_node *kn) 786fd7b9f7bSTejun Heo { 787324a56e1STejun Heo struct kernfs_node *pos, *next; 788fd7b9f7bSTejun Heo 789324a56e1STejun Heo if (!kn) 790fd7b9f7bSTejun Heo return; 791fd7b9f7bSTejun Heo 792324a56e1STejun Heo pr_debug("sysfs %s: removing\n", kn->s_name); 793fd7b9f7bSTejun Heo 794fd7b9f7bSTejun Heo next = NULL; 795fd7b9f7bSTejun Heo do { 796fd7b9f7bSTejun Heo pos = next; 797324a56e1STejun Heo next = sysfs_next_descendant_post(pos, kn); 798fd7b9f7bSTejun Heo if (pos) 799fd7b9f7bSTejun Heo sysfs_remove_one(acxt, pos); 800fd7b9f7bSTejun Heo } while (next); 801fd7b9f7bSTejun Heo } 802fd7b9f7bSTejun Heo 803fd7b9f7bSTejun Heo /** 804324a56e1STejun Heo * kernfs_remove - remove a kernfs_node recursively 805324a56e1STejun Heo * @kn: the kernfs_node to remove 806fd7b9f7bSTejun Heo * 807324a56e1STejun Heo * Remove @kn along with all its subdirectories and files. 808fd7b9f7bSTejun Heo */ 809324a56e1STejun Heo void kernfs_remove(struct kernfs_node *kn) 810fd7b9f7bSTejun Heo { 811fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 812fd7b9f7bSTejun Heo 813fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 814324a56e1STejun Heo __kernfs_remove(&acxt, kn); 815fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 816fd7b9f7bSTejun Heo } 817fd7b9f7bSTejun Heo 818fd7b9f7bSTejun Heo /** 819324a56e1STejun Heo * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it 820324a56e1STejun Heo * @parent: parent of the target 821324a56e1STejun Heo * @name: name of the kernfs_node to remove 822324a56e1STejun Heo * @ns: namespace tag of the kernfs_node to remove 823fd7b9f7bSTejun Heo * 824324a56e1STejun Heo * Look for the kernfs_node with @name and @ns under @parent and remove it. 825324a56e1STejun Heo * Returns 0 on success, -ENOENT if such entry doesn't exist. 826fd7b9f7bSTejun Heo */ 827324a56e1STejun Heo int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, 828fd7b9f7bSTejun Heo const void *ns) 829fd7b9f7bSTejun Heo { 830fd7b9f7bSTejun Heo struct sysfs_addrm_cxt acxt; 831324a56e1STejun Heo struct kernfs_node *kn; 832fd7b9f7bSTejun Heo 833324a56e1STejun Heo if (!parent) { 834fd7b9f7bSTejun Heo WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", 835fd7b9f7bSTejun Heo name); 836fd7b9f7bSTejun Heo return -ENOENT; 837fd7b9f7bSTejun Heo } 838fd7b9f7bSTejun Heo 839fd7b9f7bSTejun Heo sysfs_addrm_start(&acxt); 840fd7b9f7bSTejun Heo 841324a56e1STejun Heo kn = kernfs_find_ns(parent, name, ns); 842324a56e1STejun Heo if (kn) 843324a56e1STejun Heo __kernfs_remove(&acxt, kn); 844fd7b9f7bSTejun Heo 845fd7b9f7bSTejun Heo sysfs_addrm_finish(&acxt); 846fd7b9f7bSTejun Heo 847324a56e1STejun Heo if (kn) 848fd7b9f7bSTejun Heo return 0; 849fd7b9f7bSTejun Heo else 850fd7b9f7bSTejun Heo return -ENOENT; 851fd7b9f7bSTejun Heo } 852fd7b9f7bSTejun Heo 853fd7b9f7bSTejun Heo /** 854fd7b9f7bSTejun Heo * kernfs_rename_ns - move and rename a kernfs_node 855324a56e1STejun Heo * @kn: target node 856fd7b9f7bSTejun Heo * @new_parent: new parent to put @sd under 857fd7b9f7bSTejun Heo * @new_name: new name 858fd7b9f7bSTejun Heo * @new_ns: new namespace tag 859fd7b9f7bSTejun Heo */ 860324a56e1STejun Heo int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, 861fd7b9f7bSTejun Heo const char *new_name, const void *new_ns) 862fd7b9f7bSTejun Heo { 863fd7b9f7bSTejun Heo int error; 864fd7b9f7bSTejun Heo 865fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 866fd7b9f7bSTejun Heo 867fd7b9f7bSTejun Heo error = 0; 868324a56e1STejun Heo if ((kn->s_parent == new_parent) && (kn->s_ns == new_ns) && 869324a56e1STejun Heo (strcmp(kn->s_name, new_name) == 0)) 870fd7b9f7bSTejun Heo goto out; /* nothing to rename */ 871fd7b9f7bSTejun Heo 872fd7b9f7bSTejun Heo error = -EEXIST; 873fd7b9f7bSTejun Heo if (kernfs_find_ns(new_parent, new_name, new_ns)) 874fd7b9f7bSTejun Heo goto out; 875fd7b9f7bSTejun Heo 876324a56e1STejun Heo /* rename kernfs_node */ 877324a56e1STejun Heo if (strcmp(kn->s_name, new_name) != 0) { 878fd7b9f7bSTejun Heo error = -ENOMEM; 879fd7b9f7bSTejun Heo new_name = kstrdup(new_name, GFP_KERNEL); 880fd7b9f7bSTejun Heo if (!new_name) 881fd7b9f7bSTejun Heo goto out; 882fd7b9f7bSTejun Heo 883324a56e1STejun Heo kfree(kn->s_name); 884324a56e1STejun Heo kn->s_name = new_name; 885fd7b9f7bSTejun Heo } 886fd7b9f7bSTejun Heo 887fd7b9f7bSTejun Heo /* 888fd7b9f7bSTejun Heo * Move to the appropriate place in the appropriate directories rbtree. 889fd7b9f7bSTejun Heo */ 890324a56e1STejun Heo sysfs_unlink_sibling(kn); 891fd7b9f7bSTejun Heo kernfs_get(new_parent); 892324a56e1STejun Heo kernfs_put(kn->s_parent); 893324a56e1STejun Heo kn->s_ns = new_ns; 894324a56e1STejun Heo kn->s_hash = sysfs_name_hash(kn->s_name, kn->s_ns); 895324a56e1STejun Heo kn->s_parent = new_parent; 896324a56e1STejun Heo sysfs_link_sibling(kn); 897fd7b9f7bSTejun Heo 898fd7b9f7bSTejun Heo error = 0; 899fd7b9f7bSTejun Heo out: 900fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 901fd7b9f7bSTejun Heo return error; 902fd7b9f7bSTejun Heo } 903fd7b9f7bSTejun Heo 904fd7b9f7bSTejun Heo /* Relationship between s_mode and the DT_xxx types */ 905324a56e1STejun Heo static inline unsigned char dt_type(struct kernfs_node *kn) 906fd7b9f7bSTejun Heo { 907324a56e1STejun Heo return (kn->s_mode >> 12) & 15; 908fd7b9f7bSTejun Heo } 909fd7b9f7bSTejun Heo 910fd7b9f7bSTejun Heo static int sysfs_dir_release(struct inode *inode, struct file *filp) 911fd7b9f7bSTejun Heo { 912fd7b9f7bSTejun Heo kernfs_put(filp->private_data); 913fd7b9f7bSTejun Heo return 0; 914fd7b9f7bSTejun Heo } 915fd7b9f7bSTejun Heo 916324a56e1STejun Heo static struct kernfs_node *sysfs_dir_pos(const void *ns, 917324a56e1STejun Heo struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) 918fd7b9f7bSTejun Heo { 919fd7b9f7bSTejun Heo if (pos) { 920fd7b9f7bSTejun Heo int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && 921324a56e1STejun Heo pos->s_parent == parent && 922fd7b9f7bSTejun Heo hash == pos->s_hash; 923fd7b9f7bSTejun Heo kernfs_put(pos); 924fd7b9f7bSTejun Heo if (!valid) 925fd7b9f7bSTejun Heo pos = NULL; 926fd7b9f7bSTejun Heo } 927fd7b9f7bSTejun Heo if (!pos && (hash > 1) && (hash < INT_MAX)) { 928324a56e1STejun Heo struct rb_node *node = parent->s_dir.children.rb_node; 929fd7b9f7bSTejun Heo while (node) { 930324a56e1STejun Heo pos = rb_to_kn(node); 931fd7b9f7bSTejun Heo 932fd7b9f7bSTejun Heo if (hash < pos->s_hash) 933fd7b9f7bSTejun Heo node = node->rb_left; 934fd7b9f7bSTejun Heo else if (hash > pos->s_hash) 935fd7b9f7bSTejun Heo node = node->rb_right; 936fd7b9f7bSTejun Heo else 937fd7b9f7bSTejun Heo break; 938fd7b9f7bSTejun Heo } 939fd7b9f7bSTejun Heo } 940fd7b9f7bSTejun Heo /* Skip over entries in the wrong namespace */ 941fd7b9f7bSTejun Heo while (pos && pos->s_ns != ns) { 942fd7b9f7bSTejun Heo struct rb_node *node = rb_next(&pos->s_rb); 943fd7b9f7bSTejun Heo if (!node) 944fd7b9f7bSTejun Heo pos = NULL; 945fd7b9f7bSTejun Heo else 946324a56e1STejun Heo pos = rb_to_kn(node); 947fd7b9f7bSTejun Heo } 948fd7b9f7bSTejun Heo return pos; 949fd7b9f7bSTejun Heo } 950fd7b9f7bSTejun Heo 951324a56e1STejun Heo static struct kernfs_node *sysfs_dir_next_pos(const void *ns, 952324a56e1STejun Heo struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos) 953fd7b9f7bSTejun Heo { 954324a56e1STejun Heo pos = sysfs_dir_pos(ns, parent, ino, pos); 955fd7b9f7bSTejun Heo if (pos) 956fd7b9f7bSTejun Heo do { 957fd7b9f7bSTejun Heo struct rb_node *node = rb_next(&pos->s_rb); 958fd7b9f7bSTejun Heo if (!node) 959fd7b9f7bSTejun Heo pos = NULL; 960fd7b9f7bSTejun Heo else 961324a56e1STejun Heo pos = rb_to_kn(node); 962fd7b9f7bSTejun Heo } while (pos && pos->s_ns != ns); 963fd7b9f7bSTejun Heo return pos; 964fd7b9f7bSTejun Heo } 965fd7b9f7bSTejun Heo 966fd7b9f7bSTejun Heo static int sysfs_readdir(struct file *file, struct dir_context *ctx) 967fd7b9f7bSTejun Heo { 968fd7b9f7bSTejun Heo struct dentry *dentry = file->f_path.dentry; 969324a56e1STejun Heo struct kernfs_node *parent = dentry->d_fsdata; 970324a56e1STejun Heo struct kernfs_node *pos = file->private_data; 971fd7b9f7bSTejun Heo const void *ns = NULL; 972fd7b9f7bSTejun Heo 973fd7b9f7bSTejun Heo if (!dir_emit_dots(file, ctx)) 974fd7b9f7bSTejun Heo return 0; 975fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 976fd7b9f7bSTejun Heo 977324a56e1STejun Heo if (kernfs_ns_enabled(parent)) 978fd7b9f7bSTejun Heo ns = sysfs_info(dentry->d_sb)->ns; 979fd7b9f7bSTejun Heo 980324a56e1STejun Heo for (pos = sysfs_dir_pos(ns, parent, ctx->pos, pos); 981fd7b9f7bSTejun Heo pos; 982324a56e1STejun Heo pos = sysfs_dir_next_pos(ns, parent, ctx->pos, pos)) { 983fd7b9f7bSTejun Heo const char *name = pos->s_name; 984fd7b9f7bSTejun Heo unsigned int type = dt_type(pos); 985fd7b9f7bSTejun Heo int len = strlen(name); 986fd7b9f7bSTejun Heo ino_t ino = pos->s_ino; 987fd7b9f7bSTejun Heo 988fd7b9f7bSTejun Heo ctx->pos = pos->s_hash; 989fd7b9f7bSTejun Heo file->private_data = pos; 990fd7b9f7bSTejun Heo kernfs_get(pos); 991fd7b9f7bSTejun Heo 992fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 993fd7b9f7bSTejun Heo if (!dir_emit(ctx, name, len, ino, type)) 994fd7b9f7bSTejun Heo return 0; 995fd7b9f7bSTejun Heo mutex_lock(&sysfs_mutex); 996fd7b9f7bSTejun Heo } 997fd7b9f7bSTejun Heo mutex_unlock(&sysfs_mutex); 998fd7b9f7bSTejun Heo file->private_data = NULL; 999fd7b9f7bSTejun Heo ctx->pos = INT_MAX; 1000fd7b9f7bSTejun Heo return 0; 1001fd7b9f7bSTejun Heo } 1002fd7b9f7bSTejun Heo 1003fd7b9f7bSTejun Heo static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) 1004fd7b9f7bSTejun Heo { 1005fd7b9f7bSTejun Heo struct inode *inode = file_inode(file); 1006fd7b9f7bSTejun Heo loff_t ret; 1007fd7b9f7bSTejun Heo 1008fd7b9f7bSTejun Heo mutex_lock(&inode->i_mutex); 1009fd7b9f7bSTejun Heo ret = generic_file_llseek(file, offset, whence); 1010fd7b9f7bSTejun Heo mutex_unlock(&inode->i_mutex); 1011fd7b9f7bSTejun Heo 1012fd7b9f7bSTejun Heo return ret; 1013fd7b9f7bSTejun Heo } 1014fd7b9f7bSTejun Heo 1015fd7b9f7bSTejun Heo const struct file_operations sysfs_dir_operations = { 1016fd7b9f7bSTejun Heo .read = generic_read_dir, 1017fd7b9f7bSTejun Heo .iterate = sysfs_readdir, 1018fd7b9f7bSTejun Heo .release = sysfs_dir_release, 1019fd7b9f7bSTejun Heo .llseek = sysfs_dir_llseek, 1020fd7b9f7bSTejun Heo }; 1021