check.c 29 KB

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