Unifying dosfsck and mkdosfs sources in common src directory.

Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
This commit is contained in:
Daniel Baumann
2008-09-26 18:04:02 +02:00
parent 7552d57527
commit 61e7466965
19 changed files with 0 additions and 0 deletions

418
src/boot.c Normal file
View File

@@ -0,0 +1,418 @@
/* boot.c - Read and analyze ia PC/MS-DOS boot sector */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include "common.h"
#include "dosfsck.h"
#include "io.h"
#include "boot.h"
#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
/* don't divide by zero */
static struct {
__u8 media;
char *descr;
} mediabytes[] = {
{ 0xf0, "5.25\" or 3.5\" HD floppy" },
{ 0xf8, "hard disk" },
{ 0xf9, "3,5\" 720k floppy 2s/80tr/9sec or "
"5.25\" 1.2M floppy 2s/80tr/15sec" },
{ 0xfa, "5.25\" 320k floppy 1s/80tr/8sec" },
{ 0xfb, "3.5\" 640k floppy 2s/80tr/8sec" },
{ 0xfc, "5.25\" 180k floppy 1s/40tr/9sec" },
{ 0xfd, "5.25\" 360k floppy 2s/40tr/9sec" },
{ 0xfe, "5.25\" 160k floppy 1s/40tr/8sec" },
{ 0xff, "5.25\" 320k floppy 2s/40tr/8sec" },
};
#if defined __alpha || defined __ia64__ || defined __s390x__ || defined __x86_64__ || defined __ppc64__
/* Unaligned fields must first be copied byte-wise */
#define GET_UNALIGNED_W(f) \
({ \
unsigned short __v; \
memcpy( &__v, &f, sizeof(__v) ); \
CF_LE_W( *(unsigned short *)&__v ); \
})
#else
#define GET_UNALIGNED_W(f) CF_LE_W( *(unsigned short *)&f )
#endif
static char *get_media_descr( unsigned char media )
{
int i;
for( i = 0; i < sizeof(mediabytes)/sizeof(*mediabytes); ++i ) {
if (mediabytes[i].media == media)
return( mediabytes[i].descr );
}
return( "undefined" );
}
static void dump_boot(DOS_FS *fs,struct boot_sector *b,unsigned lss)
{
unsigned short sectors;
printf("Boot sector contents:\n");
if (!atari_format) {
char id[9];
strncpy(id,b->system_id,8);
id[8] = 0;
printf("System ID \"%s\"\n",id);
}
else {
/* On Atari, a 24 bit serial number is stored at offset 8 of the boot
* sector */
printf("Serial number 0x%x\n",
b->system_id[5] | (b->system_id[6]<<8) | (b->system_id[7]<<16));
}
printf("Media byte 0x%02x (%s)\n",b->media,get_media_descr(b->media));
printf("%10d bytes per logical sector\n",GET_UNALIGNED_W(b->sector_size));
printf("%10d bytes per cluster\n",fs->cluster_size);
printf("%10d reserved sector%s\n",CF_LE_W(b->reserved),
CF_LE_W(b->reserved) == 1 ? "" : "s");
printf("First FAT starts at byte %llu (sector %llu)\n",
(unsigned long long)fs->fat_start,
(unsigned long long)fs->fat_start/lss);
printf("%10d FATs, %d bit entries\n",b->fats,fs->fat_bits);
printf("%10d bytes per FAT (= %u sectors)\n",fs->fat_size,
fs->fat_size/lss);
if (!fs->root_cluster) {
printf("Root directory starts at byte %llu (sector %llu)\n",
(unsigned long long)fs->root_start,
(unsigned long long)fs->root_start/lss);
printf("%10d root directory entries\n",fs->root_entries);
}
else {
printf( "Root directory start at cluster %lu (arbitrary size)\n",
fs->root_cluster);
}
printf("Data area starts at byte %llu (sector %llu)\n",
(unsigned long long)fs->data_start,
(unsigned long long)fs->data_start/lss);
printf("%10lu data clusters (%llu bytes)\n",fs->clusters,
(unsigned long long)fs->clusters*fs->cluster_size);
printf("%u sectors/track, %u heads\n",CF_LE_W(b->secs_track),
CF_LE_W(b->heads));
printf("%10u hidden sectors\n",
atari_format ?
/* On Atari, the hidden field is only 16 bit wide and unused */
(((unsigned char *)&b->hidden)[0] |
((unsigned char *)&b->hidden)[1] << 8) :
CF_LE_L(b->hidden));
sectors = GET_UNALIGNED_W( b->sectors );
printf("%10u sectors total\n", sectors ? sectors : CF_LE_L(b->total_sect));
}
static void check_backup_boot(DOS_FS *fs, struct boot_sector *b, int lss)
{
struct boot_sector b2;
if (!fs->backupboot_start) {
printf( "There is no backup boot sector.\n" );
if (CF_LE_W(b->reserved) < 3) {
printf( "And there is no space for creating one!\n" );
return;
}
if (interactive)
printf( "1) Create one\n2) Do without a backup\n" );
else printf( " Auto-creating backup boot block.\n" );
if (!interactive || get_key("12","?") == '1') {
int bbs;
/* The usual place for the backup boot sector is sector 6. Choose
* that or the last reserved sector. */
if (CF_LE_W(b->reserved) >= 7 && CF_LE_W(b->info_sector) != 6)
bbs = 6;
else {
bbs = CF_LE_W(b->reserved) - 1;
if (bbs == CF_LE_W(b->info_sector))
--bbs; /* this is never 0, as we checked reserved >= 3! */
}
fs->backupboot_start = bbs*lss;
b->backup_boot = CT_LE_W(bbs);
fs_write(fs->backupboot_start,sizeof(*b),b);
fs_write((off_t)offsetof(struct boot_sector,backup_boot),
sizeof(b->backup_boot),&b->backup_boot);
printf( "Created backup of boot sector in sector %d\n", bbs );
return;
}
else return;
}
fs_read(fs->backupboot_start,sizeof(b2),&b2);
if (memcmp(b,&b2,sizeof(b2)) != 0) {
/* there are any differences */
__u8 *p, *q;
int i, pos, first = 1;
char buf[20];
printf( "There are differences between boot sector and its backup.\n" );
printf( "Differences: (offset:original/backup)\n " );
pos = 2;
for( p = (__u8 *)b, q = (__u8 *)&b2, i = 0; i < sizeof(b2);
++p, ++q, ++i ) {
if (*p != *q) {
sprintf( buf, "%s%u:%02x/%02x", first ? "" : ", ",
(unsigned)(p-(__u8 *)b), *p, *q );
if (pos + strlen(buf) > 78) printf( "\n " ), pos = 2;
printf( "%s", buf );
pos += strlen(buf);
first = 0;
}
}
printf( "\n" );
if (interactive)
printf( "1) Copy original to backup\n"
"2) Copy backup to original\n"
"3) No action\n" );
else printf( " Not automatically fixing this.\n" );
switch (interactive ? get_key("123","?") : '3') {
case '1':
fs_write(fs->backupboot_start,sizeof(*b),b);
break;
case '2':
fs_write(0,sizeof(b2),&b2);
break;
default:
break;
}
}
}
static void init_fsinfo(struct info_sector *i)
{
i->magic = CT_LE_L(0x41615252);
i->signature = CT_LE_L(0x61417272);
i->free_clusters = CT_LE_L(-1);
i->next_cluster = CT_LE_L(2);
i->boot_sign = CT_LE_W(0xaa55);
}
static void read_fsinfo(DOS_FS *fs, struct boot_sector *b,int lss)
{
struct info_sector i;
if (!b->info_sector) {
printf( "No FSINFO sector\n" );
if (interactive)
printf( "1) Create one\n2) Do without FSINFO\n" );
else printf( " Not automatically creating it.\n" );
if (interactive && get_key("12","?") == '1') {
/* search for a free reserved sector (not boot sector and not
* backup boot sector) */
__u32 s;
for( s = 1; s < CF_LE_W(b->reserved); ++s )
if (s != CF_LE_W(b->backup_boot)) break;
if (s > 0 && s < CF_LE_W(b->reserved)) {
init_fsinfo(&i);
fs_write((off_t)s*lss,sizeof(i),&i);
b->info_sector = CT_LE_W(s);
fs_write((off_t)offsetof(struct boot_sector,info_sector),
sizeof(b->info_sector),&b->info_sector);
if (fs->backupboot_start)
fs_write(fs->backupboot_start+
offsetof(struct boot_sector,info_sector),
sizeof(b->info_sector),&b->info_sector);
}
else {
printf( "No free reserved sector found -- "
"no space for FSINFO sector!\n" );
return;
}
}
else return;
}
fs->fsinfo_start = CF_LE_W(b->info_sector)*lss;
fs_read(fs->fsinfo_start,sizeof(i),&i);
if (i.magic != CT_LE_L(0x41615252) ||
i.signature != CT_LE_L(0x61417272) ||
i.boot_sign != CT_LE_W(0xaa55)) {
printf( "FSINFO sector has bad magic number(s):\n" );
if (i.magic != CT_LE_L(0x41615252))
printf( " Offset %llu: 0x%08x != expected 0x%08x\n",
(unsigned long long)offsetof(struct info_sector,magic),
CF_LE_L(i.magic),0x41615252);
if (i.signature != CT_LE_L(0x61417272))
printf( " Offset %llu: 0x%08x != expected 0x%08x\n",
(unsigned long long)offsetof(struct info_sector,signature),
CF_LE_L(i.signature),0x61417272);
if (i.boot_sign != CT_LE_W(0xaa55))
printf( " Offset %llu: 0x%04x != expected 0x%04x\n",
(unsigned long long)offsetof(struct info_sector,boot_sign),
CF_LE_W(i.boot_sign),0xaa55);
if (interactive)
printf( "1) Correct\n2) Don't correct (FSINFO invalid then)\n" );
else printf( " Auto-correcting it.\n" );
if (!interactive || get_key("12","?") == '1') {
init_fsinfo(&i);
fs_write(fs->fsinfo_start,sizeof(i),&i);
}
else fs->fsinfo_start = 0;
}
if (fs->fsinfo_start)
fs->free_clusters = CF_LE_L(i.free_clusters);
}
void read_boot(DOS_FS *fs)
{
struct boot_sector b;
unsigned total_sectors;
unsigned short logical_sector_size, sectors;
unsigned fat_length;
off_t data_size;
fs_read(0,sizeof(b),&b);
logical_sector_size = GET_UNALIGNED_W(b.sector_size);
if (!logical_sector_size) die("Logical sector size is zero.");
fs->cluster_size = b.cluster_size*logical_sector_size;
if (!fs->cluster_size) die("Cluster size is zero.");
if (b.fats != 2 && b.fats != 1)
die("Currently, only 1 or 2 FATs are supported, not %d.\n",b.fats);
fs->nfats = b.fats;
sectors = GET_UNALIGNED_W(b.sectors);
total_sectors = sectors ? sectors : CF_LE_L(b.total_sect);
if (verbose) printf("Checking we can access the last sector of the filesystem\n");
/* Can't access last odd sector anyway, so round down */
fs_test((off_t)((total_sectors & ~1)-1)*(off_t)logical_sector_size,
logical_sector_size);
fat_length = CF_LE_W(b.fat_length) ?
CF_LE_W(b.fat_length) : CF_LE_L(b.fat32_length);
fs->fat_start = (off_t)CF_LE_W(b.reserved)*logical_sector_size;
fs->root_start = ((off_t)CF_LE_W(b.reserved)+b.fats*fat_length)*
logical_sector_size;
fs->root_entries = GET_UNALIGNED_W(b.dir_entries);
fs->data_start = fs->root_start+ROUND_TO_MULTIPLE(fs->root_entries <<
MSDOS_DIR_BITS,logical_sector_size);
data_size = (off_t)total_sectors*logical_sector_size-fs->data_start;
fs->clusters = data_size/fs->cluster_size;
fs->root_cluster = 0; /* indicates standard, pre-FAT32 root dir */
fs->fsinfo_start = 0; /* no FSINFO structure */
fs->free_clusters = -1; /* unknown */
if (!b.fat_length && b.fat32_length) {
fs->fat_bits = 32;
fs->root_cluster = CF_LE_L(b.root_cluster);
if (!fs->root_cluster && fs->root_entries)
/* M$ hasn't specified this, but it looks reasonable: If
* root_cluster is 0 but there is a separate root dir
* (root_entries != 0), we handle the root dir the old way. Give a
* warning, but convertig to a root dir in a cluster chain seems
* to complex for now... */
printf( "Warning: FAT32 root dir not in cluster chain! "
"Compability mode...\n" );
else if (!fs->root_cluster && !fs->root_entries)
die("No root directory!");
else if (fs->root_cluster && fs->root_entries)
printf( "Warning: FAT32 root dir is in a cluster chain, but "
"a separate root dir\n"
" area is defined. Cannot fix this easily.\n" );
fs->backupboot_start = CF_LE_W(b.backup_boot)*logical_sector_size;
check_backup_boot(fs,&b,logical_sector_size);
read_fsinfo(fs,&b,logical_sector_size);
}
else if (!atari_format) {
/* On real MS-DOS, a 16 bit FAT is used whenever there would be too
* much clusers otherwise. */
fs->fat_bits = (fs->clusters > MSDOS_FAT12) ? 16 : 12;
}
else {
/* On Atari, things are more difficult: GEMDOS always uses 12bit FATs
* on floppies, and always 16 bit on harddisks. */
fs->fat_bits = 16; /* assume 16 bit FAT for now */
/* If more clusters than fat entries in 16-bit fat, we assume
* it's a real MSDOS FS with 12-bit fat. */
if (fs->clusters+2 > fat_length*logical_sector_size*8/16 ||
/* if it's a floppy disk --> 12bit fat */
device_no == 2 ||
/* if it's a ramdisk or loopback device and has one of the usual
* floppy sizes -> 12bit FAT */
((device_no == 1 || device_no == 7) &&
(total_sectors == 720 || total_sectors == 1440 ||
total_sectors == 2880)))
fs->fat_bits = 12;
}
/* On FAT32, the high 4 bits of a FAT entry are reserved */
fs->eff_fat_bits = (fs->fat_bits == 32) ? 28 : fs->fat_bits;
fs->fat_size = fat_length*logical_sector_size;
fs->label = calloc(12, sizeof (__u8));
if (fs->fat_bits == 12 || fs->fat_bits == 16) {
struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b;
if (b16->extended_sig == 0x29)
memmove(fs->label, b16->label, 11);
else
fs->label = NULL;
} else if (fs->fat_bits == 32) {
if (b.extended_sig == 0x29)
memmove(fs->label, &b.label, 11);
else
fs->label = NULL;
}
if (fs->clusters > ((unsigned long long)fs->fat_size*8/fs->fat_bits)-2)
die("File system has %d clusters but only space for %d FAT entries.",
fs->clusters,((unsigned long long)fs->fat_size*8/fs->fat_bits)-2);
if (!fs->root_entries && !fs->root_cluster)
die("Root directory has zero size.");
if (fs->root_entries & (MSDOS_DPS-1))
die("Root directory (%d entries) doesn't span an integral number of "
"sectors.",fs->root_entries);
if (logical_sector_size & (SECTOR_SIZE-1))
die("Logical sector size (%d bytes) is not a multiple of the physical "
"sector size.",logical_sector_size);
#if 0 /* linux kernel doesn't check that either */
/* ++roman: On Atari, these two fields are often left uninitialized */
if (!atari_format && (!b.secs_track || !b.heads))
die("Invalid disk format in boot sector.");
#endif
if (verbose) dump_boot(fs,&b,logical_sector_size);
}
void write_label(DOS_FS *fs, char *label)
{
struct boot_sector b;
struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b;
int l = strlen(label);
while (l < 11)
label[l++] = ' ';
fs_read(0, sizeof(b), &b);
if (fs->fat_bits == 12 || fs->fat_bits == 16) {
if (b16->extended_sig != 0x29) {
b16->extended_sig = 0x29;
b16->serial = 0;
memmove(b16->fs_type, fs->fat_bits == 12 ?"FAT12 ":"FAT16 ", 8);
}
memmove(b16->label, label, 11);
} else if (fs->fat_bits == 32) {
if (b.extended_sig != 0x29) {
b.extended_sig = 0x29;
b.serial = 0;
memmove(b.fs_type, "FAT32 ", 8);
}
memmove(b.label, label, 11);
}
fs_write(0, sizeof(b), &b);
if (fs->fat_bits == 32 && fs->backupboot_start)
fs_write(fs->backupboot_start, sizeof(b), &b);
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

14
src/boot.h Normal file
View File

@@ -0,0 +1,14 @@
/* boot.h - Read and analyze ia PC/MS-DOS boot sector */
/* Written 1993 by Werner Almesberger */
#ifndef _BOOT_H
#define _BOOT_H
void read_boot(DOS_FS *fs);
void write_label(DOS_FS *fs, char *label);
/* Reads the boot sector from the currently open device and initializes *FS */
#endif

865
src/check.c Normal file
View File

@@ -0,0 +1,865 @@
/* check.c - Check and repair a PC/MS-DOS file system */
/* Written 1993 by Werner Almesberger */
/* 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++) {
if (clu_num >= fs->clusters+2) clu_num = 2;
if (!fs->fat[clu_num].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) {
sprintf(de->name,pattern,curr_num);
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(d2.name,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(de->name,pattern,curr_num);
for (scan = 0; scan < fs->root_entries; scan++)
if (scan != next_free &&
!strncmp(root[scan].name,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;
}
static char *path_name(DOS_FILE *file)
{
static char path[PATH_MAX*2];
if (!file) *path = 0;
else {
if (strlen(path_name(file->parent)) > PATH_MAX)
die("Path name too long.");
if (strcmp(path,"/") != 0) strcat(path,"/");
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(unsigned char *name)
{
int i, spc, suspicious = 0;
char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
/* Do not complain about (and auto-correct) the extended attribute files
* of OS/2. */
if (strncmp(name,"EA DATA SF",11) == 0 ||
strncmp(name,"WP ROOT SF",11) == 0) 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;
/* Only complain about too much suspicious chars in interactive mode,
* never correct them automatically. The chars are all basically ok, so we
* shouldn't auto-correct such names. */
if (interactive && suspicious > 6)
return 1;
return 0;
}
static void drop_file(DOS_FS *fs,DOS_FILE *file)
{
unsigned long cluster;
MODIFY(file,name[0],DELETED_FLAG);
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) {
sprintf(file->dir_ent.name, "FSCK%04d", number / 1000);
sprintf(file->dir_ent.name, "%03d", number % 1000);
for (walk = first; walk; walk = walk->next)
if (walk != file && !strncmp(walk->dir_ent.name,file->dir_ent.
name,MSDOS_NAME)) break;
if (!walk) {
fs_write(file->offset,MSDOS_NAME,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(name,45,stdin)) {
if ((here = strchr(name,'\n'))) *here = 0;
for (walk = strrchr(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);
return;
}
}
}
}
static int handle_dot(DOS_FS *fs,DOS_FILE *file,int dots)
{
char *name;
name = strncmp(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(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(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)) {
if (!fs->fat[curr].value || bad_cluster(fs,curr)) {
printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n",
path_name(file),fs->fat[curr].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 %lu 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)->dir_ent.name)) 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((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ||
!strncmp((*walk)->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) {
if (handle_dot(fs,*walk,dots)) {
*walk = (*walk)->next;
continue;
}
if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) dot++;
else dotdot++;
}
if (!((*walk)->dir_ent.attr & ATTR_VOLUME) &&
bad_name((*walk)->dir_ent.name)) {
printf("%s\n Bad file name.\n",path_name(*walk));
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) &&
!strncmp((*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;
}
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);
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);
}
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;
for (walk = FSTART(file,fs); left && walk >= 2 && walk <
fs->clusters+2 && !fs->fat[walk].value; walk++) {
left--;
if (prev) set_fat(fs,prev,walk);
prev = 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();
}
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 {
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,de.name)) != fdt_none) {
if (type == fdt_undelete && (de.attr & ATTR_DIR))
die("Can't undelete directories.");
file_modify(cp,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->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) );
printf("\n");
}
if (offset &&
strncmp(de.name,MSDOS_DOT,MSDOS_NAME) != 0 &&
strncmp(de.name,MSDOS_DOTDOT,MSDOS_NAME) != 0)
++n_files;
test_file(fs,new,test);
}
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);
}
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(walk->dir_ent.name,MSDOS_DOT,MSDOS_NAME) &&
strncmp(walk->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME))
if (scan_dir(fs,walk,file_cd(cp,walk->dir_ent.name))) return 1;
return 0;
}
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);
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

