1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051 |
- /* check.c - Check and repair a PC/MS-DOS file system
- Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
- Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- On Debian systems, the complete text of the GNU General Public License
- can be found in /usr/share/common-licenses/GPL-3 file.
- */
- /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
- * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <limits.h>
- #include <time.h>
- #include "common.h"
- #include "dosfsck.h"
- #include "io.h"
- #include "fat.h"
- #include "file.h"
- #include "lfn.h"
- #include "check.h"
- static DOS_FILE *root;
- /* get start field of a dir entry */
- #define FSTART(p,fs) \
- ((unsigned long)CF_LE_W(p->dir_ent.start) | \
- (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0))
- #define MODIFY(p,i,v) \
- do { \
- if (p->offset) { \
- p->dir_ent.i = v; \
- fs_write(p->offset+offsetof(DIR_ENT,i), \
- sizeof(p->dir_ent.i),&p->dir_ent.i); \
- } \
- } while(0)
- #define MODIFY_START(p,v,fs) \
- do { \
- unsigned long __v = (v); \
- if (!p->offset) { \
- /* writing to fake entry for FAT32 root dir */ \
- if (!__v) die("Oops, deleting FAT32 root dir!"); \
- fs->root_cluster = __v; \
- p->dir_ent.start = CT_LE_W(__v&0xffff); \
- p->dir_ent.starthi = CT_LE_W(__v>>16); \
- __v = CT_LE_L(__v); \
- fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \
- sizeof(((struct boot_sector *)0)->root_cluster), \
- &__v); \
- } \
- else { \
- MODIFY(p,start,CT_LE_W((__v)&0xffff)); \
- if (fs->fat_bits == 32) \
- MODIFY(p,starthi,CT_LE_W((__v)>>16)); \
- } \
- } while(0)
- loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern)
- {
- static int curr_num = 0;
- loff_t offset;
- if (fs->root_cluster) {
- DIR_ENT d2;
- int i = 0, got = 0;
- unsigned long clu_num, prev = 0;
- loff_t offset2;
- clu_num = fs->root_cluster;
- offset = cluster_start(fs, clu_num);
- while (clu_num > 0 && clu_num != -1) {
- fs_read(offset, sizeof(DIR_ENT), &d2);
- if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
- got = 1;
- break;
- }
- i += sizeof(DIR_ENT);
- offset += sizeof(DIR_ENT);
- if ((i % fs->cluster_size) == 0) {
- prev = clu_num;
- if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
- break;
- offset = cluster_start(fs, clu_num);
- }
- }
- if (!got) {
- /* no free slot, need to extend root dir: alloc next free cluster
- * after previous one */
- if (!prev)
- die("Root directory has no cluster allocated!");
- for (clu_num = prev + 1; clu_num != prev; clu_num++) {
- FAT_ENTRY entry;
- if (clu_num >= fs->clusters + 2)
- clu_num = 2;
- get_fat(&entry, fs->fat, clu_num, fs);
- if (!entry.value)
- break;
- }
- if (clu_num == prev)
- die("Root directory full and no free cluster");
- set_fat(fs, prev, clu_num);
- set_fat(fs, clu_num, -1);
- set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
- /* clear new cluster */
- memset(&d2, 0, sizeof(d2));
- offset = cluster_start(fs, clu_num);
- for (i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT))
- fs_write(offset + i, sizeof(d2), &d2);
- }
- memset(de, 0, sizeof(DIR_ENT));
- while (1) {
- char expanded[12];
- sprintf(expanded, pattern, curr_num);
- memcpy(de->name + 4, expanded, 4);
- memcpy(de->ext, expanded + 4, 3);
- clu_num = fs->root_cluster;
- i = 0;
- offset2 = cluster_start(fs, clu_num);
- while (clu_num > 0 && clu_num != -1) {
- fs_read(offset2, sizeof(DIR_ENT), &d2);
- if (offset2 != offset &&
- !strncmp((const char *)d2.name, (const char *)de->name,
- MSDOS_NAME))
- break;
- i += sizeof(DIR_ENT);
- offset2 += sizeof(DIR_ENT);
- if ((i % fs->cluster_size) == 0) {
- if ((clu_num = next_cluster(fs, clu_num)) == 0 ||
- clu_num == -1)
- break;
- offset2 = cluster_start(fs, clu_num);
- }
- }
- if (clu_num == 0 || clu_num == -1)
- break;
- if (++curr_num >= 10000)
- die("Unable to create unique name");
- }
- } else {
- DIR_ENT *root;
- int next_free = 0, scan;
- root = alloc(fs->root_entries * sizeof(DIR_ENT));
- fs_read(fs->root_start, fs->root_entries * sizeof(DIR_ENT), root);
- while (next_free < fs->root_entries)
- if (IS_FREE(root[next_free].name) &&
- root[next_free].attr != VFAT_LN_ATTR)
- break;
- else
- next_free++;
- if (next_free == fs->root_entries)
- die("Root directory is full.");
- offset = fs->root_start + next_free * sizeof(DIR_ENT);
- memset(de, 0, sizeof(DIR_ENT));
- while (1) {
- sprintf((char *)de->name, pattern, curr_num);
- for (scan = 0; scan < fs->root_entries; scan++)
- if (scan != next_free &&
- !strncmp((const char *)root[scan].name,
- (const char *)de->name, MSDOS_NAME))
- break;
- if (scan == fs->root_entries)
- break;
- if (++curr_num >= 10000)
- die("Unable to create unique name");
- }
- free(root);
- }
- ++n_files;
- return offset;
- }
- /**
- * Construct a full path (starting with '/') for the specified dentry,
- * relative to the partition. All components are "long" names where possible.
- *
- * @param[in] file Information about dentry (file or directory) of interest
- *
- * return Pointer to static string containing file's full path
- */
- static char *path_name(DOS_FILE * file)
- {
- static char path[PATH_MAX * 2];
- if (!file)
- *path = 0; /* Reached the root directory */
- else {
- if (strlen(path_name(file->parent)) > PATH_MAX)
- die("Path name too long.");
- if (strcmp(path, "/") != 0)
- strcat(path, "/");
- /* Append the long name to the path,
- * or the short name if there isn't a long one
- */
- strcpy(strrchr(path, 0),
- file->lfn ? file->lfn : file_name(file->dir_ent.name));
- }
- return path;
- }
- static int day_n[] =
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 };
- /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
- /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
- time_t date_dos2unix(unsigned short time, unsigned short date)
- {
- int month, year;
- time_t secs;
- month = ((date >> 5) & 15) - 1;
- year = date >> 9;
- secs =
- (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
- 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 -
- ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
- /* days since 1.1.70 plus 80's leap day */
- return secs;
- }
- static char *file_stat(DOS_FILE * file)
- {
- static char temp[100];
- struct tm *tm;
- char tmp[100];
- time_t date;
- date =
- date_dos2unix(CF_LE_W(file->dir_ent.time), CF_LE_W(file->dir_ent.date));
- tm = localtime(&date);
- strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm);
- sprintf(temp, " Size %u bytes, date %s", CF_LE_L(file->dir_ent.size), tmp);
- return temp;
- }
- static int bad_name(DOS_FILE * file)
- {
- int i, spc, suspicious = 0;
- char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
- unsigned char *name = file->dir_ent.name;
- /* Do not complain about (and auto-correct) the extended attribute files
- * of OS/2. */
- if (strncmp((const char *)name, "EA DATA SF", 11) == 0 ||
- strncmp((const char *)name, "WP ROOT SF", 11) == 0)
- return 0;
- /* don't complain about the dummy 11 bytes used by patched Linux
- kernels */
- if (file->dir_ent.lcase & FAT_NO_83NAME)
- return 0;
- for (i = 0; i < 8; i++) {
- if (name[i] < ' ' || name[i] == 0x7f)
- return 1;
- if (name[i] > 0x7f)
- ++suspicious;
- if (strchr(bad_chars, name[i]))
- return 1;
- }
- for (i = 8; i < 11; i++) {
- if (name[i] < ' ' || name[i] == 0x7f)
- return 1;
- if (name[i] > 0x7f)
- ++suspicious;
- if (strchr(bad_chars, name[i]))
- return 1;
- }
- spc = 0;
- for (i = 0; i < 8; i++) {
- if (name[i] == ' ')
- spc = 1;
- else if (spc)
- /* non-space after a space not allowed, space terminates the name
- * part */
- return 1;
- }
- spc = 0;
- for (i = 8; i < 11; i++) {
- if (name[i] == ' ')
- spc = 1;
- else if (spc)
- /* non-space after a space not allowed, space terminates the name
- * part */
- return 1;
- }
- /* Under GEMDOS, chars >= 128 are never allowed. */
- if (atari_format && suspicious)
- return 1;
- /* Under MS-DOS and Windows, chars >= 128 in short names are valid
- * (but these characters can be visualised differently depending on
- * local codepage: CP437, CP866, etc). The chars are all basically ok,
- * so we shouldn't auto-correct such names. */
- return 0;
- }
- static void lfn_remove(loff_t from, loff_t to)
- {
- DIR_ENT empty;
- /* New dir entry is zeroed except first byte, which is set to 0xe5.
- * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
- * a directory at the first zero entry...
- */
- memset(&empty, 0, sizeof(empty));
- empty.name[0] = DELETED_FLAG;
- for (; from < to; from += sizeof(empty)) {
- fs_write(from, sizeof(DIR_ENT), &empty);
- }
- }
- static void drop_file(DOS_FS * fs, DOS_FILE * file)
- {
- unsigned long cluster;
- MODIFY(file, name[0], DELETED_FLAG);
- if (file->lfn)
- lfn_remove(file->lfn_offset, file->offset);
- for (cluster = FSTART(file, fs); cluster > 0 && cluster <
- fs->clusters + 2; cluster = next_cluster(fs, cluster))
- set_owner(fs, cluster, NULL);
- --n_files;
- }
- static void truncate_file(DOS_FS * fs, DOS_FILE * file, unsigned long clusters)
- {
- int deleting;
- unsigned long walk, next, prev;
- walk = FSTART(file, fs);
- prev = 0;
- if ((deleting = !clusters))
- MODIFY_START(file, 0, fs);
- while (walk > 0 && walk != -1) {
- next = next_cluster(fs, walk);
- if (deleting)
- set_fat(fs, walk, 0);
- else if ((deleting = !--clusters))
- set_fat(fs, walk, -1);
- prev = walk;
- walk = next;
- }
- }
- static void auto_rename(DOS_FILE * file)
- {
- DOS_FILE *first, *walk;
- unsigned long int number;
- if (!file->offset)
- return; /* cannot rename FAT32 root dir */
- first = file->parent ? file->parent->first : root;
- number = 0;
- while (1) {
- char num[8];
- sprintf(num, "%07lu", number);
- memcpy(file->dir_ent.name, "FSCK", 4);
- memcpy(file->dir_ent.name + 4, num, 4);
- memcpy(file->dir_ent.ext, num + 4, 3);
- for (walk = first; walk; walk = walk->next)
- if (walk != file
- && !strncmp((const char *)walk->dir_ent.name,
- (const char *)file->dir_ent.name, MSDOS_NAME))
- break;
- if (!walk) {
- fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
- if (file->lfn)
- lfn_fix_checksum(file->lfn_offset, file->offset,
- (const char *)file->dir_ent.name);
- return;
- }
- number++;
- if (number > 9999999) {
- die("Too many files need repair.");
- }
- }
- die("Can't generate a unique name.");
- }
- static void rename_file(DOS_FILE * file)
- {
- unsigned char name[46];
- unsigned char *walk, *here;
- if (!file->offset) {
- printf("Cannot rename FAT32 root dir\n");
- return; /* cannot rename FAT32 root dir */
- }
- while (1) {
- printf("New name: ");
- fflush(stdout);
- if (fgets((char *)name, 45, stdin)) {
- if ((here = (unsigned char *)strchr((const char *)name, '\n')))
- *here = 0;
- for (walk = (unsigned char *)strrchr((const char *)name, 0);
- walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
- walk[1] = 0;
- for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
- if (file_cvt(walk, file->dir_ent.name)) {
- fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
- if (file->lfn)
- lfn_fix_checksum(file->lfn_offset, file->offset,
- (const char *)file->dir_ent.name);
- return;
- }
- }
- }
- }
- static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots)
- {
- char *name;
- name =
- strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
- MSDOS_NAME) ? ".." : ".";
- if (!(file->dir_ent.attr & ATTR_DIR)) {
- printf("%s\n Is a non-directory.\n", path_name(file));
- if (interactive)
- printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
- "4) Convert to directory\n");
- else
- printf(" Auto-renaming it.\n");
- switch (interactive ? get_key("1234", "?") : '2') {
- case '1':
- drop_file(fs, file);
- return 1;
- case '2':
- auto_rename(file);
- printf(" Renamed to %s\n", file_name(file->dir_ent.name));
- return 0;
- case '3':
- rename_file(file);
- return 0;
- case '4':
- MODIFY(file, size, CT_LE_L(0));
- MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR);
- break;
- }
- }
- if (!dots) {
- printf("Root contains directory \"%s\". Dropping it.\n", name);
- drop_file(fs, file);
- return 1;
- }
- return 0;
- }
- static int check_file(DOS_FS * fs, DOS_FILE * file)
- {
- DOS_FILE *owner;
- int restart;
- unsigned long expect, curr, this, clusters, prev, walk, clusters2;
- if (file->dir_ent.attr & ATTR_DIR) {
- if (CF_LE_L(file->dir_ent.size)) {
- printf("%s\n Directory has non-zero size. Fixing it.\n",
- path_name(file));
- MODIFY(file, size, CT_LE_L(0));
- }
- if (file->parent
- && !strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
- MSDOS_NAME)) {
- expect = FSTART(file->parent, fs);
- if (FSTART(file, fs) != expect) {
- printf("%s\n Start (%ld) does not point to parent (%ld)\n",
- path_name(file), FSTART(file, fs), expect);
- MODIFY_START(file, expect, fs);
- }
- return 0;
- }
- if (file->parent
- && !strncmp((const char *)file->dir_ent.name, MSDOS_DOTDOT,
- MSDOS_NAME)) {
- expect =
- file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
- if (fs->root_cluster && expect == fs->root_cluster)
- expect = 0;
- if (FSTART(file, fs) != expect) {
- printf("%s\n Start (%lu) does not point to .. (%lu)\n",
- path_name(file), FSTART(file, fs), expect);
- MODIFY_START(file, expect, fs);
- }
- return 0;
- }
- if (FSTART(file, fs) == 0) {
- printf("%s\n Start does point to root directory. Deleting dir. \n",
- path_name(file));
- MODIFY(file, name[0], DELETED_FLAG);
- return 0;
- }
- }
- if (FSTART(file, fs) >= fs->clusters + 2) {
- printf
- ("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n",
- path_name(file), FSTART(file, fs), fs->clusters + 1);
- if (!file->offset)
- die("Bad FAT32 root directory! (bad start cluster)\n");
- MODIFY_START(file, 0, fs);
- }
- clusters = prev = 0;
- for (curr = FSTART(file, fs) ? FSTART(file, fs) :
- -1; curr != -1; curr = next_cluster(fs, curr)) {
- FAT_ENTRY curEntry;
- get_fat(&curEntry, fs->fat, curr, fs);
- if (!curEntry.value || bad_cluster(fs, curr)) {
- printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n",
- path_name(file), curEntry.value ? "bad" : "free", curr);
- if (prev)
- set_fat(fs, prev, -1);
- else if (!file->offset)
- die("FAT32 root dir starts with a bad cluster!");
- else
- MODIFY_START(file, 0, fs);
- break;
- }
- if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
- (unsigned long long)clusters * fs->cluster_size) {
- printf
- ("%s\n File size is %u bytes, cluster chain length is > %llu "
- "bytes.\n Truncating file to %u bytes.\n", path_name(file),
- CF_LE_L(file->dir_ent.size),
- (unsigned long long)clusters * fs->cluster_size,
- CF_LE_L(file->dir_ent.size));
- truncate_file(fs, file, clusters);
- break;
- }
- if ((owner = get_owner(fs, curr))) {
- int do_trunc = 0;
- printf("%s and\n", path_name(owner));
- printf("%s\n share clusters.\n", path_name(file));
- clusters2 = 0;
- for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
- next_cluster(fs, walk))
- if (walk == curr)
- break;
- else
- clusters2++;
- restart = file->dir_ent.attr & ATTR_DIR;
- if (!owner->offset) {
- printf(" Truncating second to %llu bytes because first "
- "is FAT32 root dir.\n",
- (unsigned long long)clusters2 * fs->cluster_size);
- do_trunc = 2;
- } else if (!file->offset) {
- printf(" Truncating first to %llu bytes because second "
- "is FAT32 root dir.\n",
- (unsigned long long)clusters * fs->cluster_size);
- do_trunc = 1;
- } else if (interactive)
- printf("1) Truncate first to %llu bytes%s\n"
- "2) Truncate second to %llu bytes\n",
- (unsigned long long)clusters * fs->cluster_size,
- restart ? " and restart" : "",
- (unsigned long long)clusters2 * fs->cluster_size);
- else
- printf(" Truncating second to %llu bytes.\n",
- (unsigned long long)clusters2 * fs->cluster_size);
- if (do_trunc != 2
- && (do_trunc == 1
- || (interactive && get_key("12", "?") == '1'))) {
- prev = 0;
- clusters = 0;
- for (this = FSTART(owner, fs); this > 0 && this != -1; this =
- next_cluster(fs, this)) {
- if (this == curr) {
- if (prev)
- set_fat(fs, prev, -1);
- else
- MODIFY_START(owner, 0, fs);
- MODIFY(owner, size,
- CT_LE_L((unsigned long long)clusters *
- fs->cluster_size));
- if (restart)
- return 1;
- while (this > 0 && this != -1) {
- set_owner(fs, this, NULL);
- this = next_cluster(fs, this);
- }
- this = curr;
- break;
- }
- clusters++;
- prev = this;
- }
- if (this != curr)
- die("Internal error: didn't find cluster %d in chain"
- " starting at %d", curr, FSTART(owner, fs));
- } else {
- if (prev)
- set_fat(fs, prev, -1);
- else
- MODIFY_START(file, 0, fs);
- break;
- }
- }
- set_owner(fs, curr, file);
- clusters++;
- prev = curr;
- }
- if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
- (unsigned long long)clusters * fs->cluster_size) {
- printf
- ("%s\n File size is %u bytes, cluster chain length is %llu bytes."
- "\n Truncating file to %llu bytes.\n", path_name(file),
- CF_LE_L(file->dir_ent.size),
- (unsigned long long)clusters * fs->cluster_size,
- (unsigned long long)clusters * fs->cluster_size);
- MODIFY(file, size,
- CT_LE_L((unsigned long long)clusters * fs->cluster_size));
- }
- return 0;
- }
- static int check_files(DOS_FS * fs, DOS_FILE * start)
- {
- while (start) {
- if (check_file(fs, start))
- return 1;
- start = start->next;
- }
- return 0;
- }
- static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
- {
- DOS_FILE *parent, **walk, **scan;
- int dot, dotdot, skip, redo;
- int good, bad;
- if (!*root)
- return 0;
- parent = (*root)->parent;
- good = bad = 0;
- for (walk = root; *walk; walk = &(*walk)->next)
- if (bad_name(*walk))
- bad++;
- else
- good++;
- if (*root && parent && good + bad > 4 && bad > good / 2) {
- printf("%s\n Has a large number of bad entries. (%d/%d)\n",
- path_name(parent), bad, good + bad);
- if (!dots)
- printf(" Not dropping root directory.\n");
- else if (!interactive)
- printf(" Not dropping it in auto-mode.\n");
- else if (get_key("yn", "Drop directory ? (y/n)") == 'y') {
- truncate_file(fs, parent, 0);
- MODIFY(parent, name[0], DELETED_FLAG);
- /* buglet: deleted directory stays in the list. */
- return 1;
- }
- }
- dot = dotdot = redo = 0;
- walk = root;
- while (*walk) {
- if (!strncmp
- ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)
- || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT,
- MSDOS_NAME)) {
- if (handle_dot(fs, *walk, dots)) {
- *walk = (*walk)->next;
- continue;
- }
- if (!strncmp
- ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME))
- dot++;
- else
- dotdot++;
- }
- if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
- puts(path_name(*walk));
- printf(" Bad short file name (%s).\n",
- file_name((*walk)->dir_ent.name));
- if (interactive)
- printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
- "4) Keep it\n");
- else
- printf(" Auto-renaming it.\n");
- switch (interactive ? get_key("1234", "?") : '3') {
- case '1':
- drop_file(fs, *walk);
- walk = &(*walk)->next;
- continue;
- case '2':
- rename_file(*walk);
- redo = 1;
- break;
- case '3':
- auto_rename(*walk);
- printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name));
- break;
- case '4':
- break;
- }
- }
- /* don't check for duplicates of the volume label */
- if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
- scan = &(*walk)->next;
- skip = 0;
- while (*scan && !skip) {
- if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
- !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
- MSDOS_NAME)) {
- printf("%s\n Duplicate directory entry.\n First %s\n",
- path_name(*walk), file_stat(*walk));
- printf(" Second %s\n", file_stat(*scan));
- if (interactive)
- printf
- ("1) Drop first\n2) Drop second\n3) Rename first\n"
- "4) Rename second\n5) Auto-rename first\n"
- "6) Auto-rename second\n");
- else
- printf(" Auto-renaming second.\n");
- switch (interactive ? get_key("123456", "?") : '6') {
- case '1':
- drop_file(fs, *walk);
- *walk = (*walk)->next;
- skip = 1;
- break;
- case '2':
- drop_file(fs, *scan);
- *scan = (*scan)->next;
- continue;
- case '3':
- rename_file(*walk);
- printf(" Renamed to %s\n", path_name(*walk));
- redo = 1;
- break;
- case '4':
- rename_file(*scan);
- printf(" Renamed to %s\n", path_name(*walk));
- redo = 1;
- break;
- case '5':
- auto_rename(*walk);
- printf(" Renamed to %s\n",
- file_name((*walk)->dir_ent.name));
- break;
- case '6':
- auto_rename(*scan);
- printf(" Renamed to %s\n",
- file_name((*scan)->dir_ent.name));
- break;
- }
- }
- scan = &(*scan)->next;
- }
- if (skip)
- continue;
- }
- if (!redo)
- walk = &(*walk)->next;
- else {
- walk = root;
- dot = dotdot = redo = 0;
- }
- }
- if (dots && !dot)
- printf("%s\n \".\" is missing. Can't fix this yet.\n",
- path_name(parent));
- if (dots && !dotdot)
- printf("%s\n \"..\" is missing. Can't fix this yet.\n",
- path_name(parent));
- return 0;
- }
- /**
- * Check a dentry's cluster chain for bad clusters.
- * If requested, we verify readability and mark unreadable clusters as bad.
- *
- * @param[inout] fs Information about the filesystem
- * @param[in] file dentry to check
- * @param[in] read_test Nonzero == verify that dentry's clusters can
- * be read
- */
- static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
- {
- DOS_FILE *owner;
- unsigned long walk, prev, clusters, next_clu;
- prev = clusters = 0;
- for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2;
- walk = next_clu) {
- next_clu = next_cluster(fs, walk);
- /* In this stage we are checking only for a loop within our own
- * cluster chain.
- * Cross-linking of clusters is handled in check_file()
- */
- if ((owner = get_owner(fs, walk))) {
- if (owner == file) {
- printf("%s\n Circular cluster chain. Truncating to %lu "
- "cluster%s.\n", path_name(file), clusters,
- clusters == 1 ? "" : "s");
- if (prev)
- set_fat(fs, prev, -1);
- else if (!file->offset)
- die("Bad FAT32 root directory! (bad start cluster)\n");
- else
- MODIFY_START(file, 0, fs);
- }
- break;
- }
- if (bad_cluster(fs, walk))
- break;
- if (read_test) {
- if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
- prev = walk;
- clusters++;
- } else {
- printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n",
- path_name(file), clusters, walk);
- if (prev)
- set_fat(fs, prev, next_cluster(fs, walk));
- else
- MODIFY_START(file, next_cluster(fs, walk), fs);
- set_fat(fs, walk, -2);
- }
- }
- set_owner(fs, walk, file);
- }
- /* Revert ownership (for now) */
- for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2;
- walk = next_cluster(fs, walk))
- if (bad_cluster(fs, walk))
- break;
- else if (get_owner(fs, walk) == file)
- set_owner(fs, walk, NULL);
- else
- break;
- }
- static void undelete(DOS_FS * fs, DOS_FILE * file)
- {
- unsigned long clusters, left, prev, walk;
- clusters = left = (CF_LE_L(file->dir_ent.size) + fs->cluster_size - 1) /
- fs->cluster_size;
- prev = 0;
- walk = FSTART(file, fs);
- while (left && (walk >= 2) && (walk < fs->clusters + 2)) {
- FAT_ENTRY curEntry;
- get_fat(&curEntry, fs->fat, walk, fs);
- if (!curEntry.value)
- break;
- left--;
- if (prev)
- set_fat(fs, prev, walk);
- prev = walk;
- walk++;
- }
- if (prev)
- set_fat(fs, prev, -1);
- else
- MODIFY_START(file, 0, fs);
- if (left)
- printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
- clusters - left, clusters, clusters == 1 ? "" : "s");
- }
- static void new_dir(void)
- {
- lfn_reset();
- }
- /**
- * Create a description for a referenced dentry and insert it in our dentry
- * tree. Then, go check the dentry's cluster chain for bad clusters and
- * cluster loops.
- *
- * @param[inout] fs Information about the filesystem
- * @param[out] chain
- * @param[in] parent Information about parent directory of this file
- * NULL == no parent ('file' is root directory)
- * @param[in] offset Partition-relative byte offset of directory entry of interest
- * 0 == Root directory
- * @param cp
- */
- static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
- loff_t offset, FDSC ** cp)
- {
- DOS_FILE *new;
- DIR_ENT de;
- FD_TYPE type;
- if (offset)
- fs_read(offset, sizeof(DIR_ENT), &de);
- else {
- /* Construct a DIR_ENT for the root directory */
- memcpy(de.name, " ", MSDOS_NAME);
- de.attr = ATTR_DIR;
- de.size = de.time = de.date = 0;
- de.start = CT_LE_W(fs->root_cluster & 0xffff);
- de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff);
- }
- if ((type = file_type(cp, (char *)de.name)) != fdt_none) {
- if (type == fdt_undelete && (de.attr & ATTR_DIR))
- die("Can't undelete directories.");
- file_modify(cp, (char *)de.name);
- fs_write(offset, 1, &de);
- }
- if (IS_FREE(de.name)) {
- lfn_check_orphaned();
- return;
- }
- if (de.attr == VFAT_LN_ATTR) {
- lfn_add_slot(&de, offset);
- return;
- }
- new = qalloc(&mem_queue, sizeof(DOS_FILE));
- new->lfn = lfn_get(&de, &new->lfn_offset);
- new->offset = offset;
- memcpy(&new->dir_ent, &de, sizeof(de));
- new->next = new->first = NULL;
- new->parent = parent;
- if (type == fdt_undelete)
- undelete(fs, new);
- **chain = new;
- *chain = &new->next;
- if (list) {
- printf("Checking file %s", path_name(new));
- if (new->lfn)
- printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */
- printf("\n");
- }
- /* Don't include root directory, '.', or '..' in the total file count */
- if (offset &&
- strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 &&
- strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0)
- ++n_files;
- test_file(fs, new, test); /* Bad cluster check */
- }
- static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
- static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
- {
- DOS_FILE **chain;
- int i;
- unsigned long clu_num;
- chain = &this->first;
- i = 0;
- clu_num = FSTART(this, fs);
- new_dir();
- while (clu_num > 0 && clu_num != -1) {
- add_file(fs, &chain, this,
- cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
- i += sizeof(DIR_ENT);
- if (!(i % fs->cluster_size))
- if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
- break;
- }
- lfn_check_orphaned();
- if (check_dir(fs, &this->first, this->offset))
- return 0;
- if (check_files(fs, this->first))
- return 1;
- return subdirs(fs, this, cp);
- }
- /**
- * Recursively scan subdirectories of the specified parent directory.
- *
- * @param[inout] fs Information about the filesystem
- * @param[in] parent Identifies the directory to scan
- * @param[in] cp
- *
- * @return 0 Success
- * @return 1 Error
- */
- static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
- {
- DOS_FILE *walk;
- for (walk = parent ? parent->first : root; walk; walk = walk->next)
- if (walk->dir_ent.attr & ATTR_DIR)
- if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME)
- && strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT,
- MSDOS_NAME))
- if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
- return 1;
- return 0;
- }
- /**
- * Scan all directory and file information for errors.
- *
- * @param[inout] fs Information about the filesystem
- *
- * @return 0 Success
- * @return 1 Error
- */
- int scan_root(DOS_FS * fs)
- {
- DOS_FILE **chain;
- int i;
- root = NULL;
- chain = &root;
- new_dir();
- if (fs->root_cluster) {
- add_file(fs, &chain, NULL, 0, &fp_root);
- } else {
- for (i = 0; i < fs->root_entries; i++)
- add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
- &fp_root);
- }
- lfn_check_orphaned();
- (void)check_dir(fs, &root, 0);
- if (check_files(fs, root))
- return 1;
- return subdirs(fs, NULL, &fp_root);
- }
|