Changeset View
Changeset View
Standalone View
Standalone View
sbin/fsck_msdosfs/dir.c
/*- | /*- | ||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||||
* | * | ||||
* Copyright (c) 2019 Google LLC | |||||
* Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank | * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank | ||||
* Copyright (c) 1995 Martin Husemann | * Copyright (c) 1995 Martin Husemann | ||||
* Some structure declaration borrowed from Paul Popelka | * Some structure declaration borrowed from Paul Popelka | ||||
* (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. | * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | |||||
/* dir.c */ | /* dir.c */ | ||||
static struct dosDirEntry *newDosDirEntry(void); | static struct dosDirEntry *newDosDirEntry(void); | ||||
static void freeDosDirEntry(struct dosDirEntry *); | static void freeDosDirEntry(struct dosDirEntry *); | ||||
static struct dirTodoNode *newDirTodo(void); | static struct dirTodoNode *newDirTodo(void); | ||||
static void freeDirTodo(struct dirTodoNode *); | static void freeDirTodo(struct dirTodoNode *); | ||||
static char *fullpath(struct dosDirEntry *); | static char *fullpath(struct dosDirEntry *); | ||||
static u_char calcShortSum(u_char *); | static u_char calcShortSum(u_char *); | ||||
static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int, | static int delete(int, struct bootblock *, struct fat_descriptor *, cl_t, int, | ||||
cl_t, int, int); | cl_t, int, int); | ||||
static int removede(int, struct bootblock *, struct fatEntry *, u_char *, | static int removede(int, struct bootblock *, struct fat_descriptor *, u_char *, | ||||
u_char *, cl_t, cl_t, cl_t, char *, int); | u_char *, cl_t, cl_t, cl_t, char *, int); | ||||
static int checksize(struct bootblock *, struct fatEntry *, u_char *, | static int checksize(struct bootblock *, struct fat_descriptor *, u_char *, | ||||
struct dosDirEntry *); | struct dosDirEntry *); | ||||
static int readDosDirSection(int, struct bootblock *, struct fatEntry *, | static int readDosDirSection(int, struct bootblock *, struct fat_descriptor *, | ||||
struct dosDirEntry *); | struct dosDirEntry *); | ||||
/* | /* | ||||
* Manage free dosDirEntry structures. | * Manage free dosDirEntry structures. | ||||
*/ | */ | ||||
static struct dosDirEntry *freede; | static struct dosDirEntry *freede; | ||||
static struct dosDirEntry * | static struct dosDirEntry * | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | |||||
static struct dosDirEntry *rootDir; | static struct dosDirEntry *rootDir; | ||||
static struct dosDirEntry *lostDir; | static struct dosDirEntry *lostDir; | ||||
/* | /* | ||||
* Init internal state for a new directory scan. | * Init internal state for a new directory scan. | ||||
*/ | */ | ||||
int | int | ||||
resetDosDirSection(struct bootblock *boot, struct fatEntry *fat) | resetDosDirSection(struct bootblock *boot, struct fat_descriptor *fat __unused) | ||||
{ | { | ||||
int b1, b2; | int b1, b2; | ||||
int ret = FSOK; | int ret = FSOK; | ||||
size_t len; | size_t len; | ||||
b1 = boot->bpbRootDirEnts * 32; | b1 = boot->bpbRootDirEnts * 32; | ||||
b2 = boot->bpbSecPerClust * boot->bpbBytesPerSec; | b2 = boot->bpbSecPerClust * boot->bpbBytesPerSec; | ||||
Show All 18 Lines | resetDosDirSection(struct bootblock *boot, struct fat_descriptor *fat __unused) | ||||
memset(rootDir, 0, sizeof *rootDir); | memset(rootDir, 0, sizeof *rootDir); | ||||
if (boot->flags & FAT32) { | if (boot->flags & FAT32) { | ||||
if (boot->bpbRootClust < CLUST_FIRST || | if (boot->bpbRootClust < CLUST_FIRST || | ||||
boot->bpbRootClust >= boot->NumClusters) { | boot->bpbRootClust >= boot->NumClusters) { | ||||
pfatal("Root directory starts with cluster out of range(%u)", | pfatal("Root directory starts with cluster out of range(%u)", | ||||
boot->bpbRootClust); | boot->bpbRootClust); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
if (fat[boot->bpbRootClust].head != boot->bpbRootClust) { | if (!fat_is_cl_head(boot->bpbRootClust)) { | ||||
pfatal("Root directory doesn't start a cluster chain"); | pfatal("Root directory doesn't start a cluster chain"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
/* | |||||
fat[boot->bpbRootClust].flags |= FAT_USED; | * Mark the chain head as used, and remove it | ||||
* from head bitmap because it's claimed by the | |||||
* root directory. | |||||
*/ | |||||
fat_set_cl_used(boot->bpbRootClust); | |||||
rootDir->head = boot->bpbRootClust; | rootDir->head = boot->bpbRootClust; | ||||
fat_clear_cl_head(boot->bpbRootClust); | |||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
/* | /* | ||||
* Cleanup after a directory scan | * Cleanup after a directory scan | ||||
*/ | */ | ||||
Show All 23 Lines | finishDosDirSection(void) | ||||
buffer = NULL; | buffer = NULL; | ||||
delbuf = NULL; | delbuf = NULL; | ||||
} | } | ||||
/* | /* | ||||
* Delete directory entries between startcl, startoff and endcl, endoff. | * Delete directory entries between startcl, startoff and endcl, endoff. | ||||
*/ | */ | ||||
static int | static int | ||||
delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl, | delete(int f, struct bootblock *boot, struct fat_descriptor *fat, cl_t startcl, | ||||
int startoff, cl_t endcl, int endoff, int notlast) | int startoff, cl_t endcl, int endoff, int notlast) | ||||
{ | { | ||||
u_char *s, *e; | u_char *s, *e; | ||||
off_t off; | off_t off; | ||||
int clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec; | int clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec; | ||||
s = delbuf + startoff; | s = delbuf + startoff; | ||||
e = delbuf + clsz; | e = delbuf + clsz; | ||||
Show All 22 Lines | if (lseek(f, off, SEEK_SET) != off) { | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
if (write(f, delbuf, clsz) != clsz) { | if (write(f, delbuf, clsz) != clsz) { | ||||
perr("Unable to write directory"); | perr("Unable to write directory"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
if (startcl == endcl) | if (startcl == endcl) | ||||
break; | break; | ||||
startcl = fat[startcl].next; | startcl = fat_get_cl_next(fat, startcl); | ||||
s = delbuf; | s = delbuf; | ||||
} | } | ||||
return FSOK; | return FSOK; | ||||
} | } | ||||
static int | static int | ||||
removede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start, | removede(int f, struct bootblock *boot, struct fat_descriptor *fat, | ||||
u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type) | u_char *start, u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, | ||||
char *path, int type) | |||||
{ | { | ||||
switch (type) { | switch (type) { | ||||
case 0: | case 0: | ||||
pwarn("Invalid long filename entry for %s\n", path); | pwarn("Invalid long filename entry for %s\n", path); | ||||
break; | break; | ||||
case 1: | case 1: | ||||
pwarn("Invalid long filename entry at end of directory %s\n", | pwarn("Invalid long filename entry at end of directory %s\n", | ||||
path); | path); | ||||
break; | break; | ||||
case 2: | case 2: | ||||
pwarn("Invalid long filename entry for volume label\n"); | pwarn("Invalid long filename entry for volume label\n"); | ||||
break; | break; | ||||
} | } | ||||
if (ask(0, "Remove")) { | if (ask(0, "Remove")) { | ||||
if (startcl != curcl) { | if (startcl != curcl) { | ||||
if (delete(f, boot, fat, | if (delete(f, boot, fat, | ||||
startcl, start - buffer, | startcl, start - buffer, | ||||
endcl, end - buffer, | endcl, end - buffer, | ||||
endcl == curcl) == FSFATAL) | endcl == curcl) == FSFATAL) | ||||
return FSFATAL; | return FSFATAL; | ||||
start = buffer; | start = buffer; | ||||
} | } | ||||
/* startcl is < CLUST_FIRST for !fat32 root */ | /* startcl is < CLUST_FIRST for !FAT32 root */ | ||||
if ((endcl == curcl) || (startcl < CLUST_FIRST)) | if ((endcl == curcl) || (startcl < CLUST_FIRST)) | ||||
for (; start < end; start += 32) | for (; start < end; start += 32) | ||||
*start = SLOT_DELETED; | *start = SLOT_DELETED; | ||||
return FSDIRMOD; | return FSDIRMOD; | ||||
} | } | ||||
return FSERROR; | return FSERROR; | ||||
} | } | ||||
/* | /* | ||||
* Check an in-memory file entry | * Check an in-memory file entry | ||||
*/ | */ | ||||
static int | static int | ||||
checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p, | checksize(struct bootblock *boot, struct fat_descriptor *fat, u_char *p, | ||||
struct dosDirEntry *dir) | struct dosDirEntry *dir) | ||||
{ | { | ||||
int ret = FSOK; | |||||
/* | /* | ||||
* Check size on ordinary files | * Check size on ordinary files | ||||
*/ | */ | ||||
u_int32_t physicalSize; | size_t physicalSize; | ||||
if (dir->head == CLUST_FREE) | if (dir->head == CLUST_FREE) | ||||
physicalSize = 0; | physicalSize = 0; | ||||
else { | else { | ||||
if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters) | if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters) | ||||
return FSERROR; | return FSERROR; | ||||
physicalSize = fat[dir->head].length * boot->ClusterSize; | ret |= checkchain(fat, dir->head, &physicalSize); | ||||
physicalSize *= boot->ClusterSize; | |||||
} | } | ||||
if (physicalSize < dir->size) { | if (physicalSize < dir->size) { | ||||
pwarn("size of %s is %u, should at most be %u\n", | pwarn("size of %s is %u, should at most be %zu\n", | ||||
fullpath(dir), dir->size, physicalSize); | fullpath(dir), dir->size, physicalSize); | ||||
if (ask(1, "Truncate")) { | if (ask(1, "Truncate")) { | ||||
dir->size = physicalSize; | dir->size = physicalSize; | ||||
p[28] = (u_char)physicalSize; | p[28] = (u_char)physicalSize; | ||||
p[29] = (u_char)(physicalSize >> 8); | p[29] = (u_char)(physicalSize >> 8); | ||||
p[30] = (u_char)(physicalSize >> 16); | p[30] = (u_char)(physicalSize >> 16); | ||||
p[31] = (u_char)(physicalSize >> 24); | p[31] = (u_char)(physicalSize >> 24); | ||||
return FSDIRMOD; | return FSDIRMOD; | ||||
} else | } else | ||||
return FSERROR; | return FSERROR; | ||||
} else if (physicalSize - dir->size >= boot->ClusterSize) { | } else if (physicalSize - dir->size >= boot->ClusterSize) { | ||||
pwarn("%s has too many clusters allocated\n", | pwarn("%s has too many clusters allocated\n", | ||||
fullpath(dir)); | fullpath(dir)); | ||||
if (ask(1, "Drop superfluous clusters")) { | if (ask(1, "Drop superfluous clusters")) { | ||||
cl_t cl; | cl_t cl; | ||||
u_int32_t sz, len; | u_int32_t sz, len; | ||||
for (cl = dir->head, len = sz = 0; | for (cl = dir->head, len = sz = 0; | ||||
(sz += boot->ClusterSize) < dir->size; len++) | (sz += boot->ClusterSize) < dir->size; len++) | ||||
cl = fat[cl].next; | cl = fat_get_cl_next(fat, cl); | ||||
clearchain(boot, fat, fat[cl].next); | clearchain(boot, fat, fat_get_cl_next(fat, cl)); | ||||
fat[cl].next = CLUST_EOF; | fat_set_cl_next(fat, cl, CLUST_EOF); | ||||
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[11] = ". "; | static const u_char dot_name[11] = ". "; | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* 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 fat_descriptor *fat, | ||||
struct dosDirEntry *dir) | struct dosDirEntry *dir) | ||||
{ | { | ||||
struct dosDirEntry dirent, *d; | struct dosDirEntry dirent, *d; | ||||
u_char *p, *vallfn, *invlfn, *empty; | u_char *p, *vallfn, *invlfn, *empty; | ||||
off_t off; | off_t off; | ||||
int i, j, k, last; | int i, j, k, last; | ||||
cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0; | cl_t cl, dircl, valcl = ~0, invcl = ~0, empcl = ~0; | ||||
char *t; | char *t; | ||||
u_int lidx = 0; | u_int lidx = 0; | ||||
int shortSum; | int shortSum; | ||||
int mod = FSOK; | int mod = FSOK; | ||||
#define THISMOD 0x8000 /* Only used within this routine */ | #define THISMOD 0x8000 /* Only used within this routine */ | ||||
cl = dir->head; | cl = dir->head; | ||||
if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) { | if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) { | ||||
▲ Show 20 Lines • Show All 224 Lines • ▼ Show 20 Lines | for (p = buffer, i = 0; i < last; i++, p += 32) { | ||||
: p != buffer) | : p != buffer) | ||||
if (k & FSDIRMOD) | if (k & FSDIRMOD) | ||||
mod |= THISMOD; | mod |= THISMOD; | ||||
} | } | ||||
vallfn = NULL; /* not used any longer */ | vallfn = NULL; /* not used any longer */ | ||||
invlfn = NULL; | invlfn = NULL; | ||||
if (dirent.flags & ATTR_DIRECTORY && | |||||
(strcmp(dirent.name, ".") == 0 || | |||||
strcmp(dirent.name, "..") == 0)) { | |||||
/* | |||||
* These are already checked when scanning the parent, | |||||
* so explicitly skip. | |||||
*/ | |||||
} else { | |||||
if (dirent.head >= CLUST_FIRST && | |||||
dirent.head < boot->NumClusters) { | |||||
dircl = fat_get_cl_next(fat, | |||||
dirent.head); | |||||
} else { | |||||
dircl = 0; | |||||
} | |||||
if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) { | if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) { | ||||
if (dirent.head != 0) { | if (dirent.head != 0) { | ||||
pwarn("%s has clusters, but size 0\n", | pwarn("%s has clusters, but size 0\n", | ||||
fullpath(&dirent)); | fullpath(&dirent)); | ||||
if (ask(1, "Drop allocated clusters")) { | if (ask(1, "Drop allocated clusters")) { | ||||
p[26] = p[27] = 0; | p[26] = p[27] = 0; | ||||
if (boot->ClustMask == CLUST32_MASK) | if (boot->ClustMask == CLUST32_MASK) | ||||
p[20] = p[21] = 0; | p[20] = p[21] = 0; | ||||
clearchain(boot, fat, dirent.head); | clearchain(boot, fat, dirent.head); | ||||
dirent.head = 0; | dirent.head = 0; | ||||
mod |= THISMOD|FSDIRMOD|FSFATMOD; | mod |= THISMOD|FSDIRMOD|FSFATMOD; | ||||
} else | } else | ||||
mod |= FSERROR; | mod |= FSERROR; | ||||
} | } | ||||
} else if (dirent.head == 0 | } else if (dirent.head == 0 | ||||
&& !strcmp(dirent.name, "..") | && !strcmp(dirent.name, "..") | ||||
&& dir->parent /* XXX */ | && dir->parent /* XXX */ | ||||
&& !dir->parent->parent) { | && !dir->parent->parent) { | ||||
/* | /* | ||||
* Do nothing, the parent is the root | * Do nothing, the parent is the root | ||||
*/ | */ | ||||
} else if (dirent.head < CLUST_FIRST | } else if (dirent.head < CLUST_FIRST | ||||
|| dirent.head >= boot->NumClusters | || dirent.head >= boot->NumClusters | ||||
|| fat[dirent.head].next == CLUST_FREE | || dircl == CLUST_FREE | ||||
|| (fat[dirent.head].next >= CLUST_RSRVD | || (dircl >= CLUST_RSRVD && dircl < CLUST_EOFS) | ||||
&& fat[dirent.head].next < CLUST_EOFS) | || (strcmp(dirent.name, ".") != 0 && !fat_is_cl_head(dirent.head))) { | ||||
|| fat[dirent.head].head != dirent.head) { | |||||
if (dirent.head == 0) | if (dirent.head == 0) | ||||
pwarn("%s has no clusters\n", | pwarn("%s has no clusters\n", | ||||
fullpath(&dirent)); | fullpath(&dirent)); | ||||
else if (dirent.head < CLUST_FIRST | else if (dirent.head < CLUST_FIRST | ||||
|| dirent.head >= boot->NumClusters) | || dirent.head >= boot->NumClusters) | ||||
pwarn("%s starts with cluster out of range(%u)\n", | pwarn("%s starts with cluster out of range(%u)\n", | ||||
fullpath(&dirent), | fullpath(&dirent), | ||||
dirent.head); | dirent.head); | ||||
else if (fat[dirent.head].next == CLUST_FREE) | else if (dircl == CLUST_FREE) | ||||
pwarn("%s starts with free cluster\n", | pwarn("%s starts with free cluster\n", | ||||
fullpath(&dirent)); | fullpath(&dirent)); | ||||
else if (fat[dirent.head].next >= CLUST_RSRVD) | else if (dircl >= CLUST_RSRVD && dircl < CLUST_EOFS) | ||||
pwarn("%s starts with cluster marked %s\n", | pwarn("%s starts with cluster marked %s\n", | ||||
fullpath(&dirent), | fullpath(&dirent), | ||||
rsrvdcltype(fat[dirent.head].next)); | rsrvdcltype(dircl)); | ||||
else | else | ||||
pwarn("%s doesn't start a cluster chain\n", | pwarn("%s doesn't start a cluster chain\n", | ||||
fullpath(&dirent)); | fullpath(&dirent)); | ||||
if (dirent.flags & ATTR_DIRECTORY) { | if (dirent.flags & ATTR_DIRECTORY) { | ||||
if (ask(0, "Remove")) { | if (ask(0, "Remove")) { | ||||
*p = SLOT_DELETED; | *p = SLOT_DELETED; | ||||
mod |= THISMOD|FSDIRMOD; | mod |= THISMOD|FSDIRMOD; | ||||
} else | } else | ||||
mod |= FSERROR; | mod |= FSERROR; | ||||
continue; | continue; | ||||
} else { | } else { | ||||
if (ask(1, "Truncate")) { | if (ask(1, "Truncate")) { | ||||
p[28] = p[29] = p[30] = p[31] = 0; | p[28] = p[29] = p[30] = p[31] = 0; | ||||
p[26] = p[27] = 0; | p[26] = p[27] = 0; | ||||
if (boot->ClustMask == CLUST32_MASK) | if (boot->ClustMask == CLUST32_MASK) | ||||
p[20] = p[21] = 0; | p[20] = p[21] = 0; | ||||
dirent.size = 0; | dirent.size = 0; | ||||
dirent.head = 0; | |||||
mod |= THISMOD|FSDIRMOD; | mod |= THISMOD|FSDIRMOD; | ||||
} else | } else | ||||
mod |= FSERROR; | mod |= FSERROR; | ||||
} | } | ||||
} | } | ||||
if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters) | if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters) { | ||||
fat[dirent.head].flags |= FAT_USED; | fat_set_cl_used(dirent.head); | ||||
fat_clear_cl_head(dirent.head); | |||||
} | |||||
} | |||||
if (dirent.flags & ATTR_DIRECTORY) { | if (dirent.flags & ATTR_DIRECTORY) { | ||||
/* | /* | ||||
* gather more info for directories | * gather more info for directories | ||||
*/ | */ | ||||
struct dirTodoNode *n; | struct dirTodoNode *n; | ||||
if (dirent.size) { | if (dirent.size) { | ||||
pwarn("Directory %s has size != 0\n", | pwarn("Directory %s has size != 0\n", | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | if (mod & THISMOD) { | ||||
last *= 32; | last *= 32; | ||||
if (lseek(f, off, SEEK_SET) != off | if (lseek(f, off, SEEK_SET) != off | ||||
|| write(f, buffer, last) != last) { | || write(f, buffer, last) != last) { | ||||
perr("Unable to write directory"); | perr("Unable to write directory"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
mod &= ~THISMOD; | mod &= ~THISMOD; | ||||
} | } | ||||
} while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters); | } while ((cl = fat_get_cl_next(fat, cl)) >= CLUST_FIRST && cl < boot->NumClusters); | ||||
if (invlfn || vallfn) | if (invlfn || vallfn) | ||||
mod |= removede(f, boot, fat, | mod |= removede(f, boot, fat, | ||||
invlfn ? invlfn : vallfn, p, | invlfn ? invlfn : vallfn, p, | ||||
invlfn ? invcl : valcl, -1, 0, | invlfn ? invcl : valcl, -1, 0, | ||||
fullpath(dir), 1); | fullpath(dir), 1); | ||||
/* The root directory of non fat32 filesystems is in a special | /* The root directory of non fat32 filesystems is in a special | ||||
* area and may have been modified above without being written out. | * area and may have been modified above without being written out. | ||||
*/ | */ | ||||
if ((mod & FSDIRMOD) && !(boot->flags & FAT32) && !dir->parent) { | if ((mod & FSDIRMOD) && !(boot->flags & FAT32) && !dir->parent) { | ||||
last *= 32; | last *= 32; | ||||
if (lseek(f, off, SEEK_SET) != off | if (lseek(f, off, SEEK_SET) != off | ||||
|| write(f, buffer, last) != last) { | || write(f, buffer, last) != last) { | ||||
perr("Unable to write directory"); | perr("Unable to write directory"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
mod &= ~THISMOD; | mod &= ~THISMOD; | ||||
} | } | ||||
return mod & ~THISMOD; | return mod & ~THISMOD; | ||||
} | } | ||||
int | int | ||||
handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat) | handleDirTree(int dosfs, struct bootblock *boot, struct fat_descriptor *fat) | ||||
{ | { | ||||
int mod; | int mod; | ||||
mod = readDosDirSection(dosfs, boot, fat, rootDir); | mod = readDosDirSection(dosfs, boot, fat, rootDir); | ||||
if (mod & FSFATAL) | if (mod & FSFATAL) | ||||
return FSFATAL; | return FSFATAL; | ||||
/* | /* | ||||
Show All 24 Lines | |||||
/* | /* | ||||
* Try to reconnect a FAT chain into dir | * Try to reconnect a FAT chain into dir | ||||
*/ | */ | ||||
static u_char *lfbuf; | static u_char *lfbuf; | ||||
static cl_t lfcl; | static cl_t lfcl; | ||||
static off_t lfoff; | static off_t lfoff; | ||||
int | int | ||||
reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head) | reconnect(int dosfs, struct fat_descriptor *fat, cl_t head, size_t length) | ||||
{ | { | ||||
struct bootblock *boot = fat_get_boot(fat); | |||||
struct dosDirEntry d; | struct dosDirEntry d; | ||||
int len; | int len; | ||||
u_char *p; | u_char *p; | ||||
if (!ask(1, "Reconnect")) | if (!ask(1, "Reconnect")) | ||||
return FSERROR; | return FSERROR; | ||||
if (!lostDir) { | if (!lostDir) { | ||||
Show All 18 Lines | reconnect(int dosfs, struct fat_descriptor *fat, cl_t head, size_t length) | ||||
while (1) { | while (1) { | ||||
if (p) | if (p) | ||||
for (; p < lfbuf + boot->ClusterSize; p += 32) | for (; p < lfbuf + boot->ClusterSize; p += 32) | ||||
if (*p == SLOT_EMPTY | if (*p == SLOT_EMPTY | ||||
|| *p == SLOT_DELETED) | || *p == SLOT_DELETED) | ||||
break; | break; | ||||
if (p && p < lfbuf + boot->ClusterSize) | if (p && p < lfbuf + boot->ClusterSize) | ||||
break; | break; | ||||
lfcl = p ? fat[lfcl].next : lostDir->head; | lfcl = p ? fat_get_cl_next(fat, lfcl) : lostDir->head; | ||||
if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { | if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { | ||||
/* Extend LOSTDIR? XXX */ | /* Extend LOSTDIR? XXX */ | ||||
pwarn("No space in %s\n", LOSTDIR); | pwarn("No space in %s\n", LOSTDIR); | ||||
lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0; | lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0; | ||||
return FSERROR; | return FSERROR; | ||||
} | } | ||||
lfoff = lfcl * boot->ClusterSize | lfoff = lfcl * boot->ClusterSize | ||||
+ boot->ClusterOffset * boot->bpbBytesPerSec; | + boot->ClusterOffset * boot->bpbBytesPerSec; | ||||
if (lseek(dosfs, lfoff, SEEK_SET) != lfoff | if (lseek(dosfs, lfoff, SEEK_SET) != lfoff | ||||
|| (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { | || (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { | ||||
perr("could not read LOST.DIR"); | perr("could not read LOST.DIR"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
p = lfbuf; | p = lfbuf; | ||||
} | } | ||||
boot->NumFiles++; | boot->NumFiles++; | ||||
/* Ensure uniqueness of entry here! XXX */ | /* Ensure uniqueness of entry here! XXX */ | ||||
memset(&d, 0, sizeof d); | memset(&d, 0, sizeof d); | ||||
/* worst case -1 = 4294967295, 10 digits */ | /* worst case -1 = 4294967295, 10 digits */ | ||||
len = snprintf(d.name, sizeof(d.name), "%u", head); | len = snprintf(d.name, sizeof(d.name), "%u", head); | ||||
d.flags = 0; | d.flags = 0; | ||||
d.head = head; | d.head = head; | ||||
d.size = fat[head].length * boot->ClusterSize; | d.size = length * boot->ClusterSize; | ||||
memcpy(p, d.name, len); | memcpy(p, d.name, len); | ||||
memset(p + len, ' ', 11 - len); | memset(p + len, ' ', 11 - len); | ||||
memset(p + 11, 0, 32 - 11); | memset(p + 11, 0, 32 - 11); | ||||
p[26] = (u_char)d.head; | p[26] = (u_char)d.head; | ||||
p[27] = (u_char)(d.head >> 8); | p[27] = (u_char)(d.head >> 8); | ||||
if (boot->ClustMask == CLUST32_MASK) { | if (boot->ClustMask == CLUST32_MASK) { | ||||
p[20] = (u_char)(d.head >> 16); | p[20] = (u_char)(d.head >> 16); | ||||
p[21] = (u_char)(d.head >> 24); | p[21] = (u_char)(d.head >> 24); | ||||
} | } | ||||
p[28] = (u_char)d.size; | p[28] = (u_char)d.size; | ||||
p[29] = (u_char)(d.size >> 8); | p[29] = (u_char)(d.size >> 8); | ||||
p[30] = (u_char)(d.size >> 16); | p[30] = (u_char)(d.size >> 16); | ||||
p[31] = (u_char)(d.size >> 24); | p[31] = (u_char)(d.size >> 24); | ||||
fat[head].flags |= FAT_USED; | fat_set_cl_used(head); | ||||
if (lseek(dosfs, lfoff, SEEK_SET) != lfoff | if (lseek(dosfs, lfoff, SEEK_SET) != lfoff | ||||
|| (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { | || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { | ||||
perr("could not write LOST.DIR"); | perr("could not write LOST.DIR"); | ||||
return FSFATAL; | return FSFATAL; | ||||
} | } | ||||
return FSDIRMOD; | return FSDIRMOD; | ||||
} | } | ||||
void | void | ||||
finishlf(void) | finishlf(void) | ||||
{ | { | ||||
if (lfbuf) | if (lfbuf) | ||||
free(lfbuf); | free(lfbuf); | ||||
lfbuf = NULL; | lfbuf = NULL; | ||||
} | } |