blob: 5a0dfb0f2631601ed18f22ee50ec5466cdd73ddd [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/* fat.c - Read/write access to the FAT
2
3 Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4 Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 On Debian systems, the complete text of the GNU General Public License
20 can be found in /usr/share/common-licenses/GPL-3 file.
21*/
22
23/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
24 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30
31#include "common.h"
32#include "dosfsck.h"
33#include "io.h"
34#include "check.h"
35#include "fat.h"
36
37/**
38 * Fetch the FAT entry for a specified cluster.
39 *
40 * @param[out] entry Cluster to which cluster of interest is linked
41 * @param[in] fat FAT table for the partition
42 * @param[in] cluster Cluster of interest
43 * @param[in] fs Information from the FAT boot sectors (bits per FAT entry)
44 */
45void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs)
46{
47 unsigned char *ptr;
48
49 switch (fs->fat_bits) {
50 case 12:
51 ptr = &((unsigned char *)fat)[cluster * 3 / 2];
52 entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) :
53 (ptr[0] | ptr[1] << 8));
54 break;
55 case 16:
56 entry->value = CF_LE_W(((unsigned short *)fat)[cluster]);
57 break;
58 case 32:
59 /* According to M$, the high 4 bits of a FAT32 entry are reserved and
60 * are not part of the cluster number. So we cut them off. */
61 {
62 unsigned long e = CF_LE_L(((unsigned int *)fat)[cluster]);
63 entry->value = e & 0xfffffff;
64 entry->reserved = e >> 28;
65 }
66 break;
67 default:
68 die("Bad FAT entry size: %d bits.", fs->fat_bits);
69 }
70}
71
72/**
73 * Build a bookkeeping structure from the partition's FAT table.
74 * If the partition has multiple FATs and they don't agree, try to pick a winner,
75 * and queue a command to overwrite the loser.
76 * One error that is fixed here is a cluster that links to something out of range.
77 *
78 * @param[inout] fs Information about the filesystem
79 */
80void read_fat(DOS_FS * fs)
81{
82 int eff_size;
83 unsigned long i;
84 void *first, *second = NULL;
85 int first_ok, second_ok;
86 unsigned long total_num_clusters;
87
88 /* Clean up from previous pass */
89 free(fs->fat);
90 free(fs->cluster_owner);
91 fs->fat = NULL;
92 fs->cluster_owner = NULL;
93
94 total_num_clusters = fs->clusters + 2UL;
95 eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL;
96 first = alloc(eff_size);
97 fs_read(fs->fat_start, eff_size, first);
98 if (fs->nfats > 1) {
99 second = alloc(eff_size);
100 fs_read(fs->fat_start + fs->fat_size, eff_size, second);
101 }
102 if (second && memcmp(first, second, eff_size) != 0) {
103 FAT_ENTRY first_media, second_media;
104 get_fat(&first_media, first, 0, fs);
105 get_fat(&second_media, second, 0, fs);
106 first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
107 second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
108 if (first_ok && !second_ok) {
109 printf("FATs differ - using first FAT.\n");
110 fs_write(fs->fat_start + fs->fat_size, eff_size, first);
111 }
112 if (!first_ok && second_ok) {
113 printf("FATs differ - using second FAT.\n");
114 fs_write(fs->fat_start, eff_size, second);
115 memcpy(first, second, eff_size);
116 }
117 if (first_ok && second_ok) {
118 if (interactive) {
119 printf("FATs differ but appear to be intact. Use which FAT ?\n"
120 "1) Use first FAT\n2) Use second FAT\n");
121 if (get_key("12", "?") == '1') {
122 fs_write(fs->fat_start + fs->fat_size, eff_size, first);
123 } else {
124 fs_write(fs->fat_start, eff_size, second);
125 memcpy(first, second, eff_size);
126 }
127 } else {
128 printf("FATs differ but appear to be intact. Using first "
129 "FAT.\n");
130 fs_write(fs->fat_start + fs->fat_size, eff_size, first);
131 }
132 }
133 if (!first_ok && !second_ok) {
134 printf("Both FATs appear to be corrupt. Giving up.\n");
135 exit(1);
136 }
137 }
138 if (second) {
139 free(second);
140 }
141 fs->fat = (unsigned char *)first;
142
143 fs->cluster_owner = alloc(total_num_clusters * sizeof(DOS_FILE *));
144 memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *)));
145
146 /* Truncate any cluster chains that link to something out of range */
147 for (i = 2; i < fs->clusters + 2; i++) {
148 FAT_ENTRY curEntry;
149 get_fat(&curEntry, fs->fat, i, fs);
150 if (curEntry.value == 1) {
151 printf("Cluster %ld out of range (1). Setting to EOF.\n", i - 2);
152 set_fat(fs, i, -1);
153 }
154 if (curEntry.value >= fs->clusters + 2 &&
155 (curEntry.value < FAT_MIN_BAD(fs))) {
156 printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n",
157 i - 2, curEntry.value, fs->clusters + 2 - 1);
158 set_fat(fs, i, -1);
159 }
160 }
161}
162
163/**
164 * Update the FAT entry for a specified cluster
165 * (i.e., change the cluster it links to).
166 * Queue a command to write out this change.
167 *
168 * @param[in,out] fs Information about the filesystem
169 * @param[in] cluster Cluster to change
170 * @param[in] new Cluster to link to
171 * Special values:
172 * 0 == free cluster
173 * -1 == end-of-chain
174 * -2 == bad cluster
175 */
176void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new)
177{
178 unsigned char *data = NULL;
179 int size;
180 loff_t offs;
181
182 if ((long)new == -1)
183 new = FAT_EOF(fs);
184 else if ((long)new == -2)
185 new = FAT_BAD(fs);
186 switch (fs->fat_bits) {
187 case 12:
188 data = fs->fat + cluster * 3 / 2;
189 offs = fs->fat_start + cluster * 3 / 2;
190 if (cluster & 1) {
191 FAT_ENTRY prevEntry;
192 get_fat(&prevEntry, fs->fat, cluster - 1, fs);
193 data[0] = ((new & 0xf) << 4) | (prevEntry.value >> 8);
194 data[1] = new >> 4;
195 } else {
196 FAT_ENTRY subseqEntry;
197 get_fat(&subseqEntry, fs->fat, cluster + 1, fs);
198 data[0] = new & 0xff;
199 data[1] = (new >> 8) | (cluster == fs->clusters - 1 ? 0 :
200 (0xff & subseqEntry.value) << 4);
201 }
202 size = 2;
203 break;
204 case 16:
205 data = fs->fat + cluster * 2;
206 offs = fs->fat_start + cluster * 2;
207 *(unsigned short *)data = CT_LE_W(new);
208 size = 2;
209 break;
210 case 32:
211 {
212 FAT_ENTRY curEntry;
213 get_fat(&curEntry, fs->fat, cluster, fs);
214
215 data = fs->fat + cluster * 4;
216 offs = fs->fat_start + cluster * 4;
217 /* According to M$, the high 4 bits of a FAT32 entry are reserved and
218 * are not part of the cluster number. So we never touch them. */
219 *(unsigned long *)data = CT_LE_L((new & 0xfffffff) |
220 (curEntry.reserved << 28));
221 size = 4;
222 }
223 break;
224 default:
225 die("Bad FAT entry size: %d bits.", fs->fat_bits);
226 }
227 fs_write(offs, size, data);
228 if (fs->nfats > 1) {
229 fs_write(offs + fs->fat_size, size, data);
230 }
231}
232
233int bad_cluster(DOS_FS * fs, unsigned long cluster)
234{
235 FAT_ENTRY curEntry;
236 get_fat(&curEntry, fs->fat, cluster, fs);
237
238 return FAT_IS_BAD(fs, curEntry.value);
239}
240
241/**
242 * Get the cluster to which the specified cluster is linked.
243 * If the linked cluster is marked bad, abort.
244 *
245 * @param[in] fs Information about the filesystem
246 * @param[in] cluster Cluster to follow
247 *
248 * @return -1 'cluster' is at the end of the chain
249 * @return Other values Next cluster in this chain
250 */
251unsigned long next_cluster(DOS_FS * fs, unsigned long cluster)
252{
253 unsigned long value;
254 FAT_ENTRY curEntry;
255
256 get_fat(&curEntry, fs->fat, cluster, fs);
257
258 value = curEntry.value;
259 if (FAT_IS_BAD(fs, value))
260 die("Internal error: next_cluster on bad cluster");
261 return FAT_IS_EOF(fs, value) ? -1 : value;
262}
263
264loff_t cluster_start(DOS_FS * fs, unsigned long cluster)
265{
266 return fs->data_start + ((loff_t) cluster -
267 2) * (unsigned long long)fs->cluster_size;
268}
269
270/**
271 * Update internal bookkeeping to show that the specified cluster belongs
272 * to the specified dentry.
273 *
274 * @param[in,out] fs Information about the filesystem
275 * @param[in] cluster Cluster being assigned
276 * @param[in] owner Information on dentry that owns this cluster
277 * (may be NULL)
278 */
279void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner)
280{
281 if (fs->cluster_owner == NULL)
282 die("Internal error: attempt to set owner in non-existent table");
283
284 if (owner && fs->cluster_owner[cluster]
285 && (fs->cluster_owner[cluster] != owner))
286 die("Internal error: attempt to change file owner");
287 fs->cluster_owner[cluster] = owner;
288}
289
290DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster)
291{
292 if (fs->cluster_owner == NULL)
293 return NULL;
294 else
295 return fs->cluster_owner[cluster];
296}
297
298void fix_bad(DOS_FS * fs)
299{
300 unsigned long i;
301
302 if (verbose)
303 printf("Checking for bad clusters.\n");
304 for (i = 2; i < fs->clusters + 2; i++) {
305 FAT_ENTRY curEntry;
306 get_fat(&curEntry, fs->fat, i, fs);
307
308 if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value))
309 if (!fs_test(cluster_start(fs, i), fs->cluster_size)) {
310 printf("Cluster %lu is unreadable.\n", i);
311 set_fat(fs, i, -2);
312 }
313 }
314}
315
316void reclaim_free(DOS_FS * fs)
317{
318 int reclaimed;
319 unsigned long i;
320
321 if (verbose)
322 printf("Checking for unused clusters.\n");
323 reclaimed = 0;
324 for (i = 2; i < fs->clusters + 2; i++) {
325 FAT_ENTRY curEntry;
326 get_fat(&curEntry, fs->fat, i, fs);
327
328 if (!get_owner(fs, i) && curEntry.value &&
329 !FAT_IS_BAD(fs, curEntry.value)) {
330 set_fat(fs, i, 0);
331 reclaimed++;
332 }
333 }
334 if (reclaimed)
335 printf("Reclaimed %d unused cluster%s (%llu bytes).\n", reclaimed,
336 reclaimed == 1 ? "" : "s",
337 (unsigned long long)reclaimed * fs->cluster_size);
338}
339
340/**
341 * Assign the specified owner to all orphan chains (except cycles).
342 * Break cross-links between orphan chains.
343 *
344 * @param[in,out] fs Information about the filesystem
345 * @param[in] owner dentry to be assigned ownership of orphans
346 * @param[in,out] num_refs For each orphan cluster [index], how many
347 * clusters link to it.
348 * @param[in] start_cluster Where to start scanning for orphans
349 */
350static void tag_free(DOS_FS * fs, DOS_FILE * owner, unsigned long *num_refs,
351 unsigned long start_cluster)
352{
353 int prev;
354 unsigned long i, walk;
355
356 if (start_cluster == 0)
357 start_cluster = 2;
358
359 for (i = start_cluster; i < fs->clusters + 2; i++) {
360 FAT_ENTRY curEntry;
361 get_fat(&curEntry, fs->fat, i, fs);
362
363 /* If the current entry is the head of an un-owned chain... */
364 if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
365 !get_owner(fs, i) && !num_refs[i]) {
366 prev = 0;
367 /* Walk the chain, claiming ownership as we go */
368 for (walk = i; walk != -1; walk = next_cluster(fs, walk)) {
369 if (!get_owner(fs, walk)) {
370 set_owner(fs, walk, owner);
371 } else {
372 /* We've run into cross-links between orphaned chains,
373 * or a cycle with a tail.
374 * Terminate this orphan chain (break the link)
375 */
376 set_fat(fs, prev, -1);
377
378 /* This is not necessary because 'walk' is owned and thus
379 * will never become the head of a chain (the only case
380 * that would matter during reclaim to files).
381 * It's easier to decrement than to prove that it's
382 * unnecessary.
383 */
384 num_refs[walk]--;
385 break;
386 }
387 prev = walk;
388 }
389 }
390 }
391}
392
393/**
394 * Recover orphan chains to files, handling any cycles or cross-links.
395 *
396 * @param[in,out] fs Information about the filesystem
397 */
398void reclaim_file(DOS_FS * fs)
399{
400 DOS_FILE orphan;
401 int reclaimed, files;
402 int changed = 0;
403 unsigned long i, next, walk;
404 unsigned long *num_refs = NULL; /* Only for orphaned clusters */
405 unsigned long total_num_clusters;
406
407 if (verbose)
408 printf("Reclaiming unconnected clusters.\n");
409
410 total_num_clusters = fs->clusters + 2UL;
411 num_refs = alloc(total_num_clusters * sizeof(unsigned long));
412 memset(num_refs, 0, (total_num_clusters * sizeof(unsigned long)));
413
414 /* Guarantee that all orphan chains (except cycles) end cleanly
415 * with an end-of-chain mark.
416 */
417
418 for (i = 2; i < total_num_clusters; i++) {
419 FAT_ENTRY curEntry;
420 get_fat(&curEntry, fs->fat, i, fs);
421
422 next = curEntry.value;
423 if (!get_owner(fs, i) && next && next < fs->clusters + 2) {
424 /* Cluster is linked, but not owned (orphan) */
425 FAT_ENTRY nextEntry;
426 get_fat(&nextEntry, fs->fat, next, fs);
427
428 /* Mark it end-of-chain if it links into an owned cluster,
429 * a free cluster, or a bad cluster.
430 */
431 if (get_owner(fs, next) || !nextEntry.value ||
432 FAT_IS_BAD(fs, nextEntry.value))
433 set_fat(fs, i, -1);
434 else
435 num_refs[next]++;
436 }
437 }
438
439 /* Scan until all the orphans are accounted for,
440 * and all cycles and cross-links are broken
441 */
442 do {
443 tag_free(fs, &orphan, num_refs, changed);
444 changed = 0;
445
446 /* Any unaccounted-for orphans must be part of a cycle */
447 for (i = 2; i < total_num_clusters; i++) {
448 FAT_ENTRY curEntry;
449 get_fat(&curEntry, fs->fat, i, fs);
450
451 if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
452 !get_owner(fs, i)) {
453 if (!num_refs[curEntry.value]--)
454 die("Internal error: num_refs going below zero");
455 set_fat(fs, i, -1);
456 changed = curEntry.value;
457 printf("Broke cycle at cluster %lu in free chain.\n", i);
458
459 /* If we've created a new chain head,
460 * tag_free() can claim it
461 */
462 if (num_refs[curEntry.value] == 0)
463 break;
464 }
465 }
466 }
467 while (changed);
468
469 /* Now we can start recovery */
470 files = reclaimed = 0;
471 for (i = 2; i < total_num_clusters; i++)
472 /* If this cluster is the head of an orphan chain... */
473 if (get_owner(fs, i) == &orphan && !num_refs[i]) {
474 DIR_ENT de;
475 loff_t offset;
476 files++;
477 offset = alloc_rootdir_entry(fs, &de, "FSCK%04d");
478 de.start = CT_LE_W(i & 0xffff);
479 if (fs->fat_bits == 32)
480 de.starthi = CT_LE_W(i >> 16);
481 for (walk = i; walk > 0 && walk != -1;
482 walk = next_cluster(fs, walk)) {
483 de.size = CT_LE_L(CF_LE_L(de.size) + fs->cluster_size);
484 reclaimed++;
485 }
486 fs_write(offset, sizeof(DIR_ENT), &de);
487 }
488 if (reclaimed)
489 printf("Reclaimed %d unused cluster%s (%llu bytes) in %d chain%s.\n",
490 reclaimed, reclaimed == 1 ? "" : "s",
491 (unsigned long long)reclaimed * fs->cluster_size, files,
492 files == 1 ? "" : "s");
493
494 free(num_refs);
495}
496
497unsigned long update_free(DOS_FS * fs)
498{
499 unsigned long i;
500 unsigned long free = 0;
501 int do_set = 0;
502
503 for (i = 2; i < fs->clusters + 2; i++) {
504 FAT_ENTRY curEntry;
505 get_fat(&curEntry, fs->fat, i, fs);
506
507 if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value))
508 ++free;
509 }
510
511 if (!fs->fsinfo_start)
512 return free;
513
514 if (verbose)
515 printf("Checking free cluster summary.\n");
516 if (fs->free_clusters != 0xFFFFFFFF) {
517 if (free != fs->free_clusters) {
518 printf("Free cluster summary wrong (%ld vs. really %ld)\n",
519 fs->free_clusters, free);
520 if (interactive)
521 printf("1) Correct\n2) Don't correct\n");
522 else
523 printf(" Auto-correcting.\n");
524 if (!interactive || get_key("12", "?") == '1')
525 do_set = 1;
526 }
527 } else {
528 printf("Free cluster summary uninitialized (should be %ld)\n", free);
529 if (rw) {
530 if (interactive)
531 printf("1) Set it\n2) Leave it uninitialized\n");
532 else
533 printf(" Auto-setting.\n");
534 if (!interactive || get_key("12", "?") == '1')
535 do_set = 1;
536 }
537 }
538
539 if (do_set) {
540 unsigned long le_free = CT_LE_L(free);
541 fs->free_clusters = free;
542 fs_write(fs->fsinfo_start + offsetof(struct info_sector, free_clusters),
543 sizeof(le_free), &le_free);
544 }
545
546 return free;
547}