"SfR Fresh" - the SfR Freeware/Shareware Archive 
As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers.
Alternatively you can here view or download the uninterpreted source code file.
That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
1 /* fat.c - Read/write access to the FAT */
2
3 /* Written 1993 by Werner Almesberger */
4
5 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
6 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
7
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include "common.h"
15 #include "dosfsck.h"
16 #include "io.h"
17 #include "check.h"
18 #include "fat.h"
19
20
21 static void get_fat(FAT_ENTRY *entry,void *fat,unsigned long cluster,DOS_FS *fs)
22 {
23 unsigned char *ptr;
24
25 switch(fs->fat_bits) {
26 case 12:
27 ptr = &((unsigned char *) fat)[cluster*3/2];
28 entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) :
29 (ptr[0] | ptr[1] << 8));
30 break;
31 case 16:
32 entry->value = CF_LE_W(((unsigned short *) fat)[cluster]);
33 break;
34 case 32:
35 /* According to M$, the high 4 bits of a FAT32 entry are reserved and
36 * are not part of the cluster number. So we cut them off. */
37 {
38 unsigned long e = CF_LE_L(((unsigned int *) fat)[cluster]);
39 entry->value = e & 0xfffffff;
40 entry->reserved = e >> 28;
41 }
42 break;
43 default:
44 die("Bad FAT entry size: %d bits.",fs->fat_bits);
45 }
46 entry->owner = NULL;
47 }
48
49
50 void read_fat(DOS_FS *fs)
51 {
52 int eff_size;
53 unsigned long i;
54 void *first,*second,*use;
55 int first_ok,second_ok;
56
57 eff_size = ((fs->clusters+2)*fs->fat_bits+7)/8;
58 first = alloc(eff_size);
59 fs_read(fs->fat_start,eff_size,first);
60 use = first;
61 if (fs->nfats > 1) {
62 second = alloc(eff_size);
63 fs_read(fs->fat_start+fs->fat_size,eff_size,second);
64 }
65 else
66 second = NULL;
67 if (second && memcmp(first,second,eff_size) != 0) {
68 FAT_ENTRY first_media, second_media;
69 get_fat(&first_media,first,0,fs);
70 get_fat(&second_media,second,0,fs);
71 first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
72 second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
73 if (first_ok && !second_ok) {
74 printf("FATs differ - using first FAT.\n");
75 fs_write(fs->fat_start+fs->fat_size,eff_size,use = first);
76 }
77 if (!first_ok && second_ok) {
78 printf("FATs differ - using second FAT.\n");
79 fs_write(fs->fat_start,eff_size,use = second);
80 }
81 if (first_ok && second_ok) {
82 if (interactive) {
83 printf("FATs differ but appear to be intact. Use which FAT ?\n"
84 "1) Use first FAT\n2) Use second FAT\n");
85 if (get_key("12","?") == '1')
86 fs_write(fs->fat_start+fs->fat_size,eff_size,use = first);
87 else fs_write(fs->fat_start,eff_size,use = second);
88 }
89 else {
90 printf("FATs differ but appear to be intact. Using first "
91 "FAT.\n");
92 fs_write(fs->fat_start+fs->fat_size,eff_size,use = first);
93 }
94 }
95 if (!first_ok && !second_ok) {
96 printf("Both FATs appear to be corrupt. Giving up.\n");
97 exit(1);
98 }
99 }
100 fs->fat = qalloc(&mem_queue,sizeof(FAT_ENTRY)*(fs->clusters+2));
101 for (i = 2; i < fs->clusters+2; i++) get_fat(&fs->fat[i],use,i,fs);
102 for (i = 2; i < fs->clusters+2; i++)
103 if (fs->fat[i].value >= fs->clusters+2 &&
104 (fs->fat[i].value < FAT_MIN_BAD(fs))) {
105 printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n",
106 i-2,fs->fat[i].value,fs->clusters+2-1);
107 set_fat(fs,i,-1);
108 }
109 free(first);
110 if (second)
111 free(second);
112 }
113
114
115 void set_fat(DOS_FS *fs,unsigned long cluster,unsigned long new)
116 {
117 unsigned char data[4];
118 int size;
119 loff_t offs;
120
121 if ((long)new == -1)
122 new = FAT_EOF(fs);
123 else if ((long)new == -2)
124 new = FAT_BAD(fs);
125 switch( fs->fat_bits ) {
126 case 12:
127 offs = fs->fat_start+cluster*3/2;
128 if (cluster & 1) {
129 data[0] = ((new & 0xf) << 4) | (fs->fat[cluster-1].value >> 8);
130 data[1] = new >> 4;
131 }
132 else {
133 data[0] = new & 0xff;
134 data[1] = (new >> 8) | (cluster == fs->clusters-1 ? 0 :
135 (0xff & fs->fat[cluster+1].value) << 4);
136 }
137 size = 2;
138 break;
139 case 16:
140 offs = fs->fat_start+cluster*2;
141 *(unsigned short *) data = CT_LE_W(new);
142 size = 2;
143 break;
144 case 32:
145 offs = fs->fat_start+cluster*4;
146 /* According to M$, the high 4 bits of a FAT32 entry are reserved and
147 * are not part of the cluster number. So we never touch them. */
148 *(unsigned long *) data = CT_LE_L( (new & 0xfffffff) |
149 (fs->fat[cluster].reserved << 28) );
150 size = 4;
151 break;
152 default:
153 die("Bad FAT entry size: %d bits.",fs->fat_bits);
154 }
155 fs->fat[cluster].value = new;
156 fs_write(offs,size,&data);
157 fs_write(offs+fs->fat_size,size,&data);
158 }
159
160
161 int bad_cluster(DOS_FS *fs,unsigned long cluster)
162 {
163 return FAT_IS_BAD(fs,fs->fat[cluster].value);
164 }
165
166
167 unsigned long next_cluster(DOS_FS *fs,unsigned long cluster)
168 {
169 unsigned long value;
170
171 value = fs->fat[cluster].value;
172 if (FAT_IS_BAD(fs,value))
173 die("Internal error: next_cluster on bad cluster");
174 return FAT_IS_EOF(fs,value) ? -1 : value;
175 }
176
177
178 loff_t cluster_start(DOS_FS *fs,unsigned long cluster)
179 {
180 return fs->data_start+((loff_t)cluster-2)*fs->cluster_size;
181 }
182
183
184 void set_owner(DOS_FS *fs,unsigned long cluster,DOS_FILE *owner)
185 {
186 if (owner && fs->fat[cluster].owner)
187 die("Internal error: attempt to change file owner");
188 fs->fat[cluster].owner = owner;
189 }
190
191
192 DOS_FILE *get_owner(DOS_FS *fs,unsigned long cluster)
193 {
194 return fs->fat[cluster].owner;
195 }
196
197
198 void fix_bad(DOS_FS *fs)
199 {
200 unsigned long i;
201
202 if (verbose)
203 printf("Checking for bad clusters.\n");
204 for (i = 2; i < fs->clusters+2; i++)
205 if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value))
206 if (!fs_test(cluster_start(fs,i),fs->cluster_size)) {
207 printf("Cluster %lu is unreadable.\n",i);
208 set_fat(fs,i,-2);
209 }
210 }
211
212
213 void reclaim_free(DOS_FS *fs)
214 {
215 int reclaimed;
216 unsigned long i;
217
218 if (verbose)
219 printf("Checking for unused clusters.\n");
220 reclaimed = 0;
221 for (i = 2; i < fs->clusters+2; i++)
222 if (!get_owner(fs,i) && fs->fat[i].value &&
223 !FAT_IS_BAD(fs,fs->fat[i].value)) {
224 set_fat(fs,i,0);
225 reclaimed++;
226 }
227 if (reclaimed)
228 printf("Reclaimed %d unused cluster%s (%d bytes).\n",reclaimed,
229 reclaimed == 1 ? "" : "s",reclaimed*fs->cluster_size);
230 }
231
232
233 static void tag_free(DOS_FS *fs,DOS_FILE *ptr)
234 {
235 DOS_FILE *owner;
236 int prev;
237 unsigned long i,walk;
238
239 for (i = 2; i < fs->clusters+2; i++)
240 if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) &&
241 !get_owner(fs,i) && !fs->fat[i].prev) {
242 prev = 0;
243 for (walk = i; walk > 0 && walk != -1;
244 walk = next_cluster(fs,walk)) {
245 if (!(owner = get_owner(fs,walk))) set_owner(fs,walk,ptr);
246 else if (owner != ptr)
247 die("Internal error: free chain collides with file");
248 else {
249 set_fat(fs,prev,-1);
250 break;
251 }
252 prev = walk;
253 }
254 }
255 }
256
257
258 void reclaim_file(DOS_FS *fs)
259 {
260 DOS_FILE dummy;
261 int reclaimed,files,changed;
262 unsigned long i,next,walk;
263
264 if (verbose)
265 printf("Reclaiming unconnected clusters.\n");
266 for (i = 2; i < fs->clusters+2; i++) fs->fat[i].prev = 0;
267 for (i = 2; i < fs->clusters+2; i++) {
268 next = fs->fat[i].value;
269 if (!get_owner(fs,i) && next && next < fs->clusters+2) {
270 if (get_owner(fs,next) || !fs->fat[next].value ||
271 FAT_IS_BAD(fs,fs->fat[next].value)) set_fat(fs,i,-1);
272 else fs->fat[next].prev++;
273 }
274 }
275 do {
276 tag_free(fs,&dummy);
277 changed = 0;
278 for (i = 2; i < fs->clusters+2; i++)
279 if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) &&
280 !get_owner(fs, i)) {
281 if (!fs->fat[fs->fat[i].value].prev--)
282 die("Internal error: prev going below zero");
283 set_fat(fs,i,-1);
284 changed = 1;
285 printf("Broke cycle at cluster %lu in free chain.\n",i);
286 break;
287 }
288 }
289 while (changed);
290 files = reclaimed = 0;
291 for (i = 2; i < fs->clusters+2; i++)
292 if (get_owner(fs,i) == &dummy && !fs->fat[i].prev) {
293 DIR_ENT de;
294 loff_t offset;
295 files++;
296 offset = alloc_rootdir_entry(fs,&de,"FSCK%04dREC");
297 de.start = CT_LE_W(i&0xffff);
298 if (fs->fat_bits == 32)
299 de.starthi = CT_LE_W(i>>16);
300 for (walk = i; walk > 0 && walk != -1;
301 walk = next_cluster(fs,walk)) {
302 de.size = CT_LE_L(CF_LE_L(de.size)+fs->cluster_size);
303 reclaimed++;
304 }
305 fs_write(offset,sizeof(DIR_ENT),&de);
306 }
307 if (reclaimed)
308 printf("Reclaimed %d unused cluster%s (%d bytes) in %d chain%s.\n",
309 reclaimed,reclaimed == 1 ? "" : "s",reclaimed*fs->cluster_size,files,
310 files == 1 ? "" : "s");
311 }
312
313
314 unsigned long update_free(DOS_FS *fs)
315 {
316 unsigned long i;
317 unsigned long free = 0;
318 int do_set = 0;
319
320 for (i = 2; i < fs->clusters+2; i++)
321 if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value))
322 ++free;
323
324 if (!fs->fsinfo_start)
325 return free;
326
327 if (verbose)
328 printf("Checking free cluster summary.\n");
329 if (fs->free_clusters >= 0) {
330 if (free != fs->free_clusters) {
331 printf( "Free cluster summary wrong (%ld vs. really %ld)\n",
332 fs->free_clusters,free);
333 if (interactive)
334 printf( "1) Correct\n2) Don't correct\n" );
335 else printf( " Auto-correcting.\n" );
336 if (!interactive || get_key("12","?") == '1')
337 do_set = 1;
338 }
339 }
340 else {
341 printf( "Free cluster summary uninitialized (should be %ld)\n", free );
342 if (interactive)
343 printf( "1) Set it\n2) Leave it uninitialized\n" );
344 else printf( " Auto-setting.\n" );
345 if (!interactive || get_key("12","?") == '1')
346 do_set = 1;
347 }
348
349 if (do_set) {
350 fs->free_clusters = free;
351 free = CT_LE_L(free);
352 fs_write(fs->fsinfo_start+offsetof(struct info_sector,free_clusters),
353 sizeof(free),&free);
354 }
355
356 return free;
357 }
358
359 /* Local Variables: */
360 /* tab-width: 8 */
361 /* End: */