23
src/check.h Normal file
View File

@@ -0,0 +1,23 @@
/* check.h - Check and repair a PC/MS-DOS file system */
/* Written 1993 by Werner Almesberger */
#ifndef _CHECK_H
#define _CHECK_H
loff_t alloc_rootdir_entry(DOS_FS *fs, DIR_ENT *de, const char *pattern);
/* Allocate a free slot in the root directory for a new file. The file name is
constructed after 'pattern', which must include a %d type format for printf
and expand to exactly 11 characters. The name actually used is written into
the 'de' structure, the rest of *de is cleared. The offset returned is to
where in the filesystem the entry belongs. */
int scan_root(DOS_FS *fs);
/* Scans the root directory and recurses into all subdirectories. See check.c
for all the details. Returns a non-zero integer if the file system has to
be checked again. */
#endif

107
src/common.c Normal file
View File

@@ -0,0 +1,107 @@
/* common.c - Common functions */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include "common.h"
typedef struct _link {
void *data;
struct _link *next;
} LINK;
void die(char *msg,...)
{
va_list args;
va_start(args,msg);
vfprintf(stderr,msg,args);
va_end(args);
fprintf(stderr,"\n");
exit(1);
}
void pdie(char *msg,...)
{
va_list args;
va_start(args,msg);
vfprintf(stderr,msg,args);
va_end(args);
fprintf(stderr,":%s\n",strerror(errno));
exit(1);
}
void *alloc(int size)
{
void *this;
if ((this = malloc(size))) return this;
pdie("malloc");
return NULL; /* for GCC */
}
void *qalloc(void **root,int size)
{
LINK *link;
link = alloc(sizeof(LINK));
link->next = *root;
*root = link;
return link->data = alloc(size);
}
void qfree(void **root)
{
LINK *this;
while (*root) {
this = (LINK *) *root;
*root = this->next;
free(this->data);
free(this);
}
}
int min(int a,int b)
{
return a < b ? a : b;
}
char get_key(char *valid,char *prompt)
{
int ch,okay;
while (1) {
if (prompt) printf("%s ",prompt);
fflush(stdout);
while (ch = getchar(), ch == ' ' || ch == '\t');
if (ch == EOF) exit(1);
if (!strchr(valid,okay = ch)) okay = 0;
while (ch = getchar(), ch != '\n' && ch != EOF);
if (ch == EOF) exit(1);
if (okay) return okay;
printf("Invalid input.\n");
}
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

41
src/common.h Normal file
View File

@@ -0,0 +1,41 @@
/* common.h - Common functions */
/* Written 1993 by Werner Almesberger */
# include <asm/types.h>
# define MSDOS_FAT12 4084 /* maximum number of clusters in a 12 bit FAT */
#ifndef _COMMON_H
#define _COMMON_H
void die(char *msg,...) __attribute((noreturn));
/* Displays a prinf-style message and terminates the program. */
void pdie(char *msg,...) __attribute((noreturn));
/* Like die, but appends an error message according to the state of errno. */
void *alloc(int size);
/* mallocs SIZE bytes and returns a pointer to the data. Terminates the program
if malloc fails. */
void *qalloc(void **root,int size);
/* Like alloc, but registers the data area in a list described by ROOT. */
void qfree(void **root);
/* Deallocates all qalloc'ed data areas described by ROOT. */
int min(int a,int b);
/* Returns the smaller integer value of a and b. */
char get_key(char *valid,char *prompt);
/* Displays PROMPT and waits for user input. Only characters in VALID are
accepted. Terminates the program on EOF. Returns the character. */
#endif

189
src/dosfsck.c Normal file
View File

@@ -0,0 +1,189 @@
/* dosfsck.c - User interface */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include "../version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include "common.h"
#include "dosfsck.h"
#include "io.h"
#include "boot.h"
#include "fat.h"
#include "file.h"
#include "check.h"
int interactive = 0,list = 0,test = 0,verbose = 0,write_immed = 0;
int atari_format = 0;
unsigned n_files = 0;
void *mem_queue = NULL;
static void usage(char *name)
{
fprintf(stderr,"usage: %s [-aAflrtvVwy] [-d path -d ...] "
"[-u path -u ...]\n%15sdevice\n",name,"");
fprintf(stderr," -a automatically repair the file system\n");
fprintf(stderr," -A toggle Atari file system format\n");
fprintf(stderr," -d path drop that file\n");
fprintf(stderr," -f salvage unused chains to files\n");
fprintf(stderr," -l list path names\n");
fprintf(stderr," -n no-op, check non-interactively without changing\n");
fprintf(stderr," -p same as -a, for compat with other *fsck\n");
fprintf(stderr," -r interactively repair the file system\n");
fprintf(stderr," -t test for bad clusters\n");
fprintf(stderr," -u path try to undelete that (non-directory) file\n");
fprintf(stderr," -v verbose mode\n");
fprintf(stderr," -V perform a verification pass\n");
fprintf(stderr," -w write changes to disk immediately\n");
fprintf(stderr," -y same as -a, for compat with other *fsck\n");
exit(2);
}
/*
* ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant
* of MS-DOS filesystem by default.
*/
static void check_atari( void )
{
#ifdef __mc68000__
FILE *f;
char line[128], *p;
if (!(f = fopen( "/proc/hardware", "r" ))) {
perror( "/proc/hardware" );
return;
}
while( fgets( line, sizeof(line), f ) ) {
if (strncmp( line, "Model:", 6 ) == 0) {
p = line + 6;
p += strspn( p, " \t" );
if (strncmp( p, "Atari ", 6 ) == 0)
atari_format = 1;
break;
}
}
fclose( f );
#endif
}
int main(int argc,char **argv)
{
DOS_FS fs;
int rw,salvage_files,verify,c;
unsigned n_files_check=0, n_files_verify=0;
unsigned long free_clusters;
rw = salvage_files = verify = 0;
interactive = 1;
check_atari();
while ((c = getopt(argc,argv,"Aad:flnprtu:vVwy")) != EOF)
switch (c) {
case 'A': /* toggle Atari format */
atari_format = !atari_format;
break;
case 'a':
case 'p':
case 'y':
rw = 1;
interactive = 0;
salvage_files = 1;
break;
case 'd':
file_add(optarg,fdt_drop);
break;
case 'f':
salvage_files = 1;
break;
case 'l':
list = 1;
break;
case 'n':
rw = 0;
interactive = 0;
break;
case 'r':
rw = 1;
interactive = 1;
break;
case 't':
test = 1;
break;
case 'u':
file_add(optarg,fdt_undelete);
break;
case 'v':
verbose = 1;
printf("dosfsck " VERSION " (" VERSION_DATE ")\n");
break;
case 'V':
verify = 1;
break;
case 'w':
write_immed = 1;
break;
default:
usage(argv[0]);
}
if ((test || write_immed) && !rw) {
fprintf(stderr,"-t and -w require -a or -r\n");
exit(2);
}
if (optind != argc-1) usage(argv[0]);
printf( "dosfsck " VERSION ", " VERSION_DATE ", FAT32, LFN\n" );
fs_open(argv[optind],rw);
read_boot(&fs);
if (verify) printf("Starting check/repair pass.\n");
while (read_fat(&fs), scan_root(&fs)) qfree(&mem_queue);
if (test) fix_bad(&fs);
if (salvage_files) reclaim_file(&fs);
else reclaim_free(&fs);
free_clusters = update_free(&fs);
file_unused();
qfree(&mem_queue);
n_files_check = n_files;
if (verify) {
n_files = 0;
printf("Starting verification pass.\n");
read_fat(&fs);
scan_root(&fs);
reclaim_free(&fs);
qfree(&mem_queue);
n_files_verify = n_files;
}
if (fs_changed()) {
if (rw) {
if (interactive)
rw = get_key("yn","Perform changes ? (y/n)") == 'y';
else printf("Performing changes.\n");
}
else
printf("Leaving file system unchanged.\n");
}
printf( "%s: %u files, %lu/%lu clusters\n", argv[optind],
n_files, fs.clusters - free_clusters, fs.clusters );
return fs_close(rw) ? 1 : 0;
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

197
src/dosfsck.h Normal file
View File

@@ -0,0 +1,197 @@
/* dosfsck.h - Common data structures and global variables */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#ifndef _DOSFSCK_H
#define _DOSFSCK_H
#include <sys/types.h>
#define _LINUX_STAT_H /* hack to avoid inclusion of <linux/stat.h> */
#define _LINUX_STRING_H_ /* hack to avoid inclusion of <linux/string.h>*/
#define _LINUX_FS_H /* hack to avoid inclusion of <linux/fs.h> */
# include <asm/types.h>
# include <asm/byteorder.h>
#include <linux/msdos_fs.h>
#undef CF_LE_W
#undef CF_LE_L
#undef CT_LE_W
#undef CT_LE_L
#if __BYTE_ORDER == __BIG_ENDIAN
#include <byteswap.h>
#define CF_LE_W(v) bswap_16(v)
#define CF_LE_L(v) bswap_32(v)
#define CT_LE_W(v) CF_LE_W(v)
#define CT_LE_L(v) CF_LE_L(v)
#else
#define CF_LE_W(v) (v)
#define CF_LE_L(v) (v)
#define CT_LE_W(v) (v)
#define CT_LE_L(v) (v)
#endif /* __BIG_ENDIAN */
#define VFAT_LN_ATTR (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME)
/* ++roman: Use own definition of boot sector structure -- the kernel headers'
* name for it is msdos_boot_sector in 2.0 and fat_boot_sector in 2.1 ... */
struct boot_sector {
__u8 ignored[3]; /* Boot strap short or near jump */
__u8 system_id[8]; /* Name - can be used to special case
partition manager volumes */
__u8 sector_size[2]; /* bytes per logical sector */
__u8 cluster_size; /* sectors/cluster */
__u16 reserved; /* reserved sectors */
__u8 fats; /* number of FATs */
__u8 dir_entries[2]; /* root directory entries */
__u8 sectors[2]; /* number of sectors */
__u8 media; /* media code (unused) */
__u16 fat_length; /* sectors/FAT */
__u16 secs_track; /* sectors per track */
__u16 heads; /* number of heads */
__u32 hidden; /* hidden sectors (unused) */
__u32 total_sect; /* number of sectors (if sectors == 0) */
/* The following fields are only used by FAT32 */
__u32 fat32_length; /* sectors/FAT */
__u16 flags; /* bit 8: fat mirroring, low 4: active fat */
__u8 version[2]; /* major, minor filesystem version */
__u32 root_cluster; /* first cluster in root directory */
__u16 info_sector; /* filesystem info sector */
__u16 backup_boot; /* backup boot sector */
__u8 reserved2[12]; /* Unused */
__u8 drive_number; /* Logical Drive Number */
__u8 reserved3; /* Unused */
__u8 extended_sig; /* Extended Signature (0x29) */
__u32 serial; /* Serial number */
__u8 label[11]; /* FS label */
__u8 fs_type[8]; /* FS Type */
/* fill up to 512 bytes */
__u8 junk[422];
} __attribute__ ((packed));
struct boot_sector_16 {
__u8 ignored[3]; /* Boot strap short or near jump */
__u8 system_id[8]; /* Name - can be used to special case
partition manager volumes */
__u8 sector_size[2]; /* bytes per logical sector */
__u8 cluster_size; /* sectors/cluster */
__u16 reserved; /* reserved sectors */
__u8 fats; /* number of FATs */
__u8 dir_entries[2]; /* root directory entries */
__u8 sectors[2]; /* number of sectors */
__u8 media; /* media code (unused) */
__u16 fat_length; /* sectors/FAT */
__u16 secs_track; /* sectors per track */
__u16 heads; /* number of heads */
__u32 hidden; /* hidden sectors (unused) */
__u32 total_sect; /* number of sectors (if sectors == 0) */
__u8 drive_number; /* Logical Drive Number */
__u8 reserved2; /* Unused */
__u8 extended_sig; /* Extended Signature (0x29) */
__u32 serial; /* Serial number */
__u8 label[11]; /* FS label */
__u8 fs_type[8]; /* FS Type */
/* fill up to 512 bytes */
__u8 junk[450];
} __attribute__ ((packed));
struct info_sector {
__u32 magic; /* Magic for info sector ('RRaA') */
__u8 junk[0x1dc];
__u32 reserved1; /* Nothing as far as I can tell */
__u32 signature; /* 0x61417272 ('rrAa') */
__u32 free_clusters; /* Free cluster count. -1 if unknown */
__u32 next_cluster; /* Most recently allocated cluster. */
__u32 reserved2[3];
__u16 reserved3;
__u16 boot_sign;
};
typedef struct {
__u8 name[8],ext[3]; /* name and extension */
__u8 attr; /* attribute bits */
__u8 lcase; /* Case for base and extension */
__u8 ctime_ms; /* Creation time, milliseconds */
__u16 ctime; /* Creation time */
__u16 cdate; /* Creation date */
__u16 adate; /* Last access date */
__u16 starthi; /* High 16 bits of cluster in FAT32 */
__u16 time,date,start;/* time, date and first cluster */
__u32 size; /* file size (in bytes) */
} DIR_ENT;
typedef struct _dos_file {
DIR_ENT dir_ent;
char *lfn;
loff_t offset;
struct _dos_file *parent; /* parent directory */
struct _dos_file *next; /* next entry */
struct _dos_file *first; /* first entry (directory only) */
} DOS_FILE;
typedef struct {
unsigned long value;
unsigned long reserved;
DOS_FILE *owner;
int prev; /* number of previous clusters */
} FAT_ENTRY;
typedef struct {
int nfats;
loff_t fat_start;
unsigned int fat_size; /* unit is bytes */
unsigned int fat_bits; /* size of a FAT entry */
unsigned int eff_fat_bits; /* # of used bits in a FAT entry */
unsigned long root_cluster; /* 0 for old-style root dir */
loff_t root_start;
unsigned int root_entries;
loff_t data_start;
unsigned int cluster_size;
unsigned long clusters;
loff_t fsinfo_start; /* 0 if not present */
long free_clusters;
loff_t backupboot_start; /* 0 if not present */
FAT_ENTRY *fat;
char *label;
} DOS_FS;
#ifndef offsetof
#define offsetof(t,e) ((int)&(((t *)0)->e))
#endif
extern int interactive,list,verbose,test,write_immed;
extern int atari_format;
extern unsigned n_files;
extern void *mem_queue;
/* value to use as end-of-file marker */
#define FAT_EOF(fs) ((atari_format ? 0xfff : 0xff8) | FAT_EXTD(fs))
#define FAT_IS_EOF(fs,v) ((unsigned long)(v) >= (0xff8|FAT_EXTD(fs)))
/* value to mark bad clusters */
#define FAT_BAD(fs) (0xff7 | FAT_EXTD(fs))
/* range of values used for bad clusters */
#define FAT_MIN_BAD(fs) ((atari_format ? 0xff0 : 0xff7) | FAT_EXTD(fs))
#define FAT_MAX_BAD(fs) ((atari_format ? 0xff7 : 0xff7) | FAT_EXTD(fs))
#define FAT_IS_BAD(fs,v) ((v) >= FAT_MIN_BAD(fs) && (v) <= FAT_MAX_BAD(fs))
/* return -16 as a number with fs->fat_bits bits */
#define FAT_EXTD(fs) (((1 << fs->eff_fat_bits)-1) & ~0xf)
#endif
/* Local Variables: */
/* tab-width: 8 */
/* End: */

110
src/dosfslabel.c Normal file
View File

@@ -0,0 +1,110 @@
/* dosfslabel.c - User interface */
/* Copyright 2007 Red Hat, Inc.
* Portions copyright 1998 Roman Hodek.
* Portions copyright 1993 Werner Almesberger.
*/
#include "../version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include "common.h"
#include "dosfsck.h"
#include "io.h"
#include "boot.h"
#include "fat.h"
#include "file.h"
#include "check.h"
int interactive = 0,list = 0,test = 0,verbose = 0,write_immed = 0;
int atari_format = 0;
unsigned n_files = 0;
void *mem_queue = NULL;
static void usage(int error)
{
FILE *f = error ? stderr : stdout;
int status = error ? 1 : 0;
fprintf(f,"usage: dosfslabel device [label]\n");
exit(status);
}
/*
* ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant
* of MS-DOS filesystem by default.
*/
static void check_atari( void )
{
#ifdef __mc68000__
FILE *f;
char line[128], *p;
if (!(f = fopen( "/proc/hardware", "r" ))) {
perror( "/proc/hardware" );
return;
}
while( fgets( line, sizeof(line), f ) ) {
if (strncmp( line, "Model:", 6 ) == 0) {
p = line + 6;
p += strspn( p, " \t" );
if (strncmp( p, "Atari ", 6 ) == 0)
atari_format = 1;
break;
}
}
fclose( f );
#endif
}
int main(int argc, char *argv[])
{
DOS_FS fs;
int rw = 0;
char *device = NULL;
char *label = NULL;
check_atari();
if (argc < 2 || argc > 3)
usage(1);
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
usage(0);
else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
printf( "dosfslabel " VERSION ", " VERSION_DATE ", FAT32, LFN\n" );
exit(0);
}
device = argv[1];
if (argc == 3) {
label = argv[2];
if (strlen(label) > 11) {
fprintf(stderr,
"dosfslabel: labels can be no longer than 11 characters\n");
exit(1);
}
rw = 1;
}
fs_open(device, rw);
read_boot(&fs);
if (!rw) {
fprintf(stdout, "%s\n", fs.label);
exit(0);
}
write_label(&fs, label);
return fs_close(rw) ? 1 : 0;
}

363
src/fat.c Normal file
View File

@@ -0,0 +1,363 @@
/* fat.c - Read/write access to the FAT */
/* Written 1993 by Werner Almesberger */
/* 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 <unistd.h>
#include "common.h"
#include "dosfsck.h"
#include "io.h"
#include "check.h"
#include "fat.h"
static void get_fat(FAT_ENTRY *entry,void *fat,unsigned long cluster,DOS_FS *fs)
{
unsigned char *ptr;
switch(fs->fat_bits) {
case 12:
ptr = &((unsigned char *) fat)[cluster*3/2];
entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) :
(ptr[0] | ptr[1] << 8));
break;
case 16:
entry->value = CF_LE_W(((unsigned short *) fat)[cluster]);
break;
case 32:
/* According to M$, the high 4 bits of a FAT32 entry are reserved and
* are not part of the cluster number. So we cut them off. */
{
unsigned long e = CF_LE_L(((unsigned int *) fat)[cluster]);
entry->value = e & 0xfffffff;
entry->reserved = e >> 28;
}
break;
default:
die("Bad FAT entry size: %d bits.",fs->fat_bits);
}
entry->owner = NULL;
}
void read_fat(DOS_FS *fs)
{
int eff_size;
unsigned long i;
void *first,*second = NULL;
int first_ok,second_ok;
eff_size = ((fs->clusters+2ULL)*fs->fat_bits+7)/8ULL;
first = alloc(eff_size);
fs_read(fs->fat_start,eff_size,first);
if (fs->nfats > 1) {
second = alloc(eff_size);
fs_read(fs->fat_start+fs->fat_size,eff_size,second);
}
if (second && memcmp(first,second,eff_size) != 0) {
FAT_ENTRY first_media, second_media;
get_fat(&first_media,first,0,fs);
get_fat(&second_media,second,0,fs);
first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
if (first_ok && !second_ok) {
printf("FATs differ - using first FAT.\n");
fs_write(fs->fat_start+fs->fat_size,eff_size,first);
}
if (!first_ok && second_ok) {
printf("FATs differ - using second FAT.\n");
fs_write(fs->fat_start,eff_size,second);
memcpy(first,second,eff_size);
}
if (first_ok && second_ok) {
if (interactive) {
printf("FATs differ but appear to be intact. Use which FAT ?\n"
"1) Use first FAT\n2) Use second FAT\n");
if (get_key("12","?") == '1') {
fs_write(fs->fat_start+fs->fat_size,eff_size,first);
} else {
fs_write(fs->fat_start,eff_size,second);
memcpy(first,second,eff_size);
}
}
else {
printf("FATs differ but appear to be intact. Using first "
"FAT.\n");
fs_write(fs->fat_start+fs->fat_size,eff_size,first);
}
}
if (!first_ok && !second_ok) {
printf("Both FATs appear to be corrupt. Giving up.\n");
exit(1);
}
}
if (second) {
free(second);
}
fs->fat = qalloc(&mem_queue,sizeof(FAT_ENTRY)*(fs->clusters+2ULL));
for (i = 2; i < fs->clusters+2; i++) get_fat(&fs->fat[i],first,i,fs);
for (i = 2; i < fs->clusters+2; i++)
if (fs->fat[i].value >= fs->clusters+2 &&
(fs->fat[i].value < FAT_MIN_BAD(fs))) {
printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n",
i-2,fs->fat[i].value,fs->clusters+2-1);
set_fat(fs,i,-1);
}
free(first);
}
void set_fat(DOS_FS *fs,unsigned long cluster,unsigned long new)
{
unsigned char data[4];
int size;
loff_t offs;
if ((long)new == -1)
new = FAT_EOF(fs);
else if ((long)new == -2)
new = FAT_BAD(fs);
switch( fs->fat_bits ) {
case 12:
offs = fs->fat_start+cluster*3/2;
if (cluster & 1) {
data[0] = ((new & 0xf) << 4) | (fs->fat[cluster-1].value >> 8);
data[1] = new >> 4;
}
else {
data[0] = new & 0xff;
data[1] = (new >> 8) | (cluster == fs->clusters-1 ? 0 :
(0xff & fs->fat[cluster+1].value) << 4);
}
size = 2;
break;
case 16:
offs = fs->fat_start+cluster*2;
*(unsigned short *) data = CT_LE_W(new);
size = 2;
break;
case 32:
offs = fs->fat_start+cluster*4;
/* According to M$, the high 4 bits of a FAT32 entry are reserved and
* are not part of the cluster number. So we never touch them. */
*(unsigned long *) data = CT_LE_L( (new & 0xfffffff) |
(fs->fat[cluster].reserved << 28) );
size = 4;
break;
default:
die("Bad FAT entry size: %d bits.",fs->fat_bits);
}
fs->fat[cluster].value = new;
fs_write(offs,size,&data);
fs_write(offs+fs->fat_size,size,&data);
}
int bad_cluster(DOS_FS *fs,unsigned long cluster)
{
return FAT_IS_BAD(fs,fs->fat[cluster].value);
}
unsigned long next_cluster(DOS_FS *fs,unsigned long cluster)
{
unsigned long value;
value = fs->fat[cluster].value;
if (FAT_IS_BAD(fs,value))
die("Internal error: next_cluster on bad cluster");
return FAT_IS_EOF(fs,value) ? -1 : value;
}
loff_t cluster_start(DOS_FS *fs,unsigned long cluster)
{
return fs->data_start+((loff_t)cluster-2)*(unsigned long long)fs->cluster_size;
}
void set_owner(DOS_FS *fs,unsigned long cluster,DOS_FILE *owner)
{
if (owner && fs->fat[cluster].owner)
die("Internal error: attempt to change file owner");
fs->fat[cluster].owner = owner;
}
DOS_FILE *get_owner(DOS_FS *fs,unsigned long cluster)
{
return fs->fat[cluster].owner;
}
void fix_bad(DOS_FS *fs)
{
unsigned long i;
if (verbose)
printf("Checking for bad clusters.\n");
for (i = 2; i < fs->clusters+2; i++)
if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value))
if (!fs_test(cluster_start(fs,i),fs->cluster_size)) {
printf("Cluster %lu is unreadable.\n",i);
set_fat(fs,i,-2);
}
}
void reclaim_free(DOS_FS *fs)
{
int reclaimed;
unsigned long i;
if (verbose)
printf("Checking for unused clusters.\n");
reclaimed = 0;
for (i = 2; i < fs->clusters+2; i++)
if (!get_owner(fs,i) && fs->fat[i].value &&
!FAT_IS_BAD(fs,fs->fat[i].value)) {
set_fat(fs,i,0);
reclaimed++;
}
if (reclaimed)
printf("Reclaimed %d unused cluster%s (%llu bytes).\n",reclaimed,
reclaimed == 1 ? "" : "s",(unsigned long long)reclaimed*fs->cluster_size);
}
static void tag_free(DOS_FS *fs,DOS_FILE *ptr)
{
DOS_FILE *owner;
int prev;
unsigned long i,walk;
for (i = 2; i < fs->clusters+2; i++)
if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) &&
!get_owner(fs,i) && !fs->fat[i].prev) {
prev = 0;
for (walk = i; walk > 0 && walk != -1;
walk = next_cluster(fs,walk)) {
if (!(owner = get_owner(fs,walk))) set_owner(fs,walk,ptr);
else if (owner != ptr)
die("Internal error: free chain collides with file");
else {
set_fat(fs,prev,-1);
break;
}
prev = walk;
}
}
}
void reclaim_file(DOS_FS *fs)
{
DOS_FILE dummy;
int reclaimed,files,changed;
unsigned long i,next,walk;
if (verbose)
printf("Reclaiming unconnected clusters.\n");
for (i = 2; i < fs->clusters+2; i++) fs->fat[i].prev = 0;
for (i = 2; i < fs->clusters+2; i++) {
next = fs->fat[i].value;
if (!get_owner(fs,i) && next && next < fs->clusters+2) {
if (get_owner(fs,next) || !fs->fat[next].value ||
FAT_IS_BAD(fs,fs->fat[next].value)) set_fat(fs,i,-1);
else fs->fat[next].prev++;
}
}
do {
tag_free(fs,&dummy);
changed = 0;
for (i = 2; i < fs->clusters+2; i++)
if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) &&
!get_owner(fs, i)) {
if (!fs->fat[fs->fat[i].value].prev--)
die("Internal error: prev going below zero");
set_fat(fs,i,-1);
changed = 1;
printf("Broke cycle at cluster %lu in free chain.\n",i);
break;
}
}
while (changed);
files = reclaimed = 0;
for (i = 2; i < fs->clusters+2; i++)
if (get_owner(fs,i) == &dummy && !fs->fat[i].prev) {
DIR_ENT de;
loff_t offset;
files++;
offset = alloc_rootdir_entry(fs,&de,"FSCK%04dREC");
de.start = CT_LE_W(i&0xffff);
if (fs->fat_bits == 32)
de.starthi = CT_LE_W(i>>16);
for (walk = i; walk > 0 && walk != -1;
walk = next_cluster(fs,walk)) {
de.size = CT_LE_L(CF_LE_L(de.size)+fs->cluster_size);
reclaimed++;
}
fs_write(offset,sizeof(DIR_ENT),&de);
}
if (reclaimed)
printf("Reclaimed %d unused cluster%s (%llu bytes) in %d chain%s.\n",
reclaimed,reclaimed == 1 ? "" : "s",(unsigned long long)reclaimed*fs->cluster_size,files,
files == 1 ? "" : "s");
}
unsigned long update_free(DOS_FS *fs)
{
unsigned long i;
unsigned long free = 0;
int do_set = 0;
for (i = 2; i < fs->clusters+2; i++)
if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value))
++free;
if (!fs->fsinfo_start)
return free;
if (verbose)
printf("Checking free cluster summary.\n");
if (fs->free_clusters >= 0) {
if (free != fs->free_clusters) {
printf( "Free cluster summary wrong (%ld vs. really %ld)\n",
fs->free_clusters,free);
if (interactive)
printf( "1) Correct\n2) Don't correct\n" );
else printf( " Auto-correcting.\n" );
if (!interactive || get_key("12","?") == '1')
do_set = 1;
}
}
else {
printf( "Free cluster summary uninitialized (should be %ld)\n", free );
if (interactive)
printf( "1) Set it\n2) Leave it uninitialized\n" );
else printf( " Auto-setting.\n" );
if (!interactive || get_key("12","?") == '1')
do_set = 1;
}
if (do_set) {
fs->free_clusters = free;
free = CT_LE_L(free);
fs_write(fs->fsinfo_start+offsetof(struct info_sector,free_clusters),
sizeof(free),&free);
}
return free;
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

