xref: /linux-6.15/drivers/base/devtmpfs.c (revision fc14f2fe)
12b2af54aSKay Sievers /*
22b2af54aSKay Sievers  * devtmpfs - kernel-maintained tmpfs-based /dev
32b2af54aSKay Sievers  *
42b2af54aSKay Sievers  * Copyright (C) 2009, Kay Sievers <[email protected]>
52b2af54aSKay Sievers  *
62b2af54aSKay Sievers  * During bootup, before any driver core device is registered,
72b2af54aSKay Sievers  * devtmpfs, a tmpfs-based filesystem is created. Every driver-core
82b2af54aSKay Sievers  * device which requests a device node, will add a node in this
9e454cea2SKay Sievers  * filesystem.
10e454cea2SKay Sievers  * By default, all devices are named after the the name of the
11e454cea2SKay Sievers  * device, owned by root and have a default mode of 0600. Subsystems
12e454cea2SKay Sievers  * can overwrite the default setting if needed.
132b2af54aSKay Sievers  */
142b2af54aSKay Sievers 
152b2af54aSKay Sievers #include <linux/kernel.h>
162b2af54aSKay Sievers #include <linux/syscalls.h>
172b2af54aSKay Sievers #include <linux/mount.h>
182b2af54aSKay Sievers #include <linux/device.h>
192b2af54aSKay Sievers #include <linux/genhd.h>
202b2af54aSKay Sievers #include <linux/namei.h>
212b2af54aSKay Sievers #include <linux/fs.h>
222b2af54aSKay Sievers #include <linux/shmem_fs.h>
23da5e4ef7SPeter Korsgaard #include <linux/ramfs.h>
242b2af54aSKay Sievers #include <linux/cred.h>
25e454cea2SKay Sievers #include <linux/sched.h>
262b2af54aSKay Sievers #include <linux/init_task.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
282b2af54aSKay Sievers 
292b2af54aSKay Sievers static struct vfsmount *dev_mnt;
302b2af54aSKay Sievers 
312b2af54aSKay Sievers #if defined CONFIG_DEVTMPFS_MOUNT
32*fc14f2feSAl Viro static int mount_dev = 1;
332b2af54aSKay Sievers #else
34*fc14f2feSAl Viro static int mount_dev;
352b2af54aSKay Sievers #endif
362b2af54aSKay Sievers 
37f1f76f86SThomas Gleixner static DEFINE_MUTEX(dirlock);
38ed413ae6SKay Sievers 
392b2af54aSKay Sievers static int __init mount_param(char *str)
402b2af54aSKay Sievers {
41*fc14f2feSAl Viro 	mount_dev = simple_strtoul(str, NULL, 0);
422b2af54aSKay Sievers 	return 1;
432b2af54aSKay Sievers }
442b2af54aSKay Sievers __setup("devtmpfs.mount=", mount_param);
452b2af54aSKay Sievers 
46*fc14f2feSAl Viro static struct dentry *dev_mount(struct file_system_type *fs_type, int flags,
47*fc14f2feSAl Viro 		      const char *dev_name, void *data)
482b2af54aSKay Sievers {
49da5e4ef7SPeter Korsgaard #ifdef CONFIG_TMPFS
50*fc14f2feSAl Viro 	return mount_single(fs_type, flags, data, shmem_fill_super);
51da5e4ef7SPeter Korsgaard #else
52*fc14f2feSAl Viro 	return mount_single(fs_type, flags, data, ramfs_fill_super);
53da5e4ef7SPeter Korsgaard #endif
542b2af54aSKay Sievers }
552b2af54aSKay Sievers 
562b2af54aSKay Sievers static struct file_system_type dev_fs_type = {
572b2af54aSKay Sievers 	.name = "devtmpfs",
58*fc14f2feSAl Viro 	.mount = dev_mount,
592b2af54aSKay Sievers 	.kill_sb = kill_litter_super,
602b2af54aSKay Sievers };
612b2af54aSKay Sievers 
622b2af54aSKay Sievers #ifdef CONFIG_BLOCK
632b2af54aSKay Sievers static inline int is_blockdev(struct device *dev)
642b2af54aSKay Sievers {
652b2af54aSKay Sievers 	return dev->class == &block_class;
662b2af54aSKay Sievers }
672b2af54aSKay Sievers #else
682b2af54aSKay Sievers static inline int is_blockdev(struct device *dev) { return 0; }
692b2af54aSKay Sievers #endif
702b2af54aSKay Sievers 
712b2af54aSKay Sievers static int dev_mkdir(const char *name, mode_t mode)
722b2af54aSKay Sievers {
732b2af54aSKay Sievers 	struct nameidata nd;
742b2af54aSKay Sievers 	struct dentry *dentry;
752b2af54aSKay Sievers 	int err;
762b2af54aSKay Sievers 
772b2af54aSKay Sievers 	err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
782b2af54aSKay Sievers 			      name, LOOKUP_PARENT, &nd);
792b2af54aSKay Sievers 	if (err)
802b2af54aSKay Sievers 		return err;
812b2af54aSKay Sievers 
822b2af54aSKay Sievers 	dentry = lookup_create(&nd, 1);
832b2af54aSKay Sievers 	if (!IS_ERR(dentry)) {
842b2af54aSKay Sievers 		err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
85015bf43bSKay Sievers 		if (!err)
86015bf43bSKay Sievers 			/* mark as kernel-created inode */
87015bf43bSKay Sievers 			dentry->d_inode->i_private = &dev_mnt;
882b2af54aSKay Sievers 		dput(dentry);
892b2af54aSKay Sievers 	} else {
902b2af54aSKay Sievers 		err = PTR_ERR(dentry);
912b2af54aSKay Sievers 	}
922b2af54aSKay Sievers 
93015bf43bSKay Sievers 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
942b2af54aSKay Sievers 	path_put(&nd.path);
952b2af54aSKay Sievers 	return err;
962b2af54aSKay Sievers }
972b2af54aSKay Sievers 
982b2af54aSKay Sievers static int create_path(const char *nodepath)
992b2af54aSKay Sievers {
100015bf43bSKay Sievers 	int err;
1012b2af54aSKay Sievers 
102f1f76f86SThomas Gleixner 	mutex_lock(&dirlock);
103015bf43bSKay Sievers 	err = dev_mkdir(nodepath, 0755);
104015bf43bSKay Sievers 	if (err == -ENOENT) {
105ed413ae6SKay Sievers 		char *path;
1062b2af54aSKay Sievers 		char *s;
1072b2af54aSKay Sievers 
1082b2af54aSKay Sievers 		/* parent directories do not exist, create them */
109ed413ae6SKay Sievers 		path = kstrdup(nodepath, GFP_KERNEL);
11080422738SKay Sievers 		if (!path) {
11180422738SKay Sievers 			err = -ENOMEM;
11280422738SKay Sievers 			goto out;
11380422738SKay Sievers 		}
1142b2af54aSKay Sievers 		s = path;
115ed413ae6SKay Sievers 		for (;;) {
1162b2af54aSKay Sievers 			s = strchr(s, '/');
1172b2af54aSKay Sievers 			if (!s)
1182b2af54aSKay Sievers 				break;
1192b2af54aSKay Sievers 			s[0] = '\0';
1202b2af54aSKay Sievers 			err = dev_mkdir(path, 0755);
1212b2af54aSKay Sievers 			if (err && err != -EEXIST)
1222b2af54aSKay Sievers 				break;
1232b2af54aSKay Sievers 			s[0] = '/';
1242b2af54aSKay Sievers 			s++;
1252b2af54aSKay Sievers 		}
1262b2af54aSKay Sievers 		kfree(path);
127ed413ae6SKay Sievers 	}
12880422738SKay Sievers out:
129f1f76f86SThomas Gleixner 	mutex_unlock(&dirlock);
1302b2af54aSKay Sievers 	return err;
1312b2af54aSKay Sievers }
1322b2af54aSKay Sievers 
1332b2af54aSKay Sievers int devtmpfs_create_node(struct device *dev)
1342b2af54aSKay Sievers {
1352b2af54aSKay Sievers 	const char *tmp = NULL;
1362b2af54aSKay Sievers 	const char *nodename;
1372b2af54aSKay Sievers 	const struct cred *curr_cred;
138e454cea2SKay Sievers 	mode_t mode = 0;
1392b2af54aSKay Sievers 	struct nameidata nd;
1402b2af54aSKay Sievers 	struct dentry *dentry;
1412b2af54aSKay Sievers 	int err;
1422b2af54aSKay Sievers 
1432b2af54aSKay Sievers 	if (!dev_mnt)
1442b2af54aSKay Sievers 		return 0;
1452b2af54aSKay Sievers 
146e454cea2SKay Sievers 	nodename = device_get_devnode(dev, &mode, &tmp);
1472b2af54aSKay Sievers 	if (!nodename)
1482b2af54aSKay Sievers 		return -ENOMEM;
1492b2af54aSKay Sievers 
150e454cea2SKay Sievers 	if (mode == 0)
151e454cea2SKay Sievers 		mode = 0600;
1522b2af54aSKay Sievers 	if (is_blockdev(dev))
153e454cea2SKay Sievers 		mode |= S_IFBLK;
1542b2af54aSKay Sievers 	else
155e454cea2SKay Sievers 		mode |= S_IFCHR;
1562b2af54aSKay Sievers 
1572b2af54aSKay Sievers 	curr_cred = override_creds(&init_cred);
15800926996SKay Sievers 
1592b2af54aSKay Sievers 	err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
1602b2af54aSKay Sievers 			      nodename, LOOKUP_PARENT, &nd);
1612b2af54aSKay Sievers 	if (err == -ENOENT) {
1622b2af54aSKay Sievers 		create_path(nodename);
1632b2af54aSKay Sievers 		err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
1642b2af54aSKay Sievers 				      nodename, LOOKUP_PARENT, &nd);
16500926996SKay Sievers 	}
1662b2af54aSKay Sievers 	if (err)
1672b2af54aSKay Sievers 		goto out;
1682b2af54aSKay Sievers 
1692b2af54aSKay Sievers 	dentry = lookup_create(&nd, 0);
1702b2af54aSKay Sievers 	if (!IS_ERR(dentry)) {
1712b2af54aSKay Sievers 		err = vfs_mknod(nd.path.dentry->d_inode,
1722b2af54aSKay Sievers 				dentry, mode, dev->devt);
17300926996SKay Sievers 		if (!err) {
17400926996SKay Sievers 			struct iattr newattrs;
17500926996SKay Sievers 
17600926996SKay Sievers 			/* fixup possibly umasked mode */
17700926996SKay Sievers 			newattrs.ia_mode = mode;
17800926996SKay Sievers 			newattrs.ia_valid = ATTR_MODE;
17900926996SKay Sievers 			mutex_lock(&dentry->d_inode->i_mutex);
18000926996SKay Sievers 			notify_change(dentry, &newattrs);
18100926996SKay Sievers 			mutex_unlock(&dentry->d_inode->i_mutex);
18200926996SKay Sievers 
18300926996SKay Sievers 			/* mark as kernel-created inode */
1842b2af54aSKay Sievers 			dentry->d_inode->i_private = &dev_mnt;
18500926996SKay Sievers 		}
1862b2af54aSKay Sievers 		dput(dentry);
1872b2af54aSKay Sievers 	} else {
1882b2af54aSKay Sievers 		err = PTR_ERR(dentry);
1892b2af54aSKay Sievers 	}
1902b2af54aSKay Sievers 
19100926996SKay Sievers 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
1922b2af54aSKay Sievers 	path_put(&nd.path);
1932b2af54aSKay Sievers out:
1942b2af54aSKay Sievers 	kfree(tmp);
1952b2af54aSKay Sievers 	revert_creds(curr_cred);
1962b2af54aSKay Sievers 	return err;
1972b2af54aSKay Sievers }
1982b2af54aSKay Sievers 
1992b2af54aSKay Sievers static int dev_rmdir(const char *name)
2002b2af54aSKay Sievers {
2012b2af54aSKay Sievers 	struct nameidata nd;
2022b2af54aSKay Sievers 	struct dentry *dentry;
2032b2af54aSKay Sievers 	int err;
2042b2af54aSKay Sievers 
2052b2af54aSKay Sievers 	err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
2062b2af54aSKay Sievers 			      name, LOOKUP_PARENT, &nd);
2072b2af54aSKay Sievers 	if (err)
2082b2af54aSKay Sievers 		return err;
2092b2af54aSKay Sievers 
2102b2af54aSKay Sievers 	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
2112b2af54aSKay Sievers 	dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
2122b2af54aSKay Sievers 	if (!IS_ERR(dentry)) {
213015bf43bSKay Sievers 		if (dentry->d_inode) {
214015bf43bSKay Sievers 			if (dentry->d_inode->i_private == &dev_mnt)
215015bf43bSKay Sievers 				err = vfs_rmdir(nd.path.dentry->d_inode,
216015bf43bSKay Sievers 						dentry);
2172b2af54aSKay Sievers 			else
218015bf43bSKay Sievers 				err = -EPERM;
219015bf43bSKay Sievers 		} else {
2202b2af54aSKay Sievers 			err = -ENOENT;
221015bf43bSKay Sievers 		}
2222b2af54aSKay Sievers 		dput(dentry);
2232b2af54aSKay Sievers 	} else {
2242b2af54aSKay Sievers 		err = PTR_ERR(dentry);
2252b2af54aSKay Sievers 	}
2262b2af54aSKay Sievers 
227015bf43bSKay Sievers 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
2282b2af54aSKay Sievers 	path_put(&nd.path);
2292b2af54aSKay Sievers 	return err;
2302b2af54aSKay Sievers }
2312b2af54aSKay Sievers 
2322b2af54aSKay Sievers static int delete_path(const char *nodepath)
2332b2af54aSKay Sievers {
2342b2af54aSKay Sievers 	const char *path;
2352b2af54aSKay Sievers 	int err = 0;
2362b2af54aSKay Sievers 
2372b2af54aSKay Sievers 	path = kstrdup(nodepath, GFP_KERNEL);
2382b2af54aSKay Sievers 	if (!path)
2392b2af54aSKay Sievers 		return -ENOMEM;
2402b2af54aSKay Sievers 
241f1f76f86SThomas Gleixner 	mutex_lock(&dirlock);
242ed413ae6SKay Sievers 	for (;;) {
2432b2af54aSKay Sievers 		char *base;
2442b2af54aSKay Sievers 
2452b2af54aSKay Sievers 		base = strrchr(path, '/');
2462b2af54aSKay Sievers 		if (!base)
2472b2af54aSKay Sievers 			break;
2482b2af54aSKay Sievers 		base[0] = '\0';
2492b2af54aSKay Sievers 		err = dev_rmdir(path);
2502b2af54aSKay Sievers 		if (err)
2512b2af54aSKay Sievers 			break;
2522b2af54aSKay Sievers 	}
253f1f76f86SThomas Gleixner 	mutex_unlock(&dirlock);
2542b2af54aSKay Sievers 
2552b2af54aSKay Sievers 	kfree(path);
2562b2af54aSKay Sievers 	return err;
2572b2af54aSKay Sievers }
2582b2af54aSKay Sievers 
2592b2af54aSKay Sievers static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
2602b2af54aSKay Sievers {
2612b2af54aSKay Sievers 	/* did we create it */
2622b2af54aSKay Sievers 	if (inode->i_private != &dev_mnt)
2632b2af54aSKay Sievers 		return 0;
2642b2af54aSKay Sievers 
2652b2af54aSKay Sievers 	/* does the dev_t match */
2662b2af54aSKay Sievers 	if (is_blockdev(dev)) {
2672b2af54aSKay Sievers 		if (!S_ISBLK(stat->mode))
2682b2af54aSKay Sievers 			return 0;
2692b2af54aSKay Sievers 	} else {
2702b2af54aSKay Sievers 		if (!S_ISCHR(stat->mode))
2712b2af54aSKay Sievers 			return 0;
2722b2af54aSKay Sievers 	}
2732b2af54aSKay Sievers 	if (stat->rdev != dev->devt)
2742b2af54aSKay Sievers 		return 0;
2752b2af54aSKay Sievers 
2762b2af54aSKay Sievers 	/* ours */
2772b2af54aSKay Sievers 	return 1;
2782b2af54aSKay Sievers }
2792b2af54aSKay Sievers 
2802b2af54aSKay Sievers int devtmpfs_delete_node(struct device *dev)
2812b2af54aSKay Sievers {
2822b2af54aSKay Sievers 	const char *tmp = NULL;
2832b2af54aSKay Sievers 	const char *nodename;
2842b2af54aSKay Sievers 	const struct cred *curr_cred;
2852b2af54aSKay Sievers 	struct nameidata nd;
2862b2af54aSKay Sievers 	struct dentry *dentry;
2872b2af54aSKay Sievers 	struct kstat stat;
2882b2af54aSKay Sievers 	int deleted = 1;
2892b2af54aSKay Sievers 	int err;
2902b2af54aSKay Sievers 
2912b2af54aSKay Sievers 	if (!dev_mnt)
2922b2af54aSKay Sievers 		return 0;
2932b2af54aSKay Sievers 
294e454cea2SKay Sievers 	nodename = device_get_devnode(dev, NULL, &tmp);
2952b2af54aSKay Sievers 	if (!nodename)
2962b2af54aSKay Sievers 		return -ENOMEM;
2972b2af54aSKay Sievers 
2982b2af54aSKay Sievers 	curr_cred = override_creds(&init_cred);
2992b2af54aSKay Sievers 	err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
3002b2af54aSKay Sievers 			      nodename, LOOKUP_PARENT, &nd);
3012b2af54aSKay Sievers 	if (err)
3022b2af54aSKay Sievers 		goto out;
3032b2af54aSKay Sievers 
3042b2af54aSKay Sievers 	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
3052b2af54aSKay Sievers 	dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
3062b2af54aSKay Sievers 	if (!IS_ERR(dentry)) {
3072b2af54aSKay Sievers 		if (dentry->d_inode) {
3082b2af54aSKay Sievers 			err = vfs_getattr(nd.path.mnt, dentry, &stat);
3092b2af54aSKay Sievers 			if (!err && dev_mynode(dev, dentry->d_inode, &stat)) {
3105e31d76fSKay Sievers 				struct iattr newattrs;
3115e31d76fSKay Sievers 				/*
3125e31d76fSKay Sievers 				 * before unlinking this node, reset permissions
3135e31d76fSKay Sievers 				 * of possible references like hardlinks
3145e31d76fSKay Sievers 				 */
3155e31d76fSKay Sievers 				newattrs.ia_uid = 0;
3165e31d76fSKay Sievers 				newattrs.ia_gid = 0;
3175e31d76fSKay Sievers 				newattrs.ia_mode = stat.mode & ~0777;
3185e31d76fSKay Sievers 				newattrs.ia_valid =
3195e31d76fSKay Sievers 					ATTR_UID|ATTR_GID|ATTR_MODE;
3205e31d76fSKay Sievers 				mutex_lock(&dentry->d_inode->i_mutex);
3215e31d76fSKay Sievers 				notify_change(dentry, &newattrs);
3225e31d76fSKay Sievers 				mutex_unlock(&dentry->d_inode->i_mutex);
3232b2af54aSKay Sievers 				err = vfs_unlink(nd.path.dentry->d_inode,
3242b2af54aSKay Sievers 						 dentry);
3252b2af54aSKay Sievers 				if (!err || err == -ENOENT)
3262b2af54aSKay Sievers 					deleted = 1;
3272b2af54aSKay Sievers 			}
3282b2af54aSKay Sievers 		} else {
3292b2af54aSKay Sievers 			err = -ENOENT;
3302b2af54aSKay Sievers 		}
3312b2af54aSKay Sievers 		dput(dentry);
3322b2af54aSKay Sievers 	} else {
3332b2af54aSKay Sievers 		err = PTR_ERR(dentry);
3342b2af54aSKay Sievers 	}
3352b2af54aSKay Sievers 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
3362b2af54aSKay Sievers 
3372b2af54aSKay Sievers 	path_put(&nd.path);
3382b2af54aSKay Sievers 	if (deleted && strchr(nodename, '/'))
3392b2af54aSKay Sievers 		delete_path(nodename);
3402b2af54aSKay Sievers out:
3412b2af54aSKay Sievers 	kfree(tmp);
3422b2af54aSKay Sievers 	revert_creds(curr_cred);
3432b2af54aSKay Sievers 	return err;
3442b2af54aSKay Sievers }
3452b2af54aSKay Sievers 
3462b2af54aSKay Sievers /*
3472b2af54aSKay Sievers  * If configured, or requested by the commandline, devtmpfs will be
3482b2af54aSKay Sievers  * auto-mounted after the kernel mounted the root filesystem.
3492b2af54aSKay Sievers  */
350073120ccSKay Sievers int devtmpfs_mount(const char *mntdir)
3512b2af54aSKay Sievers {
3522b2af54aSKay Sievers 	int err;
3532b2af54aSKay Sievers 
354*fc14f2feSAl Viro 	if (!mount_dev)
3552b2af54aSKay Sievers 		return 0;
3562b2af54aSKay Sievers 
3572b2af54aSKay Sievers 	if (!dev_mnt)
3582b2af54aSKay Sievers 		return 0;
3592b2af54aSKay Sievers 
360073120ccSKay Sievers 	err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
3612b2af54aSKay Sievers 	if (err)
3622b2af54aSKay Sievers 		printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
3632b2af54aSKay Sievers 	else
3642b2af54aSKay Sievers 		printk(KERN_INFO "devtmpfs: mounted\n");
3652b2af54aSKay Sievers 	return err;
3662b2af54aSKay Sievers }
3672b2af54aSKay Sievers 
3682b2af54aSKay Sievers /*
3692b2af54aSKay Sievers  * Create devtmpfs instance, driver-core devices will add their device
3702b2af54aSKay Sievers  * nodes here.
3712b2af54aSKay Sievers  */
3722b2af54aSKay Sievers int __init devtmpfs_init(void)
3732b2af54aSKay Sievers {
3742b2af54aSKay Sievers 	int err;
3752b2af54aSKay Sievers 	struct vfsmount *mnt;
376f776c5ecSHeiko Carstens 	char options[] = "mode=0755";
3772b2af54aSKay Sievers 
3782b2af54aSKay Sievers 	err = register_filesystem(&dev_fs_type);
3792b2af54aSKay Sievers 	if (err) {
3802b2af54aSKay Sievers 		printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
3812b2af54aSKay Sievers 		       "type %i\n", err);
3822b2af54aSKay Sievers 		return err;
3832b2af54aSKay Sievers 	}
3842b2af54aSKay Sievers 
385f776c5ecSHeiko Carstens 	mnt = kern_mount_data(&dev_fs_type, options);
3862b2af54aSKay Sievers 	if (IS_ERR(mnt)) {
3872b2af54aSKay Sievers 		err = PTR_ERR(mnt);
3882b2af54aSKay Sievers 		printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
3892b2af54aSKay Sievers 		unregister_filesystem(&dev_fs_type);
3902b2af54aSKay Sievers 		return err;
3912b2af54aSKay Sievers 	}
3922b2af54aSKay Sievers 	dev_mnt = mnt;
3932b2af54aSKay Sievers 
3942b2af54aSKay Sievers 	printk(KERN_INFO "devtmpfs: initialized\n");
3952b2af54aSKay Sievers 	return 0;
3962b2af54aSKay Sievers }
397