1 /* PFS - Pipe File Server */
2
3 #include <minix/drivers.h>
4 #include <minix/fsdriver.h>
5 #include <minix/vfsif.h>
6 #include <minix/rs.h>
7 #include <assert.h>
8
9 /*
10 * The following constant defines the number of inodes in PFS, which is
11 * therefore the maximum number of open pipes and cloned devices that can be
12 * used in the entire system. If anything, it should be kept somewhat in sync
13 * with VFS's maximum number of inodes. In the future, inodes could be
14 * allocated dynamically, but this will require extra infrastructure.
15 */
16 #define PFS_NR_INODES 512 /* maximum number of inodes in PFS */
17
18 /* The following bits can be combined in the inode's i_update field. */
19 #define ATIME 0x1 /* update access time later */
20 #define MTIME 0x2 /* update modification time later */
21 #define CTIME 0x4 /* update change time later */
22
23 static struct inode {
24 ino_t i_num; /* inode number */
25
26 mode_t i_mode; /* file mode and permissions */
27 uid_t i_uid; /* user ID of the file's owner */
28 gid_t i_gid; /* group ID of the file's owner */
29 size_t i_size; /* current file size in bytes */
30 dev_t i_rdev; /* device number for device nodes */
31 time_t i_atime; /* file access time */
32 time_t i_mtime; /* file modification time */
33 time_t i_ctime; /* file change time */
34
35 char *i_data; /* data buffer, for pipes only */
36 size_t i_start; /* start of data into data buffer */
37
38 unsigned char i_update; /* which file times to update? */
39 unsigned char i_free; /* sanity check: is the inode free? */
40
41 LIST_ENTRY(inode) i_next; /* next element in free list */
42 } inode[PFS_NR_INODES];
43
44 static LIST_HEAD(, inode) free_inodes; /* list of free inodes */
45
46 /*
47 * Mount the pipe file server.
48 */
49 static int
pfs_mount(dev_t __unused dev,unsigned int __unused flags,struct fsdriver_node * node,unsigned int * res_flags)50 pfs_mount(dev_t __unused dev, unsigned int __unused flags,
51 struct fsdriver_node * node, unsigned int * res_flags)
52 {
53 struct inode *rip;
54 unsigned int i;
55
56 LIST_INIT(&free_inodes); /* initialize the free list */
57
58 /*
59 * Initialize the inode table. We walk backwards so that the lowest
60 * inode numbers end up being used first. Silly? Sure, but aesthetics
61 * are worth something, too..
62 */
63 for (i = PFS_NR_INODES; i > 0; i--) {
64 rip = &inode[i - 1];
65
66 /* Inode number 0 is reserved. See also pfs_findnode. */
67 rip->i_num = i;
68 rip->i_free = TRUE;
69
70 LIST_INSERT_HEAD(&free_inodes, rip, i_next);
71 }
72
73 /*
74 * PFS has no root node, and VFS will ignore the returned node details
75 * anyway. The whole idea is to provide symmetry with other file
76 * systems, thus keeping libfsdriver simple and free of special cases.
77 */
78 memset(node, 0, sizeof(*node));
79 *res_flags = RES_64BIT;
80
81 return OK;
82 }
83
84 /*
85 * Unmount the pipe file server.
86 */
87 static void
pfs_unmount(void)88 pfs_unmount(void)
89 {
90 unsigned int i;
91
92 /* Warn about in-use inodes. There's nothing else we can do. */
93 for (i = 0; i < PFS_NR_INODES; i++)
94 if (inode[i].i_free == FALSE)
95 break;
96
97 if (i < PFS_NR_INODES)
98 printf("PFS: unmounting while busy!\n");
99 }
100
101 /*
102 * Find the node with the corresponding inode number. It must be in use.
103 */
104 static struct inode *
pfs_findnode(ino_t ino_nr)105 pfs_findnode(ino_t ino_nr)
106 {
107 struct inode *rip;
108
109 /* Inode numbers are 1-based, because inode number 0 is reserved. */
110 if (ino_nr < 1 || ino_nr > PFS_NR_INODES)
111 return NULL;
112
113 rip = &inode[ino_nr - 1];
114 assert(rip->i_num == ino_nr);
115
116 if (rip->i_free == TRUE)
117 return NULL;
118
119 return rip;
120 }
121
122 /*
123 * Create a new, unlinked node. It must be either a pipe or a device file.
124 */
125 static int
pfs_newnode(mode_t mode,uid_t uid,gid_t gid,dev_t dev,struct fsdriver_node * node)126 pfs_newnode(mode_t mode, uid_t uid, gid_t gid, dev_t dev,
127 struct fsdriver_node * node)
128 {
129 struct inode *rip;
130 char *data;
131 int isfifo, isdev;
132
133 /* Check the file type. Do we support it at all? */
134 isfifo = S_ISFIFO(mode);
135 isdev = S_ISBLK(mode) || S_ISCHR(mode) || S_ISSOCK(mode);
136
137 if (!isfifo && !isdev)
138 return EINVAL; /* this means VFS is misbehaving.. */
139
140 /* Is there a free inode? */
141 if (LIST_EMPTY(&free_inodes))
142 return ENFILE;
143
144 /* For pipes, we need a buffer. Try to allocate one. */
145 data = NULL;
146 if (isfifo && (data = malloc(PIPE_BUF)) == NULL)
147 return ENOSPC;
148
149 /* Nothing can go wrong now. Take an inode off the free list. */
150 rip = LIST_FIRST(&free_inodes);
151 LIST_REMOVE(rip, i_next);
152
153 assert(rip->i_free == TRUE);
154 rip->i_free = FALSE; /* this is for sanity checks only */
155
156 /* Initialize the inode's fields. */
157 rip->i_mode = mode;
158 rip->i_uid = uid;
159 rip->i_gid = gid;
160 rip->i_size = 0;
161 rip->i_update = ATIME | MTIME | CTIME;
162 if (isdev)
163 rip->i_rdev = dev;
164 else
165 rip->i_rdev = NO_DEV;
166 rip->i_data = data;
167 rip->i_start = 0;
168
169 /* Fill in the fields of the response message. */
170 node->fn_ino_nr = rip->i_num;
171 node->fn_mode = rip->i_mode;
172 node->fn_size = rip->i_size;
173 node->fn_uid = rip->i_uid;
174 node->fn_gid = rip->i_gid;
175 node->fn_dev = rip->i_rdev;
176
177 return OK;
178 }
179
180 /*
181 * Close a node.
182 */
183 static int
pfs_putnode(ino_t ino_nr,unsigned int count)184 pfs_putnode(ino_t ino_nr, unsigned int count)
185 {
186 struct inode *rip;
187
188 if ((rip = pfs_findnode(ino_nr)) == NULL)
189 return EINVAL;
190
191 /*
192 * Since the new-node call is the only way to open an inode, and there
193 * is no way to increase the use count of an already-opened inode, we
194 * can safely assume that the reference count will only ever be one.
195 * That also means we are always freeing up the target inode here.
196 */
197 if (count != 1)
198 return EINVAL;
199
200 /* For pipes, free the inode data buffer. */
201 if (rip->i_data != NULL) {
202 free(rip->i_data);
203 rip->i_data = NULL;
204 }
205
206 /* Return the inode to the free list. */
207 rip->i_free = TRUE;
208
209 LIST_INSERT_HEAD(&free_inodes, rip, i_next);
210
211 return OK;
212 }
213
214 /*
215 * Read from a pipe.
216 */
217 static ssize_t
pfs_read(ino_t ino_nr,struct fsdriver_data * data,size_t bytes,off_t __unused pos,int __unused call)218 pfs_read(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
219 off_t __unused pos, int __unused call)
220 {
221 struct inode *rip;
222 int r;
223
224 /* The target node must be a pipe. */
225 if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
226 return EINVAL;
227
228 /* We can't read beyond the maximum file position. */
229 if (bytes > PIPE_BUF)
230 return EFBIG;
231
232 /* Limit the request to how much is in the pipe. */
233 if (bytes > rip->i_size)
234 bytes = rip->i_size;
235
236 /* Copy the data to user space. */
237 if ((r = fsdriver_copyout(data, 0, rip->i_data + rip->i_start,
238 bytes)) != OK)
239 return r;
240
241 /* Update file size and access time. */
242 rip->i_size -= bytes;
243 rip->i_start += bytes;
244 rip->i_update |= ATIME;
245
246 /* Return the number of bytes transferred. */
247 return bytes;
248 }
249
250 /*
251 * Write to a pipe.
252 */
253 static ssize_t
pfs_write(ino_t ino_nr,struct fsdriver_data * data,size_t bytes,off_t __unused pos,int __unused call)254 pfs_write(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
255 off_t __unused pos, int __unused call)
256 {
257 struct inode *rip;
258 int r;
259
260 /* The target node must be a pipe. */
261 if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
262 return EINVAL;
263
264 /* Check in advance to see if file will grow too big. */
265 if (rip->i_size + bytes > PIPE_BUF)
266 return EFBIG;
267
268 /*
269 * Move any previously remaining data to the front of the buffer.
270 * Doing so upon writes rather than reads saves on memory moves when
271 * there are many small reads. Not using the buffer circularly saves
272 * on kernel calls.
273 */
274 if (rip->i_start > 0) {
275 if (rip->i_size > 0)
276 memmove(rip->i_data, rip->i_data + rip->i_start,
277 rip->i_size);
278
279 rip->i_start = 0;
280 }
281
282 /* Copy the data from user space. */
283 r = fsdriver_copyin(data, 0, rip->i_data + rip->i_size, bytes);
284 if (r != OK)
285 return r;
286
287 /* Update file size and times. */
288 rip->i_size += bytes;
289 rip->i_update |= CTIME | MTIME;
290
291 /* Return the number of bytes transferred. */
292 return bytes;
293 }
294
295 /*
296 * Truncate a pipe.
297 */
298 static int
pfs_trunc(ino_t ino_nr,off_t start_pos,off_t end_pos)299 pfs_trunc(ino_t ino_nr, off_t start_pos, off_t end_pos)
300 {
301 struct inode *rip;
302
303 /* The target node must be a pipe. */
304 if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
305 return EINVAL;
306
307 /* We only support full truncation of pipes. */
308 if (start_pos != 0 || end_pos != 0)
309 return EINVAL;
310
311 /* Update file size and times. */
312 rip->i_size = 0;
313 rip->i_update |= CTIME | MTIME;
314
315 return OK;
316 }
317
318 /*
319 * Return node status.
320 */
321 static int
pfs_stat(ino_t ino_nr,struct stat * statbuf)322 pfs_stat(ino_t ino_nr, struct stat * statbuf)
323 {
324 struct inode *rip;
325 time_t now;
326
327 if ((rip = pfs_findnode(ino_nr)) == NULL)
328 return EINVAL;
329
330 /* Update the time fields in the inode, if need be. */
331 if (rip->i_update != 0) {
332 now = clock_time(NULL);
333
334 if (rip->i_update & ATIME) rip->i_atime = now;
335 if (rip->i_update & MTIME) rip->i_mtime = now;
336 if (rip->i_update & CTIME) rip->i_ctime = now;
337
338 rip->i_update = 0;
339 }
340
341 /* Fill the stat buffer. */
342 statbuf->st_dev = rip->i_rdev; /* workaround for old socketpair bug */
343 statbuf->st_mode = rip->i_mode;
344 statbuf->st_nlink = 0;
345 statbuf->st_uid = rip->i_uid;
346 statbuf->st_gid = rip->i_gid;
347 statbuf->st_rdev = rip->i_rdev;
348 statbuf->st_size = rip->i_size;
349 statbuf->st_atime = rip->i_atime;
350 statbuf->st_mtime = rip->i_mtime;
351 statbuf->st_ctime = rip->i_ctime;
352 statbuf->st_blksize = PIPE_BUF;
353 statbuf->st_blocks = howmany(rip->i_size, S_BLKSIZE);
354
355 return OK;
356 }
357
358 /*
359 * Change node permissions.
360 */
361 static int
pfs_chmod(ino_t ino_nr,mode_t * mode)362 pfs_chmod(ino_t ino_nr, mode_t * mode)
363 {
364 struct inode *rip;
365
366 if ((rip = pfs_findnode(ino_nr)) == NULL)
367 return EINVAL;
368
369 /* Update file mode and times. */
370 rip->i_mode = (rip->i_mode & ~ALLPERMS) | (*mode & ALLPERMS);
371 rip->i_update |= MTIME | CTIME;
372
373 *mode = rip->i_mode;
374 return OK;
375 }
376
377 /*
378 * Process a signal.
379 */
380 static void
pfs_signal(int signo)381 pfs_signal(int signo)
382 {
383
384 /* Only check for termination signal, ignore anything else. */
385 if (signo != SIGTERM) return;
386
387 fsdriver_terminate();
388 }
389
390 /*
391 * Initialize PFS.
392 */
393 static int
pfs_init(int __unused type,sef_init_info_t * __unused info)394 pfs_init(int __unused type, sef_init_info_t * __unused info)
395 {
396
397 /* Drop privileges. */
398 if (setuid(SERVICE_UID) != 0)
399 printf("PFS: warning, unable to drop privileges\n");
400
401 return OK;
402 }
403
404 /*
405 * Perform SEF initialization.
406 */
407 static void
pfs_startup(void)408 pfs_startup(void)
409 {
410
411 /* Register initialization callbacks. */
412 sef_setcb_init_fresh(pfs_init);
413 sef_setcb_init_restart(SEF_CB_INIT_RESTART_STATEFUL);
414
415 /* Register signal callbacks. */
416 sef_setcb_signal_handler(pfs_signal);
417
418 /* Let SEF perform startup. */
419 sef_startup();
420 }
421
422 /*
423 * Function call table for the fsdriver library.
424 */
425 static struct fsdriver pfs_table = {
426 .fdr_mount = pfs_mount,
427 .fdr_unmount = pfs_unmount,
428 .fdr_newnode = pfs_newnode,
429 .fdr_putnode = pfs_putnode,
430 .fdr_read = pfs_read,
431 .fdr_write = pfs_write,
432 .fdr_trunc = pfs_trunc,
433 .fdr_stat = pfs_stat,
434 .fdr_chmod = pfs_chmod
435 };
436
437 /*
438 * The main routine of this service.
439 */
440 int
main(void)441 main(void)
442 {
443
444 /* Local startup. */
445 pfs_startup();
446
447 /* The fsdriver library does the actual work here. */
448 fsdriver_task(&pfs_table);
449
450 return EXIT_SUCCESS;
451 }
452