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.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/* $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 non-null
2
Assuming 'cl' is >= CLUST_FIRST
3
Assuming 'cl' is < field 'NumClusters'
4
Taking false branch
428 /*
429 * Already handled somewhere else.
430 */
431 return (FSOK0);
432 }
433 shortSum = -1;
434 vallfn = invlfn = empty = NULL((void *)0);
5
Null pointer value stored to 'empty'
435 do {
436 if (!(boot->flags & FAT321) && !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_SET0) != off
7
Assuming the condition is false
9
Taking false branch
446 || read(f, buffer, last) != last) {
8
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) {
10
Assuming 'i' is < 'last'
11
Loop condition is true. Entering loop body
455 if (dir->fsckflags & DIREMPWARN2) {
12
Assuming the condition is false
13
Taking false branch
456 *p = SLOT_EMPTY0x00;
457 continue;
458 }
459
460 if (*p == SLOT_EMPTY0x00 || *p == SLOT_DELETED0xe5) {
14
Assuming the condition is false
15
Assuming the condition is false
16
Taking false branch
461 if (*p == SLOT_EMPTY0x00) {
462 dir->fsckflags |= DIREMPTY1;
463 empty = p;
464 empcl = cl;
465 }
466 continue;
467 }
468
469 if (dir->fsckflags & DIREMPTY1) {
17
Assuming the condition is true
18
Taking true branch
470 if (!(dir->fsckflags & DIREMPWARN2)) {
19
Taking true branch
471 pwarn("%s has entries after end of directory\n",
472 fullpath(dir));
473 if (ask(1, "Extend")) {
20
Assuming the condition is true
21
Taking true branch
474 u_char *q;
475
476 dir->fsckflags &= ~DIREMPTY1;
477 if (delete(f, boot, fat,
22
Assuming the condition is false
23
Taking false branch
478 empcl, empty - buffer,
479 cl, p - buffer, 1) == FSFATAL16)
480 return (FSFATAL16);
481 q = empcl == cl ? empty : buffer;
24
Assuming 'empcl' is equal to 'cl'
25
'?' condition is true
26
Null pointer value stored to 'q'
482 for (; q
26.1
'q' is < 'p'
< p; q += 32)
27
Loop condition is true. Entering loop body
483 *q = SLOT_DELETED0xe5;
28
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}