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 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 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 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 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 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 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 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 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 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 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