check.c 29 KB

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