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