xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_vnops.c (revision 2972:10096047b1c0)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52720Sfrankho  * Common Development and Distribution License (the "License").
62720Sfrankho  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
222720Sfrankho  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/param.h>
290Sstevel@tonic-gate #include <sys/t_lock.h>
300Sstevel@tonic-gate #include <sys/systm.h>
310Sstevel@tonic-gate #include <sys/sysmacros.h>
320Sstevel@tonic-gate #include <sys/user.h>
330Sstevel@tonic-gate #include <sys/buf.h>
340Sstevel@tonic-gate #include <sys/stat.h>
350Sstevel@tonic-gate #include <sys/vfs.h>
360Sstevel@tonic-gate #include <sys/dirent.h>
370Sstevel@tonic-gate #include <sys/vnode.h>
380Sstevel@tonic-gate #include <sys/proc.h>
390Sstevel@tonic-gate #include <sys/file.h>
400Sstevel@tonic-gate #include <sys/fcntl.h>
410Sstevel@tonic-gate #include <sys/uio.h>
420Sstevel@tonic-gate #include <sys/fs/pc_label.h>
430Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
440Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
450Sstevel@tonic-gate #include <sys/fs/pc_node.h>
460Sstevel@tonic-gate #include <sys/mman.h>
470Sstevel@tonic-gate #include <sys/pathname.h>
480Sstevel@tonic-gate #include <sys/vmsystm.h>
490Sstevel@tonic-gate #include <sys/cmn_err.h>
500Sstevel@tonic-gate #include <sys/debug.h>
510Sstevel@tonic-gate #include <sys/statvfs.h>
520Sstevel@tonic-gate #include <sys/unistd.h>
530Sstevel@tonic-gate #include <sys/kmem.h>
540Sstevel@tonic-gate #include <sys/conf.h>
550Sstevel@tonic-gate #include <sys/flock.h>
560Sstevel@tonic-gate #include <sys/policy.h>
572720Sfrankho #include <sys/sdt.h>
580Sstevel@tonic-gate 
590Sstevel@tonic-gate #include <vm/seg.h>
600Sstevel@tonic-gate #include <vm/page.h>
610Sstevel@tonic-gate #include <vm/pvn.h>
620Sstevel@tonic-gate #include <vm/seg_map.h>
630Sstevel@tonic-gate #include <vm/seg_vn.h>
640Sstevel@tonic-gate #include <vm/hat.h>
650Sstevel@tonic-gate #include <vm/as.h>
660Sstevel@tonic-gate #include <vm/seg_kmem.h>
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #include <fs/fs_subr.h>
690Sstevel@tonic-gate 
700Sstevel@tonic-gate static int pcfs_open(struct vnode **, int, struct cred *);
710Sstevel@tonic-gate static int pcfs_close(struct vnode *, int, int, offset_t, struct cred *);
720Sstevel@tonic-gate static int pcfs_read(struct vnode *, struct uio *, int, struct cred *,
730Sstevel@tonic-gate 	struct caller_context *);
740Sstevel@tonic-gate static int pcfs_write(struct vnode *, struct uio *, int, struct cred *,
750Sstevel@tonic-gate 	struct caller_context *);
760Sstevel@tonic-gate static int pcfs_getattr(struct vnode *, struct vattr *, int, struct cred *);
770Sstevel@tonic-gate static int pcfs_setattr(struct vnode *, struct vattr *, int, struct cred *,
780Sstevel@tonic-gate 	caller_context_t *);
790Sstevel@tonic-gate static int pcfs_access(struct vnode *, int, int, struct cred *);
800Sstevel@tonic-gate static int pcfs_lookup(struct vnode *, char *, struct vnode **,
810Sstevel@tonic-gate 	struct pathname *, int, struct vnode *, struct cred *);
820Sstevel@tonic-gate static int pcfs_create(struct vnode *, char *, struct vattr *,
830Sstevel@tonic-gate 	enum vcexcl, int mode, struct vnode **, struct cred *, int);
840Sstevel@tonic-gate static int pcfs_remove(struct vnode *, char *, struct cred *);
850Sstevel@tonic-gate static int pcfs_rename(struct vnode *, char *, struct vnode *, char *,
860Sstevel@tonic-gate 	struct cred *);
870Sstevel@tonic-gate static int pcfs_mkdir(struct vnode *, char *, struct vattr *, struct vnode **,
880Sstevel@tonic-gate 	struct cred *);
890Sstevel@tonic-gate static int pcfs_rmdir(struct vnode *, char *, struct vnode *, struct cred *);
900Sstevel@tonic-gate static int pcfs_readdir(struct vnode *, struct uio *, struct cred *, int *);
910Sstevel@tonic-gate static int pcfs_fsync(struct vnode *, int, struct cred *);
920Sstevel@tonic-gate static void pcfs_inactive(struct vnode *, struct cred *);
930Sstevel@tonic-gate static int pcfs_fid(struct vnode *vp, struct fid *fidp);
940Sstevel@tonic-gate static int pcfs_space(struct vnode *, int, struct flock64 *, int,
950Sstevel@tonic-gate 	offset_t, cred_t *, caller_context_t *);
960Sstevel@tonic-gate static int pcfs_getpage(struct vnode *, offset_t, size_t, uint_t *, page_t *[],
970Sstevel@tonic-gate 	size_t, struct seg *, caddr_t, enum seg_rw, struct cred *);
980Sstevel@tonic-gate static int pcfs_getapage(struct vnode *, u_offset_t, size_t, uint_t *,
990Sstevel@tonic-gate 	page_t *[], size_t, struct seg *, caddr_t, enum seg_rw, struct cred *);
1000Sstevel@tonic-gate static int pcfs_putpage(struct vnode *, offset_t, size_t, int, struct cred *);
1010Sstevel@tonic-gate static int pcfs_map(struct vnode *, offset_t, struct as *, caddr_t *, size_t,
1020Sstevel@tonic-gate 	uchar_t, uchar_t, uint_t, struct cred *);
1030Sstevel@tonic-gate static int pcfs_addmap(struct vnode *, offset_t, struct as *, caddr_t,
1040Sstevel@tonic-gate 	size_t, uchar_t, uchar_t, uint_t, struct cred *);
1050Sstevel@tonic-gate static int pcfs_delmap(struct vnode *, offset_t, struct as *, caddr_t,
1060Sstevel@tonic-gate 	size_t, uint_t, uint_t, uint_t, struct cred *);
1070Sstevel@tonic-gate static int pcfs_seek(struct vnode *, offset_t, offset_t *);
1080Sstevel@tonic-gate static int pcfs_pathconf(struct vnode *, int, ulong_t *, struct cred *);
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate int pcfs_putapage(struct vnode *, page_t *, u_offset_t *, size_t *, int,
1110Sstevel@tonic-gate 	struct cred *);
1120Sstevel@tonic-gate static int rwpcp(struct pcnode *, struct uio *, enum uio_rw, int);
1130Sstevel@tonic-gate static int get_long_fn_chunk(struct pcdir_lfn *ep, char *buf, int foldcase);
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate extern krwlock_t pcnodes_lock;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate #define	lround(r)	(((r)+sizeof (long long)-1)&(~(sizeof (long long)-1)))
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate /*
1200Sstevel@tonic-gate  * vnode op vectors for files and directories.
1210Sstevel@tonic-gate  */
1220Sstevel@tonic-gate struct vnodeops *pcfs_fvnodeops;
1230Sstevel@tonic-gate struct vnodeops *pcfs_dvnodeops;
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate const fs_operation_def_t pcfs_fvnodeops_template[] = {
1260Sstevel@tonic-gate 	VOPNAME_OPEN, pcfs_open,
1270Sstevel@tonic-gate 	VOPNAME_CLOSE, pcfs_close,
1280Sstevel@tonic-gate 	VOPNAME_READ, pcfs_read,
1290Sstevel@tonic-gate 	VOPNAME_WRITE, pcfs_write,
1300Sstevel@tonic-gate 	VOPNAME_GETATTR, pcfs_getattr,
1310Sstevel@tonic-gate 	VOPNAME_SETATTR, pcfs_setattr,
1320Sstevel@tonic-gate 	VOPNAME_ACCESS, pcfs_access,
1330Sstevel@tonic-gate 	VOPNAME_FSYNC, pcfs_fsync,
1340Sstevel@tonic-gate 	VOPNAME_INACTIVE, (fs_generic_func_p) pcfs_inactive,
1350Sstevel@tonic-gate 	VOPNAME_FID, pcfs_fid,
1360Sstevel@tonic-gate 	VOPNAME_SEEK, pcfs_seek,
1370Sstevel@tonic-gate 	VOPNAME_SPACE, pcfs_space,
1380Sstevel@tonic-gate 	VOPNAME_GETPAGE, pcfs_getpage,
1390Sstevel@tonic-gate 	VOPNAME_PUTPAGE, pcfs_putpage,
1400Sstevel@tonic-gate 	VOPNAME_MAP, (fs_generic_func_p) pcfs_map,
1410Sstevel@tonic-gate 	VOPNAME_ADDMAP, (fs_generic_func_p) pcfs_addmap,
1420Sstevel@tonic-gate 	VOPNAME_DELMAP, pcfs_delmap,
1430Sstevel@tonic-gate 	VOPNAME_PATHCONF, pcfs_pathconf,
1440Sstevel@tonic-gate 	VOPNAME_VNEVENT, fs_vnevent_support,
1450Sstevel@tonic-gate 	NULL, NULL
1460Sstevel@tonic-gate };
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate const fs_operation_def_t pcfs_dvnodeops_template[] = {
1490Sstevel@tonic-gate 	VOPNAME_OPEN, pcfs_open,
1500Sstevel@tonic-gate 	VOPNAME_CLOSE, pcfs_close,
1510Sstevel@tonic-gate 	VOPNAME_GETATTR, pcfs_getattr,
1520Sstevel@tonic-gate 	VOPNAME_SETATTR, pcfs_setattr,
1530Sstevel@tonic-gate 	VOPNAME_ACCESS, pcfs_access,
1540Sstevel@tonic-gate 	VOPNAME_LOOKUP, pcfs_lookup,
1550Sstevel@tonic-gate 	VOPNAME_CREATE, pcfs_create,
1560Sstevel@tonic-gate 	VOPNAME_REMOVE, pcfs_remove,
1570Sstevel@tonic-gate 	VOPNAME_RENAME, pcfs_rename,
1580Sstevel@tonic-gate 	VOPNAME_MKDIR, pcfs_mkdir,
1590Sstevel@tonic-gate 	VOPNAME_RMDIR, pcfs_rmdir,
1600Sstevel@tonic-gate 	VOPNAME_READDIR, pcfs_readdir,
1610Sstevel@tonic-gate 	VOPNAME_FSYNC, pcfs_fsync,
1620Sstevel@tonic-gate 	VOPNAME_INACTIVE, (fs_generic_func_p) pcfs_inactive,
1630Sstevel@tonic-gate 	VOPNAME_FID, pcfs_fid,
1640Sstevel@tonic-gate 	VOPNAME_SEEK, pcfs_seek,
1650Sstevel@tonic-gate 	VOPNAME_PATHCONF, pcfs_pathconf,
1660Sstevel@tonic-gate 	VOPNAME_VNEVENT, fs_vnevent_support,
1670Sstevel@tonic-gate 	NULL, NULL
1680Sstevel@tonic-gate };
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate /*ARGSUSED*/
1720Sstevel@tonic-gate static int
1730Sstevel@tonic-gate pcfs_open(
1740Sstevel@tonic-gate 	struct vnode **vpp,
1750Sstevel@tonic-gate 	int flag,
1760Sstevel@tonic-gate 	struct cred *cr)
1770Sstevel@tonic-gate {
1780Sstevel@tonic-gate 	return (0);
1790Sstevel@tonic-gate }
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate /*
1820Sstevel@tonic-gate  * files are sync'ed on close to keep floppy up to date
1830Sstevel@tonic-gate  */
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*ARGSUSED*/
1860Sstevel@tonic-gate static int
1870Sstevel@tonic-gate pcfs_close(
1880Sstevel@tonic-gate 	struct vnode *vp,
1890Sstevel@tonic-gate 	int flag,
1900Sstevel@tonic-gate 	int count,
1910Sstevel@tonic-gate 	offset_t offset,
1920Sstevel@tonic-gate 	struct cred *cr)
1930Sstevel@tonic-gate {
1940Sstevel@tonic-gate 	return (0);
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate /*ARGSUSED*/
1980Sstevel@tonic-gate static int
1990Sstevel@tonic-gate pcfs_read(
2000Sstevel@tonic-gate 	struct vnode *vp,
2010Sstevel@tonic-gate 	struct uio *uiop,
2020Sstevel@tonic-gate 	int ioflag,
2030Sstevel@tonic-gate 	struct cred *cr,
2040Sstevel@tonic-gate 	struct caller_context *ct)
2050Sstevel@tonic-gate {
2060Sstevel@tonic-gate 	struct pcfs *fsp;
2070Sstevel@tonic-gate 	struct pcnode *pcp;
2080Sstevel@tonic-gate 	int error;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
2110Sstevel@tonic-gate 	if (error = pc_verify(fsp))
2120Sstevel@tonic-gate 		return (error);
2130Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
2140Sstevel@tonic-gate 	if (error)
2150Sstevel@tonic-gate 		return (error);
216*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
2170Sstevel@tonic-gate 		pc_unlockfs(fsp);
2180Sstevel@tonic-gate 		return (EIO);
2190Sstevel@tonic-gate 	}
2200Sstevel@tonic-gate 	error = rwpcp(pcp, uiop, UIO_READ, ioflag);
2210Sstevel@tonic-gate 	if ((fsp->pcfs_vfs->vfs_flag & VFS_RDONLY) == 0) {
2220Sstevel@tonic-gate 		pcp->pc_flags |= PC_ACC;
2230Sstevel@tonic-gate 		pc_mark_acc(pcp);
2240Sstevel@tonic-gate 	}
2250Sstevel@tonic-gate 	pc_unlockfs(fsp);
2260Sstevel@tonic-gate 	if (error) {
2270Sstevel@tonic-gate 		PC_DPRINTF1(1, "pcfs_read: io error = %d\n", error);
2280Sstevel@tonic-gate 	}
2290Sstevel@tonic-gate 	return (error);
2300Sstevel@tonic-gate }
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate /*ARGSUSED*/
2330Sstevel@tonic-gate static int
2340Sstevel@tonic-gate pcfs_write(
2350Sstevel@tonic-gate 	struct vnode *vp,
2360Sstevel@tonic-gate 	struct uio *uiop,
2370Sstevel@tonic-gate 	int ioflag,
2380Sstevel@tonic-gate 	struct cred *cr,
2390Sstevel@tonic-gate 	struct caller_context *ct)
2400Sstevel@tonic-gate {
2410Sstevel@tonic-gate 	struct pcfs *fsp;
2420Sstevel@tonic-gate 	struct pcnode *pcp;
2430Sstevel@tonic-gate 	int error;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
2460Sstevel@tonic-gate 	if (error = pc_verify(fsp))
2470Sstevel@tonic-gate 		return (error);
2480Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
2490Sstevel@tonic-gate 	if (error)
2500Sstevel@tonic-gate 		return (error);
251*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
2520Sstevel@tonic-gate 		pc_unlockfs(fsp);
2530Sstevel@tonic-gate 		return (EIO);
2540Sstevel@tonic-gate 	}
2550Sstevel@tonic-gate 	if (ioflag & FAPPEND) {
2560Sstevel@tonic-gate 		/*
2570Sstevel@tonic-gate 		 * in append mode start at end of file.
2580Sstevel@tonic-gate 		 */
2590Sstevel@tonic-gate 		uiop->uio_loffset = pcp->pc_size;
2600Sstevel@tonic-gate 	}
2610Sstevel@tonic-gate 	error = rwpcp(pcp, uiop, UIO_WRITE, ioflag);
2620Sstevel@tonic-gate 	pcp->pc_flags |= PC_MOD;
2630Sstevel@tonic-gate 	pc_mark_mod(pcp);
2640Sstevel@tonic-gate 	if (ioflag & (FSYNC|FDSYNC))
2650Sstevel@tonic-gate 		(void) pc_nodeupdate(pcp);
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	pc_unlockfs(fsp);
2680Sstevel@tonic-gate 	if (error) {
2690Sstevel@tonic-gate 		PC_DPRINTF1(1, "pcfs_write: io error = %d\n", error);
2700Sstevel@tonic-gate 	}
2710Sstevel@tonic-gate 	return (error);
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate /*
2750Sstevel@tonic-gate  * read or write a vnode
2760Sstevel@tonic-gate  */
2770Sstevel@tonic-gate static int
2780Sstevel@tonic-gate rwpcp(
2790Sstevel@tonic-gate 	struct pcnode *pcp,
2800Sstevel@tonic-gate 	struct uio *uio,
2810Sstevel@tonic-gate 	enum uio_rw rw,
2820Sstevel@tonic-gate 	int ioflag)
2830Sstevel@tonic-gate {
2840Sstevel@tonic-gate 	struct vnode *vp = PCTOV(pcp);
2850Sstevel@tonic-gate 	struct pcfs *fsp;
2860Sstevel@tonic-gate 	daddr_t bn;			/* phys block number */
2870Sstevel@tonic-gate 	int n;
2880Sstevel@tonic-gate 	offset_t off;
2890Sstevel@tonic-gate 	caddr_t base;
2900Sstevel@tonic-gate 	int mapon, pagecreate;
2910Sstevel@tonic-gate 	int newpage;
2920Sstevel@tonic-gate 	int error = 0;
2930Sstevel@tonic-gate 	rlim64_t limit = uio->uio_llimit;
2940Sstevel@tonic-gate 	int oresid = uio->uio_resid;
2950Sstevel@tonic-gate 
2962720Sfrankho 	/*
2972720Sfrankho 	 * If the filesystem was umounted by force, return immediately.
2982720Sfrankho 	 */
2992720Sfrankho 	if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
3002720Sfrankho 		return (EIO);
3012720Sfrankho 
3020Sstevel@tonic-gate 	PC_DPRINTF4(5, "rwpcp pcp=%p off=%lld resid=%ld size=%u\n", (void *)pcp,
3030Sstevel@tonic-gate 	    uio->uio_loffset, uio->uio_resid, pcp->pc_size);
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	ASSERT(rw == UIO_READ || rw == UIO_WRITE);
3060Sstevel@tonic-gate 	ASSERT(vp->v_type == VREG);
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	if (uio->uio_loffset >= UINT32_MAX && rw == UIO_READ) {
3090Sstevel@tonic-gate 		return (0);
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	if (uio->uio_loffset < 0)
3130Sstevel@tonic-gate 		return (EINVAL);
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
3160Sstevel@tonic-gate 		limit = MAXOFFSET_T;
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	if (uio->uio_loffset >= limit && rw == UIO_WRITE) {
3190Sstevel@tonic-gate 		proc_t *p = ttoproc(curthread);
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
3220Sstevel@tonic-gate 		(void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
3230Sstevel@tonic-gate 		    p, RCA_UNSAFE_SIGINFO);
3240Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
3250Sstevel@tonic-gate 		return (EFBIG);
3260Sstevel@tonic-gate 	}
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	/* the following condition will occur only for write */
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	if (uio->uio_loffset >= UINT32_MAX)
3310Sstevel@tonic-gate 		return (EFBIG);
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	if (uio->uio_resid == 0)
3340Sstevel@tonic-gate 		return (0);
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	if (limit > UINT32_MAX)
3370Sstevel@tonic-gate 		limit = UINT32_MAX;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
3400Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_IRRECOV)
3410Sstevel@tonic-gate 		return (EIO);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	do {
3440Sstevel@tonic-gate 		/*
3450Sstevel@tonic-gate 		 * Assignments to "n" in this block may appear
3460Sstevel@tonic-gate 		 * to overflow in some cases.  However, after careful
3470Sstevel@tonic-gate 		 * analysis it was determined that all assignments to
3480Sstevel@tonic-gate 		 * "n" serve only to make "n" smaller.  Since "n"
3490Sstevel@tonic-gate 		 * starts out as no larger than MAXBSIZE, "int" is
3500Sstevel@tonic-gate 		 * safe.
3510Sstevel@tonic-gate 		 */
3520Sstevel@tonic-gate 		off = uio->uio_loffset & MAXBMASK;
3530Sstevel@tonic-gate 		mapon = (int)(uio->uio_loffset & MAXBOFFSET);
3540Sstevel@tonic-gate 		n = MIN(MAXBSIZE - mapon, uio->uio_resid);
3550Sstevel@tonic-gate 		if (rw == UIO_READ) {
3560Sstevel@tonic-gate 			offset_t diff;
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 			diff = pcp->pc_size - uio->uio_loffset;
3590Sstevel@tonic-gate 			if (diff <= 0)
3600Sstevel@tonic-gate 				return (0);
3610Sstevel@tonic-gate 			if (diff < n)
3620Sstevel@tonic-gate 				n = (int)diff;
3630Sstevel@tonic-gate 		}
3640Sstevel@tonic-gate 		/*
3650Sstevel@tonic-gate 		 * Compare limit with the actual offset + n, not the
3660Sstevel@tonic-gate 		 * rounded down offset "off" or we will overflow
3670Sstevel@tonic-gate 		 * the maximum file size after all.
3680Sstevel@tonic-gate 		 */
3690Sstevel@tonic-gate 		if (rw == UIO_WRITE && uio->uio_loffset + n >= limit) {
3700Sstevel@tonic-gate 			if (uio->uio_loffset >= limit) {
3710Sstevel@tonic-gate 				error = EFBIG;
3720Sstevel@tonic-gate 				break;
3730Sstevel@tonic-gate 			}
3740Sstevel@tonic-gate 			n = (int)(limit - uio->uio_loffset);
3750Sstevel@tonic-gate 		}
3760Sstevel@tonic-gate 		base = segmap_getmap(segkmap, vp, (u_offset_t)off);
3770Sstevel@tonic-gate 		pagecreate = 0;
3780Sstevel@tonic-gate 		newpage = 0;
3790Sstevel@tonic-gate 		if (rw == UIO_WRITE) {
3800Sstevel@tonic-gate 			/*
3810Sstevel@tonic-gate 			 * If PAGESIZE < MAXBSIZE, perhaps we ought to deal
3820Sstevel@tonic-gate 			 * with one page at a time, instead of one MAXBSIZE
3830Sstevel@tonic-gate 			 * at a time, so we can fully explore pagecreate
3840Sstevel@tonic-gate 			 * optimization??
3850Sstevel@tonic-gate 			 */
3860Sstevel@tonic-gate 			if (uio->uio_loffset + n > pcp->pc_size) {
3870Sstevel@tonic-gate 				uint_t ncl, lcn;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 				ncl = (uint_t)howmany((offset_t)pcp->pc_size,
3900Sstevel@tonic-gate 					fsp->pcfs_clsize);
3910Sstevel@tonic-gate 				if (uio->uio_loffset > pcp->pc_size &&
3920Sstevel@tonic-gate 				    ncl < (uint_t)howmany(uio->uio_loffset,
3930Sstevel@tonic-gate 							fsp->pcfs_clsize)) {
3940Sstevel@tonic-gate 					/*
3950Sstevel@tonic-gate 					 * Allocate and zerofill skipped
3960Sstevel@tonic-gate 					 * clusters. This may not be worth the
3970Sstevel@tonic-gate 					 * effort since a small lseek beyond
3980Sstevel@tonic-gate 					 * eof but still within the cluster
3990Sstevel@tonic-gate 					 * will not be zeroed out.
4000Sstevel@tonic-gate 					 */
4010Sstevel@tonic-gate 					lcn = pc_lblkno(fsp, uio->uio_loffset);
4020Sstevel@tonic-gate 					error = pc_balloc(pcp, (daddr_t)lcn,
4030Sstevel@tonic-gate 					    1, &bn);
4040Sstevel@tonic-gate 					ncl = lcn + 1;
4050Sstevel@tonic-gate 				}
4060Sstevel@tonic-gate 				if (!error &&
4070Sstevel@tonic-gate 				    ncl < (uint_t)howmany(uio->uio_loffset + n,
4080Sstevel@tonic-gate 							fsp->pcfs_clsize))
4090Sstevel@tonic-gate 					/*
4100Sstevel@tonic-gate 					 * allocate clusters w/o zerofill
4110Sstevel@tonic-gate 					 */
4120Sstevel@tonic-gate 					error = pc_balloc(pcp,
4130Sstevel@tonic-gate 					    (daddr_t)pc_lblkno(fsp,
4140Sstevel@tonic-gate 					    uio->uio_loffset + n - 1),
4150Sstevel@tonic-gate 					    0, &bn);
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 				pcp->pc_flags |= PC_CHG;
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 				if (error) {
420*2972Sfrankho 					pc_cluster32_t ncl;
421*2972Sfrankho 					int nerror;
422*2972Sfrankho 
423*2972Sfrankho 					/*
424*2972Sfrankho 					 * figure out new file size from
425*2972Sfrankho 					 * cluster chain length. If this
426*2972Sfrankho 					 * is detected to loop, the chain
427*2972Sfrankho 					 * is corrupted and we'd better
428*2972Sfrankho 					 * keep our fingers off that file.
429*2972Sfrankho 					 */
430*2972Sfrankho 					nerror = pc_fileclsize(fsp,
431*2972Sfrankho 					    pcp->pc_scluster, &ncl);
432*2972Sfrankho 					if (nerror) {
433*2972Sfrankho 						PC_DPRINTF1(2,
434*2972Sfrankho 						    "cluster chain "
435*2972Sfrankho 						    "corruption, "
436*2972Sfrankho 						    "scluster=%d\n",
437*2972Sfrankho 						    pcp->pc_scluster);
438*2972Sfrankho 						pcp->pc_size = 0;
439*2972Sfrankho 						pcp->pc_flags |= PC_INVAL;
440*2972Sfrankho 						error = nerror;
441*2972Sfrankho 						(void) segmap_release(segkmap,
442*2972Sfrankho 						    base, 0);
443*2972Sfrankho 						break;
444*2972Sfrankho 					}
445*2972Sfrankho 					pcp->pc_size = fsp->pcfs_clsize * ncl;
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 					if (error == ENOSPC &&
4480Sstevel@tonic-gate 					    (pcp->pc_size - uio->uio_loffset)
4490Sstevel@tonic-gate 						> 0) {
4500Sstevel@tonic-gate 						PC_DPRINTF3(2, "rwpcp ENOSPC "
4510Sstevel@tonic-gate 						    "off=%lld n=%d size=%d\n",
4520Sstevel@tonic-gate 						    uio->uio_loffset,
4530Sstevel@tonic-gate 						    n, pcp->pc_size);
4540Sstevel@tonic-gate 						n = (int)(pcp->pc_size -
4550Sstevel@tonic-gate 							uio->uio_loffset);
4560Sstevel@tonic-gate 					} else {
4570Sstevel@tonic-gate 						PC_DPRINTF1(1,
4580Sstevel@tonic-gate 						    "rwpcp error1=%d\n", error);
4590Sstevel@tonic-gate 						(void) segmap_release(segkmap,
4600Sstevel@tonic-gate 						    base, 0);
4610Sstevel@tonic-gate 						break;
4620Sstevel@tonic-gate 					}
4630Sstevel@tonic-gate 				} else {
4640Sstevel@tonic-gate 					pcp->pc_size =
4650Sstevel@tonic-gate 					    (uint_t)(uio->uio_loffset + n);
4660Sstevel@tonic-gate 				}
4670Sstevel@tonic-gate 				if (mapon == 0) {
4680Sstevel@tonic-gate 					newpage = segmap_pagecreate(segkmap,
4690Sstevel@tonic-gate 						base, (size_t)n, 0);
4700Sstevel@tonic-gate 					pagecreate = 1;
4710Sstevel@tonic-gate 				}
4720Sstevel@tonic-gate 			} else if (n == MAXBSIZE) {
4730Sstevel@tonic-gate 				newpage = segmap_pagecreate(segkmap, base,
4740Sstevel@tonic-gate 						(size_t)n, 0);
4750Sstevel@tonic-gate 				pagecreate = 1;
4760Sstevel@tonic-gate 			}
4770Sstevel@tonic-gate 		}
4780Sstevel@tonic-gate 		error = uiomove(base + mapon, (size_t)n, rw, uio);
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 		if (pagecreate && uio->uio_loffset <
4810Sstevel@tonic-gate 			roundup(off + mapon + n, PAGESIZE)) {
4820Sstevel@tonic-gate 			offset_t nzero, nmoved;
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate 			nmoved = uio->uio_loffset - (off + mapon);
4850Sstevel@tonic-gate 			nzero = roundup(mapon + n, PAGESIZE) - nmoved;
4860Sstevel@tonic-gate 			(void) kzero(base + mapon + nmoved, (size_t)nzero);
4870Sstevel@tonic-gate 		}
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 		/*
4900Sstevel@tonic-gate 		 * Unlock the pages which have been allocated by
4910Sstevel@tonic-gate 		 * page_create_va() in segmap_pagecreate().
4920Sstevel@tonic-gate 		 */
4930Sstevel@tonic-gate 		if (newpage)
4940Sstevel@tonic-gate 			segmap_pageunlock(segkmap, base, (size_t)n,
4950Sstevel@tonic-gate 				rw == UIO_WRITE ? S_WRITE : S_READ);
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 		if (error) {
4980Sstevel@tonic-gate 			PC_DPRINTF1(1, "rwpcp error2=%d\n", error);
4990Sstevel@tonic-gate 			/*
5000Sstevel@tonic-gate 			 * If we failed on a write, we may have already
5010Sstevel@tonic-gate 			 * allocated file blocks as well as pages.  It's hard
5020Sstevel@tonic-gate 			 * to undo the block allocation, but we must be sure
5030Sstevel@tonic-gate 			 * to invalidate any pages that may have been
5040Sstevel@tonic-gate 			 * allocated.
5050Sstevel@tonic-gate 			 */
5060Sstevel@tonic-gate 			if (rw == UIO_WRITE)
5070Sstevel@tonic-gate 				(void) segmap_release(segkmap, base, SM_INVAL);
5080Sstevel@tonic-gate 			else
5090Sstevel@tonic-gate 				(void) segmap_release(segkmap, base, 0);
5100Sstevel@tonic-gate 		} else {
5110Sstevel@tonic-gate 			uint_t flags = 0;
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 			if (rw == UIO_READ) {
5140Sstevel@tonic-gate 				if (n + mapon == MAXBSIZE ||
5150Sstevel@tonic-gate 				    uio->uio_loffset == pcp->pc_size)
5160Sstevel@tonic-gate 					flags = SM_DONTNEED;
5170Sstevel@tonic-gate 			} else if (ioflag & (FSYNC|FDSYNC)) {
5180Sstevel@tonic-gate 				flags = SM_WRITE;
5190Sstevel@tonic-gate 			} else if (n + mapon == MAXBSIZE) {
5200Sstevel@tonic-gate 				flags = SM_WRITE|SM_ASYNC|SM_DONTNEED;
5210Sstevel@tonic-gate 			}
5220Sstevel@tonic-gate 			error = segmap_release(segkmap, base, flags);
5230Sstevel@tonic-gate 		}
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	if (oresid != uio->uio_resid)
5280Sstevel@tonic-gate 		error = 0;
5290Sstevel@tonic-gate 	return (error);
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate /*ARGSUSED*/
5330Sstevel@tonic-gate static int
5340Sstevel@tonic-gate pcfs_getattr(
5350Sstevel@tonic-gate 	struct vnode *vp,
5360Sstevel@tonic-gate 	struct vattr *vap,
5370Sstevel@tonic-gate 	int flags,
5380Sstevel@tonic-gate 	struct cred *cr)
5390Sstevel@tonic-gate {
5400Sstevel@tonic-gate 	struct pcnode *pcp;
5410Sstevel@tonic-gate 	struct pcfs *fsp;
5420Sstevel@tonic-gate 	int error;
5430Sstevel@tonic-gate 	char attr;
5440Sstevel@tonic-gate 	struct pctime atime;
5452720Sfrankho 	int64_t unixtime;
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	PC_DPRINTF1(8, "pcfs_getattr: vp=%p\n", (void *)vp);
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
5500Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
5510Sstevel@tonic-gate 	if (error)
5520Sstevel@tonic-gate 		return (error);
553*2972Sfrankho 
554*2972Sfrankho 	/*
555*2972Sfrankho 	 * Note that we don't check for "invalid node" (PC_INVAL) here
556*2972Sfrankho 	 * only in order to make stat() succeed. We allow no I/O on such
557*2972Sfrankho 	 * a node, but do allow to check for its existance.
558*2972Sfrankho 	 */
5590Sstevel@tonic-gate 	if ((pcp = VTOPC(vp)) == NULL) {
5600Sstevel@tonic-gate 		pc_unlockfs(fsp);
5610Sstevel@tonic-gate 		return (EIO);
5620Sstevel@tonic-gate 	}
5630Sstevel@tonic-gate 	/*
5640Sstevel@tonic-gate 	 * Copy from pcnode.
5650Sstevel@tonic-gate 	 */
5660Sstevel@tonic-gate 	vap->va_type = vp->v_type;
5670Sstevel@tonic-gate 	attr = pcp->pc_entry.pcd_attr;
5680Sstevel@tonic-gate 	if (PCA_IS_HIDDEN(fsp, attr))
5690Sstevel@tonic-gate 		vap->va_mode = 0;
5700Sstevel@tonic-gate 	else if (attr & PCA_LABEL)
5710Sstevel@tonic-gate 		vap->va_mode = 0444;
5720Sstevel@tonic-gate 	else if (attr & PCA_RDONLY)
5730Sstevel@tonic-gate 		vap->va_mode = 0555;
5740Sstevel@tonic-gate 	else if (fsp->pcfs_flags & PCFS_BOOTPART) {
5750Sstevel@tonic-gate 		vap->va_mode = 0755;
5760Sstevel@tonic-gate 	} else {
5770Sstevel@tonic-gate 		vap->va_mode = 0777;
5780Sstevel@tonic-gate 	}
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	if (attr & PCA_DIR)
5810Sstevel@tonic-gate 		vap->va_mode |= S_IFDIR;
5820Sstevel@tonic-gate 	else
5830Sstevel@tonic-gate 		vap->va_mode |= S_IFREG;
5840Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_BOOTPART) {
5850Sstevel@tonic-gate 		vap->va_uid = 0;
5860Sstevel@tonic-gate 		vap->va_gid = 0;
5870Sstevel@tonic-gate 	} else {
5880Sstevel@tonic-gate 		vap->va_uid = crgetuid(cr);
5890Sstevel@tonic-gate 		vap->va_gid = crgetgid(cr);
5900Sstevel@tonic-gate 	}
5910Sstevel@tonic-gate 	vap->va_fsid = vp->v_vfsp->vfs_dev;
5920Sstevel@tonic-gate 	vap->va_nodeid = (ino64_t)pc_makenodeid(pcp->pc_eblkno,
5930Sstevel@tonic-gate 	    pcp->pc_eoffset, pcp->pc_entry.pcd_attr,
5940Sstevel@tonic-gate 	    pc_getstartcluster(fsp, &pcp->pc_entry), fsp->pcfs_entps);
5950Sstevel@tonic-gate 	vap->va_nlink = 1;
5960Sstevel@tonic-gate 	vap->va_size = (u_offset_t)pcp->pc_size;
5972720Sfrankho 
5982720Sfrankho 	pc_pcttotv(&pcp->pc_entry.pcd_mtime, &unixtime);
5992720Sfrankho 	if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0) {
6002720Sfrankho 		if (unixtime > INT32_MAX)
6012720Sfrankho 			DTRACE_PROBE1(pcfs__mtimeclamped, int64_t, unixtime);
6022720Sfrankho 		unixtime = MIN(unixtime, INT32_MAX);
6032720Sfrankho 	} else if (unixtime > INT32_MAX &&
6042720Sfrankho 	    get_udatamodel() == DATAMODEL_ILP32) {
6052720Sfrankho 		pc_unlockfs(fsp);
6062720Sfrankho 		DTRACE_PROBE1(pcfs__mtimeoverflowed, int64_t, unixtime);
6072720Sfrankho 		return (EOVERFLOW);
6082720Sfrankho 	}
6092720Sfrankho 
6102720Sfrankho 	vap->va_mtime.tv_sec = (time_t)unixtime;
6112720Sfrankho 	vap->va_mtime.tv_nsec = 0;
6122720Sfrankho 
6132720Sfrankho 	/*
6142720Sfrankho 	 * FAT doesn't know about POSIX ctime.
6152720Sfrankho 	 * Best approximation is to always set it to mtime.
6162720Sfrankho 	 */
6170Sstevel@tonic-gate 	vap->va_ctime = vap->va_mtime;
6182720Sfrankho 
6192720Sfrankho 	/*
6202720Sfrankho 	 * FAT only stores "last access date". If that's the
6212720Sfrankho 	 * same as the date of last modification then the time
6222720Sfrankho 	 * of last access is known. Otherwise, use midnight.
6232720Sfrankho 	 */
6240Sstevel@tonic-gate 	atime.pct_date = pcp->pc_entry.pcd_ladate;
6252720Sfrankho 	if (atime.pct_date == pcp->pc_entry.pcd_mtime.pct_date)
6262720Sfrankho 		atime.pct_time = pcp->pc_entry.pcd_mtime.pct_time;
6272720Sfrankho 	else
6282720Sfrankho 		atime.pct_time = 0;
6292720Sfrankho 	pc_pcttotv(&atime, &unixtime);
6302720Sfrankho 	if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0) {
6312720Sfrankho 		if (unixtime > INT32_MAX)
6322720Sfrankho 			DTRACE_PROBE1(pcfs__atimeclamped, int64_t, unixtime);
6332720Sfrankho 		unixtime = MIN(unixtime, INT32_MAX);
6342720Sfrankho 	} else if (unixtime > INT32_MAX &&
6352720Sfrankho 	    get_udatamodel() == DATAMODEL_ILP32) {
6362720Sfrankho 		pc_unlockfs(fsp);
6372720Sfrankho 		DTRACE_PROBE1(pcfs__atimeoverflowed, int64_t, unixtime);
6382720Sfrankho 		return (EOVERFLOW);
6392720Sfrankho 	}
6402720Sfrankho 
6412720Sfrankho 	vap->va_atime.tv_sec = (time_t)unixtime;
6422720Sfrankho 	vap->va_atime.tv_nsec = 0;
6432720Sfrankho 
6440Sstevel@tonic-gate 	vap->va_rdev = 0;
6450Sstevel@tonic-gate 	vap->va_nblocks = (fsblkcnt64_t)howmany((offset_t)pcp->pc_size,
6460Sstevel@tonic-gate 				DEV_BSIZE);
6470Sstevel@tonic-gate 	vap->va_blksize = fsp->pcfs_clsize;
6480Sstevel@tonic-gate 	pc_unlockfs(fsp);
6490Sstevel@tonic-gate 	return (0);
6500Sstevel@tonic-gate }
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate /*ARGSUSED*/
6540Sstevel@tonic-gate static int
6550Sstevel@tonic-gate pcfs_setattr(
6560Sstevel@tonic-gate 	struct vnode *vp,
6570Sstevel@tonic-gate 	struct vattr *vap,
6580Sstevel@tonic-gate 	int flags,
6590Sstevel@tonic-gate 	struct cred *cr,
6600Sstevel@tonic-gate 	caller_context_t *ct)
6610Sstevel@tonic-gate {
6620Sstevel@tonic-gate 	struct pcnode *pcp;
6630Sstevel@tonic-gate 	mode_t mask = vap->va_mask;
6640Sstevel@tonic-gate 	int error;
6650Sstevel@tonic-gate 	struct pcfs *fsp;
6662720Sfrankho 	timestruc_t now, *timep;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	PC_DPRINTF2(6, "pcfs_setattr: vp=%p mask=%x\n", (void *)vp, (int)mask);
6690Sstevel@tonic-gate 	/*
6700Sstevel@tonic-gate 	 * cannot set these attributes
6710Sstevel@tonic-gate 	 */
6720Sstevel@tonic-gate 	if (mask & (AT_NOSET | AT_UID | AT_GID)) {
6730Sstevel@tonic-gate 		return (EINVAL);
6740Sstevel@tonic-gate 	}
6750Sstevel@tonic-gate 	/*
6762720Sfrankho 	 * pcfs_setattr is now allowed on directories to avoid silly warnings
6770Sstevel@tonic-gate 	 * from 'tar' when it tries to set times on a directory, and console
6780Sstevel@tonic-gate 	 * printf's on the NFS server when it gets EINVAL back on such a
6790Sstevel@tonic-gate 	 * request. One possible problem with that since a directory entry
6800Sstevel@tonic-gate 	 * identifies a file, '.' and all the '..' entries in subdirectories
6810Sstevel@tonic-gate 	 * may get out of sync when the directory is updated since they're
6820Sstevel@tonic-gate 	 * treated like separate files. We could fix that by looking for
6830Sstevel@tonic-gate 	 * '.' and giving it the same attributes, and then looking for
6840Sstevel@tonic-gate 	 * all the subdirectories and updating '..', but that's pretty
6850Sstevel@tonic-gate 	 * expensive for something that doesn't seem likely to matter.
6860Sstevel@tonic-gate 	 */
6870Sstevel@tonic-gate 	/* can't do some ops on directories anyway */
6880Sstevel@tonic-gate 	if ((vp->v_type == VDIR) &&
6890Sstevel@tonic-gate 	    (mask & AT_SIZE)) {
6900Sstevel@tonic-gate 		return (EINVAL);
6910Sstevel@tonic-gate 	}
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
6940Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
6950Sstevel@tonic-gate 	if (error)
6960Sstevel@tonic-gate 		return (error);
697*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
6980Sstevel@tonic-gate 		pc_unlockfs(fsp);
6990Sstevel@tonic-gate 		return (EIO);
7000Sstevel@tonic-gate 	}
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_BOOTPART) {
7030Sstevel@tonic-gate 		if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
7040Sstevel@tonic-gate 			pc_unlockfs(fsp);
7050Sstevel@tonic-gate 			return (EACCES);
7060Sstevel@tonic-gate 		}
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	/*
7100Sstevel@tonic-gate 	 * Change file access modes.
7110Sstevel@tonic-gate 	 * If nobody has write permission, file is marked readonly.
7120Sstevel@tonic-gate 	 * Otherwise file is writable by anyone.
7130Sstevel@tonic-gate 	 */
7140Sstevel@tonic-gate 	if ((mask & AT_MODE) && (vap->va_mode != (mode_t)-1)) {
7150Sstevel@tonic-gate 		if ((vap->va_mode & 0222) == 0)
7160Sstevel@tonic-gate 			pcp->pc_entry.pcd_attr |= PCA_RDONLY;
7170Sstevel@tonic-gate 		else
7180Sstevel@tonic-gate 			pcp->pc_entry.pcd_attr &= ~PCA_RDONLY;
7190Sstevel@tonic-gate 		pcp->pc_flags |= PC_CHG;
7200Sstevel@tonic-gate 	}
7210Sstevel@tonic-gate 	/*
7220Sstevel@tonic-gate 	 * Truncate file. Must have write permission.
7230Sstevel@tonic-gate 	 */
7240Sstevel@tonic-gate 	if ((mask & AT_SIZE) && (vap->va_size != (u_offset_t)-1)) {
7250Sstevel@tonic-gate 		if (pcp->pc_entry.pcd_attr & PCA_RDONLY) {
7260Sstevel@tonic-gate 			error = EACCES;
7270Sstevel@tonic-gate 			goto out;
7280Sstevel@tonic-gate 		}
7290Sstevel@tonic-gate 		if (vap->va_size > UINT32_MAX) {
7300Sstevel@tonic-gate 			error = EFBIG;
7310Sstevel@tonic-gate 			goto out;
7320Sstevel@tonic-gate 		}
7330Sstevel@tonic-gate 		error = pc_truncate(pcp, (uint_t)vap->va_size);
7340Sstevel@tonic-gate 		if (error)
7350Sstevel@tonic-gate 			goto out;
7360Sstevel@tonic-gate 	}
7370Sstevel@tonic-gate 	/*
7380Sstevel@tonic-gate 	 * Change file modified times.
7390Sstevel@tonic-gate 	 */
7402720Sfrankho 	if (mask & (AT_MTIME | AT_CTIME)) {
7410Sstevel@tonic-gate 		/*
7420Sstevel@tonic-gate 		 * If SysV-compatible option to set access and
7430Sstevel@tonic-gate 		 * modified times if privileged, owner, or write access,
7440Sstevel@tonic-gate 		 * use current time rather than va_mtime.
7450Sstevel@tonic-gate 		 *
7460Sstevel@tonic-gate 		 * XXX - va_mtime.tv_sec == -1 flags this.
7470Sstevel@tonic-gate 		 */
7482720Sfrankho 		timep = &vap->va_mtime;
7490Sstevel@tonic-gate 		if (vap->va_mtime.tv_sec == -1) {
7500Sstevel@tonic-gate 			gethrestime(&now);
7512720Sfrankho 			timep = &now;
7520Sstevel@tonic-gate 		}
7532720Sfrankho 		if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0 &&
7542720Sfrankho 		    timep->tv_sec > INT32_MAX) {
7552720Sfrankho 			error = EOVERFLOW;
7562720Sfrankho 			goto out;
7572720Sfrankho 		}
7582720Sfrankho 		error = pc_tvtopct(timep, &pcp->pc_entry.pcd_mtime);
7592720Sfrankho 		if (error)
7602720Sfrankho 			goto out;
7610Sstevel@tonic-gate 		pcp->pc_flags |= PC_CHG;
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate 	/*
7640Sstevel@tonic-gate 	 * Change file access times.
7650Sstevel@tonic-gate 	 */
7662720Sfrankho 	if (mask & AT_ATIME) {
7670Sstevel@tonic-gate 		/*
7680Sstevel@tonic-gate 		 * If SysV-compatible option to set access and
7690Sstevel@tonic-gate 		 * modified times if privileged, owner, or write access,
7700Sstevel@tonic-gate 		 * use current time rather than va_mtime.
7710Sstevel@tonic-gate 		 *
7720Sstevel@tonic-gate 		 * XXX - va_atime.tv_sec == -1 flags this.
7730Sstevel@tonic-gate 		 */
7740Sstevel@tonic-gate 		struct pctime	atime;
7750Sstevel@tonic-gate 
7762720Sfrankho 		timep = &vap->va_atime;
7770Sstevel@tonic-gate 		if (vap->va_atime.tv_sec == -1) {
7780Sstevel@tonic-gate 			gethrestime(&now);
7792720Sfrankho 			timep = &now;
7800Sstevel@tonic-gate 		}
7812720Sfrankho 		if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0 &&
7822720Sfrankho 		    timep->tv_sec > INT32_MAX) {
7832720Sfrankho 			error = EOVERFLOW;
7842720Sfrankho 			goto out;
7852720Sfrankho 		}
7862720Sfrankho 		error = pc_tvtopct(timep, &atime);
7872720Sfrankho 		if (error)
7882720Sfrankho 			goto out;
7890Sstevel@tonic-gate 		pcp->pc_entry.pcd_ladate = atime.pct_date;
7900Sstevel@tonic-gate 		pcp->pc_flags |= PC_CHG;
7910Sstevel@tonic-gate 	}
7920Sstevel@tonic-gate out:
7930Sstevel@tonic-gate 	pc_unlockfs(fsp);
7940Sstevel@tonic-gate 	return (error);
7950Sstevel@tonic-gate }
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate /*ARGSUSED*/
7990Sstevel@tonic-gate static int
8000Sstevel@tonic-gate pcfs_access(
8010Sstevel@tonic-gate 	struct vnode *vp,
8020Sstevel@tonic-gate 	int mode,
8030Sstevel@tonic-gate 	int flags,
8040Sstevel@tonic-gate 	struct cred *cr)
8050Sstevel@tonic-gate {
8060Sstevel@tonic-gate 	struct pcnode *pcp;
8070Sstevel@tonic-gate 	struct pcfs *fsp;
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
8110Sstevel@tonic-gate 
812*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL)
8130Sstevel@tonic-gate 		return (EIO);
8140Sstevel@tonic-gate 	if ((mode & VWRITE) && (pcp->pc_entry.pcd_attr & PCA_RDONLY))
8150Sstevel@tonic-gate 		return (EACCES);
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	/*
8180Sstevel@tonic-gate 	 * If this is a boot partition, privileged users have full access while
8190Sstevel@tonic-gate 	 * others have read-only access.
8200Sstevel@tonic-gate 	 */
8210Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_BOOTPART) {
8220Sstevel@tonic-gate 		if ((mode & VWRITE) &&
8230Sstevel@tonic-gate 		    secpolicy_pcfs_modify_bootpartition(cr) != 0)
8240Sstevel@tonic-gate 			return (EACCES);
8250Sstevel@tonic-gate 	}
8260Sstevel@tonic-gate 	return (0);
8270Sstevel@tonic-gate }
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate /*ARGSUSED*/
8310Sstevel@tonic-gate static int
8320Sstevel@tonic-gate pcfs_fsync(
8330Sstevel@tonic-gate 	struct vnode *vp,
8340Sstevel@tonic-gate 	int syncflag,
8350Sstevel@tonic-gate 	struct cred *cr)
8360Sstevel@tonic-gate {
8370Sstevel@tonic-gate 	struct pcfs *fsp;
8380Sstevel@tonic-gate 	struct pcnode *pcp;
8390Sstevel@tonic-gate 	int error;
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
8420Sstevel@tonic-gate 	if (error = pc_verify(fsp))
8430Sstevel@tonic-gate 		return (error);
8440Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
8450Sstevel@tonic-gate 	if (error)
8460Sstevel@tonic-gate 		return (error);
847*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
8480Sstevel@tonic-gate 		pc_unlockfs(fsp);
8490Sstevel@tonic-gate 		return (EIO);
8500Sstevel@tonic-gate 	}
8510Sstevel@tonic-gate 	rw_enter(&pcnodes_lock, RW_WRITER);
8520Sstevel@tonic-gate 	error = pc_nodesync(pcp);
8530Sstevel@tonic-gate 	rw_exit(&pcnodes_lock);
8540Sstevel@tonic-gate 	pc_unlockfs(fsp);
8550Sstevel@tonic-gate 	return (error);
8560Sstevel@tonic-gate }
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate /*ARGSUSED*/
8600Sstevel@tonic-gate static void
8610Sstevel@tonic-gate pcfs_inactive(
8620Sstevel@tonic-gate 	struct vnode *vp,
8630Sstevel@tonic-gate 	struct cred *cr)
8640Sstevel@tonic-gate {
8650Sstevel@tonic-gate 	struct pcnode *pcp;
8660Sstevel@tonic-gate 	struct pcfs *fsp;
8670Sstevel@tonic-gate 	int error;
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
8700Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 1);
8710Sstevel@tonic-gate 
8722720Sfrankho 	/*
8732720Sfrankho 	 * If the filesystem was umounted by force, all dirty
8742720Sfrankho 	 * pages associated with this vnode are invalidated
8752720Sfrankho 	 * and then the vnode will be freed.
8762720Sfrankho 	 */
8772720Sfrankho 	if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) {
8782720Sfrankho 		pcp = VTOPC(vp);
8792720Sfrankho 		if (vn_has_cached_data(vp)) {
8802720Sfrankho 			(void) pvn_vplist_dirty(vp, (u_offset_t)0,
8812720Sfrankho 			    pcfs_putapage, B_INVAL, (struct cred *)NULL);
8822720Sfrankho 		}
8832720Sfrankho 		remque(pcp);
8842720Sfrankho 		if (error == 0)
8852720Sfrankho 			pc_unlockfs(fsp);
8862720Sfrankho 		vn_free(vp);
8872720Sfrankho 		kmem_free(pcp, sizeof (struct pcnode));
8882720Sfrankho 		VFS_RELE(PCFSTOVFS(fsp));
8892720Sfrankho 		return;
8902720Sfrankho 	}
8912720Sfrankho 
8920Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
8930Sstevel@tonic-gate 	ASSERT(vp->v_count >= 1);
8940Sstevel@tonic-gate 	if (vp->v_count > 1) {
8950Sstevel@tonic-gate 		vp->v_count--;  /* release our hold from vn_rele */
8960Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
8970Sstevel@tonic-gate 		pc_unlockfs(fsp);
8980Sstevel@tonic-gate 		return;
8990Sstevel@tonic-gate 	}
9000Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	/*
9030Sstevel@tonic-gate 	 * Check again to confirm that no intervening I/O error
9040Sstevel@tonic-gate 	 * with a subsequent pc_diskchanged() call has released
905*2972Sfrankho 	 * the pcnode. If it has then release the vnode as above.
9060Sstevel@tonic-gate 	 */
907*2972Sfrankho 	pcp = VTOPC(vp);
908*2972Sfrankho 	if (pcp == NULL || pcp->pc_flags & PC_INVAL) {
9092720Sfrankho 		if (vn_has_cached_data(vp))
9102720Sfrankho 			(void) pvn_vplist_dirty(vp, (u_offset_t)0,
9112720Sfrankho 			    pcfs_putapage, B_INVAL | B_TRUNC,
9122720Sfrankho 			    (struct cred *)NULL);
913*2972Sfrankho 	}
914*2972Sfrankho 
915*2972Sfrankho 	if (pcp == NULL) {
9160Sstevel@tonic-gate 		vn_free(vp);
9172720Sfrankho 	} else {
9180Sstevel@tonic-gate 		pc_rele(pcp);
9192720Sfrankho 	}
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	if (!error)
9220Sstevel@tonic-gate 		pc_unlockfs(fsp);
9230Sstevel@tonic-gate }
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate /*ARGSUSED*/
9260Sstevel@tonic-gate static int
9270Sstevel@tonic-gate pcfs_lookup(
9280Sstevel@tonic-gate 	struct vnode *dvp,
9290Sstevel@tonic-gate 	char *nm,
9300Sstevel@tonic-gate 	struct vnode **vpp,
9310Sstevel@tonic-gate 	struct pathname *pnp,
9320Sstevel@tonic-gate 	int flags,
9330Sstevel@tonic-gate 	struct vnode *rdir,
9340Sstevel@tonic-gate 	struct cred *cr)
9350Sstevel@tonic-gate {
9360Sstevel@tonic-gate 	struct pcfs *fsp;
9370Sstevel@tonic-gate 	struct pcnode *pcp;
9380Sstevel@tonic-gate 	int error;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	/*
9412720Sfrankho 	 * If the filesystem was umounted by force, return immediately.
9422720Sfrankho 	 */
9432720Sfrankho 	if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
9442720Sfrankho 		return (EIO);
9452720Sfrankho 
9462720Sfrankho 	/*
9470Sstevel@tonic-gate 	 * verify that the dvp is still valid on the disk
9480Sstevel@tonic-gate 	 */
9490Sstevel@tonic-gate 	fsp = VFSTOPCFS(dvp->v_vfsp);
9500Sstevel@tonic-gate 	if (error = pc_verify(fsp))
9510Sstevel@tonic-gate 		return (error);
9520Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
9530Sstevel@tonic-gate 	if (error)
9540Sstevel@tonic-gate 		return (error);
955*2972Sfrankho 	if (VTOPC(dvp) == NULL || VTOPC(dvp)->pc_flags & PC_INVAL) {
9560Sstevel@tonic-gate 		pc_unlockfs(fsp);
9570Sstevel@tonic-gate 		return (EIO);
9580Sstevel@tonic-gate 	}
9590Sstevel@tonic-gate 	/*
9600Sstevel@tonic-gate 	 * Null component name is a synonym for directory being searched.
9610Sstevel@tonic-gate 	 */
9620Sstevel@tonic-gate 	if (*nm == '\0') {
9630Sstevel@tonic-gate 		VN_HOLD(dvp);
9640Sstevel@tonic-gate 		*vpp = dvp;
9650Sstevel@tonic-gate 		pc_unlockfs(fsp);
9660Sstevel@tonic-gate 		return (0);
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 	error = pc_dirlook(VTOPC(dvp), nm, &pcp);
9700Sstevel@tonic-gate 	if (!error) {
9710Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
9720Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
9730Sstevel@tonic-gate 	}
9740Sstevel@tonic-gate 	pc_unlockfs(fsp);
9750Sstevel@tonic-gate 	return (error);
9760Sstevel@tonic-gate }
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate /*ARGSUSED*/
9800Sstevel@tonic-gate static int
9810Sstevel@tonic-gate pcfs_create(
9820Sstevel@tonic-gate 	struct vnode *dvp,
9830Sstevel@tonic-gate 	char *nm,
9840Sstevel@tonic-gate 	struct vattr *vap,
9850Sstevel@tonic-gate 	enum vcexcl exclusive,
9860Sstevel@tonic-gate 	int mode,
9870Sstevel@tonic-gate 	struct vnode **vpp,
9880Sstevel@tonic-gate 	struct cred *cr,
9890Sstevel@tonic-gate 	int flag)
9900Sstevel@tonic-gate {
9910Sstevel@tonic-gate 	int error;
9920Sstevel@tonic-gate 	struct pcnode *pcp;
9930Sstevel@tonic-gate 	struct vnode *vp;
9940Sstevel@tonic-gate 	struct pcfs *fsp;
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	/*
9970Sstevel@tonic-gate 	 * can't create directories. use pcfs_mkdir.
9980Sstevel@tonic-gate 	 * can't create anything other than files.
9990Sstevel@tonic-gate 	 */
10000Sstevel@tonic-gate 	if (vap->va_type == VDIR)
10010Sstevel@tonic-gate 		return (EISDIR);
10020Sstevel@tonic-gate 	else if (vap->va_type != VREG)
10030Sstevel@tonic-gate 		return (EINVAL);
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	pcp = NULL;
10060Sstevel@tonic-gate 	fsp = VFSTOPCFS(dvp->v_vfsp);
10070Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
10080Sstevel@tonic-gate 	if (error)
10090Sstevel@tonic-gate 		return (error);
1010*2972Sfrankho 	if (VTOPC(dvp) == NULL || VTOPC(dvp)->pc_flags & PC_INVAL) {
10110Sstevel@tonic-gate 		pc_unlockfs(fsp);
10120Sstevel@tonic-gate 		return (EIO);
10130Sstevel@tonic-gate 	}
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_BOOTPART) {
10160Sstevel@tonic-gate 		if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
10170Sstevel@tonic-gate 			pc_unlockfs(fsp);
10180Sstevel@tonic-gate 			return (EACCES);
10190Sstevel@tonic-gate 		}
10200Sstevel@tonic-gate 	}
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	if (*nm == '\0') {
10230Sstevel@tonic-gate 		/*
10240Sstevel@tonic-gate 		 * Null component name refers to the directory itself.
10250Sstevel@tonic-gate 		 */
10260Sstevel@tonic-gate 		VN_HOLD(dvp);
10270Sstevel@tonic-gate 		pcp = VTOPC(dvp);
10280Sstevel@tonic-gate 		error = EEXIST;
10290Sstevel@tonic-gate 	} else {
10300Sstevel@tonic-gate 		error = pc_direnter(VTOPC(dvp), nm, vap, &pcp);
10310Sstevel@tonic-gate 	}
10320Sstevel@tonic-gate 	/*
10330Sstevel@tonic-gate 	 * if file exists and this is a nonexclusive create,
10340Sstevel@tonic-gate 	 * check for access permissions
10350Sstevel@tonic-gate 	 */
10360Sstevel@tonic-gate 	if (error == EEXIST) {
10370Sstevel@tonic-gate 		vp = PCTOV(pcp);
10380Sstevel@tonic-gate 		if (exclusive == NONEXCL) {
10390Sstevel@tonic-gate 			if (vp->v_type == VDIR) {
10400Sstevel@tonic-gate 				error = EISDIR;
10410Sstevel@tonic-gate 			} else if (mode) {
10420Sstevel@tonic-gate 				error = pcfs_access(PCTOV(pcp), mode, 0,
10430Sstevel@tonic-gate 					cr);
10440Sstevel@tonic-gate 			} else {
10450Sstevel@tonic-gate 				error = 0;
10460Sstevel@tonic-gate 			}
10470Sstevel@tonic-gate 		}
10480Sstevel@tonic-gate 		if (error) {
10490Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
10500Sstevel@tonic-gate 		} else if ((vp->v_type == VREG) && (vap->va_mask & AT_SIZE) &&
10510Sstevel@tonic-gate 			(vap->va_size == 0)) {
10520Sstevel@tonic-gate 			error = pc_truncate(pcp, 0L);
10530Sstevel@tonic-gate 			if (error)
10540Sstevel@tonic-gate 				VN_RELE(PCTOV(pcp));
10550Sstevel@tonic-gate 		}
10560Sstevel@tonic-gate 	}
10570Sstevel@tonic-gate 	if (error) {
10580Sstevel@tonic-gate 		pc_unlockfs(fsp);
10590Sstevel@tonic-gate 		return (error);
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
10620Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
10630Sstevel@tonic-gate 	pc_unlockfs(fsp);
10640Sstevel@tonic-gate 	return (error);
10650Sstevel@tonic-gate }
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate /*ARGSUSED*/
10680Sstevel@tonic-gate static int
10690Sstevel@tonic-gate pcfs_remove(
10700Sstevel@tonic-gate 	struct vnode *vp,
10710Sstevel@tonic-gate 	char *nm,
10720Sstevel@tonic-gate 	struct cred *cr)
10730Sstevel@tonic-gate {
10740Sstevel@tonic-gate 	struct pcfs *fsp;
10750Sstevel@tonic-gate 	struct pcnode *pcp;
10760Sstevel@tonic-gate 	int error;
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
10790Sstevel@tonic-gate 	if (error = pc_verify(fsp))
10800Sstevel@tonic-gate 		return (error);
10810Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
10820Sstevel@tonic-gate 	if (error)
10830Sstevel@tonic-gate 		return (error);
1084*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
10850Sstevel@tonic-gate 		pc_unlockfs(fsp);
10860Sstevel@tonic-gate 		return (EIO);
10870Sstevel@tonic-gate 	}
10880Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_BOOTPART) {
10890Sstevel@tonic-gate 		if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
10900Sstevel@tonic-gate 			pc_unlockfs(fsp);
10910Sstevel@tonic-gate 			return (EACCES);
10920Sstevel@tonic-gate 		}
10930Sstevel@tonic-gate 	}
10940Sstevel@tonic-gate 	error = pc_dirremove(pcp, nm, (struct vnode *)0, VREG);
10950Sstevel@tonic-gate 	pc_unlockfs(fsp);
10960Sstevel@tonic-gate 	return (error);
10970Sstevel@tonic-gate }
10980Sstevel@tonic-gate 
10990Sstevel@tonic-gate /*
11000Sstevel@tonic-gate  * Rename a file or directory
11010Sstevel@tonic-gate  * This rename is restricted to only rename files within a directory.
11020Sstevel@tonic-gate  * XX should make rename more general
11030Sstevel@tonic-gate  */
11040Sstevel@tonic-gate /*ARGSUSED*/
11050Sstevel@tonic-gate static int
11060Sstevel@tonic-gate pcfs_rename(
11070Sstevel@tonic-gate 	struct vnode *sdvp,		/* old (source) parent vnode */
11080Sstevel@tonic-gate 	char *snm,			/* old (source) entry name */
11090Sstevel@tonic-gate 	struct vnode *tdvp,		/* new (target) parent vnode */
11100Sstevel@tonic-gate 	char *tnm,			/* new (target) entry name */
11110Sstevel@tonic-gate 	struct cred *cr)
11120Sstevel@tonic-gate {
11130Sstevel@tonic-gate 	struct pcfs *fsp;
11140Sstevel@tonic-gate 	struct pcnode *dp;	/* parent pcnode */
11150Sstevel@tonic-gate 	struct pcnode *tdp;
11160Sstevel@tonic-gate 	int error;
11170Sstevel@tonic-gate 
11180Sstevel@tonic-gate 	fsp = VFSTOPCFS(sdvp->v_vfsp);
11190Sstevel@tonic-gate 	if (error = pc_verify(fsp))
11200Sstevel@tonic-gate 		return (error);
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	/*
11230Sstevel@tonic-gate 	 * make sure we can muck with this directory.
11240Sstevel@tonic-gate 	 */
11250Sstevel@tonic-gate 	error = pcfs_access(sdvp, VWRITE, 0, cr);
11260Sstevel@tonic-gate 	if (error) {
11270Sstevel@tonic-gate 		return (error);
11280Sstevel@tonic-gate 	}
11290Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
11300Sstevel@tonic-gate 	if (error)
11310Sstevel@tonic-gate 		return (error);
1132*2972Sfrankho 	if (((dp = VTOPC(sdvp)) == NULL) || ((tdp = VTOPC(tdvp)) == NULL) ||
1133*2972Sfrankho 	    (dp->pc_flags & PC_INVAL) || (tdp->pc_flags & PC_INVAL)) {
11340Sstevel@tonic-gate 		pc_unlockfs(fsp);
11350Sstevel@tonic-gate 		return (EIO);
11360Sstevel@tonic-gate 	}
11370Sstevel@tonic-gate 	error = pc_rename(dp, tdp, snm, tnm);
11380Sstevel@tonic-gate 	pc_unlockfs(fsp);
11390Sstevel@tonic-gate 	return (error);
11400Sstevel@tonic-gate }
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate /*ARGSUSED*/
11430Sstevel@tonic-gate static int
11440Sstevel@tonic-gate pcfs_mkdir(
11450Sstevel@tonic-gate 	struct vnode *dvp,
11460Sstevel@tonic-gate 	char *nm,
11470Sstevel@tonic-gate 	struct vattr *vap,
11480Sstevel@tonic-gate 	struct vnode **vpp,
11490Sstevel@tonic-gate 	struct cred *cr)
11500Sstevel@tonic-gate {
11510Sstevel@tonic-gate 	struct pcfs *fsp;
11520Sstevel@tonic-gate 	struct pcnode *pcp;
11530Sstevel@tonic-gate 	int error;
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 	fsp = VFSTOPCFS(dvp->v_vfsp);
11560Sstevel@tonic-gate 	if (error = pc_verify(fsp))
11570Sstevel@tonic-gate 		return (error);
11580Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
11590Sstevel@tonic-gate 	if (error)
11600Sstevel@tonic-gate 		return (error);
1161*2972Sfrankho 	if (VTOPC(dvp) == NULL || VTOPC(dvp)->pc_flags & PC_INVAL) {
11620Sstevel@tonic-gate 		pc_unlockfs(fsp);
11630Sstevel@tonic-gate 		return (EIO);
11640Sstevel@tonic-gate 	}
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_BOOTPART) {
11670Sstevel@tonic-gate 		if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
11680Sstevel@tonic-gate 			pc_unlockfs(fsp);
11690Sstevel@tonic-gate 			return (EACCES);
11700Sstevel@tonic-gate 		}
11710Sstevel@tonic-gate 	}
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 	error = pc_direnter(VTOPC(dvp), nm, vap, &pcp);
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 	if (!error) {
11760Sstevel@tonic-gate 		pcp -> pc_flags |= PC_EXTERNAL;
11770Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
11780Sstevel@tonic-gate 	} else if (error == EEXIST) {
11790Sstevel@tonic-gate 		VN_RELE(PCTOV(pcp));
11800Sstevel@tonic-gate 	}
11810Sstevel@tonic-gate 	pc_unlockfs(fsp);
11820Sstevel@tonic-gate 	return (error);
11830Sstevel@tonic-gate }
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate /*ARGSUSED*/
11860Sstevel@tonic-gate static int
11870Sstevel@tonic-gate pcfs_rmdir(
11880Sstevel@tonic-gate 	struct vnode *dvp,
11890Sstevel@tonic-gate 	char *nm,
11900Sstevel@tonic-gate 	struct vnode *cdir,
11910Sstevel@tonic-gate 	struct cred *cr)
11920Sstevel@tonic-gate {
11930Sstevel@tonic-gate 	struct pcfs *fsp;
11940Sstevel@tonic-gate 	struct pcnode *pcp;
11950Sstevel@tonic-gate 	int error;
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate 	fsp = VFSTOPCFS(dvp -> v_vfsp);
11980Sstevel@tonic-gate 	if (error = pc_verify(fsp))
11990Sstevel@tonic-gate 		return (error);
12000Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
12010Sstevel@tonic-gate 		return (error);
12020Sstevel@tonic-gate 
1203*2972Sfrankho 	if ((pcp = VTOPC(dvp)) == NULL || pcp->pc_flags & PC_INVAL) {
12040Sstevel@tonic-gate 		pc_unlockfs(fsp);
12050Sstevel@tonic-gate 		return (EIO);
12060Sstevel@tonic-gate 	}
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_BOOTPART) {
12090Sstevel@tonic-gate 		if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
12100Sstevel@tonic-gate 			pc_unlockfs(fsp);
12110Sstevel@tonic-gate 			return (EACCES);
12120Sstevel@tonic-gate 		}
12130Sstevel@tonic-gate 	}
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	error = pc_dirremove(pcp, nm, cdir, VDIR);
12160Sstevel@tonic-gate 	pc_unlockfs(fsp);
12170Sstevel@tonic-gate 	return (error);
12180Sstevel@tonic-gate }
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate /*
12210Sstevel@tonic-gate  * read entries in a directory.
12220Sstevel@tonic-gate  * we must convert pc format to unix format
12230Sstevel@tonic-gate  */
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate /*ARGSUSED*/
12260Sstevel@tonic-gate static int
12270Sstevel@tonic-gate pcfs_readdir(
12280Sstevel@tonic-gate 	struct vnode *dvp,
12290Sstevel@tonic-gate 	struct uio *uiop,
12300Sstevel@tonic-gate 	struct cred *cr,
12310Sstevel@tonic-gate 	int *eofp)
12320Sstevel@tonic-gate {
12330Sstevel@tonic-gate 	struct pcnode *pcp;
12340Sstevel@tonic-gate 	struct pcfs *fsp;
12350Sstevel@tonic-gate 	struct pcdir *ep;
12360Sstevel@tonic-gate 	struct buf *bp = NULL;
12370Sstevel@tonic-gate 	offset_t offset;
12380Sstevel@tonic-gate 	int boff;
12390Sstevel@tonic-gate 	struct pc_dirent lbp;
12400Sstevel@tonic-gate 	struct pc_dirent *ld = &lbp;
12410Sstevel@tonic-gate 	int error;
12420Sstevel@tonic-gate 
12432720Sfrankho 	/*
12442720Sfrankho 	 * If the filesystem was umounted by force, return immediately.
12452720Sfrankho 	 */
12462720Sfrankho 	if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
12472720Sfrankho 		return (EIO);
12482720Sfrankho 
12490Sstevel@tonic-gate 	if ((uiop->uio_iovcnt != 1) ||
12500Sstevel@tonic-gate 	    (uiop->uio_loffset % sizeof (struct pcdir)) != 0) {
12510Sstevel@tonic-gate 		return (EINVAL);
12520Sstevel@tonic-gate 	}
12530Sstevel@tonic-gate 	fsp = VFSTOPCFS(dvp->v_vfsp);
12540Sstevel@tonic-gate 	/*
12550Sstevel@tonic-gate 	 * verify that the dp is still valid on the disk
12560Sstevel@tonic-gate 	 */
12570Sstevel@tonic-gate 	if (error = pc_verify(fsp)) {
12580Sstevel@tonic-gate 		return (error);
12590Sstevel@tonic-gate 	}
12600Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
12610Sstevel@tonic-gate 	if (error)
12620Sstevel@tonic-gate 		return (error);
1263*2972Sfrankho 	if ((pcp = VTOPC(dvp)) == NULL || pcp->pc_flags & PC_INVAL) {
12640Sstevel@tonic-gate 		pc_unlockfs(fsp);
12650Sstevel@tonic-gate 		return (EIO);
12660Sstevel@tonic-gate 	}
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	bzero(ld, sizeof (*ld));
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate 	if (eofp != NULL)
12710Sstevel@tonic-gate 		*eofp = 0;
12720Sstevel@tonic-gate 	offset = uiop->uio_loffset;
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 	if (dvp->v_flag & VROOT) {
12750Sstevel@tonic-gate 		/*
12760Sstevel@tonic-gate 		 * kludge up entries for "." and ".." in the root.
12770Sstevel@tonic-gate 		 */
12780Sstevel@tonic-gate 		if (offset == 0) {
12790Sstevel@tonic-gate 			(void) strcpy(ld->d_name, ".");
12800Sstevel@tonic-gate 			ld->d_reclen = DIRENT64_RECLEN(1);
12810Sstevel@tonic-gate 			ld->d_off = (off64_t)sizeof (struct pcdir);
12820Sstevel@tonic-gate 			ld->d_ino = (ino64_t)UINT_MAX;
12830Sstevel@tonic-gate 			if (ld->d_reclen > uiop->uio_resid) {
12840Sstevel@tonic-gate 				pc_unlockfs(fsp);
12850Sstevel@tonic-gate 				return (ENOSPC);
12860Sstevel@tonic-gate 			}
12870Sstevel@tonic-gate 			(void) uiomove(ld, ld->d_reclen, UIO_READ, uiop);
12880Sstevel@tonic-gate 			uiop->uio_loffset = ld->d_off;
12890Sstevel@tonic-gate 			offset = uiop->uio_loffset;
12900Sstevel@tonic-gate 		}
12910Sstevel@tonic-gate 		if (offset == sizeof (struct pcdir)) {
12920Sstevel@tonic-gate 			(void) strcpy(ld->d_name, "..");
12930Sstevel@tonic-gate 			ld->d_reclen = DIRENT64_RECLEN(2);
12940Sstevel@tonic-gate 			if (ld->d_reclen > uiop->uio_resid) {
12950Sstevel@tonic-gate 				pc_unlockfs(fsp);
12960Sstevel@tonic-gate 				return (ENOSPC);
12970Sstevel@tonic-gate 			}
12980Sstevel@tonic-gate 			ld->d_off = (off64_t)(uiop->uio_loffset +
12990Sstevel@tonic-gate 			    sizeof (struct pcdir));
13000Sstevel@tonic-gate 			ld->d_ino = (ino64_t)UINT_MAX;
13010Sstevel@tonic-gate 			(void) uiomove(ld, ld->d_reclen, UIO_READ, uiop);
13020Sstevel@tonic-gate 			uiop->uio_loffset = ld->d_off;
13030Sstevel@tonic-gate 			offset = uiop->uio_loffset;
13040Sstevel@tonic-gate 		}
13050Sstevel@tonic-gate 		offset -= 2 * sizeof (struct pcdir);
13060Sstevel@tonic-gate 		/* offset now has the real offset value into directory file */
13070Sstevel@tonic-gate 	}
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 	for (;;) {
13100Sstevel@tonic-gate 		boff = pc_blkoff(fsp, offset);
13110Sstevel@tonic-gate 		if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
13120Sstevel@tonic-gate 			if (bp != NULL) {
13130Sstevel@tonic-gate 				brelse(bp);
13140Sstevel@tonic-gate 				bp = NULL;
13150Sstevel@tonic-gate 			}
13160Sstevel@tonic-gate 			error = pc_blkatoff(pcp, offset, &bp, &ep);
13170Sstevel@tonic-gate 			if (error) {
13180Sstevel@tonic-gate 				if (error == ENOENT) {
13190Sstevel@tonic-gate 					error = 0;
13200Sstevel@tonic-gate 					if (eofp)
13210Sstevel@tonic-gate 						*eofp = 1;
13220Sstevel@tonic-gate 				}
13230Sstevel@tonic-gate 				break;
13240Sstevel@tonic-gate 			}
13250Sstevel@tonic-gate 		}
13260Sstevel@tonic-gate 		if (ep->pcd_filename[0] == PCD_UNUSED) {
13270Sstevel@tonic-gate 			if (eofp)
13280Sstevel@tonic-gate 				*eofp = 1;
13290Sstevel@tonic-gate 			break;
13300Sstevel@tonic-gate 		}
13310Sstevel@tonic-gate 		/*
13320Sstevel@tonic-gate 		 * Don't display label because it may contain funny characters.
13330Sstevel@tonic-gate 		 */
13340Sstevel@tonic-gate 		if (ep->pcd_filename[0] == PCD_ERASED) {
13350Sstevel@tonic-gate 			uiop->uio_loffset += sizeof (struct pcdir);
13360Sstevel@tonic-gate 			offset += sizeof (struct pcdir);
13370Sstevel@tonic-gate 			ep++;
13380Sstevel@tonic-gate 			continue;
13390Sstevel@tonic-gate 		}
13400Sstevel@tonic-gate 		if (PCDL_IS_LFN(ep)) {
13410Sstevel@tonic-gate 			if (pc_read_long_fn(dvp, uiop, ld, &ep, &offset, &bp) !=
13420Sstevel@tonic-gate 			    0)
13430Sstevel@tonic-gate 				break;
13440Sstevel@tonic-gate 			continue;
13450Sstevel@tonic-gate 		}
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate 		if (pc_read_short_fn(dvp, uiop, ld, &ep, &offset, &bp) != 0)
13480Sstevel@tonic-gate 			break;
13490Sstevel@tonic-gate 	}
13500Sstevel@tonic-gate 	if (bp)
13510Sstevel@tonic-gate 		brelse(bp);
13520Sstevel@tonic-gate 	pc_unlockfs(fsp);
13530Sstevel@tonic-gate 	return (error);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate /*
13580Sstevel@tonic-gate  * Called from pvn_getpages or pcfs_getpage to get a particular page.
13590Sstevel@tonic-gate  * When we are called the pcfs is already locked.
13600Sstevel@tonic-gate  */
13610Sstevel@tonic-gate /*ARGSUSED*/
13620Sstevel@tonic-gate static int
13630Sstevel@tonic-gate pcfs_getapage(
13640Sstevel@tonic-gate 	struct vnode *vp,
13650Sstevel@tonic-gate 	u_offset_t off,
13660Sstevel@tonic-gate 	size_t len,
13670Sstevel@tonic-gate 	uint_t *protp,
13680Sstevel@tonic-gate 	page_t *pl[],		/* NULL if async IO is requested */
13690Sstevel@tonic-gate 	size_t plsz,
13700Sstevel@tonic-gate 	struct seg *seg,
13710Sstevel@tonic-gate 	caddr_t addr,
13720Sstevel@tonic-gate 	enum seg_rw rw,
13730Sstevel@tonic-gate 	struct cred *cr)
13740Sstevel@tonic-gate {
13750Sstevel@tonic-gate 	struct pcnode *pcp;
13760Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
13770Sstevel@tonic-gate 	struct vnode *devvp;
13780Sstevel@tonic-gate 	page_t *pp;
13790Sstevel@tonic-gate 	page_t *pagefound;
13800Sstevel@tonic-gate 	int err;
13810Sstevel@tonic-gate 
13822720Sfrankho 	/*
13832720Sfrankho 	 * If the filesystem was umounted by force, return immediately.
13842720Sfrankho 	 */
13852720Sfrankho 	if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
13862720Sfrankho 		return (EIO);
13872720Sfrankho 
13880Sstevel@tonic-gate 	PC_DPRINTF3(5, "pcfs_getapage: vp=%p off=%lld len=%lu\n",
13890Sstevel@tonic-gate 	    (void *)vp, off, len);
13900Sstevel@tonic-gate 
1391*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL)
13920Sstevel@tonic-gate 		return (EIO);
13930Sstevel@tonic-gate 	devvp = fsp->pcfs_devvp;
13940Sstevel@tonic-gate 
13950Sstevel@tonic-gate 	/* pcfs doesn't do readaheads */
13960Sstevel@tonic-gate 	if (pl == NULL)
13970Sstevel@tonic-gate 		return (0);
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 	pl[0] = NULL;
14000Sstevel@tonic-gate 	err = 0;
14010Sstevel@tonic-gate 	/*
14020Sstevel@tonic-gate 	 * If the accessed time on the pcnode has not already been
14030Sstevel@tonic-gate 	 * set elsewhere (e.g. for read/setattr) we set the time now.
14040Sstevel@tonic-gate 	 * This gives us approximate modified times for mmap'ed files
14050Sstevel@tonic-gate 	 * which are accessed via loads in the user address space.
14060Sstevel@tonic-gate 	 */
14070Sstevel@tonic-gate 	if ((pcp->pc_flags & PC_ACC) == 0 &&
14080Sstevel@tonic-gate 	    ((fsp->pcfs_vfs->vfs_flag & VFS_RDONLY) == 0)) {
14090Sstevel@tonic-gate 		pcp->pc_flags |= PC_ACC;
14100Sstevel@tonic-gate 		pc_mark_acc(pcp);
14110Sstevel@tonic-gate 	}
14120Sstevel@tonic-gate reread:
14130Sstevel@tonic-gate 	if ((pagefound = page_exists(vp, off)) == NULL) {
14140Sstevel@tonic-gate 		/*
14150Sstevel@tonic-gate 		 * Need to really do disk IO to get the page(s).
14160Sstevel@tonic-gate 		 */
14170Sstevel@tonic-gate 		struct buf *bp;
14180Sstevel@tonic-gate 		daddr_t lbn, bn;
14190Sstevel@tonic-gate 		u_offset_t io_off;
14200Sstevel@tonic-gate 		size_t io_len;
14210Sstevel@tonic-gate 		u_offset_t lbnoff, xferoffset;
14220Sstevel@tonic-gate 		u_offset_t pgoff;
14230Sstevel@tonic-gate 		uint_t	xfersize;
14240Sstevel@tonic-gate 		int err1;
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 		lbn = pc_lblkno(fsp, off);
14270Sstevel@tonic-gate 		lbnoff = off & ~(fsp->pcfs_clsize - 1);
14280Sstevel@tonic-gate 		xferoffset = off & ~(fsp->pcfs_secsize - 1);
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 		pp = pvn_read_kluster(vp, off, seg, addr, &io_off, &io_len,
14310Sstevel@tonic-gate 		    off, (size_t)MIN(pc_blksize(fsp, pcp, off), PAGESIZE), 0);
14320Sstevel@tonic-gate 		if (pp == NULL)
14330Sstevel@tonic-gate 			/*
14340Sstevel@tonic-gate 			 * XXX - If pcfs is made MT-hot, this should go
14350Sstevel@tonic-gate 			 * back to reread.
14360Sstevel@tonic-gate 			 */
14370Sstevel@tonic-gate 			panic("pcfs_getapage pvn_read_kluster");
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 		for (pgoff = 0; pgoff < PAGESIZE && xferoffset < pcp->pc_size;
14400Sstevel@tonic-gate 		    pgoff += xfersize,
14410Sstevel@tonic-gate 		    lbn +=  howmany(xfersize, fsp->pcfs_clsize),
14420Sstevel@tonic-gate 		    lbnoff += xfersize, xferoffset += xfersize) {
14430Sstevel@tonic-gate 			/*
14440Sstevel@tonic-gate 			 * read as many contiguous blocks as possible to
14450Sstevel@tonic-gate 			 * fill this page
14460Sstevel@tonic-gate 			 */
14470Sstevel@tonic-gate 			xfersize = PAGESIZE - pgoff;
14480Sstevel@tonic-gate 			err1 = pc_bmap(pcp, lbn, &bn, &xfersize);
14490Sstevel@tonic-gate 			if (err1) {
14500Sstevel@tonic-gate 				PC_DPRINTF1(1, "pc_getapage err=%d", err1);
14510Sstevel@tonic-gate 				err = err1;
14520Sstevel@tonic-gate 				goto out;
14530Sstevel@tonic-gate 			}
14540Sstevel@tonic-gate 			bp = pageio_setup(pp, xfersize, devvp, B_READ);
14550Sstevel@tonic-gate 			bp->b_edev = devvp->v_rdev;
14560Sstevel@tonic-gate 			bp->b_dev = cmpdev(devvp->v_rdev);
14570Sstevel@tonic-gate 			bp->b_blkno = bn +
14580Sstevel@tonic-gate 			    /* add a sector offset within the cluster */
14590Sstevel@tonic-gate 			    /* when the clustersize > PAGESIZE */
14600Sstevel@tonic-gate 			    (xferoffset - lbnoff) / fsp->pcfs_secsize;
1461973Selowe 			bp->b_un.b_addr = (caddr_t)(uintptr_t)pgoff;
14620Sstevel@tonic-gate 			bp->b_file = vp;
14630Sstevel@tonic-gate 			bp->b_offset = (offset_t)(off + pgoff);
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 			(void) bdev_strategy(bp);
14660Sstevel@tonic-gate 
14670Sstevel@tonic-gate 			lwp_stat_update(LWP_STAT_INBLK, 1);
14680Sstevel@tonic-gate 
14690Sstevel@tonic-gate 			if (err == 0)
14700Sstevel@tonic-gate 				err = biowait(bp);
14710Sstevel@tonic-gate 			else
14720Sstevel@tonic-gate 				(void) biowait(bp);
14730Sstevel@tonic-gate 			pageio_done(bp);
14740Sstevel@tonic-gate 			if (err)
14750Sstevel@tonic-gate 				goto out;
14760Sstevel@tonic-gate 		}
14770Sstevel@tonic-gate 		if (pgoff < PAGESIZE) {
14780Sstevel@tonic-gate 			pagezero(pp->p_prev, pgoff, PAGESIZE - pgoff);
14790Sstevel@tonic-gate 		}
14800Sstevel@tonic-gate 		pvn_plist_init(pp, pl, plsz, off, io_len, rw);
14810Sstevel@tonic-gate 	}
14820Sstevel@tonic-gate out:
14830Sstevel@tonic-gate 	if (err) {
14840Sstevel@tonic-gate 		if (pp != NULL)
14850Sstevel@tonic-gate 			pvn_read_done(pp, B_ERROR);
14860Sstevel@tonic-gate 		return (err);
14870Sstevel@tonic-gate 	}
14880Sstevel@tonic-gate 
14890Sstevel@tonic-gate 	if (pagefound) {
14900Sstevel@tonic-gate 		/*
14910Sstevel@tonic-gate 		 * Page exists in the cache, acquire the "shared"
14920Sstevel@tonic-gate 		 * lock.  If this fails, go back to reread.
14930Sstevel@tonic-gate 		 */
14940Sstevel@tonic-gate 		if ((pp = page_lookup(vp, off, SE_SHARED)) == NULL) {
14950Sstevel@tonic-gate 			goto reread;
14960Sstevel@tonic-gate 		}
14970Sstevel@tonic-gate 		pl[0] = pp;
14980Sstevel@tonic-gate 		pl[1] = NULL;
14990Sstevel@tonic-gate 	}
15000Sstevel@tonic-gate 	return (err);
15010Sstevel@tonic-gate }
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate /*
15040Sstevel@tonic-gate  * Return all the pages from [off..off+len] in given file
15050Sstevel@tonic-gate  */
15060Sstevel@tonic-gate static int
15070Sstevel@tonic-gate pcfs_getpage(
15080Sstevel@tonic-gate 	struct vnode *vp,
15090Sstevel@tonic-gate 	offset_t off,
15100Sstevel@tonic-gate 	size_t len,
15110Sstevel@tonic-gate 	uint_t *protp,
15120Sstevel@tonic-gate 	page_t *pl[],
15130Sstevel@tonic-gate 	size_t plsz,
15140Sstevel@tonic-gate 	struct seg *seg,
15150Sstevel@tonic-gate 	caddr_t addr,
15160Sstevel@tonic-gate 	enum seg_rw rw,
15170Sstevel@tonic-gate 	struct cred *cr)
15180Sstevel@tonic-gate {
15190Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
15200Sstevel@tonic-gate 	int err;
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate 	PC_DPRINTF0(6, "pcfs_getpage\n");
15230Sstevel@tonic-gate 	if (err = pc_verify(fsp))
15240Sstevel@tonic-gate 		return (err);
15250Sstevel@tonic-gate 	if (vp->v_flag & VNOMAP)
15260Sstevel@tonic-gate 		return (ENOSYS);
15270Sstevel@tonic-gate 	ASSERT(off <= UINT32_MAX);
15280Sstevel@tonic-gate 	err = pc_lockfs(fsp, 0, 0);
15290Sstevel@tonic-gate 	if (err)
15300Sstevel@tonic-gate 		return (err);
15310Sstevel@tonic-gate 	if (protp != NULL)
15320Sstevel@tonic-gate 		*protp = PROT_ALL;
15330Sstevel@tonic-gate 
15340Sstevel@tonic-gate 	ASSERT((off & PAGEOFFSET) == 0);
15350Sstevel@tonic-gate 	if (len <= PAGESIZE) {
15360Sstevel@tonic-gate 		err = pcfs_getapage(vp, off, len, protp, pl,
15370Sstevel@tonic-gate 		    plsz, seg, addr, rw, cr);
15380Sstevel@tonic-gate 	} else {
15390Sstevel@tonic-gate 		err = pvn_getpages(pcfs_getapage, vp, off,
15400Sstevel@tonic-gate 		    len, protp, pl, plsz, seg, addr, rw, cr);
15410Sstevel@tonic-gate 	}
15420Sstevel@tonic-gate 	pc_unlockfs(fsp);
15430Sstevel@tonic-gate 	return (err);
15440Sstevel@tonic-gate }
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate /*
15480Sstevel@tonic-gate  * Flags are composed of {B_INVAL, B_FREE, B_DONTNEED, B_FORCE}
15490Sstevel@tonic-gate  * If len == 0, do from off to EOF.
15500Sstevel@tonic-gate  *
15510Sstevel@tonic-gate  * The normal cases should be len == 0 & off == 0 (entire vp list),
15520Sstevel@tonic-gate  * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE
15530Sstevel@tonic-gate  * (from pageout).
15540Sstevel@tonic-gate  *
15550Sstevel@tonic-gate  */
15560Sstevel@tonic-gate /*ARGSUSED*/
15570Sstevel@tonic-gate static int
15580Sstevel@tonic-gate pcfs_putpage(
15590Sstevel@tonic-gate 	struct vnode *vp,
15600Sstevel@tonic-gate 	offset_t off,
15610Sstevel@tonic-gate 	size_t len,
15620Sstevel@tonic-gate 	int flags,
15630Sstevel@tonic-gate 	struct cred *cr)
15640Sstevel@tonic-gate {
15650Sstevel@tonic-gate 	struct pcnode *pcp;
15660Sstevel@tonic-gate 	page_t *pp;
15670Sstevel@tonic-gate 	struct pcfs *fsp;
15680Sstevel@tonic-gate 	u_offset_t io_off;
15690Sstevel@tonic-gate 	size_t io_len;
15700Sstevel@tonic-gate 	offset_t eoff;
15710Sstevel@tonic-gate 	int err;
15720Sstevel@tonic-gate 
15732720Sfrankho 	/*
15742720Sfrankho 	 * If the filesystem was umounted by force, return immediately.
15752720Sfrankho 	 */
15762720Sfrankho 	if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
15772720Sfrankho 		return (EIO);
15782720Sfrankho 
15790Sstevel@tonic-gate 	PC_DPRINTF1(6, "pcfs_putpage vp=0x%p\n", (void *)vp);
15800Sstevel@tonic-gate 	if (vp->v_flag & VNOMAP)
15810Sstevel@tonic-gate 		return (ENOSYS);
15820Sstevel@tonic-gate 
15830Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
15840Sstevel@tonic-gate 
15850Sstevel@tonic-gate 	if (err = pc_verify(fsp))
15860Sstevel@tonic-gate 		return (err);
15870Sstevel@tonic-gate 	if ((pcp = VTOPC(vp)) == NULL) {
15880Sstevel@tonic-gate 		PC_DPRINTF1(3, "pcfs_putpage NULL vp=0x%p\n", (void *)vp);
15890Sstevel@tonic-gate 		return (EIO);
15900Sstevel@tonic-gate 	}
1591*2972Sfrankho 	if (pcp->pc_flags & PC_INVAL)
1592*2972Sfrankho 		return (EIO);
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 	if (curproc == proc_pageout) {
15950Sstevel@tonic-gate 		/*
15960Sstevel@tonic-gate 		 * XXX - This is a quick hack to avoid blocking
15970Sstevel@tonic-gate 		 * pageout. Also to avoid pcfs_getapage deadlocking
15980Sstevel@tonic-gate 		 * with putpage when memory is running out,
15990Sstevel@tonic-gate 		 * since we only have one global lock and we don't
16000Sstevel@tonic-gate 		 * support async putpage.
16010Sstevel@tonic-gate 		 * It should be fixed someday.
16020Sstevel@tonic-gate 		 *
16030Sstevel@tonic-gate 		 * Interestingly, this used to be a test of NOMEMWAIT().
16040Sstevel@tonic-gate 		 * We only ever got here once pcfs started supporting
16050Sstevel@tonic-gate 		 * NFS sharing, and then only because the NFS server
16060Sstevel@tonic-gate 		 * threads seem to do writes in sched's process context.
16070Sstevel@tonic-gate 		 * Since everyone else seems to just care about pageout,
16080Sstevel@tonic-gate 		 * the test was changed to look for pageout directly.
16090Sstevel@tonic-gate 		 */
16100Sstevel@tonic-gate 		return (ENOMEM);
16110Sstevel@tonic-gate 	}
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate 	ASSERT(off <= UINT32_MAX);
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate 	flags &= ~B_ASYNC;	/* XXX should fix this later */
16160Sstevel@tonic-gate 
16170Sstevel@tonic-gate 	err = pc_lockfs(fsp, 0, 0);
16180Sstevel@tonic-gate 	if (err)
16190Sstevel@tonic-gate 		return (err);
16200Sstevel@tonic-gate 	if (!vn_has_cached_data(vp) || off >= pcp->pc_size) {
16210Sstevel@tonic-gate 		pc_unlockfs(fsp);
16220Sstevel@tonic-gate 		return (0);
16230Sstevel@tonic-gate 	}
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate 	if (len == 0) {
16260Sstevel@tonic-gate 		/*
16270Sstevel@tonic-gate 		 * Search the entire vp list for pages >= off
16280Sstevel@tonic-gate 		 */
16290Sstevel@tonic-gate 		err = pvn_vplist_dirty(vp, off,
16300Sstevel@tonic-gate 		    pcfs_putapage, flags, cr);
16310Sstevel@tonic-gate 	} else {
16320Sstevel@tonic-gate 		eoff = off + len;
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 		for (io_off = off; io_off < eoff &&
16350Sstevel@tonic-gate 		    io_off < pcp->pc_size; io_off += io_len) {
16360Sstevel@tonic-gate 			/*
16370Sstevel@tonic-gate 			 * If we are not invalidating, synchronously
16380Sstevel@tonic-gate 			 * freeing or writing pages use the routine
16390Sstevel@tonic-gate 			 * page_lookup_nowait() to prevent reclaiming
16400Sstevel@tonic-gate 			 * them from the free list.
16410Sstevel@tonic-gate 			 */
16420Sstevel@tonic-gate 			if ((flags & B_INVAL) || ((flags & B_ASYNC) == 0)) {
16430Sstevel@tonic-gate 				pp = page_lookup(vp, io_off,
16440Sstevel@tonic-gate 					(flags & (B_INVAL | B_FREE)) ?
16450Sstevel@tonic-gate 					    SE_EXCL : SE_SHARED);
16460Sstevel@tonic-gate 			} else {
16470Sstevel@tonic-gate 				pp = page_lookup_nowait(vp, io_off,
16480Sstevel@tonic-gate 					(flags & B_FREE) ? SE_EXCL : SE_SHARED);
16490Sstevel@tonic-gate 			}
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 			if (pp == NULL || pvn_getdirty(pp, flags) == 0)
16520Sstevel@tonic-gate 				io_len = PAGESIZE;
16530Sstevel@tonic-gate 			else {
16540Sstevel@tonic-gate 				err = pcfs_putapage(vp, pp, &io_off, &io_len,
16550Sstevel@tonic-gate 					flags, cr);
16560Sstevel@tonic-gate 				if (err != 0)
16570Sstevel@tonic-gate 					break;
16580Sstevel@tonic-gate 				/*
16590Sstevel@tonic-gate 				 * "io_off" and "io_len" are returned as
16600Sstevel@tonic-gate 				 * the range of pages we actually wrote.
16610Sstevel@tonic-gate 				 * This allows us to skip ahead more quickly
16620Sstevel@tonic-gate 				 * since several pages may've been dealt
16630Sstevel@tonic-gate 				 * with by this iteration of the loop.
16640Sstevel@tonic-gate 				 */
16650Sstevel@tonic-gate 			}
16660Sstevel@tonic-gate 		}
16670Sstevel@tonic-gate 	}
16680Sstevel@tonic-gate 	if (err == 0 && (flags & B_INVAL) &&
16690Sstevel@tonic-gate 	    off == 0 && len == 0 && vn_has_cached_data(vp)) {
16700Sstevel@tonic-gate 		/*
16710Sstevel@tonic-gate 		 * If doing "invalidation", make sure that
16720Sstevel@tonic-gate 		 * all pages on the vnode list are actually
16730Sstevel@tonic-gate 		 * gone.
16740Sstevel@tonic-gate 		 */
16750Sstevel@tonic-gate 		cmn_err(CE_PANIC,
16760Sstevel@tonic-gate 			"pcfs_putpage: B_INVAL, pages not gone");
16770Sstevel@tonic-gate 	} else if (err) {
16780Sstevel@tonic-gate 		PC_DPRINTF1(1, "pcfs_putpage err=%d\n", err);
16790Sstevel@tonic-gate 	}
16800Sstevel@tonic-gate 	pc_unlockfs(fsp);
16810Sstevel@tonic-gate 	return (err);
16820Sstevel@tonic-gate }
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate /*
16850Sstevel@tonic-gate  * Write out a single page, possibly klustering adjacent dirty pages.
16860Sstevel@tonic-gate  */
16870Sstevel@tonic-gate /*ARGSUSED*/
16880Sstevel@tonic-gate int
16890Sstevel@tonic-gate pcfs_putapage(
16900Sstevel@tonic-gate 	struct vnode *vp,
16910Sstevel@tonic-gate 	page_t *pp,
16920Sstevel@tonic-gate 	u_offset_t *offp,
16930Sstevel@tonic-gate 	size_t *lenp,
16940Sstevel@tonic-gate 	int flags,
16950Sstevel@tonic-gate 	struct cred *cr)
16960Sstevel@tonic-gate {
16970Sstevel@tonic-gate 	struct pcnode *pcp;
16980Sstevel@tonic-gate 	struct pcfs *fsp;
16990Sstevel@tonic-gate 	struct vnode *devvp;
17000Sstevel@tonic-gate 	size_t io_len;
17010Sstevel@tonic-gate 	daddr_t bn;
17020Sstevel@tonic-gate 	u_offset_t lbn, lbnoff, xferoffset;
17030Sstevel@tonic-gate 	uint_t pgoff, xfersize;
17040Sstevel@tonic-gate 	int err = 0;
17050Sstevel@tonic-gate 	u_offset_t io_off;
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	pcp = VTOPC(vp);
17080Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
17090Sstevel@tonic-gate 	devvp = fsp->pcfs_devvp;
17100Sstevel@tonic-gate 
17110Sstevel@tonic-gate 	/*
17120Sstevel@tonic-gate 	 * If the modified time on the inode has not already been
17130Sstevel@tonic-gate 	 * set elsewhere (e.g. for write/setattr) and this is not
17140Sstevel@tonic-gate 	 * a call from msync (B_FORCE) we set the time now.
17150Sstevel@tonic-gate 	 * This gives us approximate modified times for mmap'ed files
17160Sstevel@tonic-gate 	 * which are modified via stores in the user address space.
17170Sstevel@tonic-gate 	 */
17180Sstevel@tonic-gate 	if ((pcp->pc_flags & PC_MOD) == 0 || (flags & B_FORCE)) {
17190Sstevel@tonic-gate 		pcp->pc_flags |= PC_MOD;
17200Sstevel@tonic-gate 		pc_mark_mod(pcp);
17210Sstevel@tonic-gate 	}
17220Sstevel@tonic-gate 	pp = pvn_write_kluster(vp, pp, &io_off, &io_len, pp->p_offset,
17230Sstevel@tonic-gate 	    PAGESIZE, flags);
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_IRRECOV) {
17260Sstevel@tonic-gate 		goto out;
17270Sstevel@tonic-gate 	}
17280Sstevel@tonic-gate 
17290Sstevel@tonic-gate 	PC_DPRINTF1(7, "pc_putpage writing dirty page off=%llu\n", io_off);
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate 	lbn = pc_lblkno(fsp, io_off);
17320Sstevel@tonic-gate 	lbnoff = io_off & ~(fsp->pcfs_clsize - 1);
17330Sstevel@tonic-gate 	xferoffset = io_off & ~(fsp->pcfs_secsize - 1);
17340Sstevel@tonic-gate 
17350Sstevel@tonic-gate 	for (pgoff = 0; pgoff < io_len && xferoffset < pcp->pc_size;
17360Sstevel@tonic-gate 	    pgoff += xfersize,
17370Sstevel@tonic-gate 	    lbn += howmany(xfersize, fsp->pcfs_clsize),
17380Sstevel@tonic-gate 	    lbnoff += xfersize, xferoffset += xfersize) {
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 		struct buf *bp;
17410Sstevel@tonic-gate 		int err1;
17420Sstevel@tonic-gate 
17430Sstevel@tonic-gate 		/*
17440Sstevel@tonic-gate 		 * write as many contiguous blocks as possible from this page
17450Sstevel@tonic-gate 		 */
17460Sstevel@tonic-gate 		xfersize = io_len - pgoff;
17470Sstevel@tonic-gate 		err1 = pc_bmap(pcp, (daddr_t)lbn, &bn, &xfersize);
17480Sstevel@tonic-gate 		if (err1) {
17490Sstevel@tonic-gate 			err = err1;
17500Sstevel@tonic-gate 			goto out;
17510Sstevel@tonic-gate 		}
17520Sstevel@tonic-gate 		bp = pageio_setup(pp, xfersize, devvp, B_WRITE | flags);
17530Sstevel@tonic-gate 		bp->b_edev = devvp->v_rdev;
17540Sstevel@tonic-gate 		bp->b_dev = cmpdev(devvp->v_rdev);
17550Sstevel@tonic-gate 		bp->b_blkno = bn +
17560Sstevel@tonic-gate 		    /* add a sector offset within the cluster */
17570Sstevel@tonic-gate 		    /* when the clustersize > PAGESIZE */
17580Sstevel@tonic-gate 		    (xferoffset - lbnoff) / fsp->pcfs_secsize;
17590Sstevel@tonic-gate 		bp->b_un.b_addr = (caddr_t)(uintptr_t)pgoff;
17600Sstevel@tonic-gate 		bp->b_file = vp;
17610Sstevel@tonic-gate 		bp->b_offset = (offset_t)(io_off + pgoff);
17620Sstevel@tonic-gate 
17630Sstevel@tonic-gate 		(void) bdev_strategy(bp);
17640Sstevel@tonic-gate 
17650Sstevel@tonic-gate 		lwp_stat_update(LWP_STAT_OUBLK, 1);
17660Sstevel@tonic-gate 
17670Sstevel@tonic-gate 		if (err == 0)
17680Sstevel@tonic-gate 			err = biowait(bp);
17690Sstevel@tonic-gate 		else
17700Sstevel@tonic-gate 			(void) biowait(bp);
17710Sstevel@tonic-gate 		pageio_done(bp);
17720Sstevel@tonic-gate 	}
17730Sstevel@tonic-gate 	pvn_write_done(pp, ((err) ? B_ERROR : 0) | B_WRITE | flags);
17740Sstevel@tonic-gate 	pp = NULL;
17750Sstevel@tonic-gate 
17760Sstevel@tonic-gate out:
17770Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && pp != NULL) {
17780Sstevel@tonic-gate 		pvn_write_done(pp, B_WRITE | flags);
17790Sstevel@tonic-gate 	} else if (err != 0 && pp != NULL) {
17800Sstevel@tonic-gate 		pvn_write_done(pp, B_ERROR | B_WRITE | flags);
17810Sstevel@tonic-gate 	}
17820Sstevel@tonic-gate 
17830Sstevel@tonic-gate 	if (offp)
17840Sstevel@tonic-gate 		*offp = io_off;
17850Sstevel@tonic-gate 	if (lenp)
17860Sstevel@tonic-gate 		*lenp = io_len;
17870Sstevel@tonic-gate 		PC_DPRINTF4(4, "pcfs_putapage: vp=%p pp=%p off=%lld len=%lu\n",
17880Sstevel@tonic-gate 		    (void *)vp, (void *)pp, io_off, io_len);
17890Sstevel@tonic-gate 	if (err) {
17900Sstevel@tonic-gate 		PC_DPRINTF1(1, "pcfs_putapage err=%d", err);
17910Sstevel@tonic-gate 	}
17920Sstevel@tonic-gate 	return (err);
17930Sstevel@tonic-gate }
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate /*ARGSUSED*/
17960Sstevel@tonic-gate static int
17970Sstevel@tonic-gate pcfs_map(
17980Sstevel@tonic-gate 	struct vnode *vp,
17990Sstevel@tonic-gate 	offset_t off,
18000Sstevel@tonic-gate 	struct as *as,
18010Sstevel@tonic-gate 	caddr_t *addrp,
18020Sstevel@tonic-gate 	size_t len,
18030Sstevel@tonic-gate 	uchar_t prot,
18040Sstevel@tonic-gate 	uchar_t maxprot,
18050Sstevel@tonic-gate 	uint_t flags,
18060Sstevel@tonic-gate 	struct cred *cr)
18070Sstevel@tonic-gate {
18080Sstevel@tonic-gate 	struct segvn_crargs vn_a;
18090Sstevel@tonic-gate 	int error;
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 	PC_DPRINTF0(6, "pcfs_map\n");
18120Sstevel@tonic-gate 	if (vp->v_flag & VNOMAP)
18130Sstevel@tonic-gate 		return (ENOSYS);
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 	if (off > UINT32_MAX || off + len > UINT32_MAX)
18160Sstevel@tonic-gate 		return (ENXIO);
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate 	as_rangelock(as);
18190Sstevel@tonic-gate 	if ((flags & MAP_FIXED) == 0) {
18200Sstevel@tonic-gate 		map_addr(addrp, len, off, 1, flags);
18210Sstevel@tonic-gate 		if (*addrp == NULL) {
18220Sstevel@tonic-gate 			as_rangeunlock(as);
18230Sstevel@tonic-gate 			return (ENOMEM);
18240Sstevel@tonic-gate 		}
18250Sstevel@tonic-gate 	} else {
18260Sstevel@tonic-gate 		/*
18270Sstevel@tonic-gate 		 * User specified address - blow away any previous mappings
18280Sstevel@tonic-gate 		 */
18290Sstevel@tonic-gate 		(void) as_unmap(as, *addrp, len);
18300Sstevel@tonic-gate 	}
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 	vn_a.vp = vp;
18330Sstevel@tonic-gate 	vn_a.offset = off;
18340Sstevel@tonic-gate 	vn_a.type = flags & MAP_TYPE;
18350Sstevel@tonic-gate 	vn_a.prot = prot;
18360Sstevel@tonic-gate 	vn_a.maxprot = maxprot;
18370Sstevel@tonic-gate 	vn_a.flags = flags & ~MAP_TYPE;
18380Sstevel@tonic-gate 	vn_a.cred = cr;
18390Sstevel@tonic-gate 	vn_a.amp = NULL;
18400Sstevel@tonic-gate 	vn_a.szc = 0;
18410Sstevel@tonic-gate 	vn_a.lgrp_mem_policy_flags = 0;
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 	error = as_map(as, *addrp, len, segvn_create, &vn_a);
18440Sstevel@tonic-gate 	as_rangeunlock(as);
18450Sstevel@tonic-gate 	return (error);
18460Sstevel@tonic-gate }
18470Sstevel@tonic-gate 
18480Sstevel@tonic-gate /* ARGSUSED */
18490Sstevel@tonic-gate static int
18500Sstevel@tonic-gate pcfs_seek(
18510Sstevel@tonic-gate 	struct vnode *vp,
18520Sstevel@tonic-gate 	offset_t ooff,
18530Sstevel@tonic-gate 	offset_t *noffp)
18540Sstevel@tonic-gate {
18550Sstevel@tonic-gate 	if (*noffp < 0)
18560Sstevel@tonic-gate 		return (EINVAL);
18570Sstevel@tonic-gate 	else if (*noffp > MAXOFFSET_T)
18580Sstevel@tonic-gate 		return (EINVAL);
18590Sstevel@tonic-gate 	else
18600Sstevel@tonic-gate 		return (0);
18610Sstevel@tonic-gate }
18620Sstevel@tonic-gate 
18630Sstevel@tonic-gate /* ARGSUSED */
18640Sstevel@tonic-gate static int
18650Sstevel@tonic-gate pcfs_addmap(
18660Sstevel@tonic-gate 	struct vnode *vp,
18670Sstevel@tonic-gate 	offset_t off,
18680Sstevel@tonic-gate 	struct as *as,
18690Sstevel@tonic-gate 	caddr_t addr,
18700Sstevel@tonic-gate 	size_t len,
18710Sstevel@tonic-gate 	uchar_t prot,
18720Sstevel@tonic-gate 	uchar_t maxprot,
18730Sstevel@tonic-gate 	uint_t flags,
18740Sstevel@tonic-gate 	struct cred *cr)
18750Sstevel@tonic-gate {
18760Sstevel@tonic-gate 	if (vp->v_flag & VNOMAP)
18770Sstevel@tonic-gate 		return (ENOSYS);
18780Sstevel@tonic-gate 	return (0);
18790Sstevel@tonic-gate }
18800Sstevel@tonic-gate 
18810Sstevel@tonic-gate /*ARGSUSED*/
18820Sstevel@tonic-gate static int
18830Sstevel@tonic-gate pcfs_delmap(
18840Sstevel@tonic-gate 	struct vnode *vp,
18850Sstevel@tonic-gate 	offset_t off,
18860Sstevel@tonic-gate 	struct as *as,
18870Sstevel@tonic-gate 	caddr_t addr,
18880Sstevel@tonic-gate 	size_t len,
18890Sstevel@tonic-gate 	uint_t prot,
18900Sstevel@tonic-gate 	uint_t maxprot,
18910Sstevel@tonic-gate 	uint_t flags,
18920Sstevel@tonic-gate 	struct cred *cr)
18930Sstevel@tonic-gate {
18940Sstevel@tonic-gate 	if (vp->v_flag & VNOMAP)
18950Sstevel@tonic-gate 		return (ENOSYS);
18960Sstevel@tonic-gate 	return (0);
18970Sstevel@tonic-gate }
18980Sstevel@tonic-gate 
18990Sstevel@tonic-gate /*
19000Sstevel@tonic-gate  * POSIX pathconf() support.
19010Sstevel@tonic-gate  */
19020Sstevel@tonic-gate /* ARGSUSED */
19030Sstevel@tonic-gate static int
19040Sstevel@tonic-gate pcfs_pathconf(
19050Sstevel@tonic-gate 	struct vnode *vp,
19060Sstevel@tonic-gate 	int cmd,
19070Sstevel@tonic-gate 	ulong_t *valp,
19080Sstevel@tonic-gate 	struct cred *cr)
19090Sstevel@tonic-gate {
19100Sstevel@tonic-gate 	ulong_t val;
19110Sstevel@tonic-gate 	int error = 0;
19120Sstevel@tonic-gate 	struct statvfs64 vfsbuf;
19130Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate 	switch (cmd) {
19160Sstevel@tonic-gate 
19170Sstevel@tonic-gate 	case _PC_LINK_MAX:
19180Sstevel@tonic-gate 		val = 1;
19190Sstevel@tonic-gate 		break;
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate 	case _PC_MAX_CANON:
19220Sstevel@tonic-gate 		val = MAX_CANON;
19230Sstevel@tonic-gate 		break;
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 	case _PC_MAX_INPUT:
19260Sstevel@tonic-gate 		val = MAX_INPUT;
19270Sstevel@tonic-gate 		break;
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate 	case _PC_NAME_MAX:
19300Sstevel@tonic-gate 		bzero(&vfsbuf, sizeof (vfsbuf));
19310Sstevel@tonic-gate 		if (error = VFS_STATVFS(vp->v_vfsp, &vfsbuf))
19320Sstevel@tonic-gate 			break;
19330Sstevel@tonic-gate 		val = vfsbuf.f_namemax;
19340Sstevel@tonic-gate 		break;
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	case _PC_PATH_MAX:
19370Sstevel@tonic-gate 	case _PC_SYMLINK_MAX:
19380Sstevel@tonic-gate 		val = PCMAXPATHLEN;
19390Sstevel@tonic-gate 		break;
19400Sstevel@tonic-gate 
19410Sstevel@tonic-gate 	case _PC_PIPE_BUF:
19420Sstevel@tonic-gate 		val = PIPE_BUF;
19430Sstevel@tonic-gate 		break;
19440Sstevel@tonic-gate 
19450Sstevel@tonic-gate 	case _PC_NO_TRUNC:
19460Sstevel@tonic-gate 		val = (ulong_t)-1; 	/* Will truncate long file name */
19470Sstevel@tonic-gate 		break;
19480Sstevel@tonic-gate 
19490Sstevel@tonic-gate 	case _PC_VDISABLE:
19500Sstevel@tonic-gate 		val = _POSIX_VDISABLE;
19510Sstevel@tonic-gate 		break;
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate 	case _PC_CHOWN_RESTRICTED:
19540Sstevel@tonic-gate 		if (rstchown)
19550Sstevel@tonic-gate 			val = rstchown;		/* chown restricted enabled */
19560Sstevel@tonic-gate 		else
19570Sstevel@tonic-gate 			val = (ulong_t)-1;
19580Sstevel@tonic-gate 		break;
19590Sstevel@tonic-gate 
19600Sstevel@tonic-gate 	case _PC_ACL_ENABLED:
19610Sstevel@tonic-gate 		val = 0;
19620Sstevel@tonic-gate 		break;
19630Sstevel@tonic-gate 
19640Sstevel@tonic-gate 	case _PC_FILESIZEBITS:
19650Sstevel@tonic-gate 		/*
19660Sstevel@tonic-gate 		 * Both FAT16 and FAT32 support 4GB - 1 byte for file size.
19670Sstevel@tonic-gate 		 * FAT12 can only go up to the maximum filesystem capacity
19680Sstevel@tonic-gate 		 * which is ~509MB.
19690Sstevel@tonic-gate 		 */
19700Sstevel@tonic-gate 		val = IS_FAT12(fsp) ? 30 : 33;
19710Sstevel@tonic-gate 		break;
19720Sstevel@tonic-gate 	default:
19730Sstevel@tonic-gate 		error = EINVAL;
19740Sstevel@tonic-gate 		break;
19750Sstevel@tonic-gate 	}
19760Sstevel@tonic-gate 
19770Sstevel@tonic-gate 	if (error == 0)
19780Sstevel@tonic-gate 		*valp = val;
19790Sstevel@tonic-gate 	return (error);
19800Sstevel@tonic-gate }
19810Sstevel@tonic-gate 
19820Sstevel@tonic-gate /* ARGSUSED */
19830Sstevel@tonic-gate static int
19840Sstevel@tonic-gate pcfs_space(
19850Sstevel@tonic-gate 	struct vnode *vp,
19860Sstevel@tonic-gate 	int cmd,
19870Sstevel@tonic-gate 	struct flock64 *bfp,
19880Sstevel@tonic-gate 	int flag,
19890Sstevel@tonic-gate 	offset_t offset,
19900Sstevel@tonic-gate 	cred_t *cr,
19910Sstevel@tonic-gate 	caller_context_t *ct)
19920Sstevel@tonic-gate {
19930Sstevel@tonic-gate 	struct vattr vattr;
19940Sstevel@tonic-gate 	int error;
19950Sstevel@tonic-gate 
19960Sstevel@tonic-gate 	if (cmd != F_FREESP)
19970Sstevel@tonic-gate 		return (EINVAL);
19980Sstevel@tonic-gate 
19990Sstevel@tonic-gate 	if ((error = convoff(vp, bfp, 0, offset)) == 0) {
20000Sstevel@tonic-gate 		if ((bfp->l_start > UINT32_MAX) || (bfp->l_len > UINT32_MAX))
20010Sstevel@tonic-gate 			return (EFBIG);
20020Sstevel@tonic-gate 		/*
20030Sstevel@tonic-gate 		 * we only support the special case of l_len == 0,
20040Sstevel@tonic-gate 		 * meaning free to end of file at this moment.
20050Sstevel@tonic-gate 		 */
20060Sstevel@tonic-gate 		if (bfp->l_len != 0)
20070Sstevel@tonic-gate 			return (EINVAL);
20080Sstevel@tonic-gate 		vattr.va_mask = AT_SIZE;
20090Sstevel@tonic-gate 		vattr.va_size = bfp->l_start;
20100Sstevel@tonic-gate 		error = VOP_SETATTR(vp, &vattr, 0, cr, ct);
20110Sstevel@tonic-gate 	}
20120Sstevel@tonic-gate 	return (error);
20130Sstevel@tonic-gate }
20140Sstevel@tonic-gate 
20150Sstevel@tonic-gate /*
20160Sstevel@tonic-gate  * Break up 'len' chars from 'buf' into a long file name chunk.
20170Sstevel@tonic-gate  * Pad with '0xff' to make Norton Disk Doctor and Microsoft ScanDisk happy.
20180Sstevel@tonic-gate  */
20190Sstevel@tonic-gate void
20200Sstevel@tonic-gate set_long_fn_chunk(struct pcdir_lfn *ep, char *buf, int len)
20210Sstevel@tonic-gate {
20220Sstevel@tonic-gate 	char 	*tmp = buf;
20230Sstevel@tonic-gate 	int	i;
20240Sstevel@tonic-gate 
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate 	for (i = 0; i < PCLF_FIRSTNAMESIZE; i += 2) {
20270Sstevel@tonic-gate 		if (len > 0) {
20280Sstevel@tonic-gate 			ep->pcdl_firstfilename[i] = *tmp;
20290Sstevel@tonic-gate 			ep->pcdl_firstfilename[i+1] = 0;
20300Sstevel@tonic-gate 			len--;
20310Sstevel@tonic-gate 			tmp++;
20320Sstevel@tonic-gate 		} else {
20330Sstevel@tonic-gate 			ep->pcdl_firstfilename[i] = (uchar_t)0xff;
20340Sstevel@tonic-gate 			ep->pcdl_firstfilename[i+1] = (uchar_t)0xff;
20350Sstevel@tonic-gate 		}
20360Sstevel@tonic-gate 	}
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate 	for (i = 0; i < PCLF_SECONDNAMESIZE; i += 2) {
20390Sstevel@tonic-gate 		if (len > 0) {
20400Sstevel@tonic-gate 			ep->pcdl_secondfilename[i] = *tmp;
20410Sstevel@tonic-gate 			ep->pcdl_secondfilename[i+1] = 0;
20420Sstevel@tonic-gate 			len--;
20430Sstevel@tonic-gate 			tmp++;
20440Sstevel@tonic-gate 		} else {
20450Sstevel@tonic-gate 			ep->pcdl_secondfilename[i] = (uchar_t)0xff;
20460Sstevel@tonic-gate 			ep->pcdl_secondfilename[i+1] = (uchar_t)0xff;
20470Sstevel@tonic-gate 		}
20480Sstevel@tonic-gate 	}
20490Sstevel@tonic-gate 	for (i = 0; i < PCLF_THIRDNAMESIZE; i += 2) {
20500Sstevel@tonic-gate 		if (len > 0) {
20510Sstevel@tonic-gate 			ep->pcdl_thirdfilename[i] = *tmp;
20520Sstevel@tonic-gate 			ep->pcdl_thirdfilename[i+1] = 0;
20530Sstevel@tonic-gate 			len--;
20540Sstevel@tonic-gate 			tmp++;
20550Sstevel@tonic-gate 		} else {
20560Sstevel@tonic-gate 			ep->pcdl_thirdfilename[i] = (uchar_t)0xff;
20570Sstevel@tonic-gate 			ep->pcdl_thirdfilename[i+1] = (uchar_t)0xff;
20580Sstevel@tonic-gate 		}
20590Sstevel@tonic-gate 	}
20600Sstevel@tonic-gate }
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate /*
20630Sstevel@tonic-gate  * Extract the characters from the long filename chunk into 'buf'.
20640Sstevel@tonic-gate  * Return the number of characters extracted.
20650Sstevel@tonic-gate  */
20660Sstevel@tonic-gate static int
20670Sstevel@tonic-gate get_long_fn_chunk(struct pcdir_lfn *ep, char *buf, int foldcase)
20680Sstevel@tonic-gate {
20690Sstevel@tonic-gate 	char 	*tmp = buf;
20700Sstevel@tonic-gate 	int	i;
20710Sstevel@tonic-gate 
20720Sstevel@tonic-gate 	for (i = 0; i < PCLF_FIRSTNAMESIZE; i += 2, tmp++) {
20730Sstevel@tonic-gate 		if (ep->pcdl_firstfilename[i+1] != '\0')
20740Sstevel@tonic-gate 			return (-1);
20750Sstevel@tonic-gate 		if (foldcase)
20760Sstevel@tonic-gate 			*tmp = tolower(ep->pcdl_firstfilename[i]);
20770Sstevel@tonic-gate 		else
20780Sstevel@tonic-gate 			*tmp = ep->pcdl_firstfilename[i];
20790Sstevel@tonic-gate 		if (*tmp == '\0')
20800Sstevel@tonic-gate 			return (tmp - buf);
20810Sstevel@tonic-gate 	}
20820Sstevel@tonic-gate 	for (i = 0; i < PCLF_SECONDNAMESIZE; i += 2, tmp++) {
20830Sstevel@tonic-gate 		if (ep->pcdl_secondfilename[i+1] != '\0')
20840Sstevel@tonic-gate 			return (-1);
20850Sstevel@tonic-gate 		if (foldcase)
20860Sstevel@tonic-gate 			*tmp = tolower(ep->pcdl_secondfilename[i]);
20870Sstevel@tonic-gate 		else
20880Sstevel@tonic-gate 			*tmp = ep->pcdl_secondfilename[i];
20890Sstevel@tonic-gate 		if (*tmp == '\0')
20900Sstevel@tonic-gate 			return (tmp - buf);
20910Sstevel@tonic-gate 	}
20920Sstevel@tonic-gate 	for (i = 0; i < PCLF_THIRDNAMESIZE; i += 2, tmp++) {
20930Sstevel@tonic-gate 		if (ep->pcdl_thirdfilename[i+1] != '\0')
20940Sstevel@tonic-gate 			return (-1);
20950Sstevel@tonic-gate 		if (foldcase)
20960Sstevel@tonic-gate 			*tmp = tolower(ep->pcdl_thirdfilename[i]);
20970Sstevel@tonic-gate 		else
20980Sstevel@tonic-gate 			*tmp = ep->pcdl_thirdfilename[i];
20990Sstevel@tonic-gate 		if (*tmp == '\0')
21000Sstevel@tonic-gate 			return (tmp - buf);
21010Sstevel@tonic-gate 	}
21020Sstevel@tonic-gate 	*tmp = '\0';
21030Sstevel@tonic-gate 	return (tmp - buf);
21040Sstevel@tonic-gate }
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate /*
21080Sstevel@tonic-gate  * Checksum the passed in short filename.
21090Sstevel@tonic-gate  * This is used to validate each component of the long name to make
21100Sstevel@tonic-gate  * sure the long name is valid (it hasn't been "detached" from the
21110Sstevel@tonic-gate  * short filename). This algorithm was found in FreeBSD.
21120Sstevel@tonic-gate  * (sys/fs/msdosfs/msdosfs_conv.c:winChksum(), Wolfgang Solfrank)
21130Sstevel@tonic-gate  */
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate uchar_t
21160Sstevel@tonic-gate pc_checksum_long_fn(char *name, char *ext)
21170Sstevel@tonic-gate {
21180Sstevel@tonic-gate 	uchar_t c;
21190Sstevel@tonic-gate 	char	b[11];
21200Sstevel@tonic-gate 
21210Sstevel@tonic-gate 	bcopy(name, b, 8);
21220Sstevel@tonic-gate 	bcopy(ext, b+8, 3);
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 	c = b[0];
21250Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[1];
21260Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[2];
21270Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[3];
21280Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[4];
21290Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[5];
21300Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[6];
21310Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[7];
21320Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[8];
21330Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[9];
21340Sstevel@tonic-gate 	c = ((c << 7) | (c >> 1)) + b[10];
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 	return (c);
21370Sstevel@tonic-gate }
21380Sstevel@tonic-gate 
21390Sstevel@tonic-gate /*
21400Sstevel@tonic-gate  * Read a chunk of long filename entries into 'namep'.
21410Sstevel@tonic-gate  * Return with offset pointing to short entry (on success), or next
21420Sstevel@tonic-gate  * entry to read (if this wasn't a valid lfn really).
21430Sstevel@tonic-gate  * Uses the passed-in buffer if it can, otherwise kmem_allocs() room for
21440Sstevel@tonic-gate  * a long filename.
21450Sstevel@tonic-gate  *
21460Sstevel@tonic-gate  * Can also be called with a NULL namep, in which case it just returns
21470Sstevel@tonic-gate  * whether this was really a valid long filename and consumes it
21480Sstevel@tonic-gate  * (used by pc_dirempty()).
21490Sstevel@tonic-gate  */
21500Sstevel@tonic-gate int
21510Sstevel@tonic-gate pc_extract_long_fn(struct pcnode *pcp, char *namep,
21520Sstevel@tonic-gate     struct pcdir **epp, offset_t *offset, struct buf **bp)
21530Sstevel@tonic-gate {
21540Sstevel@tonic-gate 	struct pcdir *ep = *epp;
21550Sstevel@tonic-gate 	struct pcdir_lfn *lep = (struct pcdir_lfn *)ep;
21560Sstevel@tonic-gate 	struct vnode *dvp = PCTOV(pcp);
21570Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(dvp->v_vfsp);
21580Sstevel@tonic-gate 	char	*lfn;
21590Sstevel@tonic-gate 	char	*lfn_base;
21600Sstevel@tonic-gate 	int	boff;
21610Sstevel@tonic-gate 	int	i, cs;
21620Sstevel@tonic-gate 	char	buf[20];
21630Sstevel@tonic-gate 	uchar_t	cksum;
21640Sstevel@tonic-gate 	int	detached = 0;
21650Sstevel@tonic-gate 	int	error = 0;
21660Sstevel@tonic-gate 	int	foldcase;
21670Sstevel@tonic-gate 
21680Sstevel@tonic-gate 	foldcase = (fsp->pcfs_flags & PCFS_FOLDCASE);
21690Sstevel@tonic-gate 	/* use callers buffer unless we didn't get one */
21700Sstevel@tonic-gate 	if (namep)
21710Sstevel@tonic-gate 		lfn_base = namep;
21720Sstevel@tonic-gate 	else
21730Sstevel@tonic-gate 		lfn_base = kmem_alloc(PCMAXNAMLEN+1, KM_SLEEP);
21740Sstevel@tonic-gate 	lfn = lfn_base + PCMAXNAMLEN - 1;
21750Sstevel@tonic-gate 	*lfn = '\0';
21760Sstevel@tonic-gate 	cksum = lep->pcdl_checksum;
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate 	for (i = (lep->pcdl_ordinal & ~0xc0); i > 0; i--) {
21790Sstevel@tonic-gate 		/* read next block if necessary */
21800Sstevel@tonic-gate 		boff = pc_blkoff(fsp, *offset);
21810Sstevel@tonic-gate 		if (boff == 0 || *bp == NULL || boff >= (*bp)->b_bcount) {
21820Sstevel@tonic-gate 			if (*bp != NULL) {
21830Sstevel@tonic-gate 				brelse(*bp);
21840Sstevel@tonic-gate 				*bp = NULL;
21850Sstevel@tonic-gate 			}
21860Sstevel@tonic-gate 			error = pc_blkatoff(pcp, *offset, bp, &ep);
21870Sstevel@tonic-gate 			if (error) {
21880Sstevel@tonic-gate 				if (namep == NULL)
21890Sstevel@tonic-gate 					kmem_free(lfn_base, PCMAXNAMLEN+1);
21900Sstevel@tonic-gate 				return (error);
21910Sstevel@tonic-gate 			}
21920Sstevel@tonic-gate 			lep = (struct pcdir_lfn *)ep;
21930Sstevel@tonic-gate 		}
21940Sstevel@tonic-gate 		/* can this happen? Bad fs? */
21950Sstevel@tonic-gate 		if (!PCDL_IS_LFN((struct pcdir *)lep)) {
21960Sstevel@tonic-gate 			detached = 1;
21970Sstevel@tonic-gate 			break;
21980Sstevel@tonic-gate 		}
21990Sstevel@tonic-gate 		if (cksum != lep->pcdl_checksum)
22000Sstevel@tonic-gate 			detached = 1;
22010Sstevel@tonic-gate 		/* process current entry */
22020Sstevel@tonic-gate 		cs = get_long_fn_chunk(lep, buf, foldcase);
22030Sstevel@tonic-gate 		if (cs == -1) {
22040Sstevel@tonic-gate 			detached = 1;
22050Sstevel@tonic-gate 		} else {
22060Sstevel@tonic-gate 			for (; cs > 0; cs--) {
22070Sstevel@tonic-gate 				/* see if we underflow */
22080Sstevel@tonic-gate 				if (lfn >= lfn_base)
22090Sstevel@tonic-gate 					*--lfn = buf[cs - 1];
22100Sstevel@tonic-gate 				else
22110Sstevel@tonic-gate 					detached = 1;
22120Sstevel@tonic-gate 			}
22130Sstevel@tonic-gate 		}
22140Sstevel@tonic-gate 		lep++;
22150Sstevel@tonic-gate 		*offset += sizeof (struct pcdir);
22160Sstevel@tonic-gate 	}
22170Sstevel@tonic-gate 	/* read next block if necessary */
22180Sstevel@tonic-gate 	boff = pc_blkoff(fsp, *offset);
22190Sstevel@tonic-gate 	ep = (struct pcdir *)lep;
22200Sstevel@tonic-gate 	if (boff == 0 || *bp == NULL || boff >= (*bp)->b_bcount) {
22210Sstevel@tonic-gate 		if (*bp != NULL) {
22220Sstevel@tonic-gate 			brelse(*bp);
22230Sstevel@tonic-gate 			*bp = NULL;
22240Sstevel@tonic-gate 		}
22250Sstevel@tonic-gate 		error = pc_blkatoff(pcp, *offset, bp, &ep);
22260Sstevel@tonic-gate 		if (error) {
22270Sstevel@tonic-gate 			if (namep == NULL)
22280Sstevel@tonic-gate 				kmem_free(lfn_base, PCMAXNAMLEN+1);
22290Sstevel@tonic-gate 			return (error);
22300Sstevel@tonic-gate 		}
22310Sstevel@tonic-gate 	}
22320Sstevel@tonic-gate 	/* should be on the short one */
22330Sstevel@tonic-gate 	if (PCDL_IS_LFN(ep) || ((ep->pcd_filename[0] == PCD_UNUSED) ||
22340Sstevel@tonic-gate 	    (ep->pcd_filename[0] == PCD_ERASED))) {
22350Sstevel@tonic-gate 		detached = 1;
22360Sstevel@tonic-gate 	}
22370Sstevel@tonic-gate 	if (detached ||
22380Sstevel@tonic-gate 	    (cksum != pc_checksum_long_fn(ep->pcd_filename, ep->pcd_ext)) ||
22390Sstevel@tonic-gate 	    !pc_valid_long_fn(lfn)) {
22400Sstevel@tonic-gate 		/*
22410Sstevel@tonic-gate 		 * process current entry again. This may end up another lfn
22420Sstevel@tonic-gate 		 * or a short name.
22430Sstevel@tonic-gate 		 */
22440Sstevel@tonic-gate 		*epp = ep;
22450Sstevel@tonic-gate 		if (namep == NULL)
22460Sstevel@tonic-gate 			kmem_free(lfn_base, PCMAXNAMLEN+1);
22470Sstevel@tonic-gate 		return (EINVAL);
22480Sstevel@tonic-gate 	}
22490Sstevel@tonic-gate 	if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
22500Sstevel@tonic-gate 		/*
22510Sstevel@tonic-gate 		 * Don't display label because it may contain
22520Sstevel@tonic-gate 		 * funny characters.
22530Sstevel@tonic-gate 		 */
22540Sstevel@tonic-gate 		*offset += sizeof (struct pcdir);
22550Sstevel@tonic-gate 		ep++;
22560Sstevel@tonic-gate 		*epp = ep;
22570Sstevel@tonic-gate 		if (namep == NULL)
22580Sstevel@tonic-gate 			kmem_free(lfn_base, PCMAXNAMLEN+1);
22590Sstevel@tonic-gate 		return (EINVAL);
22600Sstevel@tonic-gate 	}
22610Sstevel@tonic-gate 	if (namep) {
22620Sstevel@tonic-gate 		/* lfn is part of namep, but shifted. shift it back */
22630Sstevel@tonic-gate 		cs = strlen(lfn);
22640Sstevel@tonic-gate 		for (i = 0; i < cs; i++)
22650Sstevel@tonic-gate 			namep[i] = lfn[i];
22660Sstevel@tonic-gate 		namep[i] = '\0';
22670Sstevel@tonic-gate 	} else {
22680Sstevel@tonic-gate 		kmem_free(lfn_base, PCMAXNAMLEN+1);
22690Sstevel@tonic-gate 	}
22700Sstevel@tonic-gate 	*epp = ep;
22710Sstevel@tonic-gate 	return (0);
22720Sstevel@tonic-gate }
22730Sstevel@tonic-gate /*
22740Sstevel@tonic-gate  * Read a long filename into the pc_dirent structure and copy it out.
22750Sstevel@tonic-gate  */
22760Sstevel@tonic-gate int
22770Sstevel@tonic-gate pc_read_long_fn(struct vnode *dvp, struct uio *uiop, struct pc_dirent *ld,
22780Sstevel@tonic-gate     struct pcdir **epp, offset_t *offset, struct buf **bp)
22790Sstevel@tonic-gate {
22800Sstevel@tonic-gate 	struct pcdir *ep;
22810Sstevel@tonic-gate 	struct pcnode *pcp = VTOPC(dvp);
22820Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(dvp->v_vfsp);
22830Sstevel@tonic-gate 	offset_t uiooffset = uiop->uio_loffset;
22840Sstevel@tonic-gate 	int	error = 0;
22850Sstevel@tonic-gate 	offset_t oldoffset;
22860Sstevel@tonic-gate 
22870Sstevel@tonic-gate 	oldoffset = *offset;
22880Sstevel@tonic-gate 	error = pc_extract_long_fn(pcp, ld->d_name, epp, offset, bp);
22890Sstevel@tonic-gate 	if (error) {
22900Sstevel@tonic-gate 		if (error == EINVAL) {
22910Sstevel@tonic-gate 			uiop->uio_loffset += *offset - oldoffset;
22920Sstevel@tonic-gate 			return (0);
22930Sstevel@tonic-gate 		} else
22940Sstevel@tonic-gate 			return (error);
22950Sstevel@tonic-gate 	}
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate 	ep = *epp;
22980Sstevel@tonic-gate 	uiop->uio_loffset += *offset - oldoffset;
22990Sstevel@tonic-gate 	ld->d_reclen = DIRENT64_RECLEN(strlen(ld->d_name));
23000Sstevel@tonic-gate 	if (ld->d_reclen > uiop->uio_resid) {
23010Sstevel@tonic-gate 		uiop->uio_loffset = uiooffset;
23020Sstevel@tonic-gate 		return (ENOSPC);
23030Sstevel@tonic-gate 	}
23040Sstevel@tonic-gate 	ld->d_off = uiop->uio_loffset + sizeof (struct pcdir);
23050Sstevel@tonic-gate 	ld->d_ino = pc_makenodeid(pc_daddrdb(fsp, (*bp)->b_blkno),
23060Sstevel@tonic-gate 	    pc_blkoff(fsp, *offset), ep->pcd_attr,
23070Sstevel@tonic-gate 	    pc_getstartcluster(fsp, ep), fsp->pcfs_entps);
23080Sstevel@tonic-gate 	(void) uiomove((caddr_t)ld, ld->d_reclen, UIO_READ, uiop);
23090Sstevel@tonic-gate 	uiop->uio_loffset = ld->d_off;
23100Sstevel@tonic-gate 	*offset += sizeof (struct pcdir);
23110Sstevel@tonic-gate 	ep++;
23120Sstevel@tonic-gate 	*epp = ep;
23130Sstevel@tonic-gate 	return (0);
23140Sstevel@tonic-gate }
23150Sstevel@tonic-gate 
23160Sstevel@tonic-gate /*
23170Sstevel@tonic-gate  * Read a short filename into the pc_dirent structure and copy it out.
23180Sstevel@tonic-gate  */
23190Sstevel@tonic-gate int
23200Sstevel@tonic-gate pc_read_short_fn(struct vnode *dvp, struct uio *uiop, struct pc_dirent *ld,
23210Sstevel@tonic-gate     struct pcdir **epp, offset_t *offset, struct buf **bp)
23220Sstevel@tonic-gate {
23230Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(dvp->v_vfsp);
23240Sstevel@tonic-gate 	int	boff = pc_blkoff(fsp, *offset);
23250Sstevel@tonic-gate 	struct pcdir *ep = *epp;
23260Sstevel@tonic-gate 	offset_t	oldoffset = uiop->uio_loffset;
23270Sstevel@tonic-gate 	int	error;
23280Sstevel@tonic-gate 	int	foldcase;
23290Sstevel@tonic-gate 
23300Sstevel@tonic-gate 	if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
23310Sstevel@tonic-gate 		uiop->uio_loffset += sizeof (struct pcdir);
23320Sstevel@tonic-gate 		*offset += sizeof (struct pcdir);
23330Sstevel@tonic-gate 		ep++;
23340Sstevel@tonic-gate 		*epp = ep;
23350Sstevel@tonic-gate 		return (0);
23360Sstevel@tonic-gate 	}
23370Sstevel@tonic-gate 	ld->d_ino = (ino64_t)pc_makenodeid(pc_daddrdb(fsp, (*bp)->b_blkno),
23380Sstevel@tonic-gate 	    boff, ep->pcd_attr, pc_getstartcluster(fsp, ep), fsp->pcfs_entps);
23390Sstevel@tonic-gate 	foldcase = (fsp->pcfs_flags & PCFS_FOLDCASE);
23400Sstevel@tonic-gate 	error = pc_fname_ext_to_name(&ld->d_name[0], &ep->pcd_filename[0],
23410Sstevel@tonic-gate 	    &ep->pcd_ext[0], foldcase);
23420Sstevel@tonic-gate 	if (error == 0) {
23430Sstevel@tonic-gate 		ld->d_reclen = DIRENT64_RECLEN(strlen(ld->d_name));
23440Sstevel@tonic-gate 		if (ld->d_reclen > uiop->uio_resid) {
23450Sstevel@tonic-gate 			uiop->uio_loffset = oldoffset;
23460Sstevel@tonic-gate 			return (ENOSPC);
23470Sstevel@tonic-gate 		}
23480Sstevel@tonic-gate 		ld->d_off = (off64_t)(uiop->uio_loffset +
23490Sstevel@tonic-gate 		    sizeof (struct pcdir));
23500Sstevel@tonic-gate 		(void) uiomove((caddr_t)ld,
23510Sstevel@tonic-gate 		    ld->d_reclen, UIO_READ, uiop);
23520Sstevel@tonic-gate 		uiop->uio_loffset = ld->d_off;
23530Sstevel@tonic-gate 	} else {
23540Sstevel@tonic-gate 		uiop->uio_loffset += sizeof (struct pcdir);
23550Sstevel@tonic-gate 	}
23560Sstevel@tonic-gate 	*offset += sizeof (struct pcdir);
23570Sstevel@tonic-gate 	ep++;
23580Sstevel@tonic-gate 	*epp = ep;
23590Sstevel@tonic-gate 	return (0);
23600Sstevel@tonic-gate }
23610Sstevel@tonic-gate 
23620Sstevel@tonic-gate static int
23630Sstevel@tonic-gate pcfs_fid(struct vnode *vp, struct fid *fidp)
23640Sstevel@tonic-gate {
23650Sstevel@tonic-gate 	struct pc_fid *pcfid;
23660Sstevel@tonic-gate 	struct pcnode *pcp;
23670Sstevel@tonic-gate 	struct pcfs	*fsp;
23680Sstevel@tonic-gate 	int	error;
23690Sstevel@tonic-gate 
23700Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
23710Sstevel@tonic-gate 	if (fsp == NULL)
23720Sstevel@tonic-gate 		return (EIO);
23730Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
23740Sstevel@tonic-gate 	if (error)
23750Sstevel@tonic-gate 		return (error);
2376*2972Sfrankho 	if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
23770Sstevel@tonic-gate 		pc_unlockfs(fsp);
23780Sstevel@tonic-gate 		return (EIO);
23790Sstevel@tonic-gate 	}
23800Sstevel@tonic-gate 	if (fidp->fid_len < (sizeof (struct pc_fid) - sizeof (ushort_t))) {
23810Sstevel@tonic-gate 		fidp->fid_len = sizeof (struct pc_fid) - sizeof (ushort_t);
23820Sstevel@tonic-gate 		pc_unlockfs(fsp);
23830Sstevel@tonic-gate 		return (ENOSPC);
23840Sstevel@tonic-gate 	}
23850Sstevel@tonic-gate 
23860Sstevel@tonic-gate 	pcfid = (struct pc_fid *)fidp;
23870Sstevel@tonic-gate 	bzero(pcfid, sizeof (struct pc_fid));
23880Sstevel@tonic-gate 	pcfid->pcfid_len = sizeof (struct pc_fid) - sizeof (ushort_t);
23890Sstevel@tonic-gate 	if (vp->v_flag & VROOT) {
23900Sstevel@tonic-gate 		pcfid->pcfid_block = 0;
23910Sstevel@tonic-gate 		pcfid->pcfid_offset = 0;
23920Sstevel@tonic-gate 		pcfid->pcfid_ctime = 0;
23930Sstevel@tonic-gate 	} else {
23940Sstevel@tonic-gate 		pcfid->pcfid_block = pcp->pc_eblkno;
23950Sstevel@tonic-gate 		pcfid->pcfid_offset = pcp->pc_eoffset;
23960Sstevel@tonic-gate 		pcfid->pcfid_ctime = pcp->pc_entry.pcd_crtime.pct_time;
23970Sstevel@tonic-gate 	}
23980Sstevel@tonic-gate 	pc_unlockfs(fsp);
23990Sstevel@tonic-gate 	return (0);
24000Sstevel@tonic-gate }
2401