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