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