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