1*c2c66affSColin Finck /* check.c - Check and repair a PC/MS-DOS filesystem 2*c2c66affSColin Finck 3*c2c66affSColin Finck Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> 4*c2c66affSColin Finck Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> 5*c2c66affSColin Finck Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> 6*c2c66affSColin Finck Copyright (C) 2015 Andreas Bombe <aeb@debian.org> 7*c2c66affSColin Finck 8*c2c66affSColin Finck This program is free software: you can redistribute it and/or modify 9*c2c66affSColin Finck it under the terms of the GNU General Public License as published by 10*c2c66affSColin Finck the Free Software Foundation, either version 3 of the License, or 11*c2c66affSColin Finck (at your option) any later version. 12*c2c66affSColin Finck 13*c2c66affSColin Finck This program is distributed in the hope that it will be useful, 14*c2c66affSColin Finck but WITHOUT ANY WARRANTY; without even the implied warranty of 15*c2c66affSColin Finck MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*c2c66affSColin Finck GNU General Public License for more details. 17*c2c66affSColin Finck 18*c2c66affSColin Finck You should have received a copy of the GNU General Public License 19*c2c66affSColin Finck along with this program. If not, see <http://www.gnu.org/licenses/>. 20*c2c66affSColin Finck 21*c2c66affSColin Finck The complete text of the GNU General Public License 22*c2c66affSColin Finck can be found in /usr/share/common-licenses/GPL-3 file. 23*c2c66affSColin Finck */ 24*c2c66affSColin Finck 25*c2c66affSColin Finck /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 26*c2c66affSColin Finck * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */ 27*c2c66affSColin Finck 28*c2c66affSColin Finck #include "vfatlib.h" 29*c2c66affSColin Finck 30*c2c66affSColin Finck #define NDEBUG 31*c2c66affSColin Finck #include <debug.h> 32*c2c66affSColin Finck 33*c2c66affSColin Finck 34*c2c66affSColin Finck /* the longest path on the filesystem that can be handled by path_name() */ 35*c2c66affSColin Finck #define PATH_NAME_MAX 1023 // MAX_PATH 36*c2c66affSColin Finck 37*c2c66affSColin Finck static DOS_FILE *root; 38*c2c66affSColin Finck 39*c2c66affSColin Finck /* get start field of a dir entry */ 40*c2c66affSColin Finck #define FSTART(p,fs) \ 41*c2c66affSColin Finck ((uint32_t)le16toh(p->dir_ent.start) | \ 42*c2c66affSColin Finck (fs->fat_bits == 32 ? le16toh(p->dir_ent.starthi) << 16 : 0)) 43*c2c66affSColin Finck 44*c2c66affSColin Finck #define MODIFY(p,i,v) \ 45*c2c66affSColin Finck do { \ 46*c2c66affSColin Finck if (p->offset) { \ 47*c2c66affSColin Finck p->dir_ent.i = v; \ 48*c2c66affSColin Finck fs_write(p->offset+offsetof(DIR_ENT,i), \ 49*c2c66affSColin Finck sizeof(p->dir_ent.i),&p->dir_ent.i); \ 50*c2c66affSColin Finck } \ 51*c2c66affSColin Finck } while(0) 52*c2c66affSColin Finck 53*c2c66affSColin Finck #define MODIFY_START(p,v,fs) \ 54*c2c66affSColin Finck do { \ 55*c2c66affSColin Finck uint32_t __v = (v); \ 56*c2c66affSColin Finck if (!p->offset) { \ 57*c2c66affSColin Finck /* writing to fake entry for FAT32 root dir */ \ 58*c2c66affSColin Finck if (!__v) die("Oops, deleting FAT32 root dir!"); \ 59*c2c66affSColin Finck fs->root_cluster = __v; \ 60*c2c66affSColin Finck p->dir_ent.start = htole16(__v&0xffff); \ 61*c2c66affSColin Finck p->dir_ent.starthi = htole16(__v>>16); \ 62*c2c66affSColin Finck __v = htole32(__v); \ 63*c2c66affSColin Finck fs_write(offsetof(struct boot_sector,root_cluster), \ 64*c2c66affSColin Finck sizeof(((struct boot_sector *)0)->root_cluster), \ 65*c2c66affSColin Finck &__v); \ 66*c2c66affSColin Finck } \ 67*c2c66affSColin Finck else { \ 68*c2c66affSColin Finck MODIFY(p,start,htole16((__v)&0xffff)); \ 69*c2c66affSColin Finck if (fs->fat_bits == 32) \ 70*c2c66affSColin Finck MODIFY(p,starthi,htole16((__v)>>16)); \ 71*c2c66affSColin Finck } \ 72*c2c66affSColin Finck } while(0) 73*c2c66affSColin Finck 74*c2c66affSColin Finck off_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) 75*c2c66affSColin Finck { 76*c2c66affSColin Finck static int curr_num = 0; 77*c2c66affSColin Finck off_t offset; 78*c2c66affSColin Finck 79*c2c66affSColin Finck if (fs->root_cluster) { 80*c2c66affSColin Finck DIR_ENT d2; 81*c2c66affSColin Finck int i = 0, got = 0; 82*c2c66affSColin Finck uint32_t clu_num, prev = 0; 83*c2c66affSColin Finck off_t offset2; 84*c2c66affSColin Finck 85*c2c66affSColin Finck clu_num = fs->root_cluster; 86*c2c66affSColin Finck offset = cluster_start(fs, clu_num); 87*c2c66affSColin Finck while (clu_num > 0 && clu_num != -1) { 88*c2c66affSColin Finck fs_read(offset, sizeof(DIR_ENT), &d2); 89*c2c66affSColin Finck if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) { 90*c2c66affSColin Finck got = 1; 91*c2c66affSColin Finck break; 92*c2c66affSColin Finck } 93*c2c66affSColin Finck i += sizeof(DIR_ENT); 94*c2c66affSColin Finck offset += sizeof(DIR_ENT); 95*c2c66affSColin Finck if ((i % fs->cluster_size) == 0) { 96*c2c66affSColin Finck prev = clu_num; 97*c2c66affSColin Finck if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1) 98*c2c66affSColin Finck break; 99*c2c66affSColin Finck offset = cluster_start(fs, clu_num); 100*c2c66affSColin Finck } 101*c2c66affSColin Finck } 102*c2c66affSColin Finck if (!got) { 103*c2c66affSColin Finck /* no free slot, need to extend root dir: alloc next free cluster 104*c2c66affSColin Finck * after previous one */ 105*c2c66affSColin Finck if (!prev) 106*c2c66affSColin Finck die("Root directory has no cluster allocated!"); 107*c2c66affSColin Finck for (clu_num = prev + 1; clu_num != prev; clu_num++) { 108*c2c66affSColin Finck FAT_ENTRY entry; 109*c2c66affSColin Finck 110*c2c66affSColin Finck if (clu_num >= fs->data_clusters + 2) 111*c2c66affSColin Finck clu_num = 2; 112*c2c66affSColin Finck get_fat(&entry, fs->fat, clu_num, fs); 113*c2c66affSColin Finck if (!entry.value) 114*c2c66affSColin Finck break; 115*c2c66affSColin Finck } 116*c2c66affSColin Finck if (clu_num == prev) 117*c2c66affSColin Finck die("Root directory full and no free cluster"); 118*c2c66affSColin Finck set_fat(fs, prev, clu_num); 119*c2c66affSColin Finck set_fat(fs, clu_num, -1); 120*c2c66affSColin Finck set_owner(fs, clu_num, get_owner(fs, fs->root_cluster)); 121*c2c66affSColin Finck /* clear new cluster */ 122*c2c66affSColin Finck memset(&d2, 0, sizeof(d2)); 123*c2c66affSColin Finck offset = cluster_start(fs, clu_num); 124*c2c66affSColin Finck for (i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT)) 125*c2c66affSColin Finck fs_write(offset + i, sizeof(d2), &d2); 126*c2c66affSColin Finck } 127*c2c66affSColin Finck memset(de, 0, sizeof(DIR_ENT)); 128*c2c66affSColin Finck while (1) { 129*c2c66affSColin Finck char expanded[12]; 130*c2c66affSColin Finck sprintf(expanded, pattern, curr_num); 131*c2c66affSColin Finck memcpy(de->name, expanded, MSDOS_NAME); 132*c2c66affSColin Finck clu_num = fs->root_cluster; 133*c2c66affSColin Finck i = 0; 134*c2c66affSColin Finck offset2 = cluster_start(fs, clu_num); 135*c2c66affSColin Finck while (clu_num > 0 && clu_num != -1) { 136*c2c66affSColin Finck fs_read(offset2, sizeof(DIR_ENT), &d2); 137*c2c66affSColin Finck if (offset2 != offset && 138*c2c66affSColin Finck !strncmp((const char *)d2.name, (const char *)de->name, 139*c2c66affSColin Finck MSDOS_NAME)) 140*c2c66affSColin Finck break; 141*c2c66affSColin Finck i += sizeof(DIR_ENT); 142*c2c66affSColin Finck offset2 += sizeof(DIR_ENT); 143*c2c66affSColin Finck if ((i % fs->cluster_size) == 0) { 144*c2c66affSColin Finck if ((clu_num = next_cluster(fs, clu_num)) == 0 || 145*c2c66affSColin Finck clu_num == -1) 146*c2c66affSColin Finck break; 147*c2c66affSColin Finck offset2 = cluster_start(fs, clu_num); 148*c2c66affSColin Finck } 149*c2c66affSColin Finck } 150*c2c66affSColin Finck if (clu_num == 0 || clu_num == -1) 151*c2c66affSColin Finck break; 152*c2c66affSColin Finck if (++curr_num >= 10000) 153*c2c66affSColin Finck die("Unable to create unique name"); 154*c2c66affSColin Finck } 155*c2c66affSColin Finck } else { 156*c2c66affSColin Finck DIR_ENT *root; 157*c2c66affSColin Finck int next_free = 0, scan; 158*c2c66affSColin Finck 159*c2c66affSColin Finck root = alloc(fs->root_entries * sizeof(DIR_ENT)); 160*c2c66affSColin Finck fs_read(fs->root_start, fs->root_entries * sizeof(DIR_ENT), root); 161*c2c66affSColin Finck 162*c2c66affSColin Finck while (next_free < fs->root_entries) 163*c2c66affSColin Finck if (IS_FREE(root[next_free].name) && 164*c2c66affSColin Finck root[next_free].attr != VFAT_LN_ATTR) 165*c2c66affSColin Finck break; 166*c2c66affSColin Finck else 167*c2c66affSColin Finck next_free++; 168*c2c66affSColin Finck if (next_free == fs->root_entries) 169*c2c66affSColin Finck die("Root directory is full."); 170*c2c66affSColin Finck offset = fs->root_start + next_free * sizeof(DIR_ENT); 171*c2c66affSColin Finck memset(de, 0, sizeof(DIR_ENT)); 172*c2c66affSColin Finck while (1) { 173*c2c66affSColin Finck char expanded[12]; 174*c2c66affSColin Finck sprintf(expanded, pattern, curr_num); 175*c2c66affSColin Finck memcpy(de->name, expanded, MSDOS_NAME); 176*c2c66affSColin Finck for (scan = 0; scan < fs->root_entries; scan++) 177*c2c66affSColin Finck if (scan != next_free && 178*c2c66affSColin Finck !strncmp((const char *)root[scan].name, 179*c2c66affSColin Finck (const char *)de->name, MSDOS_NAME)) 180*c2c66affSColin Finck break; 181*c2c66affSColin Finck if (scan == fs->root_entries) 182*c2c66affSColin Finck break; 183*c2c66affSColin Finck if (++curr_num >= 10000) 184*c2c66affSColin Finck die("Unable to create unique name"); 185*c2c66affSColin Finck } 186*c2c66affSColin Finck free(root); 187*c2c66affSColin Finck } 188*c2c66affSColin Finck ++n_files; 189*c2c66affSColin Finck return offset; 190*c2c66affSColin Finck } 191*c2c66affSColin Finck 192*c2c66affSColin Finck /** 193*c2c66affSColin Finck * Construct a full path (starting with '/') for the specified dentry, 194*c2c66affSColin Finck * relative to the partition. All components are "long" names where possible. 195*c2c66affSColin Finck * 196*c2c66affSColin Finck * @param[in] file Information about dentry (file or directory) of interest 197*c2c66affSColin Finck * 198*c2c66affSColin Finck * return Pointer to static string containing file's full path 199*c2c66affSColin Finck */ 200*c2c66affSColin Finck static char *path_name(DOS_FILE * file) 201*c2c66affSColin Finck { 202*c2c66affSColin Finck static char path[PATH_NAME_MAX * 2]; 203*c2c66affSColin Finck 204*c2c66affSColin Finck if (!file) 205*c2c66affSColin Finck *path = 0; /* Reached the root directory */ 206*c2c66affSColin Finck else { 207*c2c66affSColin Finck if (strlen(path_name(file->parent)) > PATH_NAME_MAX) 208*c2c66affSColin Finck die("Path name too long."); 209*c2c66affSColin Finck if (strcmp(path, "/") != 0) 210*c2c66affSColin Finck strcat(path, "/"); 211*c2c66affSColin Finck 212*c2c66affSColin Finck /* Append the long name to the path, 213*c2c66affSColin Finck * or the short name if there isn't a long one 214*c2c66affSColin Finck */ 215*c2c66affSColin Finck strcpy(strrchr(path, 0), 216*c2c66affSColin Finck file->lfn ? file->lfn : file_name(file->dir_ent.name)); 217*c2c66affSColin Finck } 218*c2c66affSColin Finck return path; 219*c2c66affSColin Finck } 220*c2c66affSColin Finck 221*c2c66affSColin Finck static const int day_n[] = 222*c2c66affSColin Finck { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; 223*c2c66affSColin Finck /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ 224*c2c66affSColin Finck 225*c2c66affSColin Finck /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ 226*c2c66affSColin Finck 227*c2c66affSColin Finck static time_t date_dos2unix(unsigned short time, unsigned short date) 228*c2c66affSColin Finck { 229*c2c66affSColin Finck int month, year; 230*c2c66affSColin Finck time_t secs; 231*c2c66affSColin Finck 232*c2c66affSColin Finck month = ((date >> 5) & 15) - 1; 233*c2c66affSColin Finck if (month < 0) { 234*c2c66affSColin Finck /* make sure that nothing bad happens if the month bits were zero */ 235*c2c66affSColin Finck month = 0; 236*c2c66affSColin Finck } 237*c2c66affSColin Finck year = date >> 9; 238*c2c66affSColin Finck secs = 239*c2c66affSColin Finck (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 240*c2c66affSColin Finck 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - 241*c2c66affSColin Finck ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); 242*c2c66affSColin Finck /* days since 1.1.70 plus 80's leap day */ 243*c2c66affSColin Finck return secs; 244*c2c66affSColin Finck } 245*c2c66affSColin Finck 246*c2c66affSColin Finck #if 1 // Old version! 247*c2c66affSColin Finck 248*c2c66affSColin Finck static char *file_stat(DOS_FILE * file) 249*c2c66affSColin Finck { 250*c2c66affSColin Finck static char temp[100]; 251*c2c66affSColin Finck char tmp[100]; 252*c2c66affSColin Finck time_t date; 253*c2c66affSColin Finck LARGE_INTEGER time; 254*c2c66affSColin Finck TIME_FIELDS time_fields; 255*c2c66affSColin Finck 256*c2c66affSColin Finck date = date_dos2unix(le16toh(file->dir_ent.time), le16toh(file->dir_ent.date)); 257*c2c66affSColin Finck 258*c2c66affSColin Finck RtlSecondsSince1970ToTime(date, &time); 259*c2c66affSColin Finck RtlTimeToTimeFields(&time, &time_fields); 260*c2c66affSColin Finck 261*c2c66affSColin Finck _snprintf(tmp, sizeof(tmp), "%d:%d:%d %d.%d.%d", 262*c2c66affSColin Finck time_fields.Hour, time_fields.Minute, time_fields.Second, 263*c2c66affSColin Finck time_fields.Day, time_fields.Month, time_fields.Year); 264*c2c66affSColin Finck 265*c2c66affSColin Finck _snprintf(temp, sizeof(temp), " Size %u bytes, date %s", le32toh(file->dir_ent.size), tmp); 266*c2c66affSColin Finck return temp; 267*c2c66affSColin Finck } 268*c2c66affSColin Finck 269*c2c66affSColin Finck #else 270*c2c66affSColin Finck 271*c2c66affSColin Finck static char *file_stat(DOS_FILE * file) 272*c2c66affSColin Finck { 273*c2c66affSColin Finck static char temp[100]; 274*c2c66affSColin Finck struct tm *tm; 275*c2c66affSColin Finck char tmp[100]; 276*c2c66affSColin Finck time_t date; 277*c2c66affSColin Finck 278*c2c66affSColin Finck date = 279*c2c66affSColin Finck date_dos2unix(le16toh(file->dir_ent.time), le16toh(file->dir_ent.date)); 280*c2c66affSColin Finck tm = localtime(&date); 281*c2c66affSColin Finck strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm); 282*c2c66affSColin Finck sprintf(temp, " Size %u bytes, date %s", le32toh(file->dir_ent.size), tmp); 283*c2c66affSColin Finck return temp; 284*c2c66affSColin Finck } 285*c2c66affSColin Finck 286*c2c66affSColin Finck #endif 287*c2c66affSColin Finck 288*c2c66affSColin Finck static int bad_name(DOS_FILE * file) 289*c2c66affSColin Finck { 290*c2c66affSColin Finck int i, spc, suspicious = 0; 291*c2c66affSColin Finck const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:"; 292*c2c66affSColin Finck const unsigned char *name = file->dir_ent.name; 293*c2c66affSColin Finck const unsigned char *ext = name + 8; 294*c2c66affSColin Finck 295*c2c66affSColin Finck /* Do not complain about (and auto-correct) the extended attribute files 296*c2c66affSColin Finck * of OS/2. */ 297*c2c66affSColin Finck if (strncmp((const char *)name, "EA DATA SF", 11) == 0 || 298*c2c66affSColin Finck strncmp((const char *)name, "WP ROOT SF", 11) == 0) 299*c2c66affSColin Finck return 0; 300*c2c66affSColin Finck 301*c2c66affSColin Finck /* check if we have neither a long filename nor a short name */ 302*c2c66affSColin Finck if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) { 303*c2c66affSColin Finck return 1; 304*c2c66affSColin Finck } 305*c2c66affSColin Finck 306*c2c66affSColin Finck /* don't complain about the dummy 11 bytes used by patched Linux 307*c2c66affSColin Finck kernels */ 308*c2c66affSColin Finck if (file->dir_ent.lcase & FAT_NO_83NAME) 309*c2c66affSColin Finck return 0; 310*c2c66affSColin Finck 311*c2c66affSColin Finck for (i = 0; i < MSDOS_NAME; i++) { 312*c2c66affSColin Finck if (name[i] < ' ' || name[i] == 0x7f) 313*c2c66affSColin Finck return 1; 314*c2c66affSColin Finck if (name[i] > 0x7f) 315*c2c66affSColin Finck ++suspicious; 316*c2c66affSColin Finck if (strchr(bad_chars, name[i])) 317*c2c66affSColin Finck return 1; 318*c2c66affSColin Finck } 319*c2c66affSColin Finck 320*c2c66affSColin Finck spc = 0; 321*c2c66affSColin Finck for (i = 0; i < 8; i++) { 322*c2c66affSColin Finck if (name[i] == ' ') 323*c2c66affSColin Finck spc = 1; 324*c2c66affSColin Finck else if (spc) 325*c2c66affSColin Finck /* non-space after a space not allowed, space terminates the name 326*c2c66affSColin Finck * part */ 327*c2c66affSColin Finck return 1; 328*c2c66affSColin Finck } 329*c2c66affSColin Finck 330*c2c66affSColin Finck spc = 0; 331*c2c66affSColin Finck for (i = 0; i < 3; i++) { 332*c2c66affSColin Finck if (ext[i] == ' ') 333*c2c66affSColin Finck spc = 1; 334*c2c66affSColin Finck else if (spc) 335*c2c66affSColin Finck /* non-space after a space not allowed, space terminates the ext 336*c2c66affSColin Finck * part */ 337*c2c66affSColin Finck return 1; 338*c2c66affSColin Finck } 339*c2c66affSColin Finck 340*c2c66affSColin Finck /* Under GEMDOS, chars >= 128 are never allowed. */ 341*c2c66affSColin Finck if (atari_format && suspicious) 342*c2c66affSColin Finck return 1; 343*c2c66affSColin Finck 344*c2c66affSColin Finck #if 1 // Old !!!!!!!!!!!!!!! 345*c2c66affSColin Finck 346*c2c66affSColin Finck /* Only complain about too much suspicious chars in interactive mode, 347*c2c66affSColin Finck * never correct them automatically. The chars are all basically ok, so we 348*c2c66affSColin Finck * shouldn't auto-correct such names. */ 349*c2c66affSColin Finck if (interactive && suspicious > 6) 350*c2c66affSColin Finck return 1; 351*c2c66affSColin Finck return 0; 352*c2c66affSColin Finck 353*c2c66affSColin Finck #else 354*c2c66affSColin Finck 355*c2c66affSColin Finck /* Under MS-DOS and Windows, chars >= 128 in short names are valid 356*c2c66affSColin Finck * (but these characters can be visualised differently depending on 357*c2c66affSColin Finck * local codepage: CP437, CP866, etc). The chars are all basically ok, 358*c2c66affSColin Finck * so we shouldn't auto-correct such names. */ 359*c2c66affSColin Finck return 0; 360*c2c66affSColin Finck 361*c2c66affSColin Finck #endif 362*c2c66affSColin Finck } 363*c2c66affSColin Finck 364*c2c66affSColin Finck static void lfn_remove(off_t from, off_t to) 365*c2c66affSColin Finck { 366*c2c66affSColin Finck DIR_ENT empty; 367*c2c66affSColin Finck 368*c2c66affSColin Finck /* New dir entry is zeroed except first byte, which is set to 0xe5. 369*c2c66affSColin Finck * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading 370*c2c66affSColin Finck * a directory at the first zero entry... 371*c2c66affSColin Finck */ 372*c2c66affSColin Finck memset(&empty, 0, sizeof(empty)); 373*c2c66affSColin Finck empty.name[0] = DELETED_FLAG; 374*c2c66affSColin Finck 375*c2c66affSColin Finck for (; from < to; from += sizeof(empty)) { 376*c2c66affSColin Finck fs_write(from, sizeof(DIR_ENT), &empty); 377*c2c66affSColin Finck } 378*c2c66affSColin Finck } 379*c2c66affSColin Finck 380*c2c66affSColin Finck static void drop_file(DOS_FS * fs, DOS_FILE * file) 381*c2c66affSColin Finck { 382*c2c66affSColin Finck uint32_t cluster; 383*c2c66affSColin Finck 384*c2c66affSColin Finck MODIFY(file, name[0], DELETED_FLAG); 385*c2c66affSColin Finck if (file->lfn) 386*c2c66affSColin Finck lfn_remove(file->lfn_offset, file->offset); 387*c2c66affSColin Finck for (cluster = FSTART(file, fs); cluster > 0 && cluster < 388*c2c66affSColin Finck fs->data_clusters + 2; cluster = next_cluster(fs, cluster)) 389*c2c66affSColin Finck set_owner(fs, cluster, NULL); 390*c2c66affSColin Finck --n_files; 391*c2c66affSColin Finck } 392*c2c66affSColin Finck 393*c2c66affSColin Finck static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters) 394*c2c66affSColin Finck { 395*c2c66affSColin Finck int deleting; 396*c2c66affSColin Finck uint32_t walk, next; 397*c2c66affSColin Finck 398*c2c66affSColin Finck walk = FSTART(file, fs); 399*c2c66affSColin Finck if ((deleting = !clusters)) 400*c2c66affSColin Finck MODIFY_START(file, 0, fs); 401*c2c66affSColin Finck while (walk > 0 && walk != -1) { 402*c2c66affSColin Finck next = next_cluster(fs, walk); 403*c2c66affSColin Finck if (deleting) 404*c2c66affSColin Finck set_fat(fs, walk, 0); 405*c2c66affSColin Finck else if ((deleting = !--clusters)) 406*c2c66affSColin Finck set_fat(fs, walk, -1); 407*c2c66affSColin Finck walk = next; 408*c2c66affSColin Finck } 409*c2c66affSColin Finck } 410*c2c66affSColin Finck 411*c2c66affSColin Finck static void auto_rename(DOS_FILE * file) 412*c2c66affSColin Finck { 413*c2c66affSColin Finck DOS_FILE *first, *walk; 414*c2c66affSColin Finck uint32_t number; 415*c2c66affSColin Finck 416*c2c66affSColin Finck if (!file->offset) 417*c2c66affSColin Finck return; /* cannot rename FAT32 root dir */ 418*c2c66affSColin Finck first = file->parent ? file->parent->first : root; 419*c2c66affSColin Finck number = 0; 420*c2c66affSColin Finck while (1) { 421*c2c66affSColin Finck char num[8]; 422*c2c66affSColin Finck sprintf(num, "%07lu", (unsigned long)number); 423*c2c66affSColin Finck memcpy(file->dir_ent.name, "FSCK", 4); 424*c2c66affSColin Finck memcpy(file->dir_ent.name + 4, num, 7); 425*c2c66affSColin Finck for (walk = first; walk; walk = walk->next) 426*c2c66affSColin Finck if (walk != file 427*c2c66affSColin Finck && !strncmp((const char *)walk->dir_ent.name, 428*c2c66affSColin Finck (const char *)file->dir_ent.name, MSDOS_NAME)) 429*c2c66affSColin Finck break; 430*c2c66affSColin Finck if (!walk) { 431*c2c66affSColin Finck if (file->dir_ent.lcase & FAT_NO_83NAME) { 432*c2c66affSColin Finck /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not 433*c2c66affSColin Finck present */ 434*c2c66affSColin Finck file->dir_ent.lcase &= ~FAT_NO_83NAME; 435*c2c66affSColin Finck /* reset the attributes, only keep DIR and VOLUME */ 436*c2c66affSColin Finck file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME); 437*c2c66affSColin Finck fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent); 438*c2c66affSColin Finck } else { 439*c2c66affSColin Finck fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); 440*c2c66affSColin Finck } 441*c2c66affSColin Finck if (file->lfn) 442*c2c66affSColin Finck lfn_fix_checksum(file->lfn_offset, file->offset, 443*c2c66affSColin Finck (const char *)file->dir_ent.name); 444*c2c66affSColin Finck return; 445*c2c66affSColin Finck } 446*c2c66affSColin Finck number++; 447*c2c66affSColin Finck if (number > 9999999) { 448*c2c66affSColin Finck die("Too many files need repair."); 449*c2c66affSColin Finck } 450*c2c66affSColin Finck } 451*c2c66affSColin Finck die("Can't generate a unique name."); 452*c2c66affSColin Finck } 453*c2c66affSColin Finck 454*c2c66affSColin Finck static void rename_file(DOS_FILE *file) 455*c2c66affSColin Finck { 456*c2c66affSColin Finck //unsigned char name[46]; 457*c2c66affSColin Finck //unsigned char *walk,*here; 458*c2c66affSColin Finck 459*c2c66affSColin Finck if (!file->offset) { 460*c2c66affSColin Finck VfatPrint( "Cannot rename FAT32 root dir\n" ); 461*c2c66affSColin Finck return; /* cannot rename FAT32 root dir */ 462*c2c66affSColin Finck } 463*c2c66affSColin Finck while (1) { 464*c2c66affSColin Finck VfatPrint("New name: "); 465*c2c66affSColin Finck #if 0 466*c2c66affSColin Finck fflush(stdout); 467*c2c66affSColin Finck if (fgets((char*)name,45,stdin)) { 468*c2c66affSColin Finck if ((here = (unsigned char*)strchr((char*)name,'\n'))) *here = 0; 469*c2c66affSColin Finck for (walk = (unsigned char*)strrchr((char*)name,0); walk >= name && (*walk == ' ' || 470*c2c66affSColin Finck *walk == '\t'); walk--); 471*c2c66affSColin Finck walk[1] = 0; 472*c2c66affSColin Finck for (walk = name; *walk == ' ' || *walk == '\t'; walk++); 473*c2c66affSColin Finck if (file_cvt(walk,file->dir_ent.name)) { 474*c2c66affSColin Finck fs_write(file->offset,MSDOS_NAME,file->dir_ent.name); 475*c2c66affSColin Finck return; 476*c2c66affSColin Finck } 477*c2c66affSColin Finck } 478*c2c66affSColin Finck #else 479*c2c66affSColin Finck return; 480*c2c66affSColin Finck #endif 481*c2c66affSColin Finck } 482*c2c66affSColin Finck } 483*c2c66affSColin Finck 484*c2c66affSColin Finck static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots) 485*c2c66affSColin Finck { 486*c2c66affSColin Finck const char *name; 487*c2c66affSColin Finck 488*c2c66affSColin Finck name = 489*c2c66affSColin Finck strncmp((const char *)file->dir_ent.name, MSDOS_DOT, 490*c2c66affSColin Finck MSDOS_NAME) ? ".." : "."; 491*c2c66affSColin Finck if (!(file->dir_ent.attr & ATTR_DIR)) { 492*c2c66affSColin Finck printf("%s\n Is a non-directory.\n", path_name(file)); 493*c2c66affSColin Finck if (interactive) 494*c2c66affSColin Finck printf("1) Drop it\n2) Auto-rename\n3) Rename\n" 495*c2c66affSColin Finck "4) Convert to directory\n"); 496*c2c66affSColin Finck else 497*c2c66affSColin Finck printf(" Auto-renaming it.\n"); 498*c2c66affSColin Finck switch (interactive ? get_key("1234", "?") : '2') { 499*c2c66affSColin Finck case '1': 500*c2c66affSColin Finck drop_file(fs, file); 501*c2c66affSColin Finck return 1; 502*c2c66affSColin Finck case '2': 503*c2c66affSColin Finck auto_rename(file); 504*c2c66affSColin Finck printf(" Renamed to %s\n", file_name(file->dir_ent.name)); 505*c2c66affSColin Finck return 0; 506*c2c66affSColin Finck case '3': 507*c2c66affSColin Finck rename_file(file); 508*c2c66affSColin Finck return 0; 509*c2c66affSColin Finck case '4': 510*c2c66affSColin Finck MODIFY(file, size, htole32(0)); 511*c2c66affSColin Finck MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR); 512*c2c66affSColin Finck break; 513*c2c66affSColin Finck } 514*c2c66affSColin Finck } 515*c2c66affSColin Finck if (!dots) { 516*c2c66affSColin Finck printf("Root contains directory \"%s\". Dropping it.\n", name); 517*c2c66affSColin Finck drop_file(fs, file); 518*c2c66affSColin Finck return 1; 519*c2c66affSColin Finck } 520*c2c66affSColin Finck return 0; 521*c2c66affSColin Finck } 522*c2c66affSColin Finck 523*c2c66affSColin Finck static int check_file(DOS_FS * fs, DOS_FILE * file) 524*c2c66affSColin Finck { 525*c2c66affSColin Finck DOS_FILE *owner; 526*c2c66affSColin Finck int restart; 527*c2c66affSColin Finck uint32_t expect, curr, this, clusters, prev, walk, clusters2; 528*c2c66affSColin Finck 529*c2c66affSColin Finck if (file->dir_ent.attr & ATTR_DIR) { 530*c2c66affSColin Finck if (le32toh(file->dir_ent.size)) { 531*c2c66affSColin Finck printf("%s\n Directory has non-zero size. Fixing it.\n", 532*c2c66affSColin Finck path_name(file)); 533*c2c66affSColin Finck MODIFY(file, size, htole32(0)); 534*c2c66affSColin Finck } 535*c2c66affSColin Finck if (file->parent 536*c2c66affSColin Finck && !strncmp((const char *)file->dir_ent.name, MSDOS_DOT, 537*c2c66affSColin Finck MSDOS_NAME)) { 538*c2c66affSColin Finck expect = FSTART(file->parent, fs); 539*c2c66affSColin Finck if (FSTART(file, fs) != expect) { 540*c2c66affSColin Finck printf("%s\n Start (%lu) does not point to parent (%lu)\n", 541*c2c66affSColin Finck path_name(file), (unsigned long)FSTART(file, fs), (long)expect); 542*c2c66affSColin Finck MODIFY_START(file, expect, fs); 543*c2c66affSColin Finck } 544*c2c66affSColin Finck return 0; 545*c2c66affSColin Finck } 546*c2c66affSColin Finck if (file->parent 547*c2c66affSColin Finck && !strncmp((const char *)file->dir_ent.name, MSDOS_DOTDOT, 548*c2c66affSColin Finck MSDOS_NAME)) { 549*c2c66affSColin Finck expect = 550*c2c66affSColin Finck file->parent->parent ? FSTART(file->parent->parent, fs) : 0; 551*c2c66affSColin Finck if (fs->root_cluster && expect == fs->root_cluster) 552*c2c66affSColin Finck expect = 0; 553*c2c66affSColin Finck if (FSTART(file, fs) != expect) { 554*c2c66affSColin Finck printf("%s\n Start (%lu) does not point to .. (%lu)\n", 555*c2c66affSColin Finck path_name(file), (unsigned long)FSTART(file, fs), (unsigned long)expect); 556*c2c66affSColin Finck MODIFY_START(file, expect, fs); 557*c2c66affSColin Finck } 558*c2c66affSColin Finck return 0; 559*c2c66affSColin Finck } 560*c2c66affSColin Finck if (FSTART(file, fs) == 0) { 561*c2c66affSColin Finck printf("%s\n Start does point to root directory. Deleting dir. \n", 562*c2c66affSColin Finck path_name(file)); 563*c2c66affSColin Finck MODIFY(file, name[0], DELETED_FLAG); 564*c2c66affSColin Finck return 0; 565*c2c66affSColin Finck } 566*c2c66affSColin Finck } 567*c2c66affSColin Finck if (FSTART(file, fs) == 1) { 568*c2c66affSColin Finck printf("%s\n Bad start cluster 1. Truncating file.\n", 569*c2c66affSColin Finck path_name(file)); 570*c2c66affSColin Finck if (!file->offset) 571*c2c66affSColin Finck die("Bad FAT32 root directory! (bad start cluster 1)\n"); 572*c2c66affSColin Finck MODIFY_START(file, 0, fs); 573*c2c66affSColin Finck } 574*c2c66affSColin Finck if (FSTART(file, fs) >= fs->data_clusters + 2) { 575*c2c66affSColin Finck printf 576*c2c66affSColin Finck ("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n", 577*c2c66affSColin Finck path_name(file), (unsigned long)FSTART(file, fs), 578*c2c66affSColin Finck (unsigned long)(fs->data_clusters + 1)); 579*c2c66affSColin Finck if (!file->offset) 580*c2c66affSColin Finck die("Bad FAT32 root directory! (start cluster beyond limit: %lu > %lu)\n", 581*c2c66affSColin Finck (unsigned long)FSTART(file, fs), 582*c2c66affSColin Finck (unsigned long)(fs->data_clusters + 1)); 583*c2c66affSColin Finck MODIFY_START(file, 0, fs); 584*c2c66affSColin Finck } 585*c2c66affSColin Finck clusters = prev = 0; 586*c2c66affSColin Finck for (curr = FSTART(file, fs) ? FSTART(file, fs) : 587*c2c66affSColin Finck -1; curr != -1; curr = next_cluster(fs, curr)) { 588*c2c66affSColin Finck FAT_ENTRY curEntry; 589*c2c66affSColin Finck get_fat(&curEntry, fs->fat, curr, fs); 590*c2c66affSColin Finck 591*c2c66affSColin Finck if (!curEntry.value || bad_cluster(fs, curr)) { 592*c2c66affSColin Finck printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n", 593*c2c66affSColin Finck path_name(file), curEntry.value ? "bad" : "free", (unsigned long)curr); 594*c2c66affSColin Finck if (prev) 595*c2c66affSColin Finck set_fat(fs, prev, -1); 596*c2c66affSColin Finck else if (!file->offset) 597*c2c66affSColin Finck die("FAT32 root dir starts with a bad cluster!"); 598*c2c66affSColin Finck else 599*c2c66affSColin Finck MODIFY_START(file, 0, fs); 600*c2c66affSColin Finck break; 601*c2c66affSColin Finck } 602*c2c66affSColin Finck if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <= 603*c2c66affSColin Finck (uint64_t)clusters * fs->cluster_size) { 604*c2c66affSColin Finck printf 605*c2c66affSColin Finck ("%s\n File size is %u bytes, cluster chain length is > %llu " 606*c2c66affSColin Finck "bytes.\n Truncating file to %u bytes.\n", path_name(file), 607*c2c66affSColin Finck le32toh(file->dir_ent.size), 608*c2c66affSColin Finck (unsigned long long)clusters * fs->cluster_size, 609*c2c66affSColin Finck le32toh(file->dir_ent.size)); 610*c2c66affSColin Finck truncate_file(fs, file, clusters); 611*c2c66affSColin Finck break; 612*c2c66affSColin Finck } 613*c2c66affSColin Finck if ((owner = get_owner(fs, curr))) { 614*c2c66affSColin Finck int do_trunc = 0; 615*c2c66affSColin Finck printf("%s and\n", path_name(owner)); 616*c2c66affSColin Finck printf("%s\n share clusters.\n", path_name(file)); 617*c2c66affSColin Finck clusters2 = 0; 618*c2c66affSColin Finck for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk = 619*c2c66affSColin Finck next_cluster(fs, walk)) 620*c2c66affSColin Finck if (walk == curr) 621*c2c66affSColin Finck break; 622*c2c66affSColin Finck else 623*c2c66affSColin Finck clusters2++; 624*c2c66affSColin Finck restart = file->dir_ent.attr & ATTR_DIR; 625*c2c66affSColin Finck if (!owner->offset) { 626*c2c66affSColin Finck printf(" Truncating second to %llu bytes because first " 627*c2c66affSColin Finck "is FAT32 root dir.\n", 628*c2c66affSColin Finck (unsigned long long)clusters2 * fs->cluster_size); 629*c2c66affSColin Finck do_trunc = 2; 630*c2c66affSColin Finck } else if (!file->offset) { 631*c2c66affSColin Finck printf(" Truncating first to %llu bytes because second " 632*c2c66affSColin Finck "is FAT32 root dir.\n", 633*c2c66affSColin Finck (unsigned long long)clusters * fs->cluster_size); 634*c2c66affSColin Finck do_trunc = 1; 635*c2c66affSColin Finck } else if (interactive) 636*c2c66affSColin Finck printf("1) Truncate first to %llu bytes%s\n" 637*c2c66affSColin Finck "2) Truncate second to %llu bytes\n", 638*c2c66affSColin Finck (unsigned long long)clusters * fs->cluster_size, 639*c2c66affSColin Finck restart ? " and restart" : "", 640*c2c66affSColin Finck (unsigned long long)clusters2 * fs->cluster_size); 641*c2c66affSColin Finck else 642*c2c66affSColin Finck printf(" Truncating second to %llu bytes.\n", 643*c2c66affSColin Finck (unsigned long long)clusters2 * fs->cluster_size); 644*c2c66affSColin Finck if (do_trunc != 2 645*c2c66affSColin Finck && (do_trunc == 1 646*c2c66affSColin Finck || (interactive && get_key("12", "?") == '1'))) { 647*c2c66affSColin Finck prev = 0; 648*c2c66affSColin Finck clusters = 0; 649*c2c66affSColin Finck for (this = FSTART(owner, fs); this > 0 && this != -1; this = 650*c2c66affSColin Finck next_cluster(fs, this)) { 651*c2c66affSColin Finck if (this == curr) { 652*c2c66affSColin Finck if (prev) 653*c2c66affSColin Finck set_fat(fs, prev, -1); 654*c2c66affSColin Finck else 655*c2c66affSColin Finck MODIFY_START(owner, 0, fs); 656*c2c66affSColin Finck MODIFY(owner, size, 657*c2c66affSColin Finck htole32((uint64_t)clusters * 658*c2c66affSColin Finck fs->cluster_size)); 659*c2c66affSColin Finck if (restart) 660*c2c66affSColin Finck return 1; 661*c2c66affSColin Finck while (this > 0 && this != -1) { 662*c2c66affSColin Finck set_owner(fs, this, NULL); 663*c2c66affSColin Finck this = next_cluster(fs, this); 664*c2c66affSColin Finck } 665*c2c66affSColin Finck this = curr; 666*c2c66affSColin Finck break; 667*c2c66affSColin Finck } 668*c2c66affSColin Finck clusters++; 669*c2c66affSColin Finck prev = this; 670*c2c66affSColin Finck } 671*c2c66affSColin Finck if (this != curr) 672*c2c66affSColin Finck die("Internal error: didn't find cluster %d in chain" 673*c2c66affSColin Finck " starting at %d", curr, FSTART(owner, fs)); 674*c2c66affSColin Finck } else { 675*c2c66affSColin Finck if (prev) 676*c2c66affSColin Finck set_fat(fs, prev, -1); 677*c2c66affSColin Finck else 678*c2c66affSColin Finck MODIFY_START(file, 0, fs); 679*c2c66affSColin Finck break; 680*c2c66affSColin Finck } 681*c2c66affSColin Finck } 682*c2c66affSColin Finck set_owner(fs, curr, file); 683*c2c66affSColin Finck clusters++; 684*c2c66affSColin Finck prev = curr; 685*c2c66affSColin Finck } 686*c2c66affSColin Finck if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) > 687*c2c66affSColin Finck (uint64_t)clusters * fs->cluster_size) { 688*c2c66affSColin Finck printf 689*c2c66affSColin Finck ("%s\n File size is %u bytes, cluster chain length is %llu bytes." 690*c2c66affSColin Finck "\n Truncating file to %llu bytes.\n", path_name(file), 691*c2c66affSColin Finck le32toh(file->dir_ent.size), 692*c2c66affSColin Finck (unsigned long long)clusters * fs->cluster_size, 693*c2c66affSColin Finck (unsigned long long)clusters * fs->cluster_size); 694*c2c66affSColin Finck MODIFY(file, size, 695*c2c66affSColin Finck htole32((uint64_t)clusters * fs->cluster_size)); 696*c2c66affSColin Finck } 697*c2c66affSColin Finck return 0; 698*c2c66affSColin Finck } 699*c2c66affSColin Finck 700*c2c66affSColin Finck static int check_files(DOS_FS * fs, DOS_FILE * start) 701*c2c66affSColin Finck { 702*c2c66affSColin Finck while (start) { 703*c2c66affSColin Finck if (check_file(fs, start)) 704*c2c66affSColin Finck return 1; 705*c2c66affSColin Finck start = start->next; 706*c2c66affSColin Finck } 707*c2c66affSColin Finck return 0; 708*c2c66affSColin Finck } 709*c2c66affSColin Finck 710*c2c66affSColin Finck static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots) 711*c2c66affSColin Finck { 712*c2c66affSColin Finck DOS_FILE *parent, **walk, **scan; 713*c2c66affSColin Finck int dot, dotdot, skip, redo; 714*c2c66affSColin Finck int good, bad; 715*c2c66affSColin Finck 716*c2c66affSColin Finck if (!*root) 717*c2c66affSColin Finck return 0; 718*c2c66affSColin Finck parent = (*root)->parent; 719*c2c66affSColin Finck good = bad = 0; 720*c2c66affSColin Finck for (walk = root; *walk; walk = &(*walk)->next) 721*c2c66affSColin Finck if (bad_name(*walk)) 722*c2c66affSColin Finck bad++; 723*c2c66affSColin Finck else 724*c2c66affSColin Finck good++; 725*c2c66affSColin Finck if (*root && parent && good + bad > 4 && bad > good / 2) { 726*c2c66affSColin Finck printf("%s\n Has a large number of bad entries. (%d/%d)\n", 727*c2c66affSColin Finck path_name(parent), bad, good + bad); 728*c2c66affSColin Finck if (!dots) 729*c2c66affSColin Finck printf(" Not dropping root directory.\n"); 730*c2c66affSColin Finck else if (!interactive) 731*c2c66affSColin Finck printf(" Not dropping it in auto-mode.\n"); 732*c2c66affSColin Finck else if (get_key("yn", "Drop directory ? (y/n)") == 'y') { 733*c2c66affSColin Finck truncate_file(fs, parent, 0); 734*c2c66affSColin Finck MODIFY(parent, name[0], DELETED_FLAG); 735*c2c66affSColin Finck /* buglet: deleted directory stays in the list. */ 736*c2c66affSColin Finck return 1; 737*c2c66affSColin Finck } 738*c2c66affSColin Finck } 739*c2c66affSColin Finck dot = dotdot = redo = 0; 740*c2c66affSColin Finck walk = root; 741*c2c66affSColin Finck while (*walk) { 742*c2c66affSColin Finck if (!strncmp 743*c2c66affSColin Finck ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME) 744*c2c66affSColin Finck || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT, 745*c2c66affSColin Finck MSDOS_NAME)) { 746*c2c66affSColin Finck if (handle_dot(fs, *walk, dots)) { 747*c2c66affSColin Finck *walk = (*walk)->next; 748*c2c66affSColin Finck continue; 749*c2c66affSColin Finck } 750*c2c66affSColin Finck if (!strncmp 751*c2c66affSColin Finck ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)) 752*c2c66affSColin Finck dot++; 753*c2c66affSColin Finck else 754*c2c66affSColin Finck dotdot++; 755*c2c66affSColin Finck } 756*c2c66affSColin Finck if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) { 757*c2c66affSColin Finck printf("%s\n Bad short file name (%s).\n", 758*c2c66affSColin Finck path_name(*walk), file_name((*walk)->dir_ent.name)); 759*c2c66affSColin Finck if (interactive) 760*c2c66affSColin Finck printf("1) Drop file\n2) Rename file\n3) Auto-rename\n" 761*c2c66affSColin Finck "4) Keep it\n"); 762*c2c66affSColin Finck else 763*c2c66affSColin Finck printf(" Auto-renaming it.\n"); 764*c2c66affSColin Finck switch (interactive ? get_key("1234", "?") : '3') { 765*c2c66affSColin Finck case '1': 766*c2c66affSColin Finck drop_file(fs, *walk); 767*c2c66affSColin Finck walk = &(*walk)->next; 768*c2c66affSColin Finck continue; 769*c2c66affSColin Finck case '2': 770*c2c66affSColin Finck rename_file(*walk); 771*c2c66affSColin Finck redo = 1; 772*c2c66affSColin Finck break; 773*c2c66affSColin Finck case '3': 774*c2c66affSColin Finck auto_rename(*walk); 775*c2c66affSColin Finck printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name)); 776*c2c66affSColin Finck break; 777*c2c66affSColin Finck case '4': 778*c2c66affSColin Finck break; 779*c2c66affSColin Finck } 780*c2c66affSColin Finck } 781*c2c66affSColin Finck /* don't check for duplicates of the volume label */ 782*c2c66affSColin Finck if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) { 783*c2c66affSColin Finck scan = &(*walk)->next; 784*c2c66affSColin Finck skip = 0; 785*c2c66affSColin Finck while (*scan && !skip) { 786*c2c66affSColin Finck if (!((*scan)->dir_ent.attr & ATTR_VOLUME) && 787*c2c66affSColin Finck !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name, 788*c2c66affSColin Finck MSDOS_NAME)) { 789*c2c66affSColin Finck printf("%s\n Duplicate directory entry.\n First %s\n", 790*c2c66affSColin Finck path_name(*walk), file_stat(*walk)); 791*c2c66affSColin Finck printf(" Second %s\n", file_stat(*scan)); 792*c2c66affSColin Finck if (interactive) 793*c2c66affSColin Finck printf 794*c2c66affSColin Finck ("1) Drop first\n2) Drop second\n3) Rename first\n" 795*c2c66affSColin Finck "4) Rename second\n5) Auto-rename first\n" 796*c2c66affSColin Finck "6) Auto-rename second\n"); 797*c2c66affSColin Finck else 798*c2c66affSColin Finck printf(" Auto-renaming second.\n"); 799*c2c66affSColin Finck switch (interactive ? get_key("123456", "?") : '6') { 800*c2c66affSColin Finck case '1': 801*c2c66affSColin Finck drop_file(fs, *walk); 802*c2c66affSColin Finck *walk = (*walk)->next; 803*c2c66affSColin Finck skip = 1; 804*c2c66affSColin Finck break; 805*c2c66affSColin Finck case '2': 806*c2c66affSColin Finck drop_file(fs, *scan); 807*c2c66affSColin Finck *scan = (*scan)->next; 808*c2c66affSColin Finck continue; 809*c2c66affSColin Finck case '3': 810*c2c66affSColin Finck rename_file(*walk); 811*c2c66affSColin Finck printf(" Renamed to %s\n", path_name(*walk)); 812*c2c66affSColin Finck redo = 1; 813*c2c66affSColin Finck break; 814*c2c66affSColin Finck case '4': 815*c2c66affSColin Finck rename_file(*scan); 816*c2c66affSColin Finck printf(" Renamed to %s\n", path_name(*walk)); 817*c2c66affSColin Finck redo = 1; 818*c2c66affSColin Finck break; 819*c2c66affSColin Finck case '5': 820*c2c66affSColin Finck auto_rename(*walk); 821*c2c66affSColin Finck printf(" Renamed to %s\n", 822*c2c66affSColin Finck file_name((*walk)->dir_ent.name)); 823*c2c66affSColin Finck break; 824*c2c66affSColin Finck case '6': 825*c2c66affSColin Finck auto_rename(*scan); 826*c2c66affSColin Finck printf(" Renamed to %s\n", 827*c2c66affSColin Finck file_name((*scan)->dir_ent.name)); 828*c2c66affSColin Finck break; 829*c2c66affSColin Finck } 830*c2c66affSColin Finck } 831*c2c66affSColin Finck scan = &(*scan)->next; 832*c2c66affSColin Finck } 833*c2c66affSColin Finck if (skip) 834*c2c66affSColin Finck continue; 835*c2c66affSColin Finck } 836*c2c66affSColin Finck if (!redo) 837*c2c66affSColin Finck walk = &(*walk)->next; 838*c2c66affSColin Finck else { 839*c2c66affSColin Finck walk = root; 840*c2c66affSColin Finck dot = dotdot = redo = 0; 841*c2c66affSColin Finck } 842*c2c66affSColin Finck } 843*c2c66affSColin Finck if (dots && !dot) 844*c2c66affSColin Finck printf("%s\n \".\" is missing. Can't fix this yet.\n", 845*c2c66affSColin Finck path_name(parent)); 846*c2c66affSColin Finck if (dots && !dotdot) 847*c2c66affSColin Finck printf("%s\n \"..\" is missing. Can't fix this yet.\n", 848*c2c66affSColin Finck path_name(parent)); 849*c2c66affSColin Finck return 0; 850*c2c66affSColin Finck } 851*c2c66affSColin Finck 852*c2c66affSColin Finck /** 853*c2c66affSColin Finck * Check a dentry's cluster chain for bad clusters. 854*c2c66affSColin Finck * If requested, we verify readability and mark unreadable clusters as bad. 855*c2c66affSColin Finck * 856*c2c66affSColin Finck * @param[inout] fs Information about the filesystem 857*c2c66affSColin Finck * @param[in] file dentry to check 858*c2c66affSColin Finck * @param[in] read_test Nonzero == verify that dentry's clusters can 859*c2c66affSColin Finck * be read 860*c2c66affSColin Finck */ 861*c2c66affSColin Finck static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) 862*c2c66affSColin Finck { 863*c2c66affSColin Finck DOS_FILE *owner; 864*c2c66affSColin Finck uint32_t walk, prev, clusters, next_clu; 865*c2c66affSColin Finck 866*c2c66affSColin Finck prev = clusters = 0; 867*c2c66affSColin Finck for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2; 868*c2c66affSColin Finck walk = next_clu) { 869*c2c66affSColin Finck next_clu = next_cluster(fs, walk); 870*c2c66affSColin Finck 871*c2c66affSColin Finck /* In this stage we are checking only for a loop within our own 872*c2c66affSColin Finck * cluster chain. 873*c2c66affSColin Finck * Cross-linking of clusters is handled in check_file() 874*c2c66affSColin Finck */ 875*c2c66affSColin Finck if ((owner = get_owner(fs, walk))) { 876*c2c66affSColin Finck if (owner == file) { 877*c2c66affSColin Finck printf("%s\n Circular cluster chain. Truncating to %lu " 878*c2c66affSColin Finck "cluster%s.\n", path_name(file), (unsigned long)clusters, 879*c2c66affSColin Finck clusters == 1 ? "" : "s"); 880*c2c66affSColin Finck if (prev) 881*c2c66affSColin Finck set_fat(fs, prev, -1); 882*c2c66affSColin Finck else if (!file->offset) 883*c2c66affSColin Finck die("Bad FAT32 root directory! (bad start cluster)\n"); 884*c2c66affSColin Finck else 885*c2c66affSColin Finck MODIFY_START(file, 0, fs); 886*c2c66affSColin Finck } 887*c2c66affSColin Finck break; 888*c2c66affSColin Finck } 889*c2c66affSColin Finck if (bad_cluster(fs, walk)) 890*c2c66affSColin Finck break; 891*c2c66affSColin Finck if (read_test) { 892*c2c66affSColin Finck if (fs_test(cluster_start(fs, walk), fs->cluster_size)) { 893*c2c66affSColin Finck prev = walk; 894*c2c66affSColin Finck clusters++; 895*c2c66affSColin Finck } else { 896*c2c66affSColin Finck printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n", 897*c2c66affSColin Finck path_name(file), (unsigned long)clusters, (unsigned long)walk); 898*c2c66affSColin Finck if (prev) 899*c2c66affSColin Finck set_fat(fs, prev, next_cluster(fs, walk)); 900*c2c66affSColin Finck else 901*c2c66affSColin Finck MODIFY_START(file, next_cluster(fs, walk), fs); 902*c2c66affSColin Finck set_fat(fs, walk, -2); 903*c2c66affSColin Finck } 904*c2c66affSColin Finck } 905*c2c66affSColin Finck set_owner(fs, walk, file); 906*c2c66affSColin Finck } 907*c2c66affSColin Finck /* Revert ownership (for now) */ 908*c2c66affSColin Finck for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2; 909*c2c66affSColin Finck walk = next_cluster(fs, walk)) 910*c2c66affSColin Finck if (bad_cluster(fs, walk)) 911*c2c66affSColin Finck break; 912*c2c66affSColin Finck else if (get_owner(fs, walk) == file) 913*c2c66affSColin Finck set_owner(fs, walk, NULL); 914*c2c66affSColin Finck else 915*c2c66affSColin Finck break; 916*c2c66affSColin Finck } 917*c2c66affSColin Finck 918*c2c66affSColin Finck static void undelete(DOS_FS * fs, DOS_FILE * file) 919*c2c66affSColin Finck { 920*c2c66affSColin Finck uint32_t clusters, left, prev, walk; 921*c2c66affSColin Finck 922*c2c66affSColin Finck clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) / 923*c2c66affSColin Finck fs->cluster_size; 924*c2c66affSColin Finck prev = 0; 925*c2c66affSColin Finck 926*c2c66affSColin Finck walk = FSTART(file, fs); 927*c2c66affSColin Finck 928*c2c66affSColin Finck while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) { 929*c2c66affSColin Finck 930*c2c66affSColin Finck FAT_ENTRY curEntry; 931*c2c66affSColin Finck get_fat(&curEntry, fs->fat, walk, fs); 932*c2c66affSColin Finck 933*c2c66affSColin Finck if (!curEntry.value) 934*c2c66affSColin Finck break; 935*c2c66affSColin Finck 936*c2c66affSColin Finck left--; 937*c2c66affSColin Finck if (prev) 938*c2c66affSColin Finck set_fat(fs, prev, walk); 939*c2c66affSColin Finck prev = walk; 940*c2c66affSColin Finck walk++; 941*c2c66affSColin Finck } 942*c2c66affSColin Finck if (prev) 943*c2c66affSColin Finck set_fat(fs, prev, -1); 944*c2c66affSColin Finck else 945*c2c66affSColin Finck MODIFY_START(file, 0, fs); 946*c2c66affSColin Finck if (left) 947*c2c66affSColin Finck printf("Warning: Did only undelete %lu of %lu cluster%s.\n", 948*c2c66affSColin Finck (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s"); 949*c2c66affSColin Finck 950*c2c66affSColin Finck } 951*c2c66affSColin Finck 952*c2c66affSColin Finck static void new_dir(void) 953*c2c66affSColin Finck { 954*c2c66affSColin Finck lfn_reset(); 955*c2c66affSColin Finck } 956*c2c66affSColin Finck 957*c2c66affSColin Finck /** 958*c2c66affSColin Finck * Create a description for a referenced dentry and insert it in our dentry 959*c2c66affSColin Finck * tree. Then, go check the dentry's cluster chain for bad clusters and 960*c2c66affSColin Finck * cluster loops. 961*c2c66affSColin Finck * 962*c2c66affSColin Finck * @param[inout] fs Information about the filesystem 963*c2c66affSColin Finck * @param[out] chain 964*c2c66affSColin Finck * @param[in] parent Information about parent directory of this file 965*c2c66affSColin Finck * NULL == no parent ('file' is root directory) 966*c2c66affSColin Finck * @param[in] offset Partition-relative byte offset of directory entry of interest 967*c2c66affSColin Finck * 0 == Root directory 968*c2c66affSColin Finck * @param cp 969*c2c66affSColin Finck */ 970*c2c66affSColin Finck static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent, 971*c2c66affSColin Finck off_t offset, FDSC ** cp) 972*c2c66affSColin Finck { 973*c2c66affSColin Finck DOS_FILE *new; 974*c2c66affSColin Finck DIR_ENT de; 975*c2c66affSColin Finck FD_TYPE type; 976*c2c66affSColin Finck 977*c2c66affSColin Finck if (offset) 978*c2c66affSColin Finck fs_read(offset, sizeof(DIR_ENT), &de); 979*c2c66affSColin Finck else { 980*c2c66affSColin Finck /* Construct a DIR_ENT for the root directory */ 981*c2c66affSColin Finck memset(&de, 0, sizeof de); 982*c2c66affSColin Finck memcpy(de.name, " ", MSDOS_NAME); 983*c2c66affSColin Finck de.attr = ATTR_DIR; 984*c2c66affSColin Finck de.start = htole16(fs->root_cluster & 0xffff); 985*c2c66affSColin Finck de.starthi = htole16((fs->root_cluster >> 16) & 0xffff); 986*c2c66affSColin Finck } 987*c2c66affSColin Finck if ((type = file_type(cp, (char *)de.name)) != fdt_none) { 988*c2c66affSColin Finck if (type == fdt_undelete && (de.attr & ATTR_DIR)) 989*c2c66affSColin Finck die("Can't undelete directories."); 990*c2c66affSColin Finck file_modify(cp, (char *)de.name); 991*c2c66affSColin Finck fs_write(offset, 1, &de); 992*c2c66affSColin Finck } 993*c2c66affSColin Finck if (IS_FREE(de.name)) { 994*c2c66affSColin Finck lfn_check_orphaned(); 995*c2c66affSColin Finck return; 996*c2c66affSColin Finck } 997*c2c66affSColin Finck if (de.attr == VFAT_LN_ATTR) { 998*c2c66affSColin Finck lfn_add_slot(&de, offset); 999*c2c66affSColin Finck return; 1000*c2c66affSColin Finck } 1001*c2c66affSColin Finck new = qalloc(&mem_queue, sizeof(DOS_FILE)); 1002*c2c66affSColin Finck new->lfn = lfn_get(&de, &new->lfn_offset); 1003*c2c66affSColin Finck new->offset = offset; 1004*c2c66affSColin Finck memcpy(&new->dir_ent, &de, sizeof(de)); 1005*c2c66affSColin Finck new->next = new->first = NULL; 1006*c2c66affSColin Finck new->parent = parent; 1007*c2c66affSColin Finck if (type == fdt_undelete) 1008*c2c66affSColin Finck undelete(fs, new); 1009*c2c66affSColin Finck **chain = new; 1010*c2c66affSColin Finck *chain = &new->next; 1011*c2c66affSColin Finck if (list) { 1012*c2c66affSColin Finck printf("Checking file %s", path_name(new)); 1013*c2c66affSColin Finck if (new->lfn) 1014*c2c66affSColin Finck printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */ 1015*c2c66affSColin Finck printf("\n"); 1016*c2c66affSColin Finck } 1017*c2c66affSColin Finck /* Don't include root directory, '.', or '..' in the total file count */ 1018*c2c66affSColin Finck if (offset && 1019*c2c66affSColin Finck strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 && 1020*c2c66affSColin Finck strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0) 1021*c2c66affSColin Finck ++n_files; 1022*c2c66affSColin Finck test_file(fs, new, test); /* Bad cluster check */ 1023*c2c66affSColin Finck } 1024*c2c66affSColin Finck 1025*c2c66affSColin Finck static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp); 1026*c2c66affSColin Finck 1027*c2c66affSColin Finck static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp) 1028*c2c66affSColin Finck { 1029*c2c66affSColin Finck DOS_FILE **chain; 1030*c2c66affSColin Finck int i; 1031*c2c66affSColin Finck uint32_t clu_num; 1032*c2c66affSColin Finck 1033*c2c66affSColin Finck chain = &this->first; 1034*c2c66affSColin Finck i = 0; 1035*c2c66affSColin Finck clu_num = FSTART(this, fs); 1036*c2c66affSColin Finck new_dir(); 1037*c2c66affSColin Finck while (clu_num > 0 && clu_num != -1) { 1038*c2c66affSColin Finck add_file(fs, &chain, this, 1039*c2c66affSColin Finck cluster_start(fs, clu_num) + (i % fs->cluster_size), cp); 1040*c2c66affSColin Finck i += sizeof(DIR_ENT); 1041*c2c66affSColin Finck if (!(i % fs->cluster_size)) 1042*c2c66affSColin Finck if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1) 1043*c2c66affSColin Finck break; 1044*c2c66affSColin Finck } 1045*c2c66affSColin Finck lfn_check_orphaned(); 1046*c2c66affSColin Finck if (check_dir(fs, &this->first, this->offset)) 1047*c2c66affSColin Finck return 0; 1048*c2c66affSColin Finck if (check_files(fs, this->first)) 1049*c2c66affSColin Finck return 1; 1050*c2c66affSColin Finck return subdirs(fs, this, cp); 1051*c2c66affSColin Finck } 1052*c2c66affSColin Finck 1053*c2c66affSColin Finck /** 1054*c2c66affSColin Finck * Recursively scan subdirectories of the specified parent directory. 1055*c2c66affSColin Finck * 1056*c2c66affSColin Finck * @param[inout] fs Information about the filesystem 1057*c2c66affSColin Finck * @param[in] parent Identifies the directory to scan 1058*c2c66affSColin Finck * @param[in] cp 1059*c2c66affSColin Finck * 1060*c2c66affSColin Finck * @return 0 Success 1061*c2c66affSColin Finck * @return 1 Error 1062*c2c66affSColin Finck */ 1063*c2c66affSColin Finck static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp) 1064*c2c66affSColin Finck { 1065*c2c66affSColin Finck DOS_FILE *walk; 1066*c2c66affSColin Finck 1067*c2c66affSColin Finck for (walk = parent ? parent->first : root; walk; walk = walk->next) 1068*c2c66affSColin Finck if (walk->dir_ent.attr & ATTR_DIR) 1069*c2c66affSColin Finck if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME) 1070*c2c66affSColin Finck && strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT, 1071*c2c66affSColin Finck MSDOS_NAME)) 1072*c2c66affSColin Finck if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name))) 1073*c2c66affSColin Finck return 1; 1074*c2c66affSColin Finck return 0; 1075*c2c66affSColin Finck } 1076*c2c66affSColin Finck 1077*c2c66affSColin Finck /** 1078*c2c66affSColin Finck * Scan all directory and file information for errors. 1079*c2c66affSColin Finck * 1080*c2c66affSColin Finck * @param[inout] fs Information about the filesystem 1081*c2c66affSColin Finck * 1082*c2c66affSColin Finck * @return 0 Success 1083*c2c66affSColin Finck * @return 1 Error 1084*c2c66affSColin Finck */ 1085*c2c66affSColin Finck int scan_root(DOS_FS * fs) 1086*c2c66affSColin Finck { 1087*c2c66affSColin Finck DOS_FILE **chain; 1088*c2c66affSColin Finck int i; 1089*c2c66affSColin Finck 1090*c2c66affSColin Finck root = NULL; 1091*c2c66affSColin Finck chain = &root; 1092*c2c66affSColin Finck new_dir(); 1093*c2c66affSColin Finck if (fs->root_cluster) { 1094*c2c66affSColin Finck add_file(fs, &chain, NULL, 0, &fp_root); 1095*c2c66affSColin Finck } else { 1096*c2c66affSColin Finck for (i = 0; i < fs->root_entries; i++) 1097*c2c66affSColin Finck add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT), 1098*c2c66affSColin Finck &fp_root); 1099*c2c66affSColin Finck } 1100*c2c66affSColin Finck lfn_check_orphaned(); 1101*c2c66affSColin Finck (void)check_dir(fs, &root, 0); 1102*c2c66affSColin Finck if (check_files(fs, root)) 1103*c2c66affSColin Finck return 1; 1104*c2c66affSColin Finck return subdirs(fs, NULL, &fp_root); 1105*c2c66affSColin Finck } 1106