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