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]
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>
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
,
51 static int generate_short_name(struct pcnode
*dp
, char *namep
,
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
,
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.
72 struct pcnode
*dp
, /* parent directory */
73 char *namep
, /* name to lookup */
74 struct pcnode
**pcpp
) /* result */
80 PC_DPRINTF2(4, "pc_dirlook (dp %p name %s)\n", (void *)dp
, namep
);
82 if (!(dp
->pc_entry
.pcd_attr
& PCA_DIR
)) {
87 * check now for changed disk, before any return(0)
89 if (error
= pc_verify(VFSTOPCFS(vp
->v_vfsp
)))
93 * Null component name is synonym for directory being searched.
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) {
111 error
= pc_findentry(dp
, namep
, &slot
, NULL
);
113 *pcpp
= pc_getnode(VFSTOPCFS(vp
->v_vfsp
),
114 slot
.sl_blkno
, slot
.sl_offset
, slot
.sl_ep
);
116 PC_DPRINTF1(4, "pc_dirlook: FOUND pcp=%p\n", (void *)*pcpp
);
117 } else if (error
== EINVAL
) {
124 * Enter a name in a directory.
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
)
135 struct vnode
*vp
= PCTOV(dp
);
136 struct pcfs
*fsp
= VFSTOPCFS(vp
->v_vfsp
);
140 struct buf
*bp
= NULL
;
143 PC_DPRINTF4(4, "pc_dirent(dp %p, name %s, vap %p, pcpp %p\n",
144 (void *)dp
, namep
, (void *)vap
, (void *)pcpp
);
149 * Leading spaces are not allowed in DOS.
154 * If name is "." or "..", just look it up.
156 if (PC_NAME_IS_DOT(namep
) || PC_NAME_IS_DOTDOT(namep
)) {
158 error
= pc_dirlook(dp
, namep
, pcpp
);
164 if (PCA_IS_HIDDEN(fsp
, dp
->pc_entry
.pcd_attr
)) {
168 * Make sure directory has not been removed while fs was unlocked.
170 if (dp
->pc_entry
.pcd_filename
[0] == PCD_ERASED
) {
173 error
= pc_findentry(dp
, namep
, &slot
, NULL
);
177 pc_getnode(fsp
, slot
.sl_blkno
, slot
.sl_offset
,
182 } else if (error
== ENOENT
) {
183 struct pcdir
*direntries
;
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
) {
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) {
204 offset
= pc_find_free_space(dp
, ndirentries
);
210 * Make an entry from the supplied attributes.
212 direntries
= pc_name_to_pcdir(dp
, namep
, ndirentries
, &error
);
213 if (direntries
== NULL
) {
216 error
= pc_makedirentry(dp
, direntries
, ndirentries
, vap
,
218 kmem_free(direntries
, ndirentries
* sizeof (struct pcdir
));
222 offset
+= (ndirentries
- 1) * sizeof (struct pcdir
);
223 boff
= pc_blkoff(fsp
, offset
);
224 error
= pc_blkatoff(dp
, offset
, &bp
, &ep
);
228 blkno
= pc_daddrdb(fsp
, bp
->b_blkno
);
230 * Get a pcnode for the new entry.
232 *pcpp
= pc_getnode(fsp
, blkno
, boff
, ep
);
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
);
242 error
= pc_nodeupdate(*pcpp
);
249 * Template for "." and ".." directory entries.
252 struct pcdir t_dot
; /* dot entry */
253 struct pcdir t_dotdot
; /* dotdot entry */
268 * Convert an attributes structure into the short filename entry
269 * and write out the whole entry.
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
);
281 struct buf
*bp
= NULL
;
284 if (vap
!= NULL
&& vap
->va_mask
& (VATTR_ATIME
|VATTR_MTIME
))
287 ep
= &direntries
[ndirentries
- 1];
289 if (error
= pc_tvtopct(&now
, &ep
->pcd_mtime
))
292 ep
->pcd_crtime
= ep
->pcd_mtime
;
293 ep
->pcd_ladate
= ep
->pcd_mtime
.pct_date
;
294 ep
->pcd_crtime_msec
= 0;
298 * Fields we don't use.
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
)) {
309 ep
->pcd_attr
|= PCA_DIR
;
311 * Make dot and dotdot entries for a new directory.
313 cn
= pc_alloccluster(fsp
, 0);
315 case PCF_FREECLUSTER
:
317 case PCF_ERRORCLUSTER
:
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
);
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;
338 bp
->b_un
.b_addr
, sizeof (dirtemplate
));
340 error
= geterror(bp
);
343 PC_DPRINTF0(1, "pc_makedirentry error");
344 pc_mark_irrecov(fsp
);
348 pc_setstartcluster(fsp
, ep
, 0);
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
) {
355 /* always modified */
357 error
= geterror(bp
);
363 error
= pc_blkatoff(dp
, offset
, &bp
, &ep
);
369 offset
+= sizeof (struct pcdir
);
372 /* always modified */
374 error
= geterror(bp
);
383 * Remove a name from a directory.
391 caller_context_t
*ctp
)
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
)) {
405 error
= pc_findentry(dp
, namep
, &slot
, &lfn_offset
);
408 if (slot
.sl_flags
== SL_DOT
) {
410 } else if (slot
.sl_flags
== SL_DOTDOT
) {
414 pc_getnode(VFSTOPCFS(vp
->v_vfsp
),
415 slot
.sl_blkno
, slot
.sl_offset
, slot
.sl_ep
);
422 if (pcp
->pc_entry
.pcd_attr
& PCA_DIR
) {
423 if (PCTOV(pcp
) == cdir
)
425 else if (!pc_dirempty(pcp
))
431 if (pcp
->pc_entry
.pcd_attr
& PCA_DIR
)
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.
442 pcp
->pc_entry
.pcd_filename
[0] = PCD_ERASED
;
443 if (lfn_offset
!= -1) {
445 error
= pc_remove_long_fn(dp
, lfn_offset
);
448 pc_mark_irrecov(VFSTOPCFS(vp
->v_vfsp
));
452 slot
.sl_ep
->pcd_filename
[0] = PCD_ERASED
;
454 error
= geterror(slot
.sl_bp
);
459 pc_mark_irrecov(VFSTOPCFS(vp
->v_vfsp
));
461 } else if (type
== VDIR
) {
462 error
= pc_truncate(pcp
, 0L);
471 vnevent_rmdir(PCTOV(pcp
), vp
, namep
, ctp
);
473 vnevent_remove(PCTOV(pcp
), vp
, namep
, ctp
);
483 * Determine whether a directory is empty.
486 pc_dirempty(struct pcnode
*pcp
)
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
) {
511 if (error
= pc_blkatoff(pcp
, offset
, &bp
, &ep
)) {
515 if (PCDL_IS_LFN(ep
)) {
516 error
= pc_extract_long_fn(pcp
, NULL
, &ep
, &offset
,
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.
532 c
= ep
->pcd_filename
[0];
535 if ((c
!= '.') && (c
!= PCD_ERASED
)) {
539 if ((c
== '.') && !PC_SHORTNAME_IS_DOT(ep
->pcd_filename
) &&
540 !PC_SHORTNAME_IS_DOTDOT(ep
->pcd_filename
)) {
545 offset
+= sizeof (struct pcdir
);
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 */
567 struct vnode
*vp
= PCTOV(dp
);
568 struct vnode
*svp
= NULL
;
569 struct pcfs
*fsp
= VFSTOPCFS(vp
->v_vfsp
);
570 int filecasechange
= 0;
573 PC_DPRINTF3(4, "pc_rename(0x%p, %s, %s)\n", (void *)dp
, snm
, tnm
);
575 * Leading spaces are not allowed in DOS.
582 if (PC_NAME_IS_DOT(snm
) || PC_NAME_IS_DOTDOT(snm
) ||
583 PC_NAME_IS_DOT(tnm
) || PC_NAME_IS_DOTDOT(tnm
))
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.
590 error
= pc_findentry(dp
, snm
, &slot
, NULL
);
594 pcp
= pc_getnode(VFSTOPCFS(vp
->v_vfsp
),
595 slot
.sl_blkno
, slot
.sl_offset
, slot
.sl_ep
);
603 * is the rename invalid, i.e. rename("a", "a/a")
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
,
621 * u8_strcmp detected an illegal character
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
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
;
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
656 /* nondir/nondir, remove target */
657 error
= pc_dirremove(tdp
, tnm
, NULL
, VREG
, ctp
);
659 vnevent_rename_dest(PCTOV(tpcp
),
660 PCTOV(tdp
), tnm
, ctp
);
661 VN_RELE(PCTOV(tpcp
));
667 } else if (oldisdir
) {
668 /* dir/dir, remove target */
669 error
= pc_dirremove(tdp
, tnm
, NULL
, VDIR
, ctp
);
671 vnevent_rename_dest(PCTOV(tpcp
), PCTOV(tdp
),
673 VN_RELE(PCTOV(tpcp
));
678 /* Follow rename(2)'s spec... */
679 if (error
== ENOTEMPTY
) {
683 /* nondir/dir, bail */
688 if ((error
== 0) || (error
== ENOENT
)) {
689 offset_t lfn_offset
= -1;
691 struct pcdir
*direntries
;
694 pc_cluster16_t pct_lo
;
695 pc_cluster16_t pct_hi
;
698 struct buf
*bp
= NULL
;
702 struct pctime crtime
;
712 * Delete the old name, and create a new name.
714 if (filecasechange
== 1 && error
== 0)
716 ndirentries
= direntries_needed(tdp
, tnm
);
717 if (ndirentries
== -1) {
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
);
731 error
= pc_findentry(dp
, snm
, &slot
, &lfn_offset
);
735 pct_lo
= slot
.sl_ep
->pcd_scluster_lo
;
737 pct_hi
= slot
.sl_ep
->un
.pcd_scluster_hi
;
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) {
750 error
= pc_remove_long_fn(dp
, lfn_offset
);
752 pc_mark_irrecov(VFSTOPCFS(vp
->v_vfsp
));
756 slot
.sl_ep
->pcd_filename
[0] =
757 pcp
->pc_entry
.pcd_filename
[0] = PCD_ERASED
;
759 error
= geterror(slot
.sl_bp
);
763 pc_mark_irrecov(VFSTOPCFS(vp
->v_vfsp
));
769 * Make an entry from the supplied attributes.
771 direntries
= pc_name_to_pcdir(tdp
, tnm
, ndirentries
, &error
);
772 if (direntries
== NULL
) {
776 error
= pc_makedirentry(tdp
, direntries
, ndirentries
, NULL
,
778 kmem_free(direntries
, ndirentries
* sizeof (struct pcdir
));
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
);
789 blkno
= pc_daddrdb(fsp
, bp
->b_blkno
);
790 ep
->pcd_scluster_lo
= pct_lo
;
792 ep
->un
.pcd_scluster_hi
= pct_hi
;
794 ep
->un
.pcd_eattr
= eattr
;
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
;
803 error
= geterror(bp
);
804 pcp
->pc_eblkno
= blkno
;
805 pcp
->pc_eoffset
= boff
;
807 pcp
->pc_flags
|= PC_CHG
;
810 pc_mark_irrecov(VFSTOPCFS(vp
->v_vfsp
));
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) {
820 if ((error
= pc_nodeupdate(pcp
)) != 0) {
826 vnevent_rename_src(PCTOV(pcp
), PCTOV(dp
), snm
, ctp
);
828 vnevent_rename_dest_dir(PCTOV(tdp
), ctp
);
833 VN_RELE(PCTOV(tpcp
));
840 * Fix the ".." entry of the child directory so that it points to the
841 * new parent directory instead of the old one.
844 pc_dirfixdotdot(struct pcnode
*dp
, /* child directory being moved */
845 struct pcnode
*opdp
, /* old parent directory */
846 struct pcnode
*npdp
) /* new parent directory */
849 struct vnode
*vp
= PCTOV(dp
);
850 struct pcfs
*fsp
= VFSTOPCFS(vp
->v_vfsp
);
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
);
863 PC_DPRINTF0(1, "pc_dirfixdotdot: error in blkatoff\n");
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");
874 cn
= pc_getstartcluster(fsp
, &npdp
->pc_entry
);
875 pc_setstartcluster(fsp
, ep
, cn
);
878 error
= geterror(bp
);
881 PC_DPRINTF0(1, "pc_dirfixdotdot: error in write\n");
882 pc_mark_irrecov(fsp
);
890 * Search a directory for an entry.
891 * The directory should be locked as this routine
892 * will sleep on I/O while searching.
896 struct pcnode
*dp
, /* parent directory */
897 char *namep
, /* name to lookup */
898 struct pcslot
*slotp
,
899 offset_t
*lfn_offset
)
902 struct pcdir
*ep
= NULL
;
909 PC_DPRINTF2(6, "pc_findentry: looking for %s in dir 0x%p\n", namep
,
911 slotp
->sl_status
= SL_NONE
;
912 if (!(dp
->pc_entry
.pcd_attr
& PCA_DIR
)) {
916 * Verify that the dp is still valid on the disk
918 fsp
= VFSTOPCFS(vp
->v_vfsp
);
919 error
= pc_verify(fsp
);
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
);
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
;
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
)
962 offset
+= sizeof (struct pcdir
);
966 if (PCDL_IS_LFN(ep
)) {
968 if (pc_match_long_fn(dp
, namep
, &ep
,
969 slotp
, &offset
) == 0) {
970 if (lfn_offset
!= NULL
)
976 if (pc_match_short_fn(dp
, namep
, &ep
, slotp
, &offset
) == 0)
979 if (slotp
->sl_bp
!= NULL
) {
980 brelse(slotp
->sl_bp
);
987 * Obtain the block at offset "offset" in file pcp.
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");
1008 error
= pc_bmap(pcp
, pc_lblkno(fsp
, offset
), &bn
, (uint_t
*)0);
1012 bp
= bread(fsp
->pcfs_xdev
, bn
, size
);
1013 if (bp
->b_flags
& B_ERROR
) {
1014 PC_DPRINTF0(1, "pc_blkatoff: error\n");
1016 pc_mark_irrecov(fsp
);
1021 (struct pcdir
*)(bp
->b_un
.b_addr
+ pc_blkoff(fsp
, offset
));
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).
1050 * check for "." and "..".
1055 if ((c
!= '.') || (c
= *namep
)) /* ".x" or "..x" */
1062 * filename up to '.'
1067 if (!pc_validchar(c
))
1072 if (enable_long_filenames
)
1075 } while ((c
= *namep
++) != '\0' && c
!= '.');
1077 while (n
-- > 0) { /* fill with blanks */
1081 * remainder is extension
1085 while ((c
= *namep
++) != '\0' && n
--) {
1087 if (!pc_validchar(c
))
1091 if (enable_long_filenames
&& (c
!= '\0')) {
1096 while (n
-- > 0) { /* fill with blanks */
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
);
1114 char lfn
[PCMAXNAMLEN
+1];
1116 error
= pc_extract_long_fn(pcp
, lfn
, epp
, offset
, &slotp
->sl_bp
);
1118 if (error
== EINVAL
) {
1124 if ((u8_strcmp(lfn
, namep
, 0, U8_STRCMP_CI_UPPER
,
1125 U8_UNICODE_LATEST
, &error
) == 0) && (error
== 0)) {
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
);
1133 *offset
+= sizeof (struct pcdir
);
1136 /* If u8_strcmp detected an error it's sufficient to rtn 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
;
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
);
1162 error
= pc_parsename(namep
, fname
, fext
);
1164 *offset
+= sizeof (struct pcdir
);
1170 if ((bcmp(fname
, ep
->pcd_filename
, PCFNAMESIZE
) == 0) &&
1171 (bcmp(fext
, ep
->pcd_ext
, PCFEXTSIZE
) == 0)) {
1175 if (fname
[0] == '.') {
1176 if (fname
[1] == '.')
1177 slotp
->sl_flags
= SL_DOTDOT
;
1179 slotp
->sl_flags
= SL_DOT
;
1181 slotp
->sl_flags
= 0;
1184 pc_daddrdb(fsp
, slotp
->sl_bp
->b_blkno
);
1185 slotp
->sl_offset
= boff
;
1189 *offset
+= sizeof (struct pcdir
);
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.
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
);
1206 struct buf
*bp
= NULL
;
1207 struct pcdir
*ep
= NULL
;
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>
1215 boff
= pc_blkoff(fsp
, lfn_offset
);
1216 if (boff
== 0 || bp
== NULL
|| boff
>= bp
->b_bcount
) {
1219 error
= geterror(bp
);
1225 error
= pc_blkatoff(pcp
, lfn_offset
, &bp
, &ep
);
1229 if (!PCDL_IS_LFN(ep
)) {
1234 ep
->pcd_filename
[0] = PCD_ERASED
;
1236 lfn_offset
+= sizeof (struct pcdir
);
1239 /* now we're on the short entry */
1241 ep
->pcd_filename
[0] = PCD_ERASED
;
1246 error
= geterror(bp
);
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.
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;
1267 struct buf
*bp
= NULL
;
1268 struct vnode
*vp
= PCTOV(pcp
);
1269 struct pcfs
*fsp
= VFSTOPCFS(vp
->v_vfsp
);
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
) {
1286 error
= pc_blkatoff(pcp
, offset
, &bp
, &ep
);
1287 if (error
== ENOENT
) {
1290 /* extend directory */
1291 if (!IS_FAT32(fsp
) && (vp
->v_flag
& VROOT
))
1293 while (spaceneeded
> spaceavail
) {
1294 error
= pc_balloc(pcp
,
1295 pc_lblkno(fsp
, offset
), 1, &bn
);
1298 pcp
->pc_size
+= fsp
->pcfs_clsize
;
1299 spaceavail
+= fsp
->pcfs_clsize
;
1300 offset
+= fsp
->pcfs_clsize
;
1302 return (spaceoffset
);
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
);
1314 offset
+= sizeof (struct pcdir
);
1316 spaceoffset
= offset
;
1322 return (spaceoffset
);
1326 * Return how many long filename entries are needed.
1327 * A maximum of PCLFNCHUNKSIZE characters per entry, plus one for a
1331 direntries_needed(struct pcnode
*dp
, char *namep
)
1338 if (enable_long_filenames
== 0) {
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
)) {
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
);
1359 ret
= 1 + u16l
/ PCLFNCHUNKSIZE
;
1360 if (u16l
% PCLFNCHUNKSIZE
!= 0)
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
;
1379 struct pcdir_lfn
*lep
;
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
);
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
);
1404 kmem_free((caddr_t
)w2_str
, PCMAXNAM_UTF16
);
1408 nameend
= (char *)(w2_str
+ u16l
);
1409 u16l
%= PCLFNCHUNKSIZE
;
1414 nchars
= PCLFNCHUNKSIZE
;
1416 nchars
*= sizeof (uint16_t);
1418 /* short file name */
1419 error
= generate_short_name(dp
, namep
, ep
);
1421 kmem_free(bpcdir
, ndirentries
* sizeof (struct pcdir
));
1425 cksum
= pc_checksum_long_fn(ep
->pcd_filename
, ep
->pcd_ext
);
1426 for (i
= 0; i
< (ndirentries
- 1); i
++) {
1427 /* long file name */
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;
1443 generate_short_name(struct pcnode
*dp
, char *namep
, struct pcdir
*inep
)
1449 char fname
[PCFNAMESIZE
+1];
1450 char fext
[PCFEXTSIZE
+1];
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
++) {
1479 if (!pc_validchar(*namep
) && !pc_validchar(toupper(*namep
)))
1483 dot
= strrchr(namep
, '.');
1486 for (j
= 0, i
= 0; j
< PCFEXTSIZE
; i
++) {
1489 /* skip valid, but not generally good characters */
1490 if (dot
[i
] == ' ' || dot
[i
] == '\\')
1492 if (pc_validchar(dot
[i
]))
1494 else if (pc_validchar(toupper(dot
[i
])))
1495 fext
[j
++] = toupper(dot
[i
]);
1497 for (i
= j
; i
< PCFEXTSIZE
; i
++)
1501 for (i
= 0; i
< PCFEXTSIZE
; 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.
1515 bzero(fname
, sizeof (fname
));
1516 nchars
= PCFNAMESIZE
;
1528 for (j
= 0, i
= 0; j
< nchars
; i
++) {
1529 if ((&namep
[i
] == dot
) || (namep
[i
] == '\0'))
1531 /* skip valid, but not generally good characters */
1532 if (namep
[i
] == ' ' || namep
[i
] == '\\')
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
]);
1540 (void) sprintf(scratch
, "~%d", rev
);
1541 (void) strcat(fname
, scratch
);
1543 for (i
= strlen(fname
); i
< PCFNAMESIZE
; 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
);
1554 if (!shortname_exists(dp
, fname
, fext
))
1558 (void) strncpy(inep
->pcd_filename
, fname
, PCFNAMESIZE
);
1559 (void) strncpy(inep
->pcd_ext
, fext
, PCFEXTSIZE
);
1564 * Returns 1 if the passed-in filename is a short name, 0 if not.
1567 pc_is_short_file_name(char *namep
, int foldcase
)
1572 for (i
= 0; i
< PCFNAMESIZE
; i
++, namep
++) {
1578 c
= toupper(*namep
);
1581 if (!pc_validchar(c
))
1589 for (i
= 0; i
< PCFEXTSIZE
; i
++, namep
++) {
1593 c
= toupper(*namep
);
1596 if (!pc_validchar(c
))
1599 /* we should be done. If not... */
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.
1615 shortname_exists(struct pcnode
*dp
, char *fname
, char *fext
)
1617 struct buf
*bp
= NULL
;
1621 struct vnode
*vp
= PCTOV(dp
);
1622 struct pcfs
*fsp
= VFSTOPCFS(vp
->v_vfsp
);
1627 boff
= pc_blkoff(fsp
, offset
);
1628 if (boff
== 0 || bp
== NULL
|| boff
>= bp
->b_bcount
) {
1633 error
= pc_blkatoff(dp
, offset
, &bp
, &ep
);
1634 if (error
== ENOENT
)
1640 if (PCDL_IS_LFN(ep
) ||
1641 (ep
->pcd_filename
[0] == PCD_ERASED
)) {
1642 offset
+= sizeof (struct pcdir
);
1646 if (ep
->pcd_filename
[0] == PCD_UNUSED
)
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)) {
1657 offset
+= sizeof (struct pcdir
);
1668 pc_getstartcluster(struct pcfs
*fsp
, struct pcdir
*ep
)
1670 if (IS_FAT32(fsp
)) {
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
;
1680 return (ltohs(ep
->pcd_scluster_lo
));
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
);
1696 pc_cluster16_t cln16
;
1698 cln16
= (pc_cluster16_t
)cln
;
1699 ep
->pcd_scluster_lo
= htols(cln16
);