check.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  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, 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((char*)de->name, pattern, curr_num);
  159. for (scan = 0; scan < fs->root_entries; scan++)
  160. if (scan != next_free &&
  161. !strncmp((const char*)root[scan].name, (const char*)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((const char*)name, "EA DATA SF", 11) == 0 ||
  237. strncmp((const char*)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. DIR_ENT empty;
  289. /* New dir entry is zeroed except first byte, which is set to 0xe5.
  290. * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
  291. * a directory at the first zero entry...
  292. */
  293. memset(&empty, 0, sizeof(empty));
  294. empty.name[0] = DELETED_FLAG;
  295. for (; from < to; from += sizeof(empty)) {
  296. fs_write(from, sizeof(DIR_ENT), &empty);
  297. }
  298. }
  299. static void drop_file(DOS_FS * fs, DOS_FILE * file)
  300. {
  301. unsigned long cluster;
  302. MODIFY(file, name[0], DELETED_FLAG);
  303. if (file->lfn)
  304. lfn_remove(file->lfn_offset, file->offset);
  305. for (cluster = FSTART(file, fs); cluster > 0 && cluster <
  306. fs->clusters + 2; cluster = next_cluster(fs, cluster))
  307. set_owner(fs, cluster, NULL);
  308. --n_files;
  309. }
  310. static void truncate_file(DOS_FS * fs, DOS_FILE * file, unsigned long clusters)
  311. {
  312. int deleting;
  313. unsigned long walk, next, prev;
  314. walk = FSTART(file, fs);
  315. prev = 0;
  316. if ((deleting = !clusters))
  317. MODIFY_START(file, 0, fs);
  318. while (walk > 0 && walk != -1) {
  319. next = next_cluster(fs, walk);
  320. if (deleting)
  321. set_fat(fs, walk, 0);
  322. else if ((deleting = !--clusters))
  323. set_fat(fs, walk, -1);
  324. prev = walk;
  325. walk = next;
  326. }
  327. }
  328. static void auto_rename(DOS_FILE * file)
  329. {
  330. DOS_FILE *first, *walk;
  331. unsigned long int number;
  332. if (!file->offset)
  333. return; /* cannot rename FAT32 root dir */
  334. first = file->parent ? file->parent->first : root;
  335. number = 0;
  336. while (1) {
  337. char num[8];
  338. sprintf(num, "%07lu", number);
  339. memcpy(file->dir_ent.name, "FSCK", 4);
  340. memcpy(file->dir_ent.name + 4, num, 4);
  341. memcpy(file->dir_ent.ext, num + 4, 3);
  342. for (walk = first; walk; walk = walk->next)
  343. if (walk != file
  344. && !strncmp((const char*)walk->dir_ent.name, (const char*)file->dir_ent.name, MSDOS_NAME))
  345. break;
  346. if (!walk) {
  347. fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
  348. if (file->lfn)
  349. lfn_fix_checksum(file->lfn_offset, file->offset,
  350. (const char*)file->dir_ent.name);
  351. return;
  352. }
  353. number++;
  354. if (number > 9999999) {
  355. die("Too many files need repair.");
  356. }
  357. }
  358. die("Can't generate a unique name.");
  359. }
  360. static void rename_file(DOS_FILE * file)
  361. {
  362. unsigned char name[46];
  363. unsigned char *walk, *here;
  364. if (!file->offset) {
  365. printf("Cannot rename FAT32 root dir\n");
  366. return; /* cannot rename FAT32 root dir */
  367. }
  368. while (1) {
  369. printf("New name: ");
  370. fflush(stdout);
  371. if (fgets((char*)name, 45, stdin)) {
  372. if ((here = (unsigned char*)strchr((const char*)name, '\n')))
  373. *here = 0;
  374. for (walk = (unsigned char*)strrchr((const char*)name, 0); walk >= name && (*walk == ' ' ||
  375. *walk == '\t');
  376. walk--) ;
  377. walk[1] = 0;
  378. for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
  379. if (file_cvt(walk, file->dir_ent.name)) {
  380. fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
  381. if (file->lfn)
  382. lfn_fix_checksum(file->lfn_offset, file->offset,
  383. (const char*)file->dir_ent.name);
  384. return;
  385. }
  386. }
  387. }
  388. }
  389. static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots)
  390. {
  391. char *name;
  392. name = strncmp((const char*)file->dir_ent.name, MSDOS_DOT, MSDOS_NAME) ? ".." : ".";
  393. if (!(file->dir_ent.attr & ATTR_DIR)) {
  394. printf("%s\n Is a non-directory.\n", path_name(file));
  395. if (interactive)
  396. printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
  397. "4) Convert to directory\n");
  398. else
  399. printf(" Auto-renaming it.\n");
  400. switch (interactive ? get_key("1234", "?") : '2') {
  401. case '1':
  402. drop_file(fs, file);
  403. return 1;
  404. case '2':
  405. auto_rename(file);
  406. printf(" Renamed to %s\n", file_name(file->dir_ent.name));
  407. return 0;
  408. case '3':
  409. rename_file(file);
  410. return 0;
  411. case '4':
  412. MODIFY(file, size, CT_LE_L(0));
  413. MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR);
  414. break;
  415. }
  416. }
  417. if (!dots) {
  418. printf("Root contains directory \"%s\". Dropping it.\n", name);
  419. drop_file(fs, file);
  420. return 1;
  421. }
  422. return 0;
  423. }
  424. static int check_file(DOS_FS * fs, DOS_FILE * file)
  425. {
  426. DOS_FILE *owner;
  427. int restart;
  428. unsigned long expect, curr, this, clusters, prev, walk, clusters2;
  429. if (file->dir_ent.attr & ATTR_DIR) {
  430. if (CF_LE_L(file->dir_ent.size)) {
  431. printf("%s\n Directory has non-zero size. Fixing it.\n",
  432. path_name(file));
  433. MODIFY(file, size, CT_LE_L(0));
  434. }
  435. if (file->parent && !strncmp((const char*)file->dir_ent.name, MSDOS_DOT, MSDOS_NAME)) {
  436. expect = FSTART(file->parent, fs);
  437. if (FSTART(file, fs) != expect) {
  438. printf("%s\n Start (%ld) does not point to parent (%ld)\n",
  439. path_name(file), FSTART(file, fs), expect);
  440. MODIFY_START(file, expect, fs);
  441. }
  442. return 0;
  443. }
  444. if (file->parent && !strncmp((const char*)file->dir_ent.name, MSDOS_DOTDOT,
  445. MSDOS_NAME)) {
  446. expect =
  447. file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
  448. if (fs->root_cluster && expect == fs->root_cluster)
  449. expect = 0;
  450. if (FSTART(file, fs) != expect) {
  451. printf("%s\n Start (%lu) does not point to .. (%lu)\n",
  452. path_name(file), FSTART(file, fs), expect);
  453. MODIFY_START(file, expect, fs);
  454. }
  455. return 0;
  456. }
  457. if (FSTART(file, fs) == 0) {
  458. printf("%s\n Start does point to root directory. Deleting dir. \n",
  459. path_name(file));
  460. MODIFY(file, name[0], DELETED_FLAG);
  461. return 0;
  462. }
  463. }
  464. if (FSTART(file, fs) >= fs->clusters + 2) {
  465. printf
  466. ("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n",
  467. path_name(file), FSTART(file, fs), fs->clusters + 1);
  468. if (!file->offset)
  469. die("Bad FAT32 root directory! (bad start cluster)\n");
  470. MODIFY_START(file, 0, fs);
  471. }
  472. clusters = prev = 0;
  473. for (curr = FSTART(file, fs) ? FSTART(file, fs) :
  474. -1; curr != -1; curr = next_cluster(fs, curr)) {
  475. FAT_ENTRY curEntry;
  476. get_fat(&curEntry, fs->fat, curr, fs);
  477. if (!curEntry.value || bad_cluster(fs, curr)) {
  478. printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n",
  479. path_name(file), curEntry.value ? "bad" : "free", curr);
  480. if (prev)
  481. set_fat(fs, prev, -1);
  482. else if (!file->offset)
  483. die("FAT32 root dir starts with a bad cluster!");
  484. else
  485. MODIFY_START(file, 0, fs);
  486. break;
  487. }
  488. if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
  489. (unsigned long long)clusters * fs->cluster_size) {
  490. printf
  491. ("%s\n File size is %u bytes, cluster chain length is > %llu "
  492. "bytes.\n Truncating file to %u bytes.\n", path_name(file),
  493. CF_LE_L(file->dir_ent.size),
  494. (unsigned long long)clusters * fs->cluster_size,
  495. CF_LE_L(file->dir_ent.size));
  496. truncate_file(fs, file, clusters);
  497. break;
  498. }
  499. if ((owner = get_owner(fs, curr))) {
  500. int do_trunc = 0;
  501. printf("%s and\n", path_name(owner));
  502. printf("%s\n share clusters.\n", path_name(file));
  503. clusters2 = 0;
  504. for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
  505. next_cluster(fs, walk))
  506. if (walk == curr)
  507. break;
  508. else
  509. clusters2++;
  510. restart = file->dir_ent.attr & ATTR_DIR;
  511. if (!owner->offset) {
  512. printf(" Truncating second to %llu bytes because first "
  513. "is FAT32 root dir.\n",
  514. (unsigned long long)clusters2 * fs->cluster_size);
  515. do_trunc = 2;
  516. } else if (!file->offset) {
  517. printf(" Truncating first to %llu bytes because second "
  518. "is FAT32 root dir.\n",
  519. (unsigned long long)clusters * fs->cluster_size);
  520. do_trunc = 1;
  521. } else if (interactive)
  522. printf("1) Truncate first to %llu bytes%s\n"
  523. "2) Truncate second to %llu bytes\n",
  524. (unsigned long long)clusters * fs->cluster_size,
  525. restart ? " and restart" : "",
  526. (unsigned long long)clusters2 * fs->cluster_size);
  527. else
  528. printf(" Truncating second to %llu bytes.\n",
  529. (unsigned long long)clusters2 * fs->cluster_size);
  530. if (do_trunc != 2
  531. && (do_trunc == 1
  532. || (interactive && get_key("12", "?") == '1'))) {
  533. prev = 0;
  534. clusters = 0;
  535. for (this = FSTART(owner, fs); this > 0 && this != -1; this =
  536. next_cluster(fs, this)) {
  537. if (this == curr) {
  538. if (prev)
  539. set_fat(fs, prev, -1);
  540. else
  541. MODIFY_START(owner, 0, fs);
  542. MODIFY(owner, size,
  543. CT_LE_L((unsigned long long)clusters *
  544. fs->cluster_size));
  545. if (restart)
  546. return 1;
  547. while (this > 0 && this != -1) {
  548. set_owner(fs, this, NULL);
  549. this = next_cluster(fs, this);
  550. }
  551. this = curr;
  552. break;
  553. }
  554. clusters++;
  555. prev = this;
  556. }
  557. if (this != curr)
  558. die("Internal error: didn't find cluster %d in chain"
  559. " starting at %d", curr, FSTART(owner, fs));
  560. } else {
  561. if (prev)
  562. set_fat(fs, prev, -1);
  563. else
  564. MODIFY_START(file, 0, fs);
  565. break;
  566. }
  567. }
  568. set_owner(fs, curr, file);
  569. clusters++;
  570. prev = curr;
  571. }
  572. if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
  573. (unsigned long long)clusters * fs->cluster_size) {
  574. printf
  575. ("%s\n File size is %u bytes, cluster chain length is %llu bytes."
  576. "\n Truncating file to %llu bytes.\n", path_name(file),
  577. CF_LE_L(file->dir_ent.size),
  578. (unsigned long long)clusters * fs->cluster_size,
  579. (unsigned long long)clusters * fs->cluster_size);
  580. MODIFY(file, size,
  581. CT_LE_L((unsigned long long)clusters * fs->cluster_size));
  582. }
  583. return 0;
  584. }
  585. static int check_files(DOS_FS * fs, DOS_FILE * start)
  586. {
  587. while (start) {
  588. if (check_file(fs, start))
  589. return 1;
  590. start = start->next;
  591. }
  592. return 0;
  593. }
  594. static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
  595. {
  596. DOS_FILE *parent, **walk, **scan;
  597. int dot, dotdot, skip, redo;
  598. int good, bad;
  599. if (!*root)
  600. return 0;
  601. parent = (*root)->parent;
  602. good = bad = 0;
  603. for (walk = root; *walk; walk = &(*walk)->next)
  604. if (bad_name(*walk))
  605. bad++;
  606. else
  607. good++;
  608. if (*root && parent && good + bad > 4 && bad > good / 2) {
  609. printf("%s\n Has a large number of bad entries. (%d/%d)\n",
  610. path_name(parent), bad, good + bad);
  611. if (!dots)
  612. printf(" Not dropping root directory.\n");
  613. else if (!interactive)
  614. printf(" Not dropping it in auto-mode.\n");
  615. else if (get_key("yn", "Drop directory ? (y/n)") == 'y') {
  616. truncate_file(fs, parent, 0);
  617. MODIFY(parent, name[0], DELETED_FLAG);
  618. /* buglet: deleted directory stays in the list. */
  619. return 1;
  620. }
  621. }
  622. dot = dotdot = redo = 0;
  623. walk = root;
  624. while (*walk) {
  625. if (!strncmp((const char*)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME) ||
  626. !strncmp((const char*)((*walk)->dir_ent.name), MSDOS_DOTDOT, MSDOS_NAME)) {
  627. if (handle_dot(fs, *walk, dots)) {
  628. *walk = (*walk)->next;
  629. continue;
  630. }
  631. if (!strncmp((const char*)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME))
  632. dot++;
  633. else
  634. dotdot++;
  635. }
  636. if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
  637. puts(path_name(*walk));
  638. printf(" Bad short file name (%s).\n",
  639. file_name((*walk)->dir_ent.name));
  640. if (interactive)
  641. printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
  642. "4) Keep it\n");
  643. else
  644. printf(" Auto-renaming it.\n");
  645. switch (interactive ? get_key("1234", "?") : '3') {
  646. case '1':
  647. drop_file(fs, *walk);
  648. walk = &(*walk)->next;
  649. continue;
  650. case '2':
  651. rename_file(*walk);
  652. redo = 1;
  653. break;
  654. case '3':
  655. auto_rename(*walk);
  656. printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name));
  657. break;
  658. case '4':
  659. break;
  660. }
  661. }
  662. /* don't check for duplicates of the volume label */
  663. if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
  664. scan = &(*walk)->next;
  665. skip = 0;
  666. while (*scan && !skip) {
  667. if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
  668. !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
  669. MSDOS_NAME)) {
  670. printf("%s\n Duplicate directory entry.\n First %s\n",
  671. path_name(*walk), file_stat(*walk));
  672. printf(" Second %s\n", file_stat(*scan));
  673. if (interactive)
  674. printf
  675. ("1) Drop first\n2) Drop second\n3) Rename first\n"
  676. "4) Rename second\n5) Auto-rename first\n"
  677. "6) Auto-rename second\n");
  678. else
  679. printf(" Auto-renaming second.\n");
  680. switch (interactive ? get_key("123456", "?") : '6') {
  681. case '1':
  682. drop_file(fs, *walk);
  683. *walk = (*walk)->next;
  684. skip = 1;
  685. break;
  686. case '2':
  687. drop_file(fs, *scan);
  688. *scan = (*scan)->next;
  689. continue;
  690. case '3':
  691. rename_file(*walk);
  692. printf(" Renamed to %s\n", path_name(*walk));
  693. redo = 1;
  694. break;
  695. case '4':
  696. rename_file(*scan);
  697. printf(" Renamed to %s\n", path_name(*walk));
  698. redo = 1;
  699. break;
  700. case '5':
  701. auto_rename(*walk);
  702. printf(" Renamed to %s\n",
  703. file_name((*walk)->dir_ent.name));
  704. break;
  705. case '6':
  706. auto_rename(*scan);
  707. printf(" Renamed to %s\n",
  708. file_name((*scan)->dir_ent.name));
  709. break;
  710. }
  711. }
  712. scan = &(*scan)->next;
  713. }
  714. if (skip)
  715. continue;
  716. }
  717. if (!redo)
  718. walk = &(*walk)->next;
  719. else {
  720. walk = root;
  721. dot = dotdot = redo = 0;
  722. }
  723. }
  724. if (dots && !dot)
  725. printf("%s\n \".\" is missing. Can't fix this yet.\n",
  726. path_name(parent));
  727. if (dots && !dotdot)
  728. printf("%s\n \"..\" is missing. Can't fix this yet.\n",
  729. path_name(parent));
  730. return 0;
  731. }
  732. /**
  733. * Check a dentry's cluster chain for bad clusters.
  734. * If requested, we verify readability and mark unreadable clusters as bad.
  735. *
  736. * @param[inout] fs Information about the filesystem
  737. * @param[in] file dentry to check
  738. * @param[in] read_test Nonzero == verify that dentry's clusters can
  739. * be read
  740. */
  741. static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
  742. {
  743. DOS_FILE *owner;
  744. unsigned long walk, prev, clusters, next_clu;
  745. prev = clusters = 0;
  746. for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2;
  747. walk = next_clu) {
  748. next_clu = next_cluster(fs, walk);
  749. /* In this stage we are checking only for a loop within our own
  750. * cluster chain.
  751. * Cross-linking of clusters is handled in check_file()
  752. */
  753. if ((owner = get_owner(fs, walk))) {
  754. if (owner == file) {
  755. printf("%s\n Circular cluster chain. Truncating to %lu "
  756. "cluster%s.\n", path_name(file), clusters,
  757. clusters == 1 ? "" : "s");
  758. if (prev)
  759. set_fat(fs, prev, -1);
  760. else if (!file->offset)
  761. die("Bad FAT32 root directory! (bad start cluster)\n");
  762. else
  763. MODIFY_START(file, 0, fs);
  764. }
  765. break;
  766. }
  767. if (bad_cluster(fs, walk))
  768. break;
  769. if (read_test) {
  770. if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
  771. prev = walk;
  772. clusters++;
  773. } else {
  774. printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n",
  775. path_name(file), clusters, walk);
  776. if (prev)
  777. set_fat(fs, prev, next_cluster(fs, walk));
  778. else
  779. MODIFY_START(file, next_cluster(fs, walk), fs);
  780. set_fat(fs, walk, -2);
  781. }
  782. }
  783. set_owner(fs, walk, file);
  784. }
  785. /* Revert ownership (for now) */
  786. for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2;
  787. walk = next_cluster(fs, walk))
  788. if (bad_cluster(fs, walk))
  789. break;
  790. else if (get_owner(fs, walk) == file)
  791. set_owner(fs, walk, NULL);
  792. else
  793. break;
  794. }
  795. static void undelete(DOS_FS * fs, DOS_FILE * file)
  796. {
  797. unsigned long clusters, left, prev, walk;
  798. clusters = left = (CF_LE_L(file->dir_ent.size) + fs->cluster_size - 1) /
  799. fs->cluster_size;
  800. prev = 0;
  801. walk = FSTART(file, fs);
  802. while (left && (walk >= 2) && (walk < fs->clusters + 2)) {
  803. FAT_ENTRY curEntry;
  804. get_fat(&curEntry, fs->fat, walk, fs);
  805. if (!curEntry.value)
  806. break;
  807. left--;
  808. if (prev)
  809. set_fat(fs, prev, walk);
  810. prev = walk;
  811. walk++;
  812. }
  813. if (prev)
  814. set_fat(fs, prev, -1);
  815. else
  816. MODIFY_START(file, 0, fs);
  817. if (left)
  818. printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
  819. clusters - left, clusters, clusters == 1 ? "" : "s");
  820. }
  821. static void new_dir(void)
  822. {
  823. lfn_reset();
  824. }
  825. /**
  826. * Create a description for a referenced dentry and insert it in our dentry
  827. * tree. Then, go check the dentry's cluster chain for bad clusters and
  828. * cluster loops.
  829. *
  830. * @param[inout] fs Information about the filesystem
  831. * @param[out] chain
  832. * @param[in] parent Information about parent directory of this file
  833. * NULL == no parent ('file' is root directory)
  834. * @param[in] offset Partition-relative byte offset of directory entry of interest
  835. * 0 == Root directory
  836. * @param cp
  837. */
  838. static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
  839. loff_t offset, FDSC ** cp)
  840. {
  841. DOS_FILE *new;
  842. DIR_ENT de;
  843. FD_TYPE type;
  844. if (offset)
  845. fs_read(offset, sizeof(DIR_ENT), &de);
  846. else {
  847. /* Construct a DIR_ENT for the root directory */
  848. memcpy(de.name, " ", MSDOS_NAME);
  849. de.attr = ATTR_DIR;
  850. de.size = de.time = de.date = 0;
  851. de.start = CT_LE_W(fs->root_cluster & 0xffff);
  852. de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff);
  853. }
  854. if ((type = file_type(cp, (char*)de.name)) != fdt_none) {
  855. if (type == fdt_undelete && (de.attr & ATTR_DIR))
  856. die("Can't undelete directories.");
  857. file_modify(cp, (char*)de.name);
  858. fs_write(offset, 1, &de);
  859. }
  860. if (IS_FREE(de.name)) {
  861. lfn_check_orphaned();
  862. return;
  863. }
  864. if (de.attr == VFAT_LN_ATTR) {
  865. lfn_add_slot(&de, offset);
  866. return;
  867. }
  868. new = qalloc(&mem_queue, sizeof(DOS_FILE));
  869. new->lfn = lfn_get(&de, &new->lfn_offset);
  870. new->offset = offset;
  871. memcpy(&new->dir_ent, &de, sizeof(de));
  872. new->next = new->first = NULL;
  873. new->parent = parent;
  874. if (type == fdt_undelete)
  875. undelete(fs, new);
  876. **chain = new;
  877. *chain = &new->next;
  878. if (list) {
  879. printf("Checking file %s", path_name(new));
  880. if (new->lfn)
  881. printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */
  882. printf("\n");
  883. }
  884. /* Don't include root directory, '.', or '..' in the total file count */
  885. if (offset &&
  886. strncmp((const char*)de.name, MSDOS_DOT, MSDOS_NAME) != 0 &&
  887. strncmp((const char*)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0)
  888. ++n_files;
  889. test_file(fs, new, test); /* Bad cluster check */
  890. }
  891. static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
  892. static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
  893. {
  894. DOS_FILE **chain;
  895. int i;
  896. unsigned long clu_num;
  897. chain = &this->first;
  898. i = 0;
  899. clu_num = FSTART(this, fs);
  900. new_dir();
  901. while (clu_num > 0 && clu_num != -1) {
  902. add_file(fs, &chain, this,
  903. cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
  904. i += sizeof(DIR_ENT);
  905. if (!(i % fs->cluster_size))
  906. if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
  907. break;
  908. }
  909. lfn_check_orphaned();
  910. if (check_dir(fs, &this->first, this->offset))
  911. return 0;
  912. if (check_files(fs, this->first))
  913. return 1;
  914. return subdirs(fs, this, cp);
  915. }
  916. /**
  917. * Recursively scan subdirectories of the specified parent directory.
  918. *
  919. * @param[inout] fs Information about the filesystem
  920. * @param[in] parent Identifies the directory to scan
  921. * @param[in] cp
  922. *
  923. * @return 0 Success
  924. * @return 1 Error
  925. */
  926. static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
  927. {
  928. DOS_FILE *walk;
  929. for (walk = parent ? parent->first : root; walk; walk = walk->next)
  930. if (walk->dir_ent.attr & ATTR_DIR)
  931. if (strncmp((const char*)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME) &&
  932. strncmp((const char*)walk->dir_ent.name, MSDOS_DOTDOT, MSDOS_NAME))
  933. if (scan_dir(fs, walk, file_cd(cp, (char*)walk->dir_ent.name)))
  934. return 1;
  935. return 0;
  936. }
  937. /**
  938. * Scan all directory and file information for errors.
  939. *
  940. * @param[inout] fs Information about the filesystem
  941. *
  942. * @return 0 Success
  943. * @return 1 Error
  944. */
  945. int scan_root(DOS_FS * fs)
  946. {
  947. DOS_FILE **chain;
  948. int i;
  949. root = NULL;
  950. chain = &root;
  951. new_dir();
  952. if (fs->root_cluster) {
  953. add_file(fs, &chain, NULL, 0, &fp_root);
  954. } else {
  955. for (i = 0; i < fs->root_entries; i++)
  956. add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
  957. &fp_root);
  958. }
  959. lfn_check_orphaned();
  960. (void)check_dir(fs, &root, 0);
  961. if (check_files(fs, root))
  962. return 1;
  963. return subdirs(fs, NULL, &fp_root);
  964. }