1*4282d606SSteven Rostedt (Red Hat) /* 2*4282d606SSteven Rostedt (Red Hat) * inode.c - part of tracefs, a pseudo file system for activating tracing 3*4282d606SSteven Rostedt (Red Hat) * 4*4282d606SSteven Rostedt (Red Hat) * Based on debugfs by: Greg Kroah-Hartman <greg@kroah.com> 5*4282d606SSteven Rostedt (Red Hat) * 6*4282d606SSteven Rostedt (Red Hat) * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com> 7*4282d606SSteven Rostedt (Red Hat) * 8*4282d606SSteven Rostedt (Red Hat) * This program is free software; you can redistribute it and/or 9*4282d606SSteven Rostedt (Red Hat) * modify it under the terms of the GNU General Public License version 10*4282d606SSteven Rostedt (Red Hat) * 2 as published by the Free Software Foundation. 11*4282d606SSteven Rostedt (Red Hat) * 12*4282d606SSteven Rostedt (Red Hat) * tracefs is the file system that is used by the tracing infrastructure. 13*4282d606SSteven Rostedt (Red Hat) * 14*4282d606SSteven Rostedt (Red Hat) */ 15*4282d606SSteven Rostedt (Red Hat) 16*4282d606SSteven Rostedt (Red Hat) #include <linux/module.h> 17*4282d606SSteven Rostedt (Red Hat) #include <linux/fs.h> 18*4282d606SSteven Rostedt (Red Hat) #include <linux/mount.h> 19*4282d606SSteven Rostedt (Red Hat) #include <linux/namei.h> 20*4282d606SSteven Rostedt (Red Hat) #include <linux/tracefs.h> 21*4282d606SSteven Rostedt (Red Hat) #include <linux/fsnotify.h> 22*4282d606SSteven Rostedt (Red Hat) #include <linux/seq_file.h> 23*4282d606SSteven Rostedt (Red Hat) #include <linux/parser.h> 24*4282d606SSteven Rostedt (Red Hat) #include <linux/magic.h> 25*4282d606SSteven Rostedt (Red Hat) #include <linux/slab.h> 26*4282d606SSteven Rostedt (Red Hat) 27*4282d606SSteven Rostedt (Red Hat) #define TRACEFS_DEFAULT_MODE 0700 28*4282d606SSteven Rostedt (Red Hat) 29*4282d606SSteven Rostedt (Red Hat) static struct vfsmount *tracefs_mount; 30*4282d606SSteven Rostedt (Red Hat) static int tracefs_mount_count; 31*4282d606SSteven Rostedt (Red Hat) static bool tracefs_registered; 32*4282d606SSteven Rostedt (Red Hat) 33*4282d606SSteven Rostedt (Red Hat) static ssize_t default_read_file(struct file *file, char __user *buf, 34*4282d606SSteven Rostedt (Red Hat) size_t count, loff_t *ppos) 35*4282d606SSteven Rostedt (Red Hat) { 36*4282d606SSteven Rostedt (Red Hat) return 0; 37*4282d606SSteven Rostedt (Red Hat) } 38*4282d606SSteven Rostedt (Red Hat) 39*4282d606SSteven Rostedt (Red Hat) static ssize_t default_write_file(struct file *file, const char __user *buf, 40*4282d606SSteven Rostedt (Red Hat) size_t count, loff_t *ppos) 41*4282d606SSteven Rostedt (Red Hat) { 42*4282d606SSteven Rostedt (Red Hat) return count; 43*4282d606SSteven Rostedt (Red Hat) } 44*4282d606SSteven Rostedt (Red Hat) 45*4282d606SSteven Rostedt (Red Hat) static const struct file_operations tracefs_file_operations = { 46*4282d606SSteven Rostedt (Red Hat) .read = default_read_file, 47*4282d606SSteven Rostedt (Red Hat) .write = default_write_file, 48*4282d606SSteven Rostedt (Red Hat) .open = simple_open, 49*4282d606SSteven Rostedt (Red Hat) .llseek = noop_llseek, 50*4282d606SSteven Rostedt (Red Hat) }; 51*4282d606SSteven Rostedt (Red Hat) 52*4282d606SSteven Rostedt (Red Hat) static struct inode *tracefs_get_inode(struct super_block *sb) 53*4282d606SSteven Rostedt (Red Hat) { 54*4282d606SSteven Rostedt (Red Hat) struct inode *inode = new_inode(sb); 55*4282d606SSteven Rostedt (Red Hat) if (inode) { 56*4282d606SSteven Rostedt (Red Hat) inode->i_ino = get_next_ino(); 57*4282d606SSteven Rostedt (Red Hat) inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; 58*4282d606SSteven Rostedt (Red Hat) } 59*4282d606SSteven Rostedt (Red Hat) return inode; 60*4282d606SSteven Rostedt (Red Hat) } 61*4282d606SSteven Rostedt (Red Hat) 62*4282d606SSteven Rostedt (Red Hat) struct tracefs_mount_opts { 63*4282d606SSteven Rostedt (Red Hat) kuid_t uid; 64*4282d606SSteven Rostedt (Red Hat) kgid_t gid; 65*4282d606SSteven Rostedt (Red Hat) umode_t mode; 66*4282d606SSteven Rostedt (Red Hat) }; 67*4282d606SSteven Rostedt (Red Hat) 68*4282d606SSteven Rostedt (Red Hat) enum { 69*4282d606SSteven Rostedt (Red Hat) Opt_uid, 70*4282d606SSteven Rostedt (Red Hat) Opt_gid, 71*4282d606SSteven Rostedt (Red Hat) Opt_mode, 72*4282d606SSteven Rostedt (Red Hat) Opt_err 73*4282d606SSteven Rostedt (Red Hat) }; 74*4282d606SSteven Rostedt (Red Hat) 75*4282d606SSteven Rostedt (Red Hat) static const match_table_t tokens = { 76*4282d606SSteven Rostedt (Red Hat) {Opt_uid, "uid=%u"}, 77*4282d606SSteven Rostedt (Red Hat) {Opt_gid, "gid=%u"}, 78*4282d606SSteven Rostedt (Red Hat) {Opt_mode, "mode=%o"}, 79*4282d606SSteven Rostedt (Red Hat) {Opt_err, NULL} 80*4282d606SSteven Rostedt (Red Hat) }; 81*4282d606SSteven Rostedt (Red Hat) 82*4282d606SSteven Rostedt (Red Hat) struct tracefs_fs_info { 83*4282d606SSteven Rostedt (Red Hat) struct tracefs_mount_opts mount_opts; 84*4282d606SSteven Rostedt (Red Hat) }; 85*4282d606SSteven Rostedt (Red Hat) 86*4282d606SSteven Rostedt (Red Hat) static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) 87*4282d606SSteven Rostedt (Red Hat) { 88*4282d606SSteven Rostedt (Red Hat) substring_t args[MAX_OPT_ARGS]; 89*4282d606SSteven Rostedt (Red Hat) int option; 90*4282d606SSteven Rostedt (Red Hat) int token; 91*4282d606SSteven Rostedt (Red Hat) kuid_t uid; 92*4282d606SSteven Rostedt (Red Hat) kgid_t gid; 93*4282d606SSteven Rostedt (Red Hat) char *p; 94*4282d606SSteven Rostedt (Red Hat) 95*4282d606SSteven Rostedt (Red Hat) opts->mode = TRACEFS_DEFAULT_MODE; 96*4282d606SSteven Rostedt (Red Hat) 97*4282d606SSteven Rostedt (Red Hat) while ((p = strsep(&data, ",")) != NULL) { 98*4282d606SSteven Rostedt (Red Hat) if (!*p) 99*4282d606SSteven Rostedt (Red Hat) continue; 100*4282d606SSteven Rostedt (Red Hat) 101*4282d606SSteven Rostedt (Red Hat) token = match_token(p, tokens, args); 102*4282d606SSteven Rostedt (Red Hat) switch (token) { 103*4282d606SSteven Rostedt (Red Hat) case Opt_uid: 104*4282d606SSteven Rostedt (Red Hat) if (match_int(&args[0], &option)) 105*4282d606SSteven Rostedt (Red Hat) return -EINVAL; 106*4282d606SSteven Rostedt (Red Hat) uid = make_kuid(current_user_ns(), option); 107*4282d606SSteven Rostedt (Red Hat) if (!uid_valid(uid)) 108*4282d606SSteven Rostedt (Red Hat) return -EINVAL; 109*4282d606SSteven Rostedt (Red Hat) opts->uid = uid; 110*4282d606SSteven Rostedt (Red Hat) break; 111*4282d606SSteven Rostedt (Red Hat) case Opt_gid: 112*4282d606SSteven Rostedt (Red Hat) if (match_int(&args[0], &option)) 113*4282d606SSteven Rostedt (Red Hat) return -EINVAL; 114*4282d606SSteven Rostedt (Red Hat) gid = make_kgid(current_user_ns(), option); 115*4282d606SSteven Rostedt (Red Hat) if (!gid_valid(gid)) 116*4282d606SSteven Rostedt (Red Hat) return -EINVAL; 117*4282d606SSteven Rostedt (Red Hat) opts->gid = gid; 118*4282d606SSteven Rostedt (Red Hat) break; 119*4282d606SSteven Rostedt (Red Hat) case Opt_mode: 120*4282d606SSteven Rostedt (Red Hat) if (match_octal(&args[0], &option)) 121*4282d606SSteven Rostedt (Red Hat) return -EINVAL; 122*4282d606SSteven Rostedt (Red Hat) opts->mode = option & S_IALLUGO; 123*4282d606SSteven Rostedt (Red Hat) break; 124*4282d606SSteven Rostedt (Red Hat) /* 125*4282d606SSteven Rostedt (Red Hat) * We might like to report bad mount options here; 126*4282d606SSteven Rostedt (Red Hat) * but traditionally tracefs has ignored all mount options 127*4282d606SSteven Rostedt (Red Hat) */ 128*4282d606SSteven Rostedt (Red Hat) } 129*4282d606SSteven Rostedt (Red Hat) } 130*4282d606SSteven Rostedt (Red Hat) 131*4282d606SSteven Rostedt (Red Hat) return 0; 132*4282d606SSteven Rostedt (Red Hat) } 133*4282d606SSteven Rostedt (Red Hat) 134*4282d606SSteven Rostedt (Red Hat) static int tracefs_apply_options(struct super_block *sb) 135*4282d606SSteven Rostedt (Red Hat) { 136*4282d606SSteven Rostedt (Red Hat) struct tracefs_fs_info *fsi = sb->s_fs_info; 137*4282d606SSteven Rostedt (Red Hat) struct inode *inode = sb->s_root->d_inode; 138*4282d606SSteven Rostedt (Red Hat) struct tracefs_mount_opts *opts = &fsi->mount_opts; 139*4282d606SSteven Rostedt (Red Hat) 140*4282d606SSteven Rostedt (Red Hat) inode->i_mode &= ~S_IALLUGO; 141*4282d606SSteven Rostedt (Red Hat) inode->i_mode |= opts->mode; 142*4282d606SSteven Rostedt (Red Hat) 143*4282d606SSteven Rostedt (Red Hat) inode->i_uid = opts->uid; 144*4282d606SSteven Rostedt (Red Hat) inode->i_gid = opts->gid; 145*4282d606SSteven Rostedt (Red Hat) 146*4282d606SSteven Rostedt (Red Hat) return 0; 147*4282d606SSteven Rostedt (Red Hat) } 148*4282d606SSteven Rostedt (Red Hat) 149*4282d606SSteven Rostedt (Red Hat) static int tracefs_remount(struct super_block *sb, int *flags, char *data) 150*4282d606SSteven Rostedt (Red Hat) { 151*4282d606SSteven Rostedt (Red Hat) int err; 152*4282d606SSteven Rostedt (Red Hat) struct tracefs_fs_info *fsi = sb->s_fs_info; 153*4282d606SSteven Rostedt (Red Hat) 154*4282d606SSteven Rostedt (Red Hat) sync_filesystem(sb); 155*4282d606SSteven Rostedt (Red Hat) err = tracefs_parse_options(data, &fsi->mount_opts); 156*4282d606SSteven Rostedt (Red Hat) if (err) 157*4282d606SSteven Rostedt (Red Hat) goto fail; 158*4282d606SSteven Rostedt (Red Hat) 159*4282d606SSteven Rostedt (Red Hat) tracefs_apply_options(sb); 160*4282d606SSteven Rostedt (Red Hat) 161*4282d606SSteven Rostedt (Red Hat) fail: 162*4282d606SSteven Rostedt (Red Hat) return err; 163*4282d606SSteven Rostedt (Red Hat) } 164*4282d606SSteven Rostedt (Red Hat) 165*4282d606SSteven Rostedt (Red Hat) static int tracefs_show_options(struct seq_file *m, struct dentry *root) 166*4282d606SSteven Rostedt (Red Hat) { 167*4282d606SSteven Rostedt (Red Hat) struct tracefs_fs_info *fsi = root->d_sb->s_fs_info; 168*4282d606SSteven Rostedt (Red Hat) struct tracefs_mount_opts *opts = &fsi->mount_opts; 169*4282d606SSteven Rostedt (Red Hat) 170*4282d606SSteven Rostedt (Red Hat) if (!uid_eq(opts->uid, GLOBAL_ROOT_UID)) 171*4282d606SSteven Rostedt (Red Hat) seq_printf(m, ",uid=%u", 172*4282d606SSteven Rostedt (Red Hat) from_kuid_munged(&init_user_ns, opts->uid)); 173*4282d606SSteven Rostedt (Red Hat) if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) 174*4282d606SSteven Rostedt (Red Hat) seq_printf(m, ",gid=%u", 175*4282d606SSteven Rostedt (Red Hat) from_kgid_munged(&init_user_ns, opts->gid)); 176*4282d606SSteven Rostedt (Red Hat) if (opts->mode != TRACEFS_DEFAULT_MODE) 177*4282d606SSteven Rostedt (Red Hat) seq_printf(m, ",mode=%o", opts->mode); 178*4282d606SSteven Rostedt (Red Hat) 179*4282d606SSteven Rostedt (Red Hat) return 0; 180*4282d606SSteven Rostedt (Red Hat) } 181*4282d606SSteven Rostedt (Red Hat) 182*4282d606SSteven Rostedt (Red Hat) static const struct super_operations tracefs_super_operations = { 183*4282d606SSteven Rostedt (Red Hat) .statfs = simple_statfs, 184*4282d606SSteven Rostedt (Red Hat) .remount_fs = tracefs_remount, 185*4282d606SSteven Rostedt (Red Hat) .show_options = tracefs_show_options, 186*4282d606SSteven Rostedt (Red Hat) }; 187*4282d606SSteven Rostedt (Red Hat) 188*4282d606SSteven Rostedt (Red Hat) static int trace_fill_super(struct super_block *sb, void *data, int silent) 189*4282d606SSteven Rostedt (Red Hat) { 190*4282d606SSteven Rostedt (Red Hat) static struct tree_descr trace_files[] = {{""}}; 191*4282d606SSteven Rostedt (Red Hat) struct tracefs_fs_info *fsi; 192*4282d606SSteven Rostedt (Red Hat) int err; 193*4282d606SSteven Rostedt (Red Hat) 194*4282d606SSteven Rostedt (Red Hat) save_mount_options(sb, data); 195*4282d606SSteven Rostedt (Red Hat) 196*4282d606SSteven Rostedt (Red Hat) fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); 197*4282d606SSteven Rostedt (Red Hat) sb->s_fs_info = fsi; 198*4282d606SSteven Rostedt (Red Hat) if (!fsi) { 199*4282d606SSteven Rostedt (Red Hat) err = -ENOMEM; 200*4282d606SSteven Rostedt (Red Hat) goto fail; 201*4282d606SSteven Rostedt (Red Hat) } 202*4282d606SSteven Rostedt (Red Hat) 203*4282d606SSteven Rostedt (Red Hat) err = tracefs_parse_options(data, &fsi->mount_opts); 204*4282d606SSteven Rostedt (Red Hat) if (err) 205*4282d606SSteven Rostedt (Red Hat) goto fail; 206*4282d606SSteven Rostedt (Red Hat) 207*4282d606SSteven Rostedt (Red Hat) err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); 208*4282d606SSteven Rostedt (Red Hat) if (err) 209*4282d606SSteven Rostedt (Red Hat) goto fail; 210*4282d606SSteven Rostedt (Red Hat) 211*4282d606SSteven Rostedt (Red Hat) sb->s_op = &tracefs_super_operations; 212*4282d606SSteven Rostedt (Red Hat) 213*4282d606SSteven Rostedt (Red Hat) tracefs_apply_options(sb); 214*4282d606SSteven Rostedt (Red Hat) 215*4282d606SSteven Rostedt (Red Hat) return 0; 216*4282d606SSteven Rostedt (Red Hat) 217*4282d606SSteven Rostedt (Red Hat) fail: 218*4282d606SSteven Rostedt (Red Hat) kfree(fsi); 219*4282d606SSteven Rostedt (Red Hat) sb->s_fs_info = NULL; 220*4282d606SSteven Rostedt (Red Hat) return err; 221*4282d606SSteven Rostedt (Red Hat) } 222*4282d606SSteven Rostedt (Red Hat) 223*4282d606SSteven Rostedt (Red Hat) static struct dentry *trace_mount(struct file_system_type *fs_type, 224*4282d606SSteven Rostedt (Red Hat) int flags, const char *dev_name, 225*4282d606SSteven Rostedt (Red Hat) void *data) 226*4282d606SSteven Rostedt (Red Hat) { 227*4282d606SSteven Rostedt (Red Hat) return mount_single(fs_type, flags, data, trace_fill_super); 228*4282d606SSteven Rostedt (Red Hat) } 229*4282d606SSteven Rostedt (Red Hat) 230*4282d606SSteven Rostedt (Red Hat) static struct file_system_type trace_fs_type = { 231*4282d606SSteven Rostedt (Red Hat) .owner = THIS_MODULE, 232*4282d606SSteven Rostedt (Red Hat) .name = "tracefs", 233*4282d606SSteven Rostedt (Red Hat) .mount = trace_mount, 234*4282d606SSteven Rostedt (Red Hat) .kill_sb = kill_litter_super, 235*4282d606SSteven Rostedt (Red Hat) }; 236*4282d606SSteven Rostedt (Red Hat) MODULE_ALIAS_FS("tracefs"); 237*4282d606SSteven Rostedt (Red Hat) 238*4282d606SSteven Rostedt (Red Hat) static struct dentry *start_creating(const char *name, struct dentry *parent) 239*4282d606SSteven Rostedt (Red Hat) { 240*4282d606SSteven Rostedt (Red Hat) struct dentry *dentry; 241*4282d606SSteven Rostedt (Red Hat) int error; 242*4282d606SSteven Rostedt (Red Hat) 243*4282d606SSteven Rostedt (Red Hat) pr_debug("tracefs: creating file '%s'\n",name); 244*4282d606SSteven Rostedt (Red Hat) 245*4282d606SSteven Rostedt (Red Hat) error = simple_pin_fs(&trace_fs_type, &tracefs_mount, 246*4282d606SSteven Rostedt (Red Hat) &tracefs_mount_count); 247*4282d606SSteven Rostedt (Red Hat) if (error) 248*4282d606SSteven Rostedt (Red Hat) return ERR_PTR(error); 249*4282d606SSteven Rostedt (Red Hat) 250*4282d606SSteven Rostedt (Red Hat) /* If the parent is not specified, we create it in the root. 251*4282d606SSteven Rostedt (Red Hat) * We need the root dentry to do this, which is in the super 252*4282d606SSteven Rostedt (Red Hat) * block. A pointer to that is in the struct vfsmount that we 253*4282d606SSteven Rostedt (Red Hat) * have around. 254*4282d606SSteven Rostedt (Red Hat) */ 255*4282d606SSteven Rostedt (Red Hat) if (!parent) 256*4282d606SSteven Rostedt (Red Hat) parent = tracefs_mount->mnt_root; 257*4282d606SSteven Rostedt (Red Hat) 258*4282d606SSteven Rostedt (Red Hat) mutex_lock(&parent->d_inode->i_mutex); 259*4282d606SSteven Rostedt (Red Hat) dentry = lookup_one_len(name, parent, strlen(name)); 260*4282d606SSteven Rostedt (Red Hat) if (!IS_ERR(dentry) && dentry->d_inode) { 261*4282d606SSteven Rostedt (Red Hat) dput(dentry); 262*4282d606SSteven Rostedt (Red Hat) dentry = ERR_PTR(-EEXIST); 263*4282d606SSteven Rostedt (Red Hat) } 264*4282d606SSteven Rostedt (Red Hat) if (IS_ERR(dentry)) 265*4282d606SSteven Rostedt (Red Hat) mutex_unlock(&parent->d_inode->i_mutex); 266*4282d606SSteven Rostedt (Red Hat) return dentry; 267*4282d606SSteven Rostedt (Red Hat) } 268*4282d606SSteven Rostedt (Red Hat) 269*4282d606SSteven Rostedt (Red Hat) static struct dentry *failed_creating(struct dentry *dentry) 270*4282d606SSteven Rostedt (Red Hat) { 271*4282d606SSteven Rostedt (Red Hat) mutex_unlock(&dentry->d_parent->d_inode->i_mutex); 272*4282d606SSteven Rostedt (Red Hat) dput(dentry); 273*4282d606SSteven Rostedt (Red Hat) simple_release_fs(&tracefs_mount, &tracefs_mount_count); 274*4282d606SSteven Rostedt (Red Hat) return NULL; 275*4282d606SSteven Rostedt (Red Hat) } 276*4282d606SSteven Rostedt (Red Hat) 277*4282d606SSteven Rostedt (Red Hat) static struct dentry *end_creating(struct dentry *dentry) 278*4282d606SSteven Rostedt (Red Hat) { 279*4282d606SSteven Rostedt (Red Hat) mutex_unlock(&dentry->d_parent->d_inode->i_mutex); 280*4282d606SSteven Rostedt (Red Hat) return dentry; 281*4282d606SSteven Rostedt (Red Hat) } 282*4282d606SSteven Rostedt (Red Hat) 283*4282d606SSteven Rostedt (Red Hat) /** 284*4282d606SSteven Rostedt (Red Hat) * tracefs_create_file - create a file in the tracefs filesystem 285*4282d606SSteven Rostedt (Red Hat) * @name: a pointer to a string containing the name of the file to create. 286*4282d606SSteven Rostedt (Red Hat) * @mode: the permission that the file should have. 287*4282d606SSteven Rostedt (Red Hat) * @parent: a pointer to the parent dentry for this file. This should be a 288*4282d606SSteven Rostedt (Red Hat) * directory dentry if set. If this parameter is NULL, then the 289*4282d606SSteven Rostedt (Red Hat) * file will be created in the root of the tracefs filesystem. 290*4282d606SSteven Rostedt (Red Hat) * @data: a pointer to something that the caller will want to get to later 291*4282d606SSteven Rostedt (Red Hat) * on. The inode.i_private pointer will point to this value on 292*4282d606SSteven Rostedt (Red Hat) * the open() call. 293*4282d606SSteven Rostedt (Red Hat) * @fops: a pointer to a struct file_operations that should be used for 294*4282d606SSteven Rostedt (Red Hat) * this file. 295*4282d606SSteven Rostedt (Red Hat) * 296*4282d606SSteven Rostedt (Red Hat) * This is the basic "create a file" function for tracefs. It allows for a 297*4282d606SSteven Rostedt (Red Hat) * wide range of flexibility in creating a file, or a directory (if you want 298*4282d606SSteven Rostedt (Red Hat) * to create a directory, the tracefs_create_dir() function is 299*4282d606SSteven Rostedt (Red Hat) * recommended to be used instead.) 300*4282d606SSteven Rostedt (Red Hat) * 301*4282d606SSteven Rostedt (Red Hat) * This function will return a pointer to a dentry if it succeeds. This 302*4282d606SSteven Rostedt (Red Hat) * pointer must be passed to the tracefs_remove() function when the file is 303*4282d606SSteven Rostedt (Red Hat) * to be removed (no automatic cleanup happens if your module is unloaded, 304*4282d606SSteven Rostedt (Red Hat) * you are responsible here.) If an error occurs, %NULL will be returned. 305*4282d606SSteven Rostedt (Red Hat) * 306*4282d606SSteven Rostedt (Red Hat) * If tracefs is not enabled in the kernel, the value -%ENODEV will be 307*4282d606SSteven Rostedt (Red Hat) * returned. 308*4282d606SSteven Rostedt (Red Hat) */ 309*4282d606SSteven Rostedt (Red Hat) struct dentry *tracefs_create_file(const char *name, umode_t mode, 310*4282d606SSteven Rostedt (Red Hat) struct dentry *parent, void *data, 311*4282d606SSteven Rostedt (Red Hat) const struct file_operations *fops) 312*4282d606SSteven Rostedt (Red Hat) { 313*4282d606SSteven Rostedt (Red Hat) struct dentry *dentry; 314*4282d606SSteven Rostedt (Red Hat) struct inode *inode; 315*4282d606SSteven Rostedt (Red Hat) 316*4282d606SSteven Rostedt (Red Hat) if (!(mode & S_IFMT)) 317*4282d606SSteven Rostedt (Red Hat) mode |= S_IFREG; 318*4282d606SSteven Rostedt (Red Hat) BUG_ON(!S_ISREG(mode)); 319*4282d606SSteven Rostedt (Red Hat) dentry = start_creating(name, parent); 320*4282d606SSteven Rostedt (Red Hat) 321*4282d606SSteven Rostedt (Red Hat) if (IS_ERR(dentry)) 322*4282d606SSteven Rostedt (Red Hat) return NULL; 323*4282d606SSteven Rostedt (Red Hat) 324*4282d606SSteven Rostedt (Red Hat) inode = tracefs_get_inode(dentry->d_sb); 325*4282d606SSteven Rostedt (Red Hat) if (unlikely(!inode)) 326*4282d606SSteven Rostedt (Red Hat) return failed_creating(dentry); 327*4282d606SSteven Rostedt (Red Hat) 328*4282d606SSteven Rostedt (Red Hat) inode->i_mode = mode; 329*4282d606SSteven Rostedt (Red Hat) inode->i_fop = fops ? fops : &tracefs_file_operations; 330*4282d606SSteven Rostedt (Red Hat) inode->i_private = data; 331*4282d606SSteven Rostedt (Red Hat) d_instantiate(dentry, inode); 332*4282d606SSteven Rostedt (Red Hat) fsnotify_create(dentry->d_parent->d_inode, dentry); 333*4282d606SSteven Rostedt (Red Hat) return end_creating(dentry); 334*4282d606SSteven Rostedt (Red Hat) } 335*4282d606SSteven Rostedt (Red Hat) 336*4282d606SSteven Rostedt (Red Hat) /** 337*4282d606SSteven Rostedt (Red Hat) * tracefs_create_dir - create a directory in the tracefs filesystem 338*4282d606SSteven Rostedt (Red Hat) * @name: a pointer to a string containing the name of the directory to 339*4282d606SSteven Rostedt (Red Hat) * create. 340*4282d606SSteven Rostedt (Red Hat) * @parent: a pointer to the parent dentry for this file. This should be a 341*4282d606SSteven Rostedt (Red Hat) * directory dentry if set. If this parameter is NULL, then the 342*4282d606SSteven Rostedt (Red Hat) * directory will be created in the root of the tracefs filesystem. 343*4282d606SSteven Rostedt (Red Hat) * 344*4282d606SSteven Rostedt (Red Hat) * This function creates a directory in tracefs with the given name. 345*4282d606SSteven Rostedt (Red Hat) * 346*4282d606SSteven Rostedt (Red Hat) * This function will return a pointer to a dentry if it succeeds. This 347*4282d606SSteven Rostedt (Red Hat) * pointer must be passed to the tracefs_remove() function when the file is 348*4282d606SSteven Rostedt (Red Hat) * to be removed. If an error occurs, %NULL will be returned. 349*4282d606SSteven Rostedt (Red Hat) * 350*4282d606SSteven Rostedt (Red Hat) * If tracing is not enabled in the kernel, the value -%ENODEV will be 351*4282d606SSteven Rostedt (Red Hat) * returned. 352*4282d606SSteven Rostedt (Red Hat) */ 353*4282d606SSteven Rostedt (Red Hat) struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) 354*4282d606SSteven Rostedt (Red Hat) { 355*4282d606SSteven Rostedt (Red Hat) struct dentry *dentry = start_creating(name, parent); 356*4282d606SSteven Rostedt (Red Hat) struct inode *inode; 357*4282d606SSteven Rostedt (Red Hat) 358*4282d606SSteven Rostedt (Red Hat) if (IS_ERR(dentry)) 359*4282d606SSteven Rostedt (Red Hat) return NULL; 360*4282d606SSteven Rostedt (Red Hat) 361*4282d606SSteven Rostedt (Red Hat) inode = tracefs_get_inode(dentry->d_sb); 362*4282d606SSteven Rostedt (Red Hat) if (unlikely(!inode)) 363*4282d606SSteven Rostedt (Red Hat) return failed_creating(dentry); 364*4282d606SSteven Rostedt (Red Hat) 365*4282d606SSteven Rostedt (Red Hat) inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; 366*4282d606SSteven Rostedt (Red Hat) inode->i_op = &simple_dir_inode_operations; 367*4282d606SSteven Rostedt (Red Hat) inode->i_fop = &simple_dir_operations; 368*4282d606SSteven Rostedt (Red Hat) 369*4282d606SSteven Rostedt (Red Hat) /* directory inodes start off with i_nlink == 2 (for "." entry) */ 370*4282d606SSteven Rostedt (Red Hat) inc_nlink(inode); 371*4282d606SSteven Rostedt (Red Hat) d_instantiate(dentry, inode); 372*4282d606SSteven Rostedt (Red Hat) inc_nlink(dentry->d_parent->d_inode); 373*4282d606SSteven Rostedt (Red Hat) fsnotify_mkdir(dentry->d_parent->d_inode, dentry); 374*4282d606SSteven Rostedt (Red Hat) return end_creating(dentry); 375*4282d606SSteven Rostedt (Red Hat) } 376*4282d606SSteven Rostedt (Red Hat) 377*4282d606SSteven Rostedt (Red Hat) static inline int tracefs_positive(struct dentry *dentry) 378*4282d606SSteven Rostedt (Red Hat) { 379*4282d606SSteven Rostedt (Red Hat) return dentry->d_inode && !d_unhashed(dentry); 380*4282d606SSteven Rostedt (Red Hat) } 381*4282d606SSteven Rostedt (Red Hat) 382*4282d606SSteven Rostedt (Red Hat) static int __tracefs_remove(struct dentry *dentry, struct dentry *parent) 383*4282d606SSteven Rostedt (Red Hat) { 384*4282d606SSteven Rostedt (Red Hat) int ret = 0; 385*4282d606SSteven Rostedt (Red Hat) 386*4282d606SSteven Rostedt (Red Hat) if (tracefs_positive(dentry)) { 387*4282d606SSteven Rostedt (Red Hat) if (dentry->d_inode) { 388*4282d606SSteven Rostedt (Red Hat) dget(dentry); 389*4282d606SSteven Rostedt (Red Hat) switch (dentry->d_inode->i_mode & S_IFMT) { 390*4282d606SSteven Rostedt (Red Hat) case S_IFDIR: 391*4282d606SSteven Rostedt (Red Hat) ret = simple_rmdir(parent->d_inode, dentry); 392*4282d606SSteven Rostedt (Red Hat) break; 393*4282d606SSteven Rostedt (Red Hat) default: 394*4282d606SSteven Rostedt (Red Hat) simple_unlink(parent->d_inode, dentry); 395*4282d606SSteven Rostedt (Red Hat) break; 396*4282d606SSteven Rostedt (Red Hat) } 397*4282d606SSteven Rostedt (Red Hat) if (!ret) 398*4282d606SSteven Rostedt (Red Hat) d_delete(dentry); 399*4282d606SSteven Rostedt (Red Hat) dput(dentry); 400*4282d606SSteven Rostedt (Red Hat) } 401*4282d606SSteven Rostedt (Red Hat) } 402*4282d606SSteven Rostedt (Red Hat) return ret; 403*4282d606SSteven Rostedt (Red Hat) } 404*4282d606SSteven Rostedt (Red Hat) 405*4282d606SSteven Rostedt (Red Hat) /** 406*4282d606SSteven Rostedt (Red Hat) * tracefs_remove - removes a file or directory from the tracefs filesystem 407*4282d606SSteven Rostedt (Red Hat) * @dentry: a pointer to a the dentry of the file or directory to be 408*4282d606SSteven Rostedt (Red Hat) * removed. 409*4282d606SSteven Rostedt (Red Hat) * 410*4282d606SSteven Rostedt (Red Hat) * This function removes a file or directory in tracefs that was previously 411*4282d606SSteven Rostedt (Red Hat) * created with a call to another tracefs function (like 412*4282d606SSteven Rostedt (Red Hat) * tracefs_create_file() or variants thereof.) 413*4282d606SSteven Rostedt (Red Hat) */ 414*4282d606SSteven Rostedt (Red Hat) void tracefs_remove(struct dentry *dentry) 415*4282d606SSteven Rostedt (Red Hat) { 416*4282d606SSteven Rostedt (Red Hat) struct dentry *parent; 417*4282d606SSteven Rostedt (Red Hat) int ret; 418*4282d606SSteven Rostedt (Red Hat) 419*4282d606SSteven Rostedt (Red Hat) if (IS_ERR_OR_NULL(dentry)) 420*4282d606SSteven Rostedt (Red Hat) return; 421*4282d606SSteven Rostedt (Red Hat) 422*4282d606SSteven Rostedt (Red Hat) parent = dentry->d_parent; 423*4282d606SSteven Rostedt (Red Hat) if (!parent || !parent->d_inode) 424*4282d606SSteven Rostedt (Red Hat) return; 425*4282d606SSteven Rostedt (Red Hat) 426*4282d606SSteven Rostedt (Red Hat) mutex_lock(&parent->d_inode->i_mutex); 427*4282d606SSteven Rostedt (Red Hat) ret = __tracefs_remove(dentry, parent); 428*4282d606SSteven Rostedt (Red Hat) mutex_unlock(&parent->d_inode->i_mutex); 429*4282d606SSteven Rostedt (Red Hat) if (!ret) 430*4282d606SSteven Rostedt (Red Hat) simple_release_fs(&tracefs_mount, &tracefs_mount_count); 431*4282d606SSteven Rostedt (Red Hat) } 432*4282d606SSteven Rostedt (Red Hat) 433*4282d606SSteven Rostedt (Red Hat) /** 434*4282d606SSteven Rostedt (Red Hat) * tracefs_remove_recursive - recursively removes a directory 435*4282d606SSteven Rostedt (Red Hat) * @dentry: a pointer to a the dentry of the directory to be removed. 436*4282d606SSteven Rostedt (Red Hat) * 437*4282d606SSteven Rostedt (Red Hat) * This function recursively removes a directory tree in tracefs that 438*4282d606SSteven Rostedt (Red Hat) * was previously created with a call to another tracefs function 439*4282d606SSteven Rostedt (Red Hat) * (like tracefs_create_file() or variants thereof.) 440*4282d606SSteven Rostedt (Red Hat) */ 441*4282d606SSteven Rostedt (Red Hat) void tracefs_remove_recursive(struct dentry *dentry) 442*4282d606SSteven Rostedt (Red Hat) { 443*4282d606SSteven Rostedt (Red Hat) struct dentry *child, *parent; 444*4282d606SSteven Rostedt (Red Hat) 445*4282d606SSteven Rostedt (Red Hat) if (IS_ERR_OR_NULL(dentry)) 446*4282d606SSteven Rostedt (Red Hat) return; 447*4282d606SSteven Rostedt (Red Hat) 448*4282d606SSteven Rostedt (Red Hat) parent = dentry->d_parent; 449*4282d606SSteven Rostedt (Red Hat) if (!parent || !parent->d_inode) 450*4282d606SSteven Rostedt (Red Hat) return; 451*4282d606SSteven Rostedt (Red Hat) 452*4282d606SSteven Rostedt (Red Hat) parent = dentry; 453*4282d606SSteven Rostedt (Red Hat) down: 454*4282d606SSteven Rostedt (Red Hat) mutex_lock(&parent->d_inode->i_mutex); 455*4282d606SSteven Rostedt (Red Hat) loop: 456*4282d606SSteven Rostedt (Red Hat) /* 457*4282d606SSteven Rostedt (Red Hat) * The parent->d_subdirs is protected by the d_lock. Outside that 458*4282d606SSteven Rostedt (Red Hat) * lock, the child can be unlinked and set to be freed which can 459*4282d606SSteven Rostedt (Red Hat) * use the d_u.d_child as the rcu head and corrupt this list. 460*4282d606SSteven Rostedt (Red Hat) */ 461*4282d606SSteven Rostedt (Red Hat) spin_lock(&parent->d_lock); 462*4282d606SSteven Rostedt (Red Hat) list_for_each_entry(child, &parent->d_subdirs, d_child) { 463*4282d606SSteven Rostedt (Red Hat) if (!tracefs_positive(child)) 464*4282d606SSteven Rostedt (Red Hat) continue; 465*4282d606SSteven Rostedt (Red Hat) 466*4282d606SSteven Rostedt (Red Hat) /* perhaps simple_empty(child) makes more sense */ 467*4282d606SSteven Rostedt (Red Hat) if (!list_empty(&child->d_subdirs)) { 468*4282d606SSteven Rostedt (Red Hat) spin_unlock(&parent->d_lock); 469*4282d606SSteven Rostedt (Red Hat) mutex_unlock(&parent->d_inode->i_mutex); 470*4282d606SSteven Rostedt (Red Hat) parent = child; 471*4282d606SSteven Rostedt (Red Hat) goto down; 472*4282d606SSteven Rostedt (Red Hat) } 473*4282d606SSteven Rostedt (Red Hat) 474*4282d606SSteven Rostedt (Red Hat) spin_unlock(&parent->d_lock); 475*4282d606SSteven Rostedt (Red Hat) 476*4282d606SSteven Rostedt (Red Hat) if (!__tracefs_remove(child, parent)) 477*4282d606SSteven Rostedt (Red Hat) simple_release_fs(&tracefs_mount, &tracefs_mount_count); 478*4282d606SSteven Rostedt (Red Hat) 479*4282d606SSteven Rostedt (Red Hat) /* 480*4282d606SSteven Rostedt (Red Hat) * The parent->d_lock protects agaist child from unlinking 481*4282d606SSteven Rostedt (Red Hat) * from d_subdirs. When releasing the parent->d_lock we can 482*4282d606SSteven Rostedt (Red Hat) * no longer trust that the next pointer is valid. 483*4282d606SSteven Rostedt (Red Hat) * Restart the loop. We'll skip this one with the 484*4282d606SSteven Rostedt (Red Hat) * tracefs_positive() check. 485*4282d606SSteven Rostedt (Red Hat) */ 486*4282d606SSteven Rostedt (Red Hat) goto loop; 487*4282d606SSteven Rostedt (Red Hat) } 488*4282d606SSteven Rostedt (Red Hat) spin_unlock(&parent->d_lock); 489*4282d606SSteven Rostedt (Red Hat) 490*4282d606SSteven Rostedt (Red Hat) mutex_unlock(&parent->d_inode->i_mutex); 491*4282d606SSteven Rostedt (Red Hat) child = parent; 492*4282d606SSteven Rostedt (Red Hat) parent = parent->d_parent; 493*4282d606SSteven Rostedt (Red Hat) mutex_lock(&parent->d_inode->i_mutex); 494*4282d606SSteven Rostedt (Red Hat) 495*4282d606SSteven Rostedt (Red Hat) if (child != dentry) 496*4282d606SSteven Rostedt (Red Hat) /* go up */ 497*4282d606SSteven Rostedt (Red Hat) goto loop; 498*4282d606SSteven Rostedt (Red Hat) 499*4282d606SSteven Rostedt (Red Hat) if (!__tracefs_remove(child, parent)) 500*4282d606SSteven Rostedt (Red Hat) simple_release_fs(&tracefs_mount, &tracefs_mount_count); 501*4282d606SSteven Rostedt (Red Hat) mutex_unlock(&parent->d_inode->i_mutex); 502*4282d606SSteven Rostedt (Red Hat) } 503*4282d606SSteven Rostedt (Red Hat) 504*4282d606SSteven Rostedt (Red Hat) /** 505*4282d606SSteven Rostedt (Red Hat) * tracefs_initialized - Tells whether tracefs has been registered 506*4282d606SSteven Rostedt (Red Hat) */ 507*4282d606SSteven Rostedt (Red Hat) bool tracefs_initialized(void) 508*4282d606SSteven Rostedt (Red Hat) { 509*4282d606SSteven Rostedt (Red Hat) return tracefs_registered; 510*4282d606SSteven Rostedt (Red Hat) } 511*4282d606SSteven Rostedt (Red Hat) 512*4282d606SSteven Rostedt (Red Hat) static int __init tracefs_init(void) 513*4282d606SSteven Rostedt (Red Hat) { 514*4282d606SSteven Rostedt (Red Hat) int retval; 515*4282d606SSteven Rostedt (Red Hat) 516*4282d606SSteven Rostedt (Red Hat) retval = register_filesystem(&trace_fs_type); 517*4282d606SSteven Rostedt (Red Hat) if (!retval) 518*4282d606SSteven Rostedt (Red Hat) tracefs_registered = true; 519*4282d606SSteven Rostedt (Red Hat) 520*4282d606SSteven Rostedt (Red Hat) return retval; 521*4282d606SSteven Rostedt (Red Hat) } 522*4282d606SSteven Rostedt (Red Hat) core_initcall(tracefs_init); 523