Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/pseudofs/pseudofs.c
39483 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 2001 Dag-Erling Smørgrav
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer
12
* in this position and unchanged.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
* 3. The name of the author may not be used to endorse or promote products
17
* derived from this software without specific prior written permission.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
*/
30
31
#include <sys/cdefs.h>
32
#include "opt_pseudofs.h"
33
34
#include <sys/param.h>
35
#include <sys/kernel.h>
36
#include <sys/systm.h>
37
#include <sys/lock.h>
38
#include <sys/malloc.h>
39
#include <sys/module.h>
40
#include <sys/mount.h>
41
#include <sys/mutex.h>
42
#include <sys/proc.h>
43
#include <sys/sbuf.h>
44
#include <sys/sysctl.h>
45
#include <sys/vnode.h>
46
47
#include <fs/pseudofs/pseudofs.h>
48
#include <fs/pseudofs/pseudofs_internal.h>
49
50
static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes");
51
52
SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
53
"pseudofs");
54
55
#ifdef PSEUDOFS_TRACE
56
int pfs_trace;
57
SYSCTL_INT(_vfs_pfs, OID_AUTO, trace, CTLFLAG_RW, &pfs_trace, 0,
58
"enable tracing of pseudofs vnode operations");
59
#endif
60
61
#if PFS_FSNAMELEN != MFSNAMELEN
62
#error "PFS_FSNAMELEN is not equal to MFSNAMELEN"
63
#endif
64
65
/*
66
* Allocate and initialize a node
67
*/
68
static struct pfs_node *
69
pfs_alloc_node_flags(struct pfs_info *pi, const char *name, pfs_type_t type, int flags)
70
{
71
struct pfs_node *pn;
72
int malloc_flags;
73
size_t len;
74
75
len = strlen(name);
76
KASSERT(len < PFS_NAMELEN,
77
("%s(): node name is too long", __func__));
78
if (flags & PFS_NOWAIT)
79
malloc_flags = M_NOWAIT | M_ZERO;
80
else
81
malloc_flags = M_WAITOK | M_ZERO;
82
pn = malloc(sizeof(*pn) + len + 1, M_PFSNODES, malloc_flags);
83
if (pn == NULL)
84
return (NULL);
85
mtx_init(&pn->pn_mutex, "pfs_node", NULL, MTX_DEF | MTX_DUPOK);
86
memcpy(pn->pn_name, name, len);
87
pn->pn_type = type;
88
pn->pn_info = pi;
89
return (pn);
90
}
91
92
static struct pfs_node *
93
pfs_alloc_node(struct pfs_info *pi, const char *name, pfs_type_t type)
94
{
95
return (pfs_alloc_node_flags(pi, name, type, 0));
96
}
97
98
/*
99
* Add a node to a directory
100
*/
101
static int
102
pfs_add_node(struct pfs_node *parent, struct pfs_node *pn)
103
{
104
struct pfs_node *iter;
105
106
KASSERT(parent != NULL,
107
("%s(): parent is NULL", __func__));
108
KASSERT(pn->pn_parent == NULL,
109
("%s(): node already has a parent", __func__));
110
KASSERT(parent->pn_info != NULL,
111
("%s(): parent has no pn_info", __func__));
112
KASSERT(parent->pn_type == pfstype_dir ||
113
parent->pn_type == pfstype_procdir ||
114
parent->pn_type == pfstype_root,
115
("%s(): parent is not a directory", __func__));
116
117
#ifdef INVARIANTS
118
/* XXX no locking! */
119
if (pn->pn_type == pfstype_procdir)
120
for (iter = parent; iter != NULL; iter = iter->pn_parent)
121
KASSERT(iter->pn_type != pfstype_procdir,
122
("%s(): nested process directories", __func__));
123
for (iter = parent->pn_nodes; iter != NULL; iter = iter->pn_next) {
124
if (pn->pn_type == pfstype_procdir)
125
KASSERT(iter->pn_type != pfstype_procdir,
126
("%s(): sibling process directories", __func__));
127
}
128
#endif
129
130
pn->pn_parent = parent;
131
pfs_fileno_alloc(pn);
132
pfs_lock(parent);
133
for (iter = parent->pn_nodes; iter != NULL; iter = iter->pn_next) {
134
if (strcmp(pn->pn_name, iter->pn_name) != 0)
135
continue;
136
printf("pfs_add_node: homonymous siblings: '%s/%s' type %d\n",
137
parent->pn_name, pn->pn_name, pn->pn_type);
138
/* Do not detach, because we are not yet attached. */
139
pn->pn_parent = NULL;
140
pfs_unlock(parent);
141
return (EEXIST);
142
}
143
144
145
if ((parent->pn_flags & PFS_PROCDEP) != 0)
146
pn->pn_flags |= PFS_PROCDEP;
147
if (parent->pn_nodes == NULL) {
148
KASSERT(parent->pn_last_node == NULL,
149
("%s(): pn_last_node not NULL", __func__));
150
parent->pn_nodes = pn;
151
parent->pn_last_node = pn;
152
} else {
153
KASSERT(parent->pn_last_node != NULL,
154
("%s(): pn_last_node is NULL", __func__));
155
KASSERT(parent->pn_last_node->pn_next == NULL,
156
("%s(): pn_last_node->pn_next not NULL", __func__));
157
parent->pn_last_node->pn_next = pn;
158
parent->pn_last_node = pn;
159
}
160
pfs_unlock(parent);
161
return (0);
162
}
163
164
/*
165
* Detach a node from its parent
166
*/
167
static void
168
pfs_detach_node(struct pfs_node *pn)
169
{
170
struct pfs_node *node, *parent = pn->pn_parent;
171
struct pfs_node **iter;
172
173
KASSERT(parent != NULL, ("%s(): node has no parent", __func__));
174
KASSERT(parent->pn_info == pn->pn_info,
175
("%s(): parent has different pn_info", __func__));
176
177
pfs_lock(parent);
178
if (pn == parent->pn_last_node) {
179
if (pn == pn->pn_nodes) {
180
parent->pn_last_node = NULL;
181
} else {
182
for (node = parent->pn_nodes;
183
node->pn_next != pn; node = node->pn_next)
184
continue;
185
parent->pn_last_node = node;
186
}
187
}
188
iter = &parent->pn_nodes;
189
while (*iter != NULL) {
190
if (*iter == pn) {
191
*iter = pn->pn_next;
192
break;
193
}
194
iter = &(*iter)->pn_next;
195
}
196
pn->pn_parent = NULL;
197
pfs_unlock(parent);
198
}
199
200
/*
201
* Add . and .. to a directory
202
*/
203
static int
204
pfs_fixup_dir_flags(struct pfs_node *parent, int flags)
205
{
206
struct pfs_node *dot, *dotdot;
207
int rc;
208
209
dot = pfs_alloc_node_flags(parent->pn_info, ".", pfstype_this, flags);
210
if (dot == NULL)
211
return (ENOMEM);
212
dotdot = pfs_alloc_node_flags(parent->pn_info, "..", pfstype_parent, flags);
213
if (dotdot == NULL) {
214
pfs_destroy(dot);
215
return (ENOMEM);
216
}
217
rc = pfs_add_node(parent, dot);
218
if (rc == 0)
219
rc = pfs_add_node(parent, dotdot);
220
if (rc != 0) {
221
pfs_destroy(dot);
222
pfs_destroy(dotdot);
223
}
224
return (rc);
225
}
226
227
static void
228
pfs_fixup_dir(struct pfs_node *parent)
229
{
230
231
pfs_fixup_dir_flags(parent, 0);
232
}
233
234
/*
235
* Create a directory
236
*/
237
int
238
pfs_create_dir(struct pfs_node *parent, struct pfs_node **opn,
239
const char *name, pfs_attr_t attr, pfs_vis_t vis,
240
pfs_destroy_t destroy, int flags)
241
{
242
struct pfs_node *pdir, *pn;
243
int rc;
244
245
/* Preserve in case the caller is reusing the one pointer for both. */
246
pdir = parent;
247
if (opn != NULL)
248
*opn = NULL;
249
pn = pfs_alloc_node_flags(pdir->pn_info, name,
250
(flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir, flags);
251
if (pn == NULL)
252
return (ENOMEM);
253
pn->pn_attr = attr;
254
pn->pn_vis = vis;
255
pn->pn_destroy = destroy;
256
pn->pn_flags = flags;
257
rc = pfs_add_node(pdir, pn);
258
if (rc == 0)
259
rc = pfs_fixup_dir_flags(pn, flags);
260
if (rc != 0) {
261
pfs_destroy(pn);
262
pn = NULL;
263
} else if (opn != NULL) {
264
*opn = pn;
265
}
266
267
return (rc);
268
}
269
270
/*
271
* Create a file
272
*/
273
int
274
pfs_create_file(struct pfs_node *parent, struct pfs_node **opn,
275
const char *name, pfs_fill_t fill, pfs_attr_t attr,
276
pfs_vis_t vis, pfs_destroy_t destroy, int flags)
277
{
278
struct pfs_node *pn;
279
int rc;
280
281
if (opn != NULL)
282
*opn = NULL;
283
pn = pfs_alloc_node_flags(parent->pn_info, name, pfstype_file, flags);
284
if (pn == NULL)
285
return (ENOMEM);
286
287
pn->pn_fill = fill;
288
pn->pn_attr = attr;
289
pn->pn_vis = vis;
290
pn->pn_destroy = destroy;
291
pn->pn_flags = flags;
292
if ((rc = pfs_add_node(parent, pn)) != 0) {
293
pfs_destroy(pn);
294
pn = NULL;
295
} else if (opn != NULL) {
296
*opn = pn;
297
}
298
299
return (rc);
300
}
301
302
/*
303
* Create a symlink
304
*/
305
int
306
pfs_create_link(struct pfs_node *parent, struct pfs_node **opn,
307
const char *name, pfs_fill_t fill, pfs_attr_t attr,
308
pfs_vis_t vis, pfs_destroy_t destroy, int flags)
309
{
310
struct pfs_node *pn;
311
int rc;
312
313
if (opn != NULL)
314
*opn = NULL;
315
pn = pfs_alloc_node_flags(parent->pn_info, name, pfstype_symlink, flags);
316
if (pn == NULL)
317
return (ENOMEM);
318
319
pn->pn_fill = fill;
320
pn->pn_attr = attr;
321
pn->pn_vis = vis;
322
pn->pn_destroy = destroy;
323
pn->pn_flags = flags;
324
if ((rc = pfs_add_node(parent, pn)) != 0) {
325
pfs_destroy(pn);
326
pn = NULL;
327
} else if (opn != NULL) {
328
*opn = pn;
329
}
330
331
return (rc);
332
}
333
334
/*
335
* Locate a node by name
336
*/
337
struct pfs_node *
338
pfs_find_node(struct pfs_node *parent, const char *name)
339
{
340
struct pfs_node *pn;
341
342
pfs_lock(parent);
343
for (pn = parent->pn_nodes; pn != NULL; pn = pn->pn_next)
344
if (strcmp(pn->pn_name, name) == 0)
345
break;
346
pfs_unlock(parent);
347
return (pn);
348
}
349
350
/*
351
* Destroy a node and all its descendants. If the node to be destroyed
352
* has a parent, the parent's mutex must be held.
353
*/
354
int
355
pfs_destroy(struct pfs_node *pn)
356
{
357
struct pfs_node *iter;
358
359
KASSERT(pn != NULL,
360
("%s(): node is NULL", __func__));
361
KASSERT(pn->pn_info != NULL,
362
("%s(): node has no pn_info", __func__));
363
364
if (pn->pn_parent)
365
pfs_detach_node(pn);
366
367
/* destroy children */
368
if (pn->pn_type == pfstype_dir ||
369
pn->pn_type == pfstype_procdir ||
370
pn->pn_type == pfstype_root) {
371
pfs_lock(pn);
372
while (pn->pn_nodes != NULL) {
373
iter = pn->pn_nodes;
374
pn->pn_nodes = iter->pn_next;
375
iter->pn_parent = NULL;
376
pfs_unlock(pn);
377
pfs_destroy(iter);
378
pfs_lock(pn);
379
}
380
pfs_unlock(pn);
381
}
382
383
/* revoke vnodes and fileno */
384
pfs_purge(pn);
385
386
/* callback to free any private resources */
387
if (pn->pn_destroy != NULL)
388
pn_destroy(pn);
389
390
/* destroy the node */
391
pfs_fileno_free(pn);
392
mtx_destroy(&pn->pn_mutex);
393
free(pn, M_PFSNODES);
394
395
return (0);
396
}
397
398
/*
399
* Mount a pseudofs instance
400
*/
401
int
402
pfs_mount(struct pfs_info *pi, struct mount *mp)
403
{
404
struct statfs *sbp;
405
406
if (mp->mnt_flag & MNT_UPDATE)
407
return (EOPNOTSUPP);
408
409
MNT_ILOCK(mp);
410
mp->mnt_flag |= MNT_LOCAL;
411
mp->mnt_kern_flag |= MNTK_NOMSYNC;
412
MNT_IUNLOCK(mp);
413
mp->mnt_data = pi;
414
vfs_getnewfsid(mp);
415
416
sbp = &mp->mnt_stat;
417
vfs_mountedfrom(mp, pi->pi_name);
418
sbp->f_bsize = PAGE_SIZE;
419
sbp->f_iosize = PAGE_SIZE;
420
sbp->f_blocks = 2;
421
sbp->f_bfree = 2;
422
sbp->f_bavail = 2;
423
sbp->f_files = 0;
424
sbp->f_ffree = 0;
425
426
return (0);
427
}
428
429
/*
430
* Compatibility shim for old mount(2) system call
431
*/
432
int
433
pfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
434
{
435
int error;
436
437
error = kernel_mount(ma, flags);
438
return (error);
439
}
440
441
/*
442
* Unmount a pseudofs instance
443
*/
444
int
445
pfs_unmount(struct mount *mp, int mntflags)
446
{
447
int error;
448
449
error = vflush(mp, 0, (mntflags & MNT_FORCE) ? FORCECLOSE : 0,
450
curthread);
451
return (error);
452
}
453
454
/*
455
* Return a root vnode
456
*/
457
int
458
pfs_root(struct mount *mp, int flags, struct vnode **vpp)
459
{
460
struct pfs_info *pi;
461
462
pi = (struct pfs_info *)mp->mnt_data;
463
return (pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID));
464
}
465
466
/*
467
* Return filesystem stats
468
*/
469
int
470
pfs_statfs(struct mount *mp, struct statfs *sbp)
471
{
472
/* no-op: always called with mp->mnt_stat */
473
return (0);
474
}
475
476
/*
477
* Initialize a pseudofs instance
478
*/
479
int
480
pfs_init(struct pfs_info *pi, struct vfsconf *vfc)
481
{
482
struct pfs_node *root;
483
int error;
484
485
pfs_fileno_init(pi);
486
487
/* set up the root directory */
488
root = pfs_alloc_node(pi, "/", pfstype_root);
489
pi->pi_root = root;
490
pfs_fileno_alloc(root);
491
pfs_fixup_dir(root);
492
493
/* construct file hierarchy */
494
error = (pi->pi_init)(pi, vfc);
495
if (error) {
496
pfs_destroy(root);
497
pi->pi_root = NULL;
498
pfs_fileno_uninit(pi);
499
return (error);
500
}
501
502
if (bootverbose)
503
printf("%s registered\n", pi->pi_name);
504
return (0);
505
}
506
507
/*
508
* Destroy a pseudofs instance
509
*/
510
int
511
pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc)
512
{
513
int error;
514
515
pfs_destroy(pi->pi_root);
516
pi->pi_root = NULL;
517
pfs_fileno_uninit(pi);
518
if (bootverbose)
519
printf("%s unregistered\n", pi->pi_name);
520
error = (pi->pi_uninit)(pi, vfc);
521
return (error);
522
}
523
524
/*
525
* Handle load / unload events
526
*/
527
static int
528
pfs_modevent(module_t mod, int evt, void *arg)
529
{
530
switch (evt) {
531
case MOD_LOAD:
532
pfs_vncache_load();
533
break;
534
case MOD_UNLOAD:
535
case MOD_SHUTDOWN:
536
pfs_vncache_unload();
537
break;
538
default:
539
return EOPNOTSUPP;
540
break;
541
}
542
return 0;
543
}
544
545
/*
546
* Module declaration
547
*/
548
static moduledata_t pseudofs_data = {
549
"pseudofs",
550
pfs_modevent,
551
NULL
552
};
553
DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST);
554
MODULE_VERSION(pseudofs, 1);
555
556