]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/fdescfs/fdesc_vnops.c
Merge llvm-project release/18.x llvmorg-18-init-18361-g22683463740e
[FreeBSD/FreeBSD.git] / sys / fs / fdescfs / fdesc_vnops.c
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 = NULLVP;
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 = NULLVP;
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 = NULLVP;
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), &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
551         off = (int)uio->uio_offset;
552         if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 ||
553             uio->uio_resid < UIO_MX)
554                 return (EINVAL);
555         i = (u_int)off / UIO_MX;
556         fdp = uio->uio_td->td_proc->p_fd;
557         error = 0;
558
559         fcnt = i - 2;           /* The first two nodes are `.' and `..' */
560
561         FILEDESC_SLOCK(fdp);
562         while (i < fdp->fd_nfiles + 2 && uio->uio_resid >= UIO_MX) {
563                 bzero((caddr_t)dp, UIO_MX);
564                 switch (i) {
565                 case 0: /* `.' */
566                 case 1: /* `..' */
567                         dp->d_fileno = i + FD_ROOT;
568                         dp->d_namlen = i + 1;
569                         dp->d_reclen = UIO_MX;
570                         bcopy("..", dp->d_name, dp->d_namlen);
571                         dp->d_type = DT_DIR;
572                         dirent_terminate(dp);
573                         break;
574                 default:
575                         if (fdp->fd_ofiles[fcnt].fde_file == NULL)
576                                 break;
577                         dp->d_namlen = sprintf(dp->d_name, "%d", fcnt);
578                         dp->d_reclen = UIO_MX;
579                         dp->d_type = (fmp->flags & (FMNT_RDLNKF |
580                             FMNT_LINRDLNKF)) == 0 ? DT_CHR : DT_LNK;
581                         dp->d_fileno = i + FD_DESC;
582                         dirent_terminate(dp);
583                         break;
584                 }
585                 /* NOTE: d_off is the offset of the *next* entry. */
586                 dp->d_off = UIO_MX * (i + 1);
587                 if (dp->d_namlen != 0) {
588                         /*
589                          * And ship to userland
590                          */
591                         FILEDESC_SUNLOCK(fdp);
592                         error = uiomove(dp, UIO_MX, uio);
593                         if (error)
594                                 goto done;
595                         FILEDESC_SLOCK(fdp);
596                 }
597                 i++;
598                 fcnt++;
599         }
600         FILEDESC_SUNLOCK(fdp);
601
602 done:
603         uio->uio_offset = i * UIO_MX;
604         return (error);
605 }
606
607 static int
608 fdesc_reclaim(struct vop_reclaim_args *ap)
609 {
610         struct vnode *vp;
611         struct fdescnode *fd;
612
613         vp = ap->a_vp;
614         fd = VTOFDESC(vp);
615         fdesc_remove_entry(fd);
616         free(vp->v_data, M_TEMP);
617         vp->v_data = NULL;
618         return (0);
619 }
620
621 static int
622 fdesc_readlink(struct vop_readlink_args *va)
623 {
624         struct vnode *vp, *vn;
625         struct thread *td;
626         struct uio *uio;
627         struct file *fp;
628         char *freepath, *fullpath;
629         size_t pathlen;
630         int lockflags, fd_fd;
631         int error;
632
633         freepath = NULL;
634         vn = va->a_vp;
635         if (VTOFDESC(vn)->fd_type != Fdesc)
636                 panic("fdesc_readlink: not fdescfs link");
637         fd_fd = ((struct fdescnode *)vn->v_data)->fd_fd;
638         lockflags = VOP_ISLOCKED(vn);
639         VOP_UNLOCK(vn);
640
641         td = curthread;
642         error = fget_cap(td, fd_fd, &cap_no_rights, &fp, NULL);
643         if (error != 0)
644                 goto out;
645
646         switch (fp->f_type) {
647         case DTYPE_VNODE:
648                 vp = fp->f_vnode;
649                 error = vn_fullpath(vp, &fullpath, &freepath);
650                 break;
651         default:
652                 fullpath = "anon_inode:[unknown]";
653                 break;
654         }
655         if (error == 0) {
656                 uio = va->a_uio;
657                 pathlen = strlen(fullpath);
658                 error = uiomove(fullpath, pathlen, uio);
659         }
660         if (freepath != NULL)
661                 free(freepath, M_TEMP);
662         fdrop(fp, td);
663
664 out:
665         vn_lock(vn, lockflags | LK_RETRY);
666         return (error);
667 }