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