diff options
author | Xin Li <delphij@google.com> | 2019-04-11 12:17:49 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-04-11 12:17:49 -0700 |
commit | 1b74b8e3fc0f97901c562eabad957e055cce21db (patch) | |
tree | 4b9be7bd71a56caf18b724fa7764950eeb4ffff5 | |
parent | 7a509b94526bce11e5b035af1356ce28c6c0a3fc (diff) | |
parent | 29bb69d1d6a848f2ce51d103738d872d6268ab54 (diff) | |
download | fsck_msdos-1b74b8e3fc0f97901c562eabad957e055cce21db.tar.gz |
Merge changes Ieddc1113,Ia12217b3 am: 2c681345e8 am: b07a349b04
am: 29bb69d1d6
Change-Id: I658e99b06968f0c7ce27d6e38520f96b42e3d242
-rw-r--r-- | dir.c | 122 | ||||
-rw-r--r-- | fat.c | 14 |
2 files changed, 128 insertions, 8 deletions
@@ -35,6 +35,8 @@ static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ +#include <assert.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -329,8 +331,11 @@ delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl, } off = startcl * boot->bpbSecPerClust + boot->ClusterOffset; off *= boot->bpbBytesPerSec; - if (lseek(f, off, SEEK_SET) != off - || read(f, delbuf, clsz) != clsz) { + if (lseek(f, off, SEEK_SET) != off) { + perr("Unable to lseek to %" PRId64, off); + return FSFATAL; + } + if (read(f, delbuf, clsz) != clsz) { perr("Unable to read directory"); return FSFATAL; } @@ -338,8 +343,11 @@ delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl, *s = SLOT_DELETED; s += 32; } - if (lseek(f, off, SEEK_SET) != off - || write(f, delbuf, clsz) != clsz) { + if (lseek(f, off, SEEK_SET) != off) { + perr("Unable to lseek to %" PRId64, off); + return FSFATAL; + } + if (write(f, delbuf, clsz) != clsz) { perr("Unable to write directory"); return FSFATAL; } @@ -436,6 +444,75 @@ checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p, return FSOK; } +static const u_char dot_name[11] = ". "; +static const u_char dotdot_name[11] = ".. "; + +/* + * Basic sanity check if the subdirectory have good '.' and '..' entries, + * and they are directory entries. Further sanity checks are performed + * when we traverse into it. + */ +static int +check_subdirectory(int f, struct bootblock *boot, struct dosDirEntry *dir) +{ + u_char *buf, *cp; + off_t off; + cl_t cl; + int retval = FSOK; + + cl = dir->head; + if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) { + return FSERROR; + } + + if (!(boot->flags & FAT32) && !dir->parent) { + off = boot->bpbResSectors + boot->bpbFATs * + boot->FATsecs; + } else { + off = cl * boot->bpbSecPerClust + boot->ClusterOffset; + } + + /* + * We only need to check the first two entries of the directory, + * which is found in the first sector of the directory entry, + * so read in only the first sector. + */ + buf = malloc(boot->bpbBytesPerSec); + if (buf == NULL) { + perr("No space for directory buffer (%u)", + boot->bpbBytesPerSec); + return FSFATAL; + } + + off *= boot->bpbBytesPerSec; + if (lseek(f, off, SEEK_SET) != off || + read(f, buf, boot->bpbBytesPerSec) != (ssize_t)boot->bpbBytesPerSec) { + perr("Unable to read directory"); + free(buf); + return FSFATAL; + } + + /* + * Both `.' and `..' must be present and be the first two entries + * and be ATTR_DIRECTORY of a valid subdirectory. + */ + cp = buf; + if (memcmp(cp, dot_name, sizeof(dot_name)) != 0 || + (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { + pwarn("%s: Incorrect `.' for %s.\n", __func__, dir->name); + retval |= FSERROR; + } + cp += 32; + if (memcmp(cp, dotdot_name, sizeof(dotdot_name)) != 0 || + (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { + pwarn("%s: Incorrect `..' for %s. \n", __func__, dir->name); + retval |= FSERROR; + } + + free(buf); + return retval; +} + /* * Read a directory and * - resolve long name records @@ -483,9 +560,6 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat, return FSFATAL; } last /= 32; - /* - * Check `.' and `..' entries here? XXX - */ for (p = buffer, i = 0; i < last; i++, p += 32) { if (dir->fsckflags & DIREMPWARN) { *p = SLOT_EMPTY; @@ -513,7 +587,8 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat, empcl, empty - buffer, cl, p - buffer, 1) == FSFATAL) return FSFATAL; - q = empcl == cl ? empty : buffer; + q = ((empcl == cl) ? empty : buffer); + assert(q != NULL); for (; q < p; q += 32) *q = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; @@ -821,6 +896,36 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat, } } continue; + } else { + /* + * Only one directory entry can point + * to dir->head, it's '.'. + */ + if (dirent.head == dir->head) { + pwarn("%s entry in %s has incorrect start cluster\n", + dirent.name, fullpath(dir)); + if (ask(1, "Remove")) { + *p = SLOT_DELETED; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + continue; + } else if ((check_subdirectory(f, boot, + &dirent) & FSERROR) == FSERROR) { + /* + * A subdirectory should have + * a dot (.) entry and a dot-dot + * (..) entry of ATTR_DIRECTORY, + * we will inspect further when + * traversing into it. + */ + if (ask(1, "Remove")) { + *p = SLOT_DELETED; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + continue; + } } /* create directory tree node */ @@ -964,6 +1069,7 @@ reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head) if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { /* Extend LOSTDIR? XXX */ pwarn("No space in %s\n", LOSTDIR); + lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0; return FSERROR; } lfoff = lfcl * boot->ClusterSize @@ -705,6 +705,20 @@ checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) ret = 1; } } + if (boot->FSNext != 0xffffffffU && + (boot->FSNext >= boot->NumClusters || + (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) { + pwarn("Next free cluster in FSInfo block (%u) %s\n", + boot->FSNext, + (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free"); + if (ask(1, "fix")) + for (head = CLUST_FIRST; head < boot->NumClusters; head++) + if (fat[head].next == CLUST_FREE) { + boot->FSNext = head; + ret = 1; + break; + } + } if (ret) mod |= writefsinfo(dosfs, boot); } |