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