Unleashed v1.4
[unleashed.git] / kernel / fs / pcfs / pc_dir.c
blobf927a4c19895cecc87cbddebf341bf4b2cd4c788
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2015 Joyent, Inc.
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>
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 pcslot *, 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);
62 * Tunables
64 int enable_long_filenames = 1;
67 * Lookup a name in a directory. Return a pointer to the pc_node
68 * which represents the entry.
70 int
71 pc_dirlook(
72 struct pcnode *dp, /* parent directory */
73 char *namep, /* name to lookup */
74 struct pcnode **pcpp) /* result */
76 struct vnode *vp;
77 struct pcslot slot;
78 int error;
80 PC_DPRINTF2(4, "pc_dirlook (dp %p name %s)\n", (void *)dp, namep);
82 if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
83 return (ENOTDIR);
85 vp = PCTOV(dp);
87 * check now for changed disk, before any return(0)
89 if (error = pc_verify(VFSTOPCFS(vp->v_vfsp)))
90 return (error);
93 * Null component name is synonym for directory being searched.
95 if (*namep == '\0') {
96 VN_HOLD(vp);
97 *pcpp = dp;
98 return (0);
101 * The root directory does not have "." and ".." entries,
102 * so they are faked here.
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);
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;
120 return (error);
124 * Enter a name in a directory.
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)
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;
143 PC_DPRINTF4(4, "pc_dirent(dp %p, name %s, vap %p, pcpp %p\n",
144 (void *)dp, namep, (void *)vap, (void *)pcpp);
146 if (pcpp != NULL)
147 *pcpp = NULL;
149 * Leading spaces are not allowed in DOS.
151 if (*namep == ' ')
152 return (EINVAL);
154 * If name is "." or "..", just look it up.
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);
162 return (EEXIST);
164 if (PCA_IS_HIDDEN(fsp, dp->pc_entry.pcd_attr)) {
165 return (EPERM);
168 * Make sure directory has not been removed while fs was unlocked.
170 if (dp->pc_entry.pcd_filename[0] == PCD_ERASED) {
171 return (ENOENT);
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;
181 brelse(slot.sl_bp);
182 } else if (error == ENOENT) {
183 struct pcdir *direntries;
184 int ndirentries;
187 * The entry does not exist. Check write permission in
188 * directory to see if entry can be created.
190 if (dp->pc_entry.pcd_attr & PCA_RDONLY) {
191 return (EPERM);
193 error = 0;
195 * Make sure there is a slot.
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);
204 offset = pc_find_free_space(dp, ndirentries);
205 if (offset == -1) {
206 return (ENOSPC);
210 * Make an entry from the supplied attributes.
212 direntries = pc_name_to_pcdir(dp, namep, ndirentries, &error);
213 if (direntries == NULL) {
214 return (error);
216 error = pc_makedirentry(dp, direntries, ndirentries, vap,
217 offset);
218 kmem_free(direntries, ndirentries * sizeof (struct pcdir));
219 if (error) {
220 return (error);
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);
228 blkno = pc_daddrdb(fsp, bp->b_blkno);
230 * Get a pcnode for the new entry.
232 *pcpp = pc_getnode(fsp, blkno, boff, ep);
233 brelse(bp);
234 if (vap->va_type == VDIR)
235 (*pcpp)->pc_size = fsp->pcfs_clsize;
238 * Write out the new entry in the parent directory.
240 error = pc_syncfat(fsp);
241 if (!error) {
242 error = pc_nodeupdate(*pcpp);
245 return (error);
249 * Template for "." and ".." directory entries.
251 static struct {
252 struct pcdir t_dot; /* dot entry */
253 struct pcdir t_dotdot; /* dotdot entry */
254 } dirtemplate = {
256 ". ",
257 " ",
258 PCA_DIR
261 ".. ",
262 " ",
263 PCA_DIR
268 * Convert an attributes structure into the short filename entry
269 * and write out the whole entry.
271 static int
272 pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
273 int ndirentries, struct vattr *vap, offset_t offset)
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;
284 if (vap != NULL && vap->va_mask & (VATTR_ATIME|VATTR_MTIME))
285 return (EOPNOTSUPP);
287 ep = &direntries[ndirentries - 1];
288 gethrestime(&now);
289 if (error = pc_tvtopct(&now, &ep->pcd_mtime))
290 return (error);
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;
298 * Fields we don't use.
300 ep->pcd_ntattr = 0;
301 if (!IS_FAT32(fsp))
302 ep->un.pcd_eattr = 0;
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;
309 ep->pcd_attr |= PCA_DIR;
311 * Make dot and dotdot entries for a new directory.
313 cn = pc_alloccluster(fsp, 0);
314 switch (cn) {
315 case PCF_FREECLUSTER:
316 return (ENOSPC);
317 case PCF_ERRORCLUSTER:
318 return (EIO);
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);
347 } else {
348 pc_setstartcluster(fsp, ep, 0);
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;
363 error = pc_blkatoff(dp, offset, &bp, &ep);
364 if (error)
365 return (error);
368 *ep = direntries[i];
369 offset += sizeof (struct pcdir);
371 if (bp != NULL) {
372 /* always modified */
373 bwrite2(bp);
374 error = geterror(bp);
375 brelse(bp);
376 if (error)
377 return (error);
379 return (0);
383 * Remove a name from a directory.
386 pc_dirremove(
387 struct pcnode *dp,
388 char *namep,
389 struct vnode *cdir,
390 enum vtype type,
391 caller_context_t *ctp)
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;
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);
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);
417 if (error) {
418 brelse(slot.sl_bp);
419 return (error);
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;
430 } else {
431 if (pcp->pc_entry.pcd_attr & PCA_DIR)
432 error = EISDIR;
434 if (error == 0) {
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.
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);
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);
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);
465 } else {
466 brelse(slot.sl_bp);
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);
477 VN_RELE(PCTOV(pcp));
479 return (error);
483 * Determine whether a directory is empty.
485 static int
486 pc_dirempty(struct pcnode *pcp)
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;
496 vp = PCTOV(pcp);
497 bp = NULL;
499 offset = 0;
500 for (;;) {
503 * If offset is on a block boundary,
504 * read in the next directory block.
505 * Release previous if it exists.
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);
515 if (PCDL_IS_LFN(ep)) {
516 error = pc_extract_long_fn(pcp, NULL, &ep, &offset,
517 &bp);
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.
524 if (error == EINVAL)
525 continue;
526 else {
527 if (bp)
528 brelse(bp);
529 return (error);
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);
539 if ((c == '.') && !PC_SHORTNAME_IS_DOT(ep->pcd_filename) &&
540 !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
541 brelse(bp);
542 return (0);
544 ep++;
545 offset += sizeof (struct pcdir);
547 if (bp != NULL)
548 brelse(bp);
549 return (1);
553 * Rename a file.
556 pc_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)
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;
573 PC_DPRINTF3(4, "pc_rename(0x%p, %s, %s)\n", (void *)dp, snm, tnm);
575 * Leading spaces are not allowed in DOS.
577 if (*tnm == ' ')
578 return (EINVAL);
580 * No dot or dotdot.
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);
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.
589 top:
590 error = pc_findentry(dp, snm, &slot, NULL);
591 if (error) {
592 return (error);
594 pcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
595 slot.sl_blkno, slot.sl_offset, slot.sl_ep);
597 brelse(slot.sl_bp);
599 if (pcp)
600 svp = PCTOV(pcp);
603 * is the rename invalid, i.e. rename("a", "a/a")
605 if (pcp == tdp) {
606 if (svp)
607 VN_RELE(svp);
608 return (EINVAL);
612 * Are we just changing the case of an existing name?
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;
621 * u8_strcmp detected an illegal character
623 if (error)
624 return (EINVAL);
626 oldisdir = pcp->pc_entry.pcd_attr & PCA_DIR;
629 * see if the target exists
631 error = pc_findentry(tdp, tnm, &slot, NULL);
632 if (error == 0 && filecasechange == 0) {
634 * Target exists. If it's a file, delete it. If it's
635 * a directory, bail.
637 int newisdir;
639 tpcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
640 slot.sl_blkno, slot.sl_offset, slot.sl_ep);
642 newisdir = tpcp->pc_entry.pcd_attr & PCA_DIR;
644 brelse(slot.sl_bp);
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
652 if (!newisdir) {
653 if (oldisdir) {
654 error = ENOTDIR;
655 } else {
656 /* nondir/nondir, remove target */
657 error = pc_dirremove(tdp, tnm, NULL, VREG, ctp);
658 if (error == 0) {
659 vnevent_rename_dest(PCTOV(tpcp),
660 PCTOV(tdp), tnm, ctp);
661 VN_RELE(PCTOV(tpcp));
662 tpcp = NULL;
663 VN_RELE(PCTOV(pcp));
664 goto top;
667 } else if (oldisdir) {
668 /* dir/dir, remove target */
669 error = pc_dirremove(tdp, tnm, NULL, VDIR, ctp);
670 if (error == 0) {
671 vnevent_rename_dest(PCTOV(tpcp), PCTOV(tdp),
672 tnm, ctp);
673 VN_RELE(PCTOV(tpcp));
674 tpcp = NULL;
675 VN_RELE(PCTOV(pcp));
676 goto top;
678 /* Follow rename(2)'s spec... */
679 if (error == ENOTEMPTY) {
680 error = EEXIST;
682 } else {
683 /* nondir/dir, bail */
684 error = EISDIR;
688 if ((error == 0) || (error == ENOENT)) {
689 offset_t lfn_offset = -1;
690 daddr_t blkno;
691 struct pcdir *direntries;
692 struct pcdir *ep;
693 int ndirentries;
694 pc_cluster16_t pct_lo;
695 pc_cluster16_t pct_hi;
696 offset_t offset;
697 int boff;
698 struct buf *bp = NULL;
699 uchar_t attr;
700 int size;
701 struct pctime mtime;
702 struct pctime crtime;
703 uchar_t ntattr;
704 ushort_t ladate;
705 ushort_t eattr;
706 uchar_t crtime_msec;
709 * Rename the source.
712 * Delete the old name, and create a new name.
714 if (filecasechange == 1 && error == 0)
715 brelse(slot.sl_bp);
716 ndirentries = direntries_needed(tdp, tnm);
717 if (ndirentries == -1) {
718 error = EINVAL;
719 goto done;
722 * first see if we have enough space to create the new
723 * name before destroying the old one.
725 offset = pc_find_free_space(tdp, ndirentries);
726 if (offset == -1) {
727 error = ENOSPC;
728 goto done;
731 error = pc_findentry(dp, snm, &slot, &lfn_offset);
732 if (error) {
733 goto done;
735 pct_lo = slot.sl_ep->pcd_scluster_lo;
736 if (IS_FAT32(fsp))
737 pct_hi = slot.sl_ep->un.pcd_scluster_hi;
738 else
739 eattr = slot.sl_ep->un.pcd_eattr;
740 size = slot.sl_ep->pcd_size;
741 attr = slot.sl_ep->pcd_attr;
742 mtime = slot.sl_ep->pcd_mtime;
743 crtime = slot.sl_ep->pcd_crtime;
744 crtime_msec = slot.sl_ep->pcd_crtime_msec;
745 ntattr = slot.sl_ep->pcd_ntattr;
746 ladate = slot.sl_ep->pcd_ladate;
748 if (lfn_offset != -1) {
749 brelse(slot.sl_bp);
750 error = pc_remove_long_fn(dp, lfn_offset);
751 if (error) {
752 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
753 goto done;
755 } else {
756 slot.sl_ep->pcd_filename[0] =
757 pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
758 bwrite2(slot.sl_bp);
759 error = geterror(slot.sl_bp);
760 brelse(slot.sl_bp);
762 if (error) {
763 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
764 error = EIO;
765 goto done;
769 * Make an entry from the supplied attributes.
771 direntries = pc_name_to_pcdir(tdp, tnm, ndirentries, &error);
772 if (direntries == NULL) {
773 goto done;
776 error = pc_makedirentry(tdp, direntries, ndirentries, NULL,
777 offset);
778 kmem_free(direntries, ndirentries * sizeof (struct pcdir));
779 if (error) {
780 goto done;
782 /* advance to short name */
783 offset += (ndirentries - 1) * sizeof (struct pcdir);
784 boff = pc_blkoff(fsp, offset);
785 error = pc_blkatoff(tdp, offset, &bp, &ep);
786 if (error) {
787 goto done;
789 blkno = pc_daddrdb(fsp, bp->b_blkno);
790 ep->pcd_scluster_lo = pct_lo;
791 if (IS_FAT32(fsp))
792 ep->un.pcd_scluster_hi = pct_hi;
793 else
794 ep->un.pcd_eattr = eattr;
795 ep->pcd_size = size;
796 ep->pcd_attr = attr;
797 ep->pcd_mtime = mtime;
798 ep->pcd_crtime = crtime;
799 ep->pcd_crtime_msec = crtime_msec;
800 ep->pcd_ntattr = ntattr;
801 ep->pcd_ladate = ladate;
802 bwrite2(bp);
803 error = geterror(bp);
804 pcp->pc_eblkno = blkno;
805 pcp->pc_eoffset = boff;
806 pcp->pc_entry = *ep;
807 pcp->pc_flags |= PC_CHG;
808 brelse(bp);
809 if (error) {
810 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
811 error = EIO;
812 goto done;
814 /* No need to fix ".." if we're renaming within a dir */
815 if (oldisdir && dp != tdp) {
816 if ((error = pc_dirfixdotdot(pcp, dp, tdp)) != 0) {
817 goto done;
820 if ((error = pc_nodeupdate(pcp)) != 0) {
821 goto done;
825 if (error == 0) {
826 vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm, ctp);
827 if (dp != tdp)
828 vnevent_rename_dest_dir(PCTOV(tdp), ctp);
831 done:
832 if (tpcp != NULL)
833 VN_RELE(PCTOV(tpcp));
834 VN_RELE(PCTOV(pcp));
836 return (error);
840 * Fix the ".." entry of the child directory so that it points to the
841 * new parent directory instead of the old one.
843 static int
844 pc_dirfixdotdot(struct pcnode *dp, /* child directory being moved */
845 struct pcnode *opdp, /* old parent directory */
846 struct pcnode *npdp) /* new parent directory */
848 pc_cluster32_t cn;
849 struct vnode *vp = PCTOV(dp);
850 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
851 int error = 0;
852 struct buf *bp = NULL;
853 struct pcdir *ep = NULL;
854 struct pcdir *tep = NULL;
857 * set the new child's ".." directory entry starting cluster to
858 * point to the new parent's starting cluster
860 ASSERT(opdp != npdp);
861 error = pc_blkatoff(dp, 0, &bp, &ep);
862 if (error) {
863 PC_DPRINTF0(1, "pc_dirfixdotdot: error in blkatoff\n");
864 return (error);
866 tep = ep;
867 ep++;
868 if (!PC_SHORTNAME_IS_DOT(tep->pcd_filename) &&
869 !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
870 PC_DPRINTF0(1, "pc_dirfixdotdot: mangled directory entry\n");
871 error = ENOTDIR;
872 return (error);
874 cn = pc_getstartcluster(fsp, &npdp->pc_entry);
875 pc_setstartcluster(fsp, ep, cn);
877 bwrite2(bp);
878 error = geterror(bp);
879 brelse(bp);
880 if (error) {
881 PC_DPRINTF0(1, "pc_dirfixdotdot: error in write\n");
882 pc_mark_irrecov(fsp);
883 return (EIO);
885 return (0);
890 * Search a directory for an entry.
891 * The directory should be locked as this routine
892 * will sleep on I/O while searching.
894 static int
895 pc_findentry(
896 struct pcnode *dp, /* parent directory */
897 char *namep, /* name to lookup */
898 struct pcslot *slotp,
899 offset_t *lfn_offset)
901 offset_t offset;
902 struct pcdir *ep = NULL;
903 int boff;
904 int error;
905 struct vnode *vp;
906 struct pcfs *fsp;
908 vp = PCTOV(dp);
909 PC_DPRINTF2(6, "pc_findentry: looking for %s in dir 0x%p\n", namep,
910 (void *)dp);
911 slotp->sl_status = SL_NONE;
912 if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
913 return (ENOTDIR);
916 * Verify that the dp is still valid on the disk
918 fsp = VFSTOPCFS(vp->v_vfsp);
919 error = pc_verify(fsp);
920 if (error)
921 return (error);
923 slotp->sl_bp = NULL;
924 offset = 0;
925 for (;;) {
927 * If offset is on a block boundary,
928 * read in the next directory block.
929 * Release previous if it exists.
931 boff = pc_blkoff(fsp, offset);
932 if (boff == 0 || slotp->sl_bp == NULL ||
933 boff >= slotp->sl_bp->b_bcount) {
934 if (slotp->sl_bp != NULL) {
935 brelse(slotp->sl_bp);
936 slotp->sl_bp = NULL;
938 error = pc_blkatoff(dp, offset, &slotp->sl_bp, &ep);
939 if (error == ENOENT && slotp->sl_status == SL_NONE) {
940 slotp->sl_status = SL_EXTEND;
941 slotp->sl_offset = (int)offset;
943 if (error)
944 return (error);
946 if ((ep->pcd_filename[0] == PCD_UNUSED) ||
947 (ep->pcd_filename[0] == PCD_ERASED)) {
949 * note empty slots, in case name is not found
951 if (slotp->sl_status == SL_NONE) {
952 slotp->sl_status = SL_FOUND;
953 slotp->sl_blkno = pc_daddrdb(fsp,
954 slotp->sl_bp->b_blkno);
955 slotp->sl_offset = boff;
958 * If unused we've hit the end of the directory
960 if (ep->pcd_filename[0] == PCD_UNUSED)
961 break;
962 offset += sizeof (struct pcdir);
963 ep++;
964 continue;
966 if (PCDL_IS_LFN(ep)) {
967 offset_t t = offset;
968 if (pc_match_long_fn(dp, namep, &ep,
969 slotp, &offset) == 0) {
970 if (lfn_offset != NULL)
971 *lfn_offset = t;
972 return (0);
974 continue;
976 if (pc_match_short_fn(dp, namep, &ep, slotp, &offset) == 0)
977 return (0);
979 if (slotp->sl_bp != NULL) {
980 brelse(slotp->sl_bp);
981 slotp->sl_bp = NULL;
983 return (ENOENT);
987 * Obtain the block at offset "offset" in file pcp.
990 pc_blkatoff(
991 struct pcnode *pcp,
992 offset_t offset,
993 struct buf **bpp,
994 struct pcdir **epp)
996 struct pcfs *fsp;
997 struct buf *bp;
998 int size;
999 int error;
1000 daddr_t bn;
1002 fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
1003 size = pc_blksize(fsp, pcp, offset);
1004 if (pc_blkoff(fsp, offset) >= size) {
1005 PC_DPRINTF0(5, "pc_blkatoff: ENOENT\n");
1006 return (ENOENT);
1008 error = pc_bmap(pcp, pc_lblkno(fsp, offset), &bn, (uint_t *)0);
1009 if (error)
1010 return (error);
1012 bp = bread(fsp->pcfs_xdev, bn, size);
1013 if (bp->b_flags & B_ERROR) {
1014 PC_DPRINTF0(1, "pc_blkatoff: error\n");
1015 brelse(bp);
1016 pc_mark_irrecov(fsp);
1017 return (EIO);
1019 if (epp) {
1020 *epp =
1021 (struct pcdir *)(bp->b_un.b_addr + pc_blkoff(fsp, offset));
1023 *bpp = bp;
1024 return (0);
1028 * Parse user filename into the pc form of "filename.extension".
1029 * If names are too long for the format (and enable_long_filenames is set)
1030 * it returns EINVAL (since either this name was read from the disk (so
1031 * it must fit), _or_ we're trying to match a long file name (so we
1032 * should fail). Tests for characters that are invalid in PCDOS and
1033 * converts to upper case (unless foldcase is 0).
1035 static int
1036 pc_parsename(
1037 char *namep,
1038 char *fnamep,
1039 char *fextp)
1041 int n;
1042 char c;
1044 n = PCFNAMESIZE;
1045 c = *namep++;
1046 if (c == 0)
1047 return (EINVAL);
1048 if (c == '.') {
1050 * check for "." and "..".
1052 *fnamep++ = c;
1053 n--;
1054 if (c = *namep++) {
1055 if ((c != '.') || (c = *namep)) /* ".x" or "..x" */
1056 return (EINVAL);
1057 *fnamep++ = '.';
1058 n--;
1060 } else {
1062 * filename up to '.'
1064 do {
1065 if (n-- > 0) {
1066 c = toupper(c);
1067 if (!pc_validchar(c))
1068 return (EINVAL);
1069 *fnamep++ = c;
1070 } else {
1071 /* not short */
1072 if (enable_long_filenames)
1073 return (EINVAL);
1075 } while ((c = *namep++) != '\0' && c != '.');
1077 while (n-- > 0) { /* fill with blanks */
1078 *fnamep++ = ' ';
1081 * remainder is extension
1083 n = PCFEXTSIZE;
1084 if (c == '.') {
1085 while ((c = *namep++) != '\0' && n--) {
1086 c = toupper(c);
1087 if (!pc_validchar(c))
1088 return (EINVAL);
1089 *fextp++ = c;
1091 if (enable_long_filenames && (c != '\0')) {
1092 /* not short */
1093 return (EINVAL);
1096 while (n-- > 0) { /* fill with blanks */
1097 *fextp++ = ' ';
1099 return (0);
1103 * Match a long filename entry with 'namep'. Also return failure
1104 * if the long filename isn't valid.
1107 pc_match_long_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1108 struct pcslot *slotp, offset_t *offset)
1110 struct pcdir *ep = (struct pcdir *)*epp;
1111 struct vnode *vp = PCTOV(pcp);
1112 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1113 int error = 0;
1114 char lfn[PCMAXNAMLEN+1];
1116 error = pc_extract_long_fn(pcp, lfn, epp, offset, &slotp->sl_bp);
1117 if (error) {
1118 if (error == EINVAL) {
1119 return (ENOENT);
1120 } else
1121 return (error);
1123 ep = *epp;
1124 if ((u8_strcmp(lfn, namep, 0, U8_STRCMP_CI_UPPER,
1125 U8_UNICODE_LATEST, &error) == 0) && (error == 0)) {
1126 /* match */
1127 slotp->sl_flags = 0;
1128 slotp->sl_blkno = pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1129 slotp->sl_offset = pc_blkoff(fsp, *offset);
1130 slotp->sl_ep = ep;
1131 return (0);
1133 *offset += sizeof (struct pcdir);
1134 ep++;
1135 *epp = ep;
1136 /* If u8_strcmp detected an error it's sufficient to rtn ENOENT */
1137 return (ENOENT);
1141 * Match a short filename entry with namep.
1144 pc_match_short_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1145 struct pcslot *slotp, offset_t *offset)
1147 char fname[PCFNAMESIZE];
1148 char fext[PCFEXTSIZE];
1149 struct pcdir *ep = *epp;
1150 int error;
1151 struct vnode *vp = PCTOV(pcp);
1152 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1153 int boff = pc_blkoff(fsp, *offset);
1155 if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
1156 *offset += sizeof (struct pcdir);
1157 ep++;
1158 *epp = ep;
1159 return (ENOENT);
1162 error = pc_parsename(namep, fname, fext);
1163 if (error) {
1164 *offset += sizeof (struct pcdir);
1165 ep++;
1166 *epp = ep;
1167 return (error);
1170 if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1171 (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1173 * found the file
1175 if (fname[0] == '.') {
1176 if (fname[1] == '.')
1177 slotp->sl_flags = SL_DOTDOT;
1178 else
1179 slotp->sl_flags = SL_DOT;
1180 } else {
1181 slotp->sl_flags = 0;
1183 slotp->sl_blkno =
1184 pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1185 slotp->sl_offset = boff;
1186 slotp->sl_ep = ep;
1187 return (0);
1189 *offset += sizeof (struct pcdir);
1190 ep++;
1191 *epp = ep;
1192 return (ENOENT);
1196 * Remove a long filename entry starting at lfn_offset. It must be
1197 * a valid entry or we wouldn't have gotten here. Also remove the
1198 * short filename entry.
1200 static int
1201 pc_remove_long_fn(struct pcnode *pcp, offset_t lfn_offset)
1203 struct vnode *vp = PCTOV(pcp);
1204 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1205 int boff;
1206 struct buf *bp = NULL;
1207 struct pcdir *ep = NULL;
1208 int error = 0;
1211 * if we're in here, we know that the lfn is in the proper format
1212 * of <series-of-lfn-entries> followed by <sfn-entry>
1214 for (;;) {
1215 boff = pc_blkoff(fsp, lfn_offset);
1216 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1217 if (bp != NULL) {
1218 bwrite2(bp);
1219 error = geterror(bp);
1220 brelse(bp);
1221 if (error)
1222 return (error);
1223 bp = NULL;
1225 error = pc_blkatoff(pcp, lfn_offset, &bp, &ep);
1226 if (error)
1227 return (error);
1229 if (!PCDL_IS_LFN(ep)) {
1230 /* done */
1231 break;
1233 /* zap it */
1234 ep->pcd_filename[0] = PCD_ERASED;
1235 ep->pcd_attr = 0;
1236 lfn_offset += sizeof (struct pcdir);
1237 ep++;
1239 /* now we're on the short entry */
1241 ep->pcd_filename[0] = PCD_ERASED;
1242 ep->pcd_attr = 0;
1244 if (bp != NULL) {
1245 bwrite2(bp);
1246 error = geterror(bp);
1247 brelse(bp);
1248 if (error)
1249 return (error);
1251 return (0);
1255 * Find (and allocate) space in the directory denoted by
1256 * 'pcp'. for 'ndirentries' pcdir structures.
1257 * Return the offset at which to start, or -1 for failure.
1259 static offset_t
1260 pc_find_free_space(struct pcnode *pcp, int ndirentries)
1262 offset_t offset = 0;
1263 offset_t spaceneeded = ndirentries * sizeof (struct pcdir);
1264 offset_t spaceoffset;
1265 offset_t spaceavail = 0;
1266 int boff;
1267 struct buf *bp = NULL;
1268 struct vnode *vp = PCTOV(pcp);
1269 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1270 struct pcdir *ep;
1271 int error;
1273 spaceoffset = offset;
1274 while (spaceneeded > spaceavail) {
1276 * If offset is on a block boundary,
1277 * read in the next directory block.
1278 * Release previous if it exists.
1280 boff = pc_blkoff(fsp, offset);
1281 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1282 if (bp != NULL) {
1283 brelse(bp);
1284 bp = NULL;
1286 error = pc_blkatoff(pcp, offset, &bp, &ep);
1287 if (error == ENOENT) {
1288 daddr_t bn;
1290 /* extend directory */
1291 if (!IS_FAT32(fsp) && (vp->v_flag & VROOT))
1292 return (-1);
1293 while (spaceneeded > spaceavail) {
1294 error = pc_balloc(pcp,
1295 pc_lblkno(fsp, offset), 1, &bn);
1296 if (error)
1297 return (-1);
1298 pcp->pc_size += fsp->pcfs_clsize;
1299 spaceavail += fsp->pcfs_clsize;
1300 offset += fsp->pcfs_clsize;
1302 return (spaceoffset);
1304 if (error)
1305 return (-1);
1307 if ((ep->pcd_filename[0] == PCD_UNUSED) ||
1308 (ep->pcd_filename[0] == PCD_ERASED)) {
1309 offset += sizeof (struct pcdir);
1310 spaceavail += sizeof (struct pcdir);
1311 ep++;
1312 continue;
1314 offset += sizeof (struct pcdir);
1315 spaceavail = 0;
1316 spaceoffset = offset;
1317 ep++;
1319 if (bp != NULL) {
1320 brelse(bp);
1322 return (spaceoffset);
1326 * Return how many long filename entries are needed.
1327 * A maximum of PCLFNCHUNKSIZE characters per entry, plus one for a
1328 * short filename.
1330 static int
1331 direntries_needed(struct pcnode *dp, char *namep)
1333 struct pcdir ep;
1334 uint16_t *w2_str;
1335 size_t u8l, u16l;
1336 int ret;
1338 if (enable_long_filenames == 0) {
1339 return (1);
1341 if (pc_is_short_file_name(namep, 0)) {
1342 (void) pc_parsename(namep, ep.pcd_filename, ep.pcd_ext);
1343 if (!shortname_exists(dp, ep.pcd_filename, ep.pcd_ext)) {
1344 return (1);
1347 if (pc_valid_long_fn(namep, 1)) {
1349 * convert to UTF-16 or UNICODE for calculating the entries
1350 * needed. Conversion will consume at the most 512 bytes
1352 u16l = PCMAXNAMLEN + 1;
1353 w2_str = kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1354 u8l = strlen(namep);
1355 ret = uconv_u8tou16((const uchar_t *)namep, &u8l,
1356 w2_str, &u16l, UCONV_OUT_LITTLE_ENDIAN);
1357 kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1358 if (ret == 0) {
1359 ret = 1 + u16l / PCLFNCHUNKSIZE;
1360 if (u16l % PCLFNCHUNKSIZE != 0)
1361 ret++;
1362 return (ret);
1365 return (-1);
1369 * Allocate and return an array of pcdir structures for the passed-in
1370 * name. ndirentries tells how many are required (including the short
1371 * filename entry). Just allocate and fill them in properly here so they
1372 * can be written out.
1374 static struct pcdir *
1375 pc_name_to_pcdir(struct pcnode *dp, char *namep, int ndirentries, int *errret)
1377 struct pcdir *bpcdir;
1378 struct pcdir *ep;
1379 struct pcdir_lfn *lep;
1380 int i;
1381 uchar_t cksum;
1382 int nchars;
1383 int error = 0;
1384 char *nameend;
1385 uint16_t *w2_str;
1386 size_t u8l, u16l;
1387 int ret;
1389 bpcdir = kmem_zalloc(ndirentries * sizeof (struct pcdir), KM_SLEEP);
1390 ep = &bpcdir[ndirentries - 1];
1391 if (ndirentries == 1) {
1392 (void) pc_parsename(namep, ep->pcd_filename, ep->pcd_ext);
1393 return (bpcdir);
1396 /* Here we need to convert to UTF-16 or UNICODE for writing */
1398 u16l = PCMAXNAMLEN + 1;
1399 w2_str = kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1400 u8l = strlen(namep);
1401 ret = uconv_u8tou16((const uchar_t *)namep, &u8l, w2_str, &u16l,
1402 UCONV_OUT_LITTLE_ENDIAN);
1403 if (ret != 0) {
1404 kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1405 *errret = ret;
1406 return (NULL);
1408 nameend = (char *)(w2_str + u16l);
1409 u16l %= PCLFNCHUNKSIZE;
1410 if (u16l != 0) {
1411 nchars = u16l + 1;
1412 nameend += 2;
1413 } else {
1414 nchars = PCLFNCHUNKSIZE;
1416 nchars *= sizeof (uint16_t);
1418 /* short file name */
1419 error = generate_short_name(dp, namep, ep);
1420 if (error) {
1421 kmem_free(bpcdir, ndirentries * sizeof (struct pcdir));
1422 *errret = error;
1423 return (NULL);
1425 cksum = pc_checksum_long_fn(ep->pcd_filename, ep->pcd_ext);
1426 for (i = 0; i < (ndirentries - 1); i++) {
1427 /* long file name */
1428 nameend -= nchars;
1429 lep = (struct pcdir_lfn *)&bpcdir[i];
1430 set_long_fn_chunk(lep, nameend, nchars);
1431 lep->pcdl_attr = PCDL_LFN_BITS;
1432 lep->pcdl_checksum = cksum;
1433 lep->pcdl_ordinal = (uchar_t)(ndirentries - i - 1);
1434 nchars = PCLFNCHUNKSIZE * sizeof (uint16_t);
1436 kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1437 lep = (struct pcdir_lfn *)&bpcdir[0];
1438 lep->pcdl_ordinal |= 0x40;
1439 return (bpcdir);
1442 static int
1443 generate_short_name(struct pcnode *dp, char *namep, struct pcdir *inep)
1445 int rev;
1446 int nchars;
1447 int i, j;
1448 char *dot = NULL;
1449 char fname[PCFNAMESIZE+1];
1450 char fext[PCFEXTSIZE+1];
1451 char scratch[8];
1452 int error = 0;
1453 struct pcslot slot;
1454 char shortname[20];
1455 int force_tilde = 0;
1458 * generate a unique short file name based on the long input name.
1460 * Say, for "This is a very long filename.txt" generate
1461 * "THISIS~1.TXT", or "THISIS~2.TXT" if that's already there.
1462 * Skip invalid short name characters in the long name, plus
1463 * a couple NT skips (space and reverse backslash).
1465 * Unfortunately, since this name would be hidden by the normal
1466 * lookup routine, we need to look for it ourselves. But luckily
1467 * we don't need to look at the lfn entries themselves.
1469 force_tilde = !pc_is_short_file_name(namep, 1);
1472 * Strip off leading invalid characters.
1473 * We need this because names like '.login' are now ok, but the
1474 * short name needs to be something like LOGIN~1.
1476 for (; *namep != '\0'; namep++) {
1477 if (*namep == ' ')
1478 continue;
1479 if (!pc_validchar(*namep) && !pc_validchar(toupper(*namep)))
1480 continue;
1481 break;
1483 dot = strrchr(namep, '.');
1484 if (dot != NULL) {
1485 dot++;
1486 for (j = 0, i = 0; j < PCFEXTSIZE; i++) {
1487 if (dot[i] == '\0')
1488 break;
1489 /* skip valid, but not generally good characters */
1490 if (dot[i] == ' ' || dot[i] == '\\')
1491 continue;
1492 if (pc_validchar(dot[i]))
1493 fext[j++] = dot[i];
1494 else if (pc_validchar(toupper(dot[i])))
1495 fext[j++] = toupper(dot[i]);
1497 for (i = j; i < PCFEXTSIZE; i++)
1498 fext[i] = ' ';
1499 dot--;
1500 } else {
1501 for (i = 0; i < PCFEXTSIZE; i++) {
1502 fext[i] = ' ';
1506 * We know we're a long name, not a short name (or we wouldn't
1507 * be here at all. But if uppercasing ourselves would be a short
1508 * name, then we can possibly avoid the ~N format.
1510 if (!force_tilde)
1511 rev = 0;
1512 else
1513 rev = 1;
1514 for (;;) {
1515 bzero(fname, sizeof (fname));
1516 nchars = PCFNAMESIZE;
1517 if (rev) {
1518 nchars--; /* ~ */
1519 i = rev;
1520 do {
1521 nchars--;
1522 i /= 10;
1523 } while (i);
1524 if (nchars <= 0) {
1525 return (ENOSPC);
1528 for (j = 0, i = 0; j < nchars; i++) {
1529 if ((&namep[i] == dot) || (namep[i] == '\0'))
1530 break;
1531 /* skip valid, but not generally good characters */
1532 if (namep[i] == ' ' || namep[i] == '\\')
1533 continue;
1534 if (pc_validchar(namep[i]))
1535 fname[j++] = namep[i];
1536 else if (pc_validchar(toupper(namep[i])))
1537 fname[j++] = toupper(namep[i]);
1539 if (rev) {
1540 (void) sprintf(scratch, "~%d", rev);
1541 (void) strcat(fname, scratch);
1543 for (i = strlen(fname); i < PCFNAMESIZE; i++)
1544 fname[i] = ' ';
1545 /* now see if it exists */
1546 (void) pc_fname_ext_to_name(shortname, fname, fext, 0);
1547 error = pc_findentry(dp, shortname, &slot, NULL);
1548 if (error == 0) {
1549 /* found it */
1550 brelse(slot.sl_bp);
1551 rev++;
1552 continue;
1554 if (!shortname_exists(dp, fname, fext))
1555 break;
1556 rev++;
1558 (void) strncpy(inep->pcd_filename, fname, PCFNAMESIZE);
1559 (void) strncpy(inep->pcd_ext, fext, PCFEXTSIZE);
1560 return (0);
1564 * Returns 1 if the passed-in filename is a short name, 0 if not.
1566 static int
1567 pc_is_short_file_name(char *namep, int foldcase)
1569 int i;
1570 char c;
1572 for (i = 0; i < PCFNAMESIZE; i++, namep++) {
1573 if (*namep == '\0')
1574 return (1);
1575 if (*namep == '.')
1576 break;
1577 if (foldcase)
1578 c = toupper(*namep);
1579 else
1580 c = *namep;
1581 if (!pc_validchar(c))
1582 return (0);
1584 if (*namep == '\0')
1585 return (1);
1586 if (*namep != '.')
1587 return (0);
1588 namep++;
1589 for (i = 0; i < PCFEXTSIZE; i++, namep++) {
1590 if (*namep == '\0')
1591 return (1);
1592 if (foldcase)
1593 c = toupper(*namep);
1594 else
1595 c = *namep;
1596 if (!pc_validchar(c))
1597 return (0);
1599 /* we should be done. If not... */
1600 if (*namep == '\0')
1601 return (1);
1602 return (0);
1607 * We call this when we want to see if a short filename already exists
1608 * in the filesystem as part of a long filename. When creating a short
1609 * name (FILENAME.TXT from the user, or when generating one for a long
1610 * filename), we cannot allow one that is part of a long filename.
1611 * pc_findentry will find all the names that are visible (long or short),
1612 * but will not crack any long filename entries.
1614 static int
1615 shortname_exists(struct pcnode *dp, char *fname, char *fext)
1617 struct buf *bp = NULL;
1618 int offset = 0;
1619 int match = 0;
1620 struct pcdir *ep;
1621 struct vnode *vp = PCTOV(dp);
1622 struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1623 int boff;
1624 int error = 0;
1626 for (;;) {
1627 boff = pc_blkoff(fsp, offset);
1628 if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1629 if (bp != NULL) {
1630 brelse(bp);
1631 bp = NULL;
1633 error = pc_blkatoff(dp, offset, &bp, &ep);
1634 if (error == ENOENT)
1635 break;
1636 if (error) {
1637 return (1);
1640 if (PCDL_IS_LFN(ep) ||
1641 (ep->pcd_filename[0] == PCD_ERASED)) {
1642 offset += sizeof (struct pcdir);
1643 ep++;
1644 continue;
1646 if (ep->pcd_filename[0] == PCD_UNUSED)
1647 break;
1649 * in use, and a short file name (either standalone
1650 * or associated with a long name
1652 if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1653 (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1654 match = 1;
1655 break;
1657 offset += sizeof (struct pcdir);
1658 ep++;
1660 if (bp) {
1661 brelse(bp);
1662 bp = NULL;
1664 return (match);
1667 pc_cluster32_t
1668 pc_getstartcluster(struct pcfs *fsp, struct pcdir *ep)
1670 if (IS_FAT32(fsp)) {
1671 pc_cluster32_t cn;
1672 pc_cluster16_t hi16;
1673 pc_cluster16_t lo16;
1675 hi16 = ltohs(ep->un.pcd_scluster_hi);
1676 lo16 = ltohs(ep->pcd_scluster_lo);
1677 cn = (hi16 << 16) | lo16;
1678 return (cn);
1679 } else {
1680 return (ltohs(ep->pcd_scluster_lo));
1684 void
1685 pc_setstartcluster(struct pcfs *fsp, struct pcdir *ep, pc_cluster32_t cln)
1687 if (IS_FAT32(fsp)) {
1688 pc_cluster16_t hi16;
1689 pc_cluster16_t lo16;
1691 hi16 = (cln >> 16) & 0xFFFF;
1692 lo16 = cln & 0xFFFF;
1693 ep->un.pcd_scluster_hi = htols(hi16);
1694 ep->pcd_scluster_lo = htols(lo16);
1695 } else {
1696 pc_cluster16_t cln16;
1698 cln16 = (pc_cluster16_t)cln;
1699 ep->pcd_scluster_lo = htols(cln16);