From: Tomohiro Kusumi Date: Sat, 14 Sep 2019 18:29:38 +0000 (+0900) Subject: sbin/fsck_msdosfs: Bring in freebsd/freebsd@437a93c1 (test ./.. for dir dirents) X-Git-Tag: v5.8.0rc1~892 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/f2a8e16c6664d6d6de5d3c76d098044f1805829d sbin/fsck_msdosfs: Bring in freebsd/freebsd@437a93c1 (test ./.. for dir dirents) Also bring in freebsd/freebsd@76844b29. --- diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c index 4046096c50..5a44095e83 100644 --- a/sbin/fsck_msdosfs/dir.c +++ b/sbin/fsck_msdosfs/dir.c @@ -415,6 +415,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) != 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 @@ -461,9 +530,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; @@ -806,6 +872,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 */