64
src/fat.h Normal file
View File

@@ -0,0 +1,64 @@
/* fat.h - Read/write access to the FAT */
/* Written 1993 by Werner Almesberger */
#ifndef _FAT_H
#define _FAT_H
void read_fat(DOS_FS *fs);
/* Loads the FAT of the file system described by FS. Initializes the FAT,
replaces broken FATs and rejects invalid cluster entries. */
void set_fat(DOS_FS *fs,unsigned long cluster,unsigned long new);
/* Changes the value of the CLUSTERth cluster of the FAT of FS to NEW. Special
values of NEW are -1 (EOF, 0xff8 or 0xfff8) and -2 (bad sector, 0xff7 or
0xfff7) */
int bad_cluster(DOS_FS *fs,unsigned long cluster);
/* Returns a non-zero integer if the CLUSTERth cluster is marked as bad or zero
otherwise. */
unsigned long next_cluster(DOS_FS *fs,unsigned long cluster);
/* Returns the number of the cluster following CLUSTER, or -1 if this is the
last cluster of the respective cluster chain. CLUSTER must not be a bad
cluster. */
loff_t cluster_start(DOS_FS *fs,unsigned long cluster);
/* Returns the byte offset of CLUSTER, relative to the respective device. */
void set_owner(DOS_FS *fs,unsigned long cluster,DOS_FILE *owner);
/* Sets the owner pointer of the respective cluster to OWNER. If OWNER was NULL
before, it can be set to NULL or any non-NULL value. Otherwise, only NULL is
accepted as the new value. */
DOS_FILE *get_owner(DOS_FS *fs,unsigned long cluster);
/* Returns the owner of the repective cluster or NULL if the cluster has no
owner. */
void fix_bad(DOS_FS *fs);
/* Scans the disk for currently unused bad clusters and marks them as bad. */
void reclaim_free(DOS_FS *fs);
/* Marks all allocated, but unused clusters as free. */
void reclaim_file(DOS_FS *fs);
/* Scans the FAT for chains of allocated, but unused clusters and creates files
for them in the root directory. Also tries to fix all inconsistencies (e.g.
loops, shared clusters, etc.) in the process. */
unsigned long update_free(DOS_FS *fs);
/* Updates free cluster count in FSINFO sector. */
#endif

