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