Changeset View
Changeset View
Standalone View
Standalone View
head/sbin/fsck_msdosfs/dir.c
Show First 20 Lines • Show All 438 Lines • ▼ Show 20 Lines | if (ask(1, "Drop superfluous clusters")) { | ||||
fat[dir->head].length = len; | fat[dir->head].length = len; | ||||
return FSFATMOD; | return FSFATMOD; | ||||
} else | } else | ||||
return FSERROR; | return FSERROR; | ||||
} | } | ||||
return FSOK; | return FSOK; | ||||
} | } | ||||
static const u_char dot_name[] = { | |||||
'.', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' }; | |||||
static const u_char dotdot_name[] = { | |||||
'.', '.',' ',' ',' ',' ',' ',' ',' ',' ',' ' }; | |||||
/* | /* | ||||
* 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 | * Read a directory and | ||||
* - resolve long name records | * - resolve long name records | ||||
* - enter file and directory records into the parent's list | * - enter file and directory records into the parent's list | ||||
* - push directories onto the todo-stack | * - push directories onto the todo-stack | ||||
*/ | */ | ||||
static int | static int | ||||
readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat, | readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat, | ||||
struct dosDirEntry *dir) | struct dosDirEntry *dir) | ||||
Show All 30 Lines | do { | ||||
off *= boot->bpbBytesPerSec; | off *= boot->bpbBytesPerSec; | ||||
if (lseek(f, off, SEEK_SET) != off | if (lseek(f, off, SEEK_SET) != off | ||||
|| read(f, buffer, last) != last) { | || read(f, buffer, last) != last) { | ||||
perr("Unable to read directory"); | perr("Unable to read directory"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
last /= 32; | last /= 32; | ||||
/* | |||||
* Check `.' and `..' entries here? XXX | |||||
*/ | |||||
for (p = buffer, i = 0; i < last; i++, p += 32) { | for (p = buffer, i = 0; i < last; i++, p += 32) { | ||||
if (dir->fsckflags & DIREMPWARN) { | if (dir->fsckflags & DIREMPWARN) { | ||||
*p = SLOT_EMPTY; | *p = SLOT_EMPTY; | ||||
continue; | continue; | ||||
} | } | ||||
if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { | if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { | ||||
if (*p == SLOT_EMPTY) { | if (*p == SLOT_EMPTY) { | ||||
▲ Show 20 Lines • Show All 320 Lines • ▼ Show 20 Lines | for (p = buffer, i = 0; i < last; i++, p += 32) { | ||||
p[21] = (u_char)(dirent.head >> 24); | p[21] = (u_char)(dirent.head >> 24); | ||||
} | } | ||||
mod |= THISMOD|FSDIRMOD; | mod |= THISMOD|FSDIRMOD; | ||||
} else | } else | ||||
mod |= FSERROR; | mod |= FSERROR; | ||||
} | } | ||||
} | } | ||||
continue; | 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 */ | /* create directory tree node */ | ||||
if (!(d = newDosDirEntry())) { | if (!(d = newDosDirEntry())) { | ||||
perr("No space for directory"); | perr("No space for directory"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
memcpy(d, &dirent, sizeof(struct dosDirEntry)); | memcpy(d, &dirent, sizeof(struct dosDirEntry)); | ||||
▲ Show 20 Lines • Show All 186 Lines • Show Last 20 Lines |