📄 check.c
字号:
/* 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 <windows.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 < (int)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 < (int)fs->root_entries)
if (IS_FREE(root[next_free].name) &&
root[next_free].attr != VFAT_LN_ATTR)
break;
else next_free++;
if (next_free == (int)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 < (int)fs->root_entries; scan++)
if (scan != next_free &&
!strncmp(root[scan].name,de->name,MSDOS_NAME))
break;
if (scan == (int)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];
static char path[MAX_PATH*2];
if (!file) *path = 0;
else {
if (strlen(path_name(file->parent)) > MAX_PATH)
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;
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);
strncpy(file->dir_ent.ext,"REN",3);
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++;
}
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;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -