dir.c revision 1.1 1 /* $NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996 Wolfgang Solfrank
5 * Copyright (c) 1995 Martin Husemann
6 * Some structure declaration borrowed from Paul Popelka
7 * (paulp (at) uts.amdahl.com), see /sys/msdosfs/ for reference.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
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.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Martin Husemann
20 * and Wolfgang Solfrank.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37
38 #ifndef lint
39 static char rcsid[] = "$NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $";
40 #endif /* not lint */
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <time.h>
49
50 #include "ext.h"
51
52 #define SLOT_EMPTY 0x00 /* slot has never been used */
53 #define SLOT_E5 0x05 /* the real value is 0xe5 */
54 #define SLOT_DELETED 0xe5 /* file in this slot deleted */
55
56 #define ATTR_NORMAL 0x00 /* normal file */
57 #define ATTR_READONLY 0x01 /* file is readonly */
58 #define ATTR_HIDDEN 0x02 /* file is hidden */
59 #define ATTR_SYSTEM 0x04 /* file is a system file */
60 #define ATTR_VOLUME 0x08 /* entry is a volume label */
61 #define ATTR_DIRECTORY 0x10 /* entry is a directory name */
62 #define ATTR_ARCHIVE 0x20 /* file is new or modified */
63
64 #define ATTR_WIN95 0x0f /* long name record */
65
66 /*
67 * This is the format of the contents of the deTime field in the direntry
68 * structure.
69 * We don't use bitfields because we don't know how compilers for
70 * arbitrary machines will lay them out.
71 */
72 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
73 #define DT_2SECONDS_SHIFT 0
74 #define DT_MINUTES_MASK 0x7E0 /* minutes */
75 #define DT_MINUTES_SHIFT 5
76 #define DT_HOURS_MASK 0xF800 /* hours */
77 #define DT_HOURS_SHIFT 11
78
79 /*
80 * This is the format of the contents of the deDate field in the direntry
81 * structure.
82 */
83 #define DD_DAY_MASK 0x1F /* day of month */
84 #define DD_DAY_SHIFT 0
85 #define DD_MONTH_MASK 0x1E0 /* month */
86 #define DD_MONTH_SHIFT 5
87 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */
88 #define DD_YEAR_SHIFT 9
89
90 /*
91 * Calculate a checksum over an 8.3 alias name
92 */
93 static u_char
94 calcShortSum(p)
95 u_char *p;
96 {
97 u_char sum = 0;
98 int i;
99
100 for (i = 0; i < 11; i++) {
101 sum = (sum << 7)|(sum >> 1); /* rotate right */
102 sum += p[i];
103 }
104
105 return sum;
106 }
107
108 /*
109 * Global variables temporarily used during a directory scan
110 */
111 static char longName[DOSLONGNAMELEN] = "";
112 static u_char *buffer = NULL;
113 static u_char *delbuf = NULL;
114
115 /*
116 * Init internal state for a new directory scan.
117 */
118 int
119 resetDosDirSection(boot)
120 struct bootblock *boot;
121 {
122 int b1, b2;
123
124 b1 = boot->RootDirEnts * 32;
125 b2 = boot->SecPerClust * boot->BytesPerSec;
126
127 if (!(buffer = malloc(b1 > b2 ? b1 : b2))
128 || !(delbuf = malloc(b2))) {
129 perror("No space for directory");
130 return FSFATAL;
131 }
132 return FSOK;
133 }
134
135 /*
136 * Cleanup after a directory scan
137 */
138 void
139 finishDosDirSection()
140 {
141 free(buffer);
142 free(delbuf);
143 buffer = NULL;
144 delbuf = NULL;
145 }
146
147 /*
148 * Delete directory entries between startcl, startoff and endcl, endoff.
149 */
150 static int
151 delete(f, boot, fat, startcl, startoff, endcl, endoff, notlast)
152 int f;
153 struct bootblock *boot;
154 struct fatEntry *fat;
155 cl_t startcl;
156 int startoff;
157 cl_t endcl;
158 int endoff;
159 int notlast;
160 {
161 u_char *s, *e;
162 off_t off;
163 int clsz = boot->SecPerClust * boot->BytesPerSec;
164
165 s = delbuf + startoff;
166 e = delbuf + clsz;
167 while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
168 if (startcl == endcl) {
169 if (notlast)
170 break;
171 e = delbuf + endoff;
172 }
173 off = startcl * boot->SecPerClust + boot->ClusterOffset;
174 off *= boot->BytesPerSec;
175 if (lseek(f, off, SEEK_SET) != off
176 || read(f, delbuf, clsz) != clsz) {
177 perror("Unable to read directory");
178 return FSFATAL;
179 }
180 while (s < e) {
181 *s = SLOT_DELETED;
182 s += 32;
183 }
184 if (lseek(f, off, SEEK_SET) != off
185 || write(f, delbuf, clsz) != clsz) {
186 perror("Unable to write directory");
187 return FSFATAL;
188 }
189 if (startcl == endcl)
190 break;
191 startcl = fat[startcl].next;
192 s = delbuf;
193 }
194 return FSOK;
195 }
196
197 static int
198 removede(f, boot, fat, start, end, startcl, endcl, curcl, path, eof)
199 int f;
200 struct bootblock *boot;
201 struct fatEntry *fat;
202 u_char *start;
203 u_char *end;
204 cl_t startcl;
205 cl_t endcl;
206 cl_t curcl;
207 char *path;
208 int eof;
209 {
210 if (!eof)
211 pwarn("Invalid long filename entry for %s\n", path);
212 else
213 pwarn("Invalid long filename entry at end of directory %s\n", path);
214 if (ask(0, "Remove")) {
215 if (startcl != curcl) {
216 if (delete(f, boot, fat,
217 startcl, start - buffer,
218 endcl, end - buffer,
219 endcl == curcl) == FSFATAL)
220 return FSFATAL;
221 start = buffer;
222 }
223 if (endcl == curcl)
224 for (; start < end; start += 32)
225 *start = SLOT_DELETED;
226 return FSDIRMOD;
227 }
228 return FSERROR;
229 }
230
231 /*
232 * Check an in-memory file entry
233 */
234 static int
235 checksize(boot, fat, p, dir)
236 struct bootblock *boot;
237 struct fatEntry *fat;
238 u_char *p;
239 struct dosDirEntry *dir;
240 {
241 /*
242 * Check size on ordinary files
243 */
244 u_int32_t physicalSize;
245
246 if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
247 return FSERROR;
248 physicalSize = fat[dir->head].length * boot->ClusterSize;
249 if (physicalSize < dir->size) {
250 pwarn("size of %s is %lu, should at most be %lu\n",
251 dir->fullpath, dir->size, physicalSize);
252 if (ask(1, "Truncate")) {
253 dir->size = physicalSize;
254 p[28] = (u_char)physicalSize;
255 p[29] = (u_char)(physicalSize >> 8);
256 p[30] = (u_char)(physicalSize >> 16);
257 p[31] = (u_char)(physicalSize >> 24);
258 return FSDIRMOD;
259 } else
260 return FSERROR;
261 } else if (physicalSize - dir->size >= boot->ClusterSize) {
262 pwarn("%s has too many clusters allocated\n",
263 dir->fullpath);
264 if (ask(1, "Drop superfluous clusters")) {
265 cl_t cl;
266 u_int32_t sz = 0;
267
268 for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;)
269 cl = fat[cl].next;
270 clearchain(boot, fat, fat[cl].next);
271 fat[cl].next = CLUST_EOF;
272 return FSFATMOD;
273 } else
274 return FSERROR;
275 }
276 return FSOK;
277 }
278
279 /*
280 * The stack of unread directories
281 */
282 struct dirTodoNode *pendingDirectories = NULL;
283
284 /*
285 * Read a directory and
286 * - resolve long name records
287 * - enter file and directory records into the parent's list
288 * - push directories onto the todo-stack
289 */
290 int
291 readDosDirSection(f, boot, fat, dir)
292 int f;
293 struct bootblock *boot;
294 struct fatEntry *fat;
295 struct dosDirEntry *dir;
296 {
297 struct dosDirEntry dirent, *d;
298 u_char *p, *vallfn, *invlfn, *empty;
299 off_t off;
300 int i, j, k, last;
301 cl_t cl, valcl, invcl, empcl;
302 char *t;
303 u_int lidx = 0;
304 int shortSum;
305 int mod = FSOK;
306 #define THISMOD 0x8000 /* Only used within this routine */
307
308 cl = dir->head;
309 if (dir->fullpath[1] && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
310 /*
311 * Already handled somewhere else.
312 */
313 return FSOK;
314 }
315 shortSum = -1;
316 vallfn = invlfn = empty = NULL;
317 do {
318 if (!dir->fullpath[1]) {
319 last = boot->RootDirEnts * 32;
320 off = boot->ResSectors + boot->FATs * boot->FATsecs;
321 } else {
322 last = boot->SecPerClust * boot->BytesPerSec;
323 off = cl * boot->SecPerClust + boot->ClusterOffset;
324 }
325
326 off *= boot->BytesPerSec;
327 if (lseek(f, off, SEEK_SET) != off
328 || read(f, buffer, last) != last) {
329 perror("Unable to read directory");
330 return FSFATAL;
331 }
332 last /= 32;
333 /*
334 * Check `.' and `..' entries here? XXX
335 */
336 for (p = buffer, i = 0; i < last; i++, p += 32) {
337 if (dir->fsckflags & DIREMPWARN) {
338 *p = SLOT_EMPTY;
339 continue;
340 }
341
342 if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
343 if (*p == SLOT_EMPTY) {
344 dir->fsckflags |= DIREMPTY;
345 empty = p;
346 empcl = cl;
347 }
348 continue;
349 }
350
351 if (dir->fsckflags & DIREMPTY) {
352 if (!(dir->fsckflags & DIREMPWARN)) {
353 pwarn("%s has entries after end of directory\n",
354 dir->fullpath);
355 if (ask(1, "Extend")) {
356 dir->fsckflags &= ~DIREMPTY;
357 if (delete(f, boot, fat,
358 empcl, empty - buffer,
359 cl, p - buffer) == FSFATAL)
360 return FSFATAL;
361 } else if (ask(0, "Truncate"))
362 dir->fsckflags |= DIREMPWARN;
363 }
364 if (dir->fsckflags & DIREMPWARN) {
365 *p = SLOT_DELETED;
366 mod |= THISMOD|FSDIRMOD;
367 continue;
368 } else if (dir->fsckflags & DIREMPTY)
369 mod |= FSERROR;
370 empty = NULL;
371 }
372
373 if (p[11] == ATTR_WIN95) {
374 if (*p & LRFIRST) {
375 if (shortSum != -1) {
376 if (!invlfn) {
377 invlfn = vallfn;
378 invcl = valcl;
379 }
380 }
381 memset(longName, 0, sizeof longName);
382 shortSum = p[13];
383 vallfn = p;
384 valcl = cl;
385 } else if (shortSum != p[13]
386 || lidx != *p & LRNOMASK) {
387 if (!invlfn) {
388 invlfn = vallfn;
389 invcl = valcl;
390 }
391 if (!invlfn) {
392 invlfn = p;
393 invcl = cl;
394 }
395 vallfn = NULL;
396 }
397 lidx = *p & LRNOMASK;
398 t = longName + --lidx * 13;
399 for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) {
400 if (!p[k] && !p[k + 1])
401 break;
402 *t++ = p[k];
403 /*
404 * Warn about those unusable chars in msdosfs here? XXX
405 */
406 if (p[k + 1])
407 t[-1] = '?';
408 }
409 if (k >= 11)
410 for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
411 if (!p[k] && !p[k + 1])
412 break;
413 *t++ = p[k];
414 if (p[k + 1])
415 t[-1] = '?';
416 }
417 if (k >= 26)
418 for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
419 if (!p[k] && !p[k + 1])
420 break;
421 *t++ = p[k];
422 if (p[k + 1])
423 t[-1] = '?';
424 }
425 if (t >= longName + sizeof(longName)) {
426 pwarn("long filename too long\n");
427 if (!invlfn) {
428 invlfn = vallfn;
429 invcl = valcl;
430 }
431 vallfn = NULL;
432 }
433 if (p[26] | (p[27] << 8)) {
434 pwarn("long filename record cluster start != 0\n");
435 if (!invlfn) {
436 invlfn = vallfn;
437 invcl = cl;
438 }
439 vallfn = NULL;
440 }
441 continue; /* long records don't carry further
442 * information */
443 }
444
445 /*
446 * This is a standard msdosfs directory entry.
447 */
448 memset(&dirent, 0, sizeof dirent);
449
450 /*
451 * it's a short name record, but we need to know
452 * more, so get the flags first.
453 */
454 dirent.flags = p[11];
455
456 /*
457 * Translate from 850 to ISO here XXX
458 */
459 for (j = 0; j < 8; j++)
460 dirent.name[j] = p[j];
461 dirent.name[8] = '\0';
462 for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
463 dirent.name[k] = '\0';
464 if (dirent.name[k] != '\0')
465 k++;
466 if (dirent.name[0] == SLOT_E5)
467 dirent.name[0] = 0xe5;
468 /*
469 * What about volume names with extensions? XXX
470 */
471 if ((dirent.flags & ATTR_VOLUME) == 0 && p[8] != ' ')
472 dirent.name[k++] = '.';
473 for (j = 0; j < 3; j++)
474 dirent.name[k++] = p[j+8];
475 dirent.name[k] = '\0';
476 for (k--; k >= 0 && dirent.name[k] == ' '; k--)
477 dirent.name[k] = '\0';
478
479 if (vallfn && shortSum != calcShortSum(p)) {
480 if (!invlfn) {
481 invlfn = vallfn;
482 invcl = valcl;
483 }
484 vallfn = NULL;
485 }
486 dirent.head = p[26] | (p[27] << 8);
487 dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
488 if (vallfn) {
489 strcpy(dirent.lname, longName);
490 longName[0] = '\0';
491 shortSum = -1;
492 }
493
494 k = strlen(dirent.lname[0] ? dirent.lname : dirent.name);
495 k += strlen(dir->fullpath) + 2;
496 dirent.fullpath = malloc(k);
497 strcpy(dirent.fullpath, dir->fullpath);
498 if (dir->fullpath[1])
499 strcat(dirent.fullpath, "/");
500 strcat(dirent.fullpath,
501 dirent.lname[0] ? dirent.lname : dirent.name);
502 if (invlfn) {
503 mod |= k = removede(f, boot, fat,
504 invlfn, vallfn ? vallfn : p,
505 invcl, vallfn ? valcl : cl, cl,
506 dirent.fullpath, 0);
507 if (mod & FSFATAL)
508 return FSFATAL;
509 if (vallfn
510 ? (valcl == cl && vallfn != buffer)
511 : p != buffer)
512 if (k & FSDIRMOD)
513 mod |= THISMOD;
514 }
515 vallfn = NULL; /* not used any longer */
516 invlfn = NULL;
517
518 if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
519 if (dirent.head != 0) {
520 pwarn("%s has clusters, but size 0\n",
521 dirent.fullpath);
522 if (ask(1, "Drop allocated clusters")) {
523 p[26] = p[27] = 0;
524 clearchain(boot, fat, dirent.head);
525 dirent.head = 0;
526 mod |= THISMOD|FSDIRMOD|FSFATMOD;
527 } else
528 mod |= FSERROR;
529 }
530 } else if (dirent.head == 0
531 && !strcmp(dirent.name, "..")
532 && !strcmp(dir->parent->fullpath, "/")) {
533 /*
534 * Do nothing, the parent is the root
535 */
536 } else if (dirent.head < CLUST_FIRST
537 || dirent.head >= boot->NumClusters
538 || fat[dirent.head].next == CLUST_FREE
539 || (fat[dirent.head].next >= CLUST_RSRVD
540 && fat[dirent.head].next < CLUST_EOFS)
541 || fat[dirent.head].head != dirent.head) {
542 if (dirent.head == 0)
543 pwarn("%s has no clusters\n",
544 dirent.fullpath);
545 else if (dirent.head < CLUST_FIRST
546 || dirent.head >= boot->NumClusters)
547 pwarn("%s starts with cluster out of range(%d)\n",
548 dirent.fullpath,
549 dirent.head);
550 else if (fat[dirent.head].next == CLUST_FREE)
551 pwarn("%s starts with free cluster\n",
552 dirent.fullpath);
553 else if (fat[dirent.head].next >= CLUST_RSRVD)
554 pwarn("%s starts with %s cluster\n",
555 dirent.fullpath,
556 rsrvdcltype(fat[dirent.head].next));
557 else
558 pwarn("%s doesn't start a cluster chain\n",
559 dirent.fullpath);
560 if (dirent.flags & ATTR_DIRECTORY) {
561 if (ask(0, "Remove")) {
562 *p = SLOT_DELETED;
563 mod |= THISMOD|FSDIRMOD;
564 } else
565 mod |= FSERROR;
566 continue;
567 } else {
568 if (ask(1, "Truncate")) {
569 p[28] = p[29] = p[30] = p[31] = 0;
570 dirent.size = 0;
571 mod |= THISMOD|FSDIRMOD;
572 } else
573 mod |= FSERROR;
574 }
575 }
576
577 /* create directory tree node */
578 d = malloc(sizeof(struct dosDirEntry));
579 memcpy(d, &dirent, sizeof(struct dosDirEntry));
580 /* link it into the directory tree */
581 d->parent = dir;
582 d->next = dir->child;
583 dir->child = d;
584 if (d->head >= CLUST_FIRST && d->head < boot->NumClusters)
585 fat[d->head].dirp = d;
586
587 if (d->flags & ATTR_DIRECTORY) {
588 /*
589 * gather more info for directories
590 */
591 struct dirTodoNode * n;
592
593 if (d->size) {
594 pwarn("Directory %s has size != 0\n",
595 d->fullpath);
596 if (ask(1, "Correct")) {
597 p[28] = p[29] = p[30] = p[31] = 0;
598 d->size = 0;
599 mod |= THISMOD|FSDIRMOD;
600 } else
601 mod |= FSERROR;
602 }
603 /*
604 * handle `.' and `..' specially
605 */
606 if (strcmp(d->name, ".") == 0) {
607 if (d->head != dir->head) {
608 pwarn("`.' entry in %s has incorrect start cluster\n",
609 dir->fullpath);
610 if (ask(1, "Correct")) {
611 d->head = dir->head;
612 p[26] = (u_char)d->head;
613 p[27] = (u_char)(d->head >> 8);
614 mod |= THISMOD|FSDIRMOD;
615 } else
616 mod |= FSERROR;
617 }
618 continue;
619 }
620 if (strcmp(d->name, "..") == 0) {
621 if (d->head != dir->parent->head) {
622 pwarn("`..' entry in %s has incorrect start cluster\n",
623 dir->fullpath);
624 if (ask(1, "Correct")) {
625 d->head = dir->parent->head;
626 p[26] = (u_char)d->head;
627 p[27] = (u_char)(d->head >> 8);
628 mod |= THISMOD|FSDIRMOD;
629 } else
630 mod |= FSERROR;
631 }
632 continue;
633 }
634
635 boot->NumFiles++;
636 /* Enter this directory into the todo list */
637 n = malloc(sizeof(struct dirTodoNode));
638 n->next = pendingDirectories;
639 n->dir = d;
640 pendingDirectories = n;
641 } else {
642 mod |= k = checksize(boot, fat, p, d);
643 if (k & FSDIRMOD)
644 mod |= THISMOD;
645 boot->NumFiles++;
646 }
647 }
648 if (mod & THISMOD) {
649 last *= 32;
650 if (lseek(f, off, SEEK_SET) != off
651 || write(f, buffer, last) != last) {
652 perror("Unable to write directory");
653 return FSFATAL;
654 }
655 mod &= ~THISMOD;
656 }
657 } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
658 if (invlfn || vallfn)
659 mod |= removede(f, boot, fat,
660 invlfn ? invlfn : vallfn, p,
661 invlfn ? invcl : valcl, -1, 0,
662 dir->fullpath, 1);
663 return mod & ~THISMOD;
664 }
665
666 /*
667 * Try to reconnect a FAT chain into dir
668 */
669 static u_char *lfbuf;
670 static cl_t lfcl;
671 static off_t lfoff;
672
673 int
674 reconnect(dosfs, boot, fat, head, dir)
675 int dosfs;
676 struct bootblock *boot;
677 struct fatEntry *fat;
678 cl_t head;
679 struct dosDirEntry *dir;
680 {
681 struct dosDirEntry d;
682 u_char *p;
683
684 if (!dir) /* Create lfdir? XXX */
685 return FSERROR;
686 if (!lfbuf) {
687 lfbuf = malloc(boot->ClusterSize);
688 if (!lfbuf) {
689 perror("No space for buffer");
690 return FSFATAL;
691 }
692 p = NULL;
693 } else
694 p = lfbuf;
695 while (1) {
696 if (p)
697 while (p < lfbuf + boot->ClusterSize)
698 if (*p == SLOT_EMPTY
699 || *p == SLOT_DELETED)
700 break;
701 if (p && p < lfbuf + boot->ClusterSize)
702 break;
703 lfcl = p ? fat[lfcl].next : dir->head;
704 if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
705 /* Extend lfdir? XXX */
706 pwarn("No space in %s\n", LOSTDIR);
707 return FSERROR;
708 }
709 lfoff = lfcl * boot->ClusterSize
710 + boot->ClusterOffset * boot->BytesPerSec;
711 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
712 || read(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) {
713 perror("could not read LOST.DIR");
714 return FSFATAL;
715 }
716 p = lfbuf;
717 }
718
719 if (!ask(0, "Reconnect"))
720 return FSERROR;
721
722 boot->NumFiles++;
723 /* Ensure uniqueness of entry here! XXX */
724 memset(&d, 0, sizeof d);
725 sprintf(d.name, "%d", head);
726 d.fullpath = malloc(strlen(dir->fullpath) + strlen(d.name) + 2);
727 sprintf(d.fullpath, "%s/%s", dir->fullpath, d.name);
728 d.flags = 0;
729 d.head = head;
730 d.size = fat[head].length * boot->ClusterSize;
731 d.parent = dir;
732 d.next = dir->child;
733 dir->child = malloc(sizeof(struct dosDirEntry));
734 memcpy(dir->child, &d, sizeof(struct dosDirEntry));
735
736 memset(p, 0, 32);
737 memset(p, ' ', 11);
738 memcpy(p, dir->name, strlen(dir->name));
739 p[26] = (u_char)dir->head;
740 p[27] = (u_char)(dir->head >> 8);
741 p[28] = (u_char)dir->size;
742 p[29] = (u_char)(dir->size >> 8);
743 p[30] = (u_char)(dir->size >> 16);
744 p[31] = (u_char)(dir->size >> 24);
745 fat[head].dirp = dir;
746 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
747 || write(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) {
748 perror("could not write LOST.DIR");
749 return FSFATAL;
750 }
751 return FSDIRMOD;
752 }
753
754 void
755 finishlf()
756 {
757 if (lfbuf)
758 free(lfbuf);
759 lfbuf = NULL;
760 }
761