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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * fsck_pcfs -- routines for manipulating directories.
38 #include <sys/param.h>
40 #include <sys/byteorder.h>
41 #include <sys/dktp/fdisk.h>
42 #include <sys/fs/pc_fs.h>
43 #include <sys/fs/pc_dir.h>
44 #include <sys/fs/pc_label.h>
45 #include "pcfs_common.h"
46 #include "fsck_pcfs.h"
48 extern int32_t HiddenClusterCount
;
49 extern int32_t FileClusterCount
;
50 extern int32_t DirClusterCount
;
51 extern int32_t HiddenFileCount
;
52 extern int32_t LastCluster
;
53 extern int32_t FileCount
;
54 extern int32_t BadCount
;
55 extern int32_t DirCount
;
56 extern int32_t FATSize
;
57 extern off64_t PartitionOffset
;
58 extern bpb_t TheBIOSParameterBlock
;
63 static uchar_t
*CHKsList
= NULL
;
65 ClusterContents TheRootDir
;
71 * We have a bunch of routines for handling CHK names. A CHK name is
72 * simply a file name of the form "FILEnnnn.CHK", where the n's are the
73 * digits in the numbers from 1 to 9999. There are always four digits
74 * used, leading zeros are added as necessary.
76 * We use CHK names to link orphaned cluster chains back into the file
77 * system's root directory under an auspicious name so that the user
78 * may be able to recover some of their data.
80 * We use these routines to ensure CHK names we use don't conflict
81 * with any already present in the file system.
84 hasCHKName(struct pcdir
*dp
)
86 return (dp
->pcd_filename
[CHKNAME_F
] == 'F' &&
87 dp
->pcd_filename
[CHKNAME_I
] == 'I' &&
88 dp
->pcd_filename
[CHKNAME_L
] == 'L' &&
89 dp
->pcd_filename
[CHKNAME_E
] == 'E' &&
90 isdigit(dp
->pcd_filename
[CHKNAME_THOUSANDS
]) &&
91 isdigit(dp
->pcd_filename
[CHKNAME_HUNDREDS
]) &&
92 isdigit(dp
->pcd_filename
[CHKNAME_TENS
]) &&
93 isdigit(dp
->pcd_filename
[CHKNAME_ONES
]) &&
94 dp
->pcd_ext
[CHKNAME_C
] == 'C' &&
95 dp
->pcd_ext
[CHKNAME_H
] == 'H' &&
96 dp
->pcd_ext
[CHKNAME_K
] == 'K');
100 addEntryToCHKList(int chkNumber
)
102 /* silent failure on bogus value */
103 if (chkNumber
< 0 || chkNumber
> MAXCHKVAL
)
105 CHKsList
[chkNumber
/ NBBY
] |= (1 << (chkNumber
% NBBY
));
109 addToCHKList(struct pcdir
*dp
)
113 chknum
= 1000 * (dp
->pcd_filename
[CHKNAME_THOUSANDS
] - '0');
114 chknum
+= 100 * (dp
->pcd_filename
[CHKNAME_HUNDREDS
] - '0');
115 chknum
+= 10 * (dp
->pcd_filename
[CHKNAME_TENS
] - '0');
116 chknum
+= (dp
->pcd_filename
[CHKNAME_ONES
] - '0');
117 addEntryToCHKList(chknum
);
121 inUseCHKName(int chkNumber
)
123 return (CHKsList
[chkNumber
/ NBBY
] & (1 << (chkNumber
% NBBY
)));
127 appendToPath(struct pcdir
*dp
, char *thePath
, int *theLen
)
132 * Sometimes caller doesn't care about keeping track of the path
140 if (*theLen
< MAXPATHLEN
)
141 *(thePath
+ (*theLen
)++) = '/';
143 * Print out the file name part, but only up to the first
146 while (*theLen
< MAXPATHLEN
&& i
< PCFNAMESIZE
) {
148 * When we start seeing spaces we assume that's the
149 * end of the interesting characters in the name.
151 if ((dp
->pcd_filename
[i
] == ' ') ||
152 !(pc_validchar(dp
->pcd_filename
[i
])))
154 *(thePath
+ (*theLen
)++) = dp
->pcd_filename
[i
++];
157 * Leave now, if we don't have an extension (or room for one)
159 if ((dp
->pcd_ext
[i
] == ' ') || ((*theLen
) >= MAXPATHLEN
) ||
160 (!(pc_validchar(dp
->pcd_ext
[i
]))))
163 * Tack on the extension
165 *(thePath
+ (*theLen
)++) = '.';
167 while ((*theLen
< MAXPATHLEN
) && (i
< PCFEXTSIZE
)) {
168 if ((dp
->pcd_ext
[i
] == ' ') || !(pc_validchar(dp
->pcd_ext
[i
])))
170 *(thePath
+ (*theLen
)++) = dp
->pcd_ext
[i
++];
175 printName(FILE *outDest
, struct pcdir
*dp
)
178 for (i
= 0; i
< PCFNAMESIZE
; i
++) {
179 if ((dp
->pcd_filename
[i
] == ' ') ||
180 !(pc_validchar(dp
->pcd_filename
[i
])))
182 (void) fprintf(outDest
, "%c", dp
->pcd_filename
[i
]);
184 (void) fprintf(outDest
, ".");
185 for (i
= 0; i
< PCFEXTSIZE
; i
++) {
186 if (!(pc_validchar(dp
->pcd_ext
[i
])))
188 (void) fprintf(outDest
, "%c", dp
->pcd_ext
[i
]);
194 * Make sure the size in the directory entry matches what is
195 * actually allocated. If there is a mismatch, orphan all
196 * the allocated clusters. Returns SIZE_MATCHED if everything matches
197 * up, TRUNCATED to indicate truncation was necessary.
200 sanityCheckSize(int fd
, struct pcdir
*dp
, int32_t actualClusterCount
,
201 int isDir
, int32_t startCluster
, struct nameinfo
*fullPathName
,
202 struct pcdir
**orphanEntry
)
204 uint32_t sizeFromDir
;
208 bpc
= TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
209 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
210 sizeFromDir
= extractSize(dp
);
212 if (sizeFromDir
== 0)
213 return (SIZE_MATCHED
);
215 if ((sizeFromDir
> ((actualClusterCount
- 1) * bpc
)) &&
216 (sizeFromDir
<= (actualClusterCount
* bpc
)))
217 return (SIZE_MATCHED
);
219 if (fullPathName
!= NULL
) {
220 fullPathName
->references
++;
221 (void) fprintf(stderr
, "%s\n", fullPathName
->fullName
);
223 squirrelPath(fullPathName
, startCluster
);
224 (void) fprintf(stderr
,
225 gettext("Truncating chain due to incorrect size "
226 "in directory. Size from directory = %u bytes,\n"), sizeFromDir
);
227 if (actualClusterCount
== 0) {
228 (void) fprintf(stderr
,
229 gettext("Zero bytes are allocated to the file.\n"));
231 (void) fprintf(stderr
,
232 gettext("Allocated size in range %llu - %llu bytes.\n"),
233 ((actualClusterCount
- 1) * bpc
) + 1,
234 (actualClusterCount
* bpc
));
237 * Use splitChain() to make an orphan that is the entire allocation
240 splitChain(fd
, dp
, startCluster
, orphanEntry
, &ignorei
);
245 noteUsage(int fd
, int32_t startAt
, struct pcdir
*dp
, struct pcdir
*lp
,
246 int32_t longEntryStartCluster
, int isHidden
, int isDir
,
247 struct nameinfo
*fullPathName
)
249 struct pcdir
*orphanEntry
;
250 int32_t chain
= startAt
;
252 int savePathNextIteration
= 0;
254 ClusterInfo
*tmpl
= NULL
;
256 while ((chain
>= FIRST_CLUSTER
) && (chain
<= LastCluster
)) {
257 if ((markInUse(fd
, chain
, dp
, lp
, longEntryStartCluster
,
258 isHidden
? HIDDEN
: VISIBLE
, &tmpl
))
259 != CLINFO_NEWLY_ALLOCED
)
262 if (savePathNextIteration
== 1) {
263 savePathNextIteration
= 0;
264 if (fullPathName
!= NULL
)
265 fullPathName
->references
++;
266 squirrelPath(fullPathName
, chain
);
268 if (isMarkedBad(chain
)) {
270 savePathNextIteration
= 1;
273 HiddenClusterCount
++;
278 chain
= nextInChain(chain
);
281 * Do a sanity check on the file size in the directory entry.
282 * This may create an orphaned cluster chain.
284 if (sanityCheckSize(fd
, dp
, count
, isDir
, startAt
,
285 fullPathName
, &orphanEntry
) == TRUNCATED
) {
287 * The pre-existing directory entry has been truncated,
288 * so the chain associated with it no longer has any
289 * bad clusters. Instead, the new orphan has them.
292 truncChainWithBadCluster(fd
, orphanEntry
, startAt
);
300 storeInfoAboutEntry(int fd
, struct pcdir
*dp
, struct pcdir
*ldp
, int depth
,
301 int32_t longEntryStartCluster
, char *fullPath
, int *fullLen
)
303 struct nameinfo
*pathCopy
;
306 int hidden
= (dp
->pcd_attr
& PCA_HIDDEN
|| dp
->pcd_attr
& PCA_SYSTEM
);
307 int dir
= (dp
->pcd_attr
& PCA_DIR
);
316 appendToPath(dp
, fullPath
, fullLen
);
319 * Make a copy of the name at this point. We may want it to
320 * note the original source of an orphaned cluster.
323 (struct nameinfo
*)malloc(sizeof (struct nameinfo
))) != NULL
) {
324 if ((pathCopy
->fullName
=
325 (char *)malloc(*fullLen
+ 1)) != NULL
) {
326 pathCopy
->references
= 0;
327 (void) strncpy(pathCopy
->fullName
, fullPath
, *fullLen
);
328 pathCopy
->fullName
[*fullLen
] = '\0';
335 for (i
= 0; i
< depth
; i
++)
336 (void) fprintf(stderr
, " ");
338 (void) fprintf(stderr
, "[");
340 (void) fprintf(stderr
, "|_");
342 (void) fprintf(stderr
, gettext("(%06d) "), FileCount
);
343 printName(stderr
, dp
);
345 (void) fprintf(stderr
, "]");
346 (void) fprintf(stderr
,
347 gettext(", %u bytes, start cluster %d"),
348 extractSize(dp
), extractStartCluster(dp
));
349 (void) fprintf(stderr
, "\n");
351 start
= extractStartCluster(dp
);
352 haveBad
= noteUsage(fd
, start
, dp
, ldp
, longEntryStartCluster
,
353 hidden
, dir
, pathCopy
);
355 if (dir
&& pathCopy
->fullName
!= NULL
) {
356 (void) fprintf(stderr
,
357 gettext("Adjusting for bad allocation units in "
358 "the meta-data of:\n "));
359 (void) fprintf(stderr
, pathCopy
->fullName
);
360 (void) fprintf(stderr
, "\n");
362 truncChainWithBadCluster(fd
, dp
, start
);
364 if ((pathCopy
!= NULL
) && (pathCopy
->references
== 0)) {
365 free(pathCopy
->fullName
);
371 storeInfoAboutLabel(struct pcdir
*dp
)
374 * XXX eventually depth should be passed to this routine just
375 * as it is with storeInfoAboutEntry(). If it isn't zero, then
376 * we've got a bogus directory entry.
379 (void) fprintf(stderr
, gettext("** "));
380 printName(stderr
, dp
);
381 (void) fprintf(stderr
, gettext(" **\n"));
386 searchChecks(struct pcdir
*dp
, int operation
, char matchRequired
,
387 struct pcdir
**found
)
390 * We support these searching operations:
393 * look for the first file with a certain attribute
394 * (e.g, find all hidden files)
396 * look for the first file with a certain status
397 * (e.g., the file has been marked deleted; making
398 * its directory entry reusable)
400 * look for all files with short names of the form
401 * FILENNNN.CHK. These are the file names we give
402 * to chains of orphaned clusters we relink into the
403 * file system. This find facility allows us to seek
404 * out all existing files of this naming form so that
405 * we may create unique file names for new orphans.
407 if (operation
== PCFS_FIND_ATTR
&& dp
->pcd_attr
== matchRequired
) {
409 } else if (operation
== PCFS_FIND_STATUS
&&
410 dp
->pcd_filename
[0] == matchRequired
) {
412 } else if (operation
== PCFS_FIND_CHKS
&& hasCHKName(dp
)) {
418 catalogEntry(int fd
, struct pcdir
*dp
, struct pcdir
*longdp
,
419 int32_t currentCluster
, int depth
, char *recordPath
, int *pathLen
)
421 if (dp
->pcd_attr
& PCA_LABEL
) {
422 storeInfoAboutLabel(dp
);
424 storeInfoAboutEntry(fd
, dp
, longdp
, depth
, currentCluster
,
425 recordPath
, pathLen
);
432 * This is the main workhouse routine for traversing pcfs metadata.
433 * There isn't a lot to the metadata. Basically there is a root
434 * directory somewhere (either in its own special place outside the
435 * data area or in a data cluster). The root directory (and all other
436 * directories) are filled with a number of fixed size entries. An
437 * entry has the filename and extension, the file's attributes, the
438 * file's size, and the starting data cluster of the storage allocated
439 * to the file. To determine which clusters are assigned to the file,
440 * you start at the starting cluster entry in the FAT, and follow the
441 * chain of entries in the FAT.
445 * descriptor for accessing the raw file system data
447 * original caller supplies the initial starting cluster,
448 * subsequent recursive calls are made with updated
449 * cluster numbers for the sub-directories.
451 * pointer to the directory data bytes
453 * size of the whole buffer of data bytes (usually it is
454 * the size of a cluster, but the root directory on
455 * FAT12/16 is not necessarily the same size as a cluster).
457 * original caller should set it to zero (assuming they are
458 * starting from the root directory). This number is used to
459 * change the indentation of file names presented as debug info.
461 * boolean indicates if we should descend into subdirectories.
463 * what, if any, matching should be performed.
464 * The PCFS_TRAVERSE_ALL operation is a depth first traversal
465 * of all nodes in the metadata tree, that tracks all the
466 * clusters in use (according to the meta-data, at least)
468 * value to be matched (if any)
471 * used to return pointer to a directory entry that matches
472 * the search requirement
473 * original caller should pass in a pointer to a NULL pointer.
476 * if no match found, last cluster num of starting directory
479 * if no match found, return parameter stores pointer to where
480 * new directory entry could be appended to existing directory
483 * as files are discovered, and directories traversed, this
484 * buffer is used to store the current full path name.
487 * this is in the integer length of the current full path name.
490 visitNodes(int fd
, int32_t currentCluster
, ClusterContents
*dirData
,
491 int32_t dirDataLen
, int depth
, int descend
, int operation
,
492 char matchRequired
, struct pcdir
**found
, int32_t *lastDirCluster
,
493 struct pcdir
**dirEnd
, char *recordPath
, int *pathLen
)
495 struct pcdir
*longdp
= NULL
;
498 int withinLongName
= 0;
499 int saveLen
= *pathLen
;
504 * A directory entry where the first character of the name is
505 * PCD_UNUSED indicates the end of the directory.
507 while ((uchar_t
*)dp
< dirData
->bytes
+ dirDataLen
&&
508 dp
->pcd_filename
[0] != PCD_UNUSED
) {
510 * Handle the special case find operations.
512 searchChecks(dp
, operation
, matchRequired
, found
);
516 * Are we looking at part of a long file name entry?
517 * If so, we may need to note the start of the name.
518 * We don't do any further processing of long file
521 * We also skip deleted entries and the '.' and '..'
524 if ((dp
->pcd_attr
& PCDL_LFN_BITS
) == PCDL_LFN_BITS
) {
525 if (!withinLongName
) {
527 longStart
= currentCluster
;
532 } else if ((dp
->pcd_filename
[0] == PCD_ERASED
) ||
533 (dp
->pcd_filename
[0] == '.')) {
535 * XXX - if we were within a long name, then
536 * its existence is bogus, because it is not
537 * attached to any real file.
544 if (operation
== PCFS_TRAVERSE_ALL
)
545 catalogEntry(fd
, dp
, longdp
, longStart
, depth
,
546 recordPath
, pathLen
);
549 if (dp
->pcd_attr
& PCA_DIR
&& descend
== PCFS_VISIT_SUBDIRS
) {
550 traverseDir(fd
, extractStartCluster(dp
), depth
+ 1,
551 descend
, operation
, matchRequired
, found
,
552 lastDirCluster
, dirEnd
, recordPath
, pathLen
);
561 if ((uchar_t
*)dp
< dirData
->bytes
+ dirDataLen
) {
563 * We reached the end of directory before the end of
564 * our provided data (a cluster). That means this cluster
565 * is the last one in this directory's chain. It also
566 * means we've just looked at the last directory entry.
568 *lastDirCluster
= currentCluster
;
573 * If there is more to the directory we'll go get it otherwise we
574 * are done traversing this directory.
576 if ((currentCluster
== FAKE_ROOTDIR_CLUST
) ||
577 (lastInFAT(currentCluster
))) {
578 *lastDirCluster
= currentCluster
;
581 traverseDir(fd
, nextInChain(currentCluster
),
582 depth
, descend
, operation
, matchRequired
,
583 found
, lastDirCluster
, dirEnd
, recordPath
, pathLen
);
590 * For use with 12 and 16 bit FATs that have a root directory outside
591 * of the file system. This is a general purpose routine that
592 * can be used simply to visit all of the nodes in the metadata or
593 * to find the first instance of something, e.g., the first directory
594 * entry where the file is marked deleted.
596 * Inputs are described in the commentary for visitNodes() above.
599 traverseFromRoot(int fd
, int depth
, int descend
, int operation
,
600 char matchRequired
, struct pcdir
**found
, int32_t *lastDirCluster
,
601 struct pcdir
**dirEnd
, char *recordPath
, int *pathLen
)
603 visitNodes(fd
, FAKE_ROOTDIR_CLUST
, &TheRootDir
, RootDirSize
, depth
,
604 descend
, operation
, matchRequired
, found
, lastDirCluster
, dirEnd
,
605 recordPath
, pathLen
);
610 * For use with all FATs outside of the initial root directory on
611 * 12 and 16 bit FAT file systems. This is a general purpose routine
612 * that can be used simply to visit all of the nodes in the metadata or
613 * to find the first instance of something, e.g., the first directory
614 * entry where the file is marked deleted.
618 * starting cluster of the directory
620 * This is the cluster that is the first one in this directory.
621 * We read it right away, so we can provide it as data to visitNodes().
622 * Note that we cache this cluster as we read it, because it is
623 * metadata and we cache all metadata. By doing so, we can
624 * keep pointers to directory entries for quickly moving around and
625 * fixing up any problems we find. Of course if we get a big
626 * filesystem with a huge amount of metadata we may be hosed, as
627 * we'll likely run out of memory.
629 * I believe in the future this will have to be addressed. It
630 * may be possible to do more of the processing of problems
631 * within directories as they are cached, so that when memory
632 * runs short we can free cached directories we are already
635 * The remainder of inputs are described in visitNodes() comments.
638 traverseDir(int fd
, int32_t startAt
, int depth
, int descend
, int operation
,
639 char matchRequired
, struct pcdir
**found
, int32_t *lastDirCluster
,
640 struct pcdir
**dirEnd
, char *recordPath
, int *pathLen
)
642 ClusterContents dirdata
;
643 int32_t dirdatasize
= 0;
645 if (startAt
< FIRST_CLUSTER
|| startAt
> LastCluster
)
648 if (readCluster(fd
, startAt
, &(dirdata
.bytes
), &dirdatasize
,
649 RDCLUST_DO_CACHE
) != RDCLUST_GOOD
) {
650 (void) fprintf(stderr
,
651 gettext("Unable to get more directory entries!\n"));
655 if (operation
== PCFS_TRAVERSE_ALL
) {
657 (void) fprintf(stderr
,
658 gettext("Directory traversal enters "
659 "allocation unit %d.\n"), startAt
);
661 visitNodes(fd
, startAt
, &dirdata
, dirdatasize
, depth
, descend
,
662 operation
, matchRequired
, found
, lastDirCluster
, dirEnd
,
663 recordPath
, pathLen
);
667 createCHKNameList(int fd
)
669 struct pcdir
*ignorep1
, *ignorep2
;
671 char *ignorecp
= NULL
;
675 ignorep1
= ignorep2
= NULL
;
676 if (!OkayToRelink
|| CHKsList
!= NULL
)
680 * Allocate an array to keep a bit map of the integer
681 * values used in CHK names.
684 (uchar_t
*)calloc(1, idivceil(MAXCHKVAL
, NBBY
))) == NULL
) {
690 * Search the root directory for all the files with names of
691 * the form FILEXXXX.CHK. The root directory is an area
692 * outside of the file space on FAT12 and FAT16 file systems.
693 * On FAT32 file systems, the root directory is in a file
694 * area cluster just like any other directory.
697 traverseFromRoot(fd
, 0, PCFS_NO_SUBDIRS
, PCFS_FIND_CHKS
,
698 ignore
, &ignorep1
, &ignore32
, &ignorep2
, ignorecp
,
702 traverseDir(fd
, TheBIOSParameterBlock
.bpb32
.root_dir_clust
,
703 0, PCFS_NO_SUBDIRS
, PCFS_FIND_CHKS
, ignore
,
704 &ignorep1
, &ignore32
, &ignorep2
, ignorecp
, &ignoreint
);
710 nextAvailableCHKName(int *chosen
)
712 static char nameBuf
[PCFNAMESIZE
];
718 nameBuf
[CHKNAME_F
] = 'F';
719 nameBuf
[CHKNAME_I
] = 'I';
720 nameBuf
[CHKNAME_L
] = 'L';
721 nameBuf
[CHKNAME_E
] = 'E';
723 for (i
= 1; i
<= MAXCHKVAL
; i
++) {
724 if (!inUseCHKName(i
))
727 if (i
<= MAXCHKVAL
) {
728 nameBuf
[CHKNAME_THOUSANDS
] = '0' + (i
/ 1000);
729 nameBuf
[CHKNAME_HUNDREDS
] = '0' + ((i
% 1000) / 100);
730 nameBuf
[CHKNAME_TENS
] = '0' + ((i
% 100) / 10);
731 nameBuf
[CHKNAME_ONES
] = '0' + (i
% 10);
735 (void) fprintf(stderr
,
736 gettext("Sorry, no names available for "
737 "relinking orphan chains!\n"));
744 extractSize(struct pcdir
*dp
)
748 read_32_bits((uchar_t
*)&(dp
->pcd_size
), &returnMe
);
753 extractStartCluster(struct pcdir
*dp
)
758 read_16_bits((uchar_t
*)&(dp
->un
.pcd_scluster_hi
), &hi
);
759 read_16_bits((uchar_t
*)&(dp
->pcd_scluster_lo
), &lo
);
760 return ((int32_t)((hi
<< 16) | lo
));
762 read_16_bits((uchar_t
*)&(dp
->pcd_scluster_lo
), &lo
);
763 return ((int32_t)lo
);
767 static struct pcdir
*
768 findAvailableRootDirEntSlot(int fd
, int32_t *clusterWithSlot
)
770 struct pcdir
*deletedEntry
= NULL
;
771 struct pcdir
*appendPoint
= NULL
;
772 char *ignorecp
= NULL
;
775 *clusterWithSlot
= 0;
778 * First off, try to find an erased entry in the root
779 * directory. The root directory is an area outside of the
780 * file space on FAT12 and FAT16 file systems. On FAT32 file
781 * systems, the root directory is in a file area cluster just
782 * like any other directory.
785 traverseFromRoot(fd
, 0, PCFS_NO_SUBDIRS
, PCFS_FIND_STATUS
,
786 PCD_ERASED
, &deletedEntry
, clusterWithSlot
,
787 &appendPoint
, ignorecp
, &ignore
);
790 traverseDir(fd
, TheBIOSParameterBlock
.bpb32
.root_dir_clust
,
791 0, PCFS_NO_SUBDIRS
, PCFS_FIND_STATUS
, PCD_ERASED
,
792 &deletedEntry
, clusterWithSlot
, &appendPoint
, ignorecp
,
796 * If we found a deleted file in the directory we'll overwrite
800 return (deletedEntry
);
802 * If there is room at the end of the existing directory, we
803 * should place the new entry there.
806 return (appendPoint
);
808 * XXX need to grow the directory
814 insertDirEnt(struct pcdir
*slot
, struct pcdir
*entry
, int32_t clusterWithSlot
)
816 (void) memcpy(slot
, entry
, sizeof (struct pcdir
));
817 markClusterModified(clusterWithSlot
);
821 * Convert current UNIX time into a PCFS timestamp (which is in local time).
823 * Since the "seconds" field of that is only accurate to 2sec precision,
824 * we allow for the optional (used only for creation times on FAT) "msec"
825 * parameter that takes the fractional part.
828 getNow(struct pctime
*pctp
, uchar_t
*msec
)
835 * Disable daylight savings corrections - Solaris PCFS doesn't
836 * support such conversions yet. Save timestamps in local time.
841 (void) localtime_r(&now
, &tm
);
843 dat
= (tm
.tm_year
- 80) << YEARSHIFT
;
844 dat
|= tm
.tm_mon
<< MONSHIFT
;
845 dat
|= tm
.tm_mday
<< DAYSHIFT
;
846 tim
= tm
.tm_hour
<< HOURSHIFT
;
847 tim
|= tm
.tm_min
<< MINSHIFT
;
848 tim
|= (tm
.tm_sec
/ 2) << SECSHIFT
;
851 * Sanity check. If we overflow the PCFS timestamp range
852 * we set the time to 01/01/1980, 00:00:00
854 if (dat
< 80 || dat
> 227)
857 pctp
->pct_date
= LE_16(dat
);
858 pctp
->pct_time
= LE_16(tim
);
860 *msec
= (tm
.tm_sec
& 1) ? 100 : 0;
864 * FAT file systems store the following time information in a directory
866 * timestamp member of "struct pcdir"
867 * ======================================================================
868 * creation time pcd_crtime.pct_time
869 * creation date pcd_crtime.pct_date
870 * last access date pcd_ladate
871 * last modify time pcd_mtime.pct_time
872 * last modify date pcd_mtime.pct_date
874 * No access time is kept.
877 updateDirEnt_CreatTime(struct pcdir
*dp
)
879 getNow(&dp
->pcd_crtime
, &dp
->pcd_crtime_msec
);
880 markClusterModified(findImpactedCluster(dp
));
884 updateDirEnt_ModTimes(struct pcdir
*dp
)
888 getNow(&dp
->pcd_mtime
, NULL
);
889 dp
->pcd_ladate
= dp
->pcd_mtime
.pct_date
;
890 dp
->pcd_attr
|= PCA_ARCH
;
891 markClusterModified(findImpactedCluster(dp
));
895 addRootDirEnt(int fd
, struct pcdir
*new)
900 if ((added
= findAvailableRootDirEntSlot(fd
, &inCluster
)) != NULL
) {
901 insertDirEnt(added
, new, inCluster
);
908 * FAT12 and FAT16 have a root directory outside the normal file space,
909 * so we have separate routines for finding and reading the root directory.
912 seekRootDirectory(int fd
)
917 * The RootDir immediately follows the FATs, which in
918 * turn immediately follow the reserved sectors.
920 seekto
= (off64_t
)TheBIOSParameterBlock
.bpb
.resv_sectors
*
921 TheBIOSParameterBlock
.bpb
.bytes_per_sector
+
922 (off64_t
)FATSize
* TheBIOSParameterBlock
.bpb
.num_fats
+
923 (off64_t
)PartitionOffset
;
925 (void) fprintf(stderr
,
926 gettext("Seeking root directory @%lld.\n"), seekto
);
927 return (lseek64(fd
, seekto
, SEEK_SET
));
931 getRootDirectory(int fd
)
935 if (TheRootDir
.bytes
!= NULL
)
937 else if ((TheRootDir
.bytes
= (uchar_t
*)malloc(RootDirSize
)) == NULL
) {
938 mountSanityCheckFails();
939 perror(gettext("No memory for a copy of the root directory"));
944 if (seekRootDirectory(fd
) < 0) {
945 mountSanityCheckFails();
946 perror(gettext("Cannot seek to RootDir"));
952 (void) fprintf(stderr
,
953 gettext("Reading root directory.\n"));
954 if ((bytesRead
= read(fd
, TheRootDir
.bytes
, RootDirSize
)) !=
956 mountSanityCheckFails();
958 perror(gettext("Cannot read a RootDir"));
960 (void) fprintf(stderr
,
961 gettext("Short read of RootDir\n"));
967 (void) fprintf(stderr
,
968 gettext("Dump of root dir's first 256 bytes.\n"));
970 dump_bytes(TheRootDir
.bytes
, 256);
975 writeRootDirMods(int fd
)
977 ssize_t bytesWritten
;
979 if (!TheRootDir
.bytes
) {
980 (void) fprintf(stderr
,
981 gettext("Internal error: No Root directory to write\n"));
985 if (!RootDirModified
) {
987 (void) fprintf(stderr
,
988 gettext("No root directory changes need to "
996 (void) fprintf(stderr
,
997 gettext("Writing root directory.\n"));
998 if (seekRootDirectory(fd
) < 0) {
999 perror(gettext("Cannot write the RootDir (seek failed)"));
1003 if ((bytesWritten
= write(fd
, TheRootDir
.bytes
, RootDirSize
)) !=
1005 if (bytesWritten
< 0) {
1006 perror(gettext("Cannot write the RootDir"));
1008 (void) fprintf(stderr
,
1009 gettext("Short write of root directory\n"));
1014 RootDirModified
= 0;
1018 newDirEnt(struct pcdir
*copyme
)
1022 if ((ndp
= (struct pcdir
*)calloc(1, sizeof (struct pcdir
))) == NULL
) {
1023 (void) fprintf(stderr
, gettext("Out of memory to create a "
1024 "new directory entry!\n"));
1028 (void) memcpy(ndp
, copyme
, sizeof (struct pcdir
));
1029 ndp
->pcd_ext
[CHKNAME_C
] = 'C';
1030 ndp
->pcd_ext
[CHKNAME_H
] = 'H';
1031 ndp
->pcd_ext
[CHKNAME_K
] = 'K';
1032 updateDirEnt_CreatTime(ndp
);
1033 updateDirEnt_ModTimes(ndp
);
1038 updateDirEnt_Size(struct pcdir
*dp
, uint32_t newSize
)
1040 uchar_t
*p
= (uchar_t
*)&(dp
->pcd_size
);
1041 store_32_bits(&p
, newSize
);
1042 markClusterModified(findImpactedCluster(dp
));
1046 updateDirEnt_Start(struct pcdir
*dp
, int32_t newStart
)
1048 uchar_t
*p
= (uchar_t
*)&(dp
->pcd_scluster_lo
);
1049 store_16_bits(&p
, newStart
& 0xffff);
1051 p
= (uchar_t
*)&(dp
->un
.pcd_scluster_hi
);
1052 store_16_bits(&p
, newStart
>> 16);
1054 markClusterModified(findImpactedCluster(dp
));
1058 updateDirEnt_Name(struct pcdir
*dp
, char *newName
)
1062 for (i
= 0; i
< PCFNAMESIZE
; i
++) {
1064 dp
->pcd_filename
[i
] = *newName
++;
1066 dp
->pcd_filename
[i
] = ' ';
1068 markClusterModified(findImpactedCluster(dp
));