245
src/file.c Normal file
View File

@@ -0,0 +1,245 @@
/* file.c - Additional file attributes */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#define _LINUX_STAT_H /* hack to avoid inclusion of <linux/stat.h> */
#define _LINUX_STRING_H_ /* hack to avoid inclusion of <linux/string.h>*/
#define _LINUX_FS_H /* hack to avoid inclusion of <linux/fs.h> */
# include <asm/types.h>
#include <linux/msdos_fs.h>
#include "common.h"
#include "file.h"
FDSC *fp_root = NULL;
static void put_char(char **p,unsigned char c)
{
if ((c >= ' ' && c < 0x7f) || c >= 0xa0) *(*p)++ = c;
else {
*(*p)++ = '\\';
*(*p)++ = '0'+(c >> 6);
*(*p)++ = '0'+((c >> 3) & 7);
*(*p)++ = '0'+(c & 7);
}
}
char *file_name(unsigned char *fixed)
{
static char path[MSDOS_NAME*4+2];
char *p;
int i,j;
p = path;
for (i = j = 0; i < 8; i++)
if (fixed[i] != ' ') {
while (j++ < i) *p++ = ' ';
put_char(&p,fixed[i]);
}
if (strncmp(fixed+8," ",3)) {
*p++ = '.';
for (i = j = 0; i < 3; i++)
if (fixed[i+8] != ' ') {
while (j++ < i) *p++ = ' ';
put_char(&p,fixed[i+8]);
}
}
*p = 0;
return path;
}
int file_cvt(unsigned char *name,unsigned char *fixed)
{
unsigned char c;
int size,ext,cnt;
size = 8;
ext = 0;
while (*name) {
c = *name;
if (c < ' ' || c > 0x7e || strchr("*?<>|\"/",c)) {
printf("Invalid character in name. Use \\ooo for special "
"characters.\n");
return 0;
}
if (c == '.') {
if (ext) {
printf("Duplicate dots in name.\n");
return 0;
}
while (size--) *fixed++ = ' ';
size = 3;
ext = 1;
name++;
continue;
}
if (c == '\\') {
c = 0;
for (cnt = 3; cnt; cnt--) {
if (*name < '0' || *name > '7') {
printf("Invalid octal character.\n");
return 0;
}
c = c*8+*name++-'0';
}
if (cnt < 4) {
printf("Expected three octal digits.\n");
return 0;
}
name += 3;
}
if (islower(c)) c = toupper(c);
if (size) {
*fixed++ = c;
size--;
}
name++;
}
if (*name || size == 8) return 0;
if (!ext) {
while (size--) *fixed++ = ' ';
size = 3;
}
while (size--) *fixed++ = ' ';
return 1;
}
void file_add(char *path,FD_TYPE type)
{
FDSC **current,*walk;
char name[MSDOS_NAME];
char *here;
current = &fp_root;
if (*path != '/') die("%s: Absolute path required.",path);
path++;
while (1) {
if ((here = strchr(path,'/'))) *here = 0;
if (!file_cvt(path,name)) exit(2);
for (walk = *current; walk; walk = walk->next)
if (!here && (!strncmp(name,walk->name,MSDOS_NAME) || (type ==
fdt_undelete && !strncmp(name+1,walk->name+1,MSDOS_NAME-1))))
die("Ambiguous name: \"%s\"",path);
else if (here && !strncmp(name,walk->name,MSDOS_NAME)) break;
if (!walk) {
walk = alloc(sizeof(FDSC));
strncpy(walk->name,name,MSDOS_NAME);
walk->type = here ? fdt_none : type;
walk->first = NULL;
walk->next = *current;
*current = walk;
}
current = &walk->first;
if (!here) break;
*here = '/';
path = here+1;
}
}
FDSC **file_cd(FDSC **curr,char *fixed)
{
FDSC **walk;
if (!curr || !*curr) return NULL;
for (walk = curr; *walk; walk = &(*walk)->next)
if (!strncmp((*walk)->name,fixed,MSDOS_NAME) && (*walk)->first)
return &(*walk)->first;
return NULL;
}
static FDSC **file_find(FDSC **dir,char *fixed)
{
if (!dir || !*dir) return NULL;
if (*(unsigned char *) fixed == DELETED_FLAG) {
while (*dir) {
if (!strncmp((*dir)->name+1,fixed+1,MSDOS_NAME-1) && !(*dir)->first)
return dir;
dir = &(*dir)->next;
}
return NULL;
}
while (*dir) {
if (!strncmp((*dir)->name,fixed,MSDOS_NAME) && !(*dir)->first)
return dir;
dir = &(*dir)->next;
}
return NULL;
}
FD_TYPE file_type(FDSC **curr,char *fixed)
{
FDSC **this;
if ((this = file_find(curr,fixed))) return (*this)->type;
return fdt_none;
}
void file_modify(FDSC **curr,char *fixed)
{
FDSC **this,*next;
if (!(this = file_find(curr,fixed)))
die("Internal error: file_find failed");
switch ((*this)->type) {
case fdt_drop:
printf("Dropping %s\n",file_name(fixed));
*(unsigned char *) fixed = DELETED_FLAG;
break;
case fdt_undelete:
*fixed = *(*this)->name;
printf("Undeleting %s\n",file_name(fixed));
break;
default:
die("Internal error: file_modify");
}
next = (*this)->next;
free(*this);
*this = next;
}
static void report_unused(FDSC *this)
{
FDSC *next;
while (this) {
next = this->next;
if (this->first) report_unused(this->first);
else if (this->type != fdt_none)
printf("Warning: did not %s file %s\n",this->type == fdt_drop ?
"drop" : "undelete",file_name(this->name));
free(this);
this = next;
}
}
void file_unused(void)
{
report_unused(fp_root);
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

55
src/file.h Normal file
View File

@@ -0,0 +1,55 @@
/* file.h - Additional file attributes */
/* Written 1993 by Werner Almesberger */
#ifndef _FILE_H
#define _FILE_H
typedef enum { fdt_none,fdt_drop,fdt_undelete } FD_TYPE;
typedef struct _fptr {
char name[MSDOS_NAME];
FD_TYPE type;
struct _fptr *first; /* first entry */
struct _fptr *next; /* next file in directory */
} FDSC;
extern FDSC *fp_root;
char *file_name(unsigned char *fixed);
/* Returns a pointer to a pretty-printed representation of a fixed MS-DOS file
name. */
int file_cvt(unsigned char *name,unsigned char *fixed);
/* Converts a pretty-printed file name to the fixed MS-DOS format. Returns a
non-zero integer on success, zero on failure. */
void file_add(char *path,FD_TYPE type);
/* Define special attributes for a path. TYPE can be either FDT_DROP or
FDT_UNDELETE. */
FDSC **file_cd(FDSC **curr,char *fixed);
/* Returns a pointer to the directory descriptor of the subdirectory FIXED of
CURR, or NULL if no such subdirectory exists. */
FD_TYPE file_type(FDSC **curr,char *fixed);
/* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no
such file exists or if CURR is NULL. */
void file_modify(FDSC **curr,char *fixed);
/* Performs the necessary operation on the entry of CURR that is named FIXED. */
void file_unused(void);
/* Displays warnings for all unused file attributes. */
#endif

191
src/io.c Normal file
View File

@@ -0,0 +1,191 @@
/* io.c - Virtual disk input/output */
/* Written 1993 by Werner Almesberger */
/*
* Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
* Fixed nasty bug that caused every file with a name like
* xxxxxxxx.xxx to be treated as bad name that needed to be fixed.
*/
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fd.h>
#include "dosfsck.h"
#include "common.h"
#include "io.h"
typedef struct _change {
void *data;
loff_t pos;
int size;
struct _change *next;
} CHANGE;
static CHANGE *changes,*last;
static int fd,did_change = 0;
unsigned device_no;
#ifdef __DJGPP__
#include "volume.h" /* DOS lowlevel disk access functions */
#undef llseek
static loff_t llseek( int fd, loff_t offset, int whence )
{
if ((whence != SEEK_SET) || (fd == 4711)) return -1; /* only those supported */
return VolumeSeek(offset);
}
#define open OpenVolume
#define close CloseVolume
#define read(a,b,c) ReadVolume(b,c)
#define write(a,b,c) WriteVolume(b,c)
#endif
void fs_open(char *path,int rw)
{
struct stat stbuf;
if ((fd = open(path,rw ? O_RDWR : O_RDONLY)) < 0)
pdie("open %s",path);
changes = last = NULL;
did_change = 0;
#ifndef _DJGPP_
if (fstat(fd,&stbuf) < 0)
pdie("fstat %s",path);
device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0;
#else
if (IsWorkingOnImageFile()) {
if (fstat(GetVolumeHandle(),&stbuf) < 0)
pdie("fstat image %s",path);
device_no = 0;
}
else {
/* return 2 for floppy, 1 for ramdisk, 7 for loopback */
/* used by boot.c in Atari mode: floppy always FAT12, */
/* loopback / ramdisk only FAT12 if usual floppy size, */
/* harddisk always FAT16 on Atari... */
device_no = (GetVolumeHandle() < 2) ? 2 : 1;
/* telling "floppy" for A:/B:, "ramdisk" for the rest */
}
#endif
}
void fs_read(loff_t pos,int size,void *data)
{
CHANGE *walk;
int got;
if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
if ((got = read(fd,data,size)) < 0) pdie("Read %d bytes at %lld",size,pos);
if (got != size) die("Got %d bytes instead of %d at %lld",got,size,pos);
for (walk = changes; walk; walk = walk->next) {
if (walk->pos < pos+size && walk->pos+walk->size > pos) {
if (walk->pos < pos)
memcpy(data,(char *) walk->data+pos-walk->pos,min(size,
walk->size-pos+walk->pos));
else memcpy((char *) data+walk->pos-pos,walk->data,min(walk->size,
size+pos-walk->pos));
}
}
}
int fs_test(loff_t pos,int size)
{
void *scratch;
int okay;
if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
scratch = alloc(size);
okay = read(fd,scratch,size) == size;
free(scratch);
return okay;
}
void fs_write(loff_t pos,int size,void *data)
{
CHANGE *new;
int did;
if (write_immed) {
did_change = 1;
if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
if ((did = write(fd,data,size)) == size) return;
if (did < 0) pdie("Write %d bytes at %lld",size,pos);
die("Wrote %d bytes instead of %d at %lld",did,size,pos);
}
new = alloc(sizeof(CHANGE));
new->pos = pos;
memcpy(new->data = alloc(new->size = size),data,size);
new->next = NULL;
if (last) last->next = new;
else changes = new;
last = new;
}
static void fs_flush(void)
{
CHANGE *this;
int size;
while (changes) {
this = changes;
changes = changes->next;
if (llseek(fd,this->pos,0) != this->pos)
fprintf(stderr,"Seek to %lld failed: %s\n Did not write %d bytes.\n",
(long long)this->pos,strerror(errno),this->size);
else if ((size = write(fd,this->data,this->size)) < 0)
fprintf(stderr,"Writing %d bytes at %lld failed: %s\n",this->size,
(long long)this->pos,strerror(errno));
else if (size != this->size)
fprintf(stderr,"Wrote %d bytes instead of %d bytes at %lld."
"\n",size,this->size,(long long)this->pos);
free(this->data);
free(this);
}
}
int fs_close(int write)
{
CHANGE *next;
int changed;
changed = !!changes;
if (write) fs_flush();
else while (changes) {
next = changes->next;
free(changes->data);
free(changes);
changes = next;
}
if (close(fd) < 0) pdie("closing file system");
return changed || did_change;
}
int fs_changed(void)
{
return !!changes || did_change;
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

53
src/io.h Normal file
View File

@@ -0,0 +1,53 @@
/* io.h - Virtual disk input/output */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#ifndef _IO_H
#define _IO_H
#include <sys/types.h> /* for loff_t */
/* In earlier versions, an own llseek() was used, but glibc lseek() is
* sufficient (or even better :) for 64 bit offsets in the meantime */
#define llseek lseek
void fs_open(char *path,int rw);
/* Opens the file system PATH. If RW is zero, the file system is opened
read-only, otherwise, it is opened read-write. */
void fs_read(loff_t pos,int size,void *data);
/* Reads SIZE bytes starting at POS into DATA. Performs all applicable
changes. */
int fs_test(loff_t pos,int size);
/* Returns a non-zero integer if SIZE bytes starting at POS can be read without
errors. Otherwise, it returns zero. */
void fs_write(loff_t pos,int size,void *data);
/* If write_immed is non-zero, SIZE bytes are written from DATA to the disk,
starting at POS. If write_immed is zero, the change is added to a list in
memory. */
int fs_close(int write);
/* Closes the file system, performs all pending changes if WRITE is non-zero
and removes the list of changes. Returns a non-zero integer if the file
system has been changed since the last fs_open, zero otherwise. */
int fs_changed(void);
/* Determines whether the file system has changed. See fs_close. */
extern unsigned device_no;
/* Major number of device (0 if file) and size (in 512 byte sectors) */
#endif

478
src/lfn.c Normal file
View File

@@ -0,0 +1,478 @@
/* lfn.c - Functions for handling VFAT long filenames */
/* Written 1998 by Roman Hodek */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include "common.h"
#include "io.h"
#include "dosfsck.h"
#include "lfn.h"
#include "file.h"
typedef struct {
__u8 id; /* sequence number for slot */
__u8 name0_4[10]; /* first 5 characters in name */
__u8 attr; /* attribute byte */
__u8 reserved; /* always 0 */
__u8 alias_checksum; /* checksum for 8.3 alias */
__u8 name5_10[12]; /* 6 more characters in name */
__u16 start; /* starting cluster number, 0 in long slots */
__u8 name11_12[4]; /* last 2 characters in name */
} LFN_ENT;
#define LFN_ID_START 0x40
#define LFN_ID_SLOTMASK 0x1f
#define CHARS_PER_LFN 13
/* These modul-global vars represent the state of the LFN parser */
unsigned char *lfn_unicode = NULL;
unsigned char lfn_checksum;
int lfn_slot = -1;
loff_t *lfn_offsets = NULL;
int lfn_parts = 0;
static unsigned char fat_uni2esc[64] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '+', '-'
};
/* This defines which unicode chars are directly convertable to ISO-8859-1 */
#define UNICODE_CONVERTABLE(cl,ch) (ch == 0 && (cl < 0x80 || cl >= 0xa0))
/* for maxlen param */
#define UNTIL_0 INT_MAX
/* Convert name part in 'lfn' from unicode to ASCII */
#define CNV_THIS_PART(lfn) \
({ \
char __part_uni[CHARS_PER_LFN*2]; \
copy_lfn_part( __part_uni, lfn ); \
cnv_unicode( __part_uni, CHARS_PER_LFN, 0 ); \
})
/* Convert name parts collected so far (from previous slots) from unicode to
* ASCII */
#define CNV_PARTS_SO_FAR() \
(cnv_unicode( lfn_unicode+(lfn_slot*CHARS_PER_LFN*2), \
lfn_parts*CHARS_PER_LFN, 0 ))
/* This function converts an unicode string to a normal ASCII string, assuming
* ISO-8859-1 charset. Characters not in 8859-1 are converted to the same
* escape notation as used by the kernel, i.e. the uuencode-like ":xxx" */
static char *cnv_unicode( const unsigned char *uni, int maxlen, int use_q )
{
const unsigned char *up;
unsigned char *out, *cp;
int len, val;
for( len = 0, up = uni; (up-uni)/2 < maxlen && (up[0] || up[1]); up += 2 ){
if (UNICODE_CONVERTABLE(up[0],up[1]))
++len;
else
len += 4;
}
cp = out = use_q ? qalloc( &mem_queue, len+1 ) : alloc( len+1 );
for( up = uni; (up-uni)/2 < maxlen && (up[0] || up[1]); up += 2 ) {
if (UNICODE_CONVERTABLE(up[0],up[1]))
*cp++ = up[0];
else {
/* here the same escape notation is used as in the Linux kernel */
*cp++ = ':';
val = (up[1] << 8) + up[0];
cp[2] = fat_uni2esc[val & 0x3f];
val >>= 6;
cp[1] = fat_uni2esc[val & 0x3f];
val >>= 6;
cp[0] = fat_uni2esc[val & 0x3f];
cp += 3;
}
}
*cp = 0;
return( out );
}
static void copy_lfn_part( char *dst, LFN_ENT *lfn )
{
memcpy( dst, lfn->name0_4, 10 );
memcpy( dst+10, lfn->name5_10, 12 );
memcpy( dst+22, lfn->name11_12, 4 );
}
static void clear_lfn_slots( int start, int end )
{
int i;
LFN_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.id = DELETED_FLAG;
for( i = start; i <= end; ++i ) {
fs_write( lfn_offsets[i], sizeof(LFN_ENT), &empty );
}
}
void lfn_reset( void )
{
if (lfn_unicode)
free( lfn_unicode );
lfn_unicode = NULL;
if (lfn_offsets)
free( lfn_offsets );
lfn_offsets = NULL;
lfn_slot = -1;
}
/* This function is only called with de->attr == VFAT_LN_ATTR. It stores part
* of the long name. */
void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
{
LFN_ENT *lfn = (LFN_ENT *)de;
int slot = lfn->id & LFN_ID_SLOTMASK;
unsigned offset;
if (lfn_slot == 0) lfn_check_orphaned();
if (de->attr != VFAT_LN_ATTR)
die("lfn_add_slot called with non-LFN directory entry");
if (lfn->id & LFN_ID_START && slot != 0) {
if (lfn_slot != -1) {
int can_clear = 0;
/* There is already a LFN "in progess", so it is an error that a
* new start entry is here. */
/* Causes: 1) if slot# == expected: start bit set mysteriously, 2)
* old LFN overwritten by new one */
/* Fixes: 1) delete previous LFN 2) if slot# == expected and
* checksum ok: clear start bit */
/* XXX: Should delay that until next LFN known (then can better
* display the name) */
printf( "A new long file name starts within an old one.\n" );
if (slot == lfn_slot &&
lfn->alias_checksum == lfn_checksum) {
char *part1 = CNV_THIS_PART(lfn);
char *part2 = CNV_PARTS_SO_FAR();
printf( " It could be that the LFN start bit is wrong here\n"
" if \"%s\" seems to match \"%s\".\n", part1, part2 );
free( part1 );
free( part2 );
can_clear = 1;
}
if (interactive) {
printf( "1: Delete previous LFN\n2: Leave it as it is.\n" );
if (can_clear)
printf( "3: Clear start bit and concatenate LFNs\n" );
}
else printf( " Not auto-correcting this.\n" );
if (interactive) {
switch( get_key( can_clear ? "123" : "12", "?" )) {
case '1':
clear_lfn_slots( 0, lfn_parts-1 );
lfn_reset();
break;
case '2':
break;
case '3':
lfn->id &= ~LFN_ID_START;
fs_write( dir_offset+offsetof(LFN_ENT,id),
sizeof(lfn->id), &lfn->id );
break;
}
}
}
lfn_slot = slot;
lfn_checksum = lfn->alias_checksum;
lfn_unicode = alloc( (lfn_slot*CHARS_PER_LFN+1)*2 );
lfn_offsets = alloc( lfn_slot*sizeof(loff_t) );
lfn_parts = 0;
}
else if (lfn_slot == -1 && slot != 0) {
/* No LFN in progress, but slot found; start bit missing */
/* Causes: 1) start bit got lost, 2) Previous slot with start bit got
* lost */
/* Fixes: 1) delete LFN, 2) set start bit */
char *part = CNV_THIS_PART(lfn);
printf( "Long filename fragment \"%s\" found outside a LFN "
"sequence.\n (Maybe the start bit is missing on the "
"last fragment)\n", part );
if (interactive) {
printf( "1: Delete fragment\n2: Leave it as it is.\n"
"3: Set start bit\n" );
}
else printf( " Not auto-correcting this.\n" );
switch( interactive ? get_key( "123", "?" ) : '2') {
case '1':
if (!lfn_offsets)
lfn_offsets = alloc( sizeof(loff_t) );
lfn_offsets[0] = dir_offset;
clear_lfn_slots( 0, 0 );
lfn_reset();
return;
case '2':
lfn_reset();
return;
case '3':
lfn->id |= LFN_ID_START;
fs_write( dir_offset+offsetof(LFN_ENT,id),
sizeof(lfn->id), &lfn->id );
lfn_slot = slot;
lfn_checksum = lfn->alias_checksum;
lfn_unicode = alloc( (lfn_slot*CHARS_PER_LFN+1)*2 );
lfn_offsets = alloc( lfn_slot*sizeof(loff_t) );
lfn_parts = 0;
break;
}
}
else if (slot != lfn_slot) {
/* wrong sequence number */
/* Causes: 1) seq-no destroyed */
/* Fixes: 1) delete LFN, 2) fix number (maybe only if following parts
* are ok?, maybe only if checksum is ok?) (Attention: space
* for name was allocated before!) */
int can_fix = 0;
printf( "Unexpected long filename sequence number "
"(%d vs. expected %d).\n",
slot, lfn_slot );
if (lfn->alias_checksum == lfn_checksum && lfn_slot > 0) {
char *part1 = CNV_THIS_PART(lfn);
char *part2 = CNV_PARTS_SO_FAR();
printf( " It could be that just the number is wrong\n"
" if \"%s\" seems to match \"%s\".\n", part1, part2 );
free( part1 );
free( part2 );
can_fix = 1;
}
if (interactive) {
printf( "1: Delete LFN\n2: Leave it as it is (and ignore LFN so far)\n" );
if (can_fix)
printf( "3: Correct sequence number\n" );
}
else printf( " Not auto-correcting this.\n" );
switch( interactive ? get_key( can_fix ? "123" : "12", "?" ) : '2') {
case '1':
if (!lfn_offsets) {
lfn_offsets = alloc( sizeof(loff_t) );
lfn_parts = 0;
}
lfn_offsets[lfn_parts++] = dir_offset;
clear_lfn_slots( 0, lfn_parts-1 );
lfn_reset();
return;
case '2':
lfn_reset();
return;
case '3':
lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot;
fs_write( dir_offset+offsetof(LFN_ENT,id),
sizeof(lfn->id), &lfn->id );
break;
}
}
if (lfn->alias_checksum != lfn_checksum) {
/* checksum mismatch */
/* Causes: 1) checksum field here destroyed */
/* Fixes: 1) delete LFN, 2) fix checksum */
printf( "Checksum in long filename part wrong "
"(%02x vs. expected %02x).\n",
lfn->alias_checksum, lfn_checksum );
if (interactive) {
printf( "1: Delete LFN\n2: Leave it as it is.\n"
"3: Correct checksum\n" );
}
else printf( " Not auto-correcting this.\n" );
if (interactive) {
switch( get_key( "123", "?" )) {
case '1':
lfn_offsets[lfn_parts++] = dir_offset;
clear_lfn_slots( 0, lfn_parts-1 );
lfn_reset();
return;
case '2':
break;
case '3':
lfn->alias_checksum = lfn_checksum;
fs_write( dir_offset+offsetof(LFN_ENT,alias_checksum),
sizeof(lfn->alias_checksum), &lfn->alias_checksum );
break;
}
}
}
if (lfn_slot != -1) {
lfn_slot--;
offset = lfn_slot * CHARS_PER_LFN*2;
copy_lfn_part( lfn_unicode+offset, lfn );
if (lfn->id & LFN_ID_START)
lfn_unicode[offset+26] = lfn_unicode[offset+27] = 0;
lfn_offsets[lfn_parts++] = dir_offset;
}
if (lfn->reserved != 0) {
printf( "Reserved field in VFAT long filename slot is not 0 "
"(but 0x%02x).\n", lfn->reserved );
if (interactive)
printf( "1: Fix.\n2: Leave it.\n" );
else printf( "Auto-setting to 0.\n" );
if (!interactive || get_key("12","?") == '1') {
lfn->reserved = 0;
fs_write( dir_offset+offsetof(LFN_ENT,reserved),
sizeof(lfn->reserved), &lfn->reserved );
}
}
if (lfn->start != CT_LE_W(0)) {
printf( "Start cluster field in VFAT long filename slot is not 0 "
"(but 0x%04x).\n", lfn->start );
if (interactive)
printf( "1: Fix.\n2: Leave it.\n" );
else printf( "Auto-setting to 0.\n" );
if (!interactive || get_key("12","?") == '1') {
lfn->start = CT_LE_W(0);
fs_write( dir_offset+offsetof(LFN_ENT,start),
sizeof(lfn->start),&lfn->start );
}
}
}
/* This function is always called when de->attr != VFAT_LN_ATTR is found, to
* retrieve the previously constructed LFN. */
char *lfn_get( DIR_ENT *de )
{
char *lfn;
__u8 sum;
int i;
if (de->attr == VFAT_LN_ATTR)
die("lfn_get called with LFN directory entry");
#if 0
if (de->lcase)
printf( "lcase=%02x\n",de->lcase );
#endif
if (lfn_slot == -1)
/* no long name for this file */
return NULL;
if (lfn_slot != 0) {
/* The long name isn't finished yet. */
/* Causes: 1) LFN slot overwritten by non-VFAT aware tool */
/* Fixes: 1) delete LFN 2) move overwriting entry to somewhere else
* and let user enter missing part of LFN (hard to do :-()
* 3) renumber entries and truncate name */
char *long_name = CNV_PARTS_SO_FAR();
char *short_name = file_name(de->name);
printf( "Unfinished long file name \"%s\".\n"
" (Start may have been overwritten by %s)\n",
long_name, short_name );
free( long_name );
if (interactive) {
printf( "1: Delete LFN\n2: Leave it as it is.\n"
"3: Fix numbering (truncates long name and attaches "
"it to short name %s)\n", short_name );
}
else printf( " Not auto-correcting this.\n" );
switch( interactive ? get_key( "123", "?" ) : '2') {
case '1':
clear_lfn_slots( 0, lfn_parts-1 );
lfn_reset();
return NULL;
case '2':
lfn_reset();
return NULL;
case '3':
for( i = 0; i < lfn_parts; ++i ) {
__u8 id = (lfn_parts-i) | (i==0 ? LFN_ID_START : 0);
fs_write( lfn_offsets[i]+offsetof(LFN_ENT,id),
sizeof(id), &id );
}
memmove( lfn_unicode, lfn_unicode+lfn_slot*CHARS_PER_LFN*2,
lfn_parts*CHARS_PER_LFN*2 );
break;
}
}
for (sum = 0, i = 0; i < 11; i++)
sum = (((sum&1) << 7) | ((sum&0xfe) >> 1)) + de->name[i];
if (sum != lfn_checksum) {
/* checksum doesn't match, long name doesn't apply to this alias */
/* Causes: 1) alias renamed */
/* Fixes: 1) Fix checksum in LFN entries */
char *long_name = CNV_PARTS_SO_FAR();
char *short_name = file_name(de->name);
printf( "Wrong checksum for long file name \"%s\".\n"
" (Short name %s may have changed without updating the long name)\n",
long_name, short_name );
free( long_name );
if (interactive) {
printf( "1: Delete LFN\n2: Leave it as it is.\n"
"3: Fix checksum (attaches to short name %s)\n",
short_name );
}
else printf( " Not auto-correcting this.\n" );
if (interactive) {
switch( get_key( "123", "?" )) {
case '1':
clear_lfn_slots( 0, lfn_parts-1 );
lfn_reset();
return NULL;
case '2':
lfn_reset();
return NULL;
case '3':
for( i = 0; i < lfn_parts; ++i ) {
fs_write( lfn_offsets[i]+offsetof(LFN_ENT,alias_checksum),
sizeof(sum), &sum );
}
break;
}
}
}
lfn = cnv_unicode( lfn_unicode, UNTIL_0, 1 );
lfn_reset();
return( lfn );
}
void lfn_check_orphaned(void)
{
char *long_name;
if (lfn_slot == -1)
return;
long_name = CNV_PARTS_SO_FAR();
printf("Orphaned long file name part \"%s\"\n", long_name);
if (interactive)
printf( "1: Delete.\n2: Leave it.\n" );
else printf( " Auto-deleting.\n" );
if (!interactive || get_key("12","?") == '1') {
clear_lfn_slots(0, lfn_parts - 1);
}
lfn_reset();
}
/* Local Variables: */
/* tab-width: 8 */
/* End: */

20
src/lfn.h Normal file
View File

@@ -0,0 +1,20 @@
/* lfn.h - Functions for handling VFAT long filenames */
/* Written 1998 by Roman Hodek */
#ifndef _LFN_H
#define _LFN_H
void lfn_reset( void );
/* Reset the state of the LFN parser. */
void lfn_add_slot( DIR_ENT *de, loff_t dir_offset );
/* Process a dir slot that is a VFAT LFN entry. */
char *lfn_get( DIR_ENT *de );
/* Retrieve the long name for the proper dir entry. */
void lfn_check_orphaned(void);
#endif

1762
src/mkdosfs.c Normal file

File diff suppressed because it is too large Load Diff

8
src/version.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef _version_h
#define _version_h
#define VERSION "2.11"
#define VERSION_DATE "12 Mar 2005"
#endif /* _version_h */