Bug Summary

File:src/sbin/fsck_msdos/dir.c
Warning:line 483, column 11
Dereference of null pointer (loaded from variable 'q')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name dir.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -I /usr/src/sbin/fsck_msdos/../fsck -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/sbin/fsck_msdos/dir.c
1/* $OpenBSD: dir.c,v 1.31 2017/06/27 12:10:21 jsg Exp $ */
2/* $NetBSD: dir.c,v 1.11 1997/10/17 11:19:35 ws Exp $ */
3
4/*
5 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
6 * Copyright (c) 1995 Martin Husemann
7 * Some structure declaration borrowed from Paul Popelka
8 * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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_EMPTY0x00 0x00 /* slot has never been used */
42#define SLOT_E50x05 0x05 /* the real value is 0xe5 */
43#define SLOT_DELETED0xe5 0xe5 /* file in this slot deleted */
44
45#define ATTR_NORMAL0x00 0x00 /* normal file */
46#define ATTR_READONLY0x01 0x01 /* file is readonly */
47#define ATTR_HIDDEN0x02 0x02 /* file is hidden */
48#define ATTR_SYSTEM0x04 0x04 /* file is a system file */
49#define ATTR_VOLUME0x08 0x08 /* entry is a volume label */
50#define ATTR_DIRECTORY0x10 0x10 /* entry is a directory name */
51#define ATTR_ARCHIVE0x20 0x20 /* file is new or modified */
52
53#define ATTR_WIN950x0f 0x0f /* long name record */
54
55/*
56 * This is the format of the contents of the deTime field in the direntry
57 * structure.
58 * We don't use bitfields because we don't know how compilers for
59 * arbitrary machines will lay them out.
60 */
61#define DT_2SECONDS_MASK0x1F 0x1F /* seconds divided by 2 */
62#define DT_2SECONDS_SHIFT0 0
63#define DT_MINUTES_MASK0x7E0 0x7E0 /* minutes */
64#define DT_MINUTES_SHIFT5 5
65#define DT_HOURS_MASK0xF800 0xF800 /* hours */
66#define DT_HOURS_SHIFT11 11
67
68/*
69 * This is the format of the contents of the deDate field in the direntry
70 * structure.
71 */
72#define DD_DAY_MASK0x1F 0x1F /* day of month */
73#define DD_DAY_SHIFT0 0
74#define DD_MONTH_MASK0x1E0 0x1E0 /* month */
75#define DD_MONTH_SHIFT5 5
76#define DD_YEAR_MASK0xFE00 0xFE00 /* year - 1980 */
77#define DD_YEAR_SHIFT9 9
78
79/* dir.c */
80static struct dosDirEntry *newDosDirEntry(void);
81static void freeDosDirEntry(struct dosDirEntry *);
82static struct dirTodoNode *newDirTodo(void);
83static void freeDirTodo(struct dirTodoNode *);
84static char *fullpath(struct dosDirEntry *);
85static u_char calcShortSum(u_char *);
86static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int,
87 cl_t, int, int);
88static int removede(int, struct bootblock *, struct fatEntry *, u_char *,
89 u_char *, cl_t, cl_t, cl_t, char *, int);
90static int checksize(struct bootblock *, struct fatEntry *, u_char *,
91 struct dosDirEntry *);
92static int readDosDirSection(int, struct bootblock *, struct fatEntry *,
93 struct dosDirEntry *);
94
95/*
96 * Manage free dosDirEntry structures.
97 */
98static struct dosDirEntry *freede;
99
100static struct dosDirEntry *
101newDosDirEntry(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
113static void
114freeDosDirEntry(struct dosDirEntry *de)
115{
116 de->next = freede;
117 freede = de;
118}
119
120/*
121 * The same for dirTodoNode structures.
122 */
123static struct dirTodoNode *freedt;
124
125static struct dirTodoNode *
126newDirTodo(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
138static void
139freeDirTodo(struct dirTodoNode *dt)
140{
141 dt->next = freedt;
142 freedt = dt;
143}
144
145/*
146 * The stack of unread directories
147 */
148static struct dirTodoNode *pendingDirectories = NULL((void *)0);
149
150/*
151 * Return the full pathname for a directory entry.
152 */
153static char *
154fullpath(struct dosDirEntry *dir)
155{
156 static char namebuf[PATH_MAX1024 + 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 /* cf dosDirEntry, sizeof(lname) < MAXPATHLEN, so test is safe */
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 * Calculate a checksum over an 8.3 alias name
182 */
183static u_char
184calcShortSum(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); /* rotate right */
191 sum += p[i];
192 }
193
194 return (sum);
195}
196
197/*
198 * Global variables temporarily used during a directory scan
199 */
200static char longName[DOSLONGNAMELEN256] = "";
201static u_char *buffer = NULL((void *)0);
202static u_char *delbuf = NULL((void *)0);
203
204static struct dosDirEntry *rootDir;
205static struct dosDirEntry *lostDir;
206
207/*
208 * Init internal state for a new directory scan.
209 */
210int
211resetDosDirSection(struct bootblock *boot, struct fatEntry *fat)
212{
213 int b1, b2;
214 int ret = FSOK0;
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 & FAT321) {
227 if (boot->RootCl < CLUST_FIRST2 || 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_USED1;
238 rootDir->head = boot->RootCl;
239 }
240
241 return (ret);
242fail:
243 finishDosDirSection();
244 return (FSFATAL16);
245}
246
247/*
248 * Cleanup after a directory scan
249 */
250void
251finishDosDirSection(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((void *)0)) {
263 d->child = 0;
264 continue;
265 }
266 if (!(nd = d->next))
267 nd = d->parent;
268 freeDosDirEntry(d);
269 }
270 rootDir = lostDir = NULL((void *)0);
271 free(buffer);
272 free(delbuf);
273 buffer = NULL((void *)0);
274 delbuf = NULL((void *)0);
275}
276
277/*
278 * Delete directory entries between startcl, startoff and endcl, endoff.
279 */
280static int
281delete(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_FIRST2 && 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_SET0) != off
299 || read(f, delbuf, clsz) != clsz) {
300 xperror("Unable to read directory");
301 return (FSFATAL16);
302 }
303 while (s < e) {
304 *s = SLOT_DELETED0xe5;
305 s += 32;
306 }
307 if (lseek(f, off, SEEK_SET0) != off
308 || write(f, delbuf, clsz) != clsz) {
309 xperror("Unable to write directory");
310 return (FSFATAL16);
311 }
312 if (startcl == endcl)
313 break;
314 startcl = fat[startcl].next;
315 s = delbuf;
316 }
317 return (FSOK0);
318}
319
320static int
321removede(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) == FSFATAL16)
341 return (FSFATAL16);
342 start = buffer;
343 }
344 if (endcl == curcl)
345 for (; start < end; start += 32)
346 *start = SLOT_DELETED0xe5;
347 return (FSDIRMOD2);
348 }
349 return (FSERROR8);
350}
351
352/*
353 * Check an in-memory file entry
354 */
355static int
356checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
357 struct dosDirEntry *dir)
358{
359 /*
360 * Check size on ordinary files
361 */
362 u_int32_t physicalSize;
363
364 if (dir->head == CLUST_FREE0)
365 physicalSize = 0;
366 else {
367 if (dir->head < CLUST_FIRST2 || dir->head >= boot->NumClusters)
368 return (FSERROR8);
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 (FSDIRMOD2);
381 } else
382 return (FSERROR8);
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_EOF0xffffffff;
397 fat[dir->head].length = len;
398 return (FSFATMOD4);
399 } else
400 return (FSERROR8);
401 }
402 return (FSOK0);
403}
404
405/*
406 * Read a directory and
407 * - resolve long name records
408 * - enter file and directory records into the parent's list
409 * - push directories onto the todo-stack
410 */
411static int
412readDosDirSection(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 = FSOK0;
424#define THISMOD0x8000 0x8000 /* Only used within this routine */
425
426 cl = dir->head;
427 if (dir->parent && (cl < CLUST_FIRST2 || cl >= boot->NumClusters)) {
1
Assuming field 'parent' is null
428 /*
429 * Already handled somewhere else.
430 */
431 return (FSOK0);
432 }
433 shortSum = -1;
434 vallfn = invlfn = empty = NULL((void *)0);
2
Null pointer value stored to 'empty'
435 do {
436 if (!(boot->flags & FAT321) && !dir->parent
3.1
Field 'parent' is null
) {
3
Assuming the condition is true
4
Taking true branch
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_SET0) != off
5
Assuming the condition is false
7
Taking false branch
446 || read(f, buffer, last) != last) {
6
Assuming the condition is false
447 xperror("Unable to read directory");
448 return (FSFATAL16);
449 }
450 last /= 32;
451 /*
452 * Check `.' and `..' entries here? XXX
453 */
454 for (p = buffer, i = 0; i < last; i++, p += 32) {
8
Assuming 'i' is < 'last'
9
Loop condition is true. Entering loop body
16
Assuming 'i' is < 'last'
17
Loop condition is true. Entering loop body
455 if (dir->fsckflags & DIREMPWARN2) {
10
Assuming the condition is false
456 *p = SLOT_EMPTY0x00;
457 continue;
458 }
459
460 if (*p == SLOT_EMPTY0x00 || *p == SLOT_DELETED0xe5) {
11
Assuming the condition is false
12
Assuming the condition is true
13
Taking true branch
18
Assuming the condition is false
19
Assuming the condition is false
20
Taking false branch
461 if (*p == SLOT_EMPTY0x00) {
14
Taking false branch
462 dir->fsckflags |= DIREMPTY1;
463 empty = p;
464 empcl = cl;
465 }
466 continue;
15
Execution continues on line 454
467 }
468
469 if (dir->fsckflags & DIREMPTY1) {
21
Assuming the condition is true
22
Taking true branch
470 if (!(dir->fsckflags & DIREMPWARN2)) {
23
Taking true branch
471 pwarn("%s has entries after end of directory\n",
472 fullpath(dir));
473 if (ask(1, "Extend")) {
24
Assuming the condition is true
25
Taking true branch
474 u_char *q;
475
476 dir->fsckflags &= ~DIREMPTY1;
477 if (delete(f, boot, fat,
26
Assuming the condition is false
478 empcl, empty - buffer,
479 cl, p - buffer, 1) == FSFATAL16)
480 return (FSFATAL16);
481 q = empcl == cl ? empty : buffer;
27
Taking false branch
28
Assuming 'empcl' is equal to 'cl'
29
'?' condition is true
30
Null pointer value stored to 'q'
482 for (; q < p; q += 32)
31
Loop condition is true. Entering loop body
483 *q = SLOT_DELETED0xe5;
32
Dereference of null pointer (loaded from variable 'q')
484 mod |= THISMOD0x8000|FSDIRMOD2;
485 } else if (ask(0, "Truncate"))
486 dir->fsckflags |= DIREMPWARN2;
487 }
488 if (dir->fsckflags & DIREMPWARN2) {
489 *p = SLOT_DELETED0xe5;
490 mod |= THISMOD0x8000|FSDIRMOD2;
491 continue;
492 } else if (dir->fsckflags & DIREMPTY1)
493 mod |= FSERROR8;
494 empty = NULL((void *)0);
495 }
496
497 if (p[11] == ATTR_WIN950x0f) {
498 if (*p & LRFIRST0x40) {
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 & LRNOMASK0x1f)) {
511 if (!invlfn) {
512 invlfn = vallfn;
513 invcl = valcl;
514 }
515 if (!invlfn) {
516 invlfn = p;
517 invcl = cl;
518 }
519 vallfn = NULL((void *)0);
520 }
521 lidx = *p & LRNOMASK0x1f;
522 if (lidx == 0) {
523 if (!invlfn) {
524 invlfn = vallfn;
525 invcl = valcl;
526 }
527 vallfn = NULL((void *)0);
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 * Warn about those unusable chars in msdosfs here? XXX
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((void *)0);
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((void *)0);
572 }
573 continue; /* long records don't carry further
574 * information */
575 }
576
577 /*
578 * This is a standard msdosfs directory entry.
579 */
580 (void)memset(&dirent, 0, sizeof dirent);
581
582 /*
583 * it's a short name record, but we need to know
584 * more, so get the flags first.
585 */
586 dirent.flags = p[11];
587
588 /*
589 * Translate from 850 to ISO here XXX
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_E50x05)
601 dirent.name[0] = 0xe5;
602
603 if (dirent.flags & ATTR_VOLUME0x08) {
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((void *)0);
610 invlfn = NULL((void *)0);
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((void *)0);
629 }
630 dirent.head = p[26] | (p[27] << 8);
631 if (boot->ClustMask == CLUST32_MASK0xfffffff)
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 & FSFATAL16)
649 return (FSFATAL16);
650 if (vallfn
651 ? (valcl == cl && vallfn != buffer)
652 : p != buffer)
653 if (k & FSDIRMOD2)
654 mod |= THISMOD0x8000;
655 }
656
657 vallfn = NULL((void *)0); /* not used any longer */
658 invlfn = NULL((void *)0);
659
660 if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY0x10)) {
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_MASK0xfffffff)
667 p[20] = p[21] = 0;
668 clearchain(boot, fat, dirent.head);
669 dirent.head = 0;
670 mod |= THISMOD0x8000|FSDIRMOD2|FSFATMOD4;
671 } else
672 mod |= FSERROR8;
673 }
674 } else if (dirent.head == 0
675 && !strcmp(dirent.name, "..")
676 && dir->parent /* XXX */
677 && !dir->parent->parent) {
678 /*
679 * Do nothing, the parent is the root
680 */
681 } else if (dirent.head < CLUST_FIRST2
682 || dirent.head >= boot->NumClusters
683 || fat[dirent.head].next == CLUST_FREE0
684 || (fat[dirent.head].next >= CLUST_RSRVD0xfffffff6
685 && fat[dirent.head].next < CLUST_EOFS0xfffffff8)
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_FIRST2
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_FREE0)
696 pwarn("%s starts with free cluster\n",
697 fullpath(&dirent));
698 else if (fat[dirent.head].next >= CLUST_RSRVD0xfffffff6)
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_DIRECTORY0x10) {
706 if (ask(0, "Remove")) {
707 *p = SLOT_DELETED0xe5;
708 mod |= THISMOD0x8000|FSDIRMOD2;
709 } else
710 mod |= FSERROR8;
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_MASK0xfffffff)
717 p[20] = p[21] = 0;
718 dirent.size = 0;
719 mod |= THISMOD0x8000|FSDIRMOD2;
720 } else
721 mod |= FSERROR8;
722 }
723 }
724
725 if (dirent.head >= CLUST_FIRST2 && dirent.head < boot->NumClusters)
726 fat[dirent.head].flags |= FAT_USED1;
727
728 if (dirent.flags & ATTR_DIRECTORY0x10) {
729 /*
730 * gather more info for directories
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 |= THISMOD0x8000|FSDIRMOD2;
741 } else
742 mod |= FSERROR8;
743 }
744 /*
745 * handle `.' and `..' specially
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_MASK0xfffffff) {
756 p[20] = (u_char)(dirent.head >> 16);
757 p[21] = (u_char)(dirent.head >> 24);
758 }
759 mod |= THISMOD0x8000|FSDIRMOD2;
760 } else
761 mod |= FSERROR8;
762 }
763 continue;
764 }
765 if (strcmp(dirent.name, "..") == 0) {
766 if (dir->parent) { /* XXX */
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_MASK0xfffffff)
775 p[20] = p[21] = 0;
776 mod |= THISMOD0x8000|FSDIRMOD2;
777 } else
778 mod |= FSERROR8;
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_MASK0xfffffff) {
788 p[20] = (u_char)(dirent.head >> 16);
789 p[21] = (u_char)(dirent.head >> 24);
790 }
791 mod |= THISMOD0x8000|FSDIRMOD2;
792 } else
793 mod |= FSERROR8;
794 }
795 }
796 continue;
797 }
798
799 /* create directory tree node */
800 if (!(d = newDosDirEntry())) {
801 xperror("No space for directory");
802 return (FSFATAL16);
803 }
804 (void)memcpy(d, &dirent, sizeof(struct dosDirEntry));
805 /* link it into the tree */
806 dir->child = d;
807
808 /* Enter this directory into the todo list */
809 if (!(n = newDirTodo())) {
810 xperror("No space for todo list");
811 return (FSFATAL16);
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 & FSDIRMOD2)
819 mod |= THISMOD0x8000;
820 }
821 boot->NumFiles++;
822 }
823 if (mod & THISMOD0x8000) {
824 last *= 32;
825 if (lseek(f, off, SEEK_SET0) != off
826 || write(f, buffer, last) != last) {
827 xperror("Unable to write directory");
828 return (FSFATAL16);
829 }
830 mod &= ~THISMOD0x8000;
831 }
832 } while ((cl = fat[cl].next) >= CLUST_FIRST2 && 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 & ~THISMOD0x8000);
839}
840
841int
842handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat)
843{
844 int mod;
845
846 mod = readDosDirSection(dosfs, boot, fat, rootDir);
847 if (mod & FSFATAL16)
848 return (FSFATAL16);
849
850 if (mod & FSFATMOD4) {
851 mod &= ~FSFATMOD4;
852 mod |= writefat(dosfs, boot, fat); /* delay writing fats? XXX */
853 }
854
855 if (mod & FSFATAL16)
856 return (FSFATAL16);
857
858 /*
859 * process the directory todo list
860 */
861 while (pendingDirectories) {
862 struct dosDirEntry *dir = pendingDirectories->dir;
863 struct dirTodoNode *n = pendingDirectories->next;
864
865 /*
866 * remove TODO entry now, the list might change during
867 * directory reads
868 */
869 freeDirTodo(pendingDirectories);
870 pendingDirectories = n;
871
872 /*
873 * handle subdirectory
874 */
875 mod |= readDosDirSection(dosfs, boot, fat, dir);
876 if (mod & FSFATAL16)
877 return (FSFATAL16);
878 if (mod & FSFATMOD4) {
879 mod &= ~FSFATMOD4;
880 mod |= writefat(dosfs, boot, fat); /* delay writing fats? XXX */
881 }
882 if (mod & FSFATAL16)
883 return (FSFATAL16);
884 }
885 return (mod);
886}
887
888/*
889 * Try to reconnect a FAT chain into dir
890 */
891static u_char *lfbuf;
892static cl_t lfcl;
893static off_t lfoff;
894
895int
896reconnect(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 FSERROR8;
903
904 if (!lostDir) {
905 for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) {
906 if (!strcmp(lostDir->name, LOSTDIR"LOST.DIR"))
907 break;
908 }
909 if (!lostDir) { /* Create LOSTDIR? XXX */
910 pwarn("No %s directory\n", LOSTDIR"LOST.DIR");
911 return (FSERROR8);
912 }
913 }
914 if (!lfbuf) {
915 lfbuf = malloc(boot->ClusterSize);
916 if (!lfbuf) {
917 xperror("No space for buffer");
918 return (FSFATAL16);
919 }
920 p = NULL((void *)0);
921 } else
922 p = lfbuf;
923 while (1) {
924 if (p)
925 for (; p < lfbuf + boot->ClusterSize; p += 32)
926 if (*p == SLOT_EMPTY0x00
927 || *p == SLOT_DELETED0xe5)
928 break;
929 if (p && p < lfbuf + boot->ClusterSize)
930 break;
931 lfcl = p ? fat[lfcl].next : lostDir->head;
932 if (lfcl < CLUST_FIRST2 || lfcl >= boot->NumClusters) {
933 /* Extend LOSTDIR? XXX */
934 pwarn("No space in %s\n", LOSTDIR"LOST.DIR");
935 return (FSERROR8);
936 }
937 lfoff = lfcl * boot->ClusterSize
938 + boot->ClusterOffset * boot->BytesPerSec;
939 if (lseek(dosfs, lfoff, SEEK_SET0) != lfoff
940 || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
941 xperror("could not read LOST.DIR");
942 return (FSFATAL16);
943 }
944 p = lfbuf;
945 }
946
947 boot->NumFiles++;
948 /* Ensure uniqueness of entry here! XXX */
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_MASK0xfffffff) {
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_USED1;
969 if (lseek(dosfs, lfoff, SEEK_SET0) != lfoff
970 || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
971 xperror("could not write LOST.DIR");
972 return (FSFATAL16);
973 }
974 return (FSDIRMOD2);
975}
976
977void
978finishlf(void)
979{
980 free(lfbuf);
981 lfbuf = NULL((void *)0);
982}