1 /* PFS - Pipe File Server */
3 #include <minix/drivers.h>
4 #include <minix/fsdriver.h>
5 #include <minix/vfsif.h>
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.
16 #define PFS_NR_INODES 512 /* maximum number of inodes in PFS */
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 */
24 ino_t i_num
; /* inode number */
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 */
35 char *i_data
; /* data buffer, for pipes only */
36 size_t i_start
; /* start of data into data buffer */
38 unsigned char i_update
; /* which file times to update? */
39 unsigned char i_free
; /* sanity check: is the inode free? */
41 LIST_ENTRY(inode
) i_next
; /* next element in free list */
42 } inode
[PFS_NR_INODES
];
44 static LIST_HEAD(, inode
) free_inodes
; /* list of free inodes */
47 * Mount the pipe file server.
50 pfs_mount(dev_t __unused dev
, unsigned int __unused flags
,
51 struct fsdriver_node
* node
, unsigned int * res_flags
)
56 LIST_INIT(&free_inodes
); /* initialize the free list */
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..
63 for (i
= PFS_NR_INODES
; i
> 0; i
--) {
66 /* Inode number 0 is reserved. See also pfs_findnode. */
70 LIST_INSERT_HEAD(&free_inodes
, rip
, i_next
);
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.
78 memset(node
, 0, sizeof(*node
));
79 *res_flags
= RES_64BIT
;
85 * Unmount the pipe file server.
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
)
97 if (i
< PFS_NR_INODES
)
98 printf("PFS: unmounting while busy!\n");
102 * Find the node with the corresponding inode number. It must be in use.
104 static struct inode
*
105 pfs_findnode(ino_t ino_nr
)
109 /* Inode numbers are 1-based, because inode number 0 is reserved. */
110 if (ino_nr
< 1 || ino_nr
> PFS_NR_INODES
)
113 rip
= &inode
[ino_nr
- 1];
114 assert(rip
->i_num
== ino_nr
);
116 if (rip
->i_free
== TRUE
)
123 * Create a new, unlinked node. It must be either a pipe or a device file.
126 pfs_newnode(mode_t mode
, uid_t uid
, gid_t gid
, dev_t dev
,
127 struct fsdriver_node
* node
)
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
);
137 if (!isfifo
&& !isdev
)
138 return EINVAL
; /* this means VFS is misbehaving.. */
140 /* Is there a free inode? */
141 if (LIST_EMPTY(&free_inodes
))
144 /* For pipes, we need a buffer. Try to allocate one. */
146 if (isfifo
&& (data
= malloc(PIPE_BUF
)) == NULL
)
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
);
153 assert(rip
->i_free
== TRUE
);
154 rip
->i_free
= FALSE
; /* this is for sanity checks only */
156 /* Initialize the inode's fields. */
161 rip
->i_update
= ATIME
| MTIME
| CTIME
;
165 rip
->i_rdev
= NO_DEV
;
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
;
184 pfs_putnode(ino_t ino_nr
, unsigned int count
)
188 if ((rip
= pfs_findnode(ino_nr
)) == NULL
)
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.
200 /* For pipes, free the inode data buffer. */
201 if (rip
->i_data
!= NULL
) {
206 /* Return the inode to the free list. */
209 LIST_INSERT_HEAD(&free_inodes
, rip
, i_next
);
218 pfs_read(ino_t ino_nr
, struct fsdriver_data
* data
, size_t bytes
,
219 off_t __unused pos
, int __unused call
)
224 /* The target node must be a pipe. */
225 if ((rip
= pfs_findnode(ino_nr
)) == NULL
|| !S_ISFIFO(rip
->i_mode
))
228 /* We can't read beyond the maximum file position. */
229 if (bytes
> PIPE_BUF
)
232 /* Limit the request to how much is in the pipe. */
233 if (bytes
> rip
->i_size
)
236 /* Copy the data to user space. */
237 if ((r
= fsdriver_copyout(data
, 0, rip
->i_data
+ rip
->i_start
,
241 /* Update file size and access time. */
242 rip
->i_size
-= bytes
;
243 rip
->i_start
+= bytes
;
244 rip
->i_update
|= ATIME
;
246 /* Return the number of bytes transferred. */
254 pfs_write(ino_t ino_nr
, struct fsdriver_data
* data
, size_t bytes
,
255 off_t __unused pos
, int __unused call
)
260 /* The target node must be a pipe. */
261 if ((rip
= pfs_findnode(ino_nr
)) == NULL
|| !S_ISFIFO(rip
->i_mode
))
264 /* Check in advance to see if file will grow too big. */
265 if (rip
->i_size
+ bytes
> PIPE_BUF
)
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
274 if (rip
->i_start
> 0) {
276 memmove(rip
->i_data
, rip
->i_data
+ rip
->i_start
,
282 /* Copy the data from user space. */
283 r
= fsdriver_copyin(data
, 0, rip
->i_data
+ rip
->i_size
, bytes
);
287 /* Update file size and times. */
288 rip
->i_size
+= bytes
;
289 rip
->i_update
|= CTIME
| MTIME
;
291 /* Return the number of bytes transferred. */
299 pfs_trunc(ino_t ino_nr
, off_t start_pos
, off_t end_pos
)
303 /* The target node must be a pipe. */
304 if ((rip
= pfs_findnode(ino_nr
)) == NULL
|| !S_ISFIFO(rip
->i_mode
))
307 /* We only support full truncation of pipes. */
308 if (start_pos
!= 0 || end_pos
!= 0)
311 /* Update file size and times. */
313 rip
->i_update
|= CTIME
| MTIME
;
319 * Return node status.
322 pfs_stat(ino_t ino_nr
, struct stat
* statbuf
)
327 if ((rip
= pfs_findnode(ino_nr
)) == NULL
)
330 /* Update the time fields in the inode, if need be. */
331 if (rip
->i_update
!= 0) {
332 now
= clock_time(NULL
);
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
;
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
);
359 * Change node permissions.
362 pfs_chmod(ino_t ino_nr
, mode_t
* mode
)
366 if ((rip
= pfs_findnode(ino_nr
)) == NULL
)
369 /* Update file mode and times. */
370 rip
->i_mode
= (rip
->i_mode
& ~ALLPERMS
) | (*mode
& ALLPERMS
);
371 rip
->i_update
|= MTIME
| CTIME
;
381 pfs_signal(int signo
)
384 /* Only check for termination signal, ignore anything else. */
385 if (signo
!= SIGTERM
) return;
387 fsdriver_terminate();
394 pfs_init(int __unused type
, sef_init_info_t
* __unused info
)
397 /* Drop privileges. */
398 if (setuid(SERVICE_UID
) != 0)
399 printf("PFS: warning, unable to drop privileges\n");
405 * Perform SEF initialization.
411 /* Register initialization callbacks. */
412 sef_setcb_init_fresh(pfs_init
);
413 sef_setcb_init_restart(SEF_CB_INIT_RESTART_STATEFUL
);
415 /* Register signal callbacks. */
416 sef_setcb_signal_handler(pfs_signal
);
418 /* Let SEF perform startup. */
423 * Function call table for the fsdriver library.
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
438 * The main routine of this service.
447 /* The fsdriver library does the actual work here. */
448 fsdriver_task(&pfs_table
);