xref: /minix/minix/fs/ptyfs/ptyfs.c (revision 0a6a1f1d)
1 /* PTYFS - file system for Unix98 pseudoterminal slave nodes (/dev/pts) */
2 
3 #include <minix/drivers.h>
4 #include <minix/fsdriver.h>
5 #include <minix/vfsif.h>
6 #include <minix/ds.h>
7 #include <sys/dirent.h>
8 #include <assert.h>
9 
10 #include "node.h"
11 
12 #define ROOT_INO_NR	1	/* inode number of the root directory */
13 #define BASE_INO_NR	2	/* first inode number for slave nodes */
14 
15 #define GETDENTS_BUF	1024	/* size of the temporary buffer for getdents */
16 
17 static struct node_data root_data = {
18 	.mode	= S_IFDIR | 0755,
19 	.uid	= 0,
20 	.gid	= 0,
21 	.dev	= NO_DEV
22 };
23 
24 /*
25  * Mount the file system.
26  */
27 static int
28 ptyfs_mount(dev_t __unused dev, unsigned int flags,
29 	struct fsdriver_node * root_node, unsigned int * res_flags)
30 {
31 
32 	/* This file system can not be used as a root file system. */
33 	if (flags & REQ_ISROOT)
34 		return EINVAL;
35 
36 	/* Return the details of the root node. */
37 	root_node->fn_ino_nr = ROOT_INO_NR;
38 	root_node->fn_mode = root_data.mode;
39 	root_node->fn_uid = root_data.uid;
40 	root_node->fn_gid = root_data.gid;
41 	root_node->fn_size = 0;
42 	root_node->fn_dev = root_data.dev;
43 
44 	*res_flags = RES_NOFLAGS;
45 
46 	return OK;
47 }
48 
49 /*
50  * Generate the name string of a slave node based on its node number.  Return
51  * OK on success, with the null-terminated name stored in the buffer 'name'
52  * which is 'size' bytes in size.  Return an error code on failure.
53  */
54 static int
55 make_name(char * name, size_t size, node_t index)
56 {
57 	ssize_t r;
58 
59 	if ((r = snprintf(name, sizeof(name), "%u", index)) < 0)
60 		return EINVAL;
61 
62 	if (r >= size)
63 		return ENAMETOOLONG;
64 
65 	return OK;
66 }
67 
68 /*
69  * Parse the name of a slave node as given by a user, and check whether it is a
70  * valid slave node number.  A valid slave number is any name that can be
71  * produced by make_name().  Return TRUE if the string was successfully parsed
72  * as a slave node number (which may or may not actually be allocated), with
73  * the number stored in 'indexp'.  Return FALSE if the name is not a number.
74  */
75 static int
76 parse_name(const char * name, node_t * indexp)
77 {
78 	node_t index;
79 	const char *p;
80 
81 	index = 0;
82 	for (p = name; *p; p++) {
83 		/* Digits only. */
84 		if (*p < '0' || *p > '9')
85 			return FALSE;
86 
87 		/* No leading zeroes. */
88 		if (p != name && index == 0)
89 			return FALSE;
90 
91 		/* No overflow. */
92 		if (index * 10 < index)
93 			return FALSE;
94 
95 		index = index * 10 + *p - '0';
96 	}
97 
98 	*indexp = index;
99 	return TRUE;
100 }
101 
102 /*
103  * Look up a name in a directory, yielding a node on success.  For a successful
104  * lookup, the given name must either be a single dot, which resolves to the
105  * file system root directory, or the number of an allocated slave node.
106  */
107 static int
108 ptyfs_lookup(ino_t dir_nr, char * name, struct fsdriver_node * node,
109 	int * is_mountpt)
110 {
111 	struct node_data *data;
112 	node_t index;
113 	ino_t ino_nr;
114 
115 	assert(name[0] != '\0');
116 
117 	if (dir_nr != ROOT_INO_NR)
118 		return ENOENT;
119 
120 	if (name[0] == '.' && name[1] == '\0') {
121 		/* The root directory itself is requested. */
122 		ino_nr = ROOT_INO_NR;
123 
124 		data = &root_data;
125 	} else {
126 		/* Parse the user-provided name, which must be a number. */
127 		if (!parse_name(name, &index))
128 			return ENOENT;
129 
130 		ino_nr = BASE_INO_NR + index;
131 
132 		/* See if the number is in use, and get its details. */
133 		if ((data = get_node(index)) == NULL)
134 			return ENOENT;
135 	}
136 
137 	node->fn_ino_nr = ino_nr;
138 	node->fn_mode = data->mode;
139 	node->fn_uid = data->uid;
140 	node->fn_gid = data->gid;
141 	node->fn_size = 0;
142 	node->fn_dev = data->dev;
143 
144 	*is_mountpt = FALSE;
145 
146 	return OK;
147 }
148 
149 /*
150  * Enumerate directory contents.
151  */
152 static ssize_t
153 ptyfs_getdents(ino_t ino_nr, struct fsdriver_data * data,
154 	size_t bytes, off_t * posp)
155 {
156 	struct fsdriver_dentry fsdentry;
157 	static char buf[GETDENTS_BUF];
158 	char name[NAME_MAX + 1];
159 	struct node_data *node_data;
160 	unsigned int type;
161 	off_t pos;
162 	node_t index;
163 	ssize_t r;
164 
165 	if (ino_nr != ROOT_INO_NR)
166 		return EINVAL;
167 
168 	fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf));
169 
170 	for (;;) {
171 		pos = (*posp)++;
172 
173 		if (pos < 2) {
174 			strlcpy(name, (pos == 0) ? "." : "..", sizeof(name));
175 			ino_nr = ROOT_INO_NR;
176 			type = DT_DIR;
177 		} else {
178 			if (pos - 2 >= get_max_node())
179 				break; /* EOF */
180 			index = (node_t)(pos - 2);
181 
182 			if ((node_data = get_node(index)) == NULL)
183 				continue; /* index not in use */
184 
185 			if (make_name(name, sizeof(name), index) != OK)
186 				continue; /* could not generate name string */
187 			ino_nr = BASE_INO_NR + index;
188 			type = IFTODT(node_data->mode);
189 		}
190 
191 		if ((r = fsdriver_dentry_add(&fsdentry, ino_nr, name,
192 		    strlen(name), type)) < 0)
193 			return r;
194 		if (r == 0)
195 			break; /* result buffer full */
196 	}
197 
198 	return fsdriver_dentry_finish(&fsdentry);
199 }
200 
201 /*
202  * Return a pointer to the node data structure for the given inode number, or
203  * NULL if no node exists for the given inode number.
204  */
205 static struct node_data *
206 get_data(ino_t ino_nr)
207 {
208 	node_t index;
209 
210 	if (ino_nr == ROOT_INO_NR)
211 		return &root_data;
212 
213 	if (ino_nr < BASE_INO_NR || ino_nr >= BASE_INO_NR + get_max_node())
214 		return NULL;
215 
216 	index = (node_t)(ino_nr - BASE_INO_NR);
217 
218 	return get_node(index);
219 }
220 
221 /*
222  * Change file ownership.
223  */
224 static int
225 ptyfs_chown(ino_t ino_nr, uid_t uid, gid_t gid, mode_t * mode)
226 {
227 	struct node_data *data;
228 
229 	if ((data = get_data(ino_nr)) == NULL)
230 		return EINVAL;
231 
232 	data->uid = uid;
233 	data->gid = gid;
234 	data->mode &= ~(S_ISUID | S_ISGID);
235 
236 	*mode = data->mode;
237 
238 	return OK;
239 }
240 
241 /*
242  * Change file mode.
243  */
244 static int
245 ptyfs_chmod(ino_t ino_nr, mode_t * mode)
246 {
247 	struct node_data *data;
248 
249 	if ((data = get_data(ino_nr)) == NULL)
250 		return EINVAL;
251 
252 	data->mode = (data->mode & ~ALLPERMS) | (*mode & ALLPERMS);
253 
254 	*mode = data->mode;
255 
256 	return OK;
257 }
258 
259 /*
260  * Return node details.
261  */
262 static int
263 ptyfs_stat(ino_t ino_nr, struct stat * buf)
264 {
265 	struct node_data *data;
266 
267 	if ((data = get_data(ino_nr)) == NULL)
268 		return EINVAL;
269 
270 	buf->st_mode = data->mode;
271 	buf->st_uid = data->uid;
272 	buf->st_gid = data->gid;
273 	buf->st_nlink = S_ISDIR(data->mode) ? 2 : 1;
274 	buf->st_rdev = data->dev;
275 	buf->st_atime = data->ctime;
276 	buf->st_mtime = data->ctime;
277 	buf->st_ctime = data->ctime;
278 
279 	return OK;
280 }
281 
282 /*
283  * Return file system statistics.
284  */
285 static int
286 ptyfs_statvfs(struct statvfs * buf)
287 {
288 
289 	buf->f_flag = ST_NOTRUNC;
290 	buf->f_namemax = NAME_MAX;
291 
292 	return OK;
293 }
294 
295 /*
296  * Process non-filesystem messages, in particular slave node creation and
297  * deletion requests from the PTY service.
298  */
299 static void
300 ptyfs_other(const message * m_ptr, int ipc_status)
301 {
302 	char label[DS_MAX_KEYLEN];
303 	struct node_data data;
304 	message m_reply;
305 	int r;
306 
307 	/*
308 	 * We only accept requests from the service with the label "pty".
309 	 * More sophisticated access checks are part of future work.
310 	 */
311 	if ((r = ds_retrieve_label_name(label, m_ptr->m_source)) != OK) {
312 		printf("PTYFS: unable to obtain label for %u (%d)\n",
313 		    m_ptr->m_source, r);
314 		return;
315 	}
316 
317 	if (strcmp(label, "pty")) {
318 		printf("PTYFS: unexpected request %x from %s/%u\n",
319 		    m_ptr->m_type, label, m_ptr->m_source);
320 		return;
321 	}
322 
323 	/* Process the request from PTY. */
324 	memset(&m_reply, 0, sizeof(m_reply));
325 
326 	switch (m_ptr->m_type) {
327 	case PTYFS_SET:
328 		memset(&data, 0, sizeof(data));
329 		data.dev = m_ptr->m_pty_ptyfs_req.dev;
330 		data.mode = m_ptr->m_pty_ptyfs_req.mode;
331 		data.uid = m_ptr->m_pty_ptyfs_req.uid;
332 		data.gid = m_ptr->m_pty_ptyfs_req.gid;
333 		data.ctime = clock_time(NULL);
334 
335 		r = set_node(m_ptr->m_pty_ptyfs_req.index, &data);
336 
337 		break;
338 
339 	case PTYFS_CLEAR:
340 		clear_node(m_ptr->m_pty_ptyfs_req.index);
341 		r = OK;
342 
343 		break;
344 
345 	case PTYFS_NAME:
346 		r = make_name(m_reply.m_ptyfs_pty_name.name,
347 		    sizeof(m_reply.m_ptyfs_pty_name.name),
348 		    m_ptr->m_pty_ptyfs_req.index);
349 
350 		break;
351 
352 	default:
353 		printf("PTYFS: invalid request %x from PTY\n", m_ptr->m_type);
354 		r = ENOSYS;
355 	}
356 
357 	/*
358 	 * Send a reply to the request.  In particular slave node addition
359 	 * requests must be blocking for the PTY service, so as to avoid race
360 	 * conditions between PTYFS creating the slave node and userland trying
361 	 * to open it.
362 	 */
363 	m_reply.m_type = r;
364 
365 	if (IPC_STATUS_CALL(ipc_status) == SENDREC)
366 		r = ipc_sendnb(m_ptr->m_source, &m_reply);
367 	else
368 		r = asynsend3(m_ptr->m_source, &m_reply, AMF_NOREPLY);
369 
370 	if (r != OK)
371 		printf("PTYFS: unable to reply to PTY (%d)\n", r);
372 }
373 
374 /*
375  * Initialize the service.
376  */
377 static int
378 ptyfs_init(int __unused type, sef_init_info_t * __unused info)
379 {
380 
381 	init_nodes();
382 
383 	root_data.ctime = clock_time(NULL);
384 
385 	return OK;
386 }
387 
388 /*
389  * Process an incoming signal.
390  */
391 static void
392 ptyfs_signal(int sig)
393 {
394 
395 	if (sig == SIGTERM)
396 		fsdriver_terminate();
397 }
398 
399 /*
400  * Perform SEF initialization.
401  */
402 static void
403 ptyfs_startup(void)
404 {
405 
406 	sef_setcb_init_fresh(ptyfs_init);
407 	sef_setcb_signal_handler(ptyfs_signal);
408 	sef_startup();
409 }
410 
411 static struct fsdriver ptyfs_table = {
412 	.fdr_mount	= ptyfs_mount,
413 	.fdr_lookup	= ptyfs_lookup,
414 	.fdr_getdents	= ptyfs_getdents,
415 	.fdr_stat	= ptyfs_stat,
416 	.fdr_chown	= ptyfs_chown,
417 	.fdr_chmod	= ptyfs_chmod,
418 	.fdr_statvfs	= ptyfs_statvfs,
419 	.fdr_other	= ptyfs_other
420 };
421 
422 /*
423  * The PTYFS service.
424  */
425 int
426 main(void)
427 {
428 
429 	ptyfs_startup();
430 
431 	fsdriver_task(&ptyfs_table);
432 
433 	return 0;
434 }
435