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