xref: /minix3/minix/fs/pfs/pfs.c (revision b5e2faaaaf60a8b9a02f8d72f64caa56a87eb312)
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
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
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 *
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
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);
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
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 
204 	/* Return the inode to the free list. */
205 	rip->i_free = TRUE;
206 
207 	LIST_INSERT_HEAD(&free_inodes, rip, i_next);
208 
209 	return OK;
210 }
211 
212 /*
213  * Read from a pipe.
214  */
215 static ssize_t
216 pfs_read(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
217 	off_t __unused pos, int __unused call)
218 {
219 	struct inode *rip;
220 	int r;
221 
222 	/* The target node must be a pipe. */
223 	if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
224 		return EINVAL;
225 
226 	/* We can't read beyond the maximum file position. */
227 	if (bytes > PIPE_BUF)
228 		return EFBIG;
229 
230 	/* Limit the request to how much is in the pipe. */
231 	if (bytes > rip->i_size)
232 		bytes = rip->i_size;
233 
234 	/* Copy the data to user space. */
235 	if ((r = fsdriver_copyout(data, 0, rip->i_data + rip->i_start,
236 	    bytes)) != OK)
237 		return r;
238 
239 	/* Update file size and access time. */
240 	rip->i_size -= bytes;
241 	rip->i_start += bytes;
242 	rip->i_update |= ATIME;
243 
244 	/* Return the number of bytes transferred. */
245 	return bytes;
246 }
247 
248 /*
249  * Write to a pipe.
250  */
251 static ssize_t
252 pfs_write(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
253 	off_t __unused pos, int __unused call)
254 {
255 	struct inode *rip;
256 	int r;
257 
258 	/* The target node must be a pipe. */
259 	if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
260 		return EINVAL;
261 
262 	/* Check in advance to see if file will grow too big. */
263 	if (rip->i_size + bytes > PIPE_BUF)
264 		return EFBIG;
265 
266 	/*
267 	 * Move any previously remaining data to the front of the buffer.
268 	 * Doing so upon writes rather than reads saves on memory moves when
269 	 * there are many small reads.  Not using the buffer circularly saves
270 	 * on kernel calls.
271 	 */
272 	if (rip->i_start > 0) {
273 		if (rip->i_size > 0)
274 			memmove(rip->i_data, rip->i_data + rip->i_start,
275 			    rip->i_size);
276 
277 		rip->i_start = 0;
278 	}
279 
280 	/* Copy the data from user space. */
281 	r = fsdriver_copyin(data, 0, rip->i_data + rip->i_size, bytes);
282 	if (r != OK)
283 		return r;
284 
285 	/* Update file size and times. */
286 	rip->i_size += bytes;
287 	rip->i_update |= CTIME | MTIME;
288 
289 	/* Return the number of bytes transferred. */
290 	return bytes;
291 }
292 
293 /*
294  * Truncate a pipe.
295  */
296 static int
297 pfs_trunc(ino_t ino_nr, off_t start_pos, off_t end_pos)
298 {
299 	struct inode *rip;
300 
301 	/* The target node must be a pipe. */
302 	if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
303 		return EINVAL;
304 
305 	/* We only support full truncation of pipes. */
306 	if (start_pos != 0 || end_pos != 0)
307 		return EINVAL;
308 
309 	/* Update file size and times. */
310 	rip->i_size = 0;
311 	rip->i_update |= CTIME | MTIME;
312 
313 	return OK;
314 }
315 
316 /*
317  * Return node status.
318  */
319 static int
320 pfs_stat(ino_t ino_nr, struct stat * statbuf)
321 {
322 	struct inode *rip;
323 	time_t now;
324 
325 	if ((rip = pfs_findnode(ino_nr)) == NULL)
326 		return EINVAL;
327 
328 	/* Update the time fields in the inode, if need be. */
329 	if (rip->i_update != 0) {
330 		now = clock_time(NULL);
331 
332 		if (rip->i_update & ATIME) rip->i_atime = now;
333 		if (rip->i_update & MTIME) rip->i_mtime = now;
334 		if (rip->i_update & CTIME) rip->i_ctime = now;
335 
336 		rip->i_update = 0;
337 	}
338 
339 	/* Fill the stat buffer. */
340 	statbuf->st_dev = rip->i_rdev;	/* workaround for old socketpair bug */
341 	statbuf->st_mode = rip->i_mode;
342 	statbuf->st_nlink = 0;
343 	statbuf->st_uid = rip->i_uid;
344 	statbuf->st_gid = rip->i_gid;
345 	statbuf->st_rdev = rip->i_rdev;
346 	statbuf->st_size = rip->i_size;
347 	statbuf->st_atime = rip->i_atime;
348 	statbuf->st_mtime = rip->i_mtime;
349 	statbuf->st_ctime = rip->i_ctime;
350 	statbuf->st_blksize = PIPE_BUF;
351 	statbuf->st_blocks = howmany(rip->i_size, S_BLKSIZE);
352 
353 	return OK;
354 }
355 
356 /*
357  * Change node permissions.
358  */
359 static int
360 pfs_chmod(ino_t ino_nr, mode_t * mode)
361 {
362 	struct inode *rip;
363 
364 	if ((rip = pfs_findnode(ino_nr)) == NULL)
365 		return EINVAL;
366 
367 	/* Update file mode and times. */
368 	rip->i_mode = (rip->i_mode & ~ALLPERMS) | (*mode & ALLPERMS);
369 	rip->i_update |= MTIME | CTIME;
370 
371 	*mode = rip->i_mode;
372 	return OK;
373 }
374 
375 /*
376  * Process a signal.
377  */
378 static void
379 pfs_signal(int signo)
380 {
381 
382 	/* Only check for termination signal, ignore anything else. */
383 	if (signo != SIGTERM) return;
384 
385 	fsdriver_terminate();
386 }
387 
388 /*
389  * Initialize PFS.
390  */
391 static int
392 pfs_init(int __unused type, sef_init_info_t * __unused info)
393 {
394 
395 	/* Drop privileges. */
396 	if (setuid(SERVICE_UID) != 0)
397 		printf("PFS: warning, unable to drop privileges\n");
398 
399 	return OK;
400 }
401 
402 /*
403  * Perform SEF initialization.
404  */
405 static void
406 pfs_startup(void)
407 {
408 
409 	/* Register initialization callbacks. */
410 	sef_setcb_init_fresh(pfs_init);
411 	sef_setcb_init_restart(SEF_CB_INIT_RESTART_STATEFUL);
412 
413 	/* Register signal callbacks. */
414 	sef_setcb_signal_handler(pfs_signal);
415 
416 	/* Let SEF perform startup. */
417 	sef_startup();
418 }
419 
420 /*
421  * Function call table for the fsdriver library.
422  */
423 static struct fsdriver pfs_table = {
424 	.fdr_mount	= pfs_mount,
425 	.fdr_unmount	= pfs_unmount,
426 	.fdr_newnode	= pfs_newnode,
427 	.fdr_putnode	= pfs_putnode,
428 	.fdr_read	= pfs_read,
429 	.fdr_write	= pfs_write,
430 	.fdr_trunc	= pfs_trunc,
431 	.fdr_stat	= pfs_stat,
432 	.fdr_chmod	= pfs_chmod
433 };
434 
435 /*
436  * The main routine of this service.
437  */
438 int
439 main(void)
440 {
441 
442 	/* Local startup. */
443 	pfs_startup();
444 
445 	/* The fsdriver library does the actual work here. */
446 	fsdriver_task(&pfs_table);
447 
448 	return EXIT_SUCCESS;
449 }
450