1 /* $NetBSD: dir.c,v 1.22 2008/06/13 20:46:09 martin Exp $ */
4 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
5 * Copyright (c) 1995 Martin Husemann
6 * Some structure declaration borrowed from Paul Popelka
7 * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: dir.c,v 1.22 2008/06/13 20:46:09 martin Exp $");
44 #include <sys/param.h>
49 #define SLOT_EMPTY 0x00 /* slot has never been used */
50 #define SLOT_E5 0x05 /* the real value is 0xe5 */
51 #define SLOT_DELETED 0xe5 /* file in this slot deleted */
53 #define ATTR_NORMAL 0x00 /* normal file */
54 #define ATTR_READONLY 0x01 /* file is readonly */
55 #define ATTR_HIDDEN 0x02 /* file is hidden */
56 #define ATTR_SYSTEM 0x04 /* file is a system file */
57 #define ATTR_VOLUME 0x08 /* entry is a volume label */
58 #define ATTR_DIRECTORY 0x10 /* entry is a directory name */
59 #define ATTR_ARCHIVE 0x20 /* file is new or modified */
61 #define ATTR_WIN95 0x0f /* long name record */
64 * This is the format of the contents of the deTime field in the direntry
66 * We don't use bitfields because we don't know how compilers for
67 * arbitrary machines will lay them out.
69 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
70 #define DT_2SECONDS_SHIFT 0
71 #define DT_MINUTES_MASK 0x7E0 /* minutes */
72 #define DT_MINUTES_SHIFT 5
73 #define DT_HOURS_MASK 0xF800 /* hours */
74 #define DT_HOURS_SHIFT 11
77 * This is the format of the contents of the deDate field in the direntry
80 #define DD_DAY_MASK 0x1F /* day of month */
81 #define DD_DAY_SHIFT 0
82 #define DD_MONTH_MASK 0x1E0 /* month */
83 #define DD_MONTH_SHIFT 5
84 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */
85 #define DD_YEAR_SHIFT 9
89 static struct dosDirEntry
*newDosDirEntry(void);
90 static void freeDosDirEntry(struct dosDirEntry
*);
91 static struct dirTodoNode
*newDirTodo(void);
92 static void freeDirTodo(struct dirTodoNode
*);
93 static char *fullpath(struct dosDirEntry
*);
94 static u_char
calcShortSum(u_char
*);
95 static int delete(int, struct bootblock
*, struct fatEntry
*, cl_t
, int,
97 static int removede(int, struct bootblock
*, struct fatEntry
*, u_char
*,
98 u_char
*, cl_t
, cl_t
, cl_t
, char *, int);
99 static int checksize(struct bootblock
*, struct fatEntry
*, u_char
*,
100 struct dosDirEntry
*);
101 static int readDosDirSection(int, struct bootblock
*, struct fatEntry
*,
102 struct dosDirEntry
*);
105 * Manage free dosDirEntry structures.
107 static struct dosDirEntry
*freede
;
109 static struct dosDirEntry
*
112 struct dosDirEntry
*de
;
114 if (!(de
= freede
)) {
115 if (!(de
= (struct dosDirEntry
*)malloc(sizeof *de
)))
123 freeDosDirEntry(struct dosDirEntry
*de
)
130 * The same for dirTodoNode structures.
132 static struct dirTodoNode
*freedt
;
134 static struct dirTodoNode
*
137 struct dirTodoNode
*dt
;
139 if (!(dt
= freedt
)) {
140 if (!(dt
= (struct dirTodoNode
*)malloc(sizeof *dt
)))
148 freeDirTodo(struct dirTodoNode
*dt
)
155 * The stack of unread directories
157 struct dirTodoNode
*pendingDirectories
= NULL
;
160 * Return the full pathname for a directory entry.
163 fullpath(struct dosDirEntry
*dir
)
165 static char namebuf
[MAXPATHLEN
+ 1];
169 cp
= namebuf
+ sizeof namebuf
- 1;
172 np
= dir
->lname
[0] ? dir
->lname
: dir
->name
;
174 if ((cp
-= nl
) <= namebuf
+ 1)
178 } while ((dir
= dir
->parent
) != NULL
);
187 * Calculate a checksum over an 8.3 alias name
190 calcShortSum(u_char
*p
)
195 for (i
= 0; i
< 11; i
++) {
196 sum
= (sum
<< 7)|(sum
>> 1); /* rotate right */
204 * Global variables temporarily used during a directory scan
206 static char longName
[DOSLONGNAMELEN
] = "";
207 static u_char
*buffer
= NULL
;
208 static u_char
*delbuf
= NULL
;
210 struct dosDirEntry
*rootDir
;
211 static struct dosDirEntry
*lostDir
;
214 * Init internal state for a new directory scan.
217 resetDosDirSection(struct bootblock
*boot
, struct fatEntry
*fat
)
224 b1
= boot
->RootDirEnts
* 32;
225 b2
= boot
->SecPerClust
* boot
->BytesPerSec
;
227 if ((buffer
= malloc(len
= b1
> b2
? b1
: b2
)) == NULL
) {
228 perr("No space for directory buffer (%zu)", len
);
232 if ((delbuf
= malloc(len
= b2
)) == NULL
) {
234 perr("No space for directory delbuf (%zu)", len
);
238 if ((rootDir
= newDosDirEntry()) == NULL
) {
241 perr("No space for directory entry");
245 memset(rootDir
, 0, sizeof *rootDir
);
246 if (boot
->flags
& FAT32
) {
247 if (boot
->RootCl
< CLUST_FIRST
|| boot
->RootCl
>= boot
->NumClusters
) {
248 pfatal("Root directory starts with cluster out of range(%u)",
252 cl
= fat
[boot
->RootCl
].next
;
254 || (cl
>= CLUST_RSRVD
&& cl
< CLUST_EOFS
)
255 || fat
[boot
->RootCl
].head
!= boot
->RootCl
) {
256 if (cl
== CLUST_FREE
)
257 pwarn("Root directory starts with free cluster\n");
258 else if (cl
>= CLUST_RSRVD
)
259 pwarn("Root directory starts with cluster marked %s\n",
262 pfatal("Root directory doesn't start a cluster chain");
266 fat
[boot
->RootCl
].next
= CLUST_FREE
;
272 fat
[boot
->RootCl
].flags
|= FAT_USED
;
273 rootDir
->head
= boot
->RootCl
;
280 * Cleanup after a directory scan
283 finishDosDirSection(void)
285 struct dirTodoNode
*p
, *np
;
286 struct dosDirEntry
*d
, *nd
;
288 for (p
= pendingDirectories
; p
; p
= np
) {
292 pendingDirectories
= 0;
293 for (d
= rootDir
; d
; d
= nd
) {
294 if ((nd
= d
->child
) != NULL
) {
302 rootDir
= lostDir
= NULL
;
310 * Delete directory entries between startcl, startoff and endcl, endoff.
313 delete(int f
, struct bootblock
*boot
, struct fatEntry
*fat
, cl_t startcl
,
314 int startoff
, cl_t endcl
, int endoff
, int notlast
)
318 int clsz
= boot
->SecPerClust
* boot
->BytesPerSec
;
320 s
= delbuf
+ startoff
;
322 while (startcl
>= CLUST_FIRST
&& startcl
< boot
->NumClusters
) {
323 if (startcl
== endcl
) {
328 off
= startcl
* boot
->SecPerClust
+ boot
->ClusterOffset
;
329 off
*= boot
->BytesPerSec
;
330 if (lseek(f
, off
, SEEK_SET
) != off
331 || read(f
, delbuf
, clsz
) != clsz
) {
332 perr("Unable to read directory");
339 if (lseek(f
, off
, SEEK_SET
) != off
340 || write(f
, delbuf
, clsz
) != clsz
) {
341 perr("Unable to write directory");
344 if (startcl
== endcl
)
346 startcl
= fat
[startcl
].next
;
353 removede(int f
, struct bootblock
*boot
, struct fatEntry
*fat
, u_char
*start
,
354 u_char
*end
, cl_t startcl
, cl_t endcl
, cl_t curcl
, char *path
,
359 pwarn("Invalid long filename entry for %s\n", path
);
362 pwarn("Invalid long filename entry at end of directory %s\n", path
);
365 pwarn("Invalid long filename entry for volume label\n");
368 if (ask(0, "Remove")) {
369 if (startcl
!= curcl
) {
370 if (delete(f
, boot
, fat
,
371 startcl
, start
- buffer
,
373 endcl
== curcl
) == FSFATAL
)
377 /* startcl is < CLUST_FIRST for !fat32 root */
378 if ((endcl
== curcl
) || (startcl
< CLUST_FIRST
))
379 for (; start
< end
; start
+= 32)
380 *start
= SLOT_DELETED
;
387 * Check an in-memory file entry
390 checksize(struct bootblock
*boot
, struct fatEntry
*fat
, u_char
*p
,
391 struct dosDirEntry
*dir
)
394 * Check size on ordinary files
396 u_int32_t physicalSize
;
398 if (dir
->head
== CLUST_FREE
)
401 if (dir
->head
< CLUST_FIRST
|| dir
->head
>= boot
->NumClusters
)
403 physicalSize
= fat
[dir
->head
].length
* boot
->ClusterSize
;
405 if (physicalSize
< dir
->size
) {
406 pwarn("size of %s is %u, should at most be %u\n",
407 fullpath(dir
), dir
->size
, physicalSize
);
408 if (ask(1, "Truncate")) {
409 dir
->size
= physicalSize
;
410 p
[28] = (u_char
)physicalSize
;
411 p
[29] = (u_char
)(physicalSize
>> 8);
412 p
[30] = (u_char
)(physicalSize
>> 16);
413 p
[31] = (u_char
)(physicalSize
>> 24);
417 } else if (physicalSize
- dir
->size
>= boot
->ClusterSize
) {
418 pwarn("%s has too many clusters allocated\n",
420 if (ask(1, "Drop superfluous clusters")) {
424 for (cl
= dir
->head
; (sz
+= boot
->ClusterSize
) < dir
->size
;)
426 clearchain(boot
, fat
, fat
[cl
].next
);
427 fat
[cl
].next
= CLUST_EOF
;
436 * Read a directory and
437 * - resolve long name records
438 * - enter file and directory records into the parent's list
439 * - push directories onto the todo-stack
442 readDosDirSection(int f
, struct bootblock
*boot
, struct fatEntry
*fat
,
443 struct dosDirEntry
*dir
)
445 struct dosDirEntry dirent
, *d
;
446 u_char
*p
, *vallfn
, *invlfn
, *empty
;
449 cl_t cl
, valcl
= ~0, invcl
= ~0, empcl
= ~0;
454 #define THISMOD 0x8000 /* Only used within this routine */
457 if (dir
->parent
&& (cl
< CLUST_FIRST
|| cl
>= boot
->NumClusters
)) {
459 * Already handled somewhere else.
464 vallfn
= invlfn
= empty
= NULL
;
466 if (!(boot
->flags
& FAT32
) && !dir
->parent
) {
467 last
= boot
->RootDirEnts
* 32;
468 off
= boot
->ResSectors
+ boot
->FATs
* boot
->FATsecs
;
470 last
= boot
->SecPerClust
* boot
->BytesPerSec
;
471 off
= cl
* boot
->SecPerClust
+ boot
->ClusterOffset
;
474 off
*= boot
->BytesPerSec
;
475 if (lseek(f
, off
, SEEK_SET
) != off
476 || read(f
, buffer
, last
) != last
) {
477 perr("Unable to read directory");
482 * Check `.' and `..' entries here? XXX
484 for (p
= buffer
, i
= 0; i
< last
; i
++, p
+= 32) {
485 if (dir
->fsckflags
& DIREMPWARN
) {
490 if (*p
== SLOT_EMPTY
|| *p
== SLOT_DELETED
) {
491 if (*p
== SLOT_EMPTY
) {
492 dir
->fsckflags
|= DIREMPTY
;
499 if (dir
->fsckflags
& DIREMPTY
) {
500 if (!(dir
->fsckflags
& DIREMPWARN
)) {
501 pwarn("%s has entries after end of directory\n",
503 if (ask(1, "Extend")) {
506 dir
->fsckflags
&= ~DIREMPTY
;
507 if (delete(f
, boot
, fat
,
508 empcl
, empty
- buffer
,
509 cl
, p
- buffer
, 1) == FSFATAL
)
511 q
= empcl
== cl
? empty
: buffer
;
513 for (; q
< p
; q
+= 32)
515 mod
|= THISMOD
|FSDIRMOD
;
516 } else if (ask(0, "Truncate"))
517 dir
->fsckflags
|= DIREMPWARN
;
519 if (dir
->fsckflags
& DIREMPWARN
) {
521 mod
|= THISMOD
|FSDIRMOD
;
523 } else if (dir
->fsckflags
& DIREMPTY
)
528 if (p
[11] == ATTR_WIN95
) {
530 if (shortSum
!= -1) {
536 memset(longName
, 0, sizeof longName
);
540 } else if (shortSum
!= p
[13]
541 || lidx
!= (*p
& LRNOMASK
)) {
552 lidx
= *p
& LRNOMASK
;
553 t
= longName
+ --lidx
* 13;
554 for (k
= 1; k
< 11 && t
< longName
+ sizeof(longName
); k
+= 2) {
555 if (!p
[k
] && !p
[k
+ 1])
559 * Warn about those unusable chars in msdosfs here? XXX
565 for (k
= 14; k
< 26 && t
< longName
+ sizeof(longName
); k
+= 2) {
566 if (!p
[k
] && !p
[k
+ 1])
573 for (k
= 28; k
< 32 && t
< longName
+ sizeof(longName
); k
+= 2) {
574 if (!p
[k
] && !p
[k
+ 1])
580 if (t
>= longName
+ sizeof(longName
)) {
581 pwarn("long filename too long\n");
588 if (p
[26] | (p
[27] << 8)) {
589 pwarn("long filename record cluster start != 0\n");
596 continue; /* long records don't carry further
601 * This is a standard msdosfs directory entry.
603 memset(&dirent
, 0, sizeof dirent
);
606 * it's a short name record, but we need to know
607 * more, so get the flags first.
609 dirent
.flags
= p
[11];
612 * Translate from 850 to ISO here XXX
614 for (j
= 0; j
< 8; j
++)
615 dirent
.name
[j
] = p
[j
];
616 dirent
.name
[8] = '\0';
617 for (k
= 7; k
>= 0 && dirent
.name
[k
] == ' '; k
--)
618 dirent
.name
[k
] = '\0';
619 if (dirent
.name
[k
] != '\0')
621 if (dirent
.name
[0] == SLOT_E5
)
622 dirent
.name
[0] = 0xe5;
624 if (dirent
.flags
& ATTR_VOLUME
) {
625 if (vallfn
|| invlfn
) {
626 mod
|= removede(f
, boot
, fat
,
627 invlfn
? invlfn
: vallfn
, p
,
628 invlfn
? invcl
: valcl
, -1, 0,
637 dirent
.name
[k
++] = '.';
638 for (j
= 0; j
< 3; j
++)
639 dirent
.name
[k
++] = p
[j
+8];
640 dirent
.name
[k
] = '\0';
641 for (k
--; k
>= 0 && dirent
.name
[k
] == ' '; k
--)
642 dirent
.name
[k
] = '\0';
644 if (vallfn
&& shortSum
!= calcShortSum(p
)) {
651 dirent
.head
= p
[26] | (p
[27] << 8);
652 if (boot
->ClustMask
== CLUST32_MASK
)
653 dirent
.head
|= (p
[20] << 16) | (p
[21] << 24);
654 dirent
.size
= p
[28] | (p
[29] << 8) | (p
[30] << 16) | (p
[31] << 24);
656 strlcpy(dirent
.lname
, longName
,
657 sizeof(dirent
.lname
));
663 dirent
.next
= dir
->child
;
666 mod
|= k
= removede(f
, boot
, fat
,
667 invlfn
, vallfn
? vallfn
: p
,
668 invcl
, vallfn
? valcl
: cl
, cl
,
669 fullpath(&dirent
), 0);
673 ? (valcl
== cl
&& vallfn
!= buffer
)
679 vallfn
= NULL
; /* not used any longer */
682 if (dirent
.size
== 0 && !(dirent
.flags
& ATTR_DIRECTORY
)) {
683 if (dirent
.head
!= 0) {
684 pwarn("%s has clusters, but size 0\n",
686 if (ask(1, "Drop allocated clusters")) {
688 if (boot
->ClustMask
== CLUST32_MASK
)
690 clearchain(boot
, fat
, dirent
.head
);
692 mod
|= THISMOD
|FSDIRMOD
|FSFATMOD
;
696 } else if (dirent
.head
== 0
697 && !strcmp(dirent
.name
, "..")
698 && dir
->parent
/* XXX */
699 && !dir
->parent
->parent
) {
701 * Do nothing, the parent is the root
703 } else if (dirent
.head
< CLUST_FIRST
704 || dirent
.head
>= boot
->NumClusters
705 || fat
[dirent
.head
].next
== CLUST_FREE
706 || (fat
[dirent
.head
].next
>= CLUST_RSRVD
707 && fat
[dirent
.head
].next
< CLUST_EOFS
)
708 || fat
[dirent
.head
].head
!= dirent
.head
) {
709 if (dirent
.head
== 0)
710 pwarn("%s has no clusters\n",
712 else if (dirent
.head
< CLUST_FIRST
713 || dirent
.head
>= boot
->NumClusters
)
714 pwarn("%s starts with cluster out of range(%u)\n",
717 else if (fat
[dirent
.head
].next
== CLUST_FREE
)
718 pwarn("%s starts with free cluster\n",
720 else if (fat
[dirent
.head
].next
>= CLUST_RSRVD
)
721 pwarn("%s starts with cluster marked %s\n",
723 rsrvdcltype(fat
[dirent
.head
].next
));
725 pwarn("%s doesn't start a cluster chain\n",
727 if (dirent
.flags
& ATTR_DIRECTORY
) {
728 if (ask(0, "Remove")) {
730 mod
|= THISMOD
|FSDIRMOD
;
735 if (ask(1, "Truncate")) {
736 p
[28] = p
[29] = p
[30] = p
[31] = 0;
738 if (boot
->ClustMask
== CLUST32_MASK
)
741 mod
|= THISMOD
|FSDIRMOD
;
747 if (dirent
.head
>= CLUST_FIRST
&& dirent
.head
< boot
->NumClusters
)
748 fat
[dirent
.head
].flags
|= FAT_USED
;
750 if (dirent
.flags
& ATTR_DIRECTORY
) {
752 * gather more info for directories
754 struct dirTodoNode
*n
;
757 pwarn("Directory %s has size != 0\n",
759 if (ask(1, "Correct")) {
760 p
[28] = p
[29] = p
[30] = p
[31] = 0;
762 mod
|= THISMOD
|FSDIRMOD
;
767 * handle `.' and `..' specially
769 if (strcmp(dirent
.name
, ".") == 0) {
770 if (dirent
.head
!= dir
->head
) {
771 pwarn("`.' entry in %s has incorrect start cluster\n",
773 if (ask(1, "Correct")) {
774 dirent
.head
= dir
->head
;
775 p
[26] = (u_char
)dirent
.head
;
776 p
[27] = (u_char
)(dirent
.head
>> 8);
777 if (boot
->ClustMask
== CLUST32_MASK
) {
778 p
[20] = (u_char
)(dirent
.head
>> 16);
779 p
[21] = (u_char
)(dirent
.head
>> 24);
781 mod
|= THISMOD
|FSDIRMOD
;
787 if (strcmp(dirent
.name
, "..") == 0) {
788 if (dir
->parent
) { /* XXX */
789 if (!dir
->parent
->parent
) {
791 pwarn("`..' entry in %s has non-zero start cluster\n",
793 if (ask(1, "Correct")) {
796 if (boot
->ClustMask
== CLUST32_MASK
)
798 mod
|= THISMOD
|FSDIRMOD
;
802 } else if (dirent
.head
!= dir
->parent
->head
) {
803 pwarn("`..' entry in %s has incorrect start cluster\n",
805 if (ask(1, "Correct")) {
806 dirent
.head
= dir
->parent
->head
;
807 p
[26] = (u_char
)dirent
.head
;
808 p
[27] = (u_char
)(dirent
.head
>> 8);
809 if (boot
->ClustMask
== CLUST32_MASK
) {
810 p
[20] = (u_char
)(dirent
.head
>> 16);
811 p
[21] = (u_char
)(dirent
.head
>> 24);
813 mod
|= THISMOD
|FSDIRMOD
;
821 /* create directory tree node */
822 if (!(d
= newDosDirEntry())) {
823 perr("No space for directory");
826 memcpy(d
, &dirent
, sizeof(struct dosDirEntry
));
827 /* link it into the tree */
830 /* Enter this directory into the todo list */
831 if (!(n
= newDirTodo())) {
832 perr("No space for todo list");
835 n
->next
= pendingDirectories
;
837 pendingDirectories
= n
;
839 mod
|= k
= checksize(boot
, fat
, p
, &dirent
);
846 if (!(boot
->flags
& FAT32
) && !dir
->parent
)
851 if (lseek(f
, off
, SEEK_SET
) != off
852 || write(f
, buffer
, last
) != last
) {
853 perr("Unable to write directory");
858 } while ((cl
= fat
[cl
].next
) >= CLUST_FIRST
&& cl
< boot
->NumClusters
);
859 if (invlfn
|| vallfn
)
860 mod
|= removede(f
, boot
, fat
,
861 invlfn
? invlfn
: vallfn
, p
,
862 invlfn
? invcl
: valcl
, -1, 0,
865 /* The root directory of non fat32 filesystems is in a special
866 * area and may have been modified above without being written out.
868 if ((mod
& FSDIRMOD
) && !(boot
->flags
& FAT32
) && !dir
->parent
) {
870 if (lseek(f
, off
, SEEK_SET
) != off
871 || write(f
, buffer
, last
) != last
) {
872 perr("Unable to write directory");
877 return mod
& ~THISMOD
;
881 handleDirTree(int dosfs
, struct bootblock
*boot
, struct fatEntry
*fat
)
885 mod
= readDosDirSection(dosfs
, boot
, fat
, rootDir
);
890 * process the directory todo list
892 while (pendingDirectories
) {
893 struct dosDirEntry
*dir
= pendingDirectories
->dir
;
894 struct dirTodoNode
*n
= pendingDirectories
->next
;
897 * remove TODO entry now, the list might change during
900 freeDirTodo(pendingDirectories
);
901 pendingDirectories
= n
;
904 * handle subdirectory
906 mod
|= readDosDirSection(dosfs
, boot
, fat
, dir
);
915 * Try to reconnect a FAT chain into dir
917 static u_char
*lfbuf
;
922 reconnect(int dosfs
, struct bootblock
*boot
, struct fatEntry
*fat
, cl_t head
)
924 struct dosDirEntry d
;
927 if (!ask(1, "Reconnect"))
931 for (lostDir
= rootDir
->child
; lostDir
; lostDir
= lostDir
->next
) {
932 if (!strcmp(lostDir
->name
, LOSTDIR
))
935 if (!lostDir
) { /* Create LOSTDIR? XXX */
936 pwarn("No %s directory\n", LOSTDIR
);
941 lfbuf
= malloc(boot
->ClusterSize
);
943 perr("No space for buffer");
951 for (; p
< lfbuf
+ boot
->ClusterSize
; p
+= 32)
953 || *p
== SLOT_DELETED
)
955 if (p
&& p
< lfbuf
+ boot
->ClusterSize
)
957 lfcl
= p
? fat
[lfcl
].next
: lostDir
->head
;
958 if (lfcl
< CLUST_FIRST
|| lfcl
>= boot
->NumClusters
) {
959 /* Extend LOSTDIR? XXX */
960 pwarn("No space in %s\n", LOSTDIR
);
963 lfoff
= lfcl
* boot
->ClusterSize
964 + boot
->ClusterOffset
* boot
->BytesPerSec
;
965 if (lseek(dosfs
, lfoff
, SEEK_SET
) != lfoff
966 || (size_t)read(dosfs
, lfbuf
, boot
->ClusterSize
) != boot
->ClusterSize
) {
967 perr("could not read LOST.DIR");
974 /* Ensure uniqueness of entry here! XXX */
975 memset(&d
, 0, sizeof d
);
976 (void)snprintf(d
.name
, sizeof(d
.name
), "%u", head
);
979 d
.size
= fat
[head
].length
* boot
->ClusterSize
;
983 memcpy(p
, d
.name
, strlen(d
.name
));
984 p
[26] = (u_char
)d
.head
;
985 p
[27] = (u_char
)(d
.head
>> 8);
986 if (boot
->ClustMask
== CLUST32_MASK
) {
987 p
[20] = (u_char
)(d
.head
>> 16);
988 p
[21] = (u_char
)(d
.head
>> 24);
990 p
[28] = (u_char
)d
.size
;
991 p
[29] = (u_char
)(d
.size
>> 8);
992 p
[30] = (u_char
)(d
.size
>> 16);
993 p
[31] = (u_char
)(d
.size
>> 24);
994 fat
[head
].flags
|= FAT_USED
;
995 if (lseek(dosfs
, lfoff
, SEEK_SET
) != lfoff
996 || (size_t)write(dosfs
, lfbuf
, boot
->ClusterSize
) != boot
->ClusterSize
) {
997 perr("could not write LOST.DIR");