"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/check.c" between
dosfstools-4.1.tar.gz and dosfstools-4.2.tar.gz

About: dosfstools are utilities to create, check and label (MS-DOS) FAT filesystems.

check.c  (dosfstools-4.1):check.c  (dosfstools-4.2)
/* check.c - Check and repair a PC/MS-DOS filesystem /* check.c - Check and repair a PC/MS-DOS filesystem
Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
Copyright (C) 2015 Andreas Bombe <aeb@debian.org> Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
Copyright (C) 2017-2021 Pali Rohár <pali.rohar@gmail.com>
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
skipping to change at line 32 skipping to change at line 33
can be found in /usr/share/common-licenses/GPL-3 file. can be found in /usr/share/common-licenses/GPL-3 file.
*/ */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <errno.h>
#include <ctype.h>
#include <wctype.h>
#include "common.h" #include "common.h"
#include "fsck.fat.h" #include "fsck.fat.h"
#include "io.h" #include "io.h"
#include "fat.h" #include "fat.h"
#include "file.h" #include "file.h"
#include "lfn.h" #include "lfn.h"
#include "check.h" #include "check.h"
#include "boot.h"
#include "charconv.h"
/* the longest path on the filesystem that can be handled by path_name() */ /* the longest path on the filesystem that can be handled by path_name() */
#define PATH_NAME_MAX 1023 #define PATH_NAME_MAX 1023
static DOS_FILE *root; static DOS_FILE *root;
/* get start field of a dir entry */ /* get start field of a dir entry */
#define FSTART(p,fs) \ #define FSTART(p,fs) \
((uint32_t)le16toh(p->dir_ent.start) | \ ((uint32_t)le16toh(p->dir_ent.start) | \
(fs->fat_bits == 32 ? le16toh(p->dir_ent.starthi) << 16 : 0)) (fs->fat_bits == 32 ? (uint32_t)le16toh(p->dir_ent.starthi) << 16 : 0))
#define MODIFY(p,i,v) \ #define MODIFY(p,i,v) \
do { \ do { \
if (p->offset) { \ if (p->offset) { \
p->dir_ent.i = v; \ p->dir_ent.i = v; \
fs_write(p->offset+offsetof(DIR_ENT,i), \ fs_write(p->offset+offsetof(DIR_ENT,i), \
sizeof(p->dir_ent.i),&p->dir_ent.i); \ sizeof(p->dir_ent.i),&p->dir_ent.i); \
} \ } \
} while(0) } while(0)
skipping to change at line 81 skipping to change at line 87
sizeof(((struct boot_sector *)0)->root_cluster), \ sizeof(((struct boot_sector *)0)->root_cluster), \
&__v); \ &__v); \
} \ } \
else { \ else { \
MODIFY(p,start,htole16((__v)&0xffff)); \ MODIFY(p,start,htole16((__v)&0xffff)); \
if (fs->fat_bits == 32) \ if (fs->fat_bits == 32) \
MODIFY(p,starthi,htole16((__v)>>16)); \ MODIFY(p,starthi,htole16((__v)>>16)); \
} \ } \
} while(0) } while(0)
off_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern, int ge
n_name)
{
static int curr_num = 0;
off_t offset;
if (fs->root_cluster) {
DIR_ENT d2;
int i = 0, got = 0;
uint32_t clu_num, prev = 0;
off_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->data_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));
if (gen_name) {
while (1) {
char expanded[12];
sprintf(expanded, pattern, curr_num);
memcpy(de->name, expanded, MSDOS_NAME);
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 {
memcpy(de->name, pattern, MSDOS_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));
if (gen_name) {
while (1) {
char expanded[12];
sprintf(expanded, pattern, curr_num);
memcpy(de->name, expanded, MSDOS_NAME);
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");
}
} else {
memcpy(de->name, pattern, MSDOS_NAME);
}
free(root);
}
++n_files;
return offset;
}
/** /**
* Construct a full path (starting with '/') for the specified dentry, * Construct a full path (starting with '/') for the specified dentry,
* relative to the partition. All components are "long" names where possible. * relative to the partition. All components are "long" names where possible.
* *
* @param[in] file Information about dentry (file or directory) of interest * @param[in] file Information about dentry (file or directory) of interest
* *
* return Pointer to static string containing file's full path * return Pointer to static string containing file's full path
*/ */
static char *path_name(DOS_FILE * file) static char *path_name(DOS_FILE * file)
{ {
skipping to change at line 236 skipping to change at line 116
/* Append the long name to the path, /* Append the long name to the path,
* or the short name if there isn't a long one * or the short name if there isn't a long one
*/ */
strcpy(strrchr(path, 0), strcpy(strrchr(path, 0),
file->lfn ? file->lfn : file_name(file->dir_ent.name)); file->lfn ? file->lfn : file_name(file->dir_ent.name));
} }
return path; return path;
} }
static const int day_n[] = static const char *month_str[] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov
/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ ", "Dec" };
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
static time_t date_dos2unix(unsigned short time, unsigned short date)
{
int month, year;
time_t secs;
month = ((date >> 5) & 15) - 1;
if (month < 0) {
/* make sure that nothing bad happens if the month bits were zero */
month = 0;
}
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 *file_stat(DOS_FILE * file)
{ {
static char temp[100]; static char temp[100];
struct tm *tm; unsigned int hours, minutes, secs, day, month, year;
char tmp[100]; unsigned short time, date;
time_t date;
time = le16toh(file->dir_ent.time);
date = date = le16toh(file->dir_ent.date);
date_dos2unix(le16toh(file->dir_ent.time), le16toh(file->dir_ent.date)); year = 1980 + (date >> 9);
tm = localtime(&date); month = ((date >> 5) & 15);
strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm); if (month < 1) month = 1;
sprintf(temp, " Size %u bytes, date %s", le32toh(file->dir_ent.size), tmp); else if (month > 12) month = 12;
day = (date & 31);
if (day < 1) day = 1;
hours = (time >> 11);
if (hours > 23) hours = 23;
minutes = ((time >> 5) & 63);
if (minutes > 59) minutes = 59;
secs = (time & 31) * 2;
if (secs > 59) secs = 59;
sprintf(temp, " Size %u bytes, date %02u:%02u:%02u %s %02u %4u",
le32toh(file->dir_ent.size), hours, minutes, secs, month_str[month-1], d
ay, year);
return temp; return temp;
} }
static int bad_name(DOS_FILE * file) static int bad_name(DOS_FILE * file)
{ {
int i, spc, suspicious = 0; int i, spc, suspicious = 0;
const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:"; const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:.";
const unsigned char *name = file->dir_ent.name; const unsigned char *name = file->dir_ent.name;
const unsigned char *ext = name + 8; const unsigned char *ext = name + 8;
/* Do not complain about (and auto-correct) the extended attribute files /* do not check synthetic FAT32 root entry */
* of OS/2. */ if (!file->offset)
if (strncmp((const char *)name, "EA DATA SF", 11) == 0 ||
strncmp((const char *)name, "WP ROOT SF", 11) == 0)
return 0; return 0;
/* check if we have neither a long filename nor a short name */ /* check if we have neither a long filename nor a short name */
if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) { if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) {
return 1; return 1;
} }
/* don't complain about the dummy 11 bytes used by patched Linux /* don't complain about the dummy 11 bytes used by patched Linux
kernels */ kernels */
if (file->dir_ent.lcase & FAT_NO_83NAME) if (file->dir_ent.lcase & FAT_NO_83NAME)
return 0; return 0;
for (i = 0; i < MSDOS_NAME; i++) { for (i = 0; i < MSDOS_NAME; i++) {
if (name[i] < ' ' || name[i] == 0x7f) if ((name[i] < ' ' && !(i == 0 && name[0] == 0x05)) || name[i] == 0x7f)
return 1; return 1;
if (name[i] > 0x7f) if (name[i] > 0x7f)
++suspicious; ++suspicious;
if (strchr(bad_chars, name[i])) if (strchr(bad_chars, name[i]))
return 1; return 1;
} }
spc = 0; if (name[0] == ' ')
for (i = 0; i < 8; i++) { return 1;
if (name[i] == ' ')
spc = 1;
else if (spc)
/* non-space after a space not allowed, space terminates the name
* part */
return 1;
}
spc = 0; if (no_spaces_in_sfns) {
for (i = 0; i < 3; i++) { spc = 0;
if (ext[i] == ' ') for (i = 0; i < 8; i++) {
spc = 1; if (name[i] == ' ')
else if (spc) spc = 1;
/* non-space after a space not allowed, space terminates the ext else if (spc)
* part */ /* non-space after a space not allowed, space terminates the name
return 1; * part */
return 1;
}
spc = 0;
for (i = 0; i < 3; i++) {
if (ext[i] == ' ')
spc = 1;
else if (spc)
/* non-space after a space not allowed, space terminates the ext
* part */
return 1;
}
} }
/* Under GEMDOS, chars >= 128 are never allowed. */ /* Under GEMDOS, chars >= 128 are never allowed. */
if (atari_format && suspicious) if (atari_format && suspicious)
return 1; return 1;
/* Under MS-DOS and Windows, chars >= 128 in short names are valid /* Under MS-DOS and Windows, chars >= 128 in short names are valid
* (but these characters can be visualised differently depending on * (but these characters can be visualised differently depending on
* local codepage: CP437, CP866, etc). The chars are all basically ok, * local codepage: CP437, CP866, etc). The chars are all basically ok,
* so we shouldn't auto-correct such names. */ * so we shouldn't auto-correct such names. */
skipping to change at line 357 skipping to change at line 228
memset(&empty, 0, sizeof(empty)); memset(&empty, 0, sizeof(empty));
empty.name[0] = DELETED_FLAG; empty.name[0] = DELETED_FLAG;
for (; from < to; from += sizeof(empty)) { for (; from < to; from += sizeof(empty)) {
fs_write(from, sizeof(DIR_ENT), &empty); fs_write(from, sizeof(DIR_ENT), &empty);
} }
} }
static void drop_file(DOS_FS * fs, DOS_FILE * file) static void drop_file(DOS_FS * fs, DOS_FILE * file)
{ {
uint32_t cluster; (void) fs;
MODIFY(file, name[0], DELETED_FLAG); MODIFY(file, name[0], DELETED_FLAG);
if (file->lfn) if (file->lfn)
lfn_remove(file->lfn_offset, file->offset); lfn_remove(file->lfn_offset, file->offset);
for (cluster = FSTART(file, fs); cluster > 0 && cluster <
fs->data_clusters + 2; cluster = next_cluster(fs, cluster))
set_owner(fs, cluster, NULL);
--n_files; --n_files;
} }
static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters) static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters)
{ {
int deleting; int deleting;
uint32_t walk, next; uint32_t walk, next;
walk = FSTART(file, fs); walk = FSTART(file, fs);
if ((deleting = !clusters)) if ((deleting = !clusters))
skipping to change at line 391 skipping to change at line 259
set_fat(fs, walk, -1); set_fat(fs, walk, -1);
walk = next; walk = next;
} }
} }
static void auto_rename(DOS_FILE * file) static void auto_rename(DOS_FILE * file)
{ {
DOS_FILE *first, *walk; DOS_FILE *first, *walk;
uint32_t number; uint32_t number;
if (!file->offset) if (!file->offset) {
printf("Cannot rename FAT32 root dir\n");
return; /* cannot rename FAT32 root dir */ return; /* cannot rename FAT32 root dir */
}
first = file->parent ? file->parent->first : root; first = file->parent ? file->parent->first : root;
number = 0; number = 0;
while (1) { while (1) {
char num[8]; char num[8];
sprintf(num, "%07lu", (unsigned long)number); sprintf(num, "%07lu", (unsigned long)number);
memcpy(file->dir_ent.name, "FSCK", 4); memcpy(file->dir_ent.name, "FSCK", 4);
memcpy(file->dir_ent.name + 4, num, 7); memcpy(file->dir_ent.name + 4, num, 7);
for (walk = first; walk; walk = walk->next) for (walk = first; walk; walk = walk->next)
if (walk != file if (walk != file
&& !strncmp((const char *)walk->dir_ent.name, && !strncmp((const char *)walk->dir_ent.name,
skipping to change at line 416 skipping to change at line 286
if (file->dir_ent.lcase & FAT_NO_83NAME) { if (file->dir_ent.lcase & FAT_NO_83NAME) {
/* as we only assign a new 8.3 filename, reset flag that 8.3 name is not /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
present */ present */
file->dir_ent.lcase &= ~FAT_NO_83NAME; file->dir_ent.lcase &= ~FAT_NO_83NAME;
/* reset the attributes, only keep DIR and VOLUME */ /* reset the attributes, only keep DIR and VOLUME */
file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME); file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent); fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
} else { } else {
fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
} }
if (file->lfn) if (file->lfn) {
lfn_fix_checksum(file->lfn_offset, file->offset, lfn_remove(file->lfn_offset, file->offset);
(const char *)file->dir_ent.name); file->lfn = NULL;
}
return; return;
} }
number++; number++;
if (number > 9999999) { if (number > 9999999) {
die("Too many files need repair."); die("Too many files need repair.");
} }
} }
die("Can't generate a unique name."); die("Can't generate a unique name.");
} }
static void rename_file(DOS_FILE * file) static void rename_file(DOS_FILE * file)
{ {
unsigned char name[46]; unsigned char name[46];
unsigned char *walk, *here; unsigned char *walk, *here;
if (!file->offset) { if (!file->offset) {
printf("Cannot rename FAT32 root dir\n"); printf("Cannot rename FAT32 root dir\n");
return; /* cannot rename FAT32 root dir */ return; /* cannot rename FAT32 root dir */
} }
while (1) { while (1) {
printf("New name: "); if (get_line("New name", (char *)name, 45)) {
fflush(stdout);
if (fgets((char *)name, 45, stdin)) {
if ((here = (unsigned char *)strchr((const char *)name, '\n'))) if ((here = (unsigned char *)strchr((const char *)name, '\n')))
*here = 0; *here = 0;
for (walk = (unsigned char *)strrchr((const char *)name, 0); for (walk = (unsigned char *)strrchr((const char *)name, 0);
walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ; walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
walk[1] = 0; walk[1] = 0;
for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ; for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
if (file_cvt(walk, file->dir_ent.name)) { if (file_cvt(walk, file->dir_ent.name)) {
if (file->dir_ent.lcase & FAT_NO_83NAME) { if (file->dir_ent.lcase & FAT_NO_83NAME) {
/* as we only assign a new 8.3 filename, reset flag that 8.3 name is not /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
present */ present */
file->dir_ent.lcase &= ~FAT_NO_83NAME; file->dir_ent.lcase &= ~FAT_NO_83NAME;
/* reset the attributes, only keep DIR and VOLUME */ /* reset the attributes, only keep DIR and VOLUME */
file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME); file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent); fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
} else { } else {
fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
} }
if (file->lfn) if (file->lfn) {
lfn_fix_checksum(file->lfn_offset, file->offset, lfn_remove(file->lfn_offset, file->offset);
(const char *)file->dir_ent.name); file->lfn = NULL;
}
return; return;
} }
} }
} }
} }
static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots) static uint32_t scan_free_entry(DOS_FS * fs, DOS_FILE * this)
{ {
const char *name; uint32_t clu_num, offset;
int i;
DIR_ENT de;
name = i = 2 * sizeof(DIR_ENT); /* Skip '.' and '..' slots */
strncmp((const char *)file->dir_ent.name, MSDOS_DOT, clu_num = FSTART(this, fs);
MSDOS_NAME) ? ".." : "."; while (clu_num > 0 && clu_num != -1) {
if (!(file->dir_ent.attr & ATTR_DIR)) { offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
printf("%s\n Is a non-directory.\n", path_name(file)); fs_read(offset, sizeof(DIR_ENT), &de);
if (interactive) if (IS_FREE(de.name))
printf("1) Drop it\n2) Auto-rename\n3) Rename\n" return offset;
"4) Convert to directory\n"); i += sizeof(DIR_ENT);
else if (!(i % fs->cluster_size))
printf(" Auto-renaming it.\n"); if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
switch (interactive ? get_key("1234", "?") : '2') { break;
case '1': }
drop_file(fs, file);
return 1; return 0;
case '2': }
auto_rename(file);
printf(" Renamed to %s\n", file_name(file->dir_ent.name)); static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dotdot)
return 0; {
case '3': const char *name, *ent;
rename_file(file); uint32_t new_offset, start;
return 0;
case '4': if (dotdot) {
MODIFY(file, size, htole32(0)); name = "..";
MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR); ent = MSDOS_DOTDOT;
break; if (!file->parent->parent) {
start = 0;
} else {
start = FSTART(file->parent->parent, fs);
if (start == fs->root_cluster)
start = 0;
}
} else {
name = ".";
ent = MSDOS_DOT;
start = FSTART(file->parent, fs);
}
if (!(file->dir_ent.attr & ATTR_DIR) || (FSTART(file, fs) != start) ||
strncmp((const char *)(file->dir_ent.name), ent, MSDOS_NAME)) {
if (IS_FREE(file->dir_ent.name)) {
printf("%s\n Expected a valid '%s' entry in the %s slot, found free
entry.\n",
path_name(file->parent), name, dotdot ? "second" : "first");
switch (get_choice(1, " Creating.",
2,
1, "Create entry",
2, "Drop parent")) {
case 1:
goto conjure;
case 2:
drop_file(fs, file->parent);
return 1;
}
}
if (!strncmp((const char *)(file->dir_ent.name), ent, MSDOS_NAME)) {
printf("%s\n Invalid '%s' entry in the %s slot. Fixing.\n",
path_name(file->parent), name, dotdot ? "second" : "first");
MODIFY_START(file, start, fs);
MODIFY(file, attr, ATTR_DIR);
} else {
printf("%s\n Expected a valid '%s' entry in this slot.\n",
path_name(file), name);
switch (get_choice(3, " Moving entry down.",
3,
1, "Drop entry",
2, "Drop parent",
3, "Move entry down")) {
case 1:
drop_file(fs, file);
goto conjure;
case 2:
drop_file(fs, file->parent);
return 1;
case 3:
new_offset = scan_free_entry(fs, file->parent);
if (!new_offset) {
printf("No free entry found.\n");
return 0;
}
fs_write(new_offset, sizeof(file->dir_ent), &file->dir_ent);
goto conjure;
}
} }
} }
if (!dots) { if (file->dir_ent.lcase & FAT_NO_83NAME) {
printf("Root contains directory \"%s\". Dropping it.\n", name); /* Some versions of mtools write these directory entries with random data
drop_file(fs, file); in
return 1; this field. */
printf("%s\n Is a dot with no 8.3 name flag set, clearing.\n", path_name
(file));
file->dir_ent.lcase &= ~FAT_NO_83NAME;
MODIFY(file, lcase, file->dir_ent.lcase);
} }
return 0;
conjure:
memset(&file->dir_ent, 0, sizeof(DIR_ENT));
memcpy(file->dir_ent.name, ent, MSDOS_NAME);
fs_write(file->offset, sizeof(file->dir_ent), &file->dir_ent);
MODIFY_START(file, start, fs);
MODIFY(file, attr, ATTR_DIR);
return 0; return 0;
} }
static int check_file(DOS_FS * fs, DOS_FILE * file) static int check_file(DOS_FS * fs, DOS_FILE * file)
{ {
DOS_FILE *owner; DOS_FILE *owner;
int restart; int restart;
uint32_t expect, curr, this, clusters, prev, walk, clusters2; uint32_t parent, grandp, curr, this, clusters, prev, walk, clusters2;
if (IS_FREE(file->dir_ent.name))
return 0;
if (file->dir_ent.attr & ATTR_DIR) { if (file->dir_ent.attr & ATTR_DIR) {
if (le32toh(file->dir_ent.size)) { if (le32toh(file->dir_ent.size)) {
printf("%s\n Directory has non-zero size. Fixing it.\n", printf("%s\n Directory has non-zero size. Fixing it.\n",
path_name(file)); path_name(file));
MODIFY(file, size, htole32(0)); MODIFY(file, size, htole32(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 (%lu) does not point to parent (%lu)\n",
path_name(file), (unsigned long)FSTART(file, fs), (long)ex
pect);
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), (unsigned long)FSTART(file, fs), (unsigne
d long)expect);
MODIFY_START(file, expect, fs);
}
return 0;
}
if (FSTART(file, fs) == 0) { if (FSTART(file, fs) == 0) {
printf("%s\n Start does point to root directory. Deleting dir. \n", printf("%s\n Start does point to root directory. Deleting dir. \n",
path_name(file)); path_name(file));
MODIFY(file, name[0], DELETED_FLAG); MODIFY(file, name[0], DELETED_FLAG);
return 0; return 0;
} }
if (file->parent) {
parent = FSTART(file->parent, fs);
grandp = file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
if (fs->root_cluster && grandp == fs->root_cluster)
grandp = 0;
if (FSTART(file, fs) == parent) {
printf("%s\n Start does point to containing directory. Deleting e
ntry.\n",
path_name(file));
MODIFY(file, name[0], DELETED_FLAG);
MODIFY_START(file, 0, fs);
return 0;
}
if (FSTART(file, fs) == grandp) {
printf("%s\n Start does point to containing directory's parent. D
eleting entry.\n",
path_name(file));
MODIFY(file, name[0], DELETED_FLAG);
MODIFY_START(file, 0, fs);
return 0;
}
}
} }
if (FSTART(file, fs) == 1) { if (FSTART(file, fs) == 1) {
printf("%s\n Bad start cluster 1. Truncating file.\n", printf("%s\n Bad start cluster 1. Truncating file.\n",
path_name(file)); path_name(file));
if (!file->offset) if (!file->offset)
die("Bad FAT32 root directory! (bad start cluster 1)\n"); die("Bad FAT32 root directory! (bad start cluster 1)\n");
MODIFY_START(file, 0, fs); MODIFY_START(file, 0, fs);
} }
if (FSTART(file, fs) >= fs->data_clusters + 2) { if (FSTART(file, fs) >= fs->data_clusters + 2) {
printf printf
skipping to change at line 587 skipping to change at line 528
path_name(file), curEntry.value ? "bad" : "free", (unsigned lo ng)curr); path_name(file), curEntry.value ? "bad" : "free", (unsigned lo ng)curr);
if (prev) if (prev)
set_fat(fs, prev, -1); set_fat(fs, prev, -1);
else if (!file->offset) else if (!file->offset)
die("FAT32 root dir starts with a bad cluster!"); die("FAT32 root dir starts with a bad cluster!");
else else
MODIFY_START(file, 0, fs); MODIFY_START(file, 0, fs);
break; break;
} }
if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <= if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <=
(uint64_t)clusters * fs->cluster_size) { clusters * fs->cluster_size) {
printf printf
("%s\n File size is %u bytes, cluster chain length is > %llu " ("%s\n File size is %u bytes, cluster chain length is > %u "
"bytes.\n Truncating file to %u bytes.\n", path_name(file), "bytes.\n Truncating file to %u bytes.\n", path_name(file),
le32toh(file->dir_ent.size), le32toh(file->dir_ent.size),
(unsigned long long)clusters * fs->cluster_size, (unsigned)clusters * fs->cluster_size,
le32toh(file->dir_ent.size)); le32toh(file->dir_ent.size));
truncate_file(fs, file, clusters); truncate_file(fs, file, clusters);
break; break;
} }
if ((owner = get_owner(fs, curr))) { if ((owner = get_owner(fs, curr))) {
int do_trunc = 0; int do_trunc = 0;
printf("%s and\n", path_name(owner)); printf("%s and\n", path_name(owner));
printf("%s\n share clusters.\n", path_name(file)); printf("%s\n share clusters.\n", path_name(file));
clusters2 = 0; clusters2 = 0;
for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk = for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
next_cluster(fs, walk)) next_cluster(fs, walk))
if (walk == curr) if (walk == curr)
break; break;
else else {
if ((unsigned long long)clusters2 * fs->cluster_size >= UINT3
2_MAX)
die("Internal error: File size is larger than 2^32-1");
clusters2++; clusters2++;
}
restart = file->dir_ent.attr & ATTR_DIR; restart = file->dir_ent.attr & ATTR_DIR;
if (!owner->offset) { if (!owner->offset) {
printf(" Truncating second to %llu bytes because first " printf(" Truncating second to %u bytes because first "
"is FAT32 root dir.\n", "is FAT32 root dir.\n",
(unsigned long long)clusters * fs->cluster_size); (unsigned)clusters * fs->cluster_size);
do_trunc = 2; do_trunc = 2;
} else if (!file->offset) { } else if (!file->offset) {
printf(" Truncating first to %llu bytes because second " printf(" Truncating first to %u bytes because second "
"is FAT32 root dir.\n", "is FAT32 root dir.\n",
(unsigned long long)clusters2 * fs->cluster_size); (unsigned)clusters2 * fs->cluster_size);
do_trunc = 1; do_trunc = 1;
} else if (interactive) } else {
printf("1) Truncate first to %llu bytes%s\n" char *trunc_first_string;
"2) Truncate second to %llu bytes\n", char *trunc_second_string;
(unsigned long long)clusters2 * fs->cluster_size, char *noninteractive_string;
restart ? " and restart" : "",
(unsigned long long)clusters * fs->cluster_size); xasprintf(&trunc_first_string,
else "Truncate first to %u bytes%s",
printf(" Truncating second to %llu bytes.\n", (unsigned)clusters2 * fs->cluster_size,
(unsigned long long)clusters * fs->cluster_size); restart ? " and restart" : "");
if (do_trunc != 2 xasprintf(&trunc_second_string,
&& (do_trunc == 1 "Truncate second to %u bytes",
|| (interactive && get_key("12", "?") == '1'))) { (unsigned)clusters * fs->cluster_size);
xasprintf(&noninteractive_string,
" Truncating second to %u bytes.",
(unsigned)clusters * fs->cluster_size);
do_trunc = get_choice(2, noninteractive_string,
2,
1, trunc_first_string,
2, trunc_second_string);
free(trunc_first_string);
free(trunc_second_string);
free(noninteractive_string);
}
if (do_trunc == 1) {
prev = 0; prev = 0;
clusters = 0; clusters = 0;
for (this = FSTART(owner, fs); this > 0 && this != -1; this = for (this = FSTART(owner, fs); this > 0 && this != -1; this =
next_cluster(fs, this)) { next_cluster(fs, this)) {
if (this == curr) { if (this == curr) {
if (prev) if (prev)
set_fat(fs, prev, -1); set_fat(fs, prev, -1);
else else
MODIFY_START(owner, 0, fs); MODIFY_START(owner, 0, fs);
MODIFY(owner, size, MODIFY(owner, size, htole32(clusters * fs->cluster_size))
htole32((uint64_t)clusters * ;
fs->cluster_size));
if (restart) if (restart)
return 1; return 1;
while (this > 0 && this != -1) { while (this > 0 && this != -1) {
set_owner(fs, this, NULL); set_owner(fs, this, NULL);
this = next_cluster(fs, this); this = next_cluster(fs, this);
} }
this = curr; this = curr;
break; break;
} }
if ((unsigned long long)clusters * fs->cluster_size >= UINT32
_MAX)
die("Internal error: File size is larger than 2^32-1");
clusters++; clusters++;
prev = this; prev = this;
} }
if (this != curr) if (this != curr)
die("Internal error: didn't find cluster %d in chain" die("Internal error: didn't find cluster %d in chain"
" starting at %d", curr, FSTART(owner, fs)); " starting at %d", curr, FSTART(owner, fs));
} else { } else {
if (prev) if (prev)
set_fat(fs, prev, -1); set_fat(fs, prev, -1);
else else
MODIFY_START(file, 0, fs); MODIFY_START(file, 0, fs);
break; break;
} }
} }
set_owner(fs, curr, file); set_owner(fs, curr, file);
if ((unsigned long long)clusters * fs->cluster_size >= UINT32_MAX)
die("Internal error: File size is larger than 2^32-1");
clusters++; clusters++;
prev = curr; prev = curr;
} }
if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) > if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) >
(uint64_t)clusters * fs->cluster_size) { clusters * fs->cluster_size) {
printf printf
("%s\n File size is %u bytes, cluster chain length is %llu bytes." ("%s\n File size is %u bytes, cluster chain length is %u bytes."
"\n Truncating file to %llu bytes.\n", path_name(file), "\n Truncating file to %u bytes.\n", path_name(file),
le32toh(file->dir_ent.size), le32toh(file->dir_ent.size),
(unsigned long long)clusters * fs->cluster_size, (unsigned)clusters * fs->cluster_size,
(unsigned long long)clusters * fs->cluster_size); (unsigned)clusters * fs->cluster_size);
MODIFY(file, size, MODIFY(file, size,
htole32((uint64_t)clusters * fs->cluster_size)); htole32(clusters * fs->cluster_size));
} }
return 0; return 0;
} }
static int check_files(DOS_FS * fs, DOS_FILE * start) static int check_files(DOS_FS * fs, DOS_FILE * start)
{ {
while (start) { while (start) {
if (check_file(fs, start)) if (check_file(fs, start))
return 1; return 1;
start = start->next; start = start->next;
} }
return 0; return 0;
} }
static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots) static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
{ {
DOS_FILE *parent, **walk, **scan; DOS_FILE *parent, **walk, **scan;
int dot, dotdot, skip, redo; int skip, redo;
int good, bad; int good, bad;
if (!*root) if (!*root)
return 0; return 0;
parent = (*root)->parent; parent = (*root)->parent;
good = bad = 0; good = bad = 0;
for (walk = root; *walk; walk = &(*walk)->next) for (walk = root; *walk; walk = &(*walk)->next)
if (bad_name(*walk)) if (bad_name(*walk))
bad++; bad++;
else else
good++; good++;
if (*root && parent && good + bad > 4 && bad > good / 2) { if (*root && parent && good + bad > 4 && bad > good / 2) {
printf("%s\n Has a large number of bad entries. (%d/%d)\n", printf("%s\n Has a large number of bad entries. (%d/%d)\n",
path_name(parent), bad, good + bad); path_name(parent), bad, good + bad);
if (!dots) if (!dots)
printf(" Not dropping root directory.\n"); printf(" Not dropping root directory.\n");
else if (!interactive) else if (get_choice(2, " Not dropping it in auto-mode.",
printf(" Not dropping it in auto-mode.\n"); 2,
else if (get_key("yn", "Drop directory ? (y/n)") == 'y') { 1, "Drop directory",
2, "Keep directory") == 1) {
truncate_file(fs, parent, 0); truncate_file(fs, parent, 0);
MODIFY(parent, name[0], DELETED_FLAG); MODIFY(parent, name[0], DELETED_FLAG);
/* buglet: deleted directory stays in the list. */ /* buglet: deleted directory stays in the list. */
return 1; return 1;
} }
} }
dot = dotdot = redo = 0; redo = 0;
walk = root; walk = root;
while (*walk) { 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)) { if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
puts(path_name(*walk)); puts(path_name(*walk));
printf(" Bad short file name (%s).\n", printf(" Bad short file name (%s).\n",
file_name((*walk)->dir_ent.name)); file_name((*walk)->dir_ent.name));
if (interactive) switch (get_choice(3, " Auto-renaming it.",
printf("1) Drop file\n2) Rename file\n3) Auto-rename\n" 4,
"4) Keep it\n"); 1, "Drop file",
else 2, "Rename file",
printf(" Auto-renaming it.\n"); 3, "Auto-rename",
switch (interactive ? get_key("1234", "?") : '3') { 4, "Keep it")) {
case '1': case 1:
drop_file(fs, *walk); drop_file(fs, *walk);
walk = &(*walk)->next; walk = &(*walk)->next;
continue; continue;
case '2': case 2:
rename_file(*walk); rename_file(*walk);
redo = 1; redo = 1;
break; break;
case '3': case 3:
auto_rename(*walk); auto_rename(*walk);
printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name)); printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name));
break; break;
case '4': case 4:
break; break;
} }
} }
/* don't check for duplicates of the volume label */ /* don't check for duplicates of the volume label */
if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) { if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
scan = &(*walk)->next; scan = &(*walk)->next;
skip = 0; skip = 0;
while (*scan && !skip) { while (*scan && !skip) {
if (!((*scan)->dir_ent.attr & ATTR_VOLUME) && if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
!memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name, !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
MSDOS_NAME)) { MSDOS_NAME)) {
printf("%s\n Duplicate directory entry.\n First %s\n", printf("%s\n Duplicate directory entry.\n First %s\n",
path_name(*walk), file_stat(*walk)); path_name(*walk), file_stat(*walk));
printf(" Second %s\n", file_stat(*scan)); printf(" Second %s\n", file_stat(*scan));
if (interactive) switch (get_choice(6, " Auto-renaming second.",
printf 6,
("1) Drop first\n2) Drop second\n3) Rename first\n" 1, "Drop first",
"4) Rename second\n5) Auto-rename first\n" 2, "Drop second",
"6) Auto-rename second\n"); 3, "Rename first",
else 4, "Rename second",
printf(" Auto-renaming second.\n"); 5, "Auto-rename first",
switch (interactive ? get_key("123456", "?") : '6') { 6, "Auto-rename second")) {
case '1': case 1:
drop_file(fs, *walk); drop_file(fs, *walk);
*walk = (*walk)->next; *walk = (*walk)->next;
skip = 1; skip = 1;
break; break;
case '2': case 2:
drop_file(fs, *scan); drop_file(fs, *scan);
*scan = (*scan)->next; *scan = (*scan)->next;
continue; continue;
case '3': case 3:
rename_file(*walk); rename_file(*walk);
printf(" Renamed to %s\n", path_name(*walk)); printf(" Renamed to %s\n", path_name(*walk));
redo = 1; redo = 1;
break; break;
case '4': case 4:
rename_file(*scan); rename_file(*scan);
printf(" Renamed to %s\n", path_name(*walk)); printf(" Renamed to %s\n", path_name(*walk));
redo = 1; redo = 1;
break; break;
case '5': case 5:
auto_rename(*walk); auto_rename(*walk);
printf(" Renamed to %s\n", printf(" Renamed to %s\n",
file_name((*walk)->dir_ent.name)); file_name((*walk)->dir_ent.name));
break; break;
case '6': case 6:
auto_rename(*scan); auto_rename(*scan);
printf(" Renamed to %s\n", printf(" Renamed to %s\n",
file_name((*scan)->dir_ent.name)); file_name((*scan)->dir_ent.name));
break; break;
} }
} }
scan = &(*scan)->next; scan = &(*scan)->next;
} }
if (skip) if (skip)
continue; continue;
} }
if (!redo) if (!redo)
walk = &(*walk)->next; walk = &(*walk)->next;
else { else {
walk = root; walk = root;
dot = dotdot = redo = 0; 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; return 0;
} }
/** /**
* Check a dentry's cluster chain for bad clusters. * Check a dentry's cluster chain for bad clusters.
* If requested, we verify readability and mark unreadable clusters as bad. * If requested, we verify readability and mark unreadable clusters as bad.
* *
* @param[inout] fs Information about the filesystem * @param[inout] fs Information about the filesystem
* @param[in] file dentry to check * @param[in] file dentry to check
* @param[in] read_test Nonzero == verify that dentry's clusters can * @param[in] read_test Nonzero == verify that dentry's clusters can
skipping to change at line 1005 skipping to change at line 947
if (type == fdt_undelete) if (type == fdt_undelete)
undelete(fs, new); undelete(fs, new);
**chain = new; **chain = new;
*chain = &new->next; *chain = &new->next;
if (list) { if (list) {
printf("Checking file %s", path_name(new)); printf("Checking file %s", path_name(new));
if (new->lfn) if (new->lfn)
printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */ printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */
printf("\n"); printf("\n");
} }
/* Don't include root directory, '.', or '..' in the total file count */ /* Don't include root directory in the total file count */
if (offset && if (offset)
strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 &&
strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0)
++n_files; ++n_files;
test_file(fs, new, test); /* Bad cluster check */ test_file(fs, new, test); /* Bad cluster check */
} }
static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp); static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp) static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
{ {
DOS_FILE **chain; DOS_FILE **chain;
int i; int i;
uint32_t clu_num; uint32_t clu_num;
chain = &this->first; chain = &this->first;
i = 0; i = 0;
clu_num = FSTART(this, fs); clu_num = FSTART(this, fs);
new_dir(); new_dir();
if (clu_num != 0 && clu_num != -1 && this->offset) {
DOS_FILE file;
file.lfn = NULL;
file.lfn_offset = 0;
file.next = NULL;
file.parent = this;
file.first = NULL;
file.offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
fs_read(file.offset, sizeof(DIR_ENT), &file.dir_ent);
if (handle_dot(fs, &file, 0))
return 1;
i += sizeof(DIR_ENT);
file.offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
fs_read(file.offset, sizeof(DIR_ENT), &file.dir_ent);
if (handle_dot(fs, &file, 1))
return 1;
i += sizeof(DIR_ENT);
}
while (clu_num > 0 && clu_num != -1) { while (clu_num > 0 && clu_num != -1) {
add_file(fs, &chain, this, add_file(fs, &chain, this,
cluster_start(fs, clu_num) + (i % fs->cluster_size), cp); cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
i += sizeof(DIR_ENT); i += sizeof(DIR_ENT);
if (!(i % fs->cluster_size)) if (!(i % fs->cluster_size))
if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1) if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
break; break;
} }
lfn_check_orphaned(); lfn_check_orphaned();
if (check_dir(fs, &this->first, this->offset)) if (check_dir(fs, &this->first, this->offset))
skipping to change at line 1056 skipping to change at line 1017
* @param[in] cp * @param[in] cp
* *
* @return 0 Success * @return 0 Success
* @return 1 Error * @return 1 Error
*/ */
static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp) static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
{ {
DOS_FILE *walk; DOS_FILE *walk;
for (walk = parent ? parent->first : root; walk; walk = walk->next) for (walk = parent ? parent->first : root; walk; walk = walk->next)
if (walk->dir_ent.attr & ATTR_DIR) if (!IS_FREE(walk->dir_ent.name) && (walk->dir_ent.attr & ATTR_DIR))
if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME) if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
&& strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT, return 1;
MSDOS_NAME))
if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
return 1;
return 0; return 0;
} }
/** /**
* Scan all directory and file information for errors. * Scan all directory and file information for errors.
* *
* @param[inout] fs Information about the filesystem * @param[inout] fs Information about the filesystem
* *
* @return 0 Success * @return 0 Success
* @return 1 Error * @return 1 Error
skipping to change at line 1094 skipping to change at line 1052
for (i = 0; i < fs->root_entries; i++) for (i = 0; i < fs->root_entries; i++)
add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT), add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
&fp_root); &fp_root);
} }
lfn_check_orphaned(); lfn_check_orphaned();
(void)check_dir(fs, &root, 0); (void)check_dir(fs, &root, 0);
if (check_files(fs, root)) if (check_files(fs, root))
return 1; return 1;
return subdirs(fs, NULL, &fp_root); return subdirs(fs, NULL, &fp_root);
} }
static char print_fat_dirty_state(void)
{
printf("Dirty bit is set. Fs was not properly unmounted and"
" some data may be corrupt.\n");
return get_choice(1, " Automatically removing dirty bit.",
2,
1, "Remove dirty bit",
2, "No action");
}
void check_dirty_bits(DOS_FS * fs)
{
if (fs->fat_bits == 32) {
struct boot_sector b32;
FAT_ENTRY fat32_flags;
get_fat(&fat32_flags, fs->fat, 1, fs);
fs_read(0, sizeof(b32), &b32);
if ((b32.boot_flags & FAT_STATE_DIRTY) || !(fat32_flags.value & FAT32_FLA
G_CLEAN_SHUTDOWN)) {
if (print_fat_dirty_state() == 1) {
if (b32.boot_flags & FAT_STATE_DIRTY) {
b32.boot_flags &= ~FAT_STATE_DIRTY;
fs_write(0, sizeof(b32), &b32);
}
if (!(fat32_flags.value & FAT32_FLAG_CLEAN_SHUTDOWN)) {
uint32_t *new_flags_ptr = (uint32_t *)(fs->fat + 4);
*new_flags_ptr = htole32(fat32_flags.value | FAT32_FLAG_CLEAN
_SHUTDOWN | (fat32_flags.reserved << 28));
fs_write(fs->fat_start + 4, 4, new_flags_ptr);
if (fs->nfats > 1)
fs_write(fs->fat_start + 4 + fs->fat_size, 4, new_flags_p
tr);
}
}
}
} else {
struct boot_sector_16 b16;
FAT_ENTRY fat16_flags;
int fat16_is_dirty = 0;
fs_read(0, sizeof(b16), &b16);
if (fs->fat_bits == 16) {
get_fat(&fat16_flags, fs->fat, 1, fs);
fat16_is_dirty = !(fat16_flags.value & FAT16_FLAG_CLEAN_SHUTDOWN);
}
if ((b16.boot_flags & FAT_STATE_DIRTY) || fat16_is_dirty) {
if (print_fat_dirty_state() == 1) {
if (b16.boot_flags & FAT_STATE_DIRTY) {
b16.boot_flags &= ~FAT_STATE_DIRTY;
fs_write(0, sizeof(b16), &b16);
}
if (fat16_is_dirty) {
uint16_t *new_flags_ptr = (uint16_t *)(fs->fat + 2);
*new_flags_ptr = htole16(fat16_flags.value | FAT16_FLAG_CLEAN
_SHUTDOWN);
fs_write(fs->fat_start + 2, 2, new_flags_ptr);
if (fs->nfats > 1)
fs_write(fs->fat_start + 2 + fs->fat_size, 2, new_flags_p
tr);
}
}
}
}
}
static void get_new_label(char doslabel[12])
{
char newlabel[256];
size_t len;
char *p;
int ret;
int i;
while (1) {
if (get_line("New label", newlabel, sizeof(newlabel))) {
if ((p = strchr(newlabel, '\n')))
*p = 0;
len = mbstowcs(NULL, newlabel, 0);
if (len != (size_t)-1 && len > 11) {
printf("Label can be no longer than 11 characters\n");
continue;
}
if (!local_string_to_dos_string(doslabel, newlabel, 12)) {
printf("Error when processing label\n");
continue;
}
for (i = strlen(doslabel); i < 11; ++i)
doslabel[i] = ' ';
doslabel[11] = 0;
ret = validate_volume_label(doslabel);
if ((ret && only_uppercase_label) || (ret & ~0x1)) {
printf("New label is invalid\n");
continue;
} else if (ret & 0x1) {
printf("Warning: lowercase labels might not work properly on som
e systems\n");
}
break;
}
}
}
static int check_boot_label(DOS_FS *fs)
{
char doslabel[12];
wchar_t wlabel[12];
int ret;
int i;
ret = validate_volume_label(fs->label);
if (ret & ~0x1) {
printf("Label '%s' stored in boot sector is not valid.\n", pretty_label(
fs->label));
switch (get_choice(1, " Auto-removing label from boot sector.",
2,
1, "Remove invalid label from boot sector",
2, "Enter new label")) {
case 1:
write_boot_label(fs, "NO NAME ");
memcpy(fs->label, "NO NAME ", 11);
return 1;
case 2:
get_new_label(doslabel);
write_boot_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
return 1;
}
} else if ((ret & 0x1) && only_uppercase_label) {
printf("Label '%s' stored in boot sector contains lowercase characters.\
n", pretty_label(fs->label));
switch (get_choice(1, " Auto-changing lowercase characters to uppercase
",
3,
1, "Change lowercase characters to uppercase",
2, "Remove invalid label",
2, "Set new label")) {
case 1:
if (!dos_string_to_wchar_string(wlabel, fs->label, sizeof(wlabel)))
die("Cannot change lowercase characters to uppercase.");
for (i = 0; i < 11; ++i)
wlabel[i] = towupper(wlabel[i]);
if (!wchar_string_to_dos_string(doslabel, wlabel, sizeof(doslabel)))
die("Cannot change lowercase characters to uppercase.");
write_boot_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
return 1;
case 2:
write_boot_label(fs, "NO NAME ");
memcpy(fs->label, "NO NAME ", 11);
return 1;
case 3:
get_new_label(doslabel);
write_boot_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
return 1;
}
}
return 0;
}
void check_label(DOS_FS *fs)
{
DIR_ENT de;
off_t offset;
char buffer[256];
char doslabel[12];
wchar_t wlabel[12];
int ret;
int i;
offset = find_volume_de(fs, &de);
if (offset == 0 && memcmp(fs->label, "NO NAME ", 11) != 0)
check_boot_label(fs);
if (offset == 0 && memcmp(fs->label, "NO NAME ", 11) != 0) {
printf("Label in boot sector is '%s', but there is no volume label in ro
ot directory.\n", pretty_label(fs->label));
switch (get_choice(1, " Auto-removing label from boot sector.",
2,
1, "Remove label from boot sector",
2, "Copy label from boot sector to root directory"))
{
case 1:
write_boot_label(fs, "NO NAME ");
memcpy(fs->label, "NO NAME ", 11);
break;
case 2:
write_volume_label(fs, fs->label);
offset = find_volume_de(fs, &de);
break;
}
}
if (offset != 0) {
memcpy(doslabel, de.name, 11);
if (doslabel[0] == 0x05)
doslabel[0] = 0xe5;
ret = validate_volume_label(doslabel);
if (ret & ~0x1) {
printf("Volume label '%s' stored in root directory is not valid.\n",
pretty_label(doslabel));
switch (get_choice(1, " Auto-removing label.",
2,
1, "Remove invalid label",
2, "Set new label")) {
case 1:
remove_label(fs);
memcpy(fs->label, "NO NAME ", 11);
offset = 0;
break;
case 2:
get_new_label(doslabel);
write_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
break;
}
} else if ((ret & 0x1) && only_uppercase_label) {
printf("Volume label '%s' stored in root directory contains lowercas
e characters.\n", pretty_label(doslabel));
switch (get_choice(1, " Auto-changing lowercase characters to upper
case",
3,
1, "Change lowercase characters to uppercase",
2, "Remove invalid label",
2, "Set new label")) {
case 1:
if (!dos_string_to_wchar_string(wlabel, doslabel, sizeof(wlabel)
))
die("Cannot change lowercase characters to uppercase.");
for (i = 0; i < 11; ++i)
wlabel[i] = towupper(wlabel[i]);
if (!wchar_string_to_dos_string(doslabel, wlabel, sizeof(doslabe
l)))
die("Cannot change lowercase characters to uppercase.");
write_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
break;
case 2:
remove_label(fs);
memcpy(fs->label, "NO NAME ", 11);
offset = 0;
break;
case 3:
get_new_label(doslabel);
write_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
break;
}
}
}
again:
if (offset != 0 && memcmp(fs->label, "NO NAME ", 11) == 0 && memcmp(dosla
bel, "NO NAME ", 11) != 0) {
printf("There is no label in boot sector, but there is volume label '%s'
stored in root directory\n", pretty_label(doslabel));
switch (get_choice(1, " Auto-copying volume label from root directory t
o boot sector.",
2,
1, "Copy volume label from root directory to boot sec
tor",
2, "Remove volume label from root directory")) {
case 1:
write_boot_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
break;
case 2:
remove_label(fs);
offset = 0;
break;
}
}
if (offset != 0 && memcmp(fs->label, "NO NAME ", 11) != 0 && memcmp(fs->l
abel, doslabel, 11) != 0) {
strncpy(buffer, pretty_label(doslabel), sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = 0;
printf("Volume label '%s' stored in root directory and label '%s' stored
in boot sector and different.\n", buffer, pretty_label(fs->label));
switch (get_choice(1, " Auto-copying volume label from root directory t
o boot sector.",
2,
1, "Copy volume label from root directory to boot sec
tor",
2, "Copy label from boot sector to root directory"))
{
case 1:
write_boot_label(fs, doslabel);
memcpy(fs->label, doslabel, 11);
break;
case 2:
ret = check_boot_label(fs);
if (ret)
goto again;
write_volume_label(fs, fs->label);
offset = find_volume_de(fs, &de);
/* NOTE: doslabel is not updated */
break;
}
}
}
 End of changes. 64 change blocks. 
346 lines changed or deleted 311 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)