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
ptyfs_mount(dev_t __unused dev,unsigned int flags,struct fsdriver_node * root_node,unsigned int * res_flags)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
make_name(char * name,size_t size,node_t index)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
parse_name(const char * name,node_t * indexp)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
ptyfs_lookup(ino_t dir_nr,char * name,struct fsdriver_node * node,int * is_mountpt)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
ptyfs_getdents(ino_t ino_nr,struct fsdriver_data * data,size_t bytes,off_t * posp)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 *
get_data(ino_t ino_nr)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
ptyfs_chown(ino_t ino_nr,uid_t uid,gid_t gid,mode_t * mode)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
ptyfs_chmod(ino_t ino_nr,mode_t * mode)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
ptyfs_stat(ino_t ino_nr,struct stat * buf)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
ptyfs_statvfs(struct statvfs * buf)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
ptyfs_other(const message * m_ptr,int ipc_status)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
ptyfs_init(int __unused type,sef_init_info_t * __unused info)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
ptyfs_signal(int sig)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
ptyfs_startup(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
main(void)426 main(void)
427 {
428
429 ptyfs_startup();
430
431 fsdriver_task(&ptyfs_table);
432
433 return 0;
434 }
435