1/*	$NetBSD: efs_vnops.c,v 1.44 2022/08/06 18:26:41 andvar Exp $	*/
2
3/*
4 * Copyright (c) 2006 Stephen M. Rumble <rumble@ephemeral.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/cdefs.h>
20__KERNEL_RCSID(0, "$NetBSD: efs_vnops.c,v 1.44 2022/08/06 18:26:41 andvar Exp $");
21
22#include <sys/param.h>
23#include <sys/systm.h>
24#include <sys/proc.h>
25#include <sys/kernel.h>
26#include <sys/vnode.h>
27#include <sys/mount.h>
28#include <sys/malloc.h>
29#include <sys/namei.h>
30#include <sys/dirent.h>
31#include <sys/lockf.h>
32#include <sys/unistd.h>
33#include <sys/buf.h>
34
35#include <miscfs/genfs/genfs.h>
36#include <miscfs/genfs/genfs_node.h>
37#include <miscfs/fifofs/fifo.h>
38#include <miscfs/specfs/specdev.h>
39
40#include <fs/efs/efs.h>
41#include <fs/efs/efs_sb.h>
42#include <fs/efs/efs_dir.h>
43#include <fs/efs/efs_genfs.h>
44#include <fs/efs/efs_mount.h>
45#include <fs/efs/efs_extent.h>
46#include <fs/efs/efs_dinode.h>
47#include <fs/efs/efs_inode.h>
48#include <fs/efs/efs_subr.h>
49
50MALLOC_DECLARE(M_EFSTMP);
51
52/*
53 * Lookup a pathname component in the given directory.
54 *
55 * Returns 0 on success.
56 */
57static int
58efs_lookup(void *v)
59{
60	struct vop_lookup_v2_args /* {
61		struct vnode *a_dvp;
62		struct vnode **a_vpp;
63		struct componentname *a_cnp;
64	} */ *ap = v;
65	struct componentname *cnp = ap->a_cnp;
66	struct vnode *vp;
67	ino_t ino;
68	int err, nameiop = cnp->cn_nameiop;
69
70	/* ensure that the directory can be accessed first */
71        err = VOP_ACCESS(ap->a_dvp, VEXEC, cnp->cn_cred);
72	if (err)
73		return (err);
74
75	if (cache_lookup(ap->a_dvp, cnp->cn_nameptr, cnp->cn_namelen,
76			 cnp->cn_nameiop, cnp->cn_flags, NULL, ap->a_vpp)) {
77		return *ap->a_vpp == NULLVP ? ENOENT : 0;
78	}
79
80	/*
81	 * Handle the lookup types: '.' or everything else.
82	 */
83	if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
84		vref(ap->a_dvp);
85		*ap->a_vpp = ap->a_dvp;
86	} else {
87		err = efs_inode_lookup(VFSTOEFS(ap->a_dvp->v_mount),
88		    EFS_VTOI(ap->a_dvp), ap->a_cnp, &ino);
89		if (err) {
90			if (cnp->cn_flags & ISDOTDOT)
91				return (err);
92			if (err == ENOENT && nameiop != CREATE)
93				cache_enter(ap->a_dvp, NULL, cnp->cn_nameptr,
94					    cnp->cn_namelen, cnp->cn_flags);
95			if (err == ENOENT && (nameiop == CREATE ||
96			    nameiop == RENAME)) {
97				err = VOP_ACCESS(ap->a_dvp, VWRITE,
98				    cnp->cn_cred);
99				if (err)
100					return (err);
101				return (EJUSTRETURN);
102			}
103			return (err);
104		}
105		err = vcache_get(ap->a_dvp->v_mount, &ino, sizeof(ino), &vp);
106		if (err)
107			return (err);
108		*ap->a_vpp = vp;
109	}
110
111	cache_enter(ap->a_dvp, *ap->a_vpp, cnp->cn_nameptr, cnp->cn_namelen,
112		    cnp->cn_flags);
113
114	return 0;
115}
116
117static int
118efs_check_possible(struct vnode *vp, struct efs_inode *eip, accmode_t accmode)
119{
120
121	if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY))
122		return (EROFS);
123
124	return 0;
125}
126
127/*
128 * Determine the accessibility of a file based on the permissions allowed by the
129 * specified credentials.
130 *
131 * Returns 0 on success.
132 */
133static int
134efs_check_permitted(struct vnode *vp, struct efs_inode *eip, accmode_t accmode,
135    kauth_cred_t cred)
136{
137
138	return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode,
139	    vp->v_type, eip->ei_mode), vp, NULL, genfs_can_access(vp,
140	    cred, eip->ei_uid, eip->ei_gid, eip->ei_mode, NULL, accmode));
141}
142
143static int
144efs_access(void *v)
145{
146	struct vop_access_args /* {
147		const struct vnodeop_desc *a_desc;
148		struct vnode *a_vp;
149		accmode_t a_accmode;
150		struct ucred *a_cred;
151	} */ *ap = v;
152	struct vnode *vp = ap->a_vp;
153	struct efs_inode *eip = EFS_VTOI(vp);
154	int error;
155
156	error = efs_check_possible(vp, eip, ap->a_accmode);
157	if (error)
158		return error;
159
160	error = efs_check_permitted(vp, eip, ap->a_accmode, ap->a_cred);
161
162	return error;
163}
164
165/*
166 * Get specific vnode attributes on a file. See vattr(9).
167 *
168 * Returns 0 on success.
169 */
170static int
171efs_getattr(void *v)
172{
173	struct vop_getattr_args /* {
174		const struct vnodeop_desc *a_desc;
175		struct vnode *a_vp;
176		struct vattr *a_vap;
177		struct ucred *a_cred;
178	} */ *ap = v;
179
180	struct vattr *vap = ap->a_vap;
181	struct efs_inode *eip = EFS_VTOI(ap->a_vp);
182
183	vattr_null(ap->a_vap);
184	vap->va_type		= ap->a_vp->v_type;
185	vap->va_mode		= eip->ei_mode;
186	vap->va_nlink		= eip->ei_nlink;
187	vap->va_uid		= eip->ei_uid;
188	vap->va_gid		= eip->ei_gid;
189	vap->va_fsid 		= ap->a_vp->v_mount->mnt_stat.f_fsid;
190	vap->va_fileid 		= eip->ei_number;
191	vap->va_size 		= eip->ei_size;
192
193	if (ap->a_vp->v_type == VBLK)
194		vap->va_blocksize = BLKDEV_IOSIZE;
195	else if (ap->a_vp->v_type == VCHR)
196		vap->va_blocksize = MAXBSIZE;
197	else
198		vap->va_blocksize = EFS_BB_SIZE;
199
200	vap->va_atime.tv_sec	= eip->ei_atime;
201	vap->va_mtime.tv_sec	= eip->ei_mtime;
202	vap->va_ctime.tv_sec	= eip->ei_ctime;
203/*	vap->va_birthtime 	= */
204	vap->va_gen		= eip->ei_gen;
205	vap->va_flags		= ap->a_vp->v_vflag |
206	    ap->a_vp->v_iflag | ap->a_vp->v_uflag;
207
208	if (ap->a_vp->v_type == VBLK || ap->a_vp->v_type == VCHR) {
209		uint32_t dmaj, dmin;
210
211		if (be16toh(eip->ei_di.di_odev) != EFS_DINODE_ODEV_INVALID) {
212			dmaj = EFS_DINODE_ODEV_MAJ(be16toh(eip->ei_di.di_odev));
213			dmin = EFS_DINODE_ODEV_MIN(be16toh(eip->ei_di.di_odev));
214		} else {
215			dmaj = EFS_DINODE_NDEV_MAJ(be32toh(eip->ei_di.di_ndev));
216			dmin = EFS_DINODE_NDEV_MIN(be32toh(eip->ei_di.di_ndev));
217		}
218
219		vap->va_rdev = makedev(dmaj, dmin);
220	}
221
222	vap->va_bytes		= eip->ei_size;
223/*	vap->va_filerev		= */
224/*	vap->va_vaflags		= */
225
226	return (0);
227}
228
229/*
230 * Read a file.
231 *
232 * Returns 0 on success.
233 */
234static int
235efs_read(void *v)
236{
237	struct vop_read_args /* {
238		const struct vnodeop_desc *a_desc;
239		struct vnode *a_vp;
240		struct uio *a_uio;
241		int a_ioflag;
242		struct ucred *a_cred;
243	} */ *ap = v;
244	struct efs_extent ex;
245	struct efs_extent_iterator exi;
246	struct uio *uio = ap->a_uio;
247	struct efs_inode *eip = EFS_VTOI(ap->a_vp);
248	off_t start;
249	vsize_t len;
250	int err, ret;
251	const int advice = IO_ADV_DECODE(ap->a_ioflag);
252
253	if (ap->a_vp->v_type == VDIR)
254		return (EISDIR);
255
256	if (ap->a_vp->v_type != VREG)
257		return (EINVAL);
258
259	efs_extent_iterator_init(&exi, eip, uio->uio_offset);
260	ret = efs_extent_iterator_next(&exi, &ex);
261	while (ret == 0) {
262		if (uio->uio_offset < 0 || uio->uio_offset >= eip->ei_size ||
263		    uio->uio_resid == 0)
264			break;
265
266		start = ex.ex_offset * EFS_BB_SIZE;
267		len   = ex.ex_length * EFS_BB_SIZE;
268
269		if (!(uio->uio_offset >= start &&
270		      uio->uio_offset < (start + len))) {
271			ret = efs_extent_iterator_next(&exi, &ex);
272			continue;
273		}
274
275		start = uio->uio_offset - start;
276
277		len = MIN(len - start, uio->uio_resid);
278		len = MIN(len, eip->ei_size - uio->uio_offset);
279
280		err = ubc_uiomove(&ap->a_vp->v_uobj, uio, len, advice,
281		    UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(ap->a_vp));
282		if (err) {
283			EFS_DPRINTF(("efs_read: uiomove error %d\n",
284			    err));
285			return (err);
286		}
287	}
288
289	return ((ret == -1) ? 0 : ret);
290}
291
292static int
293efs_readdir(void *v)
294{
295	struct vop_readdir_args /* {
296		const struct vnodeop_desc *a_desc;
297		struct vnode *a_vp;
298		struct uio *a_uio;
299		struct ucred *a_cred;
300		int *a_eofflag;
301		off_t **a_cookies;
302		int *a_ncookies;
303	} */ *ap = v;
304	struct dirent *dp;
305	struct efs_dinode edi;
306	struct efs_extent ex;
307	struct efs_extent_iterator exi;
308	struct buf *bp;
309	struct efs_dirent *de;
310	struct efs_dirblk *db;
311	struct uio *uio = ap->a_uio;
312	struct efs_inode *ei = EFS_VTOI(ap->a_vp);
313	off_t *cookies = NULL;
314	off_t offset;
315	int i, j, err, ret, s, slot, ncookies, maxcookies = 0;
316
317	if (ap->a_vp->v_type != VDIR)
318		return (ENOTDIR);
319
320	if (ap->a_eofflag != NULL)
321		*ap->a_eofflag = false;
322
323	if (ap->a_ncookies != NULL) {
324		ncookies = 0;
325		maxcookies =
326		    uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
327		cookies = malloc(maxcookies * sizeof(off_t), M_TEMP, M_WAITOK);
328 	}
329
330	dp = malloc(sizeof(struct dirent), M_EFSTMP, M_WAITOK | M_ZERO);
331
332	offset = 0;
333	efs_extent_iterator_init(&exi, ei, 0);
334	while ((ret = efs_extent_iterator_next(&exi, &ex)) == 0) {
335		for (i = 0; i < ex.ex_length; i++) {
336			err = efs_bread(VFSTOEFS(ap->a_vp->v_mount),
337			    ex.ex_bn + i, NULL, &bp);
338			if (err) {
339				goto exit_err;
340			}
341
342			db = (struct efs_dirblk *)bp->b_data;
343
344			if (be16toh(db->db_magic) != EFS_DIRBLK_MAGIC) {
345				printf("efs_readdir: bad dirblk\n");
346				brelse(bp, 0);
347				continue;
348			}
349
350			for (j = 0; j < db->db_slots; j++) {
351				slot = EFS_DIRENT_OFF_EXPND(db->db_space[j]);
352				if (slot == EFS_DIRBLK_SLOT_FREE)
353					continue;
354
355				if (!EFS_DIRENT_OFF_VALID(slot)) {
356					printf("efs_readdir: bad dirent\n");
357					continue;
358				}
359
360				de = EFS_DIRBLK_TO_DIRENT(db, slot);
361				s = _DIRENT_RECLEN(dp, de->de_namelen);
362
363				if (offset < uio->uio_offset) {
364					offset += s;
365					continue;
366				}
367
368				/* XXX - shouldn't happen, right? */
369				if (offset > uio->uio_offset ||
370				    s > uio->uio_resid) {
371					brelse(bp, 0);
372					goto exit_ok;
373				}
374
375				/* de_namelen is uint8_t, d.d_name is 512b */
376				KASSERT(sizeof(dp->d_name)-de->de_namelen > 0);
377				dp->d_fileno = be32toh(de->de_inumber);
378				dp->d_reclen = s;
379				dp->d_namlen = de->de_namelen;
380				memcpy(dp->d_name, de->de_name,
381				    de->de_namelen);
382				dp->d_name[de->de_namelen] = '\0';
383
384				/* look up inode to get type */
385				err = efs_read_inode(
386				    VFSTOEFS(ap->a_vp->v_mount),
387				    dp->d_fileno, NULL, &edi);
388				if (err) {
389					brelse(bp, 0);
390					goto exit_err;
391				}
392
393				switch (be16toh(edi.di_mode) & EFS_IFMT) {
394				case EFS_IFIFO:
395					dp->d_type = DT_FIFO;
396					break;
397				case EFS_IFCHR:
398					dp->d_type = DT_CHR;
399					break;
400				case EFS_IFDIR:
401					dp->d_type = DT_DIR;
402					break;
403				case EFS_IFBLK:
404					dp->d_type = DT_BLK;
405					break;
406				case EFS_IFREG:
407					dp->d_type = DT_REG;
408					break;
409				case EFS_IFLNK:
410					dp->d_type = DT_LNK;
411					break;
412				case EFS_IFSOCK:
413					dp->d_type = DT_SOCK;
414					break;
415				default:
416					dp->d_type = DT_UNKNOWN;
417					break;
418				}
419
420				err = uiomove(dp, s, uio);
421				if (err) {
422					brelse(bp, 0);
423					goto exit_err;
424				}
425
426				offset += s;
427
428				if (cookies != NULL && maxcookies != 0) {
429					cookies[ncookies++] = offset;
430					if (ncookies == maxcookies) {
431						brelse(bp, 0);
432						goto exit_ok;
433					}
434				}
435			}
436
437			brelse(bp, 0);
438		}
439	}
440
441	if (ret != -1) {
442		err = ret;
443		goto exit_err;
444	}
445
446	if (ap->a_eofflag != NULL)
447		*ap->a_eofflag = true;
448
449 exit_ok:
450	if (cookies != NULL) {
451		*ap->a_cookies = cookies;
452		*ap->a_ncookies = ncookies;
453	}
454
455	uio->uio_offset = offset;
456
457	free(dp, M_EFSTMP);
458
459	return (0);
460
461 exit_err:
462	if (cookies != NULL)
463		free(cookies, M_TEMP);
464
465	free(dp, M_EFSTMP);
466
467	return (err);
468}
469
470static int
471efs_readlink(void *v)
472{
473	struct vop_readlink_args /* {
474		const struct vnodeop_desc *a_desc;
475		struct vnode *a_vp;
476		struct uio *a_uio;
477		struct ucred *a_cred;
478	} */ *ap = v;
479	struct uio *uio = ap->a_uio;
480	struct efs_inode *eip = EFS_VTOI(ap->a_vp);
481	char *buf;
482	size_t len;
483	int err, i;
484
485	if ((eip->ei_mode & EFS_IFMT) != EFS_IFLNK)
486		return (EINVAL);
487
488	if (uio->uio_resid < 1)
489		return (EINVAL);
490
491	buf = malloc(eip->ei_size + 1, M_EFSTMP, M_ZERO | M_WAITOK);
492
493	/* symlinks are either inlined in the inode, or in extents */
494	if (eip->ei_numextents == 0) {
495		if (eip->ei_size > sizeof(eip->ei_di.di_symlink)) {
496			EFS_DPRINTF(("efs_readlink: too big for inline\n"));
497			free(buf, M_EFSTMP);
498			return (EBADF);
499		}
500
501		memcpy(buf, eip->ei_di.di_symlink, eip->ei_size);
502		len = MIN(uio->uio_resid, eip->ei_size + 1);
503	} else {
504		struct efs_extent_iterator exi;
505		struct efs_extent ex;
506		struct buf *bp;
507		int resid, off, ret;
508
509		off = 0;
510		resid = eip->ei_size;
511
512		efs_extent_iterator_init(&exi, eip, 0);
513		while ((ret = efs_extent_iterator_next(&exi, &ex)) == 0) {
514			for (i = 0; i < ex.ex_length; i++) {
515				err = efs_bread(VFSTOEFS(ap->a_vp->v_mount),
516				    ex.ex_bn + i, NULL, &bp);
517				if (err) {
518					free(buf, M_EFSTMP);
519					return (err);
520				}
521
522				len = MIN(resid, bp->b_bcount);
523				memcpy(buf + off, bp->b_data, len);
524				brelse(bp, 0);
525
526				off += len;
527				resid -= len;
528
529				if (resid == 0)
530					break;
531			}
532
533			if (resid == 0)
534				break;
535		}
536
537		if (ret != 0 && ret != -1) {
538			free(buf, M_EFSTMP);
539			return (ret);
540		}
541
542		len = off + 1;
543	}
544
545	KASSERT(len >= 1 && len <= (eip->ei_size + 1));
546	buf[len - 1] = '\0';
547	err = uiomove(buf, len, uio);
548	free(buf, M_EFSTMP);
549
550	return (err);
551}
552
553/*
554 * Release an inactive vnode. The vnode _must_ be unlocked on return.
555 * It is either nolonger being used by the kernel, or an unmount is being
556 * forced.
557 *
558 * Returns 0 on success.
559 */
560static int
561efs_inactive(void *v)
562{
563	struct vop_inactive_v2_args /* {
564		const struct vnodeop_desc *a_desc;
565		struct vnode *a_vp;
566		bool *a_recycle
567	} */ *ap = v;
568	struct efs_inode *eip = EFS_VTOI(ap->a_vp);
569
570	*ap->a_recycle = (eip->ei_mode == 0);
571
572	return (0);
573}
574
575static int
576efs_reclaim(void *v)
577{
578	struct vop_reclaim_v2_args /* {
579		const struct vnodeop_desc *a_desc;
580		struct vnode *a_vp;
581	} */ *ap = v;
582	struct vnode *vp = ap->a_vp;
583	struct efs_inode *eip = EFS_VTOI(vp);
584
585	VOP_UNLOCK(vp);
586
587	genfs_node_destroy(vp);
588	pool_put(&efs_inode_pool, eip);
589	vp->v_data = NULL;
590
591	return (0);
592}
593
594static int
595efs_bmap(void *v)
596{
597	struct vop_bmap_args /* {
598		const struct vnodeop_desc *a_desc;
599		struct vnode *a_vp;
600		daddr_t a_bn;
601		struct vnode **a_vpp;
602		daddr_t *a_bnp;
603		int *a_runp;
604	} */ *ap = v;
605	struct efs_extent ex;
606	struct efs_extent_iterator exi;
607	struct vnode *vp = ap->a_vp;
608	struct efs_inode *eip = EFS_VTOI(vp);
609	bool found;
610	int ret;
611
612	if (ap->a_vpp != NULL)
613		*ap->a_vpp = VFSTOEFS(vp->v_mount)->em_devvp;
614
615	found = false;
616	efs_extent_iterator_init(&exi, eip, ap->a_bn * EFS_BB_SIZE);
617	while ((ret = efs_extent_iterator_next(&exi, &ex)) == 0) {
618		if (ap->a_bn >= ex.ex_offset &&
619		    ap->a_bn < (ex.ex_offset + ex.ex_length)) {
620			found = true;
621			break;
622		}
623	}
624
625	KASSERT(!found || ret == 0);
626
627	if (!found) {
628		EFS_DPRINTF(("efs_bmap: ap->a_bn not in extents\n"));
629		return ((ret == -1) ? EIO : ret);
630	}
631
632	if (ex.ex_magic != EFS_EXTENT_MAGIC) {
633		EFS_DPRINTF(("efs_bmap: exn.ex_magic != EFS_EXTENT_MAGIC\n"));
634		return (EIO);
635	}
636
637	if (ap->a_bn < ex.ex_offset) {
638		EFS_DPRINTF(("efs_bmap: ap->a_bn < exn.ex_offset\n"));
639		return (EIO);
640	}
641
642	KASSERT(ap->a_bn >= ex.ex_offset);
643	KASSERT(ex.ex_length > ap->a_bn - ex.ex_offset);
644
645	*ap->a_bnp = ex.ex_bn + (ap->a_bn - ex.ex_offset);
646	if (ap->a_runp != NULL)
647		*ap->a_runp = ex.ex_length - (ap->a_bn - ex.ex_offset) - 1;
648
649	return (0);
650}
651
652static int
653efs_strategy(void *v)
654{
655	struct vop_strategy_args /* {
656		const struct vnodeop_desc *a_desc;
657		struct vnode *a_vp;
658		struct buf *a_bp;
659	} */ *ap = v;
660	struct vnode *vp = ap->a_vp;
661	struct buf *bp = ap->a_bp;
662	int error;
663
664	if (vp == NULL) {
665		bp->b_error = EIO;
666		biodone(bp);
667		return (EIO);
668	}
669
670	if (bp->b_blkno == bp->b_lblkno) {
671		error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
672		if (error) {
673			bp->b_error = error;
674			biodone(bp);
675			return (error);
676		}
677		if ((long)bp->b_blkno == -1)
678			clrbuf(bp);
679	}
680
681	if ((long)bp->b_blkno == -1) {
682		biodone(bp);
683		return (0);
684	}
685
686	return (VOP_STRATEGY(VFSTOEFS(vp->v_mount)->em_devvp, bp));
687}
688
689static int
690efs_print(void *v)
691{
692	struct vop_print_args /* {
693		const struct vnodeop_desc *a_desc;
694		struct vnode *a_vp;
695	} */ *ap = v;
696	struct efs_inode *eip = EFS_VTOI(ap->a_vp);
697
698	printf(	"efs_inode (ino %lu):\n"
699		"    ei_mode:         %07o\n"
700		"    ei_nlink:        %d\n"
701		"    ei_uid:          %d\n"
702		"    ei_gid:          %d\n"
703		"    ei_size:         %d\n"
704		"    ei_atime:        %d\n"
705		"    ei_mtime:        %d\n"
706		"    ei_ctime:        %d\n"
707		"    ei_gen:          %d\n"
708		"    ei_numextents:   %d\n"
709		"    ei_version:      %d\n",
710		(unsigned long)eip->ei_number,
711		(unsigned int)eip->ei_mode,
712		eip->ei_nlink,
713		eip->ei_uid,
714		eip->ei_gid,
715		eip->ei_size,
716		(int32_t)eip->ei_atime,
717		(int32_t)eip->ei_mtime,
718		(int32_t)eip->ei_ctime,
719		eip->ei_gen,
720		eip->ei_numextents,
721		eip->ei_version);
722
723	return (0);
724}
725
726static int
727efs_pathconf(void *v)
728{
729	struct vop_pathconf_args /* {
730		const struct vnodeop_desc *a_desc;
731		struct vnode *a_vp;
732		int a_name;
733		register_t *a_retval;
734	} */ *ap = v;
735
736	/* IRIX 4 values */
737	switch (ap->a_name) {
738	case _PC_LINK_MAX:
739		*ap->a_retval = 30000;
740		break;
741	case _PC_NAME_MAX:
742		*ap->a_retval = 255;
743		break;
744	case _PC_PATH_MAX:
745		*ap->a_retval = 1024;
746		break;
747	case _PC_NO_TRUNC:
748		*ap->a_retval = 1;
749		break;
750	case _PC_CHOWN_RESTRICTED:
751		*ap->a_retval = 1;
752		break;
753	case _PC_SYNC_IO:
754		*ap->a_retval = 1;
755		break;
756	case _PC_FILESIZEBITS:
757		*ap->a_retval = 32;
758		break;
759	default:
760		return genfs_pathconf(ap);
761	}
762
763	return (0);
764}
765
766static int
767efs_advlock(void *v)
768{
769	struct vop_advlock_args /* {
770		const struct vnodeop_desc *a_desc;
771		struct vnode *a_vp;
772		void *a_id;
773		int a_op;
774		struct flock *a_fl;
775		int a_flags;
776	} */ *ap = v;
777	struct efs_inode *eip = EFS_VTOI(ap->a_vp);
778
779	return (lf_advlock(ap, &eip->ei_lockf, eip->ei_size));
780}
781
782/* Global vfs data structures for efs */
783int (**efs_vnodeop_p)(void *);
784const struct vnodeopv_entry_desc efs_vnodeop_entries[] = {
785	{ &vop_default_desc,	vn_default_error},	/* error handler */
786	{ &vop_parsepath_desc, genfs_parsepath },	/* parsepath */
787	{ &vop_lookup_desc,	efs_lookup	},	/* lookup */
788	{ &vop_create_desc,	genfs_eopnotsupp},	/* create */
789	{ &vop_mknod_desc,	genfs_eopnotsupp},	/* mknod */
790	{ &vop_open_desc,	genfs_nullop	},	/* open */
791	{ &vop_close_desc,	genfs_nullop	},	/* close */
792	{ &vop_access_desc,	efs_access	},	/* access */
793	{ &vop_accessx_desc,	genfs_accessx	},	/* accessx */
794	{ &vop_getattr_desc,	efs_getattr	},	/* getattr */
795	{ &vop_setattr_desc,	genfs_eopnotsupp},	/* setattr */
796	{ &vop_read_desc,	efs_read	},	/* read */
797	{ &vop_write_desc,	genfs_eopnotsupp},	/* write */
798	{ &vop_fallocate_desc,	genfs_eopnotsupp},	/* fallocate */
799	{ &vop_fdiscard_desc,	genfs_eopnotsupp},	/* fdiscard */
800	{ &vop_ioctl_desc,	genfs_enoioctl	},	/* ioctl */
801	{ &vop_fcntl_desc,	genfs_fcntl	},	/* fcntl */
802	{ &vop_poll_desc,	genfs_poll	},	/* poll */
803	{ &vop_kqfilter_desc,	genfs_kqfilter	},	/* kqfilter */
804	{ &vop_revoke_desc,	genfs_revoke	},	/* revoke */
805	{ &vop_mmap_desc,	genfs_mmap	},	/* mmap */
806	{ &vop_fsync_desc,	genfs_eopnotsupp},	/* fsync */
807	{ &vop_seek_desc,	genfs_seek	},	/* seek */
808	{ &vop_remove_desc,	genfs_eopnotsupp},	/* remove */
809	{ &vop_link_desc,	genfs_eopnotsupp},	/* link */
810	{ &vop_rename_desc,	genfs_eopnotsupp},	/* rename */
811	{ &vop_mkdir_desc,	genfs_eopnotsupp},	/* mkdir */
812	{ &vop_rmdir_desc,	genfs_eopnotsupp},	/* rmdir */
813	{ &vop_symlink_desc,	genfs_eopnotsupp},	/* symlink */
814	{ &vop_readdir_desc,	efs_readdir	},	/* readdir */
815	{ &vop_readlink_desc,	efs_readlink	},	/* readlink */
816	{ &vop_abortop_desc,	genfs_abortop	},	/* abortop */
817	{ &vop_inactive_desc,	efs_inactive	},	/* inactive */
818	{ &vop_reclaim_desc,	efs_reclaim	},	/* reclaim */
819	{ &vop_lock_desc,	genfs_lock,	},	/* lock */
820	{ &vop_unlock_desc,	genfs_unlock,	},	/* unlock */
821	{ &vop_islocked_desc,	genfs_islocked,	},	/* islocked */
822	{ &vop_bmap_desc,	efs_bmap	},	/* bmap */
823	{ &vop_print_desc,	efs_print	},	/* print */
824	{ &vop_pathconf_desc,	efs_pathconf	},	/* pathconf */
825	{ &vop_advlock_desc,	efs_advlock	},	/* advlock */
826							/* blkatoff */
827							/* valloc */
828							/* balloc */
829							/* vfree */
830							/* truncate */
831							/* whiteout */
832	{ &vop_getpages_desc,	genfs_getpages	},	/* getpages */
833	{ &vop_putpages_desc,	genfs_putpages	},	/* putpages */
834	{ &vop_bwrite_desc,	vn_bwrite	},	/* bwrite */
835	{ &vop_strategy_desc,	efs_strategy	},	/* strategy */
836	{ NULL, NULL }
837};
838const struct vnodeopv_desc efs_vnodeop_opv_desc = {
839	&efs_vnodeop_p,
840	efs_vnodeop_entries
841};
842
843int (**efs_specop_p)(void *);
844const struct vnodeopv_entry_desc efs_specop_entries[] = {
845	{ &vop_default_desc,	vn_default_error},	/* error handler */
846	GENFS_SPECOP_ENTRIES,
847	{ &vop_close_desc,	spec_close	},	/* close */
848	{ &vop_access_desc,	efs_access	},	/* access */
849	{ &vop_accessx_desc,	genfs_accessx	},	/* accessx */
850	{ &vop_getattr_desc,	efs_getattr	},	/* getattr */
851	{ &vop_setattr_desc,	genfs_eopnotsupp},	/* setattr */
852	{ &vop_read_desc,	spec_read	},	/* read */
853	{ &vop_write_desc,	spec_write	},	/* write */
854	{ &vop_fcntl_desc,	genfs_fcntl	},	/* fcntl */
855	{ &vop_fsync_desc,	spec_fsync	},	/* fsync */
856	{ &vop_inactive_desc,	efs_inactive	},	/* inactive */
857	{ &vop_reclaim_desc,	efs_reclaim	},	/* reclaim */
858	{ &vop_lock_desc,	genfs_lock,	},	/* lock */
859	{ &vop_unlock_desc,	genfs_unlock,	},	/* unlock */
860	{ &vop_islocked_desc,	genfs_islocked,	},	/* islocked */
861	{ &vop_print_desc,	efs_print	},	/* print */
862	{ &vop_bwrite_desc,	vn_bwrite	},	/* bwrite */
863	{ NULL, NULL }
864};
865const struct vnodeopv_desc efs_specop_opv_desc = {
866	&efs_specop_p,
867	efs_specop_entries
868};
869
870int (**efs_fifoop_p)(void *);
871const struct vnodeopv_entry_desc efs_fifoop_entries[] = {
872	{ &vop_default_desc,	vn_default_error},	/* error handler */
873	GENFS_FIFOOP_ENTRIES,
874	{ &vop_close_desc,	vn_fifo_bypass	},	/* close */
875	{ &vop_access_desc,	efs_access	},	/* access */
876	{ &vop_accessx_desc,	genfs_accessx	},	/* accessx */
877	{ &vop_getattr_desc,	efs_getattr	},	/* getattr */
878	{ &vop_setattr_desc,	genfs_eopnotsupp},	/* setattr */
879	{ &vop_read_desc,	vn_fifo_bypass	},	/* read */
880	{ &vop_write_desc,	vn_fifo_bypass	},	/* write */
881	{ &vop_fcntl_desc,	genfs_fcntl	},	/* fcntl */
882	{ &vop_fsync_desc,	vn_fifo_bypass	},	/* fsync */
883	{ &vop_inactive_desc,	efs_inactive	},	/* inactive */
884	{ &vop_reclaim_desc,	efs_reclaim	},	/* reclaim */
885	{ &vop_lock_desc,	genfs_lock,	},	/* lock */
886	{ &vop_unlock_desc,	genfs_unlock,	},	/* unlock */
887	{ &vop_islocked_desc,	genfs_islocked,	},	/* islocked */
888	{ &vop_strategy_desc,   vn_fifo_bypass  },	/* strategy */
889	{ &vop_print_desc,	efs_print	},	/* print */
890	{ &vop_bwrite_desc,	vn_bwrite	},	/* bwrite */
891	{ NULL, NULL }
892};
893const struct vnodeopv_desc efs_fifoop_opv_desc = {
894	&efs_fifoop_p,
895	efs_fifoop_entries
896};
897