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