8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / fs.d / pcfs / fsck / dir.c
blob022c9350c764dacfe618014300bba094503fb1a5
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
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.
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <libintl.h>
36 #include <ctype.h>
37 #include <time.h>
38 #include <sys/param.h>
39 #include <sys/time.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;
59 extern int ReadOnly;
60 extern int IsFAT32;
61 extern int Verbose;
63 static uchar_t *CHKsList = NULL;
65 ClusterContents TheRootDir;
66 int32_t RootDirSize;
67 int RootDirModified;
68 int OkayToRelink = 1;
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.
83 static int
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');
99 void
100 addEntryToCHKList(int chkNumber)
102 /* silent failure on bogus value */
103 if (chkNumber < 0 || chkNumber > MAXCHKVAL)
104 return;
105 CHKsList[chkNumber / NBBY] |= (1 << (chkNumber % NBBY));
108 static void
109 addToCHKList(struct pcdir *dp)
111 int chknum;
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);
120 static int
121 inUseCHKName(int chkNumber)
123 return (CHKsList[chkNumber / NBBY] & (1 << (chkNumber % NBBY)));
126 static void
127 appendToPath(struct pcdir *dp, char *thePath, int *theLen)
129 int i = 0;
132 * Sometimes caller doesn't care about keeping track of the path
134 if (thePath == NULL)
135 return;
138 * Prepend /
140 if (*theLen < MAXPATHLEN)
141 *(thePath + (*theLen)++) = '/';
143 * Print out the file name part, but only up to the first
144 * space.
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])))
153 break;
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]))))
161 return;
163 * Tack on the extension
165 *(thePath + (*theLen)++) = '.';
166 i = 0;
167 while ((*theLen < MAXPATHLEN) && (i < PCFEXTSIZE)) {
168 if ((dp->pcd_ext[i] == ' ') || !(pc_validchar(dp->pcd_ext[i])))
169 break;
170 *(thePath + (*theLen)++) = dp->pcd_ext[i++];
174 static void
175 printName(FILE *outDest, struct pcdir *dp)
177 int i;
178 for (i = 0; i < PCFNAMESIZE; i++) {
179 if ((dp->pcd_filename[i] == ' ') ||
180 !(pc_validchar(dp->pcd_filename[i])))
181 break;
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])))
187 break;
188 (void) fprintf(outDest, "%c", dp->pcd_ext[i]);
193 * sanityCheckSize
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.
199 static int
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;
205 int32_t ignorei = 0;
206 int64_t bpc;
208 bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster *
209 TheBIOSParameterBlock.bpb.bytes_per_sector;
210 sizeFromDir = extractSize(dp);
211 if (isDir) {
212 if (sizeFromDir == 0)
213 return (SIZE_MATCHED);
214 } else {
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"));
230 } else {
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
238 * chain.
240 splitChain(fd, dp, startCluster, orphanEntry, &ignorei);
241 return (TRUNCATED);
244 static int
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;
251 int32_t count = 0;
252 int savePathNextIteration = 0;
253 int haveBad = 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)
260 break;
261 count++;
262 if (savePathNextIteration == 1) {
263 savePathNextIteration = 0;
264 if (fullPathName != NULL)
265 fullPathName->references++;
266 squirrelPath(fullPathName, chain);
268 if (isMarkedBad(chain)) {
269 haveBad = 1;
270 savePathNextIteration = 1;
272 if (isHidden)
273 HiddenClusterCount++;
274 else if (isDir)
275 DirClusterCount++;
276 else
277 FileClusterCount++;
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.
291 if (haveBad > 0) {
292 truncChainWithBadCluster(fd, orphanEntry, startAt);
294 haveBad = 0;
296 return (haveBad);
299 static void
300 storeInfoAboutEntry(int fd, struct pcdir *dp, struct pcdir *ldp, int depth,
301 int32_t longEntryStartCluster, char *fullPath, int *fullLen)
303 struct nameinfo *pathCopy;
304 int32_t start;
305 int haveBad;
306 int hidden = (dp->pcd_attr & PCA_HIDDEN || dp->pcd_attr & PCA_SYSTEM);
307 int dir = (dp->pcd_attr & PCA_DIR);
308 int i;
310 if (hidden)
311 HiddenFileCount++;
312 else if (dir)
313 DirCount++;
314 else
315 FileCount++;
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.
322 if ((pathCopy =
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';
329 } else {
330 free(pathCopy);
331 pathCopy = NULL;
334 if (Verbose) {
335 for (i = 0; i < depth; i++)
336 (void) fprintf(stderr, " ");
337 if (hidden)
338 (void) fprintf(stderr, "[");
339 else if (dir)
340 (void) fprintf(stderr, "|_");
341 else
342 (void) fprintf(stderr, gettext("(%06d) "), FileCount);
343 printName(stderr, dp);
344 if (hidden)
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);
354 if (haveBad > 0) {
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);
366 free(pathCopy);
370 static void
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.
378 if (Verbose) {
379 (void) fprintf(stderr, gettext("** "));
380 printName(stderr, dp);
381 (void) fprintf(stderr, gettext(" **\n"));
385 static void
386 searchChecks(struct pcdir *dp, int operation, char matchRequired,
387 struct pcdir **found)
390 * We support these searching operations:
392 * PCFS_FIND_ATTR
393 * look for the first file with a certain attribute
394 * (e.g, find all hidden files)
395 * PCFS_FIND_STATUS
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)
399 * PCFS_FIND_CHKS
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) {
408 *found = dp;
409 } else if (operation == PCFS_FIND_STATUS &&
410 dp->pcd_filename[0] == matchRequired) {
411 *found = dp;
412 } else if (operation == PCFS_FIND_CHKS && hasCHKName(dp)) {
413 addToCHKList(dp);
417 static void
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);
423 } else {
424 storeInfoAboutEntry(fd, dp, longdp, depth, currentCluster,
425 recordPath, pathLen);
430 * visitNodes()
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.
443 * Arguments are:
444 * fd
445 * descriptor for accessing the raw file system data
446 * currentCluster
447 * original caller supplies the initial starting cluster,
448 * subsequent recursive calls are made with updated
449 * cluster numbers for the sub-directories.
450 * dirData
451 * pointer to the directory data bytes
452 * dirDataLen
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).
456 * depth
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.
460 * descend
461 * boolean indicates if we should descend into subdirectories.
462 * operation
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)
467 * matchRequired
468 * value to be matched (if any)
469 * found
470 * output parameter
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.
474 * lastDirCluster
475 * output parameter
476 * if no match found, last cluster num of starting directory
477 * dirEnd
478 * output parameter
479 * if no match found, return parameter stores pointer to where
480 * new directory entry could be appended to existing directory
481 * recordPath
482 * output parameter
483 * as files are discovered, and directories traversed, this
484 * buffer is used to store the current full path name.
485 * pathLen
486 * output parameter
487 * this is in the integer length of the current full path name.
489 static void
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;
496 struct pcdir *dp;
497 int32_t longStart;
498 int withinLongName = 0;
499 int saveLen = *pathLen;
501 dp = dirData->dirp;
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);
513 if (*found)
514 break;
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
519 * name entries.
521 * We also skip deleted entries and the '.' and '..'
522 * entries.
524 if ((dp->pcd_attr & PCDL_LFN_BITS) == PCDL_LFN_BITS) {
525 if (!withinLongName) {
526 withinLongName++;
527 longStart = currentCluster;
528 longdp = dp;
530 dp++;
531 continue;
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.
539 withinLongName = 0;
540 dp++;
541 continue;
543 withinLongName = 0;
544 if (operation == PCFS_TRAVERSE_ALL)
545 catalogEntry(fd, dp, longdp, longStart, depth,
546 recordPath, pathLen);
547 longdp = NULL;
548 longStart = 0;
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);
553 if (*found)
554 break;
556 dp++;
557 *pathLen = saveLen;
559 if (*found)
560 return;
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;
569 *dirEnd = dp;
570 return;
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;
579 return;
580 } else {
581 traverseDir(fd, nextInChain(currentCluster),
582 depth, descend, operation, matchRequired,
583 found, lastDirCluster, dirEnd, recordPath, pathLen);
584 *pathLen = saveLen;
589 * traverseFromRoot()
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.
598 void
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);
609 * traverseDir()
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.
616 * Unique Input is:
617 * startAt
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
633 * finished visiting.
635 * The remainder of inputs are described in visitNodes() comments.
637 void
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)
646 return;
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"));
652 return;
655 if (operation == PCFS_TRAVERSE_ALL) {
656 if (Verbose)
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);
666 void
667 createCHKNameList(int fd)
669 struct pcdir *ignorep1, *ignorep2;
670 int32_t ignore32;
671 char *ignorecp = NULL;
672 char ignore = '\0';
673 int ignoreint = 0;
675 ignorep1 = ignorep2 = NULL;
676 if (!OkayToRelink || CHKsList != NULL)
677 return;
680 * Allocate an array to keep a bit map of the integer
681 * values used in CHK names.
683 if ((CHKsList =
684 (uchar_t *)calloc(1, idivceil(MAXCHKVAL, NBBY))) == NULL) {
685 OkayToRelink = 0;
686 return;
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.
696 if (!IsFAT32) {
697 traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS,
698 ignore, &ignorep1, &ignore32, &ignorep2, ignorecp,
699 &ignoreint);
700 } else {
701 DirCount++;
702 traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
703 0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS, ignore,
704 &ignorep1, &ignore32, &ignorep2, ignorecp, &ignoreint);
709 char *
710 nextAvailableCHKName(int *chosen)
712 static char nameBuf[PCFNAMESIZE];
713 int i;
715 if (!OkayToRelink)
716 return (NULL);
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))
725 break;
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);
732 *chosen = i;
733 return (nameBuf);
734 } else {
735 (void) fprintf(stderr,
736 gettext("Sorry, no names available for "
737 "relinking orphan chains!\n"));
738 OkayToRelink = 0;
739 return (NULL);
743 uint32_t
744 extractSize(struct pcdir *dp)
746 uint32_t returnMe;
748 read_32_bits((uchar_t *)&(dp->pcd_size), &returnMe);
749 return (returnMe);
752 int32_t
753 extractStartCluster(struct pcdir *dp)
755 uint32_t lo, hi;
757 if (IsFAT32) {
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));
761 } else {
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;
773 int ignore = 0;
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.
784 if (!IsFAT32) {
785 traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS,
786 PCD_ERASED, &deletedEntry, clusterWithSlot,
787 &appendPoint, ignorecp, &ignore);
788 } else {
789 DirCount++;
790 traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
791 0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS, PCD_ERASED,
792 &deletedEntry, clusterWithSlot, &appendPoint, ignorecp,
793 &ignore);
796 * If we found a deleted file in the directory we'll overwrite
797 * that entry.
799 if (deletedEntry)
800 return (deletedEntry);
802 * If there is room at the end of the existing directory, we
803 * should place the new entry there.
805 if (appendPoint)
806 return (appendPoint);
808 * XXX need to grow the directory
810 return (NULL);
813 static void
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.
827 static void
828 getNow(struct pctime *pctp, uchar_t *msec)
830 time_t now;
831 struct tm tm;
832 ushort_t tim, dat;
835 * Disable daylight savings corrections - Solaris PCFS doesn't
836 * support such conversions yet. Save timestamps in local time.
838 daylight = 0;
840 (void) time(&now);
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)
855 dat = tim = 0;
857 pctp->pct_date = LE_16(dat);
858 pctp->pct_time = LE_16(tim);
859 if (msec)
860 *msec = (tm.tm_sec & 1) ? 100 : 0;
864 * FAT file systems store the following time information in a directory
865 * entry:
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.
876 static void
877 updateDirEnt_CreatTime(struct pcdir *dp)
879 getNow(&dp->pcd_crtime, &dp->pcd_crtime_msec);
880 markClusterModified(findImpactedCluster(dp));
883 static void
884 updateDirEnt_ModTimes(struct pcdir *dp)
886 timestruc_t ts;
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));
894 struct pcdir *
895 addRootDirEnt(int fd, struct pcdir *new)
897 struct pcdir *added;
898 int32_t inCluster;
900 if ((added = findAvailableRootDirEntSlot(fd, &inCluster)) != NULL) {
901 insertDirEnt(added, new, inCluster);
902 return (added);
904 return (NULL);
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.
911 static off64_t
912 seekRootDirectory(int fd)
914 off64_t seekto;
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;
924 if (Verbose)
925 (void) fprintf(stderr,
926 gettext("Seeking root directory @%lld.\n"), seekto);
927 return (lseek64(fd, seekto, SEEK_SET));
930 void
931 getRootDirectory(int fd)
933 ssize_t bytesRead;
935 if (TheRootDir.bytes != NULL)
936 return;
937 else if ((TheRootDir.bytes = (uchar_t *)malloc(RootDirSize)) == NULL) {
938 mountSanityCheckFails();
939 perror(gettext("No memory for a copy of the root directory"));
940 (void) close(fd);
941 exit(8);
944 if (seekRootDirectory(fd) < 0) {
945 mountSanityCheckFails();
946 perror(gettext("Cannot seek to RootDir"));
947 (void) close(fd);
948 exit(8);
951 if (Verbose)
952 (void) fprintf(stderr,
953 gettext("Reading root directory.\n"));
954 if ((bytesRead = read(fd, TheRootDir.bytes, RootDirSize)) !=
955 RootDirSize) {
956 mountSanityCheckFails();
957 if (bytesRead < 0) {
958 perror(gettext("Cannot read a RootDir"));
959 } else {
960 (void) fprintf(stderr,
961 gettext("Short read of RootDir\n"));
963 (void) close(fd);
964 exit(8);
966 if (Verbose) {
967 (void) fprintf(stderr,
968 gettext("Dump of root dir's first 256 bytes.\n"));
969 header_for_dump();
970 dump_bytes(TheRootDir.bytes, 256);
974 void
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"));
982 (void) close(fd);
983 exit(12);
985 if (!RootDirModified) {
986 if (Verbose) {
987 (void) fprintf(stderr,
988 gettext("No root directory changes need to "
989 "be written.\n"));
991 return;
993 if (ReadOnly)
994 return;
995 if (Verbose)
996 (void) fprintf(stderr,
997 gettext("Writing root directory.\n"));
998 if (seekRootDirectory(fd) < 0) {
999 perror(gettext("Cannot write the RootDir (seek failed)"));
1000 (void) close(fd);
1001 exit(12);
1003 if ((bytesWritten = write(fd, TheRootDir.bytes, RootDirSize)) !=
1004 RootDirSize) {
1005 if (bytesWritten < 0) {
1006 perror(gettext("Cannot write the RootDir"));
1007 } else {
1008 (void) fprintf(stderr,
1009 gettext("Short write of root directory\n"));
1011 (void) close(fd);
1012 exit(12);
1014 RootDirModified = 0;
1017 struct pcdir *
1018 newDirEnt(struct pcdir *copyme)
1020 struct pcdir *ndp;
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"));
1025 return (ndp);
1027 if (copyme)
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);
1034 return (ndp);
1037 void
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));
1045 void
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);
1050 if (IsFAT32) {
1051 p = (uchar_t *)&(dp->un.pcd_scluster_hi);
1052 store_16_bits(&p, newStart >> 16);
1054 markClusterModified(findImpactedCluster(dp));
1057 void
1058 updateDirEnt_Name(struct pcdir *dp, char *newName)
1060 int i;
1062 for (i = 0; i < PCFNAMESIZE; i++) {
1063 if (*newName)
1064 dp->pcd_filename[i] = *newName++;
1065 else
1066 dp->pcd_filename[i] = ' ';
1068 markClusterModified(findImpactedCluster(dp));