fake_vop.c revision 49d8359737352b52625c23836d8a4be4ae8e55dd
1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13 * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
14 */
15
16#include <sys/types.h>
17#include <sys/param.h>
18#include <sys/systm.h>
19#include <sys/t_lock.h>
20#include <sys/errno.h>
21#include <sys/cred.h>
22#include <sys/user.h>
23#include <sys/uio.h>
24#include <sys/file.h>
25#include <sys/pathname.h>
26#include <sys/vfs.h>
27#include <sys/vnode.h>
28#include <sys/stat.h>
29#include <sys/mode.h>
30#include <sys/kmem.h>
31#include <sys/cmn_err.h>
32#include <sys/debug.h>
33#include <sys/atomic.h>
34#include <sys/acl.h>
35#include <sys/filio.h>
36#include <sys/flock.h>
37#include <sys/nbmlock.h>
38#include <sys/fcntl.h>
39#include <sys/poll.h>
40#include <sys/time.h>
41
42#include <errno.h>
43#include <fcntl.h>
44#include <unistd.h>
45
46#include "vncache.h"
47
48#define	O_RWMASK	(O_WRONLY | O_RDWR) /* == 3 */
49
50int fop_shrlock_enable = 0;
51
52int stat_to_vattr(const struct stat *, vattr_t *);
53int fop__getxvattr(vnode_t *, xvattr_t *);
54int fop__setxvattr(vnode_t *, xvattr_t *);
55
56static void fake_inactive_xattrdir(vnode_t *);
57
58/* ARGSUSED */
59int
60fop_open(
61	vnode_t **vpp,
62	int mode,
63	cred_t *cr,
64	caller_context_t *ct)
65{
66
67	if ((*vpp)->v_type == VREG) {
68		if (mode & FREAD)
69			atomic_add_32(&((*vpp)->v_rdcnt), 1);
70		if (mode & FWRITE)
71			atomic_add_32(&((*vpp)->v_wrcnt), 1);
72	}
73
74	/* call to ->vop_open was here */
75
76	return (0);
77}
78
79/* ARGSUSED */
80int
81fop_close(
82	vnode_t *vp,
83	int flag,
84	int count,
85	offset_t offset,
86	cred_t *cr,
87	caller_context_t *ct)
88{
89
90	/* call to ->vop_close was here */
91
92	/*
93	 * Check passed in count to handle possible dups. Vnode counts are only
94	 * kept on regular files
95	 */
96	if ((vp->v_type == VREG) && (count == 1))  {
97		if (flag & FREAD) {
98			ASSERT(vp->v_rdcnt > 0);
99			atomic_add_32(&(vp->v_rdcnt), -1);
100		}
101		if (flag & FWRITE) {
102			ASSERT(vp->v_wrcnt > 0);
103			atomic_add_32(&(vp->v_wrcnt), -1);
104		}
105	}
106	return (0);
107}
108
109/* ARGSUSED */
110int
111fop_read(
112	vnode_t *vp,
113	uio_t *uio,
114	int ioflag,
115	cred_t *cr,
116	caller_context_t *ct)
117{
118	struct stat st;
119	struct iovec *iov;
120	ssize_t resid;
121	size_t cnt;
122	int n;
123
124	/*
125	 * If that caller asks for read beyond end of file,
126	 * that causes the pread call to block.  (Ugh!)
127	 * Get the file size and return what we can.
128	 */
129	(void) fstat(vp->v_fd, &st);
130	resid = uio->uio_resid;
131	if ((uio->uio_loffset + resid) > st.st_size)
132		resid = st.st_size - uio->uio_loffset;
133
134	while (resid > 0) {
135
136		ASSERT(uio->uio_iovcnt > 0);
137		iov = uio->uio_iov;
138
139		if (iov->iov_len == 0) {
140			uio->uio_iov++;
141			uio->uio_iovcnt--;
142			continue;
143		}
144		cnt = iov->iov_len;
145		if (cnt > resid)
146			cnt = resid;
147
148		n = pread(vp->v_fd, iov->iov_base, cnt, uio->uio_loffset);
149		if (n < 0)
150			return (errno);
151
152		iov->iov_base += n;
153		iov->iov_len -= n;
154
155		uio->uio_resid -= n;
156		uio->uio_loffset += n;
157
158		resid -= n;
159	}
160
161	return (0);
162}
163
164/* ARGSUSED */
165int
166fop_write(
167	vnode_t *vp,
168	uio_t *uio,
169	int ioflag,
170	cred_t *cr,
171	caller_context_t *ct)
172{
173	struct iovec *iov;
174	size_t cnt;
175	int n;
176
177	while (uio->uio_resid > 0) {
178
179		ASSERT(uio->uio_iovcnt > 0);
180		iov = uio->uio_iov;
181
182		if (iov->iov_len == 0) {
183			uio->uio_iov++;
184			uio->uio_iovcnt--;
185			continue;
186		}
187		cnt = iov->iov_len;
188		if (cnt > uio->uio_resid)
189			cnt = uio->uio_resid;
190
191		n = pwrite(vp->v_fd, iov->iov_base, iov->iov_len,
192		    uio->uio_loffset);
193		if (n < 0)
194			return (errno);
195
196		iov->iov_base += n;
197		iov->iov_len -= n;
198
199		uio->uio_resid -= n;
200		uio->uio_loffset += n;
201	}
202
203	if (ioflag == FSYNC) {
204		(void) fsync(vp->v_fd);
205	}
206
207	return (0);
208}
209
210/* ARGSUSED */
211int
212fop_ioctl(
213	vnode_t *vp,
214	int cmd,
215	intptr_t arg,
216	int flag,
217	cred_t *cr,
218	int *rvalp,
219	caller_context_t *ct)
220{
221	return (ENOSYS);
222}
223
224/* ARGSUSED */
225int
226fop_setfl(
227	vnode_t *vp,
228	int oflags,
229	int nflags,
230	cred_t *cr,
231	caller_context_t *ct)
232{
233	/* allow any flags? See fs_setfl */
234	return (0);
235}
236
237/* ARGSUSED */
238int
239fop_getattr(
240	vnode_t *vp,
241	vattr_t *vap,
242	int flags,
243	cred_t *cr,
244	caller_context_t *ct)
245{
246	int error;
247	struct stat st;
248
249	if (fstat(vp->v_fd, &st) == -1)
250		return (errno);
251	error = stat_to_vattr(&st, vap);
252
253	if (vap->va_mask & AT_XVATTR)
254		(void) fop__getxvattr(vp, (xvattr_t *)vap);
255
256	return (error);
257}
258
259/* ARGSUSED */
260int
261fop_setattr(
262	vnode_t *vp,
263	vattr_t *vap,
264	int flags,
265	cred_t *cr,
266	caller_context_t *ct)
267{
268	timespec_t times[2];
269
270	if (vap->va_mask & AT_SIZE) {
271		if (ftruncate(vp->v_fd, vap->va_size) == -1)
272			return (errno);
273	}
274
275	/* AT_MODE or anything else? */
276
277	if (vap->va_mask & AT_XVATTR)
278		(void) fop__setxvattr(vp, (xvattr_t *)vap);
279
280	if (vap->va_mask & (AT_ATIME | AT_MTIME)) {
281		if (vap->va_mask & AT_ATIME) {
282			times[0] = vap->va_atime;
283		} else {
284			times[0].tv_sec = 0;
285			times[0].tv_nsec = UTIME_OMIT;
286		}
287		if (vap->va_mask & AT_MTIME) {
288			times[1] = vap->va_mtime;
289		} else {
290			times[1].tv_sec = 0;
291			times[1].tv_nsec = UTIME_OMIT;
292		}
293
294		(void) futimens(vp->v_fd, times);
295	}
296
297	return (0);
298}
299
300/* ARGSUSED */
301int
302fop_access(
303	vnode_t *vp,
304	int mode,
305	int flags,
306	cred_t *cr,
307	caller_context_t *ct)
308{
309	return (0);
310}
311
312/*
313 * Conceptually like xattr_dir_lookup()
314 */
315static int
316fake_lookup_xattrdir(
317	vnode_t *dvp,
318	vnode_t **vpp)
319{
320	int len, fd;
321	int omode = O_RDWR | O_NOFOLLOW;
322	vnode_t *vp;
323
324	*vpp = NULL;
325
326	if (dvp->v_type != VDIR && dvp->v_type != VREG)
327		return (EINVAL);
328
329	/*
330	 * If we're already in sysattr space, don't allow creation
331	 * of another level of sysattrs.
332	 */
333	if (dvp->v_flag & V_SYSATTR)
334		return (EINVAL);
335
336	mutex_enter(&dvp->v_lock);
337	if (dvp->v_xattrdir != NULL) {
338		*vpp = dvp->v_xattrdir;
339		VN_HOLD(*vpp);
340		mutex_exit(&dvp->v_lock);
341		return (0);
342	}
343	mutex_exit(&dvp->v_lock);
344
345	omode = O_RDONLY|O_XATTR;
346	fd = openat(dvp->v_fd, ".", omode);
347	if (fd < 0)
348		return (errno);
349
350	vp = vn_alloc(KM_SLEEP);
351	vp->v_fd = fd;
352	vp->v_flag = V_XATTRDIR|V_SYSATTR;
353	vp->v_type = VDIR;
354	vp->v_vfsp = dvp->v_vfsp;
355
356	/* Set v_path to parent path + "/@" (like NFS) */
357	len = strlen(dvp->v_path) + 3;
358	vp->v_path = kmem_alloc(len, KM_SLEEP);
359	(void) snprintf(vp->v_path, len, "%s/@", dvp->v_path);
360
361	/*
362	 * Keep a pointer to the parent and a hold on it.
363	 * Both are cleaned up in fake_inactive_xattrdir
364	 */
365	vp->v_data = dvp;
366	vn_hold(dvp);
367
368	mutex_enter(&dvp->v_lock);
369	if (dvp->v_xattrdir == NULL) {
370		*vpp = dvp->v_xattrdir = vp;
371		mutex_exit(&dvp->v_lock);
372	} else {
373		*vpp = dvp->v_xattrdir;
374		mutex_exit(&dvp->v_lock);
375		fake_inactive_xattrdir(vp);
376	}
377
378	return (0);
379}
380
381/* ARGSUSED */
382int
383fop_lookup(
384	vnode_t *dvp,
385	char *name,
386	vnode_t **vpp,
387	pathname_t *pnp,
388	int flags,
389	vnode_t *rdir,
390	cred_t *cr,
391	caller_context_t *ct,
392	int *deflags,		/* Returned per-dirent flags */
393	pathname_t *ppnp)	/* Returned case-preserved name in directory */
394{
395	int fd;
396	int omode = O_RDWR | O_NOFOLLOW;
397	vnode_t *vp;
398	struct stat st;
399
400	if (flags & LOOKUP_XATTR)
401		return (fake_lookup_xattrdir(dvp, vpp));
402
403	/*
404	 * If lookup is for "", just return dvp.
405	 */
406	if (name[0] == '\0') {
407		vn_hold(dvp);
408		*vpp = dvp;
409		return (0);
410	}
411
412	if (fstatat(dvp->v_fd, name, &st, AT_SYMLINK_NOFOLLOW) == -1)
413		return (errno);
414
415	vp = vncache_lookup(&st);
416	if (vp != NULL) {
417		/* lookup gave us a hold */
418		*vpp = vp;
419		return (0);
420	}
421
422	if (S_ISDIR(st.st_mode))
423		omode = O_RDONLY | O_NOFOLLOW;
424
425again:
426	fd = openat(dvp->v_fd, name, omode, 0);
427	if (fd < 0) {
428		if ((omode & O_RWMASK) == O_RDWR) {
429			omode &= ~O_RWMASK;
430			omode |= O_RDONLY;
431			goto again;
432		}
433		return (errno);
434	}
435
436	if (fstat(fd, &st) == -1) {
437		(void) close(fd);
438		return (errno);
439	}
440
441	vp = vncache_enter(&st, dvp, name, fd);
442
443	*vpp = vp;
444	return (0);
445}
446
447/* ARGSUSED */
448int
449fop_create(
450	vnode_t *dvp,
451	char *name,
452	vattr_t *vap,
453	vcexcl_t excl,
454	int mode,
455	vnode_t **vpp,
456	cred_t *cr,
457	int flags,
458	caller_context_t *ct,
459	vsecattr_t *vsecp)	/* ACL to set during create */
460{
461	struct stat st;
462	vnode_t *vp;
463	int err, fd, omode;
464
465	/*
466	 * If creating "", just return dvp.
467	 */
468	if (name[0] == '\0') {
469		vn_hold(dvp);
470		*vpp = dvp;
471		return (0);
472	}
473
474	err = fstatat(dvp->v_fd, name, &st, AT_SYMLINK_NOFOLLOW);
475	if (err != 0)
476		err = errno;
477
478	vp = NULL;
479	if (err == 0) {
480		/* The file already exists. */
481		if (excl == EXCL)
482			return (EEXIST);
483
484		vp = vncache_lookup(&st);
485		/* vp gained a hold */
486	}
487
488	if (vp == NULL) {
489		/*
490		 * Open it. (may or may not exist)
491		 */
492		omode = O_RDWR | O_CREAT | O_NOFOLLOW;
493		if (excl == EXCL)
494			omode |= O_EXCL;
495	open_again:
496		fd = openat(dvp->v_fd, name, omode, mode);
497		if (fd < 0) {
498			if ((omode & O_RWMASK) == O_RDWR) {
499				omode &= ~O_RWMASK;
500				omode |= O_RDONLY;
501				goto open_again;
502			}
503			return (errno);
504		}
505		(void) fstat(fd, &st);
506
507		vp = vncache_enter(&st, dvp, name, fd);
508		/* vp has its initial hold */
509	}
510
511	/* Should have the vp now. */
512	if (vp == NULL)
513		return (EFAULT);
514
515	if (vp->v_type == VDIR && vap->va_type != VDIR) {
516		vn_rele(vp);
517		return (EISDIR);
518	}
519	if (vp->v_type != VDIR && vap->va_type == VDIR) {
520		vn_rele(vp);
521		return (ENOTDIR);
522	}
523
524	/*
525	 * Might need to set attributes.
526	 */
527	(void) fop_setattr(vp, vap, 0, cr, ct);
528
529	*vpp = vp;
530	return (0);
531}
532
533/* ARGSUSED */
534int
535fop_remove(
536	vnode_t *dvp,
537	char *name,
538	cred_t *cr,
539	caller_context_t *ct,
540	int flags)
541{
542
543	if (unlinkat(dvp->v_fd, name, 0))
544		return (errno);
545
546	return (0);
547}
548
549/* ARGSUSED */
550int
551fop_link(
552	vnode_t *to_dvp,
553	vnode_t *fr_vp,
554	char *to_name,
555	cred_t *cr,
556	caller_context_t *ct,
557	int flags)
558{
559	int err;
560
561	/*
562	 * Would prefer to specify "from" as the combination:
563	 * (fr_vp->v_fd, NULL) but linkat does not permit it.
564	 */
565	err = linkat(AT_FDCWD, fr_vp->v_path, to_dvp->v_fd, to_name,
566	    AT_SYMLINK_FOLLOW);
567	if (err == -1)
568		err = errno;
569
570	return (err);
571}
572
573/* ARGSUSED */
574int
575fop_rename(
576	vnode_t *from_dvp,
577	char *from_name,
578	vnode_t *to_dvp,
579	char *to_name,
580	cred_t *cr,
581	caller_context_t *ct,
582	int flags)
583{
584	struct stat st;
585	vnode_t *vp;
586	int err;
587
588	if (fstatat(from_dvp->v_fd, from_name, &st,
589	    AT_SYMLINK_NOFOLLOW) == -1)
590		return (errno);
591
592	vp = vncache_lookup(&st);
593	if (vp == NULL)
594		return (ENOENT);
595
596	err = renameat(from_dvp->v_fd, from_name, to_dvp->v_fd, to_name);
597	if (err == -1)
598		err = errno;
599	else
600		vncache_renamed(vp, to_dvp, to_name);
601
602	vn_rele(vp);
603
604	return (err);
605}
606
607/* ARGSUSED */
608int
609fop_mkdir(
610	vnode_t *dvp,
611	char *name,
612	vattr_t *vap,
613	vnode_t **vpp,
614	cred_t *cr,
615	caller_context_t *ct,
616	int flags,
617	vsecattr_t *vsecp)	/* ACL to set during create */
618{
619	struct stat st;
620	int err, fd;
621
622	mode_t mode = vap->va_mode & 0777;
623
624	if (mkdirat(dvp->v_fd, name, mode) == -1)
625		return (errno);
626
627	if ((fd = openat(dvp->v_fd, name, O_RDONLY)) == -1)
628		return (errno);
629	if (fstat(fd, &st) == -1) {
630		err = errno;
631		(void) close(fd);
632		return (err);
633	}
634
635	*vpp = vncache_enter(&st, dvp, name, fd);
636
637	/*
638	 * Might need to set attributes.
639	 */
640	(void) fop_setattr(*vpp, vap, 0, cr, ct);
641
642	return (0);
643}
644
645/* ARGSUSED */
646int
647fop_rmdir(
648	vnode_t *dvp,
649	char *name,
650	vnode_t *cdir,
651	cred_t *cr,
652	caller_context_t *ct,
653	int flags)
654{
655
656	if (unlinkat(dvp->v_fd, name, AT_REMOVEDIR) == -1)
657		return (errno);
658
659	return (0);
660}
661
662/* ARGSUSED */
663int
664fop_readdir(
665	vnode_t *vp,
666	uio_t *uiop,
667	cred_t *cr,
668	int *eofp,
669	caller_context_t *ct,
670	int flags)
671{
672	struct iovec *iov;
673	int cnt;
674	int error = 0;
675	int fd = vp->v_fd;
676
677	if (eofp) {
678		*eofp = 0;
679	}
680
681	error = lseek(fd, uiop->uio_loffset, SEEK_SET);
682	if (error == -1)
683		return (errno);
684
685	ASSERT(uiop->uio_iovcnt > 0);
686	iov = uiop->uio_iov;
687	if (iov->iov_len < sizeof (struct dirent))
688		return (EINVAL);
689
690	/* LINTED E_BAD_PTR_CAST_ALIGN */
691	cnt = getdents(fd, (struct dirent *)(uiop->uio_iov->iov_base),
692	    uiop->uio_resid);
693	if (cnt == -1)
694		return (errno);
695	if (cnt == 0) {
696		if (eofp) {
697			*eofp = 1;
698		}
699		return (ENOENT);
700	}
701
702	iov->iov_base += cnt;
703	iov->iov_len  -= cnt;
704	uiop->uio_resid -= cnt;
705	uiop->uio_loffset = lseek(fd, 0LL, SEEK_CUR);
706
707	return (0);
708}
709
710/* ARGSUSED */
711int
712fop_symlink(
713	vnode_t *dvp,
714	char *linkname,
715	vattr_t *vap,
716	char *target,
717	cred_t *cr,
718	caller_context_t *ct,
719	int flags)
720{
721	return (ENOSYS);
722}
723
724/* ARGSUSED */
725int
726fop_readlink(
727	vnode_t *vp,
728	uio_t *uiop,
729	cred_t *cr,
730	caller_context_t *ct)
731{
732	return (ENOSYS);
733}
734
735/* ARGSUSED */
736int
737fop_fsync(
738	vnode_t *vp,
739	int syncflag,
740	cred_t *cr,
741	caller_context_t *ct)
742{
743
744	if (fsync(vp->v_fd) == -1)
745		return (errno);
746
747	return (0);
748}
749
750/* ARGSUSED */
751void
752fop_inactive(
753	vnode_t *vp,
754	cred_t *cr,
755	caller_context_t *ct)
756{
757	if (vp->v_flag & V_XATTRDIR) {
758		fake_inactive_xattrdir(vp);
759	} else {
760		vncache_inactive(vp);
761	}
762}
763
764/*
765 * The special xattr directories are not in the vncache AVL, but
766 * hang off the parent's v_xattrdir field.  When vn_rele finds
767 * an xattr dir at v_count == 1 it calls here, but until we
768 * take locks on both the parent and the xattrdir, we don't
769 * know if we're really at the last reference.  So in here we
770 * take both locks, re-check the count, and either bail out
771 * or proceed with "inactive" vnode cleanup.  Part of that
772 * cleanup includes releasing the hold on the parent and
773 * clearing the parent's v_xattrdir field, which were
774 * setup in fake_lookup_xattrdir()
775 */
776static void
777fake_inactive_xattrdir(vnode_t *vp)
778{
779	vnode_t *dvp = vp->v_data; /* parent */
780	mutex_enter(&dvp->v_lock);
781	mutex_enter(&vp->v_lock);
782	if (vp->v_count > 1) {
783		/* new ref. via v_xattrdir */
784		mutex_exit(&vp->v_lock);
785		mutex_exit(&dvp->v_lock);
786		return;
787	}
788	ASSERT(dvp->v_xattrdir == vp);
789	dvp->v_xattrdir = NULL;
790	mutex_exit(&vp->v_lock);
791	mutex_exit(&dvp->v_lock);
792	vn_rele(dvp);
793	vn_free(vp);
794}
795
796/* ARGSUSED */
797int
798fop_fid(
799	vnode_t *vp,
800	fid_t *fidp,
801	caller_context_t *ct)
802{
803	return (ENOSYS);
804}
805
806/* ARGSUSED */
807int
808fop_rwlock(
809	vnode_t *vp,
810	int write_lock,
811	caller_context_t *ct)
812{
813	/* See: fs_rwlock */
814	return (-1);
815}
816
817/* ARGSUSED */
818void
819fop_rwunlock(
820	vnode_t *vp,
821	int write_lock,
822	caller_context_t *ct)
823{
824	/* See: fs_rwunlock */
825}
826
827/* ARGSUSED */
828int
829fop_seek(
830	vnode_t *vp,
831	offset_t ooff,
832	offset_t *noffp,
833	caller_context_t *ct)
834{
835	return (ENOSYS);
836}
837
838/* ARGSUSED */
839int
840fop_cmp(
841	vnode_t *vp1,
842	vnode_t *vp2,
843	caller_context_t *ct)
844{
845	/* See fs_cmp */
846	return (vncache_cmp(vp1, vp2));
847}
848
849/* ARGSUSED */
850int
851fop_frlock(
852	vnode_t *vp,
853	int cmd,
854	flock64_t *bfp,
855	int flag,
856	offset_t offset,
857	struct flk_callback *flk_cbp,
858	cred_t *cr,
859	caller_context_t *ct)
860{
861#if defined(_LP64)
862	offset_t maxoffset = INT64_MAX;
863#elif defined(_ILP32)
864	/*
865	 * Sadly, the fcntl API enforces 32-bit offsets,
866	 * even though we have _FILE_OFFSET_BITS=64
867	 */
868	offset_t maxoffset = INT32_MAX;
869#else
870#error "unsupported env."
871#endif
872
873	/* See fs_frlock */
874
875	switch (cmd) {
876	case F_GETLK:
877	case F_SETLK_NBMAND:
878	case F_SETLK:
879	case F_SETLKW:
880		break;
881	default:
882		return (EINVAL);
883	}
884
885	/* We only get SEEK_SET ranges here. */
886	if (bfp->l_whence != 0)
887		return (EINVAL);
888
889	/*
890	 * One limitation of using fcntl(2) F_SETLK etc is that
891	 * the real kernel limits the offsets we can use.
892	 * (Maybe the fcntl API should loosen that up?)
893	 * See syscall/fcntl.c:flock_check()
894	 *
895	 * Here in libfksmbsrv we can just ignore such locks,
896	 * or ignore the part that extends beyond maxoffset.
897	 * The SMB layer still keeps track of such locks for
898	 * conflict detection, so not reflecting such locks
899	 * into the real FS layer is OK.  Note: this may
900	 * modify the pased bfp->l_len.
901	 */
902	if (bfp->l_start < 0 || bfp->l_start > maxoffset)
903		return (0);
904	if (bfp->l_len < 0 || bfp->l_len > maxoffset)
905		return (0);
906	if (bfp->l_len > (maxoffset - bfp->l_start + 1))
907		bfp->l_len = (maxoffset - bfp->l_start + 1);
908
909	if (fcntl(vp->v_fd, cmd, bfp) == -1)
910		return (errno);
911
912	return (0);
913}
914
915/* ARGSUSED */
916int
917fop_space(
918	vnode_t *vp,
919	int cmd,
920	flock64_t *bfp,
921	int flag,
922	offset_t offset,
923	cred_t *cr,
924	caller_context_t *ct)
925{
926	/* See fs_frlock */
927
928	switch (cmd) {
929	case F_ALLOCSP:
930	case F_FREESP:
931		break;
932	default:
933		return (EINVAL);
934	}
935
936	if (fcntl(vp->v_fd, cmd, bfp) == -1)
937		return (errno);
938
939	return (0);
940}
941
942/* ARGSUSED */
943int
944fop_realvp(
945	vnode_t *vp,
946	vnode_t **vpp,
947	caller_context_t *ct)
948{
949	return (ENOSYS);
950}
951
952/* ARGSUSED */
953int
954fop_getpage(
955	vnode_t *vp,
956	offset_t off,
957	size_t len,
958	uint_t *protp,
959	struct page **plarr,
960	size_t plsz,
961	struct seg *seg,
962	caddr_t addr,
963	enum seg_rw rw,
964	cred_t *cr,
965	caller_context_t *ct)
966{
967	return (ENOSYS);
968}
969
970/* ARGSUSED */
971int
972fop_putpage(
973	vnode_t *vp,
974	offset_t off,
975	size_t len,
976	int flags,
977	cred_t *cr,
978	caller_context_t *ct)
979{
980	return (ENOSYS);
981}
982
983/* ARGSUSED */
984int
985fop_map(
986	vnode_t *vp,
987	offset_t off,
988	struct as *as,
989	caddr_t *addrp,
990	size_t len,
991	uchar_t prot,
992	uchar_t maxprot,
993	uint_t flags,
994	cred_t *cr,
995	caller_context_t *ct)
996{
997	return (ENOSYS);
998}
999
1000/* ARGSUSED */
1001int
1002fop_addmap(
1003	vnode_t *vp,
1004	offset_t off,
1005	struct as *as,
1006	caddr_t addr,
1007	size_t len,
1008	uchar_t prot,
1009	uchar_t maxprot,
1010	uint_t flags,
1011	cred_t *cr,
1012	caller_context_t *ct)
1013{
1014	return (ENOSYS);
1015}
1016
1017/* ARGSUSED */
1018int
1019fop_delmap(
1020	vnode_t *vp,
1021	offset_t off,
1022	struct as *as,
1023	caddr_t addr,
1024	size_t len,
1025	uint_t prot,
1026	uint_t maxprot,
1027	uint_t flags,
1028	cred_t *cr,
1029	caller_context_t *ct)
1030{
1031	return (ENOSYS);
1032}
1033
1034/* ARGSUSED */
1035int
1036fop_poll(
1037	vnode_t *vp,
1038	short events,
1039	int anyyet,
1040	short *reventsp,
1041	struct pollhead **phpp,
1042	caller_context_t *ct)
1043{
1044	*reventsp = 0;
1045	if (events & POLLIN)
1046		*reventsp |= POLLIN;
1047	if (events & POLLRDNORM)
1048		*reventsp |= POLLRDNORM;
1049	if (events & POLLRDBAND)
1050		*reventsp |= POLLRDBAND;
1051	if (events & POLLOUT)
1052		*reventsp |= POLLOUT;
1053	if (events & POLLWRBAND)
1054		*reventsp |= POLLWRBAND;
1055	*phpp = NULL; /* or fake_pollhead? */
1056
1057	return (0);
1058}
1059
1060/* ARGSUSED */
1061int
1062fop_dump(
1063	vnode_t *vp,
1064	caddr_t addr,
1065	offset_t lbdn,
1066	offset_t dblks,
1067	caller_context_t *ct)
1068{
1069	return (ENOSYS);
1070}
1071
1072/*
1073 * See fs_pathconf
1074 */
1075/* ARGSUSED */
1076int
1077fop_pathconf(
1078	vnode_t *vp,
1079	int cmd,
1080	ulong_t *valp,
1081	cred_t *cr,
1082	caller_context_t *ct)
1083{
1084	register ulong_t val;
1085	register int error = 0;
1086
1087	switch (cmd) {
1088
1089	case _PC_LINK_MAX:
1090		val = MAXLINK;
1091		break;
1092
1093	case _PC_MAX_CANON:
1094		val = MAX_CANON;
1095		break;
1096
1097	case _PC_MAX_INPUT:
1098		val = MAX_INPUT;
1099		break;
1100
1101	case _PC_NAME_MAX:
1102		val = MAXNAMELEN;
1103		break;
1104
1105	case _PC_PATH_MAX:
1106	case _PC_SYMLINK_MAX:
1107		val = MAXPATHLEN;
1108		break;
1109
1110	case _PC_PIPE_BUF:
1111		val = PIPE_BUF;
1112		break;
1113
1114	case _PC_NO_TRUNC:
1115		val = (ulong_t)-1;
1116		break;
1117
1118	case _PC_VDISABLE:
1119		val = _POSIX_VDISABLE;
1120		break;
1121
1122	case _PC_CHOWN_RESTRICTED:
1123		val = 1; /* chown restricted enabled */
1124		break;
1125
1126	case _PC_FILESIZEBITS:
1127		val = (ulong_t)-1;    /* large file support */
1128		break;
1129
1130	case _PC_ACL_ENABLED:
1131		val = _ACL_ACE_ENABLED;
1132		break;
1133
1134	case _PC_CASE_BEHAVIOR:
1135		val = _CASE_SENSITIVE;
1136		break;
1137
1138	case _PC_SATTR_ENABLED:
1139	case _PC_SATTR_EXISTS:
1140		val = 0;
1141		break;
1142
1143	case _PC_ACCESS_FILTERING:
1144		val = 0;
1145		break;
1146
1147	default:
1148		error = EINVAL;
1149		break;
1150	}
1151
1152	if (error == 0)
1153		*valp = val;
1154	return (error);
1155}
1156
1157/* ARGSUSED */
1158int
1159fop_pageio(
1160	vnode_t *vp,
1161	struct page *pp,
1162	u_offset_t io_off,
1163	size_t io_len,
1164	int flags,
1165	cred_t *cr,
1166	caller_context_t *ct)
1167{
1168	return (ENOSYS);
1169}
1170
1171/* ARGSUSED */
1172int
1173fop_dumpctl(
1174	vnode_t *vp,
1175	int action,
1176	offset_t *blkp,
1177	caller_context_t *ct)
1178{
1179	return (ENOSYS);
1180}
1181
1182/* ARGSUSED */
1183void
1184fop_dispose(
1185	vnode_t *vp,
1186	struct page *pp,
1187	int flag,
1188	int dn,
1189	cred_t *cr,
1190	caller_context_t *ct)
1191{
1192}
1193
1194/* ARGSUSED */
1195int
1196fop_setsecattr(
1197	vnode_t *vp,
1198	vsecattr_t *vsap,
1199	int flag,
1200	cred_t *cr,
1201	caller_context_t *ct)
1202{
1203	return (0);
1204}
1205
1206/*
1207 * Fake up just enough of this so we can test get/set SDs.
1208 */
1209/* ARGSUSED */
1210int
1211fop_getsecattr(
1212	vnode_t *vp,
1213	vsecattr_t *vsecattr,
1214	int flag,
1215	cred_t *cr,
1216	caller_context_t *ct)
1217{
1218
1219	vsecattr->vsa_aclcnt	= 0;
1220	vsecattr->vsa_aclentsz	= 0;
1221	vsecattr->vsa_aclentp	= NULL;
1222	vsecattr->vsa_dfaclcnt	= 0;	/* Default ACLs are not fabricated */
1223	vsecattr->vsa_dfaclentp	= NULL;
1224
1225	if (vsecattr->vsa_mask & (VSA_ACLCNT | VSA_ACL)) {
1226		aclent_t *aclentp;
1227		size_t aclsize;
1228
1229		aclsize = sizeof (aclent_t);
1230		vsecattr->vsa_aclcnt = 1;
1231		vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP);
1232		aclentp = vsecattr->vsa_aclentp;
1233
1234		aclentp->a_type = OTHER_OBJ;
1235		aclentp->a_perm = 0777;
1236		aclentp->a_id = (gid_t)-1;
1237		aclentp++;
1238	} else if (vsecattr->vsa_mask & (VSA_ACECNT | VSA_ACE)) {
1239		ace_t *acl;
1240
1241		acl = kmem_alloc(sizeof (ace_t), KM_SLEEP);
1242		acl->a_who = (uint32_t)-1;
1243		acl->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
1244		acl->a_flags = ACE_EVERYONE;
1245		acl->a_access_mask  = ACE_MODIFY_PERMS;
1246
1247		vsecattr->vsa_aclentp = (void *)acl;
1248		vsecattr->vsa_aclcnt = 1;
1249		vsecattr->vsa_aclentsz = sizeof (ace_t);
1250	}
1251
1252	return (0);
1253}
1254
1255/* ARGSUSED */
1256int
1257fop_shrlock(
1258	vnode_t *vp,
1259	int cmd,
1260	struct shrlock *shr,
1261	int flag,
1262	cred_t *cr,
1263	caller_context_t *ct)
1264{
1265
1266	switch (cmd) {
1267	case F_SHARE:
1268	case F_SHARE_NBMAND:
1269	case F_UNSHARE:
1270		break;
1271	default:
1272		return (EINVAL);
1273	}
1274
1275	if (!fop_shrlock_enable)
1276		return (0);
1277
1278	if (fcntl(vp->v_fd, cmd, shr) == -1)
1279		return (errno);
1280
1281	return (0);
1282}
1283
1284/* ARGSUSED */
1285int
1286fop_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *fnm,
1287    caller_context_t *ct)
1288{
1289	return (ENOSYS);
1290}
1291
1292/* ARGSUSED */
1293int
1294fop_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *uiop, cred_t *cr,
1295    caller_context_t *ct)
1296{
1297	return (ENOSYS);
1298}
1299
1300/* ARGSUSED */
1301int
1302fop_retzcbuf(vnode_t *vp, xuio_t *uiop, cred_t *cr, caller_context_t *ct)
1303{
1304	return (ENOSYS);
1305}
1306
1307
1308/*
1309 * ***************************************************************
1310 * other VOP support
1311 */
1312
1313/*
1314 * Convert stat(2) formats to vnode types and vice versa.  (Knows about
1315 * numerical order of S_IFMT and vnode types.)
1316 */
1317enum vtype iftovt_tab[] = {
1318	VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
1319	VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VNON
1320};
1321
1322ushort_t vttoif_tab[] = {
1323	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFIFO,
1324	S_IFDOOR, 0, S_IFSOCK, S_IFPORT, 0
1325};
1326
1327/*
1328 * stat_to_vattr()
1329 *
1330 * Convert from a stat structure to an vattr structure
1331 * Note: only set fields according to va_mask
1332 */
1333
1334int
1335stat_to_vattr(const struct stat *st, vattr_t *vap)
1336{
1337
1338	if (vap->va_mask & AT_TYPE)
1339		vap->va_type = IFTOVT(st->st_mode);
1340
1341	if (vap->va_mask & AT_MODE)
1342		vap->va_mode = st->st_mode;
1343
1344	if (vap->va_mask & AT_UID)
1345		vap->va_uid = st->st_uid;
1346
1347	if (vap->va_mask & AT_GID)
1348		vap->va_gid = st->st_gid;
1349
1350	if (vap->va_mask & AT_FSID)
1351		vap->va_fsid = st->st_dev;
1352
1353	if (vap->va_mask & AT_NODEID)
1354		vap->va_nodeid = st->st_ino;
1355
1356	if (vap->va_mask & AT_NLINK)
1357		vap->va_nlink = st->st_nlink;
1358
1359	if (vap->va_mask & AT_SIZE)
1360		vap->va_size = (u_offset_t)st->st_size;
1361
1362	if (vap->va_mask & AT_ATIME) {
1363		vap->va_atime.tv_sec  = st->st_atim.tv_sec;
1364		vap->va_atime.tv_nsec = st->st_atim.tv_nsec;
1365	}
1366
1367	if (vap->va_mask & AT_MTIME) {
1368		vap->va_mtime.tv_sec  = st->st_mtim.tv_sec;
1369		vap->va_mtime.tv_nsec = st->st_mtim.tv_nsec;
1370	}
1371
1372	if (vap->va_mask & AT_CTIME) {
1373		vap->va_ctime.tv_sec  = st->st_ctim.tv_sec;
1374		vap->va_ctime.tv_nsec = st->st_ctim.tv_nsec;
1375	}
1376
1377	if (vap->va_mask & AT_RDEV)
1378		vap->va_rdev = st->st_rdev;
1379
1380	if (vap->va_mask & AT_BLKSIZE)
1381		vap->va_blksize = (uint_t)st->st_blksize;
1382
1383
1384	if (vap->va_mask & AT_NBLOCKS)
1385		vap->va_nblocks = (u_longlong_t)st->st_blocks;
1386
1387	if (vap->va_mask & AT_SEQ)
1388		vap->va_seq = 0;
1389
1390	return (0);
1391}
1392
1393/* ARGSUSED */
1394void
1395flk_init_callback(flk_callback_t *flk_cb,
1396	callb_cpr_t *(*cb_fcn)(flk_cb_when_t, void *), void *cbdata)
1397{
1398}
1399
1400void
1401vn_hold(vnode_t *vp)
1402{
1403	mutex_enter(&vp->v_lock);
1404	vp->v_count++;
1405	mutex_exit(&vp->v_lock);
1406}
1407
1408void
1409vn_rele(vnode_t *vp)
1410{
1411	VERIFY3U(vp->v_count, !=, 0);
1412	mutex_enter(&vp->v_lock);
1413	if (vp->v_count == 1) {
1414		mutex_exit(&vp->v_lock);
1415		fop_inactive(vp, NULL, NULL);
1416	} else {
1417		vp->v_count--;
1418		mutex_exit(&vp->v_lock);
1419	}
1420}
1421
1422int
1423vn_has_other_opens(
1424	vnode_t *vp,
1425	v_mode_t mode)
1426{
1427
1428	switch (mode) {
1429	case V_WRITE:
1430		if (vp->v_wrcnt > 1)
1431			return (V_TRUE);
1432		break;
1433	case V_RDORWR:
1434		if ((vp->v_rdcnt > 1) || (vp->v_wrcnt > 1))
1435			return (V_TRUE);
1436		break;
1437	case V_RDANDWR:
1438		if ((vp->v_rdcnt > 1) && (vp->v_wrcnt > 1))
1439			return (V_TRUE);
1440		break;
1441	case V_READ:
1442		if (vp->v_rdcnt > 1)
1443			return (V_TRUE);
1444		break;
1445	}
1446
1447	return (V_FALSE);
1448}
1449
1450/*
1451 * vn_is_opened() checks whether a particular file is opened and
1452 * whether the open is for read and/or write.
1453 *
1454 * Vnode counts are only kept on regular files (v_type=VREG).
1455 */
1456int
1457vn_is_opened(
1458	vnode_t *vp,
1459	v_mode_t mode)
1460{
1461
1462	ASSERT(vp != NULL);
1463
1464	switch (mode) {
1465	case V_WRITE:
1466		if (vp->v_wrcnt)
1467			return (V_TRUE);
1468		break;
1469	case V_RDANDWR:
1470		if (vp->v_rdcnt && vp->v_wrcnt)
1471			return (V_TRUE);
1472		break;
1473	case V_RDORWR:
1474		if (vp->v_rdcnt || vp->v_wrcnt)
1475			return (V_TRUE);
1476		break;
1477	case V_READ:
1478		if (vp->v_rdcnt)
1479			return (V_TRUE);
1480		break;
1481	}
1482
1483	return (V_FALSE);
1484}
1485
1486/*
1487 * vn_is_mapped() checks whether a particular file is mapped and whether
1488 * the file is mapped read and/or write.
1489 */
1490/* ARGSUSED */
1491int
1492vn_is_mapped(
1493	vnode_t *vp,
1494	v_mode_t mode)
1495{
1496	return (V_FALSE);
1497}
1498