check.c 29 KB

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