proc->thread stage 4: rework the VFS and DEVICE subsystems to take thread
[dragonfly.git] / sys / vfs / fdesc / fdesc_vnops.c
1 /*
2  * Copyright (c) 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software donated to Berkeley by
6  * Jan-Simon Pendry.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *      @(#)fdesc_vnops.c       8.9 (Berkeley) 1/21/94
37  *
38  * $FreeBSD: src/sys/miscfs/fdesc/fdesc_vnops.c,v 1.47.2.1 2001/10/22 22:49:26 chris Exp $
39  * $DragonFly: src/sys/vfs/fdesc/fdesc_vnops.c,v 1.4 2003/06/25 03:55:58 dillon Exp $
40  */
41
42 /*
43  * /dev/fd Filesystem
44  */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/conf.h>
49 #include <sys/dirent.h>
50 #include <sys/filedesc.h>
51 #include <sys/kernel.h> /* boottime */
52 #include <sys/lock.h>
53 #include <sys/malloc.h>
54 #include <sys/file.h>   /* Must come after sys/malloc.h */
55 #include <sys/mount.h>
56 #include <sys/proc.h>
57 #include <sys/namei.h>
58 #include <sys/socket.h>
59 #include <sys/stat.h>
60 #include <sys/vnode.h>
61 #include <sys/file2.h>
62
63 #include <miscfs/fdesc/fdesc.h>
64
65 #define FDL_WANT        0x01
66 #define FDL_LOCKED      0x02
67 static int fdcache_lock;
68
69 static vop_t **fdesc_vnodeop_p;
70
71 #define NFDCACHE 4
72 #define FD_NHASH(ix) \
73         (&fdhashtbl[(ix) & fdhash])
74 static LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
75 static u_long fdhash;
76
77 static int      fdesc_getattr __P((struct vop_getattr_args *ap));
78 static int      fdesc_inactive __P((struct vop_inactive_args *ap));
79 static int      fdesc_lookup __P((struct vop_lookup_args *ap));
80 static int      fdesc_open __P((struct vop_open_args *ap));
81 static int      fdesc_print __P((struct vop_print_args *ap));
82 static int      fdesc_readdir __P((struct vop_readdir_args *ap));
83 static int      fdesc_reclaim __P((struct vop_reclaim_args *ap));
84 static int      fdesc_poll __P((struct vop_poll_args *ap));
85 static int      fdesc_setattr __P((struct vop_setattr_args *ap));
86
87 /*
88  * Initialise cache headers
89  */
90 int
91 fdesc_init(vfsp)
92         struct vfsconf *vfsp;
93 {
94
95         fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
96         return (0);
97 }
98
99 int
100 fdesc_allocvp(ftype, ix, mp, vpp, td)
101         fdntype ftype;
102         int ix;
103         struct mount *mp;
104         struct vnode **vpp;
105         struct thread *td;
106 {
107         struct fdhashhead *fc;
108         struct fdescnode *fd;
109         int error = 0;
110
111         fc = FD_NHASH(ix);
112 loop:
113         LIST_FOREACH(fd, fc, fd_hash) {
114                 if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
115                         if (vget(fd->fd_vnode, 0, td))
116                                 goto loop;
117                         *vpp = fd->fd_vnode;
118                         return (error);
119                 }
120         }
121
122         /*
123          * otherwise lock the array while we call getnewvnode
124          * since that can block.
125          */
126         if (fdcache_lock & FDL_LOCKED) {
127                 fdcache_lock |= FDL_WANT;
128                 (void) tsleep((caddr_t) &fdcache_lock, PINOD, "fdalvp", 0);
129                 goto loop;
130         }
131         fdcache_lock |= FDL_LOCKED;
132
133         /*
134          * Do the MALLOC before the getnewvnode since doing so afterward
135          * might cause a bogus v_data pointer to get dereferenced
136          * elsewhere if MALLOC should block.
137          */
138         MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
139
140         error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
141         if (error) {
142                 FREE(fd, M_TEMP);
143                 goto out;
144         }
145         (*vpp)->v_data = fd;
146         fd->fd_vnode = *vpp;
147         fd->fd_type = ftype;
148         fd->fd_fd = -1;
149         fd->fd_ix = ix;
150         LIST_INSERT_HEAD(fc, fd, fd_hash);
151
152 out:
153         fdcache_lock &= ~FDL_LOCKED;
154
155         if (fdcache_lock & FDL_WANT) {
156                 fdcache_lock &= ~FDL_WANT;
157                 wakeup((caddr_t) &fdcache_lock);
158         }
159
160         return (error);
161 }
162
163 /*
164  * vp is the current namei directory
165  * ndp is the name to locate in that directory...
166  */
167 static int
168 fdesc_lookup(ap)
169         struct vop_lookup_args /* {
170                 struct vnode * a_dvp;
171                 struct vnode ** a_vpp;
172                 struct componentname * a_cnp;
173         } */ *ap;
174 {
175         struct componentname *cnp = ap->a_cnp;
176         struct thread *td = cnp->cn_td;
177         struct proc *p = td->td_proc;
178         struct vnode **vpp = ap->a_vpp;
179         struct vnode *dvp = ap->a_dvp;
180         char *pname = cnp->cn_nameptr;
181         int nlen = cnp->cn_namelen;
182         int nfiles;
183         u_int fd;
184         int error;
185         struct vnode *fvp;
186
187         KKASSERT(p);
188         nfiles = p->p_fd->fd_nfiles;
189         if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
190                 error = EROFS;
191                 goto bad;
192         }
193
194         VOP_UNLOCK(dvp, 0, td);
195         if (cnp->cn_namelen == 1 && *pname == '.') {
196                 *vpp = dvp;
197                 VREF(dvp);      
198                 vn_lock(dvp, LK_SHARED | LK_RETRY, td);
199                 return (0);
200         }
201
202         if (VTOFDESC(dvp)->fd_type != Froot) {
203                 error = ENOTDIR;
204                 goto bad;
205         }
206
207         fd = 0;
208         /* the only time a leading 0 is acceptable is if it's "0" */
209         if (*pname == '0' && nlen != 1) {
210                 error = ENOENT;
211                 goto bad;
212         }
213         while (nlen--) {
214                 if (*pname < '0' || *pname > '9') {
215                         error = ENOENT;
216                         goto bad;
217                 }
218                 fd = 10 * fd + *pname++ - '0';
219         }
220
221         if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) {
222                 error = EBADF;
223                 goto bad;
224         }
225
226         error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp, td);
227         if (error)
228                 goto bad;
229         VTOFDESC(fvp)->fd_fd = fd;
230         vn_lock(fvp, LK_SHARED | LK_RETRY, td);
231         *vpp = fvp;
232         return (0);
233
234 bad:
235         vn_lock(dvp, LK_SHARED | LK_RETRY, td);
236         *vpp = NULL;
237         return (error);
238 }
239
240 static int
241 fdesc_open(ap)
242         struct vop_open_args /* {
243                 struct vnode *a_vp;
244                 int  a_mode;
245                 struct ucred *a_cred;
246                 struct thread *a_td;
247         } */ *ap;
248 {
249         struct vnode *vp = ap->a_vp;
250         struct proc *p = ap->a_td->td_proc;
251
252         KKASSERT(p);
253
254         if (VTOFDESC(vp)->fd_type == Froot)
255                 return (0);
256
257         /*
258          * XXX Kludge: set p->p_dupfd to contain the value of the the file
259          * descriptor being sought for duplication. The error return ensures
260          * that the vnode for this device will be released by vn_open. Open
261          * will detect this special error and take the actions in dupfdopen.
262          * Other callers of vn_open or VOP_OPEN will simply report the
263          * error.
264          */
265         p->p_dupfd = VTOFDESC(vp)->fd_fd;       /* XXX */
266         return (ENODEV);
267 }
268
269 static int
270 fdesc_getattr(ap)
271         struct vop_getattr_args /* {
272                 struct vnode *a_vp;
273                 struct vattr *a_vap;
274                 struct ucred *a_cred;
275                 struct thread *a_td;
276         } */ *ap;
277 {
278         struct proc *p = ap->a_td->td_proc;
279         struct vnode *vp = ap->a_vp;
280         struct vattr *vap = ap->a_vap;
281         struct filedesc *fdp;
282         struct file *fp;
283         struct stat stb;
284         u_int fd;
285         int error = 0;
286
287         KKASSERT(p);
288         fdp = p->p_fd;
289         switch (VTOFDESC(vp)->fd_type) {
290         case Froot:
291                 VATTR_NULL(vap);
292
293                 vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
294                 vap->va_type = VDIR;
295                 vap->va_nlink = 2;
296                 vap->va_size = DEV_BSIZE;
297                 vap->va_fileid = VTOFDESC(vp)->fd_ix;
298                 vap->va_uid = 0;
299                 vap->va_gid = 0;
300                 vap->va_blocksize = DEV_BSIZE;
301                 vap->va_atime.tv_sec = boottime.tv_sec;
302                 vap->va_atime.tv_nsec = 0;
303                 vap->va_mtime = vap->va_atime;
304                 vap->va_ctime = vap->va_mtime;
305                 vap->va_gen = 0;
306                 vap->va_flags = 0;
307                 vap->va_rdev = 0;
308                 vap->va_bytes = 0;
309                 break;
310
311         case Fdesc:
312                 fd = VTOFDESC(vp)->fd_fd;
313
314                 if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL)
315                         return (EBADF);
316
317                 bzero(&stb, sizeof(stb));
318                 error = fo_stat(fp, &stb, ap->a_td);
319                 if (error == 0) {
320                         VATTR_NULL(vap);
321                         vap->va_type = IFTOVT(stb.st_mode);
322                         vap->va_mode = stb.st_mode;
323 #define FDRX (VREAD|VEXEC)
324                         if (vap->va_type == VDIR)
325                                 vap->va_mode &= ~((FDRX)|(FDRX>>3)|(FDRX>>6));
326 #undef FDRX
327                         vap->va_nlink = 1;
328                         vap->va_flags = 0;
329                         vap->va_bytes = stb.st_blocks * stb.st_blksize;
330                         vap->va_fileid = VTOFDESC(vp)->fd_ix;
331                         vap->va_size = stb.st_size;
332                         vap->va_blocksize = stb.st_blksize;
333                         vap->va_rdev = stb.st_rdev;
334
335                         /*
336                          * If no time data is provided, use the current time.
337                          */
338                         if (stb.st_atimespec.tv_sec == 0 &&
339                             stb.st_atimespec.tv_nsec == 0)
340                                 nanotime(&stb.st_atimespec);
341
342                         if (stb.st_ctimespec.tv_sec == 0 &&
343                             stb.st_ctimespec.tv_nsec == 0)
344                                 nanotime(&stb.st_ctimespec);
345
346                         if (stb.st_mtimespec.tv_sec == 0 &&
347                             stb.st_mtimespec.tv_nsec == 0)
348                                 nanotime(&stb.st_mtimespec);
349
350                         vap->va_atime = stb.st_atimespec;
351                         vap->va_mtime = stb.st_mtimespec;
352                         vap->va_ctime = stb.st_ctimespec;
353                         vap->va_uid = stb.st_uid;
354                         vap->va_gid = stb.st_gid;
355                 }
356                 break;
357
358         default:
359                 panic("fdesc_getattr");
360                 break;
361         }
362
363         if (error == 0)
364                 vp->v_type = vap->va_type;
365         return (error);
366 }
367
368 static int
369 fdesc_setattr(ap)
370         struct vop_setattr_args /* {
371                 struct vnode *a_vp;
372                 struct vattr *a_vap;
373                 struct ucred *a_cred;
374                 struct thread *a_td;
375         } */ *ap;
376 {
377         struct proc *p = ap->a_td->td_proc;
378         struct vattr *vap = ap->a_vap;
379         struct file *fp;
380         unsigned fd;
381         int error;
382
383         /*
384          * Can't mess with the root vnode
385          */
386         if (VTOFDESC(ap->a_vp)->fd_type == Froot)
387                 return (EACCES);
388
389         fd = VTOFDESC(ap->a_vp)->fd_fd;
390         KKASSERT(p);
391
392         /*
393          * Allow setattr where there is an underlying vnode.
394          */
395         error = getvnode(p->p_fd, fd, &fp);
396         if (error) {
397                 /*
398                  * getvnode() returns EINVAL if the file descriptor is not
399                  * backed by a vnode.  Silently drop all changes except
400                  * chflags(2) in this case.
401                  */
402                 if (error == EINVAL) {
403                         if (vap->va_flags != VNOVAL)
404                                 error = EOPNOTSUPP;
405                         else
406                                 error = 0;
407                 }
408                 return (error);
409         }
410         return (error);
411 }
412
413 #define UIO_MX 16
414
415 static int
416 fdesc_readdir(ap)
417         struct vop_readdir_args /* {
418                 struct vnode *a_vp;
419                 struct uio *a_uio;
420                 struct ucred *a_cred;
421                 int *a_eofflag;
422                 u_long *a_cookies;
423                 int a_ncookies;
424         } */ *ap;
425 {
426         struct uio *uio = ap->a_uio;
427         struct filedesc *fdp;
428         struct dirent d;
429         struct dirent *dp = &d;
430         int error, i, off, fcnt;
431
432         /*
433          * We don't allow exporting fdesc mounts, and currently local
434          * requests do not need cookies.
435          */
436         if (ap->a_ncookies)
437                 panic("fdesc_readdir: not hungry");
438
439         if (VTOFDESC(ap->a_vp)->fd_type != Froot)
440                 panic("fdesc_readdir: not dir");
441
442         off = (int)uio->uio_offset;
443         if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 ||
444             uio->uio_resid < UIO_MX)
445                 return (EINVAL);
446         i = (u_int)off / UIO_MX;
447         KKASSERT(uio->uio_td->td_proc);
448         fdp = uio->uio_td->td_proc->p_fd;
449         error = 0;
450
451         fcnt = i - 2;           /* The first two nodes are `.' and `..' */
452
453         while (i < fdp->fd_nfiles + 2 && uio->uio_resid >= UIO_MX) {
454                 switch (i) {
455                 case 0: /* `.' */
456                 case 1: /* `..' */
457                         bzero((caddr_t)dp, UIO_MX);
458
459                         dp->d_fileno = i + FD_ROOT;
460                         dp->d_namlen = i + 1;
461                         dp->d_reclen = UIO_MX;
462                         bcopy("..", dp->d_name, dp->d_namlen);
463                         dp->d_name[i + 1] = '\0';
464                         dp->d_type = DT_DIR;
465                         break;
466                 default:
467                         if (fdp->fd_ofiles[fcnt] == NULL)
468                                 goto done;
469
470                         bzero((caddr_t) dp, UIO_MX);
471                         dp->d_namlen = sprintf(dp->d_name, "%d", fcnt);
472                         dp->d_reclen = UIO_MX;
473                         dp->d_type = DT_UNKNOWN;
474                         dp->d_fileno = i + FD_DESC;
475                         break;
476                 }
477                 /*
478                  * And ship to userland
479                  */
480                 error = uiomove((caddr_t) dp, UIO_MX, uio);
481                 if (error)
482                         break;
483                 i++;
484                 fcnt++;
485         }
486
487 done:
488         uio->uio_offset = i * UIO_MX;
489         return (error);
490 }
491
492 static int
493 fdesc_poll(ap)
494         struct vop_poll_args /* {
495                 struct vnode *a_vp;
496                 int  a_events;
497                 struct ucred *a_cred;
498                 struct thread *a_td;
499         } */ *ap;
500 {
501         return seltrue(0, ap->a_events, ap->a_td);
502 }
503
504 static int
505 fdesc_inactive(ap)
506         struct vop_inactive_args /* {
507                 struct vnode *a_vp;
508                 struct thread *a_td;
509         } */ *ap;
510 {
511         struct vnode *vp = ap->a_vp;
512
513         /*
514          * Clear out the v_type field to avoid
515          * nasty things happening in vgone().
516          */
517         VOP_UNLOCK(vp, 0, ap->a_td);
518         vp->v_type = VNON;
519         return (0);
520 }
521
522 static int
523 fdesc_reclaim(ap)
524         struct vop_reclaim_args /* {
525                 struct vnode *a_vp;
526         } */ *ap;
527 {
528         struct vnode *vp = ap->a_vp;
529         struct fdescnode *fd = VTOFDESC(vp);
530
531         LIST_REMOVE(fd, fd_hash);
532         FREE(vp->v_data, M_TEMP);
533         vp->v_data = 0;
534
535         return (0);
536 }
537
538 /*
539  * Print out the contents of a /dev/fd vnode.
540  */
541 /* ARGSUSED */
542 static int
543 fdesc_print(ap)
544         struct vop_print_args /* {
545                 struct vnode *a_vp;
546         } */ *ap;
547 {
548
549         printf("tag VT_NON, fdesc vnode\n");
550         return (0);
551 }
552
553 static struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
554         { &vop_default_desc,            (vop_t *) vop_defaultop },
555         { &vop_access_desc,             (vop_t *) vop_null },
556         { &vop_getattr_desc,            (vop_t *) fdesc_getattr },
557         { &vop_inactive_desc,           (vop_t *) fdesc_inactive },
558         { &vop_lookup_desc,             (vop_t *) fdesc_lookup },
559         { &vop_open_desc,               (vop_t *) fdesc_open },
560         { &vop_pathconf_desc,           (vop_t *) vop_stdpathconf },
561         { &vop_poll_desc,               (vop_t *) fdesc_poll },
562         { &vop_print_desc,              (vop_t *) fdesc_print },
563         { &vop_readdir_desc,            (vop_t *) fdesc_readdir },
564         { &vop_reclaim_desc,            (vop_t *) fdesc_reclaim },
565         { &vop_setattr_desc,            (vop_t *) fdesc_setattr },
566         { NULL, NULL }
567 };
568 static struct vnodeopv_desc fdesc_vnodeop_opv_desc =
569         { &fdesc_vnodeop_p, fdesc_vnodeop_entries };
570
571 VNODEOP_SET(fdesc_vnodeop_opv_desc);