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