1/*	$NetBSD: advnops.c,v 1.37 2010/06/24 13:03:09 hannken Exp $	*/
2
3/*
4 * Copyright (c) 1994 Christian E. Hopps
5 * Copyright (c) 1996 Matthias Scheler
6 * All rights reserved.
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 Christian E. Hopps.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: advnops.c,v 1.37 2010/06/24 13:03:09 hannken Exp $");
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/vnode.h>
40#include <sys/mount.h>
41#include <sys/time.h>
42#include <sys/queue.h>
43#include <sys/namei.h>
44#include <sys/buf.h>
45#include <sys/dirent.h>
46#include <sys/inttypes.h>
47#include <sys/malloc.h>
48#include <sys/pool.h>
49#include <sys/stat.h>
50#include <sys/unistd.h>
51#include <sys/proc.h>
52#include <sys/kauth.h>
53
54#include <miscfs/genfs/genfs.h>
55#include <miscfs/specfs/specdev.h>
56#include <fs/adosfs/adosfs.h>
57
58extern struct vnodeops adosfs_vnodeops;
59
60#define	adosfs_open	genfs_nullop
61int	adosfs_getattr(void *);
62int	adosfs_read(void *);
63int	adosfs_write(void *);
64#define	adosfs_fcntl	genfs_fcntl
65#define	adosfs_ioctl	genfs_enoioctl
66#define	adosfs_poll	genfs_poll
67int	adosfs_strategy(void *);
68int	adosfs_link(void *);
69int	adosfs_symlink(void *);
70#define	adosfs_abortop	genfs_abortop
71int	adosfs_bmap(void *);
72int	adosfs_print(void *);
73int	adosfs_readdir(void *);
74int	adosfs_access(void *);
75int	adosfs_readlink(void *);
76int	adosfs_inactive(void *);
77int	adosfs_reclaim(void *);
78int	adosfs_pathconf(void *);
79
80#define adosfs_close 	genfs_nullop
81#define adosfs_fsync 	genfs_nullop
82#define adosfs_seek 	genfs_seek
83
84#define adosfs_advlock 	genfs_einval
85#define adosfs_bwrite 	genfs_eopnotsupp
86#define adosfs_create 	genfs_eopnotsupp
87#define adosfs_mkdir 	genfs_eopnotsupp
88#define adosfs_mknod 	genfs_eopnotsupp
89#define adosfs_revoke	genfs_revoke
90#define adosfs_mmap 	genfs_mmap
91#define adosfs_remove 	genfs_eopnotsupp
92#define adosfs_rename 	genfs_eopnotsupp
93#define adosfs_rmdir 	genfs_eopnotsupp
94#define adosfs_setattr 	genfs_eopnotsupp
95
96const struct vnodeopv_entry_desc adosfs_vnodeop_entries[] = {
97	{ &vop_default_desc, vn_default_error },
98	{ &vop_lookup_desc, adosfs_lookup },		/* lookup */
99	{ &vop_create_desc, adosfs_create },		/* create */
100	{ &vop_mknod_desc, adosfs_mknod },		/* mknod */
101	{ &vop_open_desc, adosfs_open },		/* open */
102	{ &vop_close_desc, adosfs_close },		/* close */
103	{ &vop_access_desc, adosfs_access },		/* access */
104	{ &vop_getattr_desc, adosfs_getattr },		/* getattr */
105	{ &vop_setattr_desc, adosfs_setattr },		/* setattr */
106	{ &vop_read_desc, adosfs_read },		/* read */
107	{ &vop_write_desc, adosfs_write },		/* write */
108	{ &vop_fcntl_desc, adosfs_fcntl },		/* fcntl */
109	{ &vop_ioctl_desc, adosfs_ioctl },		/* ioctl */
110	{ &vop_poll_desc, adosfs_poll },		/* poll */
111	{ &vop_kqfilter_desc, genfs_kqfilter },		/* kqfilter */
112	{ &vop_revoke_desc, adosfs_revoke },		/* revoke */
113	{ &vop_mmap_desc, adosfs_mmap },		/* mmap */
114	{ &vop_fsync_desc, adosfs_fsync },		/* fsync */
115	{ &vop_seek_desc, adosfs_seek },		/* seek */
116	{ &vop_remove_desc, adosfs_remove },		/* remove */
117	{ &vop_link_desc, adosfs_link },		/* link */
118	{ &vop_rename_desc, adosfs_rename },		/* rename */
119	{ &vop_mkdir_desc, adosfs_mkdir },		/* mkdir */
120	{ &vop_rmdir_desc, adosfs_rmdir },		/* rmdir */
121	{ &vop_symlink_desc, adosfs_symlink },		/* symlink */
122	{ &vop_readdir_desc, adosfs_readdir },		/* readdir */
123	{ &vop_readlink_desc, adosfs_readlink },	/* readlink */
124	{ &vop_abortop_desc, adosfs_abortop },		/* abortop */
125	{ &vop_inactive_desc, adosfs_inactive },	/* inactive */
126	{ &vop_reclaim_desc, adosfs_reclaim },		/* reclaim */
127	{ &vop_lock_desc, genfs_lock },			/* lock */
128	{ &vop_unlock_desc, genfs_unlock },		/* unlock */
129	{ &vop_bmap_desc, adosfs_bmap },		/* bmap */
130	{ &vop_strategy_desc, adosfs_strategy },	/* strategy */
131	{ &vop_print_desc, adosfs_print },		/* print */
132	{ &vop_islocked_desc, genfs_islocked },		/* islocked */
133	{ &vop_pathconf_desc, adosfs_pathconf },	/* pathconf */
134	{ &vop_advlock_desc, adosfs_advlock },		/* advlock */
135	{ &vop_bwrite_desc, adosfs_bwrite },		/* bwrite */
136	{ &vop_getpages_desc, genfs_getpages },		/* getpages */
137	{ &vop_putpages_desc, genfs_putpages },		/* putpages */
138	{ NULL, NULL }
139};
140
141const struct vnodeopv_desc adosfs_vnodeop_opv_desc =
142	{ &adosfs_vnodeop_p, adosfs_vnodeop_entries };
143
144int
145adosfs_getattr(void *v)
146{
147	struct vop_getattr_args /* {
148		struct vnode *a_vp;
149		struct vattr *a_vap;
150		kauth_cred_t a_cred;
151	} */ *sp = v;
152	struct vattr *vap;
153	struct adosfsmount *amp;
154	struct anode *ap;
155	u_long fblks;
156
157#ifdef ADOSFS_DIAGNOSTIC
158	advopprint(sp);
159#endif
160	vap = sp->a_vap;
161	ap = VTOA(sp->a_vp);
162	amp = ap->amp;
163	vattr_null(vap);
164	vap->va_uid = ap->uid;
165	vap->va_gid = ap->gid;
166	vap->va_fsid = sp->a_vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
167	vap->va_atime.tv_sec = vap->va_mtime.tv_sec = vap->va_ctime.tv_sec =
168		ap->mtime.days * 24 * 60 * 60 + ap->mtime.mins * 60 +
169		ap->mtime.ticks / 50 + (8 * 365 + 2) * 24 * 60 * 60;
170	vap->va_atime.tv_nsec = vap->va_mtime.tv_nsec = vap->va_ctime.tv_nsec = 0;
171	vap->va_gen = 0;
172	vap->va_flags = 0;
173	vap->va_rdev = NODEV;
174	vap->va_fileid = ap->block;
175	vap->va_type = sp->a_vp->v_type;
176	vap->va_mode = adunixprot(ap->adprot) & amp->mask;
177	if (sp->a_vp->v_type == VDIR) {
178		vap->va_nlink = 1;	/* XXX bogus, oh well */
179		vap->va_bytes = amp->bsize;
180		vap->va_size = amp->bsize;
181	} else {
182		/*
183		 * XXX actually we can track this if we were to walk the list
184		 * of links if it exists.
185		 * XXX for now, just set nlink to 2 if this is a hard link
186		 * to a file, or a file with a hard link.
187		 */
188		vap->va_nlink = 1 + (ap->linkto != 0);
189		/*
190		 * round up to nearest blocks add number of file list
191		 * blocks needed and mutiply by number of bytes per block.
192		 */
193		fblks = howmany(ap->fsize, amp->dbsize);
194		fblks += howmany(fblks, ANODENDATBLKENT(ap));
195		vap->va_bytes = fblks * amp->dbsize;
196		vap->va_size = ap->fsize;
197
198		vap->va_blocksize = amp->dbsize;
199	}
200#ifdef ADOSFS_DIAGNOSTIC
201	printf(" 0)");
202#endif
203	return(0);
204}
205/*
206 * are things locked??? they need to be to avoid this being
207 * deleted or changed (data block pointer blocks moving about.)
208 */
209int
210adosfs_read(void *v)
211{
212	struct vop_read_args /* {
213		struct vnode *a_vp;
214		struct uio *a_uio;
215		int a_ioflag;
216		kauth_cred_t a_cred;
217	} */ *sp = v;
218	struct vnode *vp = sp->a_vp;
219	struct adosfsmount *amp;
220	struct anode *ap;
221	struct uio *uio;
222	struct buf *bp;
223	daddr_t lbn;
224	int size, diff, error;
225	long n, on;
226
227#ifdef ADOSFS_DIAGNOSTIC
228	advopprint(sp);
229#endif
230	error = 0;
231	uio = sp->a_uio;
232	ap = VTOA(sp->a_vp);
233	amp = ap->amp;
234	/*
235	 * Return EOF for character devices, EIO for others
236	 */
237	if (sp->a_vp->v_type != VREG) {
238		error = EIO;
239		goto reterr;
240	}
241	if (uio->uio_resid == 0)
242		goto reterr;
243	if (uio->uio_offset < 0) {
244		error = EINVAL;
245		goto reterr;
246	}
247
248	/*
249	 * to expensive to let general algorithm figure out that
250	 * we are beyond the file.  Do it now.
251	 */
252	if (uio->uio_offset >= ap->fsize)
253		goto reterr;
254
255	/*
256	 * taken from ufs_read()
257	 */
258
259	if (vp->v_type == VREG && IS_FFS(amp)) {
260		const int advice = IO_ADV_DECODE(sp->a_ioflag);
261		error = 0;
262
263		while (uio->uio_resid > 0) {
264			vsize_t bytelen = MIN(ap->fsize - uio->uio_offset,
265					      uio->uio_resid);
266
267			if (bytelen == 0) {
268				break;
269			}
270			error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
271			    UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
272			if (error) {
273				break;
274			}
275		}
276		goto out;
277	}
278
279	do {
280		size = amp->dbsize;
281		lbn = uio->uio_offset / size;
282		on = uio->uio_offset % size;
283		n = MIN(size - on, uio->uio_resid);
284		diff = ap->fsize - uio->uio_offset;
285		/*
286		 * check for EOF
287		 */
288		if (diff <= 0)
289			return(0);
290		if (diff < n)
291			n = diff;
292		/*
293		 * read ahead could possibly be worth something
294		 * but not much as ados makes little attempt to
295		 * make things contigous
296		 */
297		error = bread(sp->a_vp, lbn, amp->bsize, NOCRED, 0, &bp);
298		if (error) {
299			brelse(bp, 0);
300			goto reterr;
301		}
302		if (!IS_FFS(amp)) {
303			if (bp->b_resid > 0)
304				error = EIO; /* OFS needs the complete block */
305			else if (adoswordn(bp, 0) != BPT_DATA) {
306#ifdef DIAGNOSTIC
307				printf("adosfs: bad primary type blk %" PRId64 "\n",
308				    bp->b_blkno / (amp->bsize / DEV_BSIZE));
309#endif
310				error = EINVAL;
311			} else if (adoscksum(bp, ap->nwords)) {
312#ifdef DIAGNOSTIC
313				printf("adosfs: blk %" PRId64 " failed cksum.\n",
314				    bp->b_blkno / (amp->bsize / DEV_BSIZE));
315#endif
316				error = EINVAL;
317			}
318		}
319
320		if (error) {
321			brelse(bp, 0);
322			goto reterr;
323		}
324#ifdef ADOSFS_DIAGNOSTIC
325		printf(" %" PRId64 "+%ld-%" PRId64 "+%ld", lbn, on, lbn, n);
326#endif
327		n = MIN(n, size - bp->b_resid);
328		error = uiomove((char *)bp->b_data + on +
329				amp->bsize - amp->dbsize, (int)n, uio);
330		brelse(bp, 0);
331	} while (error == 0 && uio->uio_resid > 0 && n != 0);
332
333out:
334reterr:
335#ifdef ADOSFS_DIAGNOSTIC
336	printf(" %d)", error);
337#endif
338	return(error);
339}
340
341int
342adosfs_write(void *v)
343{
344#ifdef ADOSFS_DIAGNOSTIC
345#if 0
346	struct vop_write_args /* {
347		struct vnode *a_vp;
348		struct uio *a_uio;
349		int a_ioflag;
350		kauth_cred_t a_cred;
351	} */ *sp = v;
352	advopprint(sp);
353#endif
354	printf(" EOPNOTSUPP)");
355#endif
356	return(EOPNOTSUPP);
357}
358
359/*
360 * Just call the device strategy routine
361 */
362int
363adosfs_strategy(void *v)
364{
365	struct vop_strategy_args /* {
366		struct vnode *a_vp;
367		struct buf *a_bp;
368	} */ *sp = v;
369	struct buf *bp;
370	struct anode *ap;
371	struct vnode *vp;
372	int error;
373
374#ifdef ADOSFS_DIAGNOSTIC
375	advopprint(sp);
376#endif
377	bp = sp->a_bp;
378	if (bp->b_vp == NULL) {
379		bp->b_error = EIO;
380		biodone(bp);
381		error = EIO;
382		goto reterr;
383	}
384	vp = sp->a_vp;
385	ap = VTOA(vp);
386	if (bp->b_blkno == bp->b_lblkno) {
387		error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
388		if (error) {
389			bp->b_flags = error;
390			biodone(bp);
391			goto reterr;
392		}
393	}
394	if ((long)bp->b_blkno == -1) {
395		biodone(bp);
396		error = 0;
397		goto reterr;
398	}
399	vp = ap->amp->devvp;
400	error = VOP_STRATEGY(vp, bp);
401reterr:
402#ifdef ADOSFS_DIAGNOSTIC
403	printf(" %d)", error);
404#endif
405	return(error);
406}
407
408int
409adosfs_link(void *v)
410{
411	struct vop_link_args /* {
412		struct vnode *a_dvp;
413		struct vnode *a_vp;
414		struct componentname *a_cnp;
415	} */ *ap = v;
416
417	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
418	vput(ap->a_dvp);
419	return (EROFS);
420}
421
422int
423adosfs_symlink(void *v)
424{
425	struct vop_symlink_args /* {
426		struct vnode *a_dvp;
427		struct vnode **a_vpp;
428		struct componentname *a_cnp;
429		struct vattr *a_vap;
430		char *a_target;
431	} */ *ap = v;
432
433	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
434	vput(ap->a_dvp);
435	return (EROFS);
436}
437
438/*
439 * Wait until the vnode has finished changing state.
440 */
441int
442adosfs_bmap(void *v)
443{
444	struct vop_bmap_args /* {
445		struct vnode *a_vp;
446		daddr_t  a_bn;
447		struct vnode **a_vpp;
448		daddr_t *a_bnp;
449		int *a_runp;
450	} */ *sp = v;
451	struct anode *ap;
452	struct buf *flbp;
453	long nb, flblk, flblkoff, fcnt;
454	daddr_t *bnp;
455	daddr_t bn;
456	int error;
457
458#ifdef ADOSFS_DIAGNOSTIC
459	advopprint(sp);
460#endif
461	ap = VTOA(sp->a_vp);
462	bn = sp->a_bn;
463	bnp = sp->a_bnp;
464	if (sp->a_runp) {
465		*sp->a_runp = 0;
466	}
467	error = 0;
468
469	if (sp->a_vpp != NULL)
470		*sp->a_vpp = ap->amp->devvp;
471	if (bnp == NULL)
472		goto reterr;
473	if (bn < 0) {
474		error = EFBIG;
475		goto reterr;
476	}
477	if (sp->a_vp->v_type != VREG) {
478		error = EINVAL;
479		goto reterr;
480	}
481
482	/*
483	 * walk the chain of file list blocks until we find
484	 * the one that will yield the block pointer we need.
485	 */
486	if (ap->type == AFILE)
487		nb = ap->block;			/* pointer to ourself */
488	else if (ap->type == ALFILE)
489		nb = ap->linkto;		/* pointer to real file */
490	else {
491		error = EINVAL;
492		goto reterr;
493	}
494
495	flblk = bn / ANODENDATBLKENT(ap);
496	flbp = NULL;
497
498	/*
499	 * check last indirect block cache
500	 */
501	if (flblk < ap->lastlindblk)
502		fcnt = 0;
503	else {
504		flblk -= ap->lastlindblk;
505		fcnt = ap->lastlindblk;
506		nb = ap->lastindblk;
507	}
508	while (flblk >= 0) {
509		if (flbp)
510			brelse(flbp, 0);
511		if (nb == 0) {
512#ifdef DIAGNOSTIC
513			printf("adosfs: bad file list chain.\n");
514#endif
515			error = EINVAL;
516			goto reterr;
517		}
518		error = bread(ap->amp->devvp, nb * ap->amp->bsize / DEV_BSIZE,
519			      ap->amp->bsize, NOCRED, 0, &flbp);
520		if (error) {
521			brelse(flbp, 0);
522			goto reterr;
523		}
524		if (adoscksum(flbp, ap->nwords)) {
525#ifdef DIAGNOSTIC
526			printf("adosfs: blk %ld failed cksum.\n", nb);
527#endif
528			brelse(flbp, 0);
529			error = EINVAL;
530			goto reterr;
531		}
532		/*
533		 * update last indirect block cache
534		 */
535		ap->lastlindblk = fcnt++;
536		ap->lastindblk = nb;
537
538		nb = adoswordn(flbp, ap->nwords - 2);
539		flblk--;
540	}
541	/*
542	 * calculate offset of block number in table.  The table starts
543	 * at nwords - 51 and goes to offset 6 or less if indicated by the
544	 * valid table entries stored at offset ADBI_NBLKTABENT.
545	 */
546	flblkoff = bn % ANODENDATBLKENT(ap);
547	if (flblkoff < adoswordn(flbp, 2 /* ADBI_NBLKTABENT */)) {
548		flblkoff = (ap->nwords - 51) - flblkoff;
549		*bnp = adoswordn(flbp, flblkoff) * ap->amp->bsize / DEV_BSIZE;
550	} else {
551#ifdef DIAGNOSTIC
552		printf("flblk offset %ld too large in lblk %ld blk %" PRId64 "\n",
553		    flblkoff, (long)bn, flbp->b_blkno);
554#endif
555		error = EINVAL;
556	}
557	brelse(flbp, 0);
558reterr:
559#ifdef ADOSFS_DIAGNOSTIC
560	if (error == 0 && bnp)
561		printf(" %lld => %lld", (long long)bn, (long long)*bnp);
562	printf(" %d)\n", error);
563#endif
564	return(error);
565}
566
567/*
568 * Print out the contents of a adosfs vnode.
569 */
570/* ARGSUSED */
571int
572adosfs_print(void *v)
573{
574#if 0
575	struct vop_print_args /* {
576		struct vnode *a_vp;
577	} */ *sp = v;
578#endif
579	return(0);
580}
581
582int
583adosfs_readdir(void *v)
584{
585	struct vop_readdir_args /* {
586		struct vnode *a_vp;
587		struct uio *a_uio;
588		kauth_cred_t a_cred;
589		int *a_eofflag;
590		off_t **a_cookies;
591		int *a_ncookies;
592	} */ *sp = v;
593	int error, first, useri, chainc, hashi, scanned;
594	u_long nextbn;
595	struct dirent ad, *adp;
596	struct anode *pap, *ap;
597	struct vnode *vp;
598	struct uio *uio = sp->a_uio;
599	off_t uoff = uio->uio_offset;
600	off_t *cookies = NULL;
601	int ncookies = 0;
602
603#ifdef ADOSFS_DIAGNOSTIC
604	advopprint(sp);
605#endif
606
607	if (sp->a_vp->v_type != VDIR) {
608		error = ENOTDIR;
609		goto reterr;
610	}
611
612	if (uoff < 0) {
613		error = EINVAL;
614		goto reterr;
615	}
616
617	pap = VTOA(sp->a_vp);
618	adp = &ad;
619	error = nextbn = hashi = chainc = scanned = 0;
620	first = useri = uoff / sizeof ad;
621
622	/*
623	 * If offset requested is not on a slot boundary
624	 */
625	if (uoff % sizeof ad) {
626		error = EINVAL;
627		goto reterr;
628	}
629
630	for (;;) {
631		if (hashi == pap->ntabent) {
632			*sp->a_eofflag = 1;
633			break;
634		}
635		if (pap->tab[hashi] == 0) {
636			hashi++;
637			continue;
638		}
639		if (nextbn == 0)
640			nextbn = pap->tab[hashi];
641
642		/*
643		 * First determine if we can skip this chain
644		 */
645		if (chainc == 0) {
646			int skip;
647
648			skip = useri - scanned;
649			if (pap->tabi[hashi] > 0 && pap->tabi[hashi] <= skip) {
650				scanned += pap->tabi[hashi];
651				hashi++;
652				nextbn = 0;
653				continue;
654			}
655		}
656
657		/*
658		 * Now [continue to] walk the chain
659		 */
660		ap = NULL;
661		do {
662			error = VFS_VGET(pap->amp->mp, (ino_t)nextbn, &vp);
663			if (error)
664				goto reterr;
665			ap = VTOA(vp);
666			scanned++;
667			chainc++;
668			nextbn = ap->hashf;
669
670			/*
671			 * check for end of chain.
672			 */
673			if (nextbn == 0) {
674				pap->tabi[hashi] = chainc;
675				hashi++;
676				chainc = 0;
677			} else if (pap->tabi[hashi] <= 0 &&
678			    -chainc < pap->tabi[hashi])
679				pap->tabi[hashi] = -chainc;
680
681			if (useri >= scanned) {
682				vput(vp);
683				ap = NULL;
684			}
685		} while (ap == NULL && nextbn != 0);
686
687		/*
688		 * We left the loop but without a result so do main over.
689		 */
690		if (ap == NULL)
691			continue;
692		/*
693		 * Fill in dirent record
694		 */
695		memset(adp, 0, sizeof *adp);
696		adp->d_fileno = ap->block;
697		/*
698		 * This deserves a function in kern/vfs_subr.c
699		 */
700		switch (ATOV(ap)->v_type) {
701		case VREG:
702			adp->d_type = DT_REG;
703			break;
704		case VDIR:
705			adp->d_type = DT_DIR;
706			break;
707		case VLNK:
708			adp->d_type = DT_LNK;
709			break;
710		default:
711			adp->d_type = DT_UNKNOWN;
712			break;
713		}
714		adp->d_namlen = strlen(ap->name);
715		memcpy(adp->d_name, ap->name, adp->d_namlen);
716		adp->d_reclen = _DIRENT_SIZE(adp);
717		vput(vp);
718
719		if (adp->d_reclen > uio->uio_resid) {
720			if (useri == first)	/* no room for even one entry */
721				error = EINVAL;
722			break;
723		}
724		error = uiomove(adp, adp->d_reclen, uio);
725		if (error)
726			break;
727		useri++;
728	}
729	ncookies = useri - first;
730	uio->uio_offset = uoff + ncookies * sizeof ad;
731reterr:
732#ifdef ADOSFS_DIAGNOSTIC
733	printf(" %d)", error);
734#endif
735	if (sp->a_ncookies != NULL) {
736		*sp->a_ncookies = ncookies;
737		if (!error) {
738			*sp->a_cookies = cookies =
739			   malloc(ncookies * sizeof *cookies, M_TEMP, M_WAITOK);
740
741			while (ncookies--) {
742				uoff += sizeof ad;
743				*cookies++ = uoff;
744			}
745		} else
746			*sp->a_cookies = NULL;
747	}
748
749	return(error);
750}
751
752static int
753adosfs_check_possible(struct vnode *vp, struct anode *ap, mode_t mode)
754{
755
756	/*
757	 * Disallow write attempts unless the file is a socket,
758	 * fifo, or a block or character device resident on the
759	 * file system.
760	 */
761	if (mode & VWRITE) {
762		switch (vp->v_type) {
763		case VDIR:
764		case VLNK:
765		case VREG:
766			return (EROFS);
767		default:
768			break;
769		}
770	}
771
772	return 0;
773}
774
775static int
776adosfs_check_permitted(struct vnode *vp, struct anode *ap, mode_t mode,
777    kauth_cred_t cred)
778{
779
780	return genfs_can_access(vp->v_type,
781	    adunixprot(ap->adprot) & ap->amp->mask, ap->uid, ap->gid, mode,
782	    cred);
783}
784
785int
786adosfs_access(void *v)
787{
788	struct vop_access_args /* {
789		struct vnode *a_vp;
790		int  a_mode;
791		kauth_cred_t a_cred;
792	} */ *sp = v;
793	struct anode *ap;
794	struct vnode *vp = sp->a_vp;
795	int error;
796
797#ifdef ADOSFS_DIAGNOSTIC
798	advopprint(sp);
799#endif
800
801	ap = VTOA(vp);
802#ifdef DIAGNOSTIC
803	if (!VOP_ISLOCKED(vp)) {
804		vprint("adosfs_access: not locked", sp->a_vp);
805		panic("adosfs_access: not locked");
806	}
807#endif
808
809	error = adosfs_check_possible(vp, ap, sp->a_mode);
810	if (error)
811		return error;
812
813	error = adosfs_check_permitted(vp, ap, sp->a_mode, sp->a_cred);
814
815#ifdef ADOSFS_DIAGNOSTIC
816	printf(" %d)", error);
817#endif
818	return(error);
819}
820
821int
822adosfs_readlink(void *v)
823{
824	struct vop_readlink_args /* {
825		struct vnode *a_vp;
826		struct uio *a_uio;
827		kauth_cred_t a_cred;
828	} */ *sp = v;
829	struct anode *ap;
830	int error;
831
832#ifdef ADOSFS_DIAGNOSTIC
833	advopprint(sp);
834#endif
835	ap = VTOA(sp->a_vp);
836	error = uiomove(ap->slinkto, strlen(ap->slinkto), sp->a_uio);
837#ifdef ADOSFS_DIAGNOSTIC
838	printf(" %d)", error);
839#endif
840	return (error);
841}
842
843/*ARGSUSED*/
844int
845adosfs_inactive(void *v)
846{
847	struct vop_inactive_args /* {
848		struct vnode *a_vp;
849		bool *a_recycle;
850	} */ *sp = v;
851	struct vnode *vp = sp->a_vp;
852#ifdef ADOSFS_DIAGNOSTIC
853	advopprint(sp);
854#endif
855	VOP_UNLOCK(vp);
856	/* XXX this needs to check if file was deleted */
857	*sp->a_recycle = true;
858
859#ifdef ADOSFS_DIAGNOSTIC
860	printf(" 0)");
861#endif
862	return(0);
863}
864
865/*
866 * the kernel wants its vnode back.
867 * no lock needed we are being called from vclean()
868 */
869int
870adosfs_reclaim(void *v)
871{
872	struct vop_reclaim_args /* {
873		struct vnode *a_vp;
874	} */ *sp = v;
875	struct vnode *vp;
876	struct anode *ap;
877
878#ifdef ADOSFS_DIAGNOSTIC
879	printf("(reclaim 0)");
880#endif
881	vp = sp->a_vp;
882	ap = VTOA(vp);
883	LIST_REMOVE(ap, link);
884	if (vp->v_type == VDIR && ap->tab)
885		free(ap->tab, M_ANODE);
886	else if (vp->v_type == VLNK && ap->slinkto)
887		free(ap->slinkto, M_ANODE);
888	genfs_node_destroy(vp);
889	pool_put(&adosfs_node_pool, ap);
890	vp->v_data = NULL;
891	return(0);
892}
893
894/*
895 * POSIX pathconf info, grabbed from kern/u fs, probably need to
896 * investigate exactly what each return type means as they are probably
897 * not valid currently
898 */
899int
900adosfs_pathconf(void *v)
901{
902	struct vop_pathconf_args /* {
903		struct vnode *a_vp;
904		int a_name;
905		register_t *a_retval;
906	} */ *ap = v;
907
908	switch (ap->a_name) {
909	case _PC_LINK_MAX:
910		*ap->a_retval = LINK_MAX;
911		return (0);
912	case _PC_NAME_MAX:
913		*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_namemax;
914		return (0);
915	case _PC_PATH_MAX:
916		*ap->a_retval = PATH_MAX;
917		return (0);
918	case _PC_PIPE_BUF:
919		*ap->a_retval = PIPE_BUF;
920		return (0);
921	case _PC_CHOWN_RESTRICTED:
922		*ap->a_retval = 1;
923		return (0);
924	case _PC_VDISABLE:
925		*ap->a_retval = _POSIX_VDISABLE;
926		return (0);
927	case _PC_SYNC_IO:
928		*ap->a_retval = 1;
929		return (0);
930	case _PC_FILESIZEBITS:
931		*ap->a_retval = 32;
932		return (0);
933	default:
934		return (EINVAL);
935	}
936	/* NOTREACHED */
937}
938