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