xref: /linux-6.15/drivers/misc/ntsync.c (revision b46271ec)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * ntsync.c - Kernel driver for NT synchronization primitives
4  *
5  * Copyright (C) 2024 Elizabeth Figura <[email protected]>
6  */
7 
8 #include <linux/anon_inodes.h>
9 #include <linux/file.h>
10 #include <linux/fs.h>
11 #include <linux/miscdevice.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <uapi/linux/ntsync.h>
15 
16 #define NTSYNC_NAME	"ntsync"
17 
18 enum ntsync_type {
19 	NTSYNC_TYPE_SEM,
20 };
21 
22 /*
23  * Individual synchronization primitives are represented by
24  * struct ntsync_obj, and each primitive is backed by a file.
25  *
26  * The whole namespace is represented by a struct ntsync_device also
27  * backed by a file.
28  *
29  * Both rely on struct file for reference counting. Individual
30  * ntsync_obj objects take a reference to the device when created.
31  */
32 
33 struct ntsync_obj {
34 	enum ntsync_type type;
35 
36 	union {
37 		struct {
38 			__u32 count;
39 			__u32 max;
40 		} sem;
41 	} u;
42 
43 	struct file *file;
44 	struct ntsync_device *dev;
45 };
46 
47 struct ntsync_device {
48 	struct file *file;
49 };
50 
51 static int ntsync_obj_release(struct inode *inode, struct file *file)
52 {
53 	struct ntsync_obj *obj = file->private_data;
54 
55 	fput(obj->dev->file);
56 	kfree(obj);
57 
58 	return 0;
59 }
60 
61 static const struct file_operations ntsync_obj_fops = {
62 	.owner		= THIS_MODULE,
63 	.release	= ntsync_obj_release,
64 	.llseek		= no_llseek,
65 };
66 
67 static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
68 					   enum ntsync_type type)
69 {
70 	struct ntsync_obj *obj;
71 
72 	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
73 	if (!obj)
74 		return NULL;
75 	obj->type = type;
76 	obj->dev = dev;
77 	get_file(dev->file);
78 
79 	return obj;
80 }
81 
82 static int ntsync_obj_get_fd(struct ntsync_obj *obj)
83 {
84 	struct file *file;
85 	int fd;
86 
87 	fd = get_unused_fd_flags(O_CLOEXEC);
88 	if (fd < 0)
89 		return fd;
90 	file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
91 	if (IS_ERR(file)) {
92 		put_unused_fd(fd);
93 		return PTR_ERR(file);
94 	}
95 	obj->file = file;
96 	fd_install(fd, file);
97 
98 	return fd;
99 }
100 
101 static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
102 {
103 	struct ntsync_sem_args __user *user_args = argp;
104 	struct ntsync_sem_args args;
105 	struct ntsync_obj *sem;
106 	int fd;
107 
108 	if (copy_from_user(&args, argp, sizeof(args)))
109 		return -EFAULT;
110 
111 	if (args.count > args.max)
112 		return -EINVAL;
113 
114 	sem = ntsync_alloc_obj(dev, NTSYNC_TYPE_SEM);
115 	if (!sem)
116 		return -ENOMEM;
117 	sem->u.sem.count = args.count;
118 	sem->u.sem.max = args.max;
119 	fd = ntsync_obj_get_fd(sem);
120 	if (fd < 0) {
121 		kfree(sem);
122 		return fd;
123 	}
124 
125 	return put_user(fd, &user_args->sem);
126 }
127 
128 static int ntsync_char_open(struct inode *inode, struct file *file)
129 {
130 	struct ntsync_device *dev;
131 
132 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
133 	if (!dev)
134 		return -ENOMEM;
135 
136 	file->private_data = dev;
137 	dev->file = file;
138 	return nonseekable_open(inode, file);
139 }
140 
141 static int ntsync_char_release(struct inode *inode, struct file *file)
142 {
143 	struct ntsync_device *dev = file->private_data;
144 
145 	kfree(dev);
146 
147 	return 0;
148 }
149 
150 static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
151 			      unsigned long parm)
152 {
153 	struct ntsync_device *dev = file->private_data;
154 	void __user *argp = (void __user *)parm;
155 
156 	switch (cmd) {
157 	case NTSYNC_IOC_CREATE_SEM:
158 		return ntsync_create_sem(dev, argp);
159 	default:
160 		return -ENOIOCTLCMD;
161 	}
162 }
163 
164 static const struct file_operations ntsync_fops = {
165 	.owner		= THIS_MODULE,
166 	.open		= ntsync_char_open,
167 	.release	= ntsync_char_release,
168 	.unlocked_ioctl	= ntsync_char_ioctl,
169 	.compat_ioctl	= compat_ptr_ioctl,
170 	.llseek		= no_llseek,
171 };
172 
173 static struct miscdevice ntsync_misc = {
174 	.minor		= MISC_DYNAMIC_MINOR,
175 	.name		= NTSYNC_NAME,
176 	.fops		= &ntsync_fops,
177 };
178 
179 module_misc_device(ntsync_misc);
180 
181 MODULE_AUTHOR("Elizabeth Figura <[email protected]>");
182 MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
183 MODULE_LICENSE("GPL");
184