clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name dir.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sbin/fsck_msdos/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/sbin/fsck_msdos/../fsck -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/sbin/fsck_msdos/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/sbin/fsck_msdos/dir.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | #include <stdio.h> |
32 | #include <stdlib.h> |
33 | #include <string.h> |
34 | #include <ctype.h> |
35 | #include <unistd.h> |
36 | #include <limits.h> |
37 | #include <time.h> |
38 | |
39 | #include "ext.h" |
40 | |
41 | #define SLOT_EMPTY 0x00 /* slot has never been used */ |
42 | #define SLOT_E5 0x05 /* the real value is 0xe5 */ |
43 | #define SLOT_DELETED 0xe5 /* file in this slot deleted */ |
44 | |
45 | #define ATTR_NORMAL 0x00 /* normal file */ |
46 | #define ATTR_READONLY 0x01 /* file is readonly */ |
47 | #define ATTR_HIDDEN 0x02 /* file is hidden */ |
48 | #define ATTR_SYSTEM 0x04 /* file is a system file */ |
49 | #define ATTR_VOLUME 0x08 /* entry is a volume label */ |
50 | #define ATTR_DIRECTORY 0x10 /* entry is a directory name */ |
51 | #define ATTR_ARCHIVE 0x20 /* file is new or modified */ |
52 | |
53 | #define ATTR_WIN95 0x0f /* long name record */ |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ |
62 | #define DT_2SECONDS_SHIFT 0 |
63 | #define DT_MINUTES_MASK 0x7E0 /* minutes */ |
64 | #define DT_MINUTES_SHIFT 5 |
65 | #define DT_HOURS_MASK 0xF800 /* hours */ |
66 | #define DT_HOURS_SHIFT 11 |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | #define DD_DAY_MASK 0x1F /* day of month */ |
73 | #define DD_DAY_SHIFT 0 |
74 | #define DD_MONTH_MASK 0x1E0 /* month */ |
75 | #define DD_MONTH_SHIFT 5 |
76 | #define DD_YEAR_MASK 0xFE00 /* year - 1980 */ |
77 | #define DD_YEAR_SHIFT 9 |
78 | |
79 | |
80 | static struct dosDirEntry *newDosDirEntry(void); |
81 | static void freeDosDirEntry(struct dosDirEntry *); |
82 | static struct dirTodoNode *newDirTodo(void); |
83 | static void freeDirTodo(struct dirTodoNode *); |
84 | static char *fullpath(struct dosDirEntry *); |
85 | static u_char calcShortSum(u_char *); |
86 | static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int, |
87 | cl_t, int, int); |
88 | static int removede(int, struct bootblock *, struct fatEntry *, u_char *, |
89 | u_char *, cl_t, cl_t, cl_t, char *, int); |
90 | static int checksize(struct bootblock *, struct fatEntry *, u_char *, |
91 | struct dosDirEntry *); |
92 | static int readDosDirSection(int, struct bootblock *, struct fatEntry *, |
93 | struct dosDirEntry *); |
94 | |
95 | |
96 | |
97 | |
98 | static struct dosDirEntry *freede; |
99 | |
100 | static struct dosDirEntry * |
101 | newDosDirEntry(void) |
102 | { |
103 | struct dosDirEntry *de; |
104 | |
105 | if (!(de = freede)) { |
106 | if (!(de = malloc(sizeof *de))) |
107 | return (0); |
108 | } else |
109 | freede = de->next; |
110 | return (de); |
111 | } |
112 | |
113 | static void |
114 | freeDosDirEntry(struct dosDirEntry *de) |
115 | { |
116 | de->next = freede; |
117 | freede = de; |
118 | } |
119 | |
120 | |
121 | |
122 | |
123 | static struct dirTodoNode *freedt; |
124 | |
125 | static struct dirTodoNode * |
126 | newDirTodo(void) |
127 | { |
128 | struct dirTodoNode *dt; |
129 | |
130 | if (!(dt = freedt)) { |
131 | if (!(dt = malloc(sizeof *dt))) |
132 | return (0); |
133 | } else |
134 | freedt = dt->next; |
135 | return (dt); |
136 | } |
137 | |
138 | static void |
139 | freeDirTodo(struct dirTodoNode *dt) |
140 | { |
141 | dt->next = freedt; |
142 | freedt = dt; |
143 | } |
144 | |
145 | |
146 | |
147 | |
148 | static struct dirTodoNode *pendingDirectories = NULL; |
149 | |
150 | |
151 | |
152 | |
153 | static char * |
154 | fullpath(struct dosDirEntry *dir) |
155 | { |
156 | static char namebuf[PATH_MAX + 1]; |
157 | char *cp, *np; |
158 | int nl; |
159 | |
160 | cp = namebuf + sizeof namebuf; |
161 | *--cp = '\0'; |
162 | for(;;) { |
163 | np = dir->lname[0] ? dir->lname : dir->name; |
164 | nl = strlen(np); |
165 | |
166 | if (cp <= namebuf + 1 + nl) { |
167 | *--cp = '?'; |
168 | break; |
169 | } |
170 | cp -= nl; |
171 | (void)memcpy(cp, np, nl); |
172 | dir = dir->parent; |
173 | if (!dir) |
174 | break; |
175 | *--cp = '/'; |
176 | } |
177 | return (cp); |
178 | } |
179 | |
180 | |
181 | |
182 | |
183 | static u_char |
184 | calcShortSum(u_char *p) |
185 | { |
186 | u_char sum = 0; |
187 | int i; |
188 | |
189 | for (i = 0; i < 11; i++) { |
190 | sum = (sum << 7)|(sum >> 1); |
191 | sum += p[i]; |
192 | } |
193 | |
194 | return (sum); |
195 | } |
196 | |
197 | |
198 | |
199 | |
200 | static char longName[DOSLONGNAMELEN] = ""; |
201 | static u_char *buffer = NULL; |
202 | static u_char *delbuf = NULL; |
203 | |
204 | static struct dosDirEntry *rootDir; |
205 | static struct dosDirEntry *lostDir; |
206 | |
207 | |
208 | |
209 | |
210 | int |
211 | resetDosDirSection(struct bootblock *boot, struct fatEntry *fat) |
212 | { |
213 | int b1, b2; |
214 | int ret = FSOK; |
215 | |
216 | b1 = boot->RootDirEnts * 32; |
217 | b2 = boot->SecPerClust * boot->BytesPerSec; |
218 | |
219 | if (!(buffer = malloc(b1 > b2 ? b1 : b2)) |
220 | || !(delbuf = malloc(b2)) |
221 | || !(rootDir = newDosDirEntry())) { |
222 | xperror("No space for directory"); |
223 | goto fail; |
224 | } |
225 | (void)memset(rootDir, 0, sizeof *rootDir); |
226 | if (boot->flags & FAT32) { |
227 | if (boot->RootCl < CLUST_FIRST || boot->RootCl >= boot->NumClusters) { |
228 | pfatal("Root directory starts with cluster out of range(%u)\n", |
229 | boot->RootCl); |
230 | goto fail; |
231 | } |
232 | if (fat[boot->RootCl].head != boot->RootCl) { |
233 | pfatal("Root directory doesn't start a cluster chain\n"); |
234 | goto fail; |
235 | } |
236 | |
237 | fat[boot->RootCl].flags |= FAT_USED; |
238 | rootDir->head = boot->RootCl; |
239 | } |
240 | |
241 | return (ret); |
242 | fail: |
243 | finishDosDirSection(); |
244 | return (FSFATAL); |
245 | } |
246 | |
247 | |
248 | |
249 | |
250 | void |
251 | finishDosDirSection(void) |
252 | { |
253 | struct dirTodoNode *p, *np; |
254 | struct dosDirEntry *d, *nd; |
255 | |
256 | for (p = pendingDirectories; p; p = np) { |
257 | np = p->next; |
258 | freeDirTodo(p); |
259 | } |
260 | pendingDirectories = 0; |
261 | for (d = rootDir; d; d = nd) { |
262 | if ((nd = d->child) != NULL) { |
263 | d->child = 0; |
264 | continue; |
265 | } |
266 | if (!(nd = d->next)) |
267 | nd = d->parent; |
268 | freeDosDirEntry(d); |
269 | } |
270 | rootDir = lostDir = NULL; |
271 | free(buffer); |
272 | free(delbuf); |
273 | buffer = NULL; |
274 | delbuf = NULL; |
275 | } |
276 | |
277 | |
278 | |
279 | |
280 | static int |
281 | delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl, |
282 | int startoff, cl_t endcl, int endoff, int notlast) |
283 | { |
284 | u_char *s, *e; |
285 | off_t off; |
286 | int clsz = boot->SecPerClust * boot->BytesPerSec; |
287 | |
288 | s = delbuf + startoff; |
289 | e = delbuf + clsz; |
290 | while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) { |
291 | if (startcl == endcl) { |
292 | if (notlast) |
293 | break; |
294 | e = delbuf + endoff; |
295 | } |
296 | off = startcl * boot->SecPerClust + boot->ClusterOffset; |
297 | off *= boot->BytesPerSec; |
298 | if (lseek(f, off, SEEK_SET) != off |
299 | || read(f, delbuf, clsz) != clsz) { |
300 | xperror("Unable to read directory"); |
301 | return (FSFATAL); |
302 | } |
303 | while (s < e) { |
304 | *s = SLOT_DELETED; |
305 | s += 32; |
306 | } |
307 | if (lseek(f, off, SEEK_SET) != off |
308 | || write(f, delbuf, clsz) != clsz) { |
309 | xperror("Unable to write directory"); |
310 | return (FSFATAL); |
311 | } |
312 | if (startcl == endcl) |
313 | break; |
314 | startcl = fat[startcl].next; |
315 | s = delbuf; |
316 | } |
317 | return (FSOK); |
318 | } |
319 | |
320 | static int |
321 | removede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start, |
322 | u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type) |
323 | { |
324 | switch (type) { |
325 | case 0: |
326 | pwarn("Invalid long filename entry for %s\n", path); |
327 | break; |
328 | case 1: |
329 | pwarn("Invalid long filename entry at end of directory %s\n", path); |
330 | break; |
331 | case 2: |
332 | pwarn("Invalid long filename entry for volume label\n"); |
333 | break; |
334 | } |
335 | if (ask(0, "Remove")) { |
336 | if (startcl != curcl) { |
337 | if (delete(f, boot, fat, |
338 | startcl, start - buffer, |
339 | endcl, end - buffer, |
340 | endcl == curcl) == FSFATAL) |
341 | return (FSFATAL); |
342 | start = buffer; |
343 | } |
344 | if (endcl == curcl) |
345 | for (; start < end; start += 32) |
346 | *start = SLOT_DELETED; |
347 | return (FSDIRMOD); |
348 | } |
349 | return (FSERROR); |
350 | } |
351 | |
352 | |
353 | |
354 | |
355 | static int |
356 | checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p, |
357 | struct dosDirEntry *dir) |
358 | { |
359 | |
360 | |
361 | |
362 | u_int32_t physicalSize; |
363 | |
364 | if (dir->head == CLUST_FREE) |
365 | physicalSize = 0; |
366 | else { |
367 | if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters) |
368 | return (FSERROR); |
369 | physicalSize = fat[dir->head].length * boot->ClusterSize; |
370 | } |
371 | if (physicalSize < dir->size) { |
372 | pwarn("size of %s is %u, should at most be %u\n", |
373 | fullpath(dir), dir->size, physicalSize); |
374 | if (ask(1, "Truncate")) { |
375 | dir->size = physicalSize; |
376 | p[28] = (u_char)physicalSize; |
377 | p[29] = (u_char)(physicalSize >> 8); |
378 | p[30] = (u_char)(physicalSize >> 16); |
379 | p[31] = (u_char)(physicalSize >> 24); |
380 | return (FSDIRMOD); |
381 | } else |
382 | return (FSERROR); |
383 | } else if (physicalSize - dir->size >= boot->ClusterSize) { |
384 | pwarn("%s has too many clusters allocated\n", |
385 | fullpath(dir)); |
386 | if (ask(1, "Drop superfluous clusters")) { |
387 | cl_t cl; |
388 | u_int32_t len, sz; |
389 | |
390 | len = sz = 0; |
391 | for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;) { |
392 | cl = fat[cl].next; |
393 | len++; |
394 | } |
395 | clearchain(boot, fat, fat[cl].next); |
396 | fat[cl].next = CLUST_EOF; |
397 | fat[dir->head].length = len; |
398 | return (FSFATMOD); |
399 | } else |
400 | return (FSERROR); |
401 | } |
402 | return (FSOK); |
403 | } |
404 | |
405 | |
406 | |
407 | |
408 | |
409 | |
410 | |
411 | static int |
412 | readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat, |
413 | struct dosDirEntry *dir) |
414 | { |
415 | struct dosDirEntry dirent, *d; |
416 | u_char *p, *vallfn, *invlfn, *empty; |
417 | off_t off; |
418 | int i, j, k, last; |
419 | cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0; |
420 | char *t; |
421 | u_int lidx = 0; |
422 | int shortSum; |
423 | int mod = FSOK; |
424 | #define THISMOD 0x8000 /* Only used within this routine */ |
425 | |
426 | cl = dir->head; |
427 | if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) { |
| 1 | Assuming field 'parent' is non-null | |
|
| 2 | | Assuming 'cl' is >= CLUST_FIRST | |
|
| 3 | | Assuming 'cl' is < field 'NumClusters' | |
|
| |
428 | |
429 | |
430 | |
431 | return (FSOK); |
432 | } |
433 | shortSum = -1; |
434 | vallfn = invlfn = empty = NULL; |
| 5 | | Null pointer value stored to 'empty' | |
|
435 | do { |
436 | if (!(boot->flags & FAT32) && !dir->parent) { |
| 6 | | Assuming the condition is false | |
|
437 | last = boot->RootDirEnts * 32; |
438 | off = boot->ResSectors + boot->FATs * boot->FATsecs; |
439 | } else { |
440 | last = boot->SecPerClust * boot->BytesPerSec; |
441 | off = cl * boot->SecPerClust + boot->ClusterOffset; |
442 | } |
443 | |
444 | off *= boot->BytesPerSec; |
445 | if (lseek(f, off, SEEK_SET) != off |
| 7 | | Assuming the condition is false | |
|
| |
446 | || read(f, buffer, last) != last) { |
| 8 | | Assuming the condition is false | |
|
447 | xperror("Unable to read directory"); |
448 | return (FSFATAL); |
449 | } |
450 | last /= 32; |
451 | |
452 | |
453 | |
454 | for (p = buffer, i = 0; i < last; i++, p += 32) { |
| 10 | | Assuming 'i' is < 'last' | |
|
| 11 | | Loop condition is true. Entering loop body | |
|
455 | if (dir->fsckflags & DIREMPWARN) { |
| 12 | | Assuming the condition is false | |
|
| |
456 | *p = SLOT_EMPTY; |
457 | continue; |
458 | } |
459 | |
460 | if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { |
| 14 | | Assuming the condition is false | |
|
| 15 | | Assuming the condition is false | |
|
| |
461 | if (*p == SLOT_EMPTY) { |
462 | dir->fsckflags |= DIREMPTY; |
463 | empty = p; |
464 | empcl = cl; |
465 | } |
466 | continue; |
467 | } |
468 | |
469 | if (dir->fsckflags & DIREMPTY) { |
| 17 | | Assuming the condition is true | |
|
| |
470 | if (!(dir->fsckflags & DIREMPWARN)) { |
| |
471 | pwarn("%s has entries after end of directory\n", |
472 | fullpath(dir)); |
473 | if (ask(1, "Extend")) { |
| 20 | | Assuming the condition is true | |
|
| |
474 | u_char *q; |
475 | |
476 | dir->fsckflags &= ~DIREMPTY; |
477 | if (delete(f, boot, fat, |
| 22 | | Assuming the condition is false | |
|
| |
478 | empcl, empty - buffer, |
479 | cl, p - buffer, 1) == FSFATAL) |
480 | return (FSFATAL); |
481 | q = empcl == cl ? empty : buffer; |
| 24 | | Assuming 'empcl' is equal to 'cl' | |
|
| |
| 26 | | Null pointer value stored to 'q' | |
|
482 | for (; q < p; q += 32) |
| 27 | | Loop condition is true. Entering loop body | |
|
483 | *q = SLOT_DELETED; |
| 28 | | Dereference of null pointer (loaded from variable 'q') |
|
484 | mod |= THISMOD|FSDIRMOD; |
485 | } else if (ask(0, "Truncate")) |
486 | dir->fsckflags |= DIREMPWARN; |
487 | } |
488 | if (dir->fsckflags & DIREMPWARN) { |
489 | *p = SLOT_DELETED; |
490 | mod |= THISMOD|FSDIRMOD; |
491 | continue; |
492 | } else if (dir->fsckflags & DIREMPTY) |
493 | mod |= FSERROR; |
494 | empty = NULL; |
495 | } |
496 | |
497 | if (p[11] == ATTR_WIN95) { |
498 | if (*p & LRFIRST) { |
499 | if (shortSum != -1) { |
500 | if (!invlfn) { |
501 | invlfn = vallfn; |
502 | invcl = valcl; |
503 | } |
504 | } |
505 | (void)memset(longName, 0, sizeof longName); |
506 | shortSum = p[13]; |
507 | vallfn = p; |
508 | valcl = cl; |
509 | } else if (shortSum != p[13] |
510 | || lidx != (*p & LRNOMASK)) { |
511 | if (!invlfn) { |
512 | invlfn = vallfn; |
513 | invcl = valcl; |
514 | } |
515 | if (!invlfn) { |
516 | invlfn = p; |
517 | invcl = cl; |
518 | } |
519 | vallfn = NULL; |
520 | } |
521 | lidx = *p & LRNOMASK; |
522 | if (lidx == 0) { |
523 | if (!invlfn) { |
524 | invlfn = vallfn; |
525 | invcl = valcl; |
526 | } |
527 | vallfn = NULL; |
528 | continue; |
529 | } |
530 | t = longName + --lidx * 13; |
531 | for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) { |
532 | if (!p[k] && !p[k + 1]) |
533 | break; |
534 | *t++ = p[k]; |
535 | |
536 | |
537 | |
538 | if (p[k + 1]) |
539 | t[-1] = '?'; |
540 | } |
541 | if (k >= 11) |
542 | for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) { |
543 | if (!p[k] && !p[k + 1]) |
544 | break; |
545 | *t++ = p[k]; |
546 | if (p[k + 1]) |
547 | t[-1] = '?'; |
548 | } |
549 | if (k >= 26) |
550 | for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) { |
551 | if (!p[k] && !p[k + 1]) |
552 | break; |
553 | *t++ = p[k]; |
554 | if (p[k + 1]) |
555 | t[-1] = '?'; |
556 | } |
557 | if (t >= longName + sizeof(longName)) { |
558 | pwarn("long filename too long\n"); |
559 | if (!invlfn) { |
560 | invlfn = vallfn; |
561 | invcl = valcl; |
562 | } |
563 | vallfn = NULL; |
564 | } |
565 | if (p[26] | (p[27] << 8)) { |
566 | pwarn("long filename record cluster start != 0\n"); |
567 | if (!invlfn) { |
568 | invlfn = vallfn; |
569 | invcl = cl; |
570 | } |
571 | vallfn = NULL; |
572 | } |
573 | continue; |
574 | |
575 | } |
576 | |
577 | |
578 | |
579 | |
580 | (void)memset(&dirent, 0, sizeof dirent); |
581 | |
582 | |
583 | |
584 | |
585 | |
586 | dirent.flags = p[11]; |
587 | |
588 | |
589 | |
590 | |
591 | for (j = 0; j < 8; j++) |
592 | dirent.name[j] = p[j]; |
593 | dirent.name[8] = '\0'; |
594 | for (k = 7; k >= 0 && dirent.name[k] == ' '; k--) |
595 | dirent.name[k] = '\0'; |
596 | if (k < 0) |
597 | k = 0; |
598 | if (dirent.name[k] != '\0') |
599 | k++; |
600 | if (dirent.name[0] == SLOT_E5) |
601 | dirent.name[0] = 0xe5; |
602 | |
603 | if (dirent.flags & ATTR_VOLUME) { |
604 | if (vallfn || invlfn) { |
605 | mod |= removede(f, boot, fat, |
606 | invlfn ? invlfn : vallfn, p, |
607 | invlfn ? invcl : valcl, -1, 0, |
608 | fullpath(dir), 2); |
609 | vallfn = NULL; |
610 | invlfn = NULL; |
611 | } |
612 | continue; |
613 | } |
614 | |
615 | if (p[8] != ' ') |
616 | dirent.name[k++] = '.'; |
617 | for (j = 0; j < 3; j++) |
618 | dirent.name[k++] = p[j+8]; |
619 | dirent.name[k] = '\0'; |
620 | for (k--; k >= 0 && dirent.name[k] == ' '; k--) |
621 | dirent.name[k] = '\0'; |
622 | |
623 | if (vallfn && shortSum != calcShortSum(p)) { |
624 | if (!invlfn) { |
625 | invlfn = vallfn; |
626 | invcl = valcl; |
627 | } |
628 | vallfn = NULL; |
629 | } |
630 | dirent.head = p[26] | (p[27] << 8); |
631 | if (boot->ClustMask == CLUST32_MASK) |
632 | dirent.head |= (p[20] << 16) | (p[21] << 24); |
633 | dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24); |
634 | if (vallfn) { |
635 | strlcpy(dirent.lname, longName, sizeof dirent.lname); |
636 | longName[0] = '\0'; |
637 | shortSum = -1; |
638 | } |
639 | |
640 | dirent.parent = dir; |
641 | dirent.next = dir->child; |
642 | |
643 | if (invlfn) { |
644 | mod |= k = removede(f, boot, fat, |
645 | invlfn, vallfn ? vallfn : p, |
646 | invcl, vallfn ? valcl : cl, cl, |
647 | fullpath(&dirent), 0); |
648 | if (mod & FSFATAL) |
649 | return (FSFATAL); |
650 | if (vallfn |
651 | ? (valcl == cl && vallfn != buffer) |
652 | : p != buffer) |
653 | if (k & FSDIRMOD) |
654 | mod |= THISMOD; |
655 | } |
656 | |
657 | vallfn = NULL; |
658 | invlfn = NULL; |
659 | |
660 | if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) { |
661 | if (dirent.head != 0) { |
662 | pwarn("%s has clusters, but size 0\n", |
663 | fullpath(&dirent)); |
664 | if (ask(1, "Drop allocated clusters")) { |
665 | p[26] = p[27] = 0; |
666 | if (boot->ClustMask == CLUST32_MASK) |
667 | p[20] = p[21] = 0; |
668 | clearchain(boot, fat, dirent.head); |
669 | dirent.head = 0; |
670 | mod |= THISMOD|FSDIRMOD|FSFATMOD; |
671 | } else |
672 | mod |= FSERROR; |
673 | } |
674 | } else if (dirent.head == 0 |
675 | && !strcmp(dirent.name, "..") |
676 | && dir->parent |
677 | && !dir->parent->parent) { |
678 | |
679 | |
680 | |
681 | } else if (dirent.head < CLUST_FIRST |
682 | || dirent.head >= boot->NumClusters |
683 | || fat[dirent.head].next == CLUST_FREE |
684 | || (fat[dirent.head].next >= CLUST_RSRVD |
685 | && fat[dirent.head].next < CLUST_EOFS) |
686 | || fat[dirent.head].head != dirent.head) { |
687 | if (dirent.head == 0) |
688 | pwarn("%s has no clusters\n", |
689 | fullpath(&dirent)); |
690 | else if (dirent.head < CLUST_FIRST |
691 | || dirent.head >= boot->NumClusters) |
692 | pwarn("%s starts with cluster out of range(%u)\n", |
693 | fullpath(&dirent), |
694 | dirent.head); |
695 | else if (fat[dirent.head].next == CLUST_FREE) |
696 | pwarn("%s starts with free cluster\n", |
697 | fullpath(&dirent)); |
698 | else if (fat[dirent.head].next >= CLUST_RSRVD) |
699 | pwarn("%s starts with cluster marked %s\n", |
700 | fullpath(&dirent), |
701 | rsrvdcltype(fat[dirent.head].next)); |
702 | else |
703 | pwarn("%s doesn't start a cluster chain\n", |
704 | fullpath(&dirent)); |
705 | if (dirent.flags & ATTR_DIRECTORY) { |
706 | if (ask(0, "Remove")) { |
707 | *p = SLOT_DELETED; |
708 | mod |= THISMOD|FSDIRMOD; |
709 | } else |
710 | mod |= FSERROR; |
711 | continue; |
712 | } else { |
713 | if (ask(1, "Truncate")) { |
714 | p[28] = p[29] = p[30] = p[31] = 0; |
715 | p[26] = p[27] = 0; |
716 | if (boot->ClustMask == CLUST32_MASK) |
717 | p[20] = p[21] = 0; |
718 | dirent.size = 0; |
719 | mod |= THISMOD|FSDIRMOD; |
720 | } else |
721 | mod |= FSERROR; |
722 | } |
723 | } |
724 | |
725 | if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters) |
726 | fat[dirent.head].flags |= FAT_USED; |
727 | |
728 | if (dirent.flags & ATTR_DIRECTORY) { |
729 | |
730 | |
731 | |
732 | struct dirTodoNode *n; |
733 | |
734 | if (dirent.size) { |
735 | pwarn("Directory %s has size != 0\n", |
736 | fullpath(&dirent)); |
737 | if (ask(1, "Correct")) { |
738 | p[28] = p[29] = p[30] = p[31] = 0; |
739 | dirent.size = 0; |
740 | mod |= THISMOD|FSDIRMOD; |
741 | } else |
742 | mod |= FSERROR; |
743 | } |
744 | |
745 | |
746 | |
747 | if (strcmp(dirent.name, ".") == 0) { |
748 | if (dirent.head != dir->head) { |
749 | pwarn("`.' entry in %s has incorrect start cluster\n", |
750 | fullpath(dir)); |
751 | if (ask(1, "Correct")) { |
752 | dirent.head = dir->head; |
753 | p[26] = (u_char)dirent.head; |
754 | p[27] = (u_char)(dirent.head >> 8); |
755 | if (boot->ClustMask == CLUST32_MASK) { |
756 | p[20] = (u_char)(dirent.head >> 16); |
757 | p[21] = (u_char)(dirent.head >> 24); |
758 | } |
759 | mod |= THISMOD|FSDIRMOD; |
760 | } else |
761 | mod |= FSERROR; |
762 | } |
763 | continue; |
764 | } |
765 | if (strcmp(dirent.name, "..") == 0) { |
766 | if (dir->parent) { |
767 | if (!dir->parent->parent) { |
768 | if (dirent.head) { |
769 | pwarn("`..' entry in %s has non-zero start cluster\n", |
770 | fullpath(dir)); |
771 | if (ask(1, "Correct")) { |
772 | dirent.head = 0; |
773 | p[26] = p[27] = 0; |
774 | if (boot->ClustMask == CLUST32_MASK) |
775 | p[20] = p[21] = 0; |
776 | mod |= THISMOD|FSDIRMOD; |
777 | } else |
778 | mod |= FSERROR; |
779 | } |
780 | } else if (dirent.head != dir->parent->head) { |
781 | pwarn("`..' entry in %s has incorrect start cluster\n", |
782 | fullpath(dir)); |
783 | if (ask(1, "Correct")) { |
784 | dirent.head = dir->parent->head; |
785 | p[26] = (u_char)dirent.head; |
786 | p[27] = (u_char)(dirent.head >> 8); |
787 | if (boot->ClustMask == CLUST32_MASK) { |
788 | p[20] = (u_char)(dirent.head >> 16); |
789 | p[21] = (u_char)(dirent.head >> 24); |
790 | } |
791 | mod |= THISMOD|FSDIRMOD; |
792 | } else |
793 | mod |= FSERROR; |
794 | } |
795 | } |
796 | continue; |
797 | } |
798 | |
799 | |
800 | if (!(d = newDosDirEntry())) { |
801 | xperror("No space for directory"); |
802 | return (FSFATAL); |
803 | } |
804 | (void)memcpy(d, &dirent, sizeof(struct dosDirEntry)); |
805 | |
806 | dir->child = d; |
807 | |
808 | |
809 | if (!(n = newDirTodo())) { |
810 | xperror("No space for todo list"); |
811 | return (FSFATAL); |
812 | } |
813 | n->next = pendingDirectories; |
814 | n->dir = d; |
815 | pendingDirectories = n; |
816 | } else { |
817 | mod |= k = checksize(boot, fat, p, &dirent); |
818 | if (k & FSDIRMOD) |
819 | mod |= THISMOD; |
820 | } |
821 | boot->NumFiles++; |
822 | } |
823 | if (mod & THISMOD) { |
824 | last *= 32; |
825 | if (lseek(f, off, SEEK_SET) != off |
826 | || write(f, buffer, last) != last) { |
827 | xperror("Unable to write directory"); |
828 | return (FSFATAL); |
829 | } |
830 | mod &= ~THISMOD; |
831 | } |
832 | } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters); |
833 | if (invlfn || vallfn) |
834 | mod |= removede(f, boot, fat, |
835 | invlfn ? invlfn : vallfn, p, |
836 | invlfn ? invcl : valcl, -1, 0, |
837 | fullpath(dir), 1); |
838 | return (mod & ~THISMOD); |
839 | } |
840 | |
841 | int |
842 | handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat) |
843 | { |
844 | int mod; |
845 | |
846 | mod = readDosDirSection(dosfs, boot, fat, rootDir); |
847 | if (mod & FSFATAL) |
848 | return (FSFATAL); |
849 | |
850 | if (mod & FSFATMOD) { |
851 | mod &= ~FSFATMOD; |
852 | mod |= writefat(dosfs, boot, fat); |
853 | } |
854 | |
855 | if (mod & FSFATAL) |
856 | return (FSFATAL); |
857 | |
858 | |
859 | |
860 | |
861 | while (pendingDirectories) { |
862 | struct dosDirEntry *dir = pendingDirectories->dir; |
863 | struct dirTodoNode *n = pendingDirectories->next; |
864 | |
865 | |
866 | |
867 | |
868 | |
869 | freeDirTodo(pendingDirectories); |
870 | pendingDirectories = n; |
871 | |
872 | |
873 | |
874 | |
875 | mod |= readDosDirSection(dosfs, boot, fat, dir); |
876 | if (mod & FSFATAL) |
877 | return (FSFATAL); |
878 | if (mod & FSFATMOD) { |
879 | mod &= ~FSFATMOD; |
880 | mod |= writefat(dosfs, boot, fat); |
881 | } |
882 | if (mod & FSFATAL) |
883 | return (FSFATAL); |
884 | } |
885 | return (mod); |
886 | } |
887 | |
888 | |
889 | |
890 | |
891 | static u_char *lfbuf; |
892 | static cl_t lfcl; |
893 | static off_t lfoff; |
894 | |
895 | int |
896 | reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head) |
897 | { |
898 | struct dosDirEntry d; |
899 | u_char *p; |
900 | |
901 | if (!ask(1, "Reconnect")) |
902 | return FSERROR; |
903 | |
904 | if (!lostDir) { |
905 | for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) { |
906 | if (!strcmp(lostDir->name, LOSTDIR)) |
907 | break; |
908 | } |
909 | if (!lostDir) { |
910 | pwarn("No %s directory\n", LOSTDIR); |
911 | return (FSERROR); |
912 | } |
913 | } |
914 | if (!lfbuf) { |
915 | lfbuf = malloc(boot->ClusterSize); |
916 | if (!lfbuf) { |
917 | xperror("No space for buffer"); |
918 | return (FSFATAL); |
919 | } |
920 | p = NULL; |
921 | } else |
922 | p = lfbuf; |
923 | while (1) { |
924 | if (p) |
925 | for (; p < lfbuf + boot->ClusterSize; p += 32) |
926 | if (*p == SLOT_EMPTY |
927 | || *p == SLOT_DELETED) |
928 | break; |
929 | if (p && p < lfbuf + boot->ClusterSize) |
930 | break; |
931 | lfcl = p ? fat[lfcl].next : lostDir->head; |
932 | if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { |
933 | |
934 | pwarn("No space in %s\n", LOSTDIR); |
935 | return (FSERROR); |
936 | } |
937 | lfoff = lfcl * boot->ClusterSize |
938 | + boot->ClusterOffset * boot->BytesPerSec; |
939 | if (lseek(dosfs, lfoff, SEEK_SET) != lfoff |
940 | || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { |
941 | xperror("could not read LOST.DIR"); |
942 | return (FSFATAL); |
943 | } |
944 | p = lfbuf; |
945 | } |
946 | |
947 | boot->NumFiles++; |
948 | |
949 | (void)memset(&d, 0, sizeof d); |
950 | snprintf(d.name, sizeof d.name, "%u", head); |
951 | d.flags = 0; |
952 | d.head = head; |
953 | d.size = fat[head].length * boot->ClusterSize; |
954 | |
955 | (void)memset(p, 0, 32); |
956 | (void)memset(p, ' ', 11); |
957 | (void)memcpy(p, d.name, strlen(d.name)); |
958 | p[26] = (u_char)d.head; |
959 | p[27] = (u_char)(d.head >> 8); |
960 | if (boot->ClustMask == CLUST32_MASK) { |
961 | p[20] = (u_char)(d.head >> 16); |
962 | p[21] = (u_char)(d.head >> 24); |
963 | } |
964 | p[28] = (u_char)d.size; |
965 | p[29] = (u_char)(d.size >> 8); |
966 | p[30] = (u_char)(d.size >> 16); |
967 | p[31] = (u_char)(d.size >> 24); |
968 | fat[head].flags |= FAT_USED; |
969 | if (lseek(dosfs, lfoff, SEEK_SET) != lfoff |
970 | || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { |
971 | xperror("could not write LOST.DIR"); |
972 | return (FSFATAL); |
973 | } |
974 | return (FSDIRMOD); |
975 | } |
976 | |
977 | void |
978 | finishlf(void) |
979 | { |
980 | free(lfbuf); |
981 | lfbuf = NULL; |
982 | } |