linux/drivers/base/devtmpfs.c
<<
>>
Prefs
   1/*
   2 * devtmpfs - kernel-maintained tmpfs-based /dev
   3 *
   4 * Copyright (C) 2009, Kay Sievers <kay.sievers@vrfy.org>
   5 *
   6 * During bootup, before any driver core device is registered,
   7 * devtmpfs, a tmpfs-based filesystem is created. Every driver-core
   8 * device which requests a device node, will add a node in this
   9 * filesystem.
  10 * By default, all devices are named after the the name of the
  11 * device, owned by root and have a default mode of 0600. Subsystems
  12 * can overwrite the default setting if needed.
  13 */
  14
  15#include <linux/kernel.h>
  16#include <linux/syscalls.h>
  17#include <linux/mount.h>
  18#include <linux/device.h>
  19#include <linux/genhd.h>
  20#include <linux/namei.h>
  21#include <linux/fs.h>
  22#include <linux/shmem_fs.h>
  23#include <linux/cred.h>
  24#include <linux/sched.h>
  25#include <linux/init_task.h>
  26
  27static struct vfsmount *dev_mnt;
  28
  29#if defined CONFIG_DEVTMPFS_MOUNT
  30static int dev_mount = 1;
  31#else
  32static int dev_mount;
  33#endif
  34
  35static int __init mount_param(char *str)
  36{
  37        dev_mount = simple_strtoul(str, NULL, 0);
  38        return 1;
  39}
  40__setup("devtmpfs.mount=", mount_param);
  41
  42static int dev_get_sb(struct file_system_type *fs_type, int flags,
  43                      const char *dev_name, void *data, struct vfsmount *mnt)
  44{
  45        return get_sb_single(fs_type, flags, data, shmem_fill_super, mnt);
  46}
  47
  48static struct file_system_type dev_fs_type = {
  49        .name = "devtmpfs",
  50        .get_sb = dev_get_sb,
  51        .kill_sb = kill_litter_super,
  52};
  53
  54#ifdef CONFIG_BLOCK
  55static inline int is_blockdev(struct device *dev)
  56{
  57        return dev->class == &block_class;
  58}
  59#else
  60static inline int is_blockdev(struct device *dev) { return 0; }
  61#endif
  62
  63static int dev_mkdir(const char *name, mode_t mode)
  64{
  65        struct nameidata nd;
  66        struct dentry *dentry;
  67        int err;
  68
  69        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
  70                              name, LOOKUP_PARENT, &nd);
  71        if (err)
  72                return err;
  73
  74        dentry = lookup_create(&nd, 1);
  75        if (!IS_ERR(dentry)) {
  76                err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
  77                dput(dentry);
  78        } else {
  79                err = PTR_ERR(dentry);
  80        }
  81        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
  82
  83        path_put(&nd.path);
  84        return err;
  85}
  86
  87static int create_path(const char *nodepath)
  88{
  89        char *path;
  90        struct nameidata nd;
  91        int err = 0;
  92
  93        path = kstrdup(nodepath, GFP_KERNEL);
  94        if (!path)
  95                return -ENOMEM;
  96
  97        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
  98                              path, LOOKUP_PARENT, &nd);
  99        if (err == 0) {
 100                struct dentry *dentry;
 101
 102                /* create directory right away */
 103                dentry = lookup_create(&nd, 1);
 104                if (!IS_ERR(dentry)) {
 105                        err = vfs_mkdir(nd.path.dentry->d_inode,
 106                                        dentry, 0755);
 107                        dput(dentry);
 108                }
 109                mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 110
 111                path_put(&nd.path);
 112        } else if (err == -ENOENT) {
 113                char *s;
 114
 115                /* parent directories do not exist, create them */
 116                s = path;
 117                while (1) {
 118                        s = strchr(s, '/');
 119                        if (!s)
 120                                break;
 121                        s[0] = '\0';
 122                        err = dev_mkdir(path, 0755);
 123                        if (err && err != -EEXIST)
 124                                break;
 125                        s[0] = '/';
 126                        s++;
 127                }
 128        }
 129
 130        kfree(path);
 131        return err;
 132}
 133
 134int devtmpfs_create_node(struct device *dev)
 135{
 136        const char *tmp = NULL;
 137        const char *nodename;
 138        const struct cred *curr_cred;
 139        mode_t mode = 0;
 140        struct nameidata nd;
 141        struct dentry *dentry;
 142        int err;
 143
 144        if (!dev_mnt)
 145                return 0;
 146
 147        nodename = device_get_devnode(dev, &mode, &tmp);
 148        if (!nodename)
 149                return -ENOMEM;
 150
 151        if (mode == 0)
 152                mode = 0600;
 153        if (is_blockdev(dev))
 154                mode |= S_IFBLK;
 155        else
 156                mode |= S_IFCHR;
 157
 158        curr_cred = override_creds(&init_cred);
 159        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 160                              nodename, LOOKUP_PARENT, &nd);
 161        if (err == -ENOENT) {
 162                /* create missing parent directories */
 163                create_path(nodename);
 164                err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 165                                      nodename, LOOKUP_PARENT, &nd);
 166                if (err)
 167                        goto out;
 168        }
 169
 170        dentry = lookup_create(&nd, 0);
 171        if (!IS_ERR(dentry)) {
 172                int umask;
 173
 174                umask = sys_umask(0000);
 175                err = vfs_mknod(nd.path.dentry->d_inode,
 176                                dentry, mode, dev->devt);
 177                sys_umask(umask);
 178                /* mark as kernel created inode */
 179                if (!err)
 180                        dentry->d_inode->i_private = &dev_mnt;
 181                dput(dentry);
 182        } else {
 183                err = PTR_ERR(dentry);
 184        }
 185        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 186
 187        path_put(&nd.path);
 188out:
 189        kfree(tmp);
 190        revert_creds(curr_cred);
 191        return err;
 192}
 193
 194static int dev_rmdir(const char *name)
 195{
 196        struct nameidata nd;
 197        struct dentry *dentry;
 198        int err;
 199
 200        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 201                              name, LOOKUP_PARENT, &nd);
 202        if (err)
 203                return err;
 204
 205        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
 206        dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
 207        if (!IS_ERR(dentry)) {
 208                if (dentry->d_inode)
 209                        err = vfs_rmdir(nd.path.dentry->d_inode, dentry);
 210                else
 211                        err = -ENOENT;
 212                dput(dentry);
 213        } else {
 214                err = PTR_ERR(dentry);
 215        }
 216        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 217
 218        path_put(&nd.path);
 219        return err;
 220}
 221
 222static int delete_path(const char *nodepath)
 223{
 224        const char *path;
 225        int err = 0;
 226
 227        path = kstrdup(nodepath, GFP_KERNEL);
 228        if (!path)
 229                return -ENOMEM;
 230
 231        while (1) {
 232                char *base;
 233
 234                base = strrchr(path, '/');
 235                if (!base)
 236                        break;
 237                base[0] = '\0';
 238                err = dev_rmdir(path);
 239                if (err)
 240                        break;
 241        }
 242
 243        kfree(path);
 244        return err;
 245}
 246
 247static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
 248{
 249        /* did we create it */
 250        if (inode->i_private != &dev_mnt)
 251                return 0;
 252
 253        /* does the dev_t match */
 254        if (is_blockdev(dev)) {
 255                if (!S_ISBLK(stat->mode))
 256                        return 0;
 257        } else {
 258                if (!S_ISCHR(stat->mode))
 259                        return 0;
 260        }
 261        if (stat->rdev != dev->devt)
 262                return 0;
 263
 264        /* ours */
 265        return 1;
 266}
 267
 268int devtmpfs_delete_node(struct device *dev)
 269{
 270        const char *tmp = NULL;
 271        const char *nodename;
 272        const struct cred *curr_cred;
 273        struct nameidata nd;
 274        struct dentry *dentry;
 275        struct kstat stat;
 276        int deleted = 1;
 277        int err;
 278
 279        if (!dev_mnt)
 280                return 0;
 281
 282        nodename = device_get_devnode(dev, NULL, &tmp);
 283        if (!nodename)
 284                return -ENOMEM;
 285
 286        curr_cred = override_creds(&init_cred);
 287        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 288                              nodename, LOOKUP_PARENT, &nd);
 289        if (err)
 290                goto out;
 291
 292        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
 293        dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
 294        if (!IS_ERR(dentry)) {
 295                if (dentry->d_inode) {
 296                        err = vfs_getattr(nd.path.mnt, dentry, &stat);
 297                        if (!err && dev_mynode(dev, dentry->d_inode, &stat)) {
 298                                err = vfs_unlink(nd.path.dentry->d_inode,
 299                                                 dentry);
 300                                if (!err || err == -ENOENT)
 301                                        deleted = 1;
 302                        }
 303                } else {
 304                        err = -ENOENT;
 305                }
 306                dput(dentry);
 307        } else {
 308                err = PTR_ERR(dentry);
 309        }
 310        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 311
 312        path_put(&nd.path);
 313        if (deleted && strchr(nodename, '/'))
 314                delete_path(nodename);
 315out:
 316        kfree(tmp);
 317        revert_creds(curr_cred);
 318        return err;
 319}
 320
 321/*
 322 * If configured, or requested by the commandline, devtmpfs will be
 323 * auto-mounted after the kernel mounted the root filesystem.
 324 */
 325int devtmpfs_mount(const char *mountpoint)
 326{
 327        struct path path;
 328        int err;
 329
 330        if (!dev_mount)
 331                return 0;
 332
 333        if (!dev_mnt)
 334                return 0;
 335
 336        err = kern_path(mountpoint, LOOKUP_FOLLOW, &path);
 337        if (err)
 338                return err;
 339        err = do_add_mount(dev_mnt, &path, 0, NULL);
 340        if (err)
 341                printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
 342        else
 343                printk(KERN_INFO "devtmpfs: mounted\n");
 344        path_put(&path);
 345        return err;
 346}
 347
 348/*
 349 * Create devtmpfs instance, driver-core devices will add their device
 350 * nodes here.
 351 */
 352int __init devtmpfs_init(void)
 353{
 354        int err;
 355        struct vfsmount *mnt;
 356
 357        err = register_filesystem(&dev_fs_type);
 358        if (err) {
 359                printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
 360                       "type %i\n", err);
 361                return err;
 362        }
 363
 364        mnt = kern_mount(&dev_fs_type);
 365        if (IS_ERR(mnt)) {
 366                err = PTR_ERR(mnt);
 367                printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
 368                unregister_filesystem(&dev_fs_type);
 369                return err;
 370        }
 371        dev_mnt = mnt;
 372
 373        printk(KERN_INFO "devtmpfs: initialized\n");
 374        return 0;
 375}
 376