Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kernfs: implement kernfs_get_parent(), kernfs_name/path() and friends

kernfs_node->parent and ->name are currently marked as "published"
indicating that kernfs users may access them directly; however, those
fields may get updated by kernfs_rename[_ns]() and unrestricted access
may lead to erroneous values or oops.

Protect ->parent and ->name updates with a irq-safe spinlock
kernfs_rename_lock and implement the following accessors for these
fields.

* kernfs_name() - format the node's name into the specified buffer
* kernfs_path() - format the node's path into the specified buffer
* pr_cont_kernfs_name() - pr_cont a node's name (doesn't need buffer)
* pr_cont_kernfs_path() - pr_cont a node's path (doesn't need buffer)
* kernfs_get_parent() - pin and return a node's parent

All can be called under any context. The recursive sysfs_pathname()
in fs/sysfs/dir.c is replaced with kernfs_path() and
sysfs_rename_dir_ns() is updated to use kernfs_get_parent() instead of
dereferencing parent directly.

v2: Dummy definition of kernfs_path() for !CONFIG_KERNFS was missing
static inline making it cause a lot of build warnings. Add it.

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tejun Heo and committed by
Greg Kroah-Hartman
3eef34ad 0c23b225

+203 -42
+165 -10
fs/kernfs/dir.c
··· 19 19 #include "kernfs-internal.h" 20 20 21 21 DEFINE_MUTEX(kernfs_mutex); 22 + static DEFINE_SPINLOCK(kernfs_rename_lock); /* kn->parent and ->name */ 23 + static char kernfs_pr_cont_buf[PATH_MAX]; /* protected by rename_lock */ 22 24 23 25 #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) 24 26 ··· 37 35 #else 38 36 return false; 39 37 #endif 38 + } 39 + 40 + static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen) 41 + { 42 + return strlcpy(buf, kn->parent ? kn->name : "/", buflen); 43 + } 44 + 45 + static char * __must_check kernfs_path_locked(struct kernfs_node *kn, char *buf, 46 + size_t buflen) 47 + { 48 + char *p = buf + buflen; 49 + int len; 50 + 51 + *--p = '\0'; 52 + 53 + do { 54 + len = strlen(kn->name); 55 + if (p - buf < len + 1) { 56 + buf[0] = '\0'; 57 + p = NULL; 58 + break; 59 + } 60 + p -= len; 61 + memcpy(p, kn->name, len); 62 + *--p = '/'; 63 + kn = kn->parent; 64 + } while (kn && kn->parent); 65 + 66 + return p; 67 + } 68 + 69 + /** 70 + * kernfs_name - obtain the name of a given node 71 + * @kn: kernfs_node of interest 72 + * @buf: buffer to copy @kn's name into 73 + * @buflen: size of @buf 74 + * 75 + * Copies the name of @kn into @buf of @buflen bytes. The behavior is 76 + * similar to strlcpy(). It returns the length of @kn's name and if @buf 77 + * isn't long enough, it's filled upto @buflen-1 and nul terminated. 78 + * 79 + * This function can be called from any context. 80 + */ 81 + int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen) 82 + { 83 + unsigned long flags; 84 + int ret; 85 + 86 + spin_lock_irqsave(&kernfs_rename_lock, flags); 87 + ret = kernfs_name_locked(kn, buf, buflen); 88 + spin_unlock_irqrestore(&kernfs_rename_lock, flags); 89 + return ret; 90 + } 91 + 92 + /** 93 + * kernfs_path - build full path of a given node 94 + * @kn: kernfs_node of interest 95 + * @buf: buffer to copy @kn's name into 96 + * @buflen: size of @buf 97 + * 98 + * Builds and returns the full path of @kn in @buf of @buflen bytes. The 99 + * path is built from the end of @buf so the returned pointer usually 100 + * doesn't match @buf. If @buf isn't long enough, @buf is nul terminated 101 + * and %NULL is returned. 102 + */ 103 + char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen) 104 + { 105 + unsigned long flags; 106 + char *p; 107 + 108 + spin_lock_irqsave(&kernfs_rename_lock, flags); 109 + p = kernfs_path_locked(kn, buf, buflen); 110 + spin_unlock_irqrestore(&kernfs_rename_lock, flags); 111 + return p; 112 + } 113 + 114 + /** 115 + * pr_cont_kernfs_name - pr_cont name of a kernfs_node 116 + * @kn: kernfs_node of interest 117 + * 118 + * This function can be called from any context. 119 + */ 120 + void pr_cont_kernfs_name(struct kernfs_node *kn) 121 + { 122 + unsigned long flags; 123 + 124 + spin_lock_irqsave(&kernfs_rename_lock, flags); 125 + 126 + kernfs_name_locked(kn, kernfs_pr_cont_buf, sizeof(kernfs_pr_cont_buf)); 127 + pr_cont("%s", kernfs_pr_cont_buf); 128 + 129 + spin_unlock_irqrestore(&kernfs_rename_lock, flags); 130 + } 131 + 132 + /** 133 + * pr_cont_kernfs_path - pr_cont path of a kernfs_node 134 + * @kn: kernfs_node of interest 135 + * 136 + * This function can be called from any context. 137 + */ 138 + void pr_cont_kernfs_path(struct kernfs_node *kn) 139 + { 140 + unsigned long flags; 141 + char *p; 142 + 143 + spin_lock_irqsave(&kernfs_rename_lock, flags); 144 + 145 + p = kernfs_path_locked(kn, kernfs_pr_cont_buf, 146 + sizeof(kernfs_pr_cont_buf)); 147 + if (p) 148 + pr_cont("%s", p); 149 + else 150 + pr_cont("<name too long>"); 151 + 152 + spin_unlock_irqrestore(&kernfs_rename_lock, flags); 153 + } 154 + 155 + /** 156 + * kernfs_get_parent - determine the parent node and pin it 157 + * @kn: kernfs_node of interest 158 + * 159 + * Determines @kn's parent, pins and returns it. This function can be 160 + * called from any context. 161 + */ 162 + struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn) 163 + { 164 + struct kernfs_node *parent; 165 + unsigned long flags; 166 + 167 + spin_lock_irqsave(&kernfs_rename_lock, flags); 168 + parent = kn->parent; 169 + kernfs_get(parent); 170 + spin_unlock_irqrestore(&kernfs_rename_lock, flags); 171 + 172 + return parent; 40 173 } 41 174 42 175 /** ··· 1240 1103 int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, 1241 1104 const char *new_name, const void *new_ns) 1242 1105 { 1106 + struct kernfs_node *old_parent; 1107 + const char *old_name = NULL; 1243 1108 int error; 1109 + 1110 + /* can't move or rename root */ 1111 + if (!kn->parent) 1112 + return -EINVAL; 1244 1113 1245 1114 mutex_lock(&kernfs_mutex); 1246 1115 ··· 1269 1126 new_name = kstrdup(new_name, GFP_KERNEL); 1270 1127 if (!new_name) 1271 1128 goto out; 1272 - 1273 - if (kn->flags & KERNFS_STATIC_NAME) 1274 - kn->flags &= ~KERNFS_STATIC_NAME; 1275 - else 1276 - kfree(kn->name); 1277 - 1278 - kn->name = new_name; 1129 + } else { 1130 + new_name = NULL; 1279 1131 } 1280 1132 1281 1133 /* ··· 1278 1140 */ 1279 1141 kernfs_unlink_sibling(kn); 1280 1142 kernfs_get(new_parent); 1281 - kernfs_put(kn->parent); 1282 - kn->ns = new_ns; 1283 - kn->hash = kernfs_name_hash(kn->name, kn->ns); 1143 + 1144 + /* rename_lock protects ->parent and ->name accessors */ 1145 + spin_lock_irq(&kernfs_rename_lock); 1146 + 1147 + old_parent = kn->parent; 1284 1148 kn->parent = new_parent; 1149 + 1150 + kn->ns = new_ns; 1151 + if (new_name) { 1152 + if (!(kn->flags & KERNFS_STATIC_NAME)) 1153 + old_name = kn->name; 1154 + kn->flags &= ~KERNFS_STATIC_NAME; 1155 + kn->name = new_name; 1156 + } 1157 + 1158 + spin_unlock_irq(&kernfs_rename_lock); 1159 + 1160 + kn->hash = kernfs_name_hash(new_name, new_ns); 1285 1161 kernfs_link_sibling(kn); 1162 + 1163 + kernfs_put(old_parent); 1164 + kfree(old_name); 1286 1165 1287 1166 error = 0; 1288 1167 out:
+13 -31
fs/sysfs/dir.c
··· 19 19 20 20 DEFINE_SPINLOCK(sysfs_symlink_target_lock); 21 21 22 - /** 23 - * sysfs_pathname - return full path to sysfs dirent 24 - * @kn: kernfs_node whose path we want 25 - * @path: caller allocated buffer of size PATH_MAX 26 - * 27 - * Gives the name "/" to the sysfs_root entry; any path returned 28 - * is relative to wherever sysfs is mounted. 29 - */ 30 - static char *sysfs_pathname(struct kernfs_node *kn, char *path) 31 - { 32 - if (kn->parent) { 33 - sysfs_pathname(kn->parent, path); 34 - strlcat(path, "/", PATH_MAX); 35 - } 36 - strlcat(path, kn->name, PATH_MAX); 37 - return path; 38 - } 39 - 40 22 void sysfs_warn_dup(struct kernfs_node *parent, const char *name) 41 23 { 42 - char *path; 24 + char *buf, *path = NULL; 43 25 44 - path = kzalloc(PATH_MAX, GFP_KERNEL); 45 - if (path) { 46 - sysfs_pathname(parent, path); 47 - strlcat(path, "/", PATH_MAX); 48 - strlcat(path, name, PATH_MAX); 49 - } 26 + buf = kzalloc(PATH_MAX, GFP_KERNEL); 27 + if (buf) 28 + path = kernfs_path(parent, buf, PATH_MAX); 50 29 51 - WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s'\n", 52 - path ? path : name); 30 + WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n", 31 + path, name); 53 32 54 - kfree(path); 33 + kfree(buf); 55 34 } 56 35 57 36 /** ··· 101 122 int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, 102 123 const void *new_ns) 103 124 { 104 - struct kernfs_node *parent = kobj->sd->parent; 125 + struct kernfs_node *parent; 126 + int ret; 105 127 106 - return kernfs_rename_ns(kobj->sd, parent, new_name, new_ns); 128 + parent = kernfs_get_parent(kobj->sd); 129 + ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns); 130 + kernfs_put(parent); 131 + return ret; 107 132 } 108 133 109 134 int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, ··· 116 133 struct kernfs_node *kn = kobj->sd; 117 134 struct kernfs_node *new_parent; 118 135 119 - BUG_ON(!kn->parent); 120 136 new_parent = new_parent_kobj && new_parent_kobj->sd ? 121 137 new_parent_kobj->sd : sysfs_root_kn; 122 138
+25 -1
include/linux/kernfs.h
··· 91 91 #ifdef CONFIG_DEBUG_LOCK_ALLOC 92 92 struct lockdep_map dep_map; 93 93 #endif 94 - /* the following two fields are published */ 94 + /* 95 + * Use kernfs_get_parent() and kernfs_name/path() instead of 96 + * accessing the following two fields directly. If the node is 97 + * never moved to a different parent, it is safe to access the 98 + * parent directly. 99 + */ 95 100 struct kernfs_node *parent; 96 101 const char *name; 97 102 ··· 234 229 return kn->flags & KERNFS_NS; 235 230 } 236 231 232 + int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen); 233 + char * __must_check kernfs_path(struct kernfs_node *kn, char *buf, 234 + size_t buflen); 235 + void pr_cont_kernfs_name(struct kernfs_node *kn); 236 + void pr_cont_kernfs_path(struct kernfs_node *kn); 237 + struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn); 237 238 struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, 238 239 const char *name, const void *ns); 239 240 void kernfs_get(struct kernfs_node *kn); ··· 293 282 294 283 static inline bool kernfs_ns_enabled(struct kernfs_node *kn) 295 284 { return false; } 285 + 286 + static inline int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen) 287 + { return -ENOSYS; } 288 + 289 + static inline char * __must_check kernfs_path(struct kernfs_node *kn, char *buf, 290 + size_t buflen) 291 + { return NULL; } 292 + 293 + static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { } 294 + static inline void pr_cont_kernfs_path(struct kernfs_node *kn) { } 295 + 296 + static inline struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn) 297 + { return NULL; } 296 298 297 299 static inline struct kernfs_node * 298 300 kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name,