2 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
3 * Copyright (c) 1995 Martin Husemann
4 * Some structure declaration borrowed from Paul Popelka
5 * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Martin Husemann
18 * and Wolfgang Solfrank.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * $NetBSD: dir.c,v 1.14 1998/08/25 19:18:15 ross Exp $
35 * $FreeBSD: src/sbin/fsck_msdosfs/dir.c,v 1.1.2.1 2001/08/01 05:47:55 obrien Exp $
36 * $DragonFly: src/sbin/fsck_msdosfs/dir.c,v 1.6 2004/12/18 21:43:38 swildner Exp $
40 #include <sys/cdefs.h>
50 #include <sys/param.h>
55 #define SLOT_EMPTY 0x00 /* slot has never been used */
56 #define SLOT_E5 0x05 /* the real value is 0xe5 */
57 #define SLOT_DELETED 0xe5 /* file in this slot deleted */
59 #define ATTR_NORMAL 0x00 /* normal file */
60 #define ATTR_READONLY 0x01 /* file is readonly */
61 #define ATTR_HIDDEN 0x02 /* file is hidden */
62 #define ATTR_SYSTEM 0x04 /* file is a system file */
63 #define ATTR_VOLUME 0x08 /* entry is a volume label */
64 #define ATTR_DIRECTORY 0x10 /* entry is a directory name */
65 #define ATTR_ARCHIVE 0x20 /* file is new or modified */
67 #define ATTR_WIN95 0x0f /* long name record */
70 * This is the format of the contents of the deTime field in the direntry
72 * We don't use bitfields because we don't know how compilers for
73 * arbitrary machines will lay them out.
75 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
76 #define DT_2SECONDS_SHIFT 0
77 #define DT_MINUTES_MASK 0x7E0 /* minutes */
78 #define DT_MINUTES_SHIFT 5
79 #define DT_HOURS_MASK 0xF800 /* hours */
80 #define DT_HOURS_SHIFT 11
83 * This is the format of the contents of the deDate field in the direntry
86 #define DD_DAY_MASK 0x1F /* day of month */
87 #define DD_DAY_SHIFT 0
88 #define DD_MONTH_MASK 0x1E0 /* month */
89 #define DD_MONTH_SHIFT 5
90 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */
91 #define DD_YEAR_SHIFT 9
95 static struct dosDirEntry
*newDosDirEntry(void);
96 static void freeDosDirEntry(struct dosDirEntry
*);
97 static struct dirTodoNode
*newDirTodo(void);
98 static void freeDirTodo(struct dirTodoNode
*);
99 static char *fullpath(struct dosDirEntry
*);
100 static u_char
calcShortSum(u_char
*);
101 static int delete(int, struct bootblock
*, struct fatEntry
*, cl_t
, int,
103 static int removede(int, struct bootblock
*, struct fatEntry
*, u_char
*,
104 u_char
*, cl_t
, cl_t
, cl_t
, char *, int);
105 static int checksize(struct bootblock
*, struct fatEntry
*, u_char
*,
106 struct dosDirEntry
*);
107 static int readDosDirSection(int, struct bootblock
*, struct fatEntry
*,
108 struct dosDirEntry
*);
111 * Manage free dosDirEntry structures.
113 static struct dosDirEntry
*freede
;
115 static struct dosDirEntry
*
118 struct dosDirEntry
*de
;
120 if (!(de
= freede
)) {
121 if (!(de
= (struct dosDirEntry
*)malloc(sizeof *de
)))
129 freeDosDirEntry(struct dosDirEntry
*de
)
136 * The same for dirTodoNode structures.
138 static struct dirTodoNode
*freedt
;
140 static struct dirTodoNode
*
143 struct dirTodoNode
*dt
;
145 if (!(dt
= freedt
)) {
146 if (!(dt
= (struct dirTodoNode
*)malloc(sizeof *dt
)))
154 freeDirTodo(struct dirTodoNode
*dt
)
161 * The stack of unread directories
163 struct dirTodoNode
*pendingDirectories
= NULL
;
166 * Return the full pathname for a directory entry.
169 fullpath(struct dosDirEntry
*dir
)
171 static char namebuf
[MAXPATHLEN
+ 1];
175 cp
= namebuf
+ sizeof namebuf
- 1;
178 np
= dir
->lname
[0] ? dir
->lname
: dir
->name
;
180 if ((cp
-= nl
) <= namebuf
+ 1)
184 } while ((dir
= dir
->parent
) != NULL
);
193 * Calculate a checksum over an 8.3 alias name
196 calcShortSum(u_char
*p
)
201 for (i
= 0; i
< 11; i
++) {
202 sum
= (sum
<< 7)|(sum
>> 1); /* rotate right */
210 * Global variables temporarily used during a directory scan
212 static char longName
[DOSLONGNAMELEN
] = "";
213 static u_char
*buffer
= NULL
;
214 static u_char
*delbuf
= NULL
;
216 struct dosDirEntry
*rootDir
;
217 static struct dosDirEntry
*lostDir
;
220 * Init internal state for a new directory scan.
223 resetDosDirSection(struct bootblock
*boot
, struct fatEntry
*fat
)
229 b1
= boot
->RootDirEnts
* 32;
230 b2
= boot
->SecPerClust
* boot
->BytesPerSec
;
232 if (!(buffer
= malloc(b1
> b2
? b1
: b2
))
233 || !(delbuf
= malloc(b2
))
234 || !(rootDir
= newDosDirEntry())) {
235 perror("No space for directory");
238 memset(rootDir
, 0, sizeof *rootDir
);
239 if (boot
->flags
& FAT32
) {
240 if (boot
->RootCl
< CLUST_FIRST
|| boot
->RootCl
>= boot
->NumClusters
) {
241 pfatal("Root directory starts with cluster out of range(%u)",
245 cl
= fat
[boot
->RootCl
].next
;
247 || (cl
>= CLUST_RSRVD
&& cl
< CLUST_EOFS
)
248 || fat
[boot
->RootCl
].head
!= boot
->RootCl
) {
249 if (cl
== CLUST_FREE
)
250 pwarn("Root directory starts with free cluster\n");
251 else if (cl
>= CLUST_RSRVD
)
252 pwarn("Root directory starts with cluster marked %s\n",
255 pfatal("Root directory doesn't start a cluster chain");
259 fat
[boot
->RootCl
].next
= CLUST_FREE
;
265 fat
[boot
->RootCl
].flags
|= FAT_USED
;
266 rootDir
->head
= boot
->RootCl
;
273 * Cleanup after a directory scan
276 finishDosDirSection(void)
278 struct dirTodoNode
*p
, *np
;
279 struct dosDirEntry
*d
, *nd
;
281 for (p
= pendingDirectories
; p
; p
= np
) {
285 pendingDirectories
= 0;
286 for (d
= rootDir
; d
; d
= nd
) {
287 if ((nd
= d
->child
) != NULL
) {
295 rootDir
= lostDir
= NULL
;
303 * Delete directory entries between startcl, startoff and endcl, endoff.
306 delete(int f
, struct bootblock
*boot
, struct fatEntry
*fat
, cl_t startcl
,
307 int startoff
, cl_t endcl
, int endoff
, int notlast
)
311 int clsz
= boot
->SecPerClust
* boot
->BytesPerSec
;
313 s
= delbuf
+ startoff
;
315 while (startcl
>= CLUST_FIRST
&& startcl
< boot
->NumClusters
) {
316 if (startcl
== endcl
) {
321 off
= startcl
* boot
->SecPerClust
+ boot
->ClusterOffset
;
322 off
*= boot
->BytesPerSec
;
323 if (lseek(f
, off
, SEEK_SET
) != off
324 || read(f
, delbuf
, clsz
) != clsz
) {
325 perror("Unable to read directory");
332 if (lseek(f
, off
, SEEK_SET
) != off
333 || write(f
, delbuf
, clsz
) != clsz
) {
334 perror("Unable to write directory");
337 if (startcl
== endcl
)
339 startcl
= fat
[startcl
].next
;
346 removede(int f
, struct bootblock
*boot
, struct fatEntry
*fat
, u_char
*start
,
347 u_char
*end
, cl_t startcl
, cl_t endcl
, cl_t curcl
, char *path
,
352 pwarn("Invalid long filename entry for %s\n", path
);
355 pwarn("Invalid long filename entry at end of directory %s\n", path
);
358 pwarn("Invalid long filename entry for volume label\n");
361 if (ask(0, "Remove")) {
362 if (startcl
!= curcl
) {
363 if (delete(f
, boot
, fat
,
364 startcl
, start
- buffer
,
366 endcl
== curcl
) == FSFATAL
)
371 for (; start
< end
; start
+= 32)
372 *start
= SLOT_DELETED
;
379 * Check an in-memory file entry
382 checksize(struct bootblock
*boot
, struct fatEntry
*fat
, u_char
*p
,
383 struct dosDirEntry
*dir
)
386 * Check size on ordinary files
388 int32_t physicalSize
;
390 if (dir
->head
== CLUST_FREE
)
393 if (dir
->head
< CLUST_FIRST
|| dir
->head
>= boot
->NumClusters
)
395 physicalSize
= fat
[dir
->head
].length
* boot
->ClusterSize
;
397 if (physicalSize
< dir
->size
) {
398 pwarn("size of %s is %u, should at most be %u\n",
399 fullpath(dir
), dir
->size
, physicalSize
);
400 if (ask(1, "Truncate")) {
401 dir
->size
= physicalSize
;
402 p
[28] = (u_char
)physicalSize
;
403 p
[29] = (u_char
)(physicalSize
>> 8);
404 p
[30] = (u_char
)(physicalSize
>> 16);
405 p
[31] = (u_char
)(physicalSize
>> 24);
409 } else if (physicalSize
- dir
->size
>= boot
->ClusterSize
) {
410 pwarn("%s has too many clusters allocated\n",
412 if (ask(1, "Drop superfluous clusters")) {
416 for (cl
= dir
->head
; (sz
+= boot
->ClusterSize
) < dir
->size
;)
418 clearchain(boot
, fat
, fat
[cl
].next
);
419 fat
[cl
].next
= CLUST_EOF
;
428 * Read a directory and
429 * - resolve long name records
430 * - enter file and directory records into the parent's list
431 * - push directories onto the todo-stack
434 readDosDirSection(int f
, struct bootblock
*boot
, struct fatEntry
*fat
,
435 struct dosDirEntry
*dir
)
437 struct dosDirEntry dirent
, *d
;
438 u_char
*p
, *vallfn
, *invlfn
, *empty
;
441 cl_t cl
, valcl
= ~0, invcl
= ~0, empcl
= ~0;
446 #define THISMOD 0x8000 /* Only used within this routine */
449 if (dir
->parent
&& (cl
< CLUST_FIRST
|| cl
>= boot
->NumClusters
)) {
451 * Already handled somewhere else.
456 vallfn
= invlfn
= empty
= NULL
;
458 if (!(boot
->flags
& FAT32
) && !dir
->parent
) {
459 last
= boot
->RootDirEnts
* 32;
460 off
= boot
->ResSectors
+ boot
->FATs
* boot
->FATsecs
;
462 last
= boot
->SecPerClust
* boot
->BytesPerSec
;
463 off
= cl
* boot
->SecPerClust
+ boot
->ClusterOffset
;
466 off
*= boot
->BytesPerSec
;
467 if (lseek(f
, off
, SEEK_SET
) != off
468 || read(f
, buffer
, last
) != last
) {
469 perror("Unable to read directory");
474 * Check `.' and `..' entries here? XXX
476 for (p
= buffer
, i
= 0; i
< last
; i
++, p
+= 32) {
477 if (dir
->fsckflags
& DIREMPWARN
) {
482 if (*p
== SLOT_EMPTY
|| *p
== SLOT_DELETED
) {
483 if (*p
== SLOT_EMPTY
) {
484 dir
->fsckflags
|= DIREMPTY
;
491 if (dir
->fsckflags
& DIREMPTY
) {
492 if (!(dir
->fsckflags
& DIREMPWARN
)) {
493 pwarn("%s has entries after end of directory\n",
495 if (ask(1, "Extend")) {
498 dir
->fsckflags
&= ~DIREMPTY
;
499 if (delete(f
, boot
, fat
,
500 empcl
, empty
- buffer
,
501 cl
, p
- buffer
, 1) == FSFATAL
)
503 q
= empcl
== cl
? empty
: buffer
;
504 for (; q
< p
; q
+= 32)
506 mod
|= THISMOD
|FSDIRMOD
;
507 } else if (ask(0, "Truncate"))
508 dir
->fsckflags
|= DIREMPWARN
;
510 if (dir
->fsckflags
& DIREMPWARN
) {
512 mod
|= THISMOD
|FSDIRMOD
;
514 } else if (dir
->fsckflags
& DIREMPTY
)
519 if (p
[11] == ATTR_WIN95
) {
521 if (shortSum
!= -1) {
527 memset(longName
, 0, sizeof longName
);
531 } else if (shortSum
!= p
[13]
532 || lidx
!= (*p
& LRNOMASK
)) {
543 lidx
= *p
& LRNOMASK
;
544 t
= longName
+ --lidx
* 13;
545 for (k
= 1; k
< 11 && t
< longName
+ sizeof(longName
); k
+= 2) {
546 if (!p
[k
] && !p
[k
+ 1])
550 * Warn about those unusable chars in msdosfs here? XXX
556 for (k
= 14; k
< 26 && t
< longName
+ sizeof(longName
); k
+= 2) {
557 if (!p
[k
] && !p
[k
+ 1])
564 for (k
= 28; k
< 32 && t
< longName
+ sizeof(longName
); k
+= 2) {
565 if (!p
[k
] && !p
[k
+ 1])
571 if (t
>= longName
+ sizeof(longName
)) {
572 pwarn("long filename too long\n");
579 if (p
[26] | (p
[27] << 8)) {
580 pwarn("long filename record cluster start != 0\n");
587 continue; /* long records don't carry further
592 * This is a standard msdosfs directory entry.
594 memset(&dirent
, 0, sizeof dirent
);
597 * it's a short name record, but we need to know
598 * more, so get the flags first.
600 dirent
.flags
= p
[11];
603 * Translate from 850 to ISO here XXX
605 for (j
= 0; j
< 8; j
++)
606 dirent
.name
[j
] = p
[j
];
607 dirent
.name
[8] = '\0';
608 for (k
= 7; k
>= 0 && dirent
.name
[k
] == ' '; k
--)
609 dirent
.name
[k
] = '\0';
610 if (dirent
.name
[k
] != '\0')
612 if (dirent
.name
[0] == SLOT_E5
)
613 dirent
.name
[0] = 0xe5;
615 if (dirent
.flags
& ATTR_VOLUME
) {
616 if (vallfn
|| invlfn
) {
617 mod
|= removede(f
, boot
, fat
,
618 invlfn
? invlfn
: vallfn
, p
,
619 invlfn
? invcl
: valcl
, -1, 0,
628 dirent
.name
[k
++] = '.';
629 for (j
= 0; j
< 3; j
++)
630 dirent
.name
[k
++] = p
[j
+8];
631 dirent
.name
[k
] = '\0';
632 for (k
--; k
>= 0 && dirent
.name
[k
] == ' '; k
--)
633 dirent
.name
[k
] = '\0';
635 if (vallfn
&& shortSum
!= calcShortSum(p
)) {
642 dirent
.head
= p
[26] | (p
[27] << 8);
643 if (boot
->ClustMask
== CLUST32_MASK
)
644 dirent
.head
|= (p
[20] << 16) | (p
[21] << 24);
645 dirent
.size
= p
[28] | (p
[29] << 8) | (p
[30] << 16) | (p
[31] << 24);
647 strcpy(dirent
.lname
, longName
);
653 dirent
.next
= dir
->child
;
656 mod
|= k
= removede(f
, boot
, fat
,
657 invlfn
, vallfn
? vallfn
: p
,
658 invcl
, vallfn
? valcl
: cl
, cl
,
659 fullpath(&dirent
), 0);
663 ? (valcl
== cl
&& vallfn
!= buffer
)
669 vallfn
= NULL
; /* not used any longer */
672 if (dirent
.size
== 0 && !(dirent
.flags
& ATTR_DIRECTORY
)) {
673 if (dirent
.head
!= 0) {
674 pwarn("%s has clusters, but size 0\n",
676 if (ask(1, "Drop allocated clusters")) {
678 if (boot
->ClustMask
== CLUST32_MASK
)
680 clearchain(boot
, fat
, dirent
.head
);
682 mod
|= THISMOD
|FSDIRMOD
|FSFATMOD
;
686 } else if (dirent
.head
== 0
687 && !strcmp(dirent
.name
, "..")
688 && dir
->parent
/* XXX */
689 && !dir
->parent
->parent
) {
691 * Do nothing, the parent is the root
693 } else if (dirent
.head
< CLUST_FIRST
694 || dirent
.head
>= boot
->NumClusters
695 || fat
[dirent
.head
].next
== CLUST_FREE
696 || (fat
[dirent
.head
].next
>= CLUST_RSRVD
697 && fat
[dirent
.head
].next
< CLUST_EOFS
)
698 || fat
[dirent
.head
].head
!= dirent
.head
) {
699 if (dirent
.head
== 0)
700 pwarn("%s has no clusters\n",
702 else if (dirent
.head
< CLUST_FIRST
703 || dirent
.head
>= boot
->NumClusters
)
704 pwarn("%s starts with cluster out of range(%u)\n",
707 else if (fat
[dirent
.head
].next
== CLUST_FREE
)
708 pwarn("%s starts with free cluster\n",
710 else if (fat
[dirent
.head
].next
>= CLUST_RSRVD
)
711 pwarn("%s starts with cluster marked %s\n",
713 rsrvdcltype(fat
[dirent
.head
].next
));
715 pwarn("%s doesn't start a cluster chain\n",
717 if (dirent
.flags
& ATTR_DIRECTORY
) {
718 if (ask(0, "Remove")) {
720 mod
|= THISMOD
|FSDIRMOD
;
725 if (ask(1, "Truncate")) {
726 p
[28] = p
[29] = p
[30] = p
[31] = 0;
728 if (boot
->ClustMask
== CLUST32_MASK
)
731 mod
|= THISMOD
|FSDIRMOD
;
737 if (dirent
.head
>= CLUST_FIRST
&& dirent
.head
< boot
->NumClusters
)
738 fat
[dirent
.head
].flags
|= FAT_USED
;
740 if (dirent
.flags
& ATTR_DIRECTORY
) {
742 * gather more info for directories
744 struct dirTodoNode
*n
;
747 pwarn("Directory %s has size != 0\n",
749 if (ask(1, "Correct")) {
750 p
[28] = p
[29] = p
[30] = p
[31] = 0;
752 mod
|= THISMOD
|FSDIRMOD
;
757 * handle `.' and `..' specially
759 if (strcmp(dirent
.name
, ".") == 0) {
760 if (dirent
.head
!= dir
->head
) {
761 pwarn("`.' entry in %s has incorrect start cluster\n",
763 if (ask(1, "Correct")) {
764 dirent
.head
= dir
->head
;
765 p
[26] = (u_char
)dirent
.head
;
766 p
[27] = (u_char
)(dirent
.head
>> 8);
767 if (boot
->ClustMask
== CLUST32_MASK
) {
768 p
[20] = (u_char
)(dirent
.head
>> 16);
769 p
[21] = (u_char
)(dirent
.head
>> 24);
771 mod
|= THISMOD
|FSDIRMOD
;
777 if (strcmp(dirent
.name
, "..") == 0) {
778 if (dir
->parent
) { /* XXX */
779 if (!dir
->parent
->parent
) {
781 pwarn("`..' entry in %s has non-zero start cluster\n",
783 if (ask(1, "Correct")) {
786 if (boot
->ClustMask
== CLUST32_MASK
)
788 mod
|= THISMOD
|FSDIRMOD
;
792 } else if (dirent
.head
!= dir
->parent
->head
) {
793 pwarn("`..' entry in %s has incorrect start cluster\n",
795 if (ask(1, "Correct")) {
796 dirent
.head
= dir
->parent
->head
;
797 p
[26] = (u_char
)dirent
.head
;
798 p
[27] = (u_char
)(dirent
.head
>> 8);
799 if (boot
->ClustMask
== CLUST32_MASK
) {
800 p
[20] = (u_char
)(dirent
.head
>> 16);
801 p
[21] = (u_char
)(dirent
.head
>> 24);
803 mod
|= THISMOD
|FSDIRMOD
;
811 /* create directory tree node */
812 if (!(d
= newDosDirEntry())) {
813 perror("No space for directory");
816 memcpy(d
, &dirent
, sizeof(struct dosDirEntry
));
817 /* link it into the tree */
820 /* Enter this directory into the todo list */
821 if (!(n
= newDirTodo())) {
822 perror("No space for todo list");
825 n
->next
= pendingDirectories
;
827 pendingDirectories
= n
;
829 mod
|= k
= checksize(boot
, fat
, p
, &dirent
);
837 if (lseek(f
, off
, SEEK_SET
) != off
838 || write(f
, buffer
, last
) != last
) {
839 perror("Unable to write directory");
844 } while ((cl
= fat
[cl
].next
) >= CLUST_FIRST
&& cl
< boot
->NumClusters
);
845 if (invlfn
|| vallfn
)
846 mod
|= removede(f
, boot
, fat
,
847 invlfn
? invlfn
: vallfn
, p
,
848 invlfn
? invcl
: valcl
, -1, 0,
850 return mod
& ~THISMOD
;
854 handleDirTree(int dosfs
, struct bootblock
*boot
, struct fatEntry
*fat
)
858 mod
= readDosDirSection(dosfs
, boot
, fat
, rootDir
);
863 * process the directory todo list
865 while (pendingDirectories
) {
866 struct dosDirEntry
*dir
= pendingDirectories
->dir
;
867 struct dirTodoNode
*n
= pendingDirectories
->next
;
870 * remove TODO entry now, the list might change during
873 freeDirTodo(pendingDirectories
);
874 pendingDirectories
= n
;
877 * handle subdirectory
879 mod
|= readDosDirSection(dosfs
, boot
, fat
, dir
);
888 * Try to reconnect a FAT chain into dir
890 static u_char
*lfbuf
;
895 reconnect(int dosfs
, struct bootblock
*boot
, struct fatEntry
*fat
, cl_t head
)
897 struct dosDirEntry d
;
900 if (!ask(1, "Reconnect"))
904 for (lostDir
= rootDir
->child
; lostDir
; lostDir
= lostDir
->next
) {
905 if (!strcmp(lostDir
->name
, LOSTDIR
))
908 if (!lostDir
) { /* Create LOSTDIR? XXX */
909 pwarn("No %s directory\n", LOSTDIR
);
914 lfbuf
= malloc(boot
->ClusterSize
);
916 perror("No space for buffer");
924 for (; p
< lfbuf
+ boot
->ClusterSize
; p
+= 32)
926 || *p
== SLOT_DELETED
)
928 if (p
&& p
< lfbuf
+ boot
->ClusterSize
)
930 lfcl
= p
? fat
[lfcl
].next
: lostDir
->head
;
931 if (lfcl
< CLUST_FIRST
|| lfcl
>= boot
->NumClusters
) {
932 /* Extend LOSTDIR? XXX */
933 pwarn("No space in %s\n", LOSTDIR
);
936 lfoff
= lfcl
* boot
->ClusterSize
937 + boot
->ClusterOffset
* boot
->BytesPerSec
;
938 if (lseek(dosfs
, lfoff
, SEEK_SET
) != lfoff
939 || read(dosfs
, lfbuf
, boot
->ClusterSize
) != boot
->ClusterSize
) {
940 perror("could not read LOST.DIR");
947 /* Ensure uniqueness of entry here! XXX */
948 memset(&d
, 0, sizeof d
);
949 snprintf(d
.name
, sizeof(d
.name
), "%u", head
);
952 d
.size
= fat
[head
].length
* boot
->ClusterSize
;
956 memcpy(p
, d
.name
, strlen(d
.name
));
957 p
[26] = (u_char
)d
.head
;
958 p
[27] = (u_char
)(d
.head
>> 8);
959 if (boot
->ClustMask
== CLUST32_MASK
) {
960 p
[20] = (u_char
)(d
.head
>> 16);
961 p
[21] = (u_char
)(d
.head
>> 24);
963 p
[28] = (u_char
)d
.size
;
964 p
[29] = (u_char
)(d
.size
>> 8);
965 p
[30] = (u_char
)(d
.size
>> 16);
966 p
[31] = (u_char
)(d
.size
>> 24);
967 fat
[head
].flags
|= FAT_USED
;
968 if (lseek(dosfs
, lfoff
, SEEK_SET
) != lfoff
969 || write(dosfs
, lfbuf
, boot
->ClusterSize
) != boot
->ClusterSize
) {
970 perror("could not write LOST.DIR");