xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_dir.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/param.h>
30*0Sstevel@tonic-gate #include <sys/errno.h>
31*0Sstevel@tonic-gate #include <sys/systm.h>
32*0Sstevel@tonic-gate #include <sys/sysmacros.h>
33*0Sstevel@tonic-gate #include <sys/buf.h>
34*0Sstevel@tonic-gate #include <sys/vfs.h>
35*0Sstevel@tonic-gate #include <sys/kmem.h>
36*0Sstevel@tonic-gate #include <sys/vnode.h>
37*0Sstevel@tonic-gate #include <sys/debug.h>
38*0Sstevel@tonic-gate #include <sys/cmn_err.h>
39*0Sstevel@tonic-gate #include <sys/fs/pc_label.h>
40*0Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
41*0Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
42*0Sstevel@tonic-gate #include <sys/fs/pc_node.h>
43*0Sstevel@tonic-gate 
44*0Sstevel@tonic-gate static int pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
45*0Sstevel@tonic-gate     int	ndirentries, struct vattr *vap, offset_t offset);
46*0Sstevel@tonic-gate static int pc_dirempty(struct pcnode *);
47*0Sstevel@tonic-gate static int pc_findentry(struct pcnode *, char *, struct slot *, offset_t *);
48*0Sstevel@tonic-gate static int pc_parsename(char *, char *, char *);
49*0Sstevel@tonic-gate static int pc_remove_long_fn(struct pcnode *pcp,
50*0Sstevel@tonic-gate     offset_t lfn_offset);
51*0Sstevel@tonic-gate static int generate_short_name(struct pcnode *dp, char *namep,
52*0Sstevel@tonic-gate     struct pcdir *ep);
53*0Sstevel@tonic-gate static struct pcdir *pc_name_to_pcdir(struct pcnode *dp, char *namep,
54*0Sstevel@tonic-gate     int ndirentries, int *errret);
55*0Sstevel@tonic-gate static offset_t pc_find_free_space(struct pcnode *pcp, int ndirentries);
56*0Sstevel@tonic-gate static int direntries_needed(struct pcnode *dp, char *namep);
57*0Sstevel@tonic-gate static int pc_is_short_file_name(char *namep, int foldcase);
58*0Sstevel@tonic-gate static int shortname_exists(struct pcnode *dp, char *fname, char *fext);
59*0Sstevel@tonic-gate static int pc_dirfixdotdot(struct pcnode *cdp, struct pcnode *opdp,
60*0Sstevel@tonic-gate     struct pcnode *npdp);
61*0Sstevel@tonic-gate /*
62*0Sstevel@tonic-gate  * Tunables
63*0Sstevel@tonic-gate  */
64*0Sstevel@tonic-gate int enable_long_filenames = 1;
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate /*
67*0Sstevel@tonic-gate  * Lookup a name in a directory. Return a pointer to the pc_node
68*0Sstevel@tonic-gate  * which represents the entry.
69*0Sstevel@tonic-gate  */
70*0Sstevel@tonic-gate int
71*0Sstevel@tonic-gate pc_dirlook(
72*0Sstevel@tonic-gate 	struct pcnode *dp,		/* parent directory */
73*0Sstevel@tonic-gate 	char *namep,			/* name to lookup */
74*0Sstevel@tonic-gate 	struct pcnode **pcpp)		/* result */
75*0Sstevel@tonic-gate {
76*0Sstevel@tonic-gate 	struct vnode *vp;
77*0Sstevel@tonic-gate 	struct slot slot;
78*0Sstevel@tonic-gate 	int error;
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate 	PC_DPRINTF2(4, "pc_dirlook (dp %p name %s)\n", (void *)dp, namep);
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate 	if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
83*0Sstevel@tonic-gate 		return (ENOTDIR);
84*0Sstevel@tonic-gate 	}
85*0Sstevel@tonic-gate 	vp = PCTOV(dp);
86*0Sstevel@tonic-gate 	/*
87*0Sstevel@tonic-gate 	 * check now for changed disk, before any return(0)
88*0Sstevel@tonic-gate 	 */
89*0Sstevel@tonic-gate 	if (error = pc_verify(VFSTOPCFS(vp->v_vfsp)))
90*0Sstevel@tonic-gate 		return (error);
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate 	/*
93*0Sstevel@tonic-gate 	 * Null component name is synonym for directory being searched.
94*0Sstevel@tonic-gate 	 */
95*0Sstevel@tonic-gate 	if (*namep == '\0') {
96*0Sstevel@tonic-gate 		VN_HOLD(vp);
97*0Sstevel@tonic-gate 		*pcpp = dp;
98*0Sstevel@tonic-gate 		return (0);
99*0Sstevel@tonic-gate 	}
100*0Sstevel@tonic-gate 	/*
101*0Sstevel@tonic-gate 	 * The root directory does not have "." and ".." entries,
102*0Sstevel@tonic-gate 	 * so they are faked here.
103*0Sstevel@tonic-gate 	 */
104*0Sstevel@tonic-gate 	if (vp->v_flag & VROOT) {
105*0Sstevel@tonic-gate 		if (bcmp(namep, ".", 2) == 0 || bcmp(namep, "..", 3) == 0) {
106*0Sstevel@tonic-gate 			VN_HOLD(vp);
107*0Sstevel@tonic-gate 			*pcpp = dp;
108*0Sstevel@tonic-gate 			return (0);
109*0Sstevel@tonic-gate 		}
110*0Sstevel@tonic-gate 	}
111*0Sstevel@tonic-gate 	error = pc_findentry(dp, namep, &slot, NULL);
112*0Sstevel@tonic-gate 	if (error == 0) {
113*0Sstevel@tonic-gate 		*pcpp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
114*0Sstevel@tonic-gate 		    slot.sl_blkno, slot.sl_offset, slot.sl_ep);
115*0Sstevel@tonic-gate 		brelse(slot.sl_bp);
116*0Sstevel@tonic-gate 		PC_DPRINTF1(4, "pc_dirlook: FOUND pcp=%p\n", (void *)*pcpp);
117*0Sstevel@tonic-gate 	} else if (error == EINVAL) {
118*0Sstevel@tonic-gate 		error = ENOENT;
119*0Sstevel@tonic-gate 	}
120*0Sstevel@tonic-gate 	return (error);
121*0Sstevel@tonic-gate }
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate /*
124*0Sstevel@tonic-gate  * Enter a name in a directory.
125*0Sstevel@tonic-gate  */
126*0Sstevel@tonic-gate int
127*0Sstevel@tonic-gate pc_direnter(
128*0Sstevel@tonic-gate 	struct pcnode *dp,		/* directory to make entry in */
129*0Sstevel@tonic-gate 	char *namep,			/* name of entry */
130*0Sstevel@tonic-gate 	struct vattr *vap,		/* attributes of new entry */
131*0Sstevel@tonic-gate 	struct pcnode **pcpp)
132*0Sstevel@tonic-gate {
133*0Sstevel@tonic-gate 	int error;
134*0Sstevel@tonic-gate 	struct slot slot;
135*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(dp);
136*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
137*0Sstevel@tonic-gate 	offset_t offset;
138*0Sstevel@tonic-gate 	int	blkno;
139*0Sstevel@tonic-gate 	int	boff;
140*0Sstevel@tonic-gate 	struct buf *bp = NULL;
141*0Sstevel@tonic-gate 	struct pcdir *ep;
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate 	PC_DPRINTF4(4, "pc_dirent(dp %p, name %s, vap %p, pcpp %p\n",
144*0Sstevel@tonic-gate 	    (void *)dp, namep, (void *)vap, (void *)pcpp);
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	if (pcpp != NULL)
147*0Sstevel@tonic-gate 		*pcpp = NULL;
148*0Sstevel@tonic-gate 	/*
149*0Sstevel@tonic-gate 	 * Leading spaces are not allowed in DOS.
150*0Sstevel@tonic-gate 	 */
151*0Sstevel@tonic-gate 	if (*namep == ' ')
152*0Sstevel@tonic-gate 		return (EINVAL);
153*0Sstevel@tonic-gate 	/*
154*0Sstevel@tonic-gate 	 * If name is "." or "..", just look it up.
155*0Sstevel@tonic-gate 	 */
156*0Sstevel@tonic-gate 	if (PC_NAME_IS_DOT(namep) || PC_NAME_IS_DOTDOT(namep)) {
157*0Sstevel@tonic-gate 		if (pcpp) {
158*0Sstevel@tonic-gate 			error = pc_dirlook(dp, namep, pcpp);
159*0Sstevel@tonic-gate 			if (error)
160*0Sstevel@tonic-gate 				return (error);
161*0Sstevel@tonic-gate 		}
162*0Sstevel@tonic-gate 		return (EEXIST);
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 	if (PCA_IS_HIDDEN(fsp, dp->pc_entry.pcd_attr)) {
165*0Sstevel@tonic-gate 		return (EPERM);
166*0Sstevel@tonic-gate 	}
167*0Sstevel@tonic-gate 	/*
168*0Sstevel@tonic-gate 	 * Make sure directory has not been removed while fs was unlocked.
169*0Sstevel@tonic-gate 	 */
170*0Sstevel@tonic-gate 	if (dp->pc_entry.pcd_filename[0] == PCD_ERASED) {
171*0Sstevel@tonic-gate 		return (ENOENT);
172*0Sstevel@tonic-gate 	}
173*0Sstevel@tonic-gate 	error = pc_findentry(dp, namep, &slot, NULL);
174*0Sstevel@tonic-gate 	if (error == 0) {
175*0Sstevel@tonic-gate 		if (pcpp) {
176*0Sstevel@tonic-gate 			*pcpp =
177*0Sstevel@tonic-gate 			    pc_getnode(fsp, slot.sl_blkno, slot.sl_offset,
178*0Sstevel@tonic-gate 				slot.sl_ep);
179*0Sstevel@tonic-gate 			error = EEXIST;
180*0Sstevel@tonic-gate 		}
181*0Sstevel@tonic-gate 		brelse(slot.sl_bp);
182*0Sstevel@tonic-gate 	} else if (error == ENOENT) {
183*0Sstevel@tonic-gate 		struct pcdir *direntries;
184*0Sstevel@tonic-gate 		int	ndirentries;
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 		/*
187*0Sstevel@tonic-gate 		 * The entry does not exist. Check write permission in
188*0Sstevel@tonic-gate 		 * directory to see if entry can be created.
189*0Sstevel@tonic-gate 		 */
190*0Sstevel@tonic-gate 		if (dp->pc_entry.pcd_attr & PCA_RDONLY) {
191*0Sstevel@tonic-gate 			return (EPERM);
192*0Sstevel@tonic-gate 		}
193*0Sstevel@tonic-gate 		error = 0;
194*0Sstevel@tonic-gate 		/*
195*0Sstevel@tonic-gate 		 * Make sure there is a slot.
196*0Sstevel@tonic-gate 		 */
197*0Sstevel@tonic-gate 		if (slot.sl_status == SL_NONE)
198*0Sstevel@tonic-gate 			panic("pc_direnter: no slot\n");
199*0Sstevel@tonic-gate 		ndirentries = direntries_needed(dp, namep);
200*0Sstevel@tonic-gate 		if (ndirentries == -1) {
201*0Sstevel@tonic-gate 			return (EINVAL);
202*0Sstevel@tonic-gate 		}
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 		offset = pc_find_free_space(dp, ndirentries);
205*0Sstevel@tonic-gate 		if (offset == -1) {
206*0Sstevel@tonic-gate 			return (ENOSPC);
207*0Sstevel@tonic-gate 		}
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate 		/*
210*0Sstevel@tonic-gate 		 * Make an entry from the supplied attributes.
211*0Sstevel@tonic-gate 		 */
212*0Sstevel@tonic-gate 		direntries = pc_name_to_pcdir(dp, namep, ndirentries, &error);
213*0Sstevel@tonic-gate 		if (direntries == NULL) {
214*0Sstevel@tonic-gate 			return (error);
215*0Sstevel@tonic-gate 		}
216*0Sstevel@tonic-gate 		error = pc_makedirentry(dp, direntries, ndirentries, vap,
217*0Sstevel@tonic-gate 		    offset);
218*0Sstevel@tonic-gate 		kmem_free(direntries, ndirentries * sizeof (struct pcdir));
219*0Sstevel@tonic-gate 		if (error) {
220*0Sstevel@tonic-gate 			return (error);
221*0Sstevel@tonic-gate 		}
222*0Sstevel@tonic-gate 		offset += (ndirentries - 1)  * sizeof (struct pcdir);
223*0Sstevel@tonic-gate 		boff = pc_blkoff(fsp, offset);
224*0Sstevel@tonic-gate 		error = pc_blkatoff(dp, offset, &bp, &ep);
225*0Sstevel@tonic-gate 		if (error) {
226*0Sstevel@tonic-gate 			return (error);
227*0Sstevel@tonic-gate 		}
228*0Sstevel@tonic-gate 		blkno = pc_daddrdb(fsp, bp->b_blkno);
229*0Sstevel@tonic-gate 		/*
230*0Sstevel@tonic-gate 		 * Get a pcnode for the new entry.
231*0Sstevel@tonic-gate 		 */
232*0Sstevel@tonic-gate 		*pcpp = pc_getnode(fsp, blkno, boff, ep);
233*0Sstevel@tonic-gate 		brelse(bp);
234*0Sstevel@tonic-gate 		if (vap->va_type == VDIR)
235*0Sstevel@tonic-gate 			(*pcpp)->pc_size = fsp->pcfs_clsize;
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate 		/*
238*0Sstevel@tonic-gate 		 * Write out the new entry in the parent directory.
239*0Sstevel@tonic-gate 		 */
240*0Sstevel@tonic-gate 		error = pc_syncfat(fsp);
241*0Sstevel@tonic-gate 		if (!error) {
242*0Sstevel@tonic-gate 			error = pc_nodeupdate(*pcpp);
243*0Sstevel@tonic-gate 		}
244*0Sstevel@tonic-gate 	}
245*0Sstevel@tonic-gate 	return (error);
246*0Sstevel@tonic-gate }
247*0Sstevel@tonic-gate 
248*0Sstevel@tonic-gate /*
249*0Sstevel@tonic-gate  * Template for "." and ".." directory entries.
250*0Sstevel@tonic-gate  */
251*0Sstevel@tonic-gate static struct {
252*0Sstevel@tonic-gate 	struct pcdir t_dot;		/* dot entry */
253*0Sstevel@tonic-gate 	struct pcdir t_dotdot;		/* dotdot entry */
254*0Sstevel@tonic-gate } dirtemplate = {
255*0Sstevel@tonic-gate 	{
256*0Sstevel@tonic-gate 		".       ",
257*0Sstevel@tonic-gate 		"   ",
258*0Sstevel@tonic-gate 		PCA_DIR
259*0Sstevel@tonic-gate 	},
260*0Sstevel@tonic-gate 	{
261*0Sstevel@tonic-gate 		"..      ",
262*0Sstevel@tonic-gate 		"   ",
263*0Sstevel@tonic-gate 		PCA_DIR
264*0Sstevel@tonic-gate 	}
265*0Sstevel@tonic-gate };
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate /*
268*0Sstevel@tonic-gate  * Convert an attributes structure into the short filename entry
269*0Sstevel@tonic-gate  * and write out the whole entry.
270*0Sstevel@tonic-gate  */
271*0Sstevel@tonic-gate static int
272*0Sstevel@tonic-gate pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
273*0Sstevel@tonic-gate     int ndirentries, struct vattr *vap, offset_t offset)
274*0Sstevel@tonic-gate {
275*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(dp);
276*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
277*0Sstevel@tonic-gate 	int error;
278*0Sstevel@tonic-gate 	struct pcdir *ep;
279*0Sstevel@tonic-gate 	int	boff;
280*0Sstevel@tonic-gate 	int	i;
281*0Sstevel@tonic-gate 	struct buf *bp = NULL;
282*0Sstevel@tonic-gate 	timestruc_t now;
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	if (vap != NULL && vap->va_mask & (AT_ATIME|AT_MTIME))
285*0Sstevel@tonic-gate 		return (EOPNOTSUPP);
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	ep = &direntries[ndirentries - 1];
288*0Sstevel@tonic-gate 	gethrestime(&now);
289*0Sstevel@tonic-gate 	pc_tvtopct(&now, &ep->pcd_mtime);
290*0Sstevel@tonic-gate 	ep->pcd_crtime = ep->pcd_mtime;
291*0Sstevel@tonic-gate 	ep->pcd_ladate = ep->pcd_mtime.pct_date;
292*0Sstevel@tonic-gate 	ep->pcd_crtime_msec = 0;
293*0Sstevel@tonic-gate 	ep->pcd_size = 0;
294*0Sstevel@tonic-gate 	ep->pcd_attr = 0;
295*0Sstevel@tonic-gate 	/*
296*0Sstevel@tonic-gate 	 * Fields we don't use.
297*0Sstevel@tonic-gate 	 */
298*0Sstevel@tonic-gate 	ep->pcd_ntattr = 0;
299*0Sstevel@tonic-gate 	if (!IS_FAT32(fsp))
300*0Sstevel@tonic-gate 		ep->un.pcd_eattr = 0;
301*0Sstevel@tonic-gate 
302*0Sstevel@tonic-gate 	if (vap && ((vap->va_mode & 0222) == 0))
303*0Sstevel@tonic-gate 		ep->pcd_attr |=  PCA_RDONLY;
304*0Sstevel@tonic-gate 	if (vap && (vap->va_type == VDIR)) {
305*0Sstevel@tonic-gate 		pc_cluster32_t cn;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 		ep->pcd_attr |= PCA_DIR;
308*0Sstevel@tonic-gate 		/*
309*0Sstevel@tonic-gate 		 * Make dot and dotdot entries for a new directory.
310*0Sstevel@tonic-gate 		 */
311*0Sstevel@tonic-gate 		cn = pc_alloccluster(fsp, 0);
312*0Sstevel@tonic-gate 		switch (cn) {
313*0Sstevel@tonic-gate 		case PCF_FREECLUSTER:
314*0Sstevel@tonic-gate 			return (ENOSPC);
315*0Sstevel@tonic-gate 		case PCF_ERRORCLUSTER:
316*0Sstevel@tonic-gate 			return (EIO);
317*0Sstevel@tonic-gate 		}
318*0Sstevel@tonic-gate 		bp = ngeteblk(fsp->pcfs_clsize);
319*0Sstevel@tonic-gate 		bp->b_edev = fsp->pcfs_xdev;
320*0Sstevel@tonic-gate 		bp->b_dev = cmpdev(bp->b_edev);
321*0Sstevel@tonic-gate 		bp->b_blkno = pc_cldaddr(fsp, cn);
322*0Sstevel@tonic-gate 		clrbuf(bp);
323*0Sstevel@tonic-gate 		pc_setstartcluster(fsp, ep, cn);
324*0Sstevel@tonic-gate 		pc_setstartcluster(fsp, &dirtemplate.t_dot, cn);
325*0Sstevel@tonic-gate 		cn = pc_getstartcluster(fsp, &dp->pc_entry);
326*0Sstevel@tonic-gate 		pc_setstartcluster(fsp, &dirtemplate.t_dotdot, cn);
327*0Sstevel@tonic-gate 		dirtemplate.t_dot.pcd_mtime =
328*0Sstevel@tonic-gate 		    dirtemplate.t_dotdot.pcd_mtime = ep->pcd_mtime;
329*0Sstevel@tonic-gate 		dirtemplate.t_dot.pcd_crtime =
330*0Sstevel@tonic-gate 		    dirtemplate.t_dotdot.pcd_crtime = ep->pcd_crtime;
331*0Sstevel@tonic-gate 		dirtemplate.t_dot.pcd_ladate =
332*0Sstevel@tonic-gate 		    dirtemplate.t_dotdot.pcd_ladate = ep->pcd_ladate;
333*0Sstevel@tonic-gate 		dirtemplate.t_dot.pcd_crtime_msec =
334*0Sstevel@tonic-gate 		    dirtemplate.t_dotdot.pcd_crtime_msec = 0;
335*0Sstevel@tonic-gate 		bcopy(&dirtemplate,
336*0Sstevel@tonic-gate 		    bp->b_un.b_addr, sizeof (dirtemplate));
337*0Sstevel@tonic-gate 		bwrite2(bp);
338*0Sstevel@tonic-gate 		error = geterror(bp);
339*0Sstevel@tonic-gate 		brelse(bp);
340*0Sstevel@tonic-gate 		if (error) {
341*0Sstevel@tonic-gate 			PC_DPRINTF0(1, "pc_makedirentry error");
342*0Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
343*0Sstevel@tonic-gate 			return (EIO);
344*0Sstevel@tonic-gate 		}
345*0Sstevel@tonic-gate 	} else {
346*0Sstevel@tonic-gate 		pc_setstartcluster(fsp, ep, 0);
347*0Sstevel@tonic-gate 	}
348*0Sstevel@tonic-gate 	bp = NULL;
349*0Sstevel@tonic-gate 	for (i = 0, ep = NULL; i < ndirentries; i++, ep++) {
350*0Sstevel@tonic-gate 		boff = pc_blkoff(fsp, offset);
351*0Sstevel@tonic-gate 		if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
352*0Sstevel@tonic-gate 			if (bp != NULL) {
353*0Sstevel@tonic-gate 				/* always modified */
354*0Sstevel@tonic-gate 				bwrite2(bp);
355*0Sstevel@tonic-gate 				error = geterror(bp);
356*0Sstevel@tonic-gate 				brelse(bp);
357*0Sstevel@tonic-gate 				if (error)
358*0Sstevel@tonic-gate 					return (error);
359*0Sstevel@tonic-gate 				bp = NULL;
360*0Sstevel@tonic-gate 			}
361*0Sstevel@tonic-gate 			error = pc_blkatoff(dp, offset, &bp, &ep);
362*0Sstevel@tonic-gate 			if (error)
363*0Sstevel@tonic-gate 				return (error);
364*0Sstevel@tonic-gate 		}
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 		*ep = direntries[i];
367*0Sstevel@tonic-gate 		offset += sizeof (struct pcdir);
368*0Sstevel@tonic-gate 	}
369*0Sstevel@tonic-gate 	if (bp != NULL) {
370*0Sstevel@tonic-gate 		/* always modified */
371*0Sstevel@tonic-gate 		bwrite2(bp);
372*0Sstevel@tonic-gate 		error = geterror(bp);
373*0Sstevel@tonic-gate 		brelse(bp);
374*0Sstevel@tonic-gate 		if (error)
375*0Sstevel@tonic-gate 			return (error);
376*0Sstevel@tonic-gate 	}
377*0Sstevel@tonic-gate 	return (0);
378*0Sstevel@tonic-gate }
379*0Sstevel@tonic-gate 
380*0Sstevel@tonic-gate /*
381*0Sstevel@tonic-gate  * Remove a name from a directory.
382*0Sstevel@tonic-gate  */
383*0Sstevel@tonic-gate int
384*0Sstevel@tonic-gate pc_dirremove(
385*0Sstevel@tonic-gate 	struct pcnode *dp,
386*0Sstevel@tonic-gate 	char *namep,
387*0Sstevel@tonic-gate 	struct vnode *cdir,
388*0Sstevel@tonic-gate 	enum vtype type)
389*0Sstevel@tonic-gate {
390*0Sstevel@tonic-gate 	struct slot slot;
391*0Sstevel@tonic-gate 	struct pcnode *pcp;
392*0Sstevel@tonic-gate 	int error;
393*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(dp);
394*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
395*0Sstevel@tonic-gate 	offset_t lfn_offset = -1;
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 	PC_DPRINTF2(4, "pc_dirremove (dp %p name %s)\n", (void *)dp, namep);
398*0Sstevel@tonic-gate 	if ((dp->pc_entry.pcd_attr & PCA_RDONLY) ||
399*0Sstevel@tonic-gate 	    PCA_IS_HIDDEN(fsp, dp->pc_entry.pcd_attr)) {
400*0Sstevel@tonic-gate 		return (EPERM);
401*0Sstevel@tonic-gate 	}
402*0Sstevel@tonic-gate 	error = pc_findentry(dp, namep, &slot, &lfn_offset);
403*0Sstevel@tonic-gate 	if (error)
404*0Sstevel@tonic-gate 		return (error);
405*0Sstevel@tonic-gate 	if (slot.sl_flags == SL_DOT) {
406*0Sstevel@tonic-gate 		error = EINVAL;
407*0Sstevel@tonic-gate 	} else if (slot.sl_flags == SL_DOTDOT) {
408*0Sstevel@tonic-gate 		error = ENOTEMPTY;
409*0Sstevel@tonic-gate 	} else {
410*0Sstevel@tonic-gate 		pcp =
411*0Sstevel@tonic-gate 		    pc_getnode(VFSTOPCFS(vp->v_vfsp),
412*0Sstevel@tonic-gate 			slot.sl_blkno, slot.sl_offset, slot.sl_ep);
413*0Sstevel@tonic-gate 	}
414*0Sstevel@tonic-gate 	if (error) {
415*0Sstevel@tonic-gate 		brelse(slot.sl_bp);
416*0Sstevel@tonic-gate 		return (error);
417*0Sstevel@tonic-gate 	}
418*0Sstevel@tonic-gate 	if (type == VDIR) {
419*0Sstevel@tonic-gate 		if (pcp->pc_entry.pcd_attr & PCA_DIR) {
420*0Sstevel@tonic-gate 			if (PCTOV(pcp) == cdir)
421*0Sstevel@tonic-gate 				error = EINVAL;
422*0Sstevel@tonic-gate 			else if (!pc_dirempty(pcp))
423*0Sstevel@tonic-gate 				error = ENOTEMPTY;
424*0Sstevel@tonic-gate 		} else {
425*0Sstevel@tonic-gate 			error = ENOTDIR;
426*0Sstevel@tonic-gate 		}
427*0Sstevel@tonic-gate 	} else {
428*0Sstevel@tonic-gate 		if (pcp->pc_entry.pcd_attr & PCA_DIR)
429*0Sstevel@tonic-gate 			error = EISDIR;
430*0Sstevel@tonic-gate 	}
431*0Sstevel@tonic-gate 	if (error == 0) {
432*0Sstevel@tonic-gate 		/*
433*0Sstevel@tonic-gate 		 * Mark the in core node and on disk entry
434*0Sstevel@tonic-gate 		 * as removed. The slot may then be reused.
435*0Sstevel@tonic-gate 		 * The files clusters will be deallocated
436*0Sstevel@tonic-gate 		 * when the last reference goes away.
437*0Sstevel@tonic-gate 		 */
438*0Sstevel@tonic-gate 		pcp->pc_eblkno = -1;
439*0Sstevel@tonic-gate 		pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
440*0Sstevel@tonic-gate 		if (lfn_offset != -1) {
441*0Sstevel@tonic-gate 			brelse(slot.sl_bp);
442*0Sstevel@tonic-gate 			error = pc_remove_long_fn(dp, lfn_offset);
443*0Sstevel@tonic-gate 			if (error) {
444*0Sstevel@tonic-gate 				VN_RELE(PCTOV(pcp));
445*0Sstevel@tonic-gate 				pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
446*0Sstevel@tonic-gate 				return (EIO);
447*0Sstevel@tonic-gate 			}
448*0Sstevel@tonic-gate 		} else {
449*0Sstevel@tonic-gate 			slot.sl_ep->pcd_filename[0] = PCD_ERASED;
450*0Sstevel@tonic-gate 			bwrite2(slot.sl_bp);
451*0Sstevel@tonic-gate 			error = geterror(slot.sl_bp);
452*0Sstevel@tonic-gate 			brelse(slot.sl_bp);
453*0Sstevel@tonic-gate 		}
454*0Sstevel@tonic-gate 		if (error) {
455*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
456*0Sstevel@tonic-gate 			pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
457*0Sstevel@tonic-gate 			return (EIO);
458*0Sstevel@tonic-gate 		} else if (type == VDIR) {
459*0Sstevel@tonic-gate 			error = pc_truncate(pcp, 0L);
460*0Sstevel@tonic-gate 		}
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	} else {
463*0Sstevel@tonic-gate 		brelse(slot.sl_bp);
464*0Sstevel@tonic-gate 	}
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate 	if (error == 0) {
467*0Sstevel@tonic-gate 		if (type == VDIR) {
468*0Sstevel@tonic-gate 			vnevent_rmdir(PCTOV(pcp));
469*0Sstevel@tonic-gate 		} else {
470*0Sstevel@tonic-gate 			vnevent_remove(PCTOV(pcp));
471*0Sstevel@tonic-gate 		}
472*0Sstevel@tonic-gate 	}
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	VN_RELE(PCTOV(pcp));
475*0Sstevel@tonic-gate 
476*0Sstevel@tonic-gate 	return (error);
477*0Sstevel@tonic-gate }
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate /*
480*0Sstevel@tonic-gate  * Determine whether a directory is empty.
481*0Sstevel@tonic-gate  */
482*0Sstevel@tonic-gate static int
483*0Sstevel@tonic-gate pc_dirempty(struct pcnode *pcp)
484*0Sstevel@tonic-gate {
485*0Sstevel@tonic-gate 	struct buf *bp;
486*0Sstevel@tonic-gate 	struct pcdir *ep;
487*0Sstevel@tonic-gate 	offset_t offset;
488*0Sstevel@tonic-gate 	int boff;
489*0Sstevel@tonic-gate 	char c;
490*0Sstevel@tonic-gate 	int error;
491*0Sstevel@tonic-gate 	struct vnode *vp;
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	vp = PCTOV(pcp);
494*0Sstevel@tonic-gate 	bp = NULL;
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 	offset = 0;
497*0Sstevel@tonic-gate 	for (;;) {
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 		/*
500*0Sstevel@tonic-gate 		 * If offset is on a block boundary,
501*0Sstevel@tonic-gate 		 * read in the next directory block.
502*0Sstevel@tonic-gate 		 * Release previous if it exists.
503*0Sstevel@tonic-gate 		 */
504*0Sstevel@tonic-gate 		boff = pc_blkoff(VFSTOPCFS(vp->v_vfsp), offset);
505*0Sstevel@tonic-gate 		if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
506*0Sstevel@tonic-gate 			if (bp != NULL)
507*0Sstevel@tonic-gate 				brelse(bp);
508*0Sstevel@tonic-gate 			if (error = pc_blkatoff(pcp, offset, &bp, &ep)) {
509*0Sstevel@tonic-gate 				return (error);
510*0Sstevel@tonic-gate 			}
511*0Sstevel@tonic-gate 		}
512*0Sstevel@tonic-gate 		if (PCDL_IS_LFN(ep)) {
513*0Sstevel@tonic-gate 			error = pc_extract_long_fn(pcp, NULL, &ep, &offset,
514*0Sstevel@tonic-gate 			    &bp);
515*0Sstevel@tonic-gate 			/*
516*0Sstevel@tonic-gate 			 * EINVAL means the lfn was invalid, so start with
517*0Sstevel@tonic-gate 			 * the next entry. Otherwise, an error occurred _or_
518*0Sstevel@tonic-gate 			 * the lfn is valid, either of which means the
519*0Sstevel@tonic-gate 			 * directory is not empty.
520*0Sstevel@tonic-gate 			 */
521*0Sstevel@tonic-gate 			if (error == EINVAL)
522*0Sstevel@tonic-gate 				continue;
523*0Sstevel@tonic-gate 			else {
524*0Sstevel@tonic-gate 				if (bp)
525*0Sstevel@tonic-gate 					brelse(bp);
526*0Sstevel@tonic-gate 				return (error);
527*0Sstevel@tonic-gate 			}
528*0Sstevel@tonic-gate 		}
529*0Sstevel@tonic-gate 		c = ep->pcd_filename[0];
530*0Sstevel@tonic-gate 		if (c == PCD_UNUSED)
531*0Sstevel@tonic-gate 			break;
532*0Sstevel@tonic-gate 		if ((c != '.') && (c != PCD_ERASED)) {
533*0Sstevel@tonic-gate 			brelse(bp);
534*0Sstevel@tonic-gate 			return (0);
535*0Sstevel@tonic-gate 		}
536*0Sstevel@tonic-gate 		if ((c == '.') && !PC_SHORTNAME_IS_DOT(ep->pcd_filename) &&
537*0Sstevel@tonic-gate 		    !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
538*0Sstevel@tonic-gate 			brelse(bp);
539*0Sstevel@tonic-gate 			return (0);
540*0Sstevel@tonic-gate 		}
541*0Sstevel@tonic-gate 		ep++;
542*0Sstevel@tonic-gate 		offset += sizeof (struct pcdir);
543*0Sstevel@tonic-gate 	}
544*0Sstevel@tonic-gate 	if (bp != NULL)
545*0Sstevel@tonic-gate 		brelse(bp);
546*0Sstevel@tonic-gate 	return (1);
547*0Sstevel@tonic-gate }
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate /*
550*0Sstevel@tonic-gate  * Rename a file.
551*0Sstevel@tonic-gate  */
552*0Sstevel@tonic-gate int
553*0Sstevel@tonic-gate pc_rename(
554*0Sstevel@tonic-gate 	struct pcnode *dp,		/* parent directory */
555*0Sstevel@tonic-gate 	struct pcnode *tdp,		/* target directory */
556*0Sstevel@tonic-gate 	char *snm,			/* source file name */
557*0Sstevel@tonic-gate 	char *tnm)			/* target file name */
558*0Sstevel@tonic-gate {
559*0Sstevel@tonic-gate 	struct pcnode *pcp;	/* pcnode we are trying to rename */
560*0Sstevel@tonic-gate 	struct pcnode *tpcp;	/* pcnode that's in our way */
561*0Sstevel@tonic-gate 	struct slot slot;
562*0Sstevel@tonic-gate 	int error;
563*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(dp);
564*0Sstevel@tonic-gate 	struct vnode *svp = NULL;
565*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
566*0Sstevel@tonic-gate 	int filecasechange = 0;
567*0Sstevel@tonic-gate 	int oldisdir = 0;
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 	PC_DPRINTF3(4, "pc_rename(0x%p, %s, %s)\n", (void *)dp, snm, tnm);
570*0Sstevel@tonic-gate 	/*
571*0Sstevel@tonic-gate 	 * Leading spaces are not allowed in DOS.
572*0Sstevel@tonic-gate 	 */
573*0Sstevel@tonic-gate 	if (*tnm == ' ')
574*0Sstevel@tonic-gate 		return (EINVAL);
575*0Sstevel@tonic-gate 	/*
576*0Sstevel@tonic-gate 	 * No dot or dotdot.
577*0Sstevel@tonic-gate 	 */
578*0Sstevel@tonic-gate 	if (PC_NAME_IS_DOT(snm) || PC_NAME_IS_DOTDOT(snm) ||
579*0Sstevel@tonic-gate 	    PC_NAME_IS_DOT(tnm) || PC_NAME_IS_DOTDOT(tnm))
580*0Sstevel@tonic-gate 		return (EINVAL);
581*0Sstevel@tonic-gate 	/*
582*0Sstevel@tonic-gate 	 * Get the source node.  We'll jump back to here if trying to
583*0Sstevel@tonic-gate 	 * move on top of an existing file, after deleting that file.
584*0Sstevel@tonic-gate 	 */
585*0Sstevel@tonic-gate top:
586*0Sstevel@tonic-gate 	error = pc_findentry(dp, snm, &slot, NULL);
587*0Sstevel@tonic-gate 	if (error) {
588*0Sstevel@tonic-gate 		return (error);
589*0Sstevel@tonic-gate 	}
590*0Sstevel@tonic-gate 	pcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
591*0Sstevel@tonic-gate 	    slot.sl_blkno, slot.sl_offset, slot.sl_ep);
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 	brelse(slot.sl_bp);
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate 	if (pcp)
596*0Sstevel@tonic-gate 		svp = PCTOV(pcp);
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 	/*
599*0Sstevel@tonic-gate 	 * is the rename invalid, i.e. rename("a", "a/a")
600*0Sstevel@tonic-gate 	 */
601*0Sstevel@tonic-gate 	if (pcp == tdp) {
602*0Sstevel@tonic-gate 		if (svp)
603*0Sstevel@tonic-gate 			VN_RELE(svp);
604*0Sstevel@tonic-gate 		return (EINVAL);
605*0Sstevel@tonic-gate 	}
606*0Sstevel@tonic-gate 
607*0Sstevel@tonic-gate 	/*
608*0Sstevel@tonic-gate 	 * Are we just changing the case of an existing name?
609*0Sstevel@tonic-gate 	 */
610*0Sstevel@tonic-gate 	if ((dp->pc_scluster == tdp->pc_scluster) &&
611*0Sstevel@tonic-gate 	    (strcasecmp(snm, tnm) == 0)) {
612*0Sstevel@tonic-gate 		filecasechange = 1;
613*0Sstevel@tonic-gate 	}
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	oldisdir = pcp->pc_entry.pcd_attr & PCA_DIR;
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 	/*
618*0Sstevel@tonic-gate 	 * see if the target exists
619*0Sstevel@tonic-gate 	 */
620*0Sstevel@tonic-gate 	error = pc_findentry(tdp, tnm, &slot, NULL);
621*0Sstevel@tonic-gate 	if (error == 0 && filecasechange == 0) {
622*0Sstevel@tonic-gate 		/*
623*0Sstevel@tonic-gate 		 * Target exists.  If it's a file, delete it.  If it's
624*0Sstevel@tonic-gate 		 * a directory, bail.
625*0Sstevel@tonic-gate 		 */
626*0Sstevel@tonic-gate 		int newisdir;
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate 		tpcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
629*0Sstevel@tonic-gate 		    slot.sl_blkno, slot.sl_offset, slot.sl_ep);
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 		newisdir = tpcp->pc_entry.pcd_attr & PCA_DIR;
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 		brelse(slot.sl_bp);
634*0Sstevel@tonic-gate 		vnevent_rename_dest(PCTOV(tpcp));
635*0Sstevel@tonic-gate 		VN_RELE(PCTOV(tpcp));
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate 		/*
638*0Sstevel@tonic-gate 		 * Error cases (from rename(2)):
639*0Sstevel@tonic-gate 		 * old is dir, new is dir: EEXIST
640*0Sstevel@tonic-gate 		 * old is dir, new is nondir: ENOTDIR
641*0Sstevel@tonic-gate 		 * old is nondir, new is dir: EISDIR
642*0Sstevel@tonic-gate 		 */
643*0Sstevel@tonic-gate 		if (!newisdir) {
644*0Sstevel@tonic-gate 			if (oldisdir) {
645*0Sstevel@tonic-gate 				error = ENOTDIR;
646*0Sstevel@tonic-gate 			} else {
647*0Sstevel@tonic-gate 				/* nondir/nondir, remove target */
648*0Sstevel@tonic-gate 				error = pc_dirremove(tdp, tnm,
649*0Sstevel@tonic-gate 				    (struct vnode *)NULL, VREG);
650*0Sstevel@tonic-gate 				if (error == 0) {
651*0Sstevel@tonic-gate 					VN_RELE(PCTOV(pcp));
652*0Sstevel@tonic-gate 					goto top;
653*0Sstevel@tonic-gate 				}
654*0Sstevel@tonic-gate 			}
655*0Sstevel@tonic-gate 		} else if (oldisdir) {
656*0Sstevel@tonic-gate 			/* dir/dir, remove target */
657*0Sstevel@tonic-gate 			error = pc_dirremove(tdp, tnm,
658*0Sstevel@tonic-gate 			    (struct vnode *)NULL, VDIR);
659*0Sstevel@tonic-gate 			if (error == 0) {
660*0Sstevel@tonic-gate 				VN_RELE(PCTOV(pcp));
661*0Sstevel@tonic-gate 				goto top;
662*0Sstevel@tonic-gate 			}
663*0Sstevel@tonic-gate 			/* Follow rename(2)'s spec... */
664*0Sstevel@tonic-gate 			if (error == ENOTEMPTY) {
665*0Sstevel@tonic-gate 				error = EEXIST;
666*0Sstevel@tonic-gate 			}
667*0Sstevel@tonic-gate 		} else {
668*0Sstevel@tonic-gate 			/* nondir/dir, bail */
669*0Sstevel@tonic-gate 			error = EISDIR;
670*0Sstevel@tonic-gate 		}
671*0Sstevel@tonic-gate 	}
672*0Sstevel@tonic-gate 
673*0Sstevel@tonic-gate 	if ((error == 0) || (error == ENOENT)) {
674*0Sstevel@tonic-gate 		offset_t lfn_offset = -1;
675*0Sstevel@tonic-gate 		int	blkno;
676*0Sstevel@tonic-gate 		struct pcdir *direntries;
677*0Sstevel@tonic-gate 		struct pcdir *ep;
678*0Sstevel@tonic-gate 		int	ndirentries;
679*0Sstevel@tonic-gate 		pc_cluster16_t pct_lo;
680*0Sstevel@tonic-gate 		pc_cluster16_t pct_hi;
681*0Sstevel@tonic-gate 		offset_t offset;
682*0Sstevel@tonic-gate 		int	boff;
683*0Sstevel@tonic-gate 		struct buf *bp = NULL;
684*0Sstevel@tonic-gate 		uchar_t	attr;
685*0Sstevel@tonic-gate 		int	size;
686*0Sstevel@tonic-gate 		struct pctime mtime;
687*0Sstevel@tonic-gate 		struct pctime crtime;
688*0Sstevel@tonic-gate 		uchar_t	ntattr;
689*0Sstevel@tonic-gate 		ushort_t ladate;
690*0Sstevel@tonic-gate 		ushort_t eattr;
691*0Sstevel@tonic-gate 		uchar_t	crtime_msec;
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate 		/*
694*0Sstevel@tonic-gate 		 * Rename the source.
695*0Sstevel@tonic-gate 		 */
696*0Sstevel@tonic-gate 		/*
697*0Sstevel@tonic-gate 		 * Delete the old name, and create a new name.
698*0Sstevel@tonic-gate 		 */
699*0Sstevel@tonic-gate 		if (filecasechange == 1 && error == 0)
700*0Sstevel@tonic-gate 			brelse(slot.sl_bp);
701*0Sstevel@tonic-gate 		ndirentries = direntries_needed(tdp, tnm);
702*0Sstevel@tonic-gate 		if (ndirentries == -1) {
703*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
704*0Sstevel@tonic-gate 			return (EINVAL);
705*0Sstevel@tonic-gate 		}
706*0Sstevel@tonic-gate 		/*
707*0Sstevel@tonic-gate 		 * first see if we have enough space to create the new
708*0Sstevel@tonic-gate 		 * name before destroying the old one.
709*0Sstevel@tonic-gate 		 */
710*0Sstevel@tonic-gate 		offset = pc_find_free_space(tdp, ndirentries);
711*0Sstevel@tonic-gate 		if (offset == -1) {
712*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
713*0Sstevel@tonic-gate 			return (ENOSPC);
714*0Sstevel@tonic-gate 		}
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate 		error = pc_findentry(dp, snm, &slot, &lfn_offset);
717*0Sstevel@tonic-gate 		if (error) {
718*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
719*0Sstevel@tonic-gate 			return (error);
720*0Sstevel@tonic-gate 		}
721*0Sstevel@tonic-gate 		pct_lo = slot.sl_ep->pcd_scluster_lo;
722*0Sstevel@tonic-gate 		if (IS_FAT32(fsp))
723*0Sstevel@tonic-gate 			pct_hi = slot.sl_ep->un.pcd_scluster_hi;
724*0Sstevel@tonic-gate 		else
725*0Sstevel@tonic-gate 			eattr = slot.sl_ep->un.pcd_eattr;
726*0Sstevel@tonic-gate 		size = slot.sl_ep->pcd_size;
727*0Sstevel@tonic-gate 		attr = slot.sl_ep->pcd_attr;
728*0Sstevel@tonic-gate 		mtime = slot.sl_ep->pcd_mtime;
729*0Sstevel@tonic-gate 		crtime = slot.sl_ep->pcd_crtime;
730*0Sstevel@tonic-gate 		crtime_msec = slot.sl_ep->pcd_crtime_msec;
731*0Sstevel@tonic-gate 		ntattr = slot.sl_ep->pcd_ntattr;
732*0Sstevel@tonic-gate 		ladate = slot.sl_ep->pcd_ladate;
733*0Sstevel@tonic-gate 
734*0Sstevel@tonic-gate 		if (lfn_offset != -1) {
735*0Sstevel@tonic-gate 			brelse(slot.sl_bp);
736*0Sstevel@tonic-gate 			error = pc_remove_long_fn(dp, lfn_offset);
737*0Sstevel@tonic-gate 			if (error) {
738*0Sstevel@tonic-gate 				VN_RELE(PCTOV(pcp));
739*0Sstevel@tonic-gate 				pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
740*0Sstevel@tonic-gate 				return (error);
741*0Sstevel@tonic-gate 			}
742*0Sstevel@tonic-gate 		} else {
743*0Sstevel@tonic-gate 			slot.sl_ep->pcd_filename[0] =
744*0Sstevel@tonic-gate 			    pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
745*0Sstevel@tonic-gate 			bwrite2(slot.sl_bp);
746*0Sstevel@tonic-gate 			error = geterror(slot.sl_bp);
747*0Sstevel@tonic-gate 			brelse(slot.sl_bp);
748*0Sstevel@tonic-gate 		}
749*0Sstevel@tonic-gate 		if (error) {
750*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
751*0Sstevel@tonic-gate 			pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
752*0Sstevel@tonic-gate 			return (EIO);
753*0Sstevel@tonic-gate 		}
754*0Sstevel@tonic-gate 
755*0Sstevel@tonic-gate 		/*
756*0Sstevel@tonic-gate 		 * Make an entry from the supplied attributes.
757*0Sstevel@tonic-gate 		 */
758*0Sstevel@tonic-gate 		direntries = pc_name_to_pcdir(tdp, tnm, ndirentries, &error);
759*0Sstevel@tonic-gate 		if (direntries == NULL) {
760*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
761*0Sstevel@tonic-gate 			return (error);
762*0Sstevel@tonic-gate 		}
763*0Sstevel@tonic-gate 		error = pc_makedirentry(tdp, direntries, ndirentries, NULL,
764*0Sstevel@tonic-gate 		    offset);
765*0Sstevel@tonic-gate 		kmem_free(direntries, ndirentries * sizeof (struct pcdir));
766*0Sstevel@tonic-gate 		if (error) {
767*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
768*0Sstevel@tonic-gate 			return (error);
769*0Sstevel@tonic-gate 		}
770*0Sstevel@tonic-gate 		/* advance to short name */
771*0Sstevel@tonic-gate 		offset += (ndirentries - 1)  * sizeof (struct pcdir);
772*0Sstevel@tonic-gate 		boff = pc_blkoff(fsp, offset);
773*0Sstevel@tonic-gate 		error = pc_blkatoff(tdp, offset, &bp, &ep);
774*0Sstevel@tonic-gate 		if (error) {
775*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
776*0Sstevel@tonic-gate 			return (error);
777*0Sstevel@tonic-gate 		}
778*0Sstevel@tonic-gate 		blkno = pc_daddrdb(fsp, bp->b_blkno);
779*0Sstevel@tonic-gate 		ep->pcd_scluster_lo = pct_lo;
780*0Sstevel@tonic-gate 		if (IS_FAT32(fsp))
781*0Sstevel@tonic-gate 			ep->un.pcd_scluster_hi = pct_hi;
782*0Sstevel@tonic-gate 		else
783*0Sstevel@tonic-gate 			ep->un.pcd_eattr = eattr;
784*0Sstevel@tonic-gate 		ep->pcd_size = size;
785*0Sstevel@tonic-gate 		ep->pcd_attr = attr;
786*0Sstevel@tonic-gate 		ep->pcd_mtime = mtime;
787*0Sstevel@tonic-gate 		ep->pcd_crtime = crtime;
788*0Sstevel@tonic-gate 		ep->pcd_crtime_msec = crtime_msec;
789*0Sstevel@tonic-gate 		ep->pcd_ntattr = ntattr;
790*0Sstevel@tonic-gate 		ep->pcd_ladate = ladate;
791*0Sstevel@tonic-gate 		bwrite2(bp);
792*0Sstevel@tonic-gate 		error = geterror(bp);
793*0Sstevel@tonic-gate 		pcp->pc_eblkno = blkno;
794*0Sstevel@tonic-gate 		pcp->pc_eoffset = boff;
795*0Sstevel@tonic-gate 		pcp->pc_entry = *ep;
796*0Sstevel@tonic-gate 		pcp->pc_flags |= PC_CHG;
797*0Sstevel@tonic-gate 		brelse(bp);
798*0Sstevel@tonic-gate 		if (error) {
799*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
800*0Sstevel@tonic-gate 			pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
801*0Sstevel@tonic-gate 			return (EIO);
802*0Sstevel@tonic-gate 		}
803*0Sstevel@tonic-gate 		/* No need to fix ".." if we're renaming within a dir */
804*0Sstevel@tonic-gate 		if (oldisdir && dp != tdp) {
805*0Sstevel@tonic-gate 			if ((error = pc_dirfixdotdot(pcp, dp, tdp)) != 0) {
806*0Sstevel@tonic-gate 				VN_RELE(PCTOV(pcp));
807*0Sstevel@tonic-gate 				return (error);
808*0Sstevel@tonic-gate 			}
809*0Sstevel@tonic-gate 		}
810*0Sstevel@tonic-gate 		if ((error = pc_nodeupdate(pcp)) != 0) {
811*0Sstevel@tonic-gate 			VN_RELE(PCTOV(pcp));
812*0Sstevel@tonic-gate 			return (error);
813*0Sstevel@tonic-gate 		}
814*0Sstevel@tonic-gate 	}
815*0Sstevel@tonic-gate out:
816*0Sstevel@tonic-gate 	vnevent_rename_src(PCTOV(pcp));
817*0Sstevel@tonic-gate 	VN_RELE(PCTOV(pcp));
818*0Sstevel@tonic-gate 
819*0Sstevel@tonic-gate 	return (error);
820*0Sstevel@tonic-gate }
821*0Sstevel@tonic-gate 
822*0Sstevel@tonic-gate /*
823*0Sstevel@tonic-gate  * Fix the ".." entry of the child directory so that it points to the
824*0Sstevel@tonic-gate  * new parent directory instead of the old one.
825*0Sstevel@tonic-gate  */
826*0Sstevel@tonic-gate static int
827*0Sstevel@tonic-gate pc_dirfixdotdot(struct pcnode *dp,	/* child directory being moved */
828*0Sstevel@tonic-gate 	struct pcnode *opdp,		/* old parent directory */
829*0Sstevel@tonic-gate 	struct pcnode *npdp)		/* new parent directory */
830*0Sstevel@tonic-gate {
831*0Sstevel@tonic-gate 	pc_cluster32_t cn;
832*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(dp);
833*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
834*0Sstevel@tonic-gate 	int error = 0;
835*0Sstevel@tonic-gate 	struct buf *bp = NULL;
836*0Sstevel@tonic-gate 	struct pcdir *ep = NULL;
837*0Sstevel@tonic-gate 	struct pcdir *tep = NULL;
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 	/*
840*0Sstevel@tonic-gate 	 * set the new child's ".." directory entry starting cluster to
841*0Sstevel@tonic-gate 	 * point to the new parent's starting cluster
842*0Sstevel@tonic-gate 	 */
843*0Sstevel@tonic-gate 	ASSERT(opdp != npdp);
844*0Sstevel@tonic-gate 	error = pc_blkatoff(dp, (offset_t)0, &bp, &ep);
845*0Sstevel@tonic-gate 	if (error) {
846*0Sstevel@tonic-gate 		PC_DPRINTF0(1, "pc_dirfixdotdot: error in blkatoff\n");
847*0Sstevel@tonic-gate 		return (error);
848*0Sstevel@tonic-gate 	}
849*0Sstevel@tonic-gate 	tep = ep;
850*0Sstevel@tonic-gate 	ep++;
851*0Sstevel@tonic-gate 	if (!PC_SHORTNAME_IS_DOT(tep->pcd_filename) &&
852*0Sstevel@tonic-gate 	    !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
853*0Sstevel@tonic-gate 		PC_DPRINTF0(1, "pc_dirfixdotdot: mangled directory entry\n");
854*0Sstevel@tonic-gate 		error = ENOTDIR;
855*0Sstevel@tonic-gate 		return (error);
856*0Sstevel@tonic-gate 	}
857*0Sstevel@tonic-gate 	cn = pc_getstartcluster(fsp, &npdp->pc_entry);
858*0Sstevel@tonic-gate 	pc_setstartcluster(fsp, ep, cn);
859*0Sstevel@tonic-gate 
860*0Sstevel@tonic-gate 	bwrite2(bp);
861*0Sstevel@tonic-gate 	error = geterror(bp);
862*0Sstevel@tonic-gate 	brelse(bp);
863*0Sstevel@tonic-gate 	if (error) {
864*0Sstevel@tonic-gate 		PC_DPRINTF0(1, "pc_dirfixdotdot: error in write\n");
865*0Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
866*0Sstevel@tonic-gate 		return (EIO);
867*0Sstevel@tonic-gate 	}
868*0Sstevel@tonic-gate 	return (0);
869*0Sstevel@tonic-gate }
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 
872*0Sstevel@tonic-gate /*
873*0Sstevel@tonic-gate  * Search a directory for an entry.
874*0Sstevel@tonic-gate  * The directory should be locked as this routine
875*0Sstevel@tonic-gate  * will sleep on I/O while searching.
876*0Sstevel@tonic-gate  */
877*0Sstevel@tonic-gate static int
878*0Sstevel@tonic-gate pc_findentry(
879*0Sstevel@tonic-gate 	struct pcnode *dp,		/* parent directory */
880*0Sstevel@tonic-gate 	char *namep,			/* name to lookup */
881*0Sstevel@tonic-gate 	struct slot *slotp,
882*0Sstevel@tonic-gate 	offset_t *lfn_offset)
883*0Sstevel@tonic-gate {
884*0Sstevel@tonic-gate 	offset_t offset;
885*0Sstevel@tonic-gate 	struct pcdir *ep = NULL;
886*0Sstevel@tonic-gate 	int boff;
887*0Sstevel@tonic-gate 	int error;
888*0Sstevel@tonic-gate 	struct vnode *vp;
889*0Sstevel@tonic-gate 	struct pcfs *fsp;
890*0Sstevel@tonic-gate 
891*0Sstevel@tonic-gate 	vp = PCTOV(dp);
892*0Sstevel@tonic-gate 	PC_DPRINTF2(6, "pc_findentry: looking for %s in dir 0x%p\n", namep,
893*0Sstevel@tonic-gate 	    (void *)dp);
894*0Sstevel@tonic-gate 	slotp->sl_status = SL_NONE;
895*0Sstevel@tonic-gate 	if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
896*0Sstevel@tonic-gate 		return (ENOTDIR);
897*0Sstevel@tonic-gate 	}
898*0Sstevel@tonic-gate 	/*
899*0Sstevel@tonic-gate 	 * Verify that the dp is still valid on the disk
900*0Sstevel@tonic-gate 	 */
901*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
902*0Sstevel@tonic-gate 	error = pc_verify(fsp);
903*0Sstevel@tonic-gate 	if (error)
904*0Sstevel@tonic-gate 		return (error);
905*0Sstevel@tonic-gate 
906*0Sstevel@tonic-gate 	slotp->sl_bp = NULL;
907*0Sstevel@tonic-gate 	offset = 0;
908*0Sstevel@tonic-gate 	for (;;) {
909*0Sstevel@tonic-gate 		/*
910*0Sstevel@tonic-gate 		 * If offset is on a block boundary,
911*0Sstevel@tonic-gate 		 * read in the next directory block.
912*0Sstevel@tonic-gate 		 * Release previous if it exists.
913*0Sstevel@tonic-gate 		 */
914*0Sstevel@tonic-gate 		boff = pc_blkoff(fsp, offset);
915*0Sstevel@tonic-gate 		if (boff == 0 || slotp->sl_bp == NULL ||
916*0Sstevel@tonic-gate 		    boff >= slotp->sl_bp->b_bcount) {
917*0Sstevel@tonic-gate 			if (slotp->sl_bp != NULL) {
918*0Sstevel@tonic-gate 				brelse(slotp->sl_bp);
919*0Sstevel@tonic-gate 				slotp->sl_bp = NULL;
920*0Sstevel@tonic-gate 			}
921*0Sstevel@tonic-gate 			error = pc_blkatoff(dp, offset, &slotp->sl_bp, &ep);
922*0Sstevel@tonic-gate 			if (error == ENOENT && slotp->sl_status == SL_NONE) {
923*0Sstevel@tonic-gate 				slotp->sl_status = SL_EXTEND;
924*0Sstevel@tonic-gate 				slotp->sl_offset = (int)offset;
925*0Sstevel@tonic-gate 			}
926*0Sstevel@tonic-gate 			if (error)
927*0Sstevel@tonic-gate 				return (error);
928*0Sstevel@tonic-gate 		}
929*0Sstevel@tonic-gate 		if ((ep->pcd_filename[0] == PCD_UNUSED) ||
930*0Sstevel@tonic-gate 		    (ep->pcd_filename[0] == PCD_ERASED)) {
931*0Sstevel@tonic-gate 			/*
932*0Sstevel@tonic-gate 			 * note empty slots, in case name is not found
933*0Sstevel@tonic-gate 			 */
934*0Sstevel@tonic-gate 			if (slotp->sl_status == SL_NONE) {
935*0Sstevel@tonic-gate 				slotp->sl_status = SL_FOUND;
936*0Sstevel@tonic-gate 				slotp->sl_blkno = pc_daddrdb(fsp,
937*0Sstevel@tonic-gate 				    slotp->sl_bp->b_blkno);
938*0Sstevel@tonic-gate 				slotp->sl_offset = boff;
939*0Sstevel@tonic-gate 			}
940*0Sstevel@tonic-gate 			/*
941*0Sstevel@tonic-gate 			 * If unused we've hit the end of the directory
942*0Sstevel@tonic-gate 			 */
943*0Sstevel@tonic-gate 			if (ep->pcd_filename[0] == PCD_UNUSED)
944*0Sstevel@tonic-gate 				break;
945*0Sstevel@tonic-gate 			offset += sizeof (struct pcdir);
946*0Sstevel@tonic-gate 			ep++;
947*0Sstevel@tonic-gate 			continue;
948*0Sstevel@tonic-gate 		}
949*0Sstevel@tonic-gate 		if (PCDL_IS_LFN(ep)) {
950*0Sstevel@tonic-gate 			offset_t t = offset;
951*0Sstevel@tonic-gate 			if (pc_match_long_fn(dp, namep, &ep,
952*0Sstevel@tonic-gate 			    slotp, &offset) == 0) {
953*0Sstevel@tonic-gate 				if (lfn_offset != NULL)
954*0Sstevel@tonic-gate 					*lfn_offset = t;
955*0Sstevel@tonic-gate 				return (0);
956*0Sstevel@tonic-gate 			}
957*0Sstevel@tonic-gate 			continue;
958*0Sstevel@tonic-gate 		}
959*0Sstevel@tonic-gate 		if (pc_match_short_fn(dp, namep, &ep, slotp, &offset) == 0)
960*0Sstevel@tonic-gate 			return (0);
961*0Sstevel@tonic-gate 	}
962*0Sstevel@tonic-gate 	if (slotp->sl_bp != NULL) {
963*0Sstevel@tonic-gate 		brelse(slotp->sl_bp);
964*0Sstevel@tonic-gate 		slotp->sl_bp = NULL;
965*0Sstevel@tonic-gate 	}
966*0Sstevel@tonic-gate 	return (ENOENT);
967*0Sstevel@tonic-gate }
968*0Sstevel@tonic-gate 
969*0Sstevel@tonic-gate /*
970*0Sstevel@tonic-gate  * Obtain the block at offset "offset" in file pcp.
971*0Sstevel@tonic-gate  */
972*0Sstevel@tonic-gate int
973*0Sstevel@tonic-gate pc_blkatoff(
974*0Sstevel@tonic-gate 	struct pcnode *pcp,
975*0Sstevel@tonic-gate 	offset_t offset,
976*0Sstevel@tonic-gate 	struct buf **bpp,
977*0Sstevel@tonic-gate 	struct pcdir **epp)
978*0Sstevel@tonic-gate {
979*0Sstevel@tonic-gate 	struct pcfs *fsp;
980*0Sstevel@tonic-gate 	struct buf *bp;
981*0Sstevel@tonic-gate 	int size;
982*0Sstevel@tonic-gate 	int error;
983*0Sstevel@tonic-gate 	daddr_t bn;
984*0Sstevel@tonic-gate 
985*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
986*0Sstevel@tonic-gate 	size = pc_blksize(fsp, pcp, offset);
987*0Sstevel@tonic-gate 	if (pc_blkoff(fsp, offset) >= size) {
988*0Sstevel@tonic-gate 		PC_DPRINTF0(5, "pc_blkatoff: ENOENT\n");
989*0Sstevel@tonic-gate 		return (ENOENT);
990*0Sstevel@tonic-gate 	}
991*0Sstevel@tonic-gate 	error = pc_bmap(pcp, pc_lblkno(fsp, offset), &bn, (uint_t *)0);
992*0Sstevel@tonic-gate 	if (error)
993*0Sstevel@tonic-gate 		return (error);
994*0Sstevel@tonic-gate 
995*0Sstevel@tonic-gate 	bp = bread(fsp->pcfs_xdev, bn, size);
996*0Sstevel@tonic-gate 	if (bp->b_flags & B_ERROR) {
997*0Sstevel@tonic-gate 		PC_DPRINTF0(1, "pc_blkatoff: error\n");
998*0Sstevel@tonic-gate 		brelse(bp);
999*0Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
1000*0Sstevel@tonic-gate 		return (EIO);
1001*0Sstevel@tonic-gate 	}
1002*0Sstevel@tonic-gate 	if (epp) {
1003*0Sstevel@tonic-gate 		*epp =
1004*0Sstevel@tonic-gate 		    (struct pcdir *)(bp->b_un.b_addr + pc_blkoff(fsp, offset));
1005*0Sstevel@tonic-gate 	}
1006*0Sstevel@tonic-gate 	*bpp = bp;
1007*0Sstevel@tonic-gate 	return (0);
1008*0Sstevel@tonic-gate }
1009*0Sstevel@tonic-gate 
1010*0Sstevel@tonic-gate /*
1011*0Sstevel@tonic-gate  * Parse user filename into the pc form of "filename.extension".
1012*0Sstevel@tonic-gate  * If names are too long for the format (and enable_long_filenames is set)
1013*0Sstevel@tonic-gate  * it returns EINVAL (since either this name was read from the disk (so
1014*0Sstevel@tonic-gate  * it must fit), _or_ we're trying to match a long file name (so we
1015*0Sstevel@tonic-gate  * should fail).  Tests for characters that are invalid in PCDOS and
1016*0Sstevel@tonic-gate  * converts to upper case (unless foldcase is 0).
1017*0Sstevel@tonic-gate  */
1018*0Sstevel@tonic-gate static int
1019*0Sstevel@tonic-gate pc_parsename(
1020*0Sstevel@tonic-gate 	char *namep,
1021*0Sstevel@tonic-gate 	char *fnamep,
1022*0Sstevel@tonic-gate 	char *fextp)
1023*0Sstevel@tonic-gate {
1024*0Sstevel@tonic-gate 	int n;
1025*0Sstevel@tonic-gate 	char c;
1026*0Sstevel@tonic-gate 
1027*0Sstevel@tonic-gate 	n = PCFNAMESIZE;
1028*0Sstevel@tonic-gate 	c = *namep++;
1029*0Sstevel@tonic-gate 	if (c == 0)
1030*0Sstevel@tonic-gate 		return (EINVAL);
1031*0Sstevel@tonic-gate 	if (c == '.') {
1032*0Sstevel@tonic-gate 		/*
1033*0Sstevel@tonic-gate 		 * check for "." and "..".
1034*0Sstevel@tonic-gate 		 */
1035*0Sstevel@tonic-gate 		*fnamep++ = c;
1036*0Sstevel@tonic-gate 		n--;
1037*0Sstevel@tonic-gate 		if (c = *namep++) {
1038*0Sstevel@tonic-gate 			if ((c != '.') || (c = *namep)) /* ".x" or "..x" */
1039*0Sstevel@tonic-gate 				return (EINVAL);
1040*0Sstevel@tonic-gate 			*fnamep++ = '.';
1041*0Sstevel@tonic-gate 			n--;
1042*0Sstevel@tonic-gate 		}
1043*0Sstevel@tonic-gate 	} else {
1044*0Sstevel@tonic-gate 		/*
1045*0Sstevel@tonic-gate 		 * filename up to '.'
1046*0Sstevel@tonic-gate 		 */
1047*0Sstevel@tonic-gate 		do {
1048*0Sstevel@tonic-gate 			if (n-- > 0) {
1049*0Sstevel@tonic-gate 				c = toupper(c);
1050*0Sstevel@tonic-gate 				if (!pc_validchar(c))
1051*0Sstevel@tonic-gate 					return (EINVAL);
1052*0Sstevel@tonic-gate 				*fnamep++ = c;
1053*0Sstevel@tonic-gate 			} else {
1054*0Sstevel@tonic-gate 				/* not short */
1055*0Sstevel@tonic-gate 				if (enable_long_filenames)
1056*0Sstevel@tonic-gate 					return (EINVAL);
1057*0Sstevel@tonic-gate 			}
1058*0Sstevel@tonic-gate 		} while ((c = *namep++) != '\0' && c != '.');
1059*0Sstevel@tonic-gate 	}
1060*0Sstevel@tonic-gate 	while (n-- > 0) {		/* fill with blanks */
1061*0Sstevel@tonic-gate 		*fnamep++ = ' ';
1062*0Sstevel@tonic-gate 	}
1063*0Sstevel@tonic-gate 	/*
1064*0Sstevel@tonic-gate 	 * remainder is extension
1065*0Sstevel@tonic-gate 	 */
1066*0Sstevel@tonic-gate 	n = PCFEXTSIZE;
1067*0Sstevel@tonic-gate 	if (c == '.') {
1068*0Sstevel@tonic-gate 		while ((c = *namep++) != '\0' && n--) {
1069*0Sstevel@tonic-gate 			c = toupper(c);
1070*0Sstevel@tonic-gate 			if (!pc_validchar(c))
1071*0Sstevel@tonic-gate 				return (EINVAL);
1072*0Sstevel@tonic-gate 			*fextp++ = c;
1073*0Sstevel@tonic-gate 		}
1074*0Sstevel@tonic-gate 		if (enable_long_filenames && (c != '\0')) {
1075*0Sstevel@tonic-gate 			/* not short */
1076*0Sstevel@tonic-gate 			return (EINVAL);
1077*0Sstevel@tonic-gate 		}
1078*0Sstevel@tonic-gate 	}
1079*0Sstevel@tonic-gate 	while (n-- > 0) {		/* fill with blanks */
1080*0Sstevel@tonic-gate 		*fextp++ = ' ';
1081*0Sstevel@tonic-gate 	}
1082*0Sstevel@tonic-gate 	return (0);
1083*0Sstevel@tonic-gate }
1084*0Sstevel@tonic-gate 
1085*0Sstevel@tonic-gate /*
1086*0Sstevel@tonic-gate  * Match a long filename entry with 'namep'. Also return failure
1087*0Sstevel@tonic-gate  * if the long filename isn't valid.
1088*0Sstevel@tonic-gate  */
1089*0Sstevel@tonic-gate int
1090*0Sstevel@tonic-gate pc_match_long_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1091*0Sstevel@tonic-gate     struct slot *slotp, offset_t *offset)
1092*0Sstevel@tonic-gate {
1093*0Sstevel@tonic-gate 	struct pcdir *ep = (struct pcdir *)*epp;
1094*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(pcp);
1095*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1096*0Sstevel@tonic-gate 	int	error = 0;
1097*0Sstevel@tonic-gate 	char	lfn[PCMAXNAMLEN+1];
1098*0Sstevel@tonic-gate 
1099*0Sstevel@tonic-gate 	error = pc_extract_long_fn(pcp, lfn, epp, offset, &slotp->sl_bp);
1100*0Sstevel@tonic-gate 	if (error) {
1101*0Sstevel@tonic-gate 		if (error == EINVAL) {
1102*0Sstevel@tonic-gate 			return (ENOENT);
1103*0Sstevel@tonic-gate 		} else
1104*0Sstevel@tonic-gate 			return (error);
1105*0Sstevel@tonic-gate 	}
1106*0Sstevel@tonic-gate 	ep = *epp;
1107*0Sstevel@tonic-gate 	if (strcasecmp(lfn, namep) == 0) {
1108*0Sstevel@tonic-gate 		/* match */
1109*0Sstevel@tonic-gate 		slotp->sl_flags = 0;
1110*0Sstevel@tonic-gate 		slotp->sl_blkno = pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1111*0Sstevel@tonic-gate 		slotp->sl_offset = pc_blkoff(fsp, *offset);
1112*0Sstevel@tonic-gate 		slotp->sl_ep = ep;
1113*0Sstevel@tonic-gate 		return (0);
1114*0Sstevel@tonic-gate 	}
1115*0Sstevel@tonic-gate 	*offset += sizeof (struct pcdir);
1116*0Sstevel@tonic-gate 	ep++;
1117*0Sstevel@tonic-gate 	*epp = ep;
1118*0Sstevel@tonic-gate 	return (ENOENT);
1119*0Sstevel@tonic-gate }
1120*0Sstevel@tonic-gate 
1121*0Sstevel@tonic-gate /*
1122*0Sstevel@tonic-gate  * Match a short filename entry with namep.
1123*0Sstevel@tonic-gate  */
1124*0Sstevel@tonic-gate int
1125*0Sstevel@tonic-gate pc_match_short_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1126*0Sstevel@tonic-gate     struct slot *slotp, offset_t *offset)
1127*0Sstevel@tonic-gate {
1128*0Sstevel@tonic-gate 	char fname[PCFNAMESIZE];
1129*0Sstevel@tonic-gate 	char fext[PCFEXTSIZE];
1130*0Sstevel@tonic-gate 	struct pcdir *ep = *epp;
1131*0Sstevel@tonic-gate 	int	error;
1132*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(pcp);
1133*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1134*0Sstevel@tonic-gate 	int boff = pc_blkoff(fsp, *offset);
1135*0Sstevel@tonic-gate 
1136*0Sstevel@tonic-gate 	if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
1137*0Sstevel@tonic-gate 		*offset += sizeof (struct pcdir);
1138*0Sstevel@tonic-gate 		ep++;
1139*0Sstevel@tonic-gate 		*epp = ep;
1140*0Sstevel@tonic-gate 		return (ENOENT);
1141*0Sstevel@tonic-gate 	}
1142*0Sstevel@tonic-gate 
1143*0Sstevel@tonic-gate 	error = pc_parsename(namep, fname, fext);
1144*0Sstevel@tonic-gate 	if (error) {
1145*0Sstevel@tonic-gate 		*offset += sizeof (struct pcdir);
1146*0Sstevel@tonic-gate 		ep++;
1147*0Sstevel@tonic-gate 		*epp = ep;
1148*0Sstevel@tonic-gate 		return (error);
1149*0Sstevel@tonic-gate 	}
1150*0Sstevel@tonic-gate 
1151*0Sstevel@tonic-gate 	if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1152*0Sstevel@tonic-gate 	    (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1153*0Sstevel@tonic-gate 		/*
1154*0Sstevel@tonic-gate 		 * found the file
1155*0Sstevel@tonic-gate 		 */
1156*0Sstevel@tonic-gate 		if (fname[0] == '.') {
1157*0Sstevel@tonic-gate 			if (fname[1] == '.')
1158*0Sstevel@tonic-gate 				slotp->sl_flags = SL_DOTDOT;
1159*0Sstevel@tonic-gate 			else
1160*0Sstevel@tonic-gate 				slotp->sl_flags = SL_DOT;
1161*0Sstevel@tonic-gate 		} else {
1162*0Sstevel@tonic-gate 			slotp->sl_flags = 0;
1163*0Sstevel@tonic-gate 		}
1164*0Sstevel@tonic-gate 		slotp->sl_blkno =
1165*0Sstevel@tonic-gate 		    pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1166*0Sstevel@tonic-gate 		slotp->sl_offset = boff;
1167*0Sstevel@tonic-gate 		slotp->sl_ep = ep;
1168*0Sstevel@tonic-gate 		return (0);
1169*0Sstevel@tonic-gate 	}
1170*0Sstevel@tonic-gate 	*offset += sizeof (struct pcdir);
1171*0Sstevel@tonic-gate 	ep++;
1172*0Sstevel@tonic-gate 	*epp = ep;
1173*0Sstevel@tonic-gate 	return (ENOENT);
1174*0Sstevel@tonic-gate }
1175*0Sstevel@tonic-gate 
1176*0Sstevel@tonic-gate /*
1177*0Sstevel@tonic-gate  * Remove a long filename entry starting at lfn_offset. It must be
1178*0Sstevel@tonic-gate  * a valid entry or we wouldn't have gotten here. Also remove the
1179*0Sstevel@tonic-gate  * short filename entry.
1180*0Sstevel@tonic-gate  */
1181*0Sstevel@tonic-gate static int
1182*0Sstevel@tonic-gate pc_remove_long_fn(struct pcnode *pcp, offset_t lfn_offset)
1183*0Sstevel@tonic-gate {
1184*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(pcp);
1185*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1186*0Sstevel@tonic-gate 	int boff;
1187*0Sstevel@tonic-gate 	struct buf *bp = NULL;
1188*0Sstevel@tonic-gate 	struct pcdir *ep = NULL;
1189*0Sstevel@tonic-gate 	int	error = 0;
1190*0Sstevel@tonic-gate 
1191*0Sstevel@tonic-gate 	/*
1192*0Sstevel@tonic-gate 	 * if we're in here, we know that the lfn is in the proper format
1193*0Sstevel@tonic-gate 	 * of <series-of-lfn-entries> followed by <sfn-entry>
1194*0Sstevel@tonic-gate 	 */
1195*0Sstevel@tonic-gate 	for (;;) {
1196*0Sstevel@tonic-gate 		boff = pc_blkoff(fsp, lfn_offset);
1197*0Sstevel@tonic-gate 		if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1198*0Sstevel@tonic-gate 			if (bp != NULL) {
1199*0Sstevel@tonic-gate 				bwrite2(bp);
1200*0Sstevel@tonic-gate 				error = geterror(bp);
1201*0Sstevel@tonic-gate 				brelse(bp);
1202*0Sstevel@tonic-gate 				if (error)
1203*0Sstevel@tonic-gate 					return (error);
1204*0Sstevel@tonic-gate 				bp = NULL;
1205*0Sstevel@tonic-gate 			}
1206*0Sstevel@tonic-gate 			error = pc_blkatoff(pcp, lfn_offset, &bp, &ep);
1207*0Sstevel@tonic-gate 			if (error)
1208*0Sstevel@tonic-gate 				return (error);
1209*0Sstevel@tonic-gate 		}
1210*0Sstevel@tonic-gate 		if (!PCDL_IS_LFN(ep)) {
1211*0Sstevel@tonic-gate 			/* done */
1212*0Sstevel@tonic-gate 			break;
1213*0Sstevel@tonic-gate 		}
1214*0Sstevel@tonic-gate 		/* zap it */
1215*0Sstevel@tonic-gate 		ep->pcd_filename[0] = PCD_ERASED;
1216*0Sstevel@tonic-gate 		ep->pcd_attr = 0;
1217*0Sstevel@tonic-gate 		lfn_offset += sizeof (struct pcdir);
1218*0Sstevel@tonic-gate 		ep++;
1219*0Sstevel@tonic-gate 	}
1220*0Sstevel@tonic-gate 	/* now we're on the short entry */
1221*0Sstevel@tonic-gate 
1222*0Sstevel@tonic-gate 	ep->pcd_filename[0] = PCD_ERASED;
1223*0Sstevel@tonic-gate 	ep->pcd_attr = 0;
1224*0Sstevel@tonic-gate 
1225*0Sstevel@tonic-gate 	if (bp != NULL) {
1226*0Sstevel@tonic-gate 		bwrite2(bp);
1227*0Sstevel@tonic-gate 		error = geterror(bp);
1228*0Sstevel@tonic-gate 		brelse(bp);
1229*0Sstevel@tonic-gate 		if (error)
1230*0Sstevel@tonic-gate 			return (error);
1231*0Sstevel@tonic-gate 	}
1232*0Sstevel@tonic-gate 	return (0);
1233*0Sstevel@tonic-gate }
1234*0Sstevel@tonic-gate 
1235*0Sstevel@tonic-gate /*
1236*0Sstevel@tonic-gate  * Find (and allocate) space in the directory denoted by
1237*0Sstevel@tonic-gate  * 'pcp'. for 'ndirentries' pcdir structures.
1238*0Sstevel@tonic-gate  * Return the offset at which to start, or -1 for failure.
1239*0Sstevel@tonic-gate  */
1240*0Sstevel@tonic-gate static offset_t
1241*0Sstevel@tonic-gate pc_find_free_space(struct pcnode *pcp, int ndirentries)
1242*0Sstevel@tonic-gate {
1243*0Sstevel@tonic-gate 	offset_t offset = 0;
1244*0Sstevel@tonic-gate 	offset_t spaceneeded = ndirentries * sizeof (struct pcdir);
1245*0Sstevel@tonic-gate 	offset_t spaceoffset;
1246*0Sstevel@tonic-gate 	offset_t spaceavail = 0;
1247*0Sstevel@tonic-gate 	int boff;
1248*0Sstevel@tonic-gate 	struct buf *bp = NULL;
1249*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(pcp);
1250*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1251*0Sstevel@tonic-gate 	struct pcdir *ep;
1252*0Sstevel@tonic-gate 	int	error;
1253*0Sstevel@tonic-gate 
1254*0Sstevel@tonic-gate 	spaceoffset = offset;
1255*0Sstevel@tonic-gate 	while (spaceneeded > spaceavail) {
1256*0Sstevel@tonic-gate 		/*
1257*0Sstevel@tonic-gate 		 * If offset is on a block boundary,
1258*0Sstevel@tonic-gate 		 * read in the next directory block.
1259*0Sstevel@tonic-gate 		 * Release previous if it exists.
1260*0Sstevel@tonic-gate 		 */
1261*0Sstevel@tonic-gate 		boff = pc_blkoff(fsp, offset);
1262*0Sstevel@tonic-gate 		if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1263*0Sstevel@tonic-gate 			if (bp != NULL) {
1264*0Sstevel@tonic-gate 				brelse(bp);
1265*0Sstevel@tonic-gate 				bp = NULL;
1266*0Sstevel@tonic-gate 			}
1267*0Sstevel@tonic-gate 			error = pc_blkatoff(pcp, offset, &bp, &ep);
1268*0Sstevel@tonic-gate 			if (error == ENOENT) {
1269*0Sstevel@tonic-gate 				daddr_t bn;
1270*0Sstevel@tonic-gate 
1271*0Sstevel@tonic-gate 				/* extend directory */
1272*0Sstevel@tonic-gate 				if (!IS_FAT32(fsp) && (vp->v_flag & VROOT))
1273*0Sstevel@tonic-gate 					return (-1);
1274*0Sstevel@tonic-gate 				while (spaceneeded > spaceavail) {
1275*0Sstevel@tonic-gate 					error = pc_balloc(pcp,
1276*0Sstevel@tonic-gate 					    pc_lblkno(fsp, offset), 1, &bn);
1277*0Sstevel@tonic-gate 					if (error)
1278*0Sstevel@tonic-gate 						return (-1);
1279*0Sstevel@tonic-gate 					pcp->pc_size += fsp->pcfs_clsize;
1280*0Sstevel@tonic-gate 					spaceavail += fsp->pcfs_clsize;
1281*0Sstevel@tonic-gate 					offset += fsp->pcfs_clsize;
1282*0Sstevel@tonic-gate 				}
1283*0Sstevel@tonic-gate 				return (spaceoffset);
1284*0Sstevel@tonic-gate 			}
1285*0Sstevel@tonic-gate 			if (error)
1286*0Sstevel@tonic-gate 				return (-1);
1287*0Sstevel@tonic-gate 		}
1288*0Sstevel@tonic-gate 		if ((ep->pcd_filename[0] == PCD_UNUSED) ||
1289*0Sstevel@tonic-gate 		    (ep->pcd_filename[0] == PCD_ERASED)) {
1290*0Sstevel@tonic-gate 			offset += sizeof (struct pcdir);
1291*0Sstevel@tonic-gate 			spaceavail += sizeof (struct pcdir);
1292*0Sstevel@tonic-gate 			ep++;
1293*0Sstevel@tonic-gate 			continue;
1294*0Sstevel@tonic-gate 		}
1295*0Sstevel@tonic-gate 		offset += sizeof (struct pcdir);
1296*0Sstevel@tonic-gate 		spaceavail = 0;
1297*0Sstevel@tonic-gate 		spaceoffset = offset;
1298*0Sstevel@tonic-gate 		ep++;
1299*0Sstevel@tonic-gate 	}
1300*0Sstevel@tonic-gate 	if (bp != NULL) {
1301*0Sstevel@tonic-gate 		brelse(bp);
1302*0Sstevel@tonic-gate 	}
1303*0Sstevel@tonic-gate 	return (spaceoffset);
1304*0Sstevel@tonic-gate }
1305*0Sstevel@tonic-gate 
1306*0Sstevel@tonic-gate /*
1307*0Sstevel@tonic-gate  * Return how many long filename entries are needed.
1308*0Sstevel@tonic-gate  * A maximum of PCLFNCHUNKSIZE characters per entry, plus one for a
1309*0Sstevel@tonic-gate  * short filename.
1310*0Sstevel@tonic-gate  */
1311*0Sstevel@tonic-gate static int
1312*0Sstevel@tonic-gate direntries_needed(struct pcnode *dp, char *namep)
1313*0Sstevel@tonic-gate {
1314*0Sstevel@tonic-gate 	int	ndirentries;
1315*0Sstevel@tonic-gate 	int	n;
1316*0Sstevel@tonic-gate 	struct pcdir ep;
1317*0Sstevel@tonic-gate 
1318*0Sstevel@tonic-gate 	if (enable_long_filenames == 0) {
1319*0Sstevel@tonic-gate 		return (1);
1320*0Sstevel@tonic-gate 	}
1321*0Sstevel@tonic-gate 	if (pc_is_short_file_name(namep, 0)) {
1322*0Sstevel@tonic-gate 		(void) pc_parsename(namep, ep.pcd_filename, ep.pcd_ext);
1323*0Sstevel@tonic-gate 		if (!shortname_exists(dp, ep.pcd_filename, ep.pcd_ext)) {
1324*0Sstevel@tonic-gate 			return (1);
1325*0Sstevel@tonic-gate 		}
1326*0Sstevel@tonic-gate 	}
1327*0Sstevel@tonic-gate 	if (pc_valid_long_fn(namep)) {
1328*0Sstevel@tonic-gate 		n = strlen(namep);
1329*0Sstevel@tonic-gate 		ndirentries = 1 + n / PCLFNCHUNKSIZE;
1330*0Sstevel@tonic-gate 		if ((n % PCLFNCHUNKSIZE) != 0)
1331*0Sstevel@tonic-gate 			ndirentries++;
1332*0Sstevel@tonic-gate 		return (ndirentries);
1333*0Sstevel@tonic-gate 	}
1334*0Sstevel@tonic-gate 	return (-1);
1335*0Sstevel@tonic-gate }
1336*0Sstevel@tonic-gate 
1337*0Sstevel@tonic-gate /*
1338*0Sstevel@tonic-gate  * Allocate and return an array of pcdir structures for the passed-in
1339*0Sstevel@tonic-gate  * name. ndirentries tells how many are required (including the short
1340*0Sstevel@tonic-gate  * filename entry). Just allocate and fill them in properly here so they
1341*0Sstevel@tonic-gate  * can be written out.
1342*0Sstevel@tonic-gate  */
1343*0Sstevel@tonic-gate static struct pcdir *
1344*0Sstevel@tonic-gate pc_name_to_pcdir(struct pcnode *dp, char *namep, int ndirentries, int *errret)
1345*0Sstevel@tonic-gate {
1346*0Sstevel@tonic-gate 	struct pcdir *bpcdir;
1347*0Sstevel@tonic-gate 	struct pcdir *ep;
1348*0Sstevel@tonic-gate 	struct pcdir_lfn *lep;
1349*0Sstevel@tonic-gate 	int	i;
1350*0Sstevel@tonic-gate 	uchar_t	cksum;
1351*0Sstevel@tonic-gate 	int	nchars;
1352*0Sstevel@tonic-gate 	int	error = 0;
1353*0Sstevel@tonic-gate 	int	n;
1354*0Sstevel@tonic-gate 	char	*nameend;
1355*0Sstevel@tonic-gate 
1356*0Sstevel@tonic-gate 	bpcdir = kmem_zalloc(ndirentries * sizeof (struct pcdir), KM_SLEEP);
1357*0Sstevel@tonic-gate 	ep = &bpcdir[ndirentries - 1];
1358*0Sstevel@tonic-gate 	if (ndirentries == 1) {
1359*0Sstevel@tonic-gate 		(void) pc_parsename(namep, ep->pcd_filename, ep->pcd_ext);
1360*0Sstevel@tonic-gate 		return (bpcdir);
1361*0Sstevel@tonic-gate 	}
1362*0Sstevel@tonic-gate 	n = strlen(namep);
1363*0Sstevel@tonic-gate 	nchars = PCLFNCHUNKSIZE;
1364*0Sstevel@tonic-gate 	nameend = namep + n;
1365*0Sstevel@tonic-gate 	if ((n % PCLFNCHUNKSIZE) != 0) {
1366*0Sstevel@tonic-gate 		nchars = (n % PCLFNCHUNKSIZE) + 1; /* null */
1367*0Sstevel@tonic-gate 		nameend++;
1368*0Sstevel@tonic-gate 	}
1369*0Sstevel@tonic-gate 
1370*0Sstevel@tonic-gate 	/* short file name */
1371*0Sstevel@tonic-gate 	error = generate_short_name(dp, namep, ep);
1372*0Sstevel@tonic-gate 	if (error) {
1373*0Sstevel@tonic-gate 		kmem_free(bpcdir, ndirentries * sizeof (struct pcdir));
1374*0Sstevel@tonic-gate 		*errret = error;
1375*0Sstevel@tonic-gate 		return (NULL);
1376*0Sstevel@tonic-gate 	}
1377*0Sstevel@tonic-gate 	cksum = pc_checksum_long_fn(ep->pcd_filename, ep->pcd_ext);
1378*0Sstevel@tonic-gate 	for (i = 0; i < (ndirentries - 1); i++) {
1379*0Sstevel@tonic-gate 		/* long file name */
1380*0Sstevel@tonic-gate 		nameend -= nchars;
1381*0Sstevel@tonic-gate 		lep = (struct pcdir_lfn *)&bpcdir[i];
1382*0Sstevel@tonic-gate 		set_long_fn_chunk(lep, nameend, nchars);
1383*0Sstevel@tonic-gate 		lep->pcdl_attr = PCDL_LFN_BITS;
1384*0Sstevel@tonic-gate 		lep->pcdl_checksum = cksum;
1385*0Sstevel@tonic-gate 		lep->pcdl_ordinal = (uchar_t)(ndirentries - i - 1);
1386*0Sstevel@tonic-gate 		nchars = PCLFNCHUNKSIZE;
1387*0Sstevel@tonic-gate 	}
1388*0Sstevel@tonic-gate 	lep = (struct pcdir_lfn *)&bpcdir[0];
1389*0Sstevel@tonic-gate 	lep->pcdl_ordinal |= 0x40;
1390*0Sstevel@tonic-gate 	return (bpcdir);
1391*0Sstevel@tonic-gate }
1392*0Sstevel@tonic-gate 
1393*0Sstevel@tonic-gate static int
1394*0Sstevel@tonic-gate generate_short_name(struct pcnode *dp, char *namep, struct pcdir *inep)
1395*0Sstevel@tonic-gate {
1396*0Sstevel@tonic-gate 	int	rev;
1397*0Sstevel@tonic-gate 	int	nchars;
1398*0Sstevel@tonic-gate 	int	i, j;
1399*0Sstevel@tonic-gate 	char	*dot = NULL;
1400*0Sstevel@tonic-gate 	char	fname[PCFNAMESIZE+1];
1401*0Sstevel@tonic-gate 	char	fext[PCFEXTSIZE+1];
1402*0Sstevel@tonic-gate 	char	scratch[8];
1403*0Sstevel@tonic-gate 	int	error = 0;
1404*0Sstevel@tonic-gate 	struct slot slot;
1405*0Sstevel@tonic-gate 	char	shortname[20];
1406*0Sstevel@tonic-gate 	int	force_tilde = 0;
1407*0Sstevel@tonic-gate 
1408*0Sstevel@tonic-gate 	/*
1409*0Sstevel@tonic-gate 	 * generate a unique short file name based on the long input name.
1410*0Sstevel@tonic-gate 	 *
1411*0Sstevel@tonic-gate 	 * Say, for "This is a very long filename.txt" generate
1412*0Sstevel@tonic-gate 	 * "THISIS~1.TXT", or "THISIS~2.TXT" if that's already there.
1413*0Sstevel@tonic-gate 	 * Skip invalid short name characters in the long name, plus
1414*0Sstevel@tonic-gate 	 * a couple NT skips (space and reverse backslash).
1415*0Sstevel@tonic-gate 	 *
1416*0Sstevel@tonic-gate 	 * Unfortunately, since this name would be hidden by the normal
1417*0Sstevel@tonic-gate 	 * lookup routine, we need to look for it ourselves. But luckily
1418*0Sstevel@tonic-gate 	 * we don't need to look at the lfn entries themselves.
1419*0Sstevel@tonic-gate 	 */
1420*0Sstevel@tonic-gate 	force_tilde = !pc_is_short_file_name(namep, 1);
1421*0Sstevel@tonic-gate 
1422*0Sstevel@tonic-gate 	/*
1423*0Sstevel@tonic-gate 	 * Strip off leading invalid characters.
1424*0Sstevel@tonic-gate 	 * We need this because names like '.login' are now ok, but the
1425*0Sstevel@tonic-gate 	 * short name needs to be something like LOGIN~1.
1426*0Sstevel@tonic-gate 	 */
1427*0Sstevel@tonic-gate 	for (; *namep != '\0'; namep++) {
1428*0Sstevel@tonic-gate 		if (*namep == ' ')
1429*0Sstevel@tonic-gate 			continue;
1430*0Sstevel@tonic-gate 		if (!pc_validchar(*namep) && !pc_validchar(toupper(*namep)))
1431*0Sstevel@tonic-gate 			continue;
1432*0Sstevel@tonic-gate 		break;
1433*0Sstevel@tonic-gate 	}
1434*0Sstevel@tonic-gate 	if (*namep == '\0')
1435*0Sstevel@tonic-gate 		return (EINVAL);
1436*0Sstevel@tonic-gate 	dot = strrchr(namep, '.');
1437*0Sstevel@tonic-gate 	if (dot != NULL) {
1438*0Sstevel@tonic-gate 		dot++;
1439*0Sstevel@tonic-gate 		for (j = 0, i = 0; j < PCFEXTSIZE; i++) {
1440*0Sstevel@tonic-gate 			if (dot[i] == '\0')
1441*0Sstevel@tonic-gate 				break;
1442*0Sstevel@tonic-gate 			/* skip valid, but not generally good characters */
1443*0Sstevel@tonic-gate 			if (dot[i] == ' ' || dot[i] == '\\')
1444*0Sstevel@tonic-gate 				continue;
1445*0Sstevel@tonic-gate 			if (pc_validchar(dot[i]))
1446*0Sstevel@tonic-gate 				fext[j++] = dot[i];
1447*0Sstevel@tonic-gate 			else if (pc_validchar(toupper(dot[i])))
1448*0Sstevel@tonic-gate 				fext[j++] = toupper(dot[i]);
1449*0Sstevel@tonic-gate 		}
1450*0Sstevel@tonic-gate 		for (i = j; i < PCFEXTSIZE; i++)
1451*0Sstevel@tonic-gate 			fext[i] = ' ';
1452*0Sstevel@tonic-gate 		dot--;
1453*0Sstevel@tonic-gate 	} else {
1454*0Sstevel@tonic-gate 		for (i = 0; i < PCFEXTSIZE; i++) {
1455*0Sstevel@tonic-gate 			fext[i] = ' ';
1456*0Sstevel@tonic-gate 		}
1457*0Sstevel@tonic-gate 	}
1458*0Sstevel@tonic-gate 	/*
1459*0Sstevel@tonic-gate 	 * We know we're a long name, not a short name (or we wouldn't
1460*0Sstevel@tonic-gate 	 * be here at all. But if uppercasing ourselves would be a short
1461*0Sstevel@tonic-gate 	 * name, then we can possibly avoid the ~N format.
1462*0Sstevel@tonic-gate 	 */
1463*0Sstevel@tonic-gate 	if (!force_tilde)
1464*0Sstevel@tonic-gate 		rev = 0;
1465*0Sstevel@tonic-gate 	else
1466*0Sstevel@tonic-gate 		rev = 1;
1467*0Sstevel@tonic-gate 	for (;;) {
1468*0Sstevel@tonic-gate 		bzero(fname, sizeof (fname));
1469*0Sstevel@tonic-gate 		nchars = PCFNAMESIZE;
1470*0Sstevel@tonic-gate 		if (rev) {
1471*0Sstevel@tonic-gate 			nchars--; /* ~ */
1472*0Sstevel@tonic-gate 			i = rev;
1473*0Sstevel@tonic-gate 			do {
1474*0Sstevel@tonic-gate 				nchars--;
1475*0Sstevel@tonic-gate 				i /= 10;
1476*0Sstevel@tonic-gate 			} while (i);
1477*0Sstevel@tonic-gate 			if (nchars <= 0) {
1478*0Sstevel@tonic-gate 				return (ENOSPC);
1479*0Sstevel@tonic-gate 			}
1480*0Sstevel@tonic-gate 		}
1481*0Sstevel@tonic-gate 		for (j = 0, i = 0; j < nchars; i++) {
1482*0Sstevel@tonic-gate 			if ((&namep[i] == dot) || (namep[i] == '\0'))
1483*0Sstevel@tonic-gate 				break;
1484*0Sstevel@tonic-gate 			/* skip valid, but not generally good characters */
1485*0Sstevel@tonic-gate 			if (namep[i] == ' ' || namep[i] == '\\')
1486*0Sstevel@tonic-gate 				continue;
1487*0Sstevel@tonic-gate 			if (pc_validchar(namep[i]))
1488*0Sstevel@tonic-gate 				fname[j++] = namep[i];
1489*0Sstevel@tonic-gate 			else if (pc_validchar(toupper(namep[i])))
1490*0Sstevel@tonic-gate 				fname[j++] = toupper(namep[i]);
1491*0Sstevel@tonic-gate 		}
1492*0Sstevel@tonic-gate 		if (j == 0) {
1493*0Sstevel@tonic-gate 			/* no characters in the prefix? */
1494*0Sstevel@tonic-gate 			return (EINVAL);
1495*0Sstevel@tonic-gate 		}
1496*0Sstevel@tonic-gate 		if (rev) {
1497*0Sstevel@tonic-gate 			(void) sprintf(scratch, "~%d", rev);
1498*0Sstevel@tonic-gate 			(void) strcat(fname, scratch);
1499*0Sstevel@tonic-gate 		}
1500*0Sstevel@tonic-gate 		for (i = strlen(fname); i < PCFNAMESIZE; i++)
1501*0Sstevel@tonic-gate 			fname[i] = ' ';
1502*0Sstevel@tonic-gate 		/* now see if it exists */
1503*0Sstevel@tonic-gate 		(void) pc_fname_ext_to_name(shortname, fname, fext, 0);
1504*0Sstevel@tonic-gate 		error = pc_findentry(dp, shortname, &slot, NULL);
1505*0Sstevel@tonic-gate 		if (error == 0) {
1506*0Sstevel@tonic-gate 			/* found it */
1507*0Sstevel@tonic-gate 			brelse(slot.sl_bp);
1508*0Sstevel@tonic-gate 			rev++;
1509*0Sstevel@tonic-gate 			continue;
1510*0Sstevel@tonic-gate 		}
1511*0Sstevel@tonic-gate 		if (!shortname_exists(dp, fname, fext))
1512*0Sstevel@tonic-gate 			break;
1513*0Sstevel@tonic-gate 		rev++;
1514*0Sstevel@tonic-gate 	}
1515*0Sstevel@tonic-gate 	(void) strncpy(inep->pcd_filename, fname, PCFNAMESIZE);
1516*0Sstevel@tonic-gate 	(void) strncpy(inep->pcd_ext, fext, PCFEXTSIZE);
1517*0Sstevel@tonic-gate 	return (0);
1518*0Sstevel@tonic-gate }
1519*0Sstevel@tonic-gate 
1520*0Sstevel@tonic-gate /*
1521*0Sstevel@tonic-gate  * Returns 1 if the passed-in filename is a short name, 0 if not.
1522*0Sstevel@tonic-gate  */
1523*0Sstevel@tonic-gate static int
1524*0Sstevel@tonic-gate pc_is_short_file_name(char *namep, int foldcase)
1525*0Sstevel@tonic-gate {
1526*0Sstevel@tonic-gate 	int	i;
1527*0Sstevel@tonic-gate 	char	c;
1528*0Sstevel@tonic-gate 
1529*0Sstevel@tonic-gate 	for (i = 0; i < PCFNAMESIZE; i++, namep++) {
1530*0Sstevel@tonic-gate 		if (*namep == '\0')
1531*0Sstevel@tonic-gate 			return (1);
1532*0Sstevel@tonic-gate 		if (*namep == '.')
1533*0Sstevel@tonic-gate 			break;
1534*0Sstevel@tonic-gate 		if (foldcase)
1535*0Sstevel@tonic-gate 			c = toupper(*namep);
1536*0Sstevel@tonic-gate 		else
1537*0Sstevel@tonic-gate 			c = *namep;
1538*0Sstevel@tonic-gate 		if (!pc_validchar(c))
1539*0Sstevel@tonic-gate 			return (0);
1540*0Sstevel@tonic-gate 	}
1541*0Sstevel@tonic-gate 	if (*namep == '\0')
1542*0Sstevel@tonic-gate 		return (1);
1543*0Sstevel@tonic-gate 	if (*namep != '.')
1544*0Sstevel@tonic-gate 		return (0);
1545*0Sstevel@tonic-gate 	namep++;
1546*0Sstevel@tonic-gate 	for (i = 0; i < PCFEXTSIZE; i++, namep++) {
1547*0Sstevel@tonic-gate 		if (*namep == '\0')
1548*0Sstevel@tonic-gate 			return (1);
1549*0Sstevel@tonic-gate 		if (foldcase)
1550*0Sstevel@tonic-gate 			c = toupper(*namep);
1551*0Sstevel@tonic-gate 		else
1552*0Sstevel@tonic-gate 			c = *namep;
1553*0Sstevel@tonic-gate 		if (!pc_validchar(c))
1554*0Sstevel@tonic-gate 			return (0);
1555*0Sstevel@tonic-gate 	}
1556*0Sstevel@tonic-gate 	/* we should be done. If not... */
1557*0Sstevel@tonic-gate 	if (*namep == '\0')
1558*0Sstevel@tonic-gate 		return (1);
1559*0Sstevel@tonic-gate 	return (0);
1560*0Sstevel@tonic-gate 
1561*0Sstevel@tonic-gate }
1562*0Sstevel@tonic-gate 
1563*0Sstevel@tonic-gate /*
1564*0Sstevel@tonic-gate  * We call this when we want to see if a short filename already exists
1565*0Sstevel@tonic-gate  * in the filesystem as part of a long filename. When creating a short
1566*0Sstevel@tonic-gate  * name (FILENAME.TXT from the user, or when generating one for a long
1567*0Sstevel@tonic-gate  * filename), we cannot allow one that is part of a long filename.
1568*0Sstevel@tonic-gate  * pc_findentry will find all the names that are visible (long or short),
1569*0Sstevel@tonic-gate  * but will not crack any long filename entries.
1570*0Sstevel@tonic-gate  */
1571*0Sstevel@tonic-gate static int
1572*0Sstevel@tonic-gate shortname_exists(struct pcnode *dp, char *fname, char *fext)
1573*0Sstevel@tonic-gate {
1574*0Sstevel@tonic-gate 	struct buf *bp = NULL;
1575*0Sstevel@tonic-gate 	int	offset = 0;
1576*0Sstevel@tonic-gate 	int	match = 0;
1577*0Sstevel@tonic-gate 	struct pcdir *ep;
1578*0Sstevel@tonic-gate 	struct vnode *vp = PCTOV(dp);
1579*0Sstevel@tonic-gate 	struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1580*0Sstevel@tonic-gate 	int	boff;
1581*0Sstevel@tonic-gate 	int	error = 0;
1582*0Sstevel@tonic-gate 
1583*0Sstevel@tonic-gate 	for (;;) {
1584*0Sstevel@tonic-gate 		boff = pc_blkoff(fsp, offset);
1585*0Sstevel@tonic-gate 		if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1586*0Sstevel@tonic-gate 			if (bp != NULL) {
1587*0Sstevel@tonic-gate 				brelse(bp);
1588*0Sstevel@tonic-gate 				bp = NULL;
1589*0Sstevel@tonic-gate 			}
1590*0Sstevel@tonic-gate 			error = pc_blkatoff(dp, offset, &bp, &ep);
1591*0Sstevel@tonic-gate 			if (error == ENOENT)
1592*0Sstevel@tonic-gate 				break;
1593*0Sstevel@tonic-gate 			if (error) {
1594*0Sstevel@tonic-gate 				return (1);
1595*0Sstevel@tonic-gate 			}
1596*0Sstevel@tonic-gate 		}
1597*0Sstevel@tonic-gate 		if (PCDL_IS_LFN(ep) ||
1598*0Sstevel@tonic-gate 		    (ep->pcd_filename[0] == PCD_ERASED)) {
1599*0Sstevel@tonic-gate 			offset += sizeof (struct pcdir);
1600*0Sstevel@tonic-gate 			ep++;
1601*0Sstevel@tonic-gate 			continue;
1602*0Sstevel@tonic-gate 		}
1603*0Sstevel@tonic-gate 		if (ep->pcd_filename[0] == PCD_UNUSED)
1604*0Sstevel@tonic-gate 			break;
1605*0Sstevel@tonic-gate 		/*
1606*0Sstevel@tonic-gate 		 * in use, and a short file name (either standalone
1607*0Sstevel@tonic-gate 		 * or associated with a long name
1608*0Sstevel@tonic-gate 		 */
1609*0Sstevel@tonic-gate 		if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1610*0Sstevel@tonic-gate 		    (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1611*0Sstevel@tonic-gate 			match = 1;
1612*0Sstevel@tonic-gate 			break;
1613*0Sstevel@tonic-gate 		}
1614*0Sstevel@tonic-gate 		offset += sizeof (struct pcdir);
1615*0Sstevel@tonic-gate 		ep++;
1616*0Sstevel@tonic-gate 	}
1617*0Sstevel@tonic-gate 	if (bp) {
1618*0Sstevel@tonic-gate 		brelse(bp);
1619*0Sstevel@tonic-gate 		bp = NULL;
1620*0Sstevel@tonic-gate 	}
1621*0Sstevel@tonic-gate 	return (match);
1622*0Sstevel@tonic-gate }
1623*0Sstevel@tonic-gate 
1624*0Sstevel@tonic-gate pc_cluster32_t
1625*0Sstevel@tonic-gate pc_getstartcluster(struct pcfs *fsp, struct pcdir *ep)
1626*0Sstevel@tonic-gate {
1627*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
1628*0Sstevel@tonic-gate 		pc_cluster32_t cn;
1629*0Sstevel@tonic-gate 		pc_cluster16_t hi16;
1630*0Sstevel@tonic-gate 		pc_cluster16_t lo16;
1631*0Sstevel@tonic-gate 
1632*0Sstevel@tonic-gate 		hi16 = ltohs(ep->un.pcd_scluster_hi);
1633*0Sstevel@tonic-gate 		lo16 = ltohs(ep->pcd_scluster_lo);
1634*0Sstevel@tonic-gate 		cn = (hi16 << 16) | lo16;
1635*0Sstevel@tonic-gate 		return (cn);
1636*0Sstevel@tonic-gate 	} else {
1637*0Sstevel@tonic-gate 		return (ltohs(ep->pcd_scluster_lo));
1638*0Sstevel@tonic-gate 	}
1639*0Sstevel@tonic-gate }
1640*0Sstevel@tonic-gate 
1641*0Sstevel@tonic-gate void
1642*0Sstevel@tonic-gate pc_setstartcluster(struct pcfs *fsp, struct pcdir *ep, pc_cluster32_t cln)
1643*0Sstevel@tonic-gate {
1644*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
1645*0Sstevel@tonic-gate 		pc_cluster16_t hi16;
1646*0Sstevel@tonic-gate 		pc_cluster16_t lo16;
1647*0Sstevel@tonic-gate 
1648*0Sstevel@tonic-gate 		hi16 = (cln >> 16) & 0xFFFF;
1649*0Sstevel@tonic-gate 		lo16 = cln & 0xFFFF;
1650*0Sstevel@tonic-gate 		ep->un.pcd_scluster_hi = htols(hi16);
1651*0Sstevel@tonic-gate 		ep->pcd_scluster_lo = htols(lo16);
1652*0Sstevel@tonic-gate 	} else {
1653*0Sstevel@tonic-gate 		pc_cluster16_t cln16;
1654*0Sstevel@tonic-gate 
1655*0Sstevel@tonic-gate 		cln16 = (pc_cluster16_t)cln;
1656*0Sstevel@tonic-gate 		ep->pcd_scluster_lo = htols(cln16);
1657*0Sstevel@tonic-gate 	}
1658*0Sstevel@tonic-gate }
1659