xref: /minix3/minix/fs/procfs/tree.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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