Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/fdescfs/fdesc_vnops.c
39586 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 1992, 1993
5
* The Regents of the University of California. All rights reserved.
6
*
7
* This code is derived from software donated to Berkeley by
8
* Jan-Simon Pendry.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
* 3. Neither the name of the University nor the names of its contributors
19
* may be used to endorse or promote products derived from this software
20
* without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
* SUCH DAMAGE.
33
*/
34
35
/*
36
* /dev/fd Filesystem
37
*/
38
39
#include <sys/param.h>
40
#include <sys/systm.h>
41
#include <sys/capsicum.h>
42
#include <sys/conf.h>
43
#include <sys/dirent.h>
44
#include <sys/filedesc.h>
45
#include <sys/kernel.h> /* boottime */
46
#include <sys/lock.h>
47
#include <sys/mutex.h>
48
#include <sys/malloc.h>
49
#include <sys/file.h> /* Must come after sys/malloc.h */
50
#include <sys/mount.h>
51
#include <sys/namei.h>
52
#include <sys/proc.h>
53
#include <sys/stat.h>
54
#include <sys/syscallsubr.h>
55
#include <sys/unistd.h>
56
#include <sys/vnode.h>
57
58
#include <fs/fdescfs/fdesc.h>
59
60
#define NFDCACHE 4
61
#define FD_NHASH(ix) \
62
(&fdhashtbl[(ix) & fdhash])
63
static LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
64
static u_long fdhash;
65
66
struct mtx fdesc_hashmtx;
67
68
static vop_getattr_t fdesc_getattr;
69
static vop_lookup_t fdesc_lookup;
70
static vop_open_t fdesc_open;
71
static vop_pathconf_t fdesc_pathconf;
72
static vop_readdir_t fdesc_readdir;
73
static vop_readlink_t fdesc_readlink;
74
static vop_reclaim_t fdesc_reclaim;
75
static vop_setattr_t fdesc_setattr;
76
77
static struct vop_vector fdesc_vnodeops = {
78
.vop_default = &default_vnodeops,
79
80
.vop_access = VOP_NULL,
81
.vop_getattr = fdesc_getattr,
82
.vop_lookup = fdesc_lookup,
83
.vop_open = fdesc_open,
84
.vop_pathconf = fdesc_pathconf,
85
.vop_readdir = fdesc_readdir,
86
.vop_readlink = fdesc_readlink,
87
.vop_reclaim = fdesc_reclaim,
88
.vop_setattr = fdesc_setattr,
89
};
90
VFS_VOP_VECTOR_REGISTER(fdesc_vnodeops);
91
92
static void fdesc_remove_entry(struct fdescnode *);
93
94
/*
95
* Initialise cache headers
96
*/
97
int
98
fdesc_init(struct vfsconf *vfsp)
99
{
100
101
mtx_init(&fdesc_hashmtx, "fdescfs_hash", NULL, MTX_DEF);
102
fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
103
return (0);
104
}
105
106
/*
107
* Uninit ready for unload.
108
*/
109
int
110
fdesc_uninit(struct vfsconf *vfsp)
111
{
112
113
hashdestroy(fdhashtbl, M_CACHE, fdhash);
114
mtx_destroy(&fdesc_hashmtx);
115
return (0);
116
}
117
118
/*
119
* Remove an entry from the hash if it exists.
120
*/
121
static void
122
fdesc_remove_entry(struct fdescnode *fd)
123
{
124
struct fdhashhead *fc;
125
struct fdescnode *fd2;
126
127
fc = FD_NHASH(fd->fd_ix);
128
mtx_lock(&fdesc_hashmtx);
129
LIST_FOREACH(fd2, fc, fd_hash) {
130
if (fd == fd2) {
131
LIST_REMOVE(fd, fd_hash);
132
break;
133
}
134
}
135
mtx_unlock(&fdesc_hashmtx);
136
}
137
138
int
139
fdesc_allocvp(fdntype ftype, unsigned fd_fd, int ix, struct mount *mp,
140
struct vnode **vpp)
141
{
142
struct fdescmount *fmp;
143
struct fdhashhead *fc;
144
struct fdescnode *fd, *fd2;
145
struct vnode *vp, *vp2;
146
enum vgetstate vgs;
147
int error;
148
149
fc = FD_NHASH(ix);
150
loop:
151
mtx_lock(&fdesc_hashmtx);
152
/*
153
* If a forced unmount is progressing, we need to drop it. The flags are
154
* protected by the hashmtx.
155
*/
156
fmp = mp->mnt_data;
157
if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) {
158
mtx_unlock(&fdesc_hashmtx);
159
return (-1);
160
}
161
162
LIST_FOREACH(fd, fc, fd_hash) {
163
if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
164
/* Get reference to vnode in case it's being free'd */
165
vp = fd->fd_vnode;
166
vgs = vget_prep(vp);
167
mtx_unlock(&fdesc_hashmtx);
168
if (vget_finish(vp, LK_EXCLUSIVE, vgs) != 0)
169
goto loop;
170
*vpp = vp;
171
return (0);
172
}
173
}
174
mtx_unlock(&fdesc_hashmtx);
175
176
fd = malloc(sizeof(struct fdescnode), M_TEMP, M_WAITOK);
177
178
error = getnewvnode("fdescfs", mp, &fdesc_vnodeops, &vp);
179
if (error) {
180
free(fd, M_TEMP);
181
return (error);
182
}
183
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
184
vp->v_data = fd;
185
fd->fd_vnode = vp;
186
fd->fd_type = ftype;
187
fd->fd_fd = fd_fd;
188
fd->fd_ix = ix;
189
if (ftype == Fdesc) {
190
if ((fmp->flags & FMNT_RDLNKF) != 0)
191
vp->v_type = VLNK;
192
else if ((fmp->flags & FMNT_LINRDLNKF) != 0)
193
vp->v_vflag |= VV_READLINK;
194
}
195
error = insmntque1(vp, mp);
196
if (error != 0) {
197
vgone(vp);
198
vput(vp);
199
*vpp = NULL;
200
return (error);
201
}
202
203
/* Make sure that someone didn't beat us when inserting the vnode. */
204
mtx_lock(&fdesc_hashmtx);
205
/*
206
* If a forced unmount is progressing, we need to drop it. The flags are
207
* protected by the hashmtx.
208
*/
209
fmp = mp->mnt_data;
210
if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) {
211
mtx_unlock(&fdesc_hashmtx);
212
vgone(vp);
213
vput(vp);
214
*vpp = NULL;
215
return (-1);
216
}
217
218
LIST_FOREACH(fd2, fc, fd_hash) {
219
if (fd2->fd_ix == ix && fd2->fd_vnode->v_mount == mp) {
220
/* Get reference to vnode in case it's being free'd */
221
vp2 = fd2->fd_vnode;
222
vgs = vget_prep(vp2);
223
mtx_unlock(&fdesc_hashmtx);
224
error = vget_finish(vp2, LK_EXCLUSIVE, vgs);
225
/* Someone beat us, dec use count and wait for reclaim */
226
vgone(vp);
227
vput(vp);
228
/* If we didn't get it, return no vnode. */
229
if (error)
230
vp2 = NULL;
231
*vpp = vp2;
232
return (error);
233
}
234
}
235
236
/* If we came here, we can insert it safely. */
237
LIST_INSERT_HEAD(fc, fd, fd_hash);
238
mtx_unlock(&fdesc_hashmtx);
239
vn_set_state(vp, VSTATE_CONSTRUCTED);
240
*vpp = vp;
241
return (0);
242
}
243
244
struct fdesc_get_ino_args {
245
fdntype ftype;
246
unsigned fd_fd;
247
int ix;
248
struct file *fp;
249
struct thread *td;
250
bool fdropped;
251
};
252
253
static int
254
fdesc_get_ino_alloc(struct mount *mp, void *arg, int lkflags,
255
struct vnode **rvp)
256
{
257
struct fdesc_get_ino_args *a;
258
struct fdescmount *fdm;
259
struct vnode *vp;
260
int error;
261
262
a = arg;
263
fdm = VFSTOFDESC(mp);
264
if ((fdm->flags & FMNT_NODUP) != 0 && a->fp->f_type == DTYPE_VNODE) {
265
vp = a->fp->f_vnode;
266
vget(vp, lkflags | LK_RETRY);
267
*rvp = vp;
268
error = 0;
269
} else {
270
error = fdesc_allocvp(a->ftype, a->fd_fd, a->ix, mp, rvp);
271
}
272
fdrop(a->fp, a->td);
273
a->fdropped = true;
274
return (error);
275
}
276
277
/*
278
* vp is the current namei directory
279
* ndp is the name to locate in that directory...
280
*/
281
static int
282
fdesc_lookup(struct vop_lookup_args *ap)
283
{
284
struct vnode **vpp = ap->a_vpp;
285
struct vnode *dvp = ap->a_dvp;
286
struct componentname *cnp = ap->a_cnp;
287
char *pname = cnp->cn_nameptr;
288
struct thread *td = curthread;
289
struct file *fp;
290
struct fdesc_get_ino_args arg;
291
int nlen = cnp->cn_namelen;
292
u_int fd, fd1;
293
int error;
294
struct vnode *fvp;
295
296
if ((cnp->cn_flags & ISLASTCN) &&
297
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
298
error = EROFS;
299
goto bad;
300
}
301
302
if (cnp->cn_namelen == 1 && *pname == '.') {
303
*vpp = dvp;
304
vref(dvp);
305
return (0);
306
}
307
308
if (VTOFDESC(dvp)->fd_type != Froot) {
309
error = ENOTDIR;
310
goto bad;
311
}
312
313
fd = 0;
314
/* the only time a leading 0 is acceptable is if it's "0" */
315
if (*pname == '0' && nlen != 1) {
316
error = ENOENT;
317
goto bad;
318
}
319
while (nlen--) {
320
if (*pname < '0' || *pname > '9') {
321
error = ENOENT;
322
goto bad;
323
}
324
fd1 = 10 * fd + *pname++ - '0';
325
if (fd1 < fd) {
326
error = ENOENT;
327
goto bad;
328
}
329
fd = fd1;
330
}
331
332
/*
333
* No rights to check since 'fp' isn't actually used.
334
*/
335
if ((error = fget(td, fd, &cap_no_rights, &fp)) != 0)
336
goto bad;
337
338
/*
339
* Make sure we do not deadlock looking up the dvp itself.
340
*
341
* Unlock our root node (dvp) when doing this, since we might
342
* deadlock since the vnode might be locked by another thread
343
* and the root vnode lock will be obtained afterwards (in case
344
* we're looking up the fd of the root vnode), which will be the
345
* opposite lock order.
346
*/
347
arg.ftype = Fdesc;
348
arg.fd_fd = fd;
349
arg.ix = FD_DESC + fd;
350
arg.fp = fp;
351
arg.td = td;
352
arg.fdropped = false;
353
error = vn_vget_ino_gen(dvp, fdesc_get_ino_alloc, &arg,
354
LK_EXCLUSIVE, &fvp);
355
356
if (!arg.fdropped) {
357
/*
358
* In case we're holding the last reference to the file, the dvp
359
* will be re-acquired.
360
*/
361
VOP_UNLOCK(dvp);
362
fdrop(fp, td);
363
364
vn_lock(dvp, LK_RETRY | LK_EXCLUSIVE);
365
fvp = dvp;
366
if (error == 0 && VN_IS_DOOMED(dvp))
367
error = ENOENT;
368
}
369
370
if (error)
371
goto bad;
372
*vpp = fvp;
373
return (0);
374
375
bad:
376
*vpp = NULL;
377
return (error);
378
}
379
380
static int
381
fdesc_open(struct vop_open_args *ap)
382
{
383
struct vnode *vp = ap->a_vp;
384
385
if (VTOFDESC(vp)->fd_type == Froot)
386
return (0);
387
388
/*
389
* XXX Kludge: set td->td_proc->p_dupfd to contain the value of the file
390
* descriptor being sought for duplication. The error return ensures
391
* that the vnode for this device will be released by vn_open. Open
392
* will detect this special error and take the actions in dupfdopen.
393
* Other callers of vn_open or VOP_OPEN will simply report the
394
* error.
395
*/
396
ap->a_td->td_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */
397
return (ENODEV);
398
}
399
400
static int
401
fdesc_pathconf(struct vop_pathconf_args *ap)
402
{
403
struct vnode *vp = ap->a_vp;
404
int error;
405
406
switch (ap->a_name) {
407
case _PC_NAME_MAX:
408
*ap->a_retval = NAME_MAX;
409
return (0);
410
case _PC_LINK_MAX:
411
if (VTOFDESC(vp)->fd_type == Froot)
412
*ap->a_retval = 2;
413
else
414
*ap->a_retval = 1;
415
return (0);
416
default:
417
if (VTOFDESC(vp)->fd_type == Froot)
418
return (vop_stdpathconf(ap));
419
vref(vp);
420
VOP_UNLOCK(vp);
421
error = kern_fpathconf(curthread, VTOFDESC(vp)->fd_fd,
422
ap->a_name, ap->a_retval);
423
vn_lock(vp, LK_SHARED | LK_RETRY);
424
vunref(vp);
425
return (error);
426
}
427
}
428
429
static int
430
fdesc_getattr(struct vop_getattr_args *ap)
431
{
432
struct vnode *vp = ap->a_vp;
433
struct vattr *vap = ap->a_vap;
434
struct timeval boottime;
435
436
getboottime(&boottime);
437
vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
438
vap->va_fileid = VTOFDESC(vp)->fd_ix;
439
vap->va_uid = 0;
440
vap->va_gid = 0;
441
vap->va_blocksize = DEV_BSIZE;
442
vap->va_atime.tv_sec = boottime.tv_sec;
443
vap->va_atime.tv_nsec = 0;
444
vap->va_mtime = vap->va_atime;
445
vap->va_ctime = vap->va_mtime;
446
vap->va_gen = 0;
447
vap->va_flags = 0;
448
vap->va_bytes = 0;
449
vap->va_filerev = 0;
450
451
switch (VTOFDESC(vp)->fd_type) {
452
case Froot:
453
vap->va_type = VDIR;
454
vap->va_nlink = 2;
455
vap->va_size = DEV_BSIZE;
456
vap->va_rdev = NODEV;
457
break;
458
459
case Fdesc:
460
vap->va_type = (VFSTOFDESC(vp->v_mount)->flags &
461
(FMNT_RDLNKF | FMNT_LINRDLNKF)) == 0 ? VCHR : VLNK;
462
vap->va_nlink = 1;
463
vap->va_size = 0;
464
vap->va_rdev = makedev(0, vap->va_fileid);
465
break;
466
467
default:
468
panic("fdesc_getattr");
469
break;
470
}
471
472
vp->v_type = vap->va_type;
473
return (0);
474
}
475
476
static int
477
fdesc_setattr(struct vop_setattr_args *ap)
478
{
479
struct vattr *vap = ap->a_vap;
480
struct vnode *vp;
481
struct mount *mp;
482
struct file *fp;
483
struct thread *td = curthread;
484
cap_rights_t rights;
485
unsigned fd;
486
int error;
487
488
/*
489
* Can't mess with the root vnode
490
*/
491
if (VTOFDESC(ap->a_vp)->fd_type == Froot)
492
return (EACCES);
493
494
fd = VTOFDESC(ap->a_vp)->fd_fd;
495
496
/*
497
* Allow setattr where there is an underlying vnode.
498
* For O_PATH descriptors, disallow truncate.
499
*/
500
if (vap->va_size != VNOVAL) {
501
error = getvnode(td, fd,
502
cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
503
} else {
504
error = getvnode_path(td, fd,
505
cap_rights_init_one(&rights, CAP_EXTATTR_SET), NULL, &fp);
506
}
507
if (error) {
508
/*
509
* getvnode() returns EINVAL if the file descriptor is not
510
* backed by a vnode. Silently drop all changes except
511
* chflags(2) in this case.
512
*/
513
if (error == EINVAL) {
514
if (vap->va_flags != VNOVAL)
515
error = EOPNOTSUPP;
516
else
517
error = 0;
518
}
519
return (error);
520
}
521
vp = fp->f_vnode;
522
if ((error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH)) == 0) {
523
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
524
error = VOP_SETATTR(vp, ap->a_vap, ap->a_cred);
525
VOP_UNLOCK(vp);
526
vn_finished_write(mp);
527
}
528
fdrop(fp, td);
529
return (error);
530
}
531
532
#define UIO_MX _GENERIC_DIRLEN(10) /* number of symbols in INT_MAX printout */
533
534
static int
535
fdesc_readdir(struct vop_readdir_args *ap)
536
{
537
struct fdescmount *fmp;
538
struct uio *uio = ap->a_uio;
539
struct filedesc *fdp;
540
struct dirent d;
541
struct dirent *dp = &d;
542
int error, i, off, fcnt;
543
544
if (VTOFDESC(ap->a_vp)->fd_type != Froot)
545
panic("fdesc_readdir: not dir");
546
547
fmp = VFSTOFDESC(ap->a_vp->v_mount);
548
if (ap->a_ncookies != NULL)
549
*ap->a_ncookies = 0;
550
if (ap->a_eofflag != NULL)
551
*ap->a_eofflag = 0;
552
553
off = (int)uio->uio_offset;
554
if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 ||
555
uio->uio_resid < UIO_MX)
556
return (EINVAL);
557
i = (u_int)off / UIO_MX;
558
fdp = uio->uio_td->td_proc->p_fd;
559
error = 0;
560
561
fcnt = i - 2; /* The first two nodes are `.' and `..' */
562
563
FILEDESC_SLOCK(fdp);
564
while (uio->uio_resid >= UIO_MX) {
565
if (i >= fdp->fd_nfiles + 2) {
566
if (ap->a_eofflag != NULL)
567
*ap->a_eofflag = 1;
568
break;
569
}
570
bzero((caddr_t)dp, UIO_MX);
571
switch (i) {
572
case 0: /* `.' */
573
case 1: /* `..' */
574
dp->d_fileno = i + FD_ROOT;
575
dp->d_namlen = i + 1;
576
dp->d_reclen = UIO_MX;
577
bcopy("..", dp->d_name, dp->d_namlen);
578
dp->d_type = DT_DIR;
579
dirent_terminate(dp);
580
break;
581
default:
582
if (fdp->fd_ofiles[fcnt].fde_file == NULL)
583
break;
584
dp->d_namlen = sprintf(dp->d_name, "%d", fcnt);
585
dp->d_reclen = UIO_MX;
586
dp->d_type = (fmp->flags & (FMNT_RDLNKF |
587
FMNT_LINRDLNKF)) == 0 ? DT_CHR : DT_LNK;
588
dp->d_fileno = i + FD_DESC;
589
dirent_terminate(dp);
590
break;
591
}
592
/* NOTE: d_off is the offset of the *next* entry. */
593
dp->d_off = UIO_MX * (i + 1);
594
if (dp->d_namlen != 0) {
595
/*
596
* And ship to userland
597
*/
598
FILEDESC_SUNLOCK(fdp);
599
error = uiomove(dp, UIO_MX, uio);
600
if (error)
601
goto done;
602
FILEDESC_SLOCK(fdp);
603
}
604
i++;
605
fcnt++;
606
}
607
FILEDESC_SUNLOCK(fdp);
608
609
done:
610
uio->uio_offset = i * UIO_MX;
611
return (error);
612
}
613
614
static int
615
fdesc_reclaim(struct vop_reclaim_args *ap)
616
{
617
struct vnode *vp;
618
struct fdescnode *fd;
619
620
vp = ap->a_vp;
621
fd = VTOFDESC(vp);
622
fdesc_remove_entry(fd);
623
free(vp->v_data, M_TEMP);
624
vp->v_data = NULL;
625
return (0);
626
}
627
628
static int
629
fdesc_readlink(struct vop_readlink_args *va)
630
{
631
struct vnode *vp, *vn;
632
struct thread *td;
633
struct uio *uio;
634
struct file *fp;
635
char *freepath, *fullpath;
636
size_t pathlen;
637
int lockflags, fd_fd;
638
int error;
639
640
freepath = NULL;
641
vn = va->a_vp;
642
if (VTOFDESC(vn)->fd_type != Fdesc)
643
panic("fdesc_readlink: not fdescfs link");
644
fd_fd = ((struct fdescnode *)vn->v_data)->fd_fd;
645
lockflags = VOP_ISLOCKED(vn);
646
VOP_UNLOCK(vn);
647
648
td = curthread;
649
error = fget_cap(td, fd_fd, &cap_no_rights, NULL, &fp, NULL);
650
if (error != 0)
651
goto out;
652
653
switch (fp->f_type) {
654
case DTYPE_VNODE:
655
vp = fp->f_vnode;
656
error = vn_fullpath(vp, &fullpath, &freepath);
657
break;
658
default:
659
fullpath = "anon_inode:[unknown]";
660
break;
661
}
662
if (error == 0) {
663
uio = va->a_uio;
664
pathlen = strlen(fullpath);
665
error = uiomove(fullpath, pathlen, uio);
666
}
667
if (freepath != NULL)
668
free(freepath, M_TEMP);
669
fdrop(fp, td);
670
671
out:
672
vn_lock(vn, lockflags | LK_RETRY);
673
return (error);
674
}
675
676