check.c 28 KB

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