1 /*
2 libparted
3 Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20 #include <parted/endian.h>
21 #include "fat.h"
22
23 #ifndef DISCOVER_ONLY
24
25 FatTable*
26 fat_table_new (FatType fat_type, FatCluster size)
27 {
28 FatTable* ft;
29 int entry_size = fat_table_entry_size (fat_type);
30
31 ft = (FatTable*) ped_malloc (sizeof (FatTable));
32 if (!ft) return NULL;
33
34 ft->cluster_count = ft->free_cluster_count = size - 2;
35
36 /* ensure there's some free room on the end, to finish off the sector */
37 ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size;
38 ft->fat_type = fat_type;
39 ft->raw_size = ft->size * entry_size;
40
41 ft->table = ped_malloc (ft->raw_size);
42 if (!ft->table) {
43 ped_free (ft);
44 return NULL;
45 }
46
47 fat_table_clear (ft);
48 return ft;
49 }
50
51 void
52 fat_table_destroy (FatTable* ft)
53 {
54 ped_free (ft->table);
55 ped_free (ft);
56 }
57
58 FatTable*
59 fat_table_duplicate (const FatTable* ft)
60 {
61 FatTable* dup;
62
63 dup = fat_table_new (ft->fat_type, ft->size);
64 if (!dup) return NULL;
65
66 dup->cluster_count = ft->cluster_count;
67 dup->free_cluster_count = ft->free_cluster_count;
68 dup->bad_cluster_count = ft->bad_cluster_count;
69 dup->last_alloc = ft->last_alloc;
70
71 memcpy (dup->table, ft->table, ft->raw_size);
72
73 return dup;
74 }
75
76 void
77 fat_table_clear (FatTable* ft)
78 {
79 memset (ft->table, 0, ft->raw_size);
80
81 fat_table_set (ft, 0, 0x0ffffff8);
82 fat_table_set (ft, 1, 0x0fffffff);
83
84 ft->free_cluster_count = ft->cluster_count;
85 ft->bad_cluster_count = 0;
86 ft->last_alloc = 1;
87 }
88
89 int
90 fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count)
91 {
92 PED_ASSERT (new_cluster_count + 2 <= ft->size, return 0);
93
94 ft->cluster_count = new_cluster_count;
95 return fat_table_count_stats (ft);
96 }
97
98 int
99 fat_table_count_stats (FatTable* ft)
100 {
101 FatCluster i;
102
103 PED_ASSERT (ft->cluster_count + 2 <= ft->size, return 0);
104
105 ft->free_cluster_count = 0;
106 ft->bad_cluster_count = 0;
107
108 for (i=2; i < ft->cluster_count + 2; i++) {
109 if (fat_table_is_available (ft, i))
110 ft->free_cluster_count++;
111 if (fat_table_is_bad (ft, i))
112 ft->bad_cluster_count++;
113 }
114 return 1;
115 }
116
117 int
118 fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num)
119 {
120 FatSpecific* fs_info = FAT_SPECIFIC (fs);
121
122 PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0);
123
124 memset (ft->table, 0, ft->raw_size);
125
126 if (!ped_geometry_read (fs->geom, (void *) ft->table,
127 fs_info->fat_offset
128 + table_num * fs_info->fat_sectors,
129 fs_info->fat_sectors))
130 return 0;
131
132 if ( *((unsigned char*) ft->table) != fs_info->boot_sector.media) {
133 if (ped_exception_throw (
134 PED_EXCEPTION_ERROR,
135 PED_EXCEPTION_IGNORE_CANCEL,
136 _("FAT %d media %x doesn't match the boot sector's "
137 "media %x. You should probably run scandisk."),
138 (int) table_num + 1,
139 (int) *((unsigned char*) ft->table),
140 (int) fs_info->boot_sector.media)
141 != PED_EXCEPTION_IGNORE)
142 return 0;
143 }
144
145 ft->cluster_count = fs_info->cluster_count;
146
147 fat_table_count_stats (ft);
148
149 return 1;
150 }
151
152 int
153 fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num)
154 {
155 FatSpecific* fs_info = FAT_SPECIFIC (fs);
156
157 PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0);
158
159 if (!ped_geometry_write (fs->geom, (void *) ft->table,
160 fs_info->fat_offset
161 + table_num * fs_info->fat_sectors,
162 fs_info->fat_sectors))
163 return 0;
164 if (!ped_geometry_sync (fs->geom))
165 return 0;
166
167 return 1;
168 }
169
170 int
171 fat_table_write_all (const FatTable* ft, PedFileSystem* fs)
172 {
173 FatSpecific* fs_info = FAT_SPECIFIC (fs);
174 int i;
175
176 for (i = 0; i < fs_info->fat_table_count; i++) {
177 if (!fat_table_write (ft, fs, i))
178 return 0;
179 }
180
181 return 1;
182 }
183
184 int
185 fat_table_compare (const FatTable* a, const FatTable* b)
186 {
187 FatCluster i;
188
189 if (a->cluster_count != b->cluster_count)
190 return 0;
191
192 for (i = 0; i < a->cluster_count + 2; i++) {
193 if (fat_table_get (a, i) != fat_table_get (b, i))
194 return 0;
195 }
196
197 return 1;
198 }
199
200 static int
201 _test_code_available (const FatTable* ft, FatCluster code)
202 {
203 return code == 0;
204 }
205
206 static int
207 _test_code_bad (const FatTable* ft, FatCluster code)
208 {
209 switch (ft->fat_type) {
210 case FAT_TYPE_FAT12:
211 if (code == 0xff7) return 1;
212 break;
213
214 case FAT_TYPE_FAT16:
215 if (code == 0xfff7) return 1;
216 break;
217
218 case FAT_TYPE_FAT32:
219 if (code == 0x0ffffff7) return 1;
220 break;
221 }
222 return 0;
223 }
224
225 static int
226 _test_code_eof (const FatTable* ft, FatCluster code)
227 {
228 switch (ft->fat_type) {
229 case FAT_TYPE_FAT12:
230 if (code >= 0xff7) return 1;
231 break;
232
233 case FAT_TYPE_FAT16:
234 if (code >= 0xfff7) return 1;
235 break;
236
237 case FAT_TYPE_FAT32:
238 if (code >= 0x0ffffff7) return 1;
239 break;
240 }
241 return 0;
242 }
243
244 void
245 _update_stats (FatTable* ft, FatCluster cluster, FatCluster value)
246 {
247 if (_test_code_available (ft, value)
248 && !fat_table_is_available (ft, cluster)) {
249 ft->free_cluster_count++;
250 if (fat_table_is_bad (ft, cluster))
251 ft->bad_cluster_count--;
252 }
253
254 if (!_test_code_available (ft, value)
255 && fat_table_is_available (ft, cluster)) {
256 ft->free_cluster_count--;
257 if (_test_code_bad (ft, cluster))
258 ft->bad_cluster_count--;
259 }
260 }
261
262 int
263 fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value)
264 {
265 if (cluster >= ft->cluster_count + 2) {
266 ped_exception_throw (PED_EXCEPTION_BUG,
267 PED_EXCEPTION_CANCEL,
268 _("fat_table_set: cluster %ld outside "
269 "file system"),
270 (long) cluster);
271 return 0;
272 }
273
274 _update_stats (ft, cluster, value);
275
276 switch (ft->fat_type) {
277 case FAT_TYPE_FAT12:
278 PED_ASSERT (0, (void) 0);
279 break;
280
281 case FAT_TYPE_FAT16:
282 ((unsigned short *) ft->table) [cluster]
283 = PED_CPU_TO_LE16 (value);
284 break;
285
286 case FAT_TYPE_FAT32:
287 ((unsigned int *) ft->table) [cluster]
288 = PED_CPU_TO_LE32 (value);
289 break;
290 }
291 return 1;
292 }
293
294 FatCluster
295 fat_table_get (const FatTable* ft, FatCluster cluster)
296 {
297 if (cluster >= ft->cluster_count + 2) {
298 ped_exception_throw (PED_EXCEPTION_BUG,
299 PED_EXCEPTION_CANCEL,
300 _("fat_table_get: cluster %ld outside "
301 "file system"),
302 (long) cluster);
303 exit (1); /* FIXME */
304 }
305
306 switch (ft->fat_type) {
307 case FAT_TYPE_FAT12:
308 PED_ASSERT (0, (void) 0);
309 break;
310
311 case FAT_TYPE_FAT16:
312 return PED_LE16_TO_CPU
313 (((unsigned short *) ft->table) [cluster]);
314
315 case FAT_TYPE_FAT32:
316 return PED_LE32_TO_CPU
317 (((unsigned int *) ft->table) [cluster]);
318 }
319
320 return 0;
321 }
322
323 FatCluster
324 fat_table_alloc_cluster (FatTable* ft)
325 {
326 FatCluster i;
327 FatCluster cluster;
328
329 /* hack: assumes the first two FAT entries are marked as used (which they
330 * always should be)
331 */
332 for (i=1; i < ft->cluster_count + 1; i++) {
333 cluster = (i + ft->last_alloc) % ft->cluster_count;
334 if (fat_table_is_available (ft, cluster)) {
335 ft->last_alloc = cluster;
336 return cluster;
337 }
338 }
339
340 ped_exception_throw (PED_EXCEPTION_ERROR,
341 PED_EXCEPTION_CANCEL,
342 _("fat_table_alloc_cluster: no free clusters"));
343 return 0;
344 }
345
346 FatCluster
347 fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs)
348 {
349 FatSpecific* fs_info = FAT_SPECIFIC (fs);
350 FatCluster result;
351
352 while (1) {
353 result = fat_table_alloc_cluster (ft);
354 if (!result)
355 return 0;
356 if (fat_read_cluster (fs, fs_info->buffer, result))
357 return result;
358 fat_table_set_bad (ft, result);
359 }
360 }
361
362 /*
363 returns true if <cluster> is marked as bad
364 */
365 int
366 fat_table_is_bad (const FatTable* ft, FatCluster cluster)
367 {
368 return _test_code_bad (ft, fat_table_get (ft, cluster));
369 }
370
371 /*
372 returns true if <cluster> represents an EOF marker
373 */
374 int
375 fat_table_is_eof (const FatTable* ft, FatCluster cluster)
376 {
377 return _test_code_eof (ft, cluster);
378 }
379
380 /*
381 returns true if <cluster> is available.
382 */
383 int
384 fat_table_is_available (const FatTable* ft, FatCluster cluster)
385 {
386 return _test_code_available (ft, fat_table_get (ft, cluster));
387 }
388
389 /*
390 returns true if <cluster> is empty. Note that this includes bad clusters.
391 */
392 int
393 fat_table_is_empty (const FatTable* ft, FatCluster cluster)
394 {
395 return fat_table_is_available (ft, cluster)
396 || fat_table_is_bad (ft, cluster);
397 }
398
399 /*
400 returns true if <cluster> is being used for something constructive.
401 */
402 int
403 fat_table_is_active (const FatTable* ft, FatCluster cluster)
404 {
405 return !fat_table_is_bad (ft, cluster)
406 && !fat_table_is_available (ft, cluster);
407 }
408
409 /*
410 marks <cluster> as the last cluster in the chain
411 */
412 int
413 fat_table_set_eof (FatTable* ft, FatCluster cluster)
414 {
415
416 switch (ft->fat_type) {
417 case FAT_TYPE_FAT12:
418 PED_ASSERT (0, (void) 0);
419 break;
420
421 case FAT_TYPE_FAT16:
422 return fat_table_set (ft, cluster, 0xfff8);
423
424 case FAT_TYPE_FAT32:
425 return fat_table_set (ft, cluster, 0x0fffffff);
426 }
427
428 return 0;
429 }
430
431 /*
432 Marks a clusters as unusable, due to physical disk damage.
433 */
434 int
435 fat_table_set_bad (FatTable* ft, FatCluster cluster)
436 {
437 if (!fat_table_is_bad (ft, cluster))
438 ft->bad_cluster_count++;
439
440 switch (ft->fat_type) {
441 case FAT_TYPE_FAT12:
442 return fat_table_set (ft, cluster, 0xff7);
443
444 case FAT_TYPE_FAT16:
445 return fat_table_set (ft, cluster, 0xfff7);
446
447 case FAT_TYPE_FAT32:
448 return fat_table_set (ft, cluster, 0x0ffffff7);
449 }
450
451 return 0;
452 }
453
454 /*
455 marks <cluster> as unused/free/available
456 */
457 int
458 fat_table_set_avail (FatTable* ft, FatCluster cluster)
459 {
460 return fat_table_set (ft, cluster, 0);
461 }
462
463 #endif /* !DISCOVER_ONLY */
464
465 int
466 fat_table_entry_size (FatType fat_type)
467 {
468 switch (fat_type) {
469 case FAT_TYPE_FAT12:
470 return 2; /* FIXME: how? */
471
472 case FAT_TYPE_FAT16:
473 return 2;
474
475 case FAT_TYPE_FAT32:
476 return 4;
477 }
478
479 return 0;
480 }
481