dir.c revision 1.4 1 /* $NetBSD: dir.c,v 1.4 1996/09/11 20:31:24 christos 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.4 1996/09/11 20:31:24 christos 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 <sys/param.h>
51
52 #include "ext.h"
53
54 #define SLOT_EMPTY 0x00 /* slot has never been used */
55 #define SLOT_E5 0x05 /* the real value is 0xe5 */
56 #define SLOT_DELETED 0xe5 /* file in this slot deleted */
57
58 #define ATTR_NORMAL 0x00 /* normal file */
59 #define ATTR_READONLY 0x01 /* file is readonly */
60 #define ATTR_HIDDEN 0x02 /* file is hidden */
61 #define ATTR_SYSTEM 0x04 /* file is a system file */
62 #define ATTR_VOLUME 0x08 /* entry is a volume label */
63 #define ATTR_DIRECTORY 0x10 /* entry is a directory name */
64 #define ATTR_ARCHIVE 0x20 /* file is new or modified */
65
66 #define ATTR_WIN95 0x0f /* long name record */
67
68 /*
69 * This is the format of the contents of the deTime field in the direntry
70 * structure.
71 * We don't use bitfields because we don't know how compilers for
72 * arbitrary machines will lay them out.
73 */
74 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
75 #define DT_2SECONDS_SHIFT 0
76 #define DT_MINUTES_MASK 0x7E0 /* minutes */
77 #define DT_MINUTES_SHIFT 5
78 #define DT_HOURS_MASK 0xF800 /* hours */
79 #define DT_HOURS_SHIFT 11
80
81 /*
82 * This is the format of the contents of the deDate field in the direntry
83 * structure.
84 */
85 #define DD_DAY_MASK 0x1F /* day of month */
86 #define DD_DAY_SHIFT 0
87 #define DD_MONTH_MASK 0x1E0 /* month */
88 #define DD_MONTH_SHIFT 5
89 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */
90 #define DD_YEAR_SHIFT 9
91
92 /*
93 * Manage free dosDirEntry structures.
94 */
95 static struct dosDirEntry *freede;
96
97 static struct dosDirEntry *
98 newDosDirEntry()
99 {
100 struct dosDirEntry *de;
101
102 if (!(de = freede)) {
103 if (!(de = (struct dosDirEntry *)malloc(sizeof *de)))
104 return 0;
105 } else
106 freede = de->next;
107 return de;
108 }
109
110 static void
111 freeDosDirEntry(de)
112 struct dosDirEntry *de;
113 {
114 de->next = freede;
115 freede = de;
116 }
117
118 /*
119 * The same for dirTodoNode structures.
120 */
121 static struct dirTodoNode *freedt;
122
123 static struct dirTodoNode *
124 newDirTodo()
125 {
126 struct dirTodoNode *dt;
127
128 if (!(dt = freedt)) {
129 if (!(dt = (struct dirTodoNode *)malloc(sizeof *dt)))
130 return 0;
131 } else
132 freedt = dt->next;
133 return dt;
134 }
135
136 static void
137 freeDirTodo(dt)
138 struct dirTodoNode *dt;
139 {
140 dt->next = freedt;
141 freedt = dt;
142 }
143
144 /*
145 * The stack of unread directories
146 */
147 struct dirTodoNode *pendingDirectories = NULL;
148
149 /*
150 * Return the full pathname for a directory entry.
151 */
152 static char *
153 fullpath(dir)
154 struct dosDirEntry *dir;
155 {
156 static char namebuf[MAXPATHLEN + 1];
157 char *cp, *np;
158 int nl;
159
160 cp = namebuf + sizeof namebuf - 1;
161 *cp = '\0';
162 do {
163 np = dir->lname[0] ? dir->lname : dir->name;
164 nl = strlen(np);
165 if ((cp -= nl) <= namebuf + 1)
166 break;
167 memcpy(cp, np, nl);
168 *--cp = '/';
169 } while ((dir = dir->parent) != NULL);
170 if (dir->parent)
171 *--cp = '?';
172 return cp;
173 }
174
175 /*
176 * Calculate a checksum over an 8.3 alias name
177 */
178 static u_char
179 calcShortSum(p)
180 u_char *p;
181 {
182 u_char sum = 0;
183 int i;
184
185 for (i = 0; i < 11; i++) {
186 sum = (sum << 7)|(sum >> 1); /* rotate right */
187 sum += p[i];
188 }
189
190 return sum;
191 }
192
193 /*
194 * Global variables temporarily used during a directory scan
195 */
196 static char longName[DOSLONGNAMELEN] = "";
197 static u_char *buffer = NULL;
198 static u_char *delbuf = NULL;
199
200 struct dosDirEntry *rootDir;
201 static struct dosDirEntry *lostDir;
202
203 /*
204 * Init internal state for a new directory scan.
205 */
206 int
207 resetDosDirSection(boot)
208 struct bootblock *boot;
209 {
210 int b1, b2;
211
212 b1 = boot->RootDirEnts * 32;
213 b2 = boot->SecPerClust * boot->BytesPerSec;
214
215 if (!(buffer = malloc(b1 > b2 ? b1 : b2))
216 || !(delbuf = malloc(b2))
217 || !(rootDir = newDosDirEntry())) {
218 perror("No space for directory");
219 return FSFATAL;
220 }
221 memset(rootDir, 0, sizeof *rootDir);
222 return FSOK;
223 }
224
225 /*
226 * Cleanup after a directory scan
227 */
228 void
229 finishDosDirSection()
230 {
231 struct dirTodoNode *p, *np;
232 struct dosDirEntry *d, *nd;
233
234 for (p = pendingDirectories; p; p = np) {
235 np = p->next;
236 freeDirTodo(p);
237 }
238 pendingDirectories = 0;
239 for (d = rootDir; d; d = nd) {
240 if ((nd = d->child) != NULL) {
241 d->child = 0;
242 continue;
243 }
244 if (!(nd = d->next))
245 nd = d->parent;
246 freeDosDirEntry(d);
247 }
248 rootDir = lostDir = NULL;
249 free(buffer);
250 free(delbuf);
251 buffer = NULL;
252 delbuf = NULL;
253 }
254
255 /*
256 * Delete directory entries between startcl, startoff and endcl, endoff.
257 */
258 static int
259 delete(f, boot, fat, startcl, startoff, endcl, endoff, notlast)
260 int f;
261 struct bootblock *boot;
262 struct fatEntry *fat;
263 cl_t startcl;
264 int startoff;
265 cl_t endcl;
266 int endoff;
267 int notlast;
268 {
269 u_char *s, *e;
270 off_t off;
271 int clsz = boot->SecPerClust * boot->BytesPerSec;
272
273 s = delbuf + startoff;
274 e = delbuf + clsz;
275 while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
276 if (startcl == endcl) {
277 if (notlast)
278 break;
279 e = delbuf + endoff;
280 }
281 off = startcl * boot->SecPerClust + boot->ClusterOffset;
282 off *= boot->BytesPerSec;
283 if (lseek(f, off, SEEK_SET) != off
284 || read(f, delbuf, clsz) != clsz) {
285 perror("Unable to read directory");
286 return FSFATAL;
287 }
288 while (s < e) {
289 *s = SLOT_DELETED;
290 s += 32;
291 }
292 if (lseek(f, off, SEEK_SET) != off
293 || write(f, delbuf, clsz) != clsz) {
294 perror("Unable to write directory");
295 return FSFATAL;
296 }
297 if (startcl == endcl)
298 break;
299 startcl = fat[startcl].next;
300 s = delbuf;
301 }
302 return FSOK;
303 }
304
305 static int
306 removede(f, boot, fat, start, end, startcl, endcl, curcl, path, type)
307 int f;
308 struct bootblock *boot;
309 struct fatEntry *fat;
310 u_char *start;
311 u_char *end;
312 cl_t startcl;
313 cl_t endcl;
314 cl_t curcl;
315 char *path;
316 int type;
317 {
318 switch (type) {
319 case 0:
320 pwarn("Invalid long filename entry for %s\n", path);
321 break;
322 case 1:
323 pwarn("Invalid long filename entry at end of directory %s\n", path);
324 break;
325 case 2:
326 pwarn("Invalid long filename entry for volume label\n");
327 break;
328 }
329 if (ask(0, "Remove")) {
330 if (startcl != curcl) {
331 if (delete(f, boot, fat,
332 startcl, start - buffer,
333 endcl, end - buffer,
334 endcl == curcl) == FSFATAL)
335 return FSFATAL;
336 start = buffer;
337 }
338 if (endcl == curcl)
339 for (; start < end; start += 32)
340 *start = SLOT_DELETED;
341 return FSDIRMOD;
342 }
343 return FSERROR;
344 }
345
346 /*
347 * Check an in-memory file entry
348 */
349 static int
350 checksize(boot, fat, p, dir)
351 struct bootblock *boot;
352 struct fatEntry *fat;
353 u_char *p;
354 struct dosDirEntry *dir;
355 {
356 /*
357 * Check size on ordinary files
358 */
359 int32_t physicalSize;
360
361 if (dir->head == CLUST_FREE)
362 physicalSize = 0;
363 else {
364 if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
365 return FSERROR;
366 physicalSize = fat[dir->head].length * boot->ClusterSize;
367 }
368 if (physicalSize < dir->size) {
369 pwarn("size of %s is %lu, should at most be %lu\n",
370 fullpath(dir), dir->size, physicalSize);
371 if (ask(1, "Truncate")) {
372 dir->size = physicalSize;
373 p[28] = (u_char)physicalSize;
374 p[29] = (u_char)(physicalSize >> 8);
375 p[30] = (u_char)(physicalSize >> 16);
376 p[31] = (u_char)(physicalSize >> 24);
377 return FSDIRMOD;
378 } else
379 return FSERROR;
380 } else if (physicalSize - dir->size >= boot->ClusterSize) {
381 pwarn("%s has too many clusters allocated\n",
382 fullpath(dir));
383 if (ask(1, "Drop superfluous clusters")) {
384 cl_t cl;
385 u_int32_t sz = 0;
386
387 for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;)
388 cl = fat[cl].next;
389 clearchain(boot, fat, fat[cl].next);
390 fat[cl].next = CLUST_EOF;
391 return FSFATMOD;
392 } else
393 return FSERROR;
394 }
395 return FSOK;
396 }
397
398 /*
399 * Read a directory and
400 * - resolve long name records
401 * - enter file and directory records into the parent's list
402 * - push directories onto the todo-stack
403 */
404 static int
405 readDosDirSection(f, boot, fat, dir)
406 int f;
407 struct bootblock *boot;
408 struct fatEntry *fat;
409 struct dosDirEntry *dir;
410 {
411 struct dosDirEntry dirent, *d;
412 u_char *p, *vallfn, *invlfn, *empty;
413 off_t off;
414 int i, j, k, last;
415 cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
416 char *t;
417 u_int lidx = 0;
418 int shortSum;
419 int mod = FSOK;
420 #define THISMOD 0x8000 /* Only used within this routine */
421
422 cl = dir->head;
423 if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
424 /*
425 * Already handled somewhere else.
426 */
427 return FSOK;
428 }
429 shortSum = -1;
430 vallfn = invlfn = empty = NULL;
431 do {
432 if (!dir->parent) {
433 last = boot->RootDirEnts * 32;
434 off = boot->ResSectors + boot->FATs * boot->FATsecs;
435 } else {
436 last = boot->SecPerClust * boot->BytesPerSec;
437 off = cl * boot->SecPerClust + boot->ClusterOffset;
438 }
439
440 off *= boot->BytesPerSec;
441 if (lseek(f, off, SEEK_SET) != off
442 || read(f, buffer, last) != last) {
443 perror("Unable to read directory");
444 return FSFATAL;
445 }
446 last /= 32;
447 /*
448 * Check `.' and `..' entries here? XXX
449 */
450 for (p = buffer, i = 0; i < last; i++, p += 32) {
451 if (dir->fsckflags & DIREMPWARN) {
452 *p = SLOT_EMPTY;
453 continue;
454 }
455
456 if (*p == SLOT_EMPTY || *p == SLOT_DELETED) {
457 if (*p == SLOT_EMPTY) {
458 dir->fsckflags |= DIREMPTY;
459 empty = p;
460 empcl = cl;
461 }
462 continue;
463 }
464
465 if (dir->fsckflags & DIREMPTY) {
466 if (!(dir->fsckflags & DIREMPWARN)) {
467 pwarn("%s has entries after end of directory\n",
468 fullpath(dir));
469 if (ask(1, "Extend")) {
470 dir->fsckflags &= ~DIREMPTY;
471 if (delete(f, boot, fat,
472 empcl, empty - buffer,
473 cl, p - buffer) == FSFATAL)
474 return FSFATAL;
475 } else if (ask(0, "Truncate"))
476 dir->fsckflags |= DIREMPWARN;
477 }
478 if (dir->fsckflags & DIREMPWARN) {
479 *p = SLOT_DELETED;
480 mod |= THISMOD|FSDIRMOD;
481 continue;
482 } else if (dir->fsckflags & DIREMPTY)
483 mod |= FSERROR;
484 empty = NULL;
485 }
486
487 if (p[11] == ATTR_WIN95) {
488 if (*p & LRFIRST) {
489 if (shortSum != -1) {
490 if (!invlfn) {
491 invlfn = vallfn;
492 invcl = valcl;
493 }
494 }
495 memset(longName, 0, sizeof longName);
496 shortSum = p[13];
497 vallfn = p;
498 valcl = cl;
499 } else if (shortSum != p[13]
500 || lidx != (*p & LRNOMASK)) {
501 if (!invlfn) {
502 invlfn = vallfn;
503 invcl = valcl;
504 }
505 if (!invlfn) {
506 invlfn = p;
507 invcl = cl;
508 }
509 vallfn = NULL;
510 }
511 lidx = *p & LRNOMASK;
512 t = longName + --lidx * 13;
513 for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) {
514 if (!p[k] && !p[k + 1])
515 break;
516 *t++ = p[k];
517 /*
518 * Warn about those unusable chars in msdosfs here? XXX
519 */
520 if (p[k + 1])
521 t[-1] = '?';
522 }
523 if (k >= 11)
524 for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) {
525 if (!p[k] && !p[k + 1])
526 break;
527 *t++ = p[k];
528 if (p[k + 1])
529 t[-1] = '?';
530 }
531 if (k >= 26)
532 for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) {
533 if (!p[k] && !p[k + 1])
534 break;
535 *t++ = p[k];
536 if (p[k + 1])
537 t[-1] = '?';
538 }
539 if (t >= longName + sizeof(longName)) {
540 pwarn("long filename too long\n");
541 if (!invlfn) {
542 invlfn = vallfn;
543 invcl = valcl;
544 }
545 vallfn = NULL;
546 }
547 if (p[26] | (p[27] << 8)) {
548 pwarn("long filename record cluster start != 0\n");
549 if (!invlfn) {
550 invlfn = vallfn;
551 invcl = cl;
552 }
553 vallfn = NULL;
554 }
555 continue; /* long records don't carry further
556 * information */
557 }
558
559 /*
560 * This is a standard msdosfs directory entry.
561 */
562 memset(&dirent, 0, sizeof dirent);
563
564 /*
565 * it's a short name record, but we need to know
566 * more, so get the flags first.
567 */
568 dirent.flags = p[11];
569
570 /*
571 * Translate from 850 to ISO here XXX
572 */
573 for (j = 0; j < 8; j++)
574 dirent.name[j] = p[j];
575 dirent.name[8] = '\0';
576 for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
577 dirent.name[k] = '\0';
578 if (dirent.name[k] != '\0')
579 k++;
580 if (dirent.name[0] == SLOT_E5)
581 dirent.name[0] = 0xe5;
582
583 if (dirent.flags & ATTR_VOLUME) {
584 if (vallfn || invlfn) {
585 mod |= removede(f, boot, fat,
586 invlfn ? invlfn : vallfn, p,
587 invlfn ? invcl : valcl, -1, 0,
588 fullpath(dir), 2);
589 vallfn = NULL;
590 invlfn = NULL;
591 }
592 continue;
593 }
594
595 if (p[8] != ' ')
596 dirent.name[k++] = '.';
597 for (j = 0; j < 3; j++)
598 dirent.name[k++] = p[j+8];
599 dirent.name[k] = '\0';
600 for (k--; k >= 0 && dirent.name[k] == ' '; k--)
601 dirent.name[k] = '\0';
602
603 if (vallfn && shortSum != calcShortSum(p)) {
604 if (!invlfn) {
605 invlfn = vallfn;
606 invcl = valcl;
607 }
608 vallfn = NULL;
609 }
610 dirent.head = p[26] | (p[27] << 8);
611 dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
612 if (vallfn) {
613 strcpy(dirent.lname, longName);
614 longName[0] = '\0';
615 shortSum = -1;
616 }
617
618 if (invlfn) {
619 mod |= k = removede(f, boot, fat,
620 invlfn, vallfn ? vallfn : p,
621 invcl, vallfn ? valcl : cl, cl,
622 fullpath(&dirent), 0);
623 if (mod & FSFATAL)
624 return FSFATAL;
625 if (vallfn
626 ? (valcl == cl && vallfn != buffer)
627 : p != buffer)
628 if (k & FSDIRMOD)
629 mod |= THISMOD;
630 }
631 vallfn = NULL; /* not used any longer */
632 invlfn = NULL;
633
634 if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
635 if (dirent.head != 0) {
636 pwarn("%s has clusters, but size 0\n",
637 fullpath(&dirent));
638 if (ask(1, "Drop allocated clusters")) {
639 p[26] = p[27] = 0;
640 clearchain(boot, fat, dirent.head);
641 dirent.head = 0;
642 mod |= THISMOD|FSDIRMOD|FSFATMOD;
643 } else
644 mod |= FSERROR;
645 }
646 } else if (dirent.head == 0
647 && !strcmp(dirent.name, "..")
648 && dir->parent /* XXX */
649 && !dir->parent->parent) {
650 /*
651 * Do nothing, the parent is the root
652 */
653 } else if (dirent.head < CLUST_FIRST
654 || dirent.head >= boot->NumClusters
655 || fat[dirent.head].next == CLUST_FREE
656 || (fat[dirent.head].next >= CLUST_RSRVD
657 && fat[dirent.head].next < CLUST_EOFS)
658 || fat[dirent.head].head != dirent.head) {
659 if (dirent.head == 0)
660 pwarn("%s has no clusters\n",
661 fullpath(&dirent));
662 else if (dirent.head < CLUST_FIRST
663 || dirent.head >= boot->NumClusters)
664 pwarn("%s starts with cluster out of range(%d)\n",
665 fullpath(&dirent),
666 dirent.head);
667 else if (fat[dirent.head].next == CLUST_FREE)
668 pwarn("%s starts with free cluster\n",
669 fullpath(&dirent));
670 else if (fat[dirent.head].next >= CLUST_RSRVD)
671 pwarn("%s starts with %s cluster\n",
672 fullpath(&dirent),
673 rsrvdcltype(fat[dirent.head].next));
674 else
675 pwarn("%s doesn't start a cluster chain\n",
676 fullpath(&dirent));
677 if (dirent.flags & ATTR_DIRECTORY) {
678 if (ask(0, "Remove")) {
679 *p = SLOT_DELETED;
680 mod |= THISMOD|FSDIRMOD;
681 } else
682 mod |= FSERROR;
683 continue;
684 } else {
685 if (ask(1, "Truncate")) {
686 p[28] = p[29] = p[30] = p[31] = 0;
687 dirent.size = 0;
688 mod |= THISMOD|FSDIRMOD;
689 } else
690 mod |= FSERROR;
691 }
692 }
693
694 dirent.parent = dir;
695 dirent.next = dir->child;
696 if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters)
697 fat[dirent.head].flags |= FAT_USED;
698
699 if (dirent.flags & ATTR_DIRECTORY) {
700 /*
701 * gather more info for directories
702 */
703 struct dirTodoNode * n;
704
705 if (dirent.size) {
706 pwarn("Directory %s has size != 0\n",
707 fullpath(&dirent));
708 if (ask(1, "Correct")) {
709 p[28] = p[29] = p[30] = p[31] = 0;
710 dirent.size = 0;
711 mod |= THISMOD|FSDIRMOD;
712 } else
713 mod |= FSERROR;
714 }
715 /*
716 * handle `.' and `..' specially
717 */
718 if (strcmp(dirent.name, ".") == 0) {
719 if (dirent.head != dir->head) {
720 pwarn("`.' entry in %s has incorrect start cluster\n",
721 fullpath(dir));
722 if (ask(1, "Correct")) {
723 dirent.head = dir->head;
724 p[26] = (u_char)dirent.head;
725 p[27] = (u_char)(dirent.head >> 8);
726 mod |= THISMOD|FSDIRMOD;
727 } else
728 mod |= FSERROR;
729 }
730 continue;
731 }
732 if (strcmp(dirent.name, "..") == 0) {
733 if (dir->parent /* XXX */
734 && dirent.head != dir->parent->head) {
735 pwarn("`..' entry in %s has incorrect start cluster\n",
736 fullpath(dir));
737 if (ask(1, "Correct")) {
738 dirent.head = dir->parent->head;
739 p[26] = (u_char)dirent.head;
740 p[27] = (u_char)(dirent.head >> 8);
741 mod |= THISMOD|FSDIRMOD;
742 } else
743 mod |= FSERROR;
744 }
745 continue;
746 }
747
748 boot->NumFiles++;
749
750 /* create directory tree node */
751 if (!(d = newDosDirEntry())) {
752 perror("No space for directory");
753 return FSFATAL;
754 }
755
756 memcpy(d, &dirent, sizeof(struct dosDirEntry));
757 /* link it into the tree */
758 dir->child = d;
759
760 /* Enter this directory into the todo list */
761 if (!(n = newDirTodo())) {
762 perror("No space for todo list");
763 return FSFATAL;
764 }
765 n->next = pendingDirectories;
766 n->dir = d;
767 pendingDirectories = n;
768 } else {
769 mod |= k = checksize(boot, fat, p, &dirent);
770 if (k & FSDIRMOD)
771 mod |= THISMOD;
772 boot->NumFiles++;
773 }
774 }
775 if (mod & THISMOD) {
776 last *= 32;
777 if (lseek(f, off, SEEK_SET) != off
778 || write(f, buffer, last) != last) {
779 perror("Unable to write directory");
780 return FSFATAL;
781 }
782 mod &= ~THISMOD;
783 }
784 } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
785 if (invlfn || vallfn)
786 mod |= removede(f, boot, fat,
787 invlfn ? invlfn : vallfn, p,
788 invlfn ? invcl : valcl, -1, 0,
789 fullpath(dir), 1);
790 return mod & ~THISMOD;
791 }
792
793 int
794 handleDirTree(dosfs, boot, fat)
795 int dosfs;
796 struct bootblock *boot;
797 struct fatEntry *fat;
798 {
799 int mod;
800
801 mod = readDosDirSection(dosfs, boot, fat, rootDir);
802 if (mod & FSFATAL)
803 return FSFATAL;
804
805 if (mod & FSFATMOD) {
806 mod &= ~FSFATMOD;
807 mod |= writefat(dosfs, boot, fat); /* delay writing fats? XXX */
808 }
809
810 if (mod & FSFATAL)
811 return FSFATAL;
812
813 /*
814 * process the directory todo list
815 */
816 while (pendingDirectories) {
817 struct dosDirEntry *dir = pendingDirectories->dir;
818 struct dirTodoNode *n = pendingDirectories->next;
819
820 /*
821 * remove TODO entry now, the list might change during
822 * directory reads
823 */
824 freeDirTodo(pendingDirectories);
825 pendingDirectories = n;
826
827 /*
828 * handle subdirectory
829 */
830 mod |= readDosDirSection(dosfs, boot, fat, dir);
831 if (mod & FSFATAL)
832 return FSFATAL;
833 if (mod & FSFATMOD) {
834 mod &= ~FSFATMOD;
835 mod |= writefat(dosfs, boot, fat); /* delay writing fats? XXX */
836 }
837 if (mod & FSFATAL)
838 return FSFATAL;
839 }
840 return mod;
841 }
842
843 /*
844 * Try to reconnect a FAT chain into dir
845 */
846 static u_char *lfbuf;
847 static cl_t lfcl;
848 static off_t lfoff;
849
850 int
851 reconnect(dosfs, boot, fat, head)
852 int dosfs;
853 struct bootblock *boot;
854 struct fatEntry *fat;
855 cl_t head;
856 {
857 struct dosDirEntry d;
858 u_char *p;
859
860 if (!lostDir) {
861 for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) {
862 if (!strcmp(lostDir->name, LOSTDIR))
863 break;
864 }
865 if (!lostDir) { /* Create LOSTDIR? XXX */
866 pwarn("No %s directory\n", LOSTDIR);
867 return FSERROR;
868 }
869 }
870 if (!lfbuf) {
871 lfbuf = malloc(boot->ClusterSize);
872 if (!lfbuf) {
873 perror("No space for buffer");
874 return FSFATAL;
875 }
876 p = NULL;
877 } else
878 p = lfbuf;
879 while (1) {
880 if (p)
881 while (p < lfbuf + boot->ClusterSize)
882 if (*p == SLOT_EMPTY
883 || *p == SLOT_DELETED)
884 break;
885 if (p && p < lfbuf + boot->ClusterSize)
886 break;
887 lfcl = p ? fat[lfcl].next : lostDir->head;
888 if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
889 /* Extend LOSTDIR? XXX */
890 pwarn("No space in %s\n", LOSTDIR);
891 return FSERROR;
892 }
893 lfoff = lfcl * boot->ClusterSize
894 + boot->ClusterOffset * boot->BytesPerSec;
895 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
896 || read(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) {
897 perror("could not read LOST.DIR");
898 return FSFATAL;
899 }
900 p = lfbuf;
901 }
902
903 if (!ask(0, "Reconnect"))
904 return FSERROR;
905
906 boot->NumFiles++;
907 /* Ensure uniqueness of entry here! XXX */
908 memset(&d, 0, sizeof d);
909 sprintf(d.name, "%d", head);
910 d.flags = 0;
911 d.head = head;
912 d.size = fat[head].length * boot->ClusterSize;
913
914 memset(p, 0, 32);
915 memset(p, ' ', 11);
916 memcpy(p, d.name, strlen(d.name));
917 p[26] = (u_char)d.head;
918 p[27] = (u_char)(d.head >> 8);
919 p[28] = (u_char)d.size;
920 p[29] = (u_char)(d.size >> 8);
921 p[30] = (u_char)(d.size >> 16);
922 p[31] = (u_char)(d.size >> 24);
923 fat[head].flags |= FAT_USED;
924 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
925 || write(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) {
926 perror("could not write LOST.DIR");
927 return FSFATAL;
928 }
929 return FSDIRMOD;
930 }
931
932 void
933 finishlf()
934 {
935 if (lfbuf)
936 free(lfbuf);
937 lfbuf = NULL;
938 }
939