1 /* ProcFS - tree.c - dynamic PID tree management and hook implementations */ 2 3 #include "inc.h" 4 5 typedef struct proc ixfer_proc_t; 6 typedef struct fproc ixfer_fproc_t; 7 typedef struct mproc ixfer_mproc_t; 8 9 ixfer_proc_t proc[NR_PROCS + NR_TASKS]; 10 ixfer_mproc_t mproc[NR_PROCS]; 11 ixfer_fproc_t fproc[NR_PROCS]; 12 13 static int nr_pid_entries; 14 15 /* 16 * Return whether the given slot is in use by a process. 17 */ 18 static int 19 slot_in_use(int slot) 20 { 21 22 /* 23 * For kernel tasks, check only the kernel slot. Tasks do not have a 24 * PM/VFS process slot. 25 */ 26 if (slot < NR_TASKS) 27 return (proc[slot].p_rts_flags != RTS_SLOT_FREE); 28 29 /* For regular processes, check only the PM slot. Do not check the 30 * kernel slot, because that would skip zombie processes. The PID 31 * check should be redundant, but if it fails, procfs could crash. 32 */ 33 return ((mproc[slot - NR_TASKS].mp_flags & IN_USE) && 34 mproc[slot - NR_TASKS].mp_pid != 0); 35 } 36 37 /* 38 * Check if the owner user and group ID of the inode are still in sync with 39 * the current effective user and group ID of the given process. 40 */ 41 static int 42 check_owner(struct inode * node, int slot) 43 { 44 struct inode_stat stat; 45 46 if (slot < NR_TASKS) return TRUE; 47 48 get_inode_stat(node, &stat); 49 50 return (stat.uid == mproc[slot - NR_TASKS].mp_effuid && 51 stat.gid == mproc[slot - NR_TASKS].mp_effgid); 52 } 53 54 /* 55 * Fill in an inode_stat structure for the given process slot and per-PID file 56 * index (or NO_INDEX for the process subdirectory root). 57 */ 58 static void 59 make_stat(struct inode_stat * stat, int slot, int index) 60 { 61 62 if (index == NO_INDEX) 63 stat->mode = DIR_ALL_MODE; 64 else 65 stat->mode = pid_files[index].mode; 66 67 if (slot < NR_TASKS) { 68 stat->uid = SUPER_USER; 69 stat->gid = SUPER_USER; 70 } else { 71 stat->uid = mproc[slot - NR_TASKS].mp_effuid; 72 stat->gid = mproc[slot - NR_TASKS].mp_effgid; 73 } 74 75 stat->size = 0; 76 stat->dev = NO_DEV; 77 } 78 79 /* 80 * Return whether the given node is a PID directory. 81 */ 82 static int 83 dir_is_pid(struct inode *node) 84 { 85 86 return (get_parent_inode(node) == get_root_inode() && 87 get_inode_index(node) != NO_INDEX); 88 } 89 90 /* 91 * Get the process table from the kernel. Check the magic number in the table 92 * entries. 93 */ 94 static int 95 update_proc_table(void) 96 { 97 int r, slot; 98 99 if ((r = sys_getproctab(proc)) != OK) return r; 100 101 for (slot = 0; slot < NR_PROCS + NR_TASKS; slot++) { 102 if (proc[slot].p_magic != PMAGIC) { 103 printf("PROCFS: system version mismatch!\n"); 104 105 return EINVAL; 106 } 107 } 108 109 return OK; 110 } 111 112 /* 113 * Get the process table from PM. Check the magic number in the table entries. 114 */ 115 static int 116 update_mproc_table(void) 117 { 118 int r, slot; 119 120 r = getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc, sizeof(mproc)); 121 if (r != OK) return r; 122 123 for (slot = 0; slot < NR_PROCS; slot++) { 124 if (mproc[slot].mp_magic != MP_MAGIC) { 125 printf("PROCFS: PM version mismatch!\n"); 126 127 return EINVAL; 128 } 129 } 130 131 return OK; 132 } 133 134 /* 135 * Get the process table from VFS. 136 */ 137 static int 138 update_fproc_table(void) 139 { 140 141 return getsysinfo(VFS_PROC_NR, SI_PROC_TAB, fproc, sizeof(fproc)); 142 } 143 144 /* 145 * Get the process tables from the kernel, PM, and VFS. 146 */ 147 static int 148 update_tables(void) 149 { 150 int r; 151 152 if ((r = update_proc_table()) != OK) return r; 153 154 if ((r = update_mproc_table()) != OK) return r; 155 156 if ((r = update_fproc_table()) != OK) return r; 157 158 return OK; 159 } 160 161 /* 162 * Initialize this module, before VTreeFS is started. As part of the process, 163 * check if we're not compiled against a kernel different from the one that is 164 * running at the moment. 165 */ 166 int 167 init_tree(void) 168 { 169 int i, r; 170 171 if ((r = update_tables()) != OK) 172 return r; 173 174 /* 175 * Get the maximum number of entries that we may add to each PID's 176 * directory. We could just default to a large value, but why not get 177 * it right? 178 */ 179 for (i = 0; pid_files[i].name != NULL; i++); 180 181 nr_pid_entries = i; 182 183 return OK; 184 } 185 186 /* 187 * Out of inodes - the NR_INODES value is set too low. We can not do much, but 188 * we might be able to continue with degraded functionality, so do not panic. 189 * If the NR_INODES value is not below the *crucial* minimum, the symptom of 190 * this case will be an incomplete listing of the main proc directory. 191 */ 192 void 193 out_of_inodes(void) 194 { 195 static int warned = FALSE; 196 197 if (warned == FALSE) { 198 printf("PROCFS: out of inodes!\n"); 199 200 warned = TRUE; 201 } 202 } 203 204 /* 205 * Regenerate the set of PID directories in the root directory of the file 206 * system. Add new directories and delete old directories as appropriate; 207 * leave unchanged those that should remain the same. 208 */ 209 static void 210 construct_pid_dirs(void) 211 { 212 /* 213 * We have to make two passes. Otherwise, we would trigger a vtreefs 214 * assert when we add an entry for a PID before deleting the previous 215 * entry for that PID. While rare, such rapid PID reuse does occur in 216 * practice. 217 */ 218 struct inode *root, *node; 219 struct inode_stat stat; 220 char name[PNAME_MAX+1]; 221 pid_t pid; 222 int i; 223 224 root = get_root_inode(); 225 226 /* First pass: delete old entries. */ 227 for (i = 0; i < NR_PROCS + NR_TASKS; i++) { 228 /* Do we already have an inode associated with this slot? */ 229 node = get_inode_by_index(root, i); 230 if (node == NULL) 231 continue; 232 233 /* 234 * If the process slot is not in use, delete the associated 235 * inode. 236 */ 237 if (!slot_in_use(i)) { 238 delete_inode(node); 239 240 continue; 241 } 242 243 /* Otherwise, get the process ID. */ 244 if (i < NR_TASKS) 245 pid = (pid_t)(i - NR_TASKS); 246 else 247 pid = mproc[i - NR_TASKS].mp_pid; 248 249 /* 250 * If there is an old entry, see if the pid matches the current 251 * entry, and the owner is still the same. Otherwise, delete 252 * the old entry first. We reconstruct the entire subtree even 253 * if only the owner changed, for security reasons: if a 254 * process could keep open a file or directory across the owner 255 * change, it might be able to access information it shouldn't. 256 */ 257 if (pid != (pid_t)get_inode_cbdata(node) || 258 !check_owner(node, i)) 259 delete_inode(node); 260 } 261 262 /* Second pass: add new entries. */ 263 for (i = 0; i < NR_PROCS + NR_TASKS; i++) { 264 /* If the process slot is not in use, skip this slot. */ 265 if (!slot_in_use(i)) 266 continue; 267 268 /* 269 * If we have an inode associated with this slot, we have 270 * already checked it to be up-to-date above. 271 */ 272 if (get_inode_by_index(root, i) != NULL) 273 continue; 274 275 /* Get the process ID. */ 276 if (i < NR_TASKS) 277 pid = (pid_t)(i - NR_TASKS); 278 else 279 pid = mproc[i - NR_TASKS].mp_pid; 280 281 /* Add the entry for the process slot. */ 282 snprintf(name, PNAME_MAX + 1, "%d", pid); 283 284 make_stat(&stat, i, NO_INDEX); 285 286 node = add_inode(root, name, i, &stat, nr_pid_entries, 287 (cbdata_t)pid); 288 289 if (node == NULL) 290 out_of_inodes(); 291 } 292 } 293 294 /* 295 * Construct one file in a PID directory, if a file with the given name should 296 * exist at all. 297 */ 298 static void 299 make_one_pid_entry(struct inode * parent, char * name, int slot) 300 { 301 struct inode *node; 302 struct inode_stat stat; 303 int i; 304 305 /* Don't readd if it is already there. */ 306 node = get_inode_by_name(parent, name); 307 if (node != NULL) 308 return; 309 310 /* Only add the file if it is a known, registered name. */ 311 for (i = 0; pid_files[i].name != NULL; i++) { 312 if (!strcmp(name, pid_files[i].name)) { 313 make_stat(&stat, slot, i); 314 315 node = add_inode(parent, name, i, &stat, (index_t)0, 316 (cbdata_t)0); 317 318 if (node == NULL) 319 out_of_inodes(); 320 321 break; 322 } 323 } 324 } 325 326 /* 327 * Construct all files in a PID directory. 328 */ 329 static void 330 make_all_pid_entries(struct inode * parent, int slot) 331 { 332 struct inode *node; 333 struct inode_stat stat; 334 int i; 335 336 for (i = 0; pid_files[i].name != NULL; i++) { 337 node = get_inode_by_index(parent, i); 338 if (node != NULL) 339 continue; 340 341 make_stat(&stat, slot, i); 342 343 node = add_inode(parent, pid_files[i].name, i, &stat, 344 (index_t)0, (cbdata_t)0); 345 346 if (node == NULL) 347 out_of_inodes(); 348 } 349 } 350 351 /* 352 * Construct one requested file entry, or all file entries, in a PID directory. 353 */ 354 static void 355 construct_pid_entries(struct inode * parent, char * name) 356 { 357 int slot; 358 359 slot = get_inode_index(parent); 360 assert(slot >= 0 && slot < NR_TASKS + NR_PROCS); 361 362 /* If this process is already gone, delete the directory now. */ 363 if (!slot_in_use(slot)) { 364 delete_inode(parent); 365 366 return; 367 } 368 369 /* 370 * If a specific file name is being looked up, see if we have to add 371 * an inode for that file. If the directory contents are being 372 * retrieved, add all files that have not yet been added. 373 */ 374 if (name != NULL) 375 make_one_pid_entry(parent, name, slot); 376 else 377 make_all_pid_entries(parent, slot); 378 } 379 380 /* 381 * Data is requested from one of the files in a PID directory. Call the 382 * function that is responsible for generating the data for that file. 383 */ 384 static void 385 pid_read(struct inode * node) 386 { 387 struct inode *parent; 388 int slot, index; 389 390 /* 391 * Get the slot number of the process. Note that this currently will 392 * not work for files not in the top-level pid subdirectory. 393 */ 394 parent = get_parent_inode(node); 395 396 slot = get_inode_index(parent); 397 398 /* Get this file's index number. */ 399 index = get_inode_index(node); 400 401 /* Call the handler procedure for the file. */ 402 ((void (*)(int))pid_files[index].data)(slot); 403 } 404 405 /* 406 * The contents of a symbolic link in a PID directory are requested. This 407 * function is a placeholder for future use. 408 */ 409 static int 410 pid_link(struct inode * __unused node, char * ptr, int max) 411 { 412 413 /* Nothing yet. */ 414 strlcpy(ptr, "", max); 415 416 return OK; 417 } 418 419 /* 420 * Path name resolution hook, for a specific parent and name pair. If needed, 421 * update our own view of the system first; after that, determine whether we 422 * need to (re)generate certain files. 423 */ 424 int 425 lookup_hook(struct inode * parent, char * name, cbdata_t __unused cbdata) 426 { 427 static clock_t last_update = 0; 428 clock_t now; 429 430 /* 431 * Update lazily for lookups, as this gets too expensive otherwise. 432 * Alternative: pull in only PM's table? 433 */ 434 now = getticks(); 435 436 if (last_update != now) { 437 update_tables(); 438 439 last_update = now; 440 } 441 442 /* 443 * If the parent is the root directory, we must now reconstruct all 444 * entries, because some of them might have been garbage collected. 445 * We must update the entire tree at once; if we update individual 446 * entries, we risk name collisions. 447 * 448 * If the parent is a process directory, we may need to (re)construct 449 * the entry being looked up. 450 */ 451 if (parent == get_root_inode()) 452 construct_pid_dirs(); 453 else if (dir_is_pid(parent)) 454 /* 455 * We might now have deleted our current containing directory; 456 * construct_pid_entries() will take care of this case. 457 */ 458 construct_pid_entries(parent, name); 459 else 460 /* TODO: skip updating the main tables in this case. */ 461 service_lookup(parent, now); 462 463 return OK; 464 } 465 466 /* 467 * Directory entry retrieval hook, for potentially all files in a directory. 468 * Make sure that all files that are supposed to be returned, are actually part 469 * of the virtual tree. 470 */ 471 int 472 getdents_hook(struct inode * node, cbdata_t __unused cbdata) 473 { 474 475 if (node == get_root_inode()) { 476 update_tables(); 477 478 construct_pid_dirs(); 479 } else if (dir_is_pid(node)) 480 construct_pid_entries(node, NULL /*name*/); 481 else 482 service_getdents(node); 483 484 return OK; 485 } 486 487 /* 488 * Regular file read hook. Call the appropriate callback function to generate 489 * and return the data. 490 */ 491 ssize_t 492 read_hook(struct inode * node, char * ptr, size_t len, off_t off, 493 cbdata_t cbdata) 494 { 495 struct inode *parent; 496 497 buf_init(ptr, len, off); 498 499 /* Populate the buffer with the proper content. */ 500 if (get_inode_index(node) != NO_INDEX) { 501 parent = get_parent_inode(node); 502 503 /* The PID directories are indexed; service/ is not. */ 504 if (get_inode_index(parent) != NO_INDEX) 505 pid_read(node); 506 else 507 service_read(node); 508 } else 509 ((void (*)(void))cbdata)(); 510 511 return buf_result(); 512 } 513 514 /* 515 * Symbolic link resolution hook. Not used yet. 516 */ 517 int 518 rdlink_hook(struct inode * node, char * ptr, size_t max, 519 cbdata_t __unused cbdata) 520 { 521 struct inode *parent; 522 523 /* Get the parent inode. */ 524 parent = get_parent_inode(node); 525 526 /* If the parent inode is a pid directory, call the pid handler. */ 527 if (parent != NULL && dir_is_pid(parent)) 528 pid_link(node, ptr, max); 529 530 return OK; 531 } 532