check.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. /* check.c - Check and repair a PC/MS-DOS file system
  2. Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
  3. Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. On Debian systems, the complete text of the GNU General Public License
  15. can be found in /usr/share/common-licenses/GPL-3 file.
  16. */
  17. /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
  18. * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <limits.h>
  23. #include <time.h>
  24. #include "common.h"
  25. #include "dosfsck.h"
  26. #include "io.h"
  27. #include "fat.h"
  28. #include "file.h"
  29. #include "lfn.h"
  30. #include "check.h"
  31. static DOS_FILE *root;
  32. /* get start field of a dir entry */
  33. #define FSTART(p,fs) \
  34. ((unsigned long)CF_LE_W(p->dir_ent.start) | \
  35. (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0))
  36. #define MODIFY(p,i,v) \
  37. do { \
  38. if (p->offset) { \
  39. p->dir_ent.i = v; \
  40. fs_write(p->offset+offsetof(DIR_ENT,i), \
  41. sizeof(p->dir_ent.i),&p->dir_ent.i); \
  42. } \
  43. } while(0)
  44. #define MODIFY_START(p,v,fs) \
  45. do { \
  46. unsigned long __v = (v); \
  47. if (!p->offset) { \
  48. /* writing to fake entry for FAT32 root dir */ \
  49. if (!__v) die("Oops, deleting FAT32 root dir!"); \
  50. fs->root_cluster = __v; \
  51. p->dir_ent.start = CT_LE_W(__v&0xffff); \
  52. p->dir_ent.starthi = CT_LE_W(__v>>16); \
  53. __v = CT_LE_L(__v); \
  54. fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \
  55. sizeof(((struct boot_sector *)0)->root_cluster), \
  56. &__v); \
  57. } \
  58. else { \
  59. MODIFY(p,start,CT_LE_W((__v)&0xffff)); \
  60. if (fs->fat_bits == 32) \
  61. MODIFY(p,starthi,CT_LE_W((__v)>>16)); \
  62. } \
  63. } while(0)
  64. loff_t alloc_rootdir_entry(DOS_FS *fs, DIR_ENT *de, const char *pattern)
  65. {
  66. static int curr_num = 0;
  67. loff_t offset;
  68. if (fs->root_cluster) {
  69. DIR_ENT d2;
  70. int i = 0, got = 0;
  71. unsigned long clu_num, prev = 0;
  72. loff_t offset2;
  73. clu_num = fs->root_cluster;
  74. offset = cluster_start(fs,clu_num);
  75. while (clu_num > 0 && clu_num != -1) {
  76. fs_read(offset,sizeof(DIR_ENT),&d2);
  77. if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
  78. got = 1;
  79. break;
  80. }
  81. i += sizeof(DIR_ENT);
  82. offset += sizeof(DIR_ENT);
  83. if ((i % fs->cluster_size) == 0) {
  84. prev = clu_num;
  85. if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
  86. break;
  87. offset = cluster_start(fs,clu_num);
  88. }
  89. }
  90. if (!got) {
  91. /* no free slot, need to extend root dir: alloc next free cluster
  92. * after previous one */
  93. if (!prev)
  94. die("Root directory has no cluster allocated!");
  95. for (clu_num = prev+1; clu_num != prev; clu_num++) {
  96. FAT_ENTRY entry;
  97. if (clu_num >= fs->clusters+2) clu_num = 2;
  98. get_fat(&entry, fs->fat, clu_num, fs);
  99. if (!entry.value)
  100. break;
  101. }
  102. if (clu_num == prev)
  103. die("Root directory full and no free cluster");
  104. set_fat(fs,prev,clu_num);
  105. set_fat(fs,clu_num,-1);
  106. set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
  107. /* clear new cluster */
  108. memset( &d2, 0, sizeof(d2) );
  109. offset = cluster_start(fs,clu_num);
  110. for( i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT) )
  111. fs_write( offset+i, sizeof(d2), &d2 );
  112. }
  113. memset(de,0,sizeof(DIR_ENT));
  114. while (1) {
  115. sprintf(de->name,pattern,curr_num);
  116. clu_num = fs->root_cluster;
  117. i = 0;
  118. offset2 = cluster_start(fs,clu_num);
  119. while (clu_num > 0 && clu_num != -1) {
  120. fs_read(offset2,sizeof(DIR_ENT),&d2);
  121. if (offset2 != offset &&
  122. !strncmp(d2.name,de->name,MSDOS_NAME))
  123. break;
  124. i += sizeof(DIR_ENT);
  125. offset2 += sizeof(DIR_ENT);
  126. if ((i % fs->cluster_size) == 0) {
  127. if ((clu_num = next_cluster(fs,clu_num)) == 0 ||
  128. clu_num == -1)
  129. break;
  130. offset2 = cluster_start(fs,clu_num);
  131. }
  132. }
  133. if (clu_num == 0 || clu_num == -1)
  134. break;
  135. if (++curr_num >= 10000) die("Unable to create unique name");
  136. }
  137. }
  138. else {
  139. DIR_ENT *root;
  140. int next_free = 0, scan;
  141. root = alloc(fs->root_entries*sizeof(DIR_ENT));
  142. fs_read(fs->root_start,fs->root_entries*sizeof(DIR_ENT),root);
  143. while (next_free < fs->root_entries)
  144. if (IS_FREE(root[next_free].name) &&
  145. root[next_free].attr != VFAT_LN_ATTR)
  146. break;
  147. else next_free++;
  148. if (next_free == fs->root_entries)
  149. die("Root directory is full.");
  150. offset = fs->root_start+next_free*sizeof(DIR_ENT);
  151. memset(de,0,sizeof(DIR_ENT));
  152. while (1) {
  153. sprintf(de->name,pattern,curr_num);
  154. for (scan = 0; scan < fs->root_entries; scan++)
  155. if (scan != next_free &&
  156. !strncmp(root[scan].name,de->name,MSDOS_NAME))
  157. break;
  158. if (scan == fs->root_entries) break;
  159. if (++curr_num >= 10000) die("Unable to create unique name");
  160. }
  161. free(root);
  162. }
  163. ++n_files;
  164. return offset;
  165. }
  166. /**
  167. * Construct a full path (starting with '/') for the specified dentry,
  168. * relative to the partition. All components are "long" names where possible.
  169. *
  170. * @param[in] file Information about dentry (file or directory) of interest
  171. *
  172. * return Pointer to static string containing file's full path
  173. */
  174. static char *path_name(DOS_FILE *file)
  175. {
  176. static char path[PATH_MAX*2];
  177. if (!file) *path = 0; /* Reached the root directory */
  178. else {
  179. if (strlen(path_name(file->parent)) > PATH_MAX)
  180. die("Path name too long.");
  181. if (strcmp(path,"/") != 0) strcat(path,"/");
  182. /* Append the long name to the path,
  183. * or the short name if there isn't a long one
  184. */
  185. strcpy(strrchr(path,0),file->lfn?file->lfn:file_name(file->dir_ent.name));
  186. }
  187. return path;
  188. }
  189. static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
  190. /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
  191. /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
  192. time_t date_dos2unix(unsigned short time,unsigned short date)
  193. {
  194. int month,year;
  195. time_t secs;
  196. month = ((date >> 5) & 15)-1;
  197. year = date >> 9;
  198. secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
  199. ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
  200. month < 2 ? 1 : 0)+3653);
  201. /* days since 1.1.70 plus 80's leap day */
  202. return secs;
  203. }
  204. static char *file_stat(DOS_FILE *file)
  205. {
  206. static char temp[100];
  207. struct tm *tm;
  208. char tmp[100];
  209. time_t date;
  210. date = date_dos2unix(CF_LE_W(file->dir_ent.time),CF_LE_W(file->
  211. dir_ent.date));
  212. tm = localtime(&date);
  213. strftime(tmp,99,"%H:%M:%S %b %d %Y",tm);
  214. sprintf(temp," Size %u bytes, date %s",CF_LE_L(file->dir_ent.size),tmp);
  215. return temp;
  216. }
  217. static int bad_name(DOS_FILE *file)
  218. {
  219. int i, spc, suspicious = 0;
  220. char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
  221. char *name = file->dir_ent.name;
  222. /* Do not complain about (and auto-correct) the extended attribute files
  223. * of OS/2. */
  224. if (strncmp(name,"EA DATA SF",11) == 0 ||
  225. strncmp(name,"WP ROOT SF",11) == 0) return 0;
  226. /* don't complain about the dummy 11 bytes used by patched Linux
  227. kernels */
  228. if (file->lfn && name[0] == ' ') return 0;
  229. for (i = 0; i < 8; i++) {
  230. if (name[i] < ' ' || name[i] == 0x7f) return 1;
  231. if (name[i] > 0x7f) ++suspicious;
  232. if (strchr(bad_chars,name[i])) return 1;
  233. }
  234. for (i = 8; i < 11; i++) {
  235. if (name[i] < ' ' || name[i] == 0x7f) return 1;
  236. if (name[i] > 0x7f) ++suspicious;
  237. if (strchr(bad_chars,name[i])) return 1;
  238. }
  239. spc = 0;
  240. for (i = 0; i < 8; i++) {
  241. if (name[i] == ' ')
  242. spc = 1;
  243. else if (spc)
  244. /* non-space after a space not allowed, space terminates the name
  245. * part */
  246. return 1;
  247. }
  248. spc = 0;
  249. for (i = 8; i < 11; i++) {
  250. if (name[i] == ' ')
  251. spc = 1;
  252. else if (spc)
  253. /* non-space after a space not allowed, space terminates the name
  254. * part */
  255. return 1;
  256. }
  257. /* Under GEMDOS, chars >= 128 are never allowed. */
  258. if (atari_format && suspicious)
  259. return 1;
  260. /* Only complain about too much suspicious chars in interactive mode,
  261. * never correct them automatically. The chars are all basically ok, so we
  262. * shouldn't auto-correct such names. */
  263. if (interactive && suspicious > 6)
  264. return 1;
  265. return 0;
  266. }
  267. static void drop_file(DOS_FS *fs,DOS_FILE *file)
  268. {
  269. unsigned long cluster;
  270. MODIFY(file,name[0],DELETED_FLAG);
  271. for (cluster = FSTART(file,fs); cluster > 0 && cluster <
  272. fs->clusters+2; cluster = next_cluster(fs,cluster))
  273. set_owner(fs,cluster,NULL);
  274. --n_files;
  275. }
  276. static void truncate_file(DOS_FS *fs,DOS_FILE *file,unsigned long clusters)
  277. {
  278. int deleting;
  279. unsigned long walk,next,prev;
  280. walk = FSTART(file,fs);
  281. prev = 0;
  282. if ((deleting = !clusters)) MODIFY_START(file,0,fs);
  283. while (walk > 0 && walk != -1) {
  284. next = next_cluster(fs,walk);
  285. if (deleting) set_fat(fs,walk,0);
  286. else if ((deleting = !--clusters)) set_fat(fs,walk,-1);
  287. prev = walk;
  288. walk = next;
  289. }
  290. }
  291. static void auto_rename(DOS_FILE *file)
  292. {
  293. DOS_FILE *first,*walk;
  294. unsigned long int number;
  295. if (!file->offset) return; /* cannot rename FAT32 root dir */
  296. first = file->parent ? file->parent->first : root;
  297. number = 0;
  298. while (1) {
  299. sprintf(file->dir_ent.name, "FSCK%04d", number / 1000);
  300. sprintf(file->dir_ent.ext, "%03d", number % 1000);
  301. for (walk = first; walk; walk = walk->next)
  302. if (walk != file && !strncmp(walk->dir_ent.name,file->dir_ent.
  303. name,MSDOS_NAME)) break;
  304. if (!walk) {
  305. fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
  306. return;
  307. }
  308. number++;
  309. if (number > 9999999) {
  310. die("Too many files need repair.");
  311. }
  312. }
  313. die("Can't generate a unique name.");
  314. }
  315. static void rename_file(DOS_FILE *file)
  316. {
  317. unsigned char name[46];
  318. unsigned char *walk,*here;
  319. if (!file->offset) {
  320. printf( "Cannot rename FAT32 root dir\n" );
  321. return; /* cannot rename FAT32 root dir */
  322. }
  323. while (1) {
  324. printf("New name: ");
  325. fflush(stdout);
  326. if (fgets(name,45,stdin)) {
  327. if ((here = strchr(name,'\n'))) *here = 0;
  328. for (walk = strrchr(name,0); walk >= name && (*walk == ' ' ||
  329. *walk == '\t'); walk--);
  330. walk[1] = 0;
  331. for (walk = name; *walk == ' ' || *walk == '\t'; walk++);
  332. if (file_cvt(walk,file->dir_ent.name)) {
  333. fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
  334. return;
  335. }
  336. }
  337. }
  338. }
  339. static int handle_dot(DOS_FS *fs,DOS_FILE *file,int dots)
  340. {
  341. char *name;
  342. name = strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ? ".." : ".";
  343. if (!(file->dir_ent.attr & ATTR_DIR)) {
  344. printf("%s\n Is a non-directory.\n",path_name(file));
  345. if (interactive)
  346. printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
  347. "4) Convert to directory\n");
  348. else printf(" Auto-renaming it.\n");
  349. switch (interactive ? get_key("1234","?") : '2') {
  350. case '1':
  351. drop_file(fs,file);
  352. return 1;
  353. case '2':
  354. auto_rename(file);
  355. printf(" Renamed to %s\n",file_name(file->dir_ent.name));
  356. return 0;
  357. case '3':
  358. rename_file(file);
  359. return 0;
  360. case '4':
  361. MODIFY(file,size,CT_LE_L(0));
  362. MODIFY(file,attr,file->dir_ent.attr | ATTR_DIR);
  363. break;
  364. }
  365. }
  366. if (!dots) {
  367. printf("Root contains directory \"%s\". Dropping it.\n",name);
  368. drop_file(fs,file);
  369. return 1;
  370. }
  371. return 0;
  372. }
  373. static int check_file(DOS_FS *fs,DOS_FILE *file)
  374. {
  375. DOS_FILE *owner;
  376. int restart;
  377. unsigned long expect,curr,this,clusters,prev,walk,clusters2;
  378. if (file->dir_ent.attr & ATTR_DIR) {
  379. if (CF_LE_L(file->dir_ent.size)) {
  380. printf("%s\n Directory has non-zero size. Fixing it.\n",
  381. path_name(file));
  382. MODIFY(file,size,CT_LE_L(0));
  383. }
  384. if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) {
  385. expect = FSTART(file->parent,fs);
  386. if (FSTART(file,fs) != expect) {
  387. printf("%s\n Start (%ld) does not point to parent (%ld)\n",
  388. path_name(file),FSTART(file,fs),expect);
  389. MODIFY_START(file,expect,fs);
  390. }
  391. return 0;
  392. }
  393. if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOTDOT,
  394. MSDOS_NAME)) {
  395. expect = file->parent->parent ? FSTART(file->parent->parent,fs):0;
  396. if (fs->root_cluster && expect == fs->root_cluster)
  397. expect = 0;
  398. if (FSTART(file,fs) != expect) {
  399. printf("%s\n Start (%lu) does not point to .. (%lu)\n",
  400. path_name(file),FSTART(file,fs),expect);
  401. MODIFY_START(file,expect,fs);
  402. }
  403. return 0;
  404. }
  405. if (FSTART(file,fs)==0){
  406. printf ("%s\n Start does point to root directory. Deleting dir. \n",
  407. path_name(file));
  408. MODIFY(file,name[0],DELETED_FLAG);
  409. return 0;
  410. }
  411. }
  412. if (FSTART(file,fs) >= fs->clusters+2) {
  413. printf("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n",
  414. path_name(file),FSTART(file,fs),fs->clusters+1);
  415. if (!file->offset)
  416. die( "Bad FAT32 root directory! (bad start cluster)\n" );
  417. MODIFY_START(file,0,fs);
  418. }
  419. clusters = prev = 0;
  420. for (curr = FSTART(file,fs) ? FSTART(file,fs) :
  421. -1; curr != -1; curr = next_cluster(fs,curr)) {
  422. FAT_ENTRY curEntry;
  423. get_fat(&curEntry, fs->fat, curr, fs);
  424. if (!curEntry.value || bad_cluster(fs,curr)) {
  425. printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n",
  426. path_name(file), curEntry.value ? "bad" : "free",curr);
  427. if (prev) set_fat(fs,prev,-1);
  428. else if (!file->offset)
  429. die( "FAT32 root dir starts with a bad cluster!" );
  430. else MODIFY_START(file,0,fs);
  431. break;
  432. }
  433. if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
  434. (unsigned long long)clusters*fs->cluster_size) {
  435. printf("%s\n File size is %u bytes, cluster chain length is > %llu "
  436. "bytes.\n Truncating file to %u bytes.\n",path_name(file),
  437. CF_LE_L(file->dir_ent.size),(unsigned long long)clusters*fs->cluster_size,
  438. CF_LE_L(file->dir_ent.size));
  439. truncate_file(fs,file,clusters);
  440. break;
  441. }
  442. if ((owner = get_owner(fs,curr))) {
  443. int do_trunc = 0;
  444. printf("%s and\n",path_name(owner));
  445. printf("%s\n share clusters.\n",path_name(file));
  446. clusters2 = 0;
  447. for (walk = FSTART(owner,fs); walk > 0 && walk != -1; walk =
  448. next_cluster(fs,walk))
  449. if (walk == curr) break;
  450. else clusters2++;
  451. restart = file->dir_ent.attr & ATTR_DIR;
  452. if (!owner->offset) {
  453. printf( " Truncating second to %llu bytes because first "
  454. "is FAT32 root dir.\n", (unsigned long long)clusters2*fs->cluster_size );
  455. do_trunc = 2;
  456. }
  457. else if (!file->offset) {
  458. printf( " Truncating first to %llu bytes because second "
  459. "is FAT32 root dir.\n", (unsigned long long)clusters*fs->cluster_size );
  460. do_trunc = 1;
  461. }
  462. else if (interactive)
  463. printf("1) Truncate first to %llu bytes%s\n"
  464. "2) Truncate second to %llu bytes\n",(unsigned long long)clusters*fs->cluster_size,
  465. restart ? " and restart" : "",(unsigned long long)clusters2*fs->cluster_size);
  466. else printf(" Truncating second to %llu bytes.\n",(unsigned long long)clusters2*
  467. fs->cluster_size);
  468. if (do_trunc != 2 &&
  469. (do_trunc == 1 ||
  470. (interactive && get_key("12","?") == '1'))) {
  471. prev = 0;
  472. clusters = 0;
  473. for (this = FSTART(owner,fs); this > 0 && this != -1; this =
  474. next_cluster(fs,this)) {
  475. if (this == curr) {
  476. if (prev) set_fat(fs,prev,-1);
  477. else MODIFY_START(owner,0,fs);
  478. MODIFY(owner,size,CT_LE_L((unsigned long long)clusters*fs->cluster_size));
  479. if (restart) return 1;
  480. while (this > 0 && this != -1) {
  481. set_owner(fs,this,NULL);
  482. this = next_cluster(fs,this);
  483. }
  484. this = curr;
  485. break;
  486. }
  487. clusters++;
  488. prev = this;
  489. }
  490. if (this != curr)
  491. die("Internal error: didn't find cluster %d in chain"
  492. " starting at %d",curr,FSTART(owner,fs));
  493. }
  494. else {
  495. if (prev) set_fat(fs,prev,-1);
  496. else MODIFY_START(file,0,fs);
  497. break;
  498. }
  499. }
  500. set_owner(fs,curr,file);
  501. clusters++;
  502. prev = curr;
  503. }
  504. if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
  505. (unsigned long long)clusters*fs->cluster_size) {
  506. printf("%s\n File size is %u bytes, cluster chain length is %llu bytes."
  507. "\n Truncating file to %lu bytes.\n",path_name(file),CF_LE_L(file->
  508. dir_ent.size),(unsigned long long)clusters*fs->cluster_size,(unsigned long long)clusters*fs->cluster_size);
  509. MODIFY(file,size,CT_LE_L((unsigned long long)clusters*fs->cluster_size));
  510. }
  511. return 0;
  512. }
  513. static int check_files(DOS_FS *fs,DOS_FILE *start)
  514. {
  515. while (start) {
  516. if (check_file(fs,start)) return 1;
  517. start = start->next;
  518. }
  519. return 0;
  520. }
  521. static int check_dir(DOS_FS *fs,DOS_FILE **root,int dots)
  522. {
  523. DOS_FILE *parent,**walk,**scan;
  524. int dot,dotdot,skip,redo;
  525. int good,bad;
  526. if (!*root) return 0;
  527. parent = (*root)->parent;
  528. good = bad = 0;
  529. for (walk = root; *walk; walk = &(*walk)->next)
  530. if (bad_name(*walk)) bad++;
  531. else good++;
  532. if (*root && parent && good+bad > 4 && bad > good/2) {
  533. printf("%s\n Has a large number of bad entries. (%d/%d)\n",
  534. path_name(parent),bad,good+bad);
  535. if (!dots) printf( " Not dropping root directory.\n" );
  536. else if (!interactive) printf(" Not dropping it in auto-mode.\n");
  537. else if (get_key("yn","Drop directory ? (y/n)") == 'y') {
  538. truncate_file(fs,parent,0);
  539. MODIFY(parent,name[0],DELETED_FLAG);
  540. /* buglet: deleted directory stays in the list. */
  541. return 1;
  542. }
  543. }
  544. dot = dotdot = redo = 0;
  545. walk = root;
  546. while (*walk) {
  547. if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ||
  548. !strncmp((*walk)->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) {
  549. if (handle_dot(fs,*walk,dots)) {
  550. *walk = (*walk)->next;
  551. continue;
  552. }
  553. if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) dot++;
  554. else dotdot++;
  555. }
  556. if (!((*walk)->dir_ent.attr & ATTR_VOLUME) &&
  557. bad_name(*walk)) {
  558. printf("%s\n Bad file name.\n",path_name(*walk));
  559. if (interactive)
  560. printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
  561. "4) Keep it\n");
  562. else printf(" Auto-renaming it.\n");
  563. switch (interactive ? get_key("1234","?") : '3') {
  564. case '1':
  565. drop_file(fs,*walk);
  566. walk = &(*walk)->next;
  567. continue;
  568. case '2':
  569. rename_file(*walk);
  570. redo = 1;
  571. break;
  572. case '3':
  573. auto_rename(*walk);
  574. printf(" Renamed to %s\n",file_name((*walk)->dir_ent.
  575. name));
  576. break;
  577. case '4':
  578. break;
  579. }
  580. }
  581. /* don't check for duplicates of the volume label */
  582. if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
  583. scan = &(*walk)->next;
  584. skip = 0;
  585. while (*scan && !skip) {
  586. if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
  587. !memcmp((*walk)->dir_ent.name,(*scan)->dir_ent.name,MSDOS_NAME)) {
  588. printf("%s\n Duplicate directory entry.\n First %s\n",
  589. path_name(*walk),file_stat(*walk));
  590. printf(" Second %s\n",file_stat(*scan));
  591. if (interactive)
  592. printf("1) Drop first\n2) Drop second\n3) Rename first\n"
  593. "4) Rename second\n5) Auto-rename first\n"
  594. "6) Auto-rename second\n");
  595. else printf(" Auto-renaming second.\n");
  596. switch (interactive ? get_key("123456","?") : '6') {
  597. case '1':
  598. drop_file(fs,*walk);
  599. *walk = (*walk)->next;
  600. skip = 1;
  601. break;
  602. case '2':
  603. drop_file(fs,*scan);
  604. *scan = (*scan)->next;
  605. continue;
  606. case '3':
  607. rename_file(*walk);
  608. printf(" Renamed to %s\n",path_name(*walk));
  609. redo = 1;
  610. break;
  611. case '4':
  612. rename_file(*scan);
  613. printf(" Renamed to %s\n",path_name(*walk));
  614. redo = 1;
  615. break;
  616. case '5':
  617. auto_rename(*walk);
  618. printf(" Renamed to %s\n",file_name((*walk)->dir_ent.
  619. name));
  620. break;
  621. case '6':
  622. auto_rename(*scan);
  623. printf(" Renamed to %s\n",file_name((*scan)->dir_ent.
  624. name));
  625. break;
  626. }
  627. }
  628. scan = &(*scan)->next;
  629. }
  630. if (skip) continue;
  631. }
  632. if (!redo) walk = &(*walk)->next;
  633. else {
  634. walk = root;
  635. dot = dotdot = redo = 0;
  636. }
  637. }
  638. if (dots && !dot)
  639. printf("%s\n \".\" is missing. Can't fix this yet.\n",
  640. path_name(parent));
  641. if (dots && !dotdot)
  642. printf("%s\n \"..\" is missing. Can't fix this yet.\n",
  643. path_name(parent));
  644. return 0;
  645. }
  646. /**
  647. * Check a dentry's cluster chain for bad clusters.
  648. * If requested, we verify readability and mark unreadable clusters as bad.
  649. *
  650. * @param[inout] fs Information about the filesystem
  651. * @param[in] file dentry to check
  652. * @param[in] read_test Nonzero == verify that dentry's clusters can
  653. * be read
  654. */
  655. static void test_file(DOS_FS *fs,DOS_FILE *file,int read_test)
  656. {
  657. DOS_FILE *owner;
  658. unsigned long walk,prev,clusters,next_clu;
  659. prev = clusters = 0;
  660. for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
  661. walk = next_clu) {
  662. next_clu = next_cluster(fs,walk);
  663. /* In this stage we are checking only for a loop within our own
  664. * cluster chain.
  665. * Cross-linking of clusters is handled in check_file()
  666. */
  667. if ((owner = get_owner(fs,walk))) {
  668. if (owner == file) {
  669. printf("%s\n Circular cluster chain. Truncating to %lu "
  670. "cluster%s.\n",path_name(file),clusters,clusters == 1 ? "" :
  671. "s");
  672. if (prev) set_fat(fs,prev,-1);
  673. else if (!file->offset)
  674. die( "Bad FAT32 root directory! (bad start cluster)\n" );
  675. else MODIFY_START(file,0,fs);
  676. }
  677. break;
  678. }
  679. if (bad_cluster(fs,walk)) break;
  680. if (read_test) {
  681. if (fs_test(cluster_start(fs,walk),fs->cluster_size)) {
  682. prev = walk;
  683. clusters++;
  684. }
  685. else {
  686. printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n",
  687. path_name(file),clusters,walk);
  688. if (prev) set_fat(fs,prev,next_cluster(fs,walk));
  689. else MODIFY_START(file,next_cluster(fs,walk),fs);
  690. set_fat(fs,walk,-2);
  691. }
  692. }
  693. set_owner(fs,walk,file);
  694. }
  695. /* Revert ownership (for now) */
  696. for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
  697. walk = next_cluster(fs,walk))
  698. if (bad_cluster(fs,walk)) break;
  699. else if (get_owner(fs,walk) == file) set_owner(fs,walk,NULL);
  700. else break;
  701. }
  702. static void undelete(DOS_FS *fs,DOS_FILE *file)
  703. {
  704. unsigned long clusters,left,prev,walk;
  705. clusters = left = (CF_LE_L(file->dir_ent.size)+fs->cluster_size-1)/
  706. fs->cluster_size;
  707. prev = 0;
  708. walk = FSTART(file,fs);
  709. while (left && (walk >= 2) && (walk < fs->clusters+2)) {
  710. FAT_ENTRY curEntry;
  711. get_fat(&curEntry, fs->fat, walk, fs);
  712. if (!curEntry.value)
  713. break;
  714. left--;
  715. if (prev) set_fat(fs,prev,walk);
  716. prev = walk;
  717. walk++;
  718. }
  719. if (prev) set_fat(fs,prev,-1);
  720. else MODIFY_START(file,0,fs);
  721. if (left)
  722. printf("Warning: Did only undelete %lu of %lu cluster%s.\n",clusters-left,
  723. clusters,clusters == 1 ? "" : "s");
  724. }
  725. static void new_dir( void )
  726. {
  727. lfn_reset();
  728. }
  729. /**
  730. * Create a description for a referenced dentry and insert it in our dentry
  731. * tree. Then, go check the dentry's cluster chain for bad clusters and
  732. * cluster loops.
  733. *
  734. * @param[inout] fs Information about the filesystem
  735. * @param[out] chain
  736. * @param[in] parent Information about parent directory of this file
  737. * NULL == no parent ('file' is root directory)
  738. * @param[in] offset Partition-relative byte offset of directory entry of interest
  739. * 0 == Root directory
  740. * @param cp
  741. */
  742. static void add_file(DOS_FS *fs,DOS_FILE ***chain,DOS_FILE *parent,
  743. loff_t offset,FDSC **cp)
  744. {
  745. DOS_FILE *new;
  746. DIR_ENT de;
  747. FD_TYPE type;
  748. if (offset)
  749. fs_read(offset,sizeof(DIR_ENT),&de);
  750. else {
  751. /* Construct a DIR_ENT for the root directory */
  752. memcpy(de.name," ",MSDOS_NAME);
  753. de.attr = ATTR_DIR;
  754. de.size = de.time = de.date = 0;
  755. de.start = CT_LE_W(fs->root_cluster & 0xffff);
  756. de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff);
  757. }
  758. if ((type = file_type(cp,de.name)) != fdt_none) {
  759. if (type == fdt_undelete && (de.attr & ATTR_DIR))
  760. die("Can't undelete directories.");
  761. file_modify(cp,de.name);
  762. fs_write(offset,1,&de);
  763. }
  764. if (IS_FREE(de.name)) {
  765. lfn_check_orphaned();
  766. return;
  767. }
  768. if (de.attr == VFAT_LN_ATTR) {
  769. lfn_add_slot(&de,offset);
  770. return;
  771. }
  772. new = qalloc(&mem_queue,sizeof(DOS_FILE));
  773. new->lfn = lfn_get(&de);
  774. new->offset = offset;
  775. memcpy(&new->dir_ent,&de,sizeof(de));
  776. new->next = new->first = NULL;
  777. new->parent = parent;
  778. if (type == fdt_undelete) undelete(fs,new);
  779. **chain = new;
  780. *chain = &new->next;
  781. if (list) {
  782. printf("Checking file %s",path_name(new));
  783. if (new->lfn)
  784. printf(" (%s)", file_name(new->dir_ent.name) ); /* (8.3) */
  785. printf("\n");
  786. }
  787. /* Don't include root directory, '.', or '..' in the total file count */
  788. if (offset &&
  789. strncmp(de.name,MSDOS_DOT,MSDOS_NAME) != 0 &&
  790. strncmp(de.name,MSDOS_DOTDOT,MSDOS_NAME) != 0)
  791. ++n_files;
  792. test_file(fs,new,test); /* Bad cluster check */
  793. }
  794. static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp);
  795. static int scan_dir(DOS_FS *fs,DOS_FILE *this,FDSC **cp)
  796. {
  797. DOS_FILE **chain;
  798. int i;
  799. unsigned long clu_num;
  800. chain = &this->first;
  801. i = 0;
  802. clu_num = FSTART(this,fs);
  803. new_dir();
  804. while (clu_num > 0 && clu_num != -1) {
  805. add_file(fs,&chain,this,cluster_start(fs,clu_num)+(i % fs->
  806. cluster_size),cp);
  807. i += sizeof(DIR_ENT);
  808. if (!(i % fs->cluster_size))
  809. if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
  810. break;
  811. }
  812. lfn_check_orphaned();
  813. if (check_dir(fs,&this->first,this->offset)) return 0;
  814. if (check_files(fs,this->first)) return 1;
  815. return subdirs(fs,this,cp);
  816. }
  817. /**
  818. * Recursively scan subdirectories of the specified parent directory.
  819. *
  820. * @param[inout] fs Information about the filesystem
  821. * @param[in] parent Identifies the directory to scan
  822. * @param[in] cp
  823. *
  824. * @return 0 Success
  825. * @return 1 Error
  826. */
  827. static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp)
  828. {
  829. DOS_FILE *walk;
  830. for (walk = parent ? parent->first : root; walk; walk = walk->next)
  831. if (walk->dir_ent.attr & ATTR_DIR)
  832. if (strncmp(walk->dir_ent.name,MSDOS_DOT,MSDOS_NAME) &&
  833. strncmp(walk->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME))
  834. if (scan_dir(fs,walk,file_cd(cp,walk->dir_ent.name))) return 1;
  835. return 0;
  836. }
  837. /**
  838. * Scan all directory and file information for errors.
  839. *
  840. * @param[inout] fs Information about the filesystem
  841. *
  842. * @return 0 Success
  843. * @return 1 Error
  844. */
  845. int scan_root(DOS_FS *fs)
  846. {
  847. DOS_FILE **chain;
  848. int i;
  849. root = NULL;
  850. chain = &root;
  851. new_dir();
  852. if (fs->root_cluster) {
  853. add_file(fs,&chain,NULL,0,&fp_root);
  854. }
  855. else {
  856. for (i = 0; i < fs->root_entries; i++)
  857. add_file(fs,&chain,NULL,fs->root_start+i*sizeof(DIR_ENT),&fp_root);
  858. }
  859. lfn_check_orphaned();
  860. (void) check_dir(fs,&root,0);
  861. if (check_files(fs,root)) return 1;
  862. return subdirs(fs,NULL,&fp_root);
  863. }
  864. /* Local Variables: */
  865. /* tab-width: 8 */
  866. /* End: */