Mercurial > pihelp
comparison fat.c @ 19:5f9a40d6991b
Import SD handling from http://www.roland-riegel.de/sd-reader/index.html
Use smaller build options
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 25 Jun 2013 13:55:11 +0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
18:021e6e0006f4 | 19:5f9a40d6991b |
---|---|
1 | |
2 /* | |
3 * Copyright (c) 2006-2012 by Roland Riegel <[email protected]> | |
4 * | |
5 * This file is free software; you can redistribute it and/or modify | |
6 * it under the terms of either the GNU General Public License version 2 | |
7 * or the GNU Lesser General Public License version 2.1, both as | |
8 * published by the Free Software Foundation. | |
9 */ | |
10 | |
11 #include "byteordering.h" | |
12 #include "partition.h" | |
13 #include "fat.h" | |
14 #include "fat_config.h" | |
15 #include "sd-reader_config.h" | |
16 | |
17 #include <string.h> | |
18 | |
19 #if USE_DYNAMIC_MEMORY | |
20 #include <stdlib.h> | |
21 #endif | |
22 | |
23 /** | |
24 * \addtogroup fat FAT support | |
25 * | |
26 * This module implements FAT16/FAT32 read and write access. | |
27 * | |
28 * The following features are supported: | |
29 * - File names up to 31 characters long. | |
30 * - Unlimited depth of subdirectories. | |
31 * - Short 8.3 and long filenames. | |
32 * - Creating and deleting files. | |
33 * - Reading and writing from and to files. | |
34 * - File resizing. | |
35 * - File sizes of up to 4 gigabytes. | |
36 * | |
37 * @{ | |
38 */ | |
39 /** | |
40 * \file | |
41 * FAT implementation (license: GPLv2 or LGPLv2.1) | |
42 * | |
43 * \author Roland Riegel | |
44 */ | |
45 | |
46 /** | |
47 * \addtogroup fat_config FAT configuration | |
48 * Preprocessor defines to configure the FAT implementation. | |
49 */ | |
50 | |
51 /** | |
52 * \addtogroup fat_fs FAT access | |
53 * Basic functions for handling a FAT filesystem. | |
54 */ | |
55 | |
56 /** | |
57 * \addtogroup fat_file FAT file functions | |
58 * Functions for managing files. | |
59 */ | |
60 | |
61 /** | |
62 * \addtogroup fat_dir FAT directory functions | |
63 * Functions for managing directories. | |
64 */ | |
65 | |
66 /** | |
67 * @} | |
68 */ | |
69 | |
70 #define FAT16_CLUSTER_FREE 0x0000 | |
71 #define FAT16_CLUSTER_RESERVED_MIN 0xfff0 | |
72 #define FAT16_CLUSTER_RESERVED_MAX 0xfff6 | |
73 #define FAT16_CLUSTER_BAD 0xfff7 | |
74 #define FAT16_CLUSTER_LAST_MIN 0xfff8 | |
75 #define FAT16_CLUSTER_LAST_MAX 0xffff | |
76 | |
77 #define FAT32_CLUSTER_FREE 0x00000000 | |
78 #define FAT32_CLUSTER_RESERVED_MIN 0x0ffffff0 | |
79 #define FAT32_CLUSTER_RESERVED_MAX 0x0ffffff6 | |
80 #define FAT32_CLUSTER_BAD 0x0ffffff7 | |
81 #define FAT32_CLUSTER_LAST_MIN 0x0ffffff8 | |
82 #define FAT32_CLUSTER_LAST_MAX 0x0fffffff | |
83 | |
84 #define FAT_DIRENTRY_DELETED 0xe5 | |
85 #define FAT_DIRENTRY_LFNLAST (1 << 6) | |
86 #define FAT_DIRENTRY_LFNSEQMASK ((1 << 6) - 1) | |
87 | |
88 /* Each entry within the directory table has a size of 32 bytes | |
89 * and either contains a 8.3 DOS-style file name or a part of a | |
90 * long file name, which may consist of several directory table | |
91 * entries at once. | |
92 * | |
93 * multi-byte integer values are stored little-endian! | |
94 * | |
95 * 8.3 file name entry: | |
96 * ==================== | |
97 * offset length description | |
98 * 0 8 name (space padded) | |
99 * 8 3 extension (space padded) | |
100 * 11 1 attributes (FAT_ATTRIB_*) | |
101 * | |
102 * long file name (lfn) entry ordering for a single file name: | |
103 * =========================================================== | |
104 * LFN entry n | |
105 * ... | |
106 * LFN entry 2 | |
107 * LFN entry 1 | |
108 * 8.3 entry (see above) | |
109 * | |
110 * lfn entry: | |
111 * ========== | |
112 * offset length description | |
113 * 0 1 ordinal field | |
114 * 1 2 unicode character 1 | |
115 * 3 3 unicode character 2 | |
116 * 5 3 unicode character 3 | |
117 * 7 3 unicode character 4 | |
118 * 9 3 unicode character 5 | |
119 * 11 1 attribute (always 0x0f) | |
120 * 12 1 type (reserved, always 0) | |
121 * 13 1 checksum | |
122 * 14 2 unicode character 6 | |
123 * 16 2 unicode character 7 | |
124 * 18 2 unicode character 8 | |
125 * 20 2 unicode character 9 | |
126 * 22 2 unicode character 10 | |
127 * 24 2 unicode character 11 | |
128 * 26 2 cluster (unused, always 0) | |
129 * 28 2 unicode character 12 | |
130 * 30 2 unicode character 13 | |
131 * | |
132 * The ordinal field contains a descending number, from n to 1. | |
133 * For the n'th lfn entry the ordinal field is or'ed with 0x40. | |
134 * For deleted lfn entries, the ordinal field is set to 0xe5. | |
135 */ | |
136 | |
137 struct fat_header_struct | |
138 { | |
139 offset_t size; | |
140 | |
141 offset_t fat_offset; | |
142 uint32_t fat_size; | |
143 | |
144 uint16_t sector_size; | |
145 uint16_t cluster_size; | |
146 | |
147 offset_t cluster_zero_offset; | |
148 | |
149 offset_t root_dir_offset; | |
150 #if FAT_FAT32_SUPPORT | |
151 cluster_t root_dir_cluster; | |
152 #endif | |
153 }; | |
154 | |
155 struct fat_fs_struct | |
156 { | |
157 struct partition_struct* partition; | |
158 struct fat_header_struct header; | |
159 cluster_t cluster_free; | |
160 }; | |
161 | |
162 struct fat_file_struct | |
163 { | |
164 struct fat_fs_struct* fs; | |
165 struct fat_dir_entry_struct dir_entry; | |
166 offset_t pos; | |
167 cluster_t pos_cluster; | |
168 }; | |
169 | |
170 struct fat_dir_struct | |
171 { | |
172 struct fat_fs_struct* fs; | |
173 struct fat_dir_entry_struct dir_entry; | |
174 cluster_t entry_cluster; | |
175 uint16_t entry_offset; | |
176 }; | |
177 | |
178 struct fat_read_dir_callback_arg | |
179 { | |
180 struct fat_dir_entry_struct* dir_entry; | |
181 uintptr_t bytes_read; | |
182 #if FAT_LFN_SUPPORT | |
183 uint8_t checksum; | |
184 #endif | |
185 uint8_t finished; | |
186 }; | |
187 | |
188 struct fat_usage_count_callback_arg | |
189 { | |
190 cluster_t cluster_count; | |
191 uintptr_t buffer_size; | |
192 }; | |
193 | |
194 #if !USE_DYNAMIC_MEMORY | |
195 static struct fat_fs_struct fat_fs_handles[FAT_FS_COUNT]; | |
196 static struct fat_file_struct fat_file_handles[FAT_FILE_COUNT]; | |
197 static struct fat_dir_struct fat_dir_handles[FAT_DIR_COUNT]; | |
198 #endif | |
199 | |
200 static uint8_t fat_read_header(struct fat_fs_struct* fs); | |
201 static cluster_t fat_get_next_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num); | |
202 static offset_t fat_cluster_offset(const struct fat_fs_struct* fs, cluster_t cluster_num); | |
203 static uint8_t fat_dir_entry_read_callback(uint8_t* buffer, offset_t offset, void* p); | |
204 #if FAT_LFN_SUPPORT | |
205 static uint8_t fat_calc_83_checksum(const uint8_t* file_name_83); | |
206 #endif | |
207 | |
208 static uint8_t fat_get_fs_free_16_callback(uint8_t* buffer, offset_t offset, void* p); | |
209 #if FAT_FAT32_SUPPORT | |
210 static uint8_t fat_get_fs_free_32_callback(uint8_t* buffer, offset_t offset, void* p); | |
211 #endif | |
212 | |
213 #if FAT_WRITE_SUPPORT | |
214 static cluster_t fat_append_clusters(struct fat_fs_struct* fs, cluster_t cluster_num, cluster_t count); | |
215 static uint8_t fat_free_clusters(struct fat_fs_struct* fs, cluster_t cluster_num); | |
216 static uint8_t fat_terminate_clusters(struct fat_fs_struct* fs, cluster_t cluster_num); | |
217 static uint8_t fat_clear_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num); | |
218 static uintptr_t fat_clear_cluster_callback(uint8_t* buffer, offset_t offset, void* p); | |
219 static offset_t fat_find_offset_for_dir_entry(struct fat_fs_struct* fs, const struct fat_dir_struct* parent, const struct fat_dir_entry_struct* dir_entry); | |
220 static uint8_t fat_write_dir_entry(const struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry); | |
221 #if FAT_DATETIME_SUPPORT | |
222 static void fat_set_file_modification_date(struct fat_dir_entry_struct* dir_entry, uint16_t year, uint8_t month, uint8_t day); | |
223 static void fat_set_file_modification_time(struct fat_dir_entry_struct* dir_entry, uint8_t hour, uint8_t min, uint8_t sec); | |
224 #endif | |
225 #endif | |
226 | |
227 /** | |
228 * \ingroup fat_fs | |
229 * Opens a FAT filesystem. | |
230 * | |
231 * \param[in] partition Discriptor of partition on which the filesystem resides. | |
232 * \returns 0 on error, a FAT filesystem descriptor on success. | |
233 * \see fat_close | |
234 */ | |
235 struct fat_fs_struct* fat_open(struct partition_struct* partition) | |
236 { | |
237 if(!partition || | |
238 #if FAT_WRITE_SUPPORT | |
239 !partition->device_write || | |
240 !partition->device_write_interval | |
241 #else | |
242 0 | |
243 #endif | |
244 ) | |
245 return 0; | |
246 | |
247 #if USE_DYNAMIC_MEMORY | |
248 struct fat_fs_struct* fs = malloc(sizeof(*fs)); | |
249 if(!fs) | |
250 return 0; | |
251 #else | |
252 struct fat_fs_struct* fs = fat_fs_handles; | |
253 uint8_t i; | |
254 for(i = 0; i < FAT_FS_COUNT; ++i) | |
255 { | |
256 if(!fs->partition) | |
257 break; | |
258 | |
259 ++fs; | |
260 } | |
261 if(i >= FAT_FS_COUNT) | |
262 return 0; | |
263 #endif | |
264 | |
265 memset(fs, 0, sizeof(*fs)); | |
266 | |
267 fs->partition = partition; | |
268 if(!fat_read_header(fs)) | |
269 { | |
270 #if USE_DYNAMIC_MEMORY | |
271 free(fs); | |
272 #else | |
273 fs->partition = 0; | |
274 #endif | |
275 return 0; | |
276 } | |
277 | |
278 return fs; | |
279 } | |
280 | |
281 /** | |
282 * \ingroup fat_fs | |
283 * Closes a FAT filesystem. | |
284 * | |
285 * When this function returns, the given filesystem descriptor | |
286 * will be invalid. | |
287 * | |
288 * \param[in] fs The filesystem to close. | |
289 * \see fat_open | |
290 */ | |
291 void fat_close(struct fat_fs_struct* fs) | |
292 { | |
293 if(!fs) | |
294 return; | |
295 | |
296 #if USE_DYNAMIC_MEMORY | |
297 free(fs); | |
298 #else | |
299 fs->partition = 0; | |
300 #endif | |
301 } | |
302 | |
303 /** | |
304 * \ingroup fat_fs | |
305 * Reads and parses the header of a FAT filesystem. | |
306 * | |
307 * \param[in,out] fs The filesystem for which to parse the header. | |
308 * \returns 0 on failure, 1 on success. | |
309 */ | |
310 uint8_t fat_read_header(struct fat_fs_struct* fs) | |
311 { | |
312 if(!fs) | |
313 return 0; | |
314 | |
315 struct partition_struct* partition = fs->partition; | |
316 if(!partition) | |
317 return 0; | |
318 | |
319 /* read fat parameters */ | |
320 #if FAT_FAT32_SUPPORT | |
321 uint8_t buffer[37]; | |
322 #else | |
323 uint8_t buffer[25]; | |
324 #endif | |
325 offset_t partition_offset = (offset_t) partition->offset * 512; | |
326 if(!partition->device_read(partition_offset + 0x0b, buffer, sizeof(buffer))) | |
327 return 0; | |
328 | |
329 uint16_t bytes_per_sector = read16(&buffer[0x00]); | |
330 uint16_t reserved_sectors = read16(&buffer[0x03]); | |
331 uint8_t sectors_per_cluster = buffer[0x02]; | |
332 uint8_t fat_copies = buffer[0x05]; | |
333 uint16_t max_root_entries = read16(&buffer[0x06]); | |
334 uint16_t sector_count_16 = read16(&buffer[0x08]); | |
335 uint16_t sectors_per_fat = read16(&buffer[0x0b]); | |
336 uint32_t sector_count = read32(&buffer[0x15]); | |
337 #if FAT_FAT32_SUPPORT | |
338 uint32_t sectors_per_fat32 = read32(&buffer[0x19]); | |
339 uint32_t cluster_root_dir = read32(&buffer[0x21]); | |
340 #endif | |
341 | |
342 if(sector_count == 0) | |
343 { | |
344 if(sector_count_16 == 0) | |
345 /* illegal volume size */ | |
346 return 0; | |
347 else | |
348 sector_count = sector_count_16; | |
349 } | |
350 #if FAT_FAT32_SUPPORT | |
351 if(sectors_per_fat != 0) | |
352 sectors_per_fat32 = sectors_per_fat; | |
353 else if(sectors_per_fat32 == 0) | |
354 /* this is neither FAT16 nor FAT32 */ | |
355 return 0; | |
356 #else | |
357 if(sectors_per_fat == 0) | |
358 /* this is not a FAT16 */ | |
359 return 0; | |
360 #endif | |
361 | |
362 /* determine the type of FAT we have here */ | |
363 uint32_t data_sector_count = sector_count | |
364 - reserved_sectors | |
365 #if FAT_FAT32_SUPPORT | |
366 - sectors_per_fat32 * fat_copies | |
367 #else | |
368 - (uint32_t) sectors_per_fat * fat_copies | |
369 #endif | |
370 - ((max_root_entries * 32 + bytes_per_sector - 1) / bytes_per_sector); | |
371 uint32_t data_cluster_count = data_sector_count / sectors_per_cluster; | |
372 if(data_cluster_count < 4085) | |
373 /* this is a FAT12, not supported */ | |
374 return 0; | |
375 else if(data_cluster_count < 65525) | |
376 /* this is a FAT16 */ | |
377 partition->type = PARTITION_TYPE_FAT16; | |
378 else | |
379 /* this is a FAT32 */ | |
380 partition->type = PARTITION_TYPE_FAT32; | |
381 | |
382 /* fill header information */ | |
383 struct fat_header_struct* header = &fs->header; | |
384 memset(header, 0, sizeof(*header)); | |
385 | |
386 header->size = (offset_t) sector_count * bytes_per_sector; | |
387 | |
388 header->fat_offset = /* jump to partition */ | |
389 partition_offset + | |
390 /* jump to fat */ | |
391 (offset_t) reserved_sectors * bytes_per_sector; | |
392 header->fat_size = (data_cluster_count + 2) * (partition->type == PARTITION_TYPE_FAT16 ? 2 : 4); | |
393 | |
394 header->sector_size = bytes_per_sector; | |
395 header->cluster_size = (uint16_t) bytes_per_sector * sectors_per_cluster; | |
396 | |
397 #if FAT_FAT32_SUPPORT | |
398 if(partition->type == PARTITION_TYPE_FAT16) | |
399 #endif | |
400 { | |
401 header->root_dir_offset = /* jump to fats */ | |
402 header->fat_offset + | |
403 /* jump to root directory entries */ | |
404 (offset_t) fat_copies * sectors_per_fat * bytes_per_sector; | |
405 | |
406 header->cluster_zero_offset = /* jump to root directory entries */ | |
407 header->root_dir_offset + | |
408 /* skip root directory entries */ | |
409 (offset_t) max_root_entries * 32; | |
410 } | |
411 #if FAT_FAT32_SUPPORT | |
412 else | |
413 { | |
414 header->cluster_zero_offset = /* jump to fats */ | |
415 header->fat_offset + | |
416 /* skip fats */ | |
417 (offset_t) fat_copies * sectors_per_fat32 * bytes_per_sector; | |
418 | |
419 header->root_dir_cluster = cluster_root_dir; | |
420 } | |
421 #endif | |
422 | |
423 return 1; | |
424 } | |
425 | |
426 /** | |
427 * \ingroup fat_fs | |
428 * Retrieves the next following cluster of a given cluster. | |
429 * | |
430 * Using the filesystem file allocation table, this function returns | |
431 * the number of the cluster containing the data directly following | |
432 * the data within the cluster with the given number. | |
433 * | |
434 * \param[in] fs The filesystem for which to determine the next cluster. | |
435 * \param[in] cluster_num The number of the cluster for which to determine its successor. | |
436 * \returns The wanted cluster number, or 0 on error. | |
437 */ | |
438 cluster_t fat_get_next_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num) | |
439 { | |
440 if(!fs || cluster_num < 2) | |
441 return 0; | |
442 | |
443 #if FAT_FAT32_SUPPORT | |
444 if(fs->partition->type == PARTITION_TYPE_FAT32) | |
445 { | |
446 /* read appropriate fat entry */ | |
447 uint32_t fat_entry; | |
448 if(!fs->partition->device_read(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) | |
449 return 0; | |
450 | |
451 /* determine next cluster from fat */ | |
452 cluster_num = ltoh32(fat_entry); | |
453 | |
454 if(cluster_num == FAT32_CLUSTER_FREE || | |
455 cluster_num == FAT32_CLUSTER_BAD || | |
456 (cluster_num >= FAT32_CLUSTER_RESERVED_MIN && cluster_num <= FAT32_CLUSTER_RESERVED_MAX) || | |
457 (cluster_num >= FAT32_CLUSTER_LAST_MIN && cluster_num <= FAT32_CLUSTER_LAST_MAX)) | |
458 return 0; | |
459 } | |
460 else | |
461 #endif | |
462 { | |
463 /* read appropriate fat entry */ | |
464 uint16_t fat_entry; | |
465 if(!fs->partition->device_read(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) | |
466 return 0; | |
467 | |
468 /* determine next cluster from fat */ | |
469 cluster_num = ltoh16(fat_entry); | |
470 | |
471 if(cluster_num == FAT16_CLUSTER_FREE || | |
472 cluster_num == FAT16_CLUSTER_BAD || | |
473 (cluster_num >= FAT16_CLUSTER_RESERVED_MIN && cluster_num <= FAT16_CLUSTER_RESERVED_MAX) || | |
474 (cluster_num >= FAT16_CLUSTER_LAST_MIN && cluster_num <= FAT16_CLUSTER_LAST_MAX)) | |
475 return 0; | |
476 } | |
477 | |
478 return cluster_num; | |
479 } | |
480 | |
481 #if DOXYGEN || FAT_WRITE_SUPPORT | |
482 /** | |
483 * \ingroup fat_fs | |
484 * Appends a new cluster chain to an existing one. | |
485 * | |
486 * Set cluster_num to zero to create a completely new one. | |
487 * | |
488 * \param[in] fs The file system on which to operate. | |
489 * \param[in] cluster_num The cluster to which to append the new chain. | |
490 * \param[in] count The number of clusters to allocate. | |
491 * \returns 0 on failure, the number of the first new cluster on success. | |
492 */ | |
493 cluster_t fat_append_clusters(struct fat_fs_struct* fs, cluster_t cluster_num, cluster_t count) | |
494 { | |
495 if(!fs) | |
496 return 0; | |
497 | |
498 device_read_t device_read = fs->partition->device_read; | |
499 device_write_t device_write = fs->partition->device_write; | |
500 offset_t fat_offset = fs->header.fat_offset; | |
501 cluster_t count_left = count; | |
502 cluster_t cluster_current = fs->cluster_free; | |
503 cluster_t cluster_next = 0; | |
504 cluster_t cluster_count; | |
505 uint16_t fat_entry16; | |
506 #if FAT_FAT32_SUPPORT | |
507 uint32_t fat_entry32; | |
508 uint8_t is_fat32 = (fs->partition->type == PARTITION_TYPE_FAT32); | |
509 | |
510 if(is_fat32) | |
511 cluster_count = fs->header.fat_size / sizeof(fat_entry32); | |
512 else | |
513 #endif | |
514 cluster_count = fs->header.fat_size / sizeof(fat_entry16); | |
515 | |
516 fs->cluster_free = 0; | |
517 for(cluster_t cluster_left = cluster_count; cluster_left > 0; --cluster_left, ++cluster_current) | |
518 { | |
519 if(cluster_current < 2 || cluster_current >= cluster_count) | |
520 cluster_current = 2; | |
521 | |
522 #if FAT_FAT32_SUPPORT | |
523 if(is_fat32) | |
524 { | |
525 if(!device_read(fat_offset + (offset_t) cluster_current * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32))) | |
526 return 0; | |
527 } | |
528 else | |
529 #endif | |
530 { | |
531 if(!device_read(fat_offset + (offset_t) cluster_current * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16))) | |
532 return 0; | |
533 } | |
534 | |
535 #if FAT_FAT32_SUPPORT | |
536 if(is_fat32) | |
537 { | |
538 /* check if this is a free cluster */ | |
539 if(fat_entry32 != HTOL32(FAT32_CLUSTER_FREE)) | |
540 continue; | |
541 | |
542 /* If we don't need this free cluster for the | |
543 * current allocation, we keep it in mind for | |
544 * the next time. | |
545 */ | |
546 if(count_left == 0) | |
547 { | |
548 fs->cluster_free = cluster_current; | |
549 break; | |
550 } | |
551 | |
552 /* allocate cluster */ | |
553 if(cluster_next == 0) | |
554 fat_entry32 = HTOL32(FAT32_CLUSTER_LAST_MAX); | |
555 else | |
556 fat_entry32 = htol32(cluster_next); | |
557 | |
558 if(!device_write(fat_offset + (offset_t) cluster_current * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32))) | |
559 break; | |
560 } | |
561 else | |
562 #endif | |
563 { | |
564 /* check if this is a free cluster */ | |
565 if(fat_entry16 != HTOL16(FAT16_CLUSTER_FREE)) | |
566 continue; | |
567 | |
568 /* If we don't need this free cluster for the | |
569 * current allocation, we keep it in mind for | |
570 * the next time. | |
571 */ | |
572 if(count_left == 0) | |
573 { | |
574 fs->cluster_free = cluster_current; | |
575 break; | |
576 } | |
577 | |
578 /* allocate cluster */ | |
579 if(cluster_next == 0) | |
580 fat_entry16 = HTOL16(FAT16_CLUSTER_LAST_MAX); | |
581 else | |
582 fat_entry16 = htol16((uint16_t) cluster_next); | |
583 | |
584 if(!device_write(fat_offset + (offset_t) cluster_current * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16))) | |
585 break; | |
586 } | |
587 | |
588 cluster_next = cluster_current; | |
589 --count_left; | |
590 } | |
591 | |
592 do | |
593 { | |
594 if(count_left > 0) | |
595 break; | |
596 | |
597 /* We allocated a new cluster chain. Now join | |
598 * it with the existing one (if any). | |
599 */ | |
600 if(cluster_num >= 2) | |
601 { | |
602 #if FAT_FAT32_SUPPORT | |
603 if(is_fat32) | |
604 { | |
605 fat_entry32 = htol32(cluster_next); | |
606 | |
607 if(!device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32))) | |
608 break; | |
609 } | |
610 else | |
611 #endif | |
612 { | |
613 fat_entry16 = htol16((uint16_t) cluster_next); | |
614 | |
615 if(!device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16))) | |
616 break; | |
617 } | |
618 } | |
619 | |
620 return cluster_next; | |
621 | |
622 } while(0); | |
623 | |
624 /* No space left on device or writing error. | |
625 * Free up all clusters already allocated. | |
626 */ | |
627 fat_free_clusters(fs, cluster_next); | |
628 | |
629 return 0; | |
630 } | |
631 #endif | |
632 | |
633 #if DOXYGEN || FAT_WRITE_SUPPORT | |
634 /** | |
635 * \ingroup fat_fs | |
636 * Frees a cluster chain, or a part thereof. | |
637 * | |
638 * Marks the specified cluster and all clusters which are sequentially | |
639 * referenced by it as free. They may then be used again for future | |
640 * file allocations. | |
641 * | |
642 * \note If this function is used for freeing just a part of a cluster | |
643 * chain, the new end of the chain is not correctly terminated | |
644 * within the FAT. Use fat_terminate_clusters() instead. | |
645 * | |
646 * \param[in] fs The filesystem on which to operate. | |
647 * \param[in] cluster_num The starting cluster of the chain which to free. | |
648 * \returns 0 on failure, 1 on success. | |
649 * \see fat_terminate_clusters | |
650 */ | |
651 uint8_t fat_free_clusters(struct fat_fs_struct* fs, cluster_t cluster_num) | |
652 { | |
653 if(!fs || cluster_num < 2) | |
654 return 0; | |
655 | |
656 offset_t fat_offset = fs->header.fat_offset; | |
657 #if FAT_FAT32_SUPPORT | |
658 if(fs->partition->type == PARTITION_TYPE_FAT32) | |
659 { | |
660 uint32_t fat_entry; | |
661 while(cluster_num) | |
662 { | |
663 if(!fs->partition->device_read(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) | |
664 return 0; | |
665 | |
666 /* get next cluster of current cluster before freeing current cluster */ | |
667 uint32_t cluster_num_next = ltoh32(fat_entry); | |
668 | |
669 if(cluster_num_next == FAT32_CLUSTER_FREE) | |
670 return 1; | |
671 if(cluster_num_next == FAT32_CLUSTER_BAD || | |
672 (cluster_num_next >= FAT32_CLUSTER_RESERVED_MIN && | |
673 cluster_num_next <= FAT32_CLUSTER_RESERVED_MAX | |
674 ) | |
675 ) | |
676 return 0; | |
677 if(cluster_num_next >= FAT32_CLUSTER_LAST_MIN && cluster_num_next <= FAT32_CLUSTER_LAST_MAX) | |
678 cluster_num_next = 0; | |
679 | |
680 /* We know we will free the cluster, so remember it as | |
681 * free for the next allocation. | |
682 */ | |
683 if(!fs->cluster_free) | |
684 fs->cluster_free = cluster_num; | |
685 | |
686 /* free cluster */ | |
687 fat_entry = HTOL32(FAT32_CLUSTER_FREE); | |
688 fs->partition->device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)); | |
689 | |
690 /* We continue in any case here, even if freeing the cluster failed. | |
691 * The cluster is lost, but maybe we can still free up some later ones. | |
692 */ | |
693 | |
694 cluster_num = cluster_num_next; | |
695 } | |
696 } | |
697 else | |
698 #endif | |
699 { | |
700 uint16_t fat_entry; | |
701 while(cluster_num) | |
702 { | |
703 if(!fs->partition->device_read(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) | |
704 return 0; | |
705 | |
706 /* get next cluster of current cluster before freeing current cluster */ | |
707 uint16_t cluster_num_next = ltoh16(fat_entry); | |
708 | |
709 if(cluster_num_next == FAT16_CLUSTER_FREE) | |
710 return 1; | |
711 if(cluster_num_next == FAT16_CLUSTER_BAD || | |
712 (cluster_num_next >= FAT16_CLUSTER_RESERVED_MIN && | |
713 cluster_num_next <= FAT16_CLUSTER_RESERVED_MAX | |
714 ) | |
715 ) | |
716 return 0; | |
717 if(cluster_num_next >= FAT16_CLUSTER_LAST_MIN && cluster_num_next <= FAT16_CLUSTER_LAST_MAX) | |
718 cluster_num_next = 0; | |
719 | |
720 /* free cluster */ | |
721 fat_entry = HTOL16(FAT16_CLUSTER_FREE); | |
722 fs->partition->device_write(fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)); | |
723 | |
724 /* We continue in any case here, even if freeing the cluster failed. | |
725 * The cluster is lost, but maybe we can still free up some later ones. | |
726 */ | |
727 | |
728 cluster_num = cluster_num_next; | |
729 } | |
730 } | |
731 | |
732 return 1; | |
733 } | |
734 #endif | |
735 | |
736 #if DOXYGEN || FAT_WRITE_SUPPORT | |
737 /** | |
738 * \ingroup fat_fs | |
739 * Frees a part of a cluster chain and correctly terminates the rest. | |
740 * | |
741 * Marks the specified cluster as the new end of a cluster chain and | |
742 * frees all following clusters. | |
743 * | |
744 * \param[in] fs The filesystem on which to operate. | |
745 * \param[in] cluster_num The new end of the cluster chain. | |
746 * \returns 0 on failure, 1 on success. | |
747 * \see fat_free_clusters | |
748 */ | |
749 uint8_t fat_terminate_clusters(struct fat_fs_struct* fs, cluster_t cluster_num) | |
750 { | |
751 if(!fs || cluster_num < 2) | |
752 return 0; | |
753 | |
754 /* fetch next cluster before overwriting the cluster entry */ | |
755 cluster_t cluster_num_next = fat_get_next_cluster(fs, cluster_num); | |
756 | |
757 /* mark cluster as the last one */ | |
758 #if FAT_FAT32_SUPPORT | |
759 if(fs->partition->type == PARTITION_TYPE_FAT32) | |
760 { | |
761 uint32_t fat_entry = HTOL32(FAT32_CLUSTER_LAST_MAX); | |
762 if(!fs->partition->device_write(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) | |
763 return 0; | |
764 } | |
765 else | |
766 #endif | |
767 { | |
768 uint16_t fat_entry = HTOL16(FAT16_CLUSTER_LAST_MAX); | |
769 if(!fs->partition->device_write(fs->header.fat_offset + (offset_t) cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) | |
770 return 0; | |
771 } | |
772 | |
773 /* free remaining clusters */ | |
774 if(cluster_num_next) | |
775 return fat_free_clusters(fs, cluster_num_next); | |
776 else | |
777 return 1; | |
778 } | |
779 #endif | |
780 | |
781 #if DOXYGEN || FAT_WRITE_SUPPORT | |
782 /** | |
783 * \ingroup fat_fs | |
784 * Clears a single cluster. | |
785 * | |
786 * The complete cluster is filled with zeros. | |
787 * | |
788 * \param[in] fs The filesystem on which to operate. | |
789 * \param[in] cluster_num The cluster to clear. | |
790 * \returns 0 on failure, 1 on success. | |
791 */ | |
792 uint8_t fat_clear_cluster(const struct fat_fs_struct* fs, cluster_t cluster_num) | |
793 { | |
794 if(cluster_num < 2) | |
795 return 0; | |
796 | |
797 offset_t cluster_offset = fat_cluster_offset(fs, cluster_num); | |
798 | |
799 uint8_t zero[16]; | |
800 memset(zero, 0, sizeof(zero)); | |
801 return fs->partition->device_write_interval(cluster_offset, | |
802 zero, | |
803 fs->header.cluster_size, | |
804 fat_clear_cluster_callback, | |
805 0 | |
806 ); | |
807 } | |
808 #endif | |
809 | |
810 #if DOXYGEN || FAT_WRITE_SUPPORT | |
811 /** | |
812 * \ingroup fat_fs | |
813 * Callback function for clearing a cluster. | |
814 */ | |
815 uintptr_t fat_clear_cluster_callback(uint8_t* buffer, offset_t offset, void* p) | |
816 { | |
817 return 16; | |
818 } | |
819 #endif | |
820 | |
821 /** | |
822 * \ingroup fat_fs | |
823 * Calculates the offset of the specified cluster. | |
824 * | |
825 * \param[in] fs The filesystem on which to operate. | |
826 * \param[in] cluster_num The cluster whose offset to calculate. | |
827 * \returns The cluster offset. | |
828 */ | |
829 offset_t fat_cluster_offset(const struct fat_fs_struct* fs, cluster_t cluster_num) | |
830 { | |
831 if(!fs || cluster_num < 2) | |
832 return 0; | |
833 | |
834 return fs->header.cluster_zero_offset + (offset_t) (cluster_num - 2) * fs->header.cluster_size; | |
835 } | |
836 | |
837 /** | |
838 * \ingroup fat_file | |
839 * Retrieves the directory entry of a path. | |
840 * | |
841 * The given path may both describe a file or a directory. | |
842 * | |
843 * \param[in] fs The FAT filesystem on which to search. | |
844 * \param[in] path The path of which to read the directory entry. | |
845 * \param[out] dir_entry The directory entry to fill. | |
846 * \returns 0 on failure, 1 on success. | |
847 * \see fat_read_dir | |
848 */ | |
849 uint8_t fat_get_dir_entry_of_path(struct fat_fs_struct* fs, const char* path, struct fat_dir_entry_struct* dir_entry) | |
850 { | |
851 if(!fs || !path || path[0] == '\0' || !dir_entry) | |
852 return 0; | |
853 | |
854 if(path[0] == '/') | |
855 ++path; | |
856 | |
857 /* begin with the root directory */ | |
858 memset(dir_entry, 0, sizeof(*dir_entry)); | |
859 dir_entry->attributes = FAT_ATTRIB_DIR; | |
860 | |
861 while(1) | |
862 { | |
863 if(path[0] == '\0') | |
864 return 1; | |
865 | |
866 struct fat_dir_struct* dd = fat_open_dir(fs, dir_entry); | |
867 if(!dd) | |
868 break; | |
869 | |
870 /* extract the next hierarchy we will search for */ | |
871 const char* sub_path = strchr(path, '/'); | |
872 uint8_t length_to_sep; | |
873 if(sub_path) | |
874 { | |
875 length_to_sep = sub_path - path; | |
876 ++sub_path; | |
877 } | |
878 else | |
879 { | |
880 length_to_sep = strlen(path); | |
881 sub_path = path + length_to_sep; | |
882 } | |
883 | |
884 /* read directory entries */ | |
885 while(fat_read_dir(dd, dir_entry)) | |
886 { | |
887 /* check if we have found the next hierarchy */ | |
888 if((strlen(dir_entry->long_name) != length_to_sep || | |
889 strncmp(path, dir_entry->long_name, length_to_sep) != 0)) | |
890 continue; | |
891 | |
892 fat_close_dir(dd); | |
893 dd = 0; | |
894 | |
895 if(path[length_to_sep] == '\0') | |
896 /* we iterated through the whole path and have found the file */ | |
897 return 1; | |
898 | |
899 if(dir_entry->attributes & FAT_ATTRIB_DIR) | |
900 { | |
901 /* we found a parent directory of the file we are searching for */ | |
902 path = sub_path; | |
903 break; | |
904 } | |
905 | |
906 /* a parent of the file exists, but not the file itself */ | |
907 return 0; | |
908 } | |
909 | |
910 fat_close_dir(dd); | |
911 } | |
912 | |
913 return 0; | |
914 } | |
915 | |
916 /** | |
917 * \ingroup fat_file | |
918 * Opens a file on a FAT filesystem. | |
919 * | |
920 * \param[in] fs The filesystem on which the file to open lies. | |
921 * \param[in] dir_entry The directory entry of the file to open. | |
922 * \returns The file handle, or 0 on failure. | |
923 * \see fat_close_file | |
924 */ | |
925 struct fat_file_struct* fat_open_file(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry) | |
926 { | |
927 if(!fs || !dir_entry || (dir_entry->attributes & FAT_ATTRIB_DIR)) | |
928 return 0; | |
929 | |
930 #if USE_DYNAMIC_MEMORY | |
931 struct fat_file_struct* fd = malloc(sizeof(*fd)); | |
932 if(!fd) | |
933 return 0; | |
934 #else | |
935 struct fat_file_struct* fd = fat_file_handles; | |
936 uint8_t i; | |
937 for(i = 0; i < FAT_FILE_COUNT; ++i) | |
938 { | |
939 if(!fd->fs) | |
940 break; | |
941 | |
942 ++fd; | |
943 } | |
944 if(i >= FAT_FILE_COUNT) | |
945 return 0; | |
946 #endif | |
947 | |
948 memcpy(&fd->dir_entry, dir_entry, sizeof(*dir_entry)); | |
949 fd->fs = fs; | |
950 fd->pos = 0; | |
951 fd->pos_cluster = dir_entry->cluster; | |
952 | |
953 return fd; | |
954 } | |
955 | |
956 /** | |
957 * \ingroup fat_file | |
958 * Closes a file. | |
959 * | |
960 * \param[in] fd The file handle of the file to close. | |
961 * \see fat_open_file | |
962 */ | |
963 void fat_close_file(struct fat_file_struct* fd) | |
964 { | |
965 if(fd) | |
966 { | |
967 #if FAT_DELAY_DIRENTRY_UPDATE | |
968 /* write directory entry */ | |
969 fat_write_dir_entry(fd->fs, &fd->dir_entry); | |
970 #endif | |
971 | |
972 #if USE_DYNAMIC_MEMORY | |
973 free(fd); | |
974 #else | |
975 fd->fs = 0; | |
976 #endif | |
977 } | |
978 } | |
979 | |
980 /** | |
981 * \ingroup fat_file | |
982 * Reads data from a file. | |
983 * | |
984 * The data requested is read from the current file location. | |
985 * | |
986 * \param[in] fd The file handle of the file from which to read. | |
987 * \param[out] buffer The buffer into which to write. | |
988 * \param[in] buffer_len The amount of data to read. | |
989 * \returns The number of bytes read, 0 on end of file, or -1 on failure. | |
990 * \see fat_write_file | |
991 */ | |
992 intptr_t fat_read_file(struct fat_file_struct* fd, uint8_t* buffer, uintptr_t buffer_len) | |
993 { | |
994 /* check arguments */ | |
995 if(!fd || !buffer || buffer_len < 1) | |
996 return -1; | |
997 | |
998 /* determine number of bytes to read */ | |
999 if(fd->pos + buffer_len > fd->dir_entry.file_size) | |
1000 buffer_len = fd->dir_entry.file_size - fd->pos; | |
1001 if(buffer_len == 0) | |
1002 return 0; | |
1003 | |
1004 uint16_t cluster_size = fd->fs->header.cluster_size; | |
1005 cluster_t cluster_num = fd->pos_cluster; | |
1006 uintptr_t buffer_left = buffer_len; | |
1007 uint16_t first_cluster_offset = (uint16_t) (fd->pos & (cluster_size - 1)); | |
1008 | |
1009 /* find cluster in which to start reading */ | |
1010 if(!cluster_num) | |
1011 { | |
1012 cluster_num = fd->dir_entry.cluster; | |
1013 | |
1014 if(!cluster_num) | |
1015 { | |
1016 if(!fd->pos) | |
1017 return 0; | |
1018 else | |
1019 return -1; | |
1020 } | |
1021 | |
1022 if(fd->pos) | |
1023 { | |
1024 uint32_t pos = fd->pos; | |
1025 while(pos >= cluster_size) | |
1026 { | |
1027 pos -= cluster_size; | |
1028 cluster_num = fat_get_next_cluster(fd->fs, cluster_num); | |
1029 if(!cluster_num) | |
1030 return -1; | |
1031 } | |
1032 } | |
1033 } | |
1034 | |
1035 /* read data */ | |
1036 do | |
1037 { | |
1038 /* calculate data size to copy from cluster */ | |
1039 offset_t cluster_offset = fat_cluster_offset(fd->fs, cluster_num) + first_cluster_offset; | |
1040 uint16_t copy_length = cluster_size - first_cluster_offset; | |
1041 if(copy_length > buffer_left) | |
1042 copy_length = buffer_left; | |
1043 | |
1044 /* read data */ | |
1045 if(!fd->fs->partition->device_read(cluster_offset, buffer, copy_length)) | |
1046 return buffer_len - buffer_left; | |
1047 | |
1048 /* calculate new file position */ | |
1049 buffer += copy_length; | |
1050 buffer_left -= copy_length; | |
1051 fd->pos += copy_length; | |
1052 | |
1053 if(first_cluster_offset + copy_length >= cluster_size) | |
1054 { | |
1055 /* we are on a cluster boundary, so get the next cluster */ | |
1056 if((cluster_num = fat_get_next_cluster(fd->fs, cluster_num))) | |
1057 { | |
1058 first_cluster_offset = 0; | |
1059 } | |
1060 else | |
1061 { | |
1062 fd->pos_cluster = 0; | |
1063 return buffer_len - buffer_left; | |
1064 } | |
1065 } | |
1066 | |
1067 fd->pos_cluster = cluster_num; | |
1068 | |
1069 } while(buffer_left > 0); /* check if we are done */ | |
1070 | |
1071 return buffer_len; | |
1072 } | |
1073 | |
1074 #if DOXYGEN || FAT_WRITE_SUPPORT | |
1075 /** | |
1076 * \ingroup fat_file | |
1077 * Writes data to a file. | |
1078 * | |
1079 * The data is written to the current file location. | |
1080 * | |
1081 * \param[in] fd The file handle of the file to which to write. | |
1082 * \param[in] buffer The buffer from which to read the data to be written. | |
1083 * \param[in] buffer_len The amount of data to write. | |
1084 * \returns The number of bytes written (0 or something less than \c buffer_len on disk full) or -1 on failure. | |
1085 * \see fat_read_file | |
1086 */ | |
1087 intptr_t fat_write_file(struct fat_file_struct* fd, const uint8_t* buffer, uintptr_t buffer_len) | |
1088 { | |
1089 /* check arguments */ | |
1090 if(!fd || !buffer || buffer_len < 1) | |
1091 return -1; | |
1092 if(fd->pos > fd->dir_entry.file_size) | |
1093 return -1; | |
1094 | |
1095 uint16_t cluster_size = fd->fs->header.cluster_size; | |
1096 cluster_t cluster_num = fd->pos_cluster; | |
1097 uintptr_t buffer_left = buffer_len; | |
1098 uint16_t first_cluster_offset = (uint16_t) (fd->pos & (cluster_size - 1)); | |
1099 | |
1100 /* find cluster in which to start writing */ | |
1101 if(!cluster_num) | |
1102 { | |
1103 cluster_num = fd->dir_entry.cluster; | |
1104 | |
1105 if(!cluster_num) | |
1106 { | |
1107 if(!fd->pos) | |
1108 { | |
1109 /* empty file */ | |
1110 fd->dir_entry.cluster = cluster_num = fat_append_clusters(fd->fs, 0, 1); | |
1111 if(!cluster_num) | |
1112 return 0; | |
1113 } | |
1114 else | |
1115 { | |
1116 return -1; | |
1117 } | |
1118 } | |
1119 | |
1120 if(fd->pos) | |
1121 { | |
1122 uint32_t pos = fd->pos; | |
1123 cluster_t cluster_num_next; | |
1124 while(pos >= cluster_size) | |
1125 { | |
1126 pos -= cluster_size; | |
1127 cluster_num_next = fat_get_next_cluster(fd->fs, cluster_num); | |
1128 if(!cluster_num_next) | |
1129 { | |
1130 if(pos != 0) | |
1131 return -1; /* current file position points beyond end of file */ | |
1132 | |
1133 /* the file exactly ends on a cluster boundary, and we append to it */ | |
1134 cluster_num_next = fat_append_clusters(fd->fs, cluster_num, 1); | |
1135 if(!cluster_num_next) | |
1136 return 0; | |
1137 } | |
1138 | |
1139 cluster_num = cluster_num_next; | |
1140 } | |
1141 } | |
1142 } | |
1143 | |
1144 /* write data */ | |
1145 do | |
1146 { | |
1147 /* calculate data size to write to cluster */ | |
1148 offset_t cluster_offset = fat_cluster_offset(fd->fs, cluster_num) + first_cluster_offset; | |
1149 uint16_t write_length = cluster_size - first_cluster_offset; | |
1150 if(write_length > buffer_left) | |
1151 write_length = buffer_left; | |
1152 | |
1153 /* write data which fits into the current cluster */ | |
1154 if(!fd->fs->partition->device_write(cluster_offset, buffer, write_length)) | |
1155 break; | |
1156 | |
1157 /* calculate new file position */ | |
1158 buffer += write_length; | |
1159 buffer_left -= write_length; | |
1160 fd->pos += write_length; | |
1161 | |
1162 if(first_cluster_offset + write_length >= cluster_size) | |
1163 { | |
1164 /* we are on a cluster boundary, so get the next cluster */ | |
1165 cluster_t cluster_num_next = fat_get_next_cluster(fd->fs, cluster_num); | |
1166 if(!cluster_num_next && buffer_left > 0) | |
1167 /* we reached the last cluster, append a new one */ | |
1168 cluster_num_next = fat_append_clusters(fd->fs, cluster_num, 1); | |
1169 if(!cluster_num_next) | |
1170 { | |
1171 fd->pos_cluster = 0; | |
1172 break; | |
1173 } | |
1174 | |
1175 cluster_num = cluster_num_next; | |
1176 first_cluster_offset = 0; | |
1177 } | |
1178 | |
1179 fd->pos_cluster = cluster_num; | |
1180 | |
1181 } while(buffer_left > 0); /* check if we are done */ | |
1182 | |
1183 /* update directory entry */ | |
1184 if(fd->pos > fd->dir_entry.file_size) | |
1185 { | |
1186 #if !FAT_DELAY_DIRENTRY_UPDATE | |
1187 uint32_t size_old = fd->dir_entry.file_size; | |
1188 #endif | |
1189 | |
1190 /* update file size */ | |
1191 fd->dir_entry.file_size = fd->pos; | |
1192 | |
1193 #if !FAT_DELAY_DIRENTRY_UPDATE | |
1194 /* write directory entry */ | |
1195 if(!fat_write_dir_entry(fd->fs, &fd->dir_entry)) | |
1196 { | |
1197 /* We do not return an error here since we actually wrote | |
1198 * some data to disk. So we calculate the amount of data | |
1199 * we wrote to disk and which lies within the old file size. | |
1200 */ | |
1201 buffer_left = fd->pos - size_old; | |
1202 fd->pos = size_old; | |
1203 } | |
1204 #endif | |
1205 } | |
1206 | |
1207 return buffer_len - buffer_left; | |
1208 } | |
1209 #endif | |
1210 | |
1211 /** | |
1212 * \ingroup fat_file | |
1213 * Repositions the read/write file offset. | |
1214 * | |
1215 * Changes the file offset where the next call to fat_read_file() | |
1216 * or fat_write_file() starts reading/writing. | |
1217 * | |
1218 * If the new offset is beyond the end of the file, fat_resize_file() | |
1219 * is implicitly called, i.e. the file is expanded. | |
1220 * | |
1221 * The new offset can be given in different ways determined by | |
1222 * the \c whence parameter: | |
1223 * - \b FAT_SEEK_SET: \c *offset is relative to the beginning of the file. | |
1224 * - \b FAT_SEEK_CUR: \c *offset is relative to the current file position. | |
1225 * - \b FAT_SEEK_END: \c *offset is relative to the end of the file. | |
1226 * | |
1227 * The resulting absolute offset is written to the location the \c offset | |
1228 * parameter points to. | |
1229 * | |
1230 * Calling this function can also be used to retrieve the current file position: | |
1231 \code | |
1232 int32_t file_pos = 0; | |
1233 if(!fat_seek_file(fd, &file_pos, FAT_SEEK_CUR)) | |
1234 { | |
1235 // error | |
1236 } | |
1237 // file_pos now contains the absolute file position | |
1238 \endcode | |
1239 * | |
1240 * \param[in] fd The file decriptor of the file on which to seek. | |
1241 * \param[in,out] offset A pointer to the new offset, as affected by the \c whence | |
1242 * parameter. The function writes the new absolute offset | |
1243 * to this location before it returns. | |
1244 * \param[in] whence Affects the way \c offset is interpreted, see above. | |
1245 * \returns 0 on failure, 1 on success. | |
1246 */ | |
1247 uint8_t fat_seek_file(struct fat_file_struct* fd, int32_t* offset, uint8_t whence) | |
1248 { | |
1249 if(!fd || !offset) | |
1250 return 0; | |
1251 | |
1252 uint32_t new_pos = fd->pos; | |
1253 switch(whence) | |
1254 { | |
1255 case FAT_SEEK_SET: | |
1256 new_pos = *offset; | |
1257 break; | |
1258 case FAT_SEEK_CUR: | |
1259 new_pos += *offset; | |
1260 break; | |
1261 case FAT_SEEK_END: | |
1262 new_pos = fd->dir_entry.file_size + *offset; | |
1263 break; | |
1264 default: | |
1265 return 0; | |
1266 } | |
1267 | |
1268 if(new_pos > fd->dir_entry.file_size | |
1269 #if FAT_WRITE_SUPPORT | |
1270 && !fat_resize_file(fd, new_pos) | |
1271 #endif | |
1272 ) | |
1273 return 0; | |
1274 | |
1275 fd->pos = new_pos; | |
1276 fd->pos_cluster = 0; | |
1277 | |
1278 *offset = (int32_t) new_pos; | |
1279 return 1; | |
1280 } | |
1281 | |
1282 #if DOXYGEN || FAT_WRITE_SUPPORT | |
1283 /** | |
1284 * \ingroup fat_file | |
1285 * Resizes a file to have a specific size. | |
1286 * | |
1287 * Enlarges or shrinks the file pointed to by the file descriptor to have | |
1288 * exactly the specified size. | |
1289 * | |
1290 * If the file is truncated, all bytes having an equal or larger offset | |
1291 * than the given size are lost. If the file is expanded, the additional | |
1292 * bytes are allocated. | |
1293 * | |
1294 * \note Please be aware that this function just allocates or deallocates disk | |
1295 * space, it does not explicitely clear it. To avoid data leakage, this | |
1296 * must be done manually. | |
1297 * | |
1298 * \param[in] fd The file decriptor of the file which to resize. | |
1299 * \param[in] size The new size of the file. | |
1300 * \returns 0 on failure, 1 on success. | |
1301 */ | |
1302 uint8_t fat_resize_file(struct fat_file_struct* fd, uint32_t size) | |
1303 { | |
1304 if(!fd) | |
1305 return 0; | |
1306 | |
1307 cluster_t cluster_num = fd->dir_entry.cluster; | |
1308 uint16_t cluster_size = fd->fs->header.cluster_size; | |
1309 uint32_t size_new = size; | |
1310 | |
1311 do | |
1312 { | |
1313 if(cluster_num == 0 && size_new == 0) | |
1314 /* the file stays empty */ | |
1315 break; | |
1316 | |
1317 /* seek to the next cluster as long as we need the space */ | |
1318 while(size_new > cluster_size) | |
1319 { | |
1320 /* get next cluster of file */ | |
1321 cluster_t cluster_num_next = fat_get_next_cluster(fd->fs, cluster_num); | |
1322 if(cluster_num_next) | |
1323 { | |
1324 cluster_num = cluster_num_next; | |
1325 size_new -= cluster_size; | |
1326 } | |
1327 else | |
1328 { | |
1329 break; | |
1330 } | |
1331 } | |
1332 | |
1333 if(size_new > cluster_size || cluster_num == 0) | |
1334 { | |
1335 /* Allocate new cluster chain and append | |
1336 * it to the existing one, if available. | |
1337 */ | |
1338 cluster_t cluster_count = (size_new + cluster_size - 1) / cluster_size; | |
1339 cluster_t cluster_new_chain = fat_append_clusters(fd->fs, cluster_num, cluster_count); | |
1340 if(!cluster_new_chain) | |
1341 return 0; | |
1342 | |
1343 if(!cluster_num) | |
1344 { | |
1345 cluster_num = cluster_new_chain; | |
1346 fd->dir_entry.cluster = cluster_num; | |
1347 } | |
1348 } | |
1349 | |
1350 /* write new directory entry */ | |
1351 fd->dir_entry.file_size = size; | |
1352 if(size == 0) | |
1353 fd->dir_entry.cluster = 0; | |
1354 if(!fat_write_dir_entry(fd->fs, &fd->dir_entry)) | |
1355 return 0; | |
1356 | |
1357 if(size == 0) | |
1358 { | |
1359 /* free all clusters of file */ | |
1360 fat_free_clusters(fd->fs, cluster_num); | |
1361 } | |
1362 else if(size_new <= cluster_size) | |
1363 { | |
1364 /* free all clusters no longer needed */ | |
1365 fat_terminate_clusters(fd->fs, cluster_num); | |
1366 } | |
1367 | |
1368 } while(0); | |
1369 | |
1370 /* correct file position */ | |
1371 if(size < fd->pos) | |
1372 { | |
1373 fd->pos = size; | |
1374 fd->pos_cluster = 0; | |
1375 } | |
1376 | |
1377 return 1; | |
1378 } | |
1379 #endif | |
1380 | |
1381 /** | |
1382 * \ingroup fat_dir | |
1383 * Opens a directory. | |
1384 * | |
1385 * \param[in] fs The filesystem on which the directory to open resides. | |
1386 * \param[in] dir_entry The directory entry which stands for the directory to open. | |
1387 * \returns An opaque directory descriptor on success, 0 on failure. | |
1388 * \see fat_close_dir | |
1389 */ | |
1390 struct fat_dir_struct* fat_open_dir(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry) | |
1391 { | |
1392 if(!fs || !dir_entry || !(dir_entry->attributes & FAT_ATTRIB_DIR)) | |
1393 return 0; | |
1394 | |
1395 #if USE_DYNAMIC_MEMORY | |
1396 struct fat_dir_struct* dd = malloc(sizeof(*dd)); | |
1397 if(!dd) | |
1398 return 0; | |
1399 #else | |
1400 struct fat_dir_struct* dd = fat_dir_handles; | |
1401 uint8_t i; | |
1402 for(i = 0; i < FAT_DIR_COUNT; ++i) | |
1403 { | |
1404 if(!dd->fs) | |
1405 break; | |
1406 | |
1407 ++dd; | |
1408 } | |
1409 if(i >= FAT_DIR_COUNT) | |
1410 return 0; | |
1411 #endif | |
1412 | |
1413 memcpy(&dd->dir_entry, dir_entry, sizeof(*dir_entry)); | |
1414 dd->fs = fs; | |
1415 dd->entry_cluster = dir_entry->cluster; | |
1416 dd->entry_offset = 0; | |
1417 | |
1418 return dd; | |
1419 } | |
1420 | |
1421 /** | |
1422 * \ingroup fat_dir | |
1423 * Closes a directory descriptor. | |
1424 * | |
1425 * This function destroys a directory descriptor which was | |
1426 * previously obtained by calling fat_open_dir(). When this | |
1427 * function returns, the given descriptor will be invalid. | |
1428 * | |
1429 * \param[in] dd The directory descriptor to close. | |
1430 * \see fat_open_dir | |
1431 */ | |
1432 void fat_close_dir(struct fat_dir_struct* dd) | |
1433 { | |
1434 if(dd) | |
1435 #if USE_DYNAMIC_MEMORY | |
1436 free(dd); | |
1437 #else | |
1438 dd->fs = 0; | |
1439 #endif | |
1440 } | |
1441 | |
1442 /** | |
1443 * \ingroup fat_dir | |
1444 * Reads the next directory entry contained within a parent directory. | |
1445 * | |
1446 * \param[in] dd The descriptor of the parent directory from which to read the entry. | |
1447 * \param[out] dir_entry Pointer to a buffer into which to write the directory entry information. | |
1448 * \returns 0 on failure, 1 on success. | |
1449 * \see fat_reset_dir | |
1450 */ | |
1451 uint8_t fat_read_dir(struct fat_dir_struct* dd, struct fat_dir_entry_struct* dir_entry) | |
1452 { | |
1453 if(!dd || !dir_entry) | |
1454 return 0; | |
1455 | |
1456 /* get current position of directory handle */ | |
1457 struct fat_fs_struct* fs = dd->fs; | |
1458 const struct fat_header_struct* header = &fs->header; | |
1459 uint16_t cluster_size = header->cluster_size; | |
1460 cluster_t cluster_num = dd->entry_cluster; | |
1461 uint16_t cluster_offset = dd->entry_offset; | |
1462 struct fat_read_dir_callback_arg arg; | |
1463 | |
1464 if(cluster_offset >= cluster_size) | |
1465 { | |
1466 /* The latest call hit the border of the last cluster in | |
1467 * the chain, but it still returned a directory entry. | |
1468 * So we now reset the handle and signal the caller the | |
1469 * end of the listing. | |
1470 */ | |
1471 fat_reset_dir(dd); | |
1472 return 0; | |
1473 } | |
1474 | |
1475 /* reset callback arguments */ | |
1476 memset(&arg, 0, sizeof(arg)); | |
1477 memset(dir_entry, 0, sizeof(*dir_entry)); | |
1478 arg.dir_entry = dir_entry; | |
1479 | |
1480 /* check if we read from the root directory */ | |
1481 if(cluster_num == 0) | |
1482 { | |
1483 #if FAT_FAT32_SUPPORT | |
1484 if(fs->partition->type == PARTITION_TYPE_FAT32) | |
1485 cluster_num = header->root_dir_cluster; | |
1486 else | |
1487 #endif | |
1488 cluster_size = header->cluster_zero_offset - header->root_dir_offset; | |
1489 } | |
1490 | |
1491 /* read entries */ | |
1492 uint8_t buffer[32]; | |
1493 while(!arg.finished) | |
1494 { | |
1495 /* read directory entries up to the cluster border */ | |
1496 uint16_t cluster_left = cluster_size - cluster_offset; | |
1497 offset_t pos = cluster_offset; | |
1498 if(cluster_num == 0) | |
1499 pos += header->root_dir_offset; | |
1500 else | |
1501 pos += fat_cluster_offset(fs, cluster_num); | |
1502 | |
1503 arg.bytes_read = 0; | |
1504 if(!fs->partition->device_read_interval(pos, | |
1505 buffer, | |
1506 sizeof(buffer), | |
1507 cluster_left, | |
1508 fat_dir_entry_read_callback, | |
1509 &arg) | |
1510 ) | |
1511 return 0; | |
1512 | |
1513 cluster_offset += arg.bytes_read; | |
1514 | |
1515 if(cluster_offset >= cluster_size) | |
1516 { | |
1517 /* we reached the cluster border and switch to the next cluster */ | |
1518 | |
1519 /* get number of next cluster */ | |
1520 if((cluster_num = fat_get_next_cluster(fs, cluster_num)) != 0) | |
1521 { | |
1522 cluster_offset = 0; | |
1523 continue; | |
1524 } | |
1525 | |
1526 /* we are at the end of the cluster chain */ | |
1527 if(!arg.finished) | |
1528 { | |
1529 /* directory entry not found, reset directory handle */ | |
1530 fat_reset_dir(dd); | |
1531 return 0; | |
1532 } | |
1533 else | |
1534 { | |
1535 /* The current execution of the function has been successful, | |
1536 * so we can not signal an end of the directory listing to | |
1537 * the caller, but must wait for the next call. So we keep an | |
1538 * invalid cluster offset to mark this directory handle's | |
1539 * traversal as finished. | |
1540 */ | |
1541 } | |
1542 | |
1543 break; | |
1544 } | |
1545 } | |
1546 | |
1547 dd->entry_cluster = cluster_num; | |
1548 dd->entry_offset = cluster_offset; | |
1549 | |
1550 return arg.finished; | |
1551 } | |
1552 | |
1553 /** | |
1554 * \ingroup fat_dir | |
1555 * Resets a directory handle. | |
1556 * | |
1557 * Resets the directory handle such that reading restarts | |
1558 * with the first directory entry. | |
1559 * | |
1560 * \param[in] dd The directory handle to reset. | |
1561 * \returns 0 on failure, 1 on success. | |
1562 * \see fat_read_dir | |
1563 */ | |
1564 uint8_t fat_reset_dir(struct fat_dir_struct* dd) | |
1565 { | |
1566 if(!dd) | |
1567 return 0; | |
1568 | |
1569 dd->entry_cluster = dd->dir_entry.cluster; | |
1570 dd->entry_offset = 0; | |
1571 return 1; | |
1572 } | |
1573 | |
1574 /** | |
1575 * \ingroup fat_fs | |
1576 * Callback function for reading a directory entry. | |
1577 * | |
1578 * Interprets a raw directory entry and puts the contained | |
1579 * information into a fat_dir_entry_struct structure. | |
1580 * | |
1581 * For a single file there may exist multiple directory | |
1582 * entries. All except the last one are lfn entries, which | |
1583 * contain parts of the long filename. The last directory | |
1584 * entry is a traditional 8.3 style one. It contains all | |
1585 * other information like size, cluster, date and time. | |
1586 * | |
1587 * \param[in] buffer A pointer to 32 bytes of raw data. | |
1588 * \param[in] offset The absolute offset of the raw data. | |
1589 * \param[in,out] p An argument structure controlling operation. | |
1590 * \returns 0 on failure or completion, 1 if reading has | |
1591 * to be continued | |
1592 */ | |
1593 uint8_t fat_dir_entry_read_callback(uint8_t* buffer, offset_t offset, void* p) | |
1594 { | |
1595 struct fat_read_dir_callback_arg* arg = p; | |
1596 struct fat_dir_entry_struct* dir_entry = arg->dir_entry; | |
1597 | |
1598 arg->bytes_read += 32; | |
1599 | |
1600 /* skip deleted or empty entries */ | |
1601 if(buffer[0] == FAT_DIRENTRY_DELETED || !buffer[0]) | |
1602 { | |
1603 #if FAT_LFN_SUPPORT | |
1604 arg->checksum = 0; | |
1605 #endif | |
1606 return 1; | |
1607 } | |
1608 | |
1609 #if !FAT_LFN_SUPPORT | |
1610 /* skip lfn entries */ | |
1611 if(buffer[11] == 0x0f) | |
1612 return 1; | |
1613 #endif | |
1614 | |
1615 char* long_name = dir_entry->long_name; | |
1616 #if FAT_LFN_SUPPORT | |
1617 if(buffer[11] == 0x0f) | |
1618 { | |
1619 /* checksum validation */ | |
1620 if(arg->checksum == 0 || arg->checksum != buffer[13]) | |
1621 { | |
1622 /* reset directory entry */ | |
1623 memset(dir_entry, 0, sizeof(*dir_entry)); | |
1624 | |
1625 arg->checksum = buffer[13]; | |
1626 dir_entry->entry_offset = offset; | |
1627 } | |
1628 | |
1629 /* lfn supports unicode, but we do not, for now. | |
1630 * So we assume pure ascii and read only every | |
1631 * second byte. | |
1632 */ | |
1633 uint16_t char_offset = ((buffer[0] & 0x3f) - 1) * 13; | |
1634 const uint8_t char_mapping[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; | |
1635 for(uint8_t i = 0; i <= 12 && char_offset + i < sizeof(dir_entry->long_name) - 1; ++i) | |
1636 long_name[char_offset + i] = buffer[char_mapping[i]]; | |
1637 | |
1638 return 1; | |
1639 } | |
1640 else | |
1641 #endif | |
1642 { | |
1643 #if FAT_LFN_SUPPORT | |
1644 /* if we do not have a long name or the previous lfn does not match, take the 8.3 name */ | |
1645 if(long_name[0] == '\0' || arg->checksum != fat_calc_83_checksum(buffer)) | |
1646 #endif | |
1647 { | |
1648 /* reset directory entry */ | |
1649 memset(dir_entry, 0, sizeof(*dir_entry)); | |
1650 dir_entry->entry_offset = offset; | |
1651 | |
1652 uint8_t i; | |
1653 for(i = 0; i < 8; ++i) | |
1654 { | |
1655 if(buffer[i] == ' ') | |
1656 break; | |
1657 long_name[i] = buffer[i]; | |
1658 | |
1659 /* Windows NT and later versions do not store lfn entries | |
1660 * for 8.3 names which have a lowercase basename, extension | |
1661 * or both when everything else is uppercase. They use two | |
1662 * extra bits to signal a lowercase basename or extension. | |
1663 */ | |
1664 if((buffer[12] & 0x08) && buffer[i] >= 'A' && buffer[i] <= 'Z') | |
1665 long_name[i] += 'a' - 'A'; | |
1666 } | |
1667 if(long_name[0] == 0x05) | |
1668 long_name[0] = (char) FAT_DIRENTRY_DELETED; | |
1669 | |
1670 if(buffer[8] != ' ') | |
1671 { | |
1672 long_name[i++] = '.'; | |
1673 | |
1674 uint8_t j = 8; | |
1675 for(; j < 11; ++j) | |
1676 { | |
1677 if(buffer[j] == ' ') | |
1678 break; | |
1679 long_name[i] = buffer[j]; | |
1680 | |
1681 /* See above for the lowercase 8.3 name handling of | |
1682 * Windows NT and later. | |
1683 */ | |
1684 if((buffer[12] & 0x10) && buffer[j] >= 'A' && buffer[j] <= 'Z') | |
1685 long_name[i] += 'a' - 'A'; | |
1686 | |
1687 ++i; | |
1688 } | |
1689 } | |
1690 | |
1691 long_name[i] = '\0'; | |
1692 } | |
1693 | |
1694 /* extract properties of file and store them within the structure */ | |
1695 dir_entry->attributes = buffer[11]; | |
1696 dir_entry->cluster = read16(&buffer[26]); | |
1697 #if FAT_FAT32_SUPPORT | |
1698 dir_entry->cluster |= ((cluster_t) read16(&buffer[20])) << 16; | |
1699 #endif | |
1700 dir_entry->file_size = read32(&buffer[28]); | |
1701 | |
1702 #if FAT_DATETIME_SUPPORT | |
1703 dir_entry->modification_time = read16(&buffer[22]); | |
1704 dir_entry->modification_date = read16(&buffer[24]); | |
1705 #endif | |
1706 | |
1707 arg->finished = 1; | |
1708 return 0; | |
1709 } | |
1710 } | |
1711 | |
1712 #if DOXYGEN || FAT_LFN_SUPPORT | |
1713 /** | |
1714 * \ingroup fat_fs | |
1715 * Calculates the checksum for 8.3 names used within the | |
1716 * corresponding lfn directory entries. | |
1717 * | |
1718 * \param[in] file_name_83 The 11-byte file name buffer. | |
1719 * \returns The checksum of the given file name. | |
1720 */ | |
1721 uint8_t fat_calc_83_checksum(const uint8_t* file_name_83) | |
1722 { | |
1723 uint8_t checksum = file_name_83[0]; | |
1724 for(uint8_t i = 1; i < 11; ++i) | |
1725 checksum = ((checksum >> 1) | (checksum << 7)) + file_name_83[i]; | |
1726 | |
1727 return checksum; | |
1728 } | |
1729 #endif | |
1730 | |
1731 #if DOXYGEN || FAT_WRITE_SUPPORT | |
1732 /** | |
1733 * \ingroup fat_fs | |
1734 * Searches for space where to store a directory entry. | |
1735 * | |
1736 * \param[in] fs The filesystem on which to operate. | |
1737 * \param[in] parent The directory in which to search. | |
1738 * \param[in] dir_entry The directory entry for which to search space. | |
1739 * \returns 0 on failure, a device offset on success. | |
1740 */ | |
1741 offset_t fat_find_offset_for_dir_entry(struct fat_fs_struct* fs, const struct fat_dir_struct* parent, const struct fat_dir_entry_struct* dir_entry) | |
1742 { | |
1743 if(!fs || !dir_entry) | |
1744 return 0; | |
1745 | |
1746 /* search for a place where to write the directory entry to disk */ | |
1747 #if FAT_LFN_SUPPORT | |
1748 uint8_t free_dir_entries_needed = (strlen(dir_entry->long_name) + 12) / 13 + 1; | |
1749 uint8_t free_dir_entries_found = 0; | |
1750 #endif | |
1751 cluster_t cluster_num = parent->dir_entry.cluster; | |
1752 offset_t dir_entry_offset = 0; | |
1753 offset_t offset = 0; | |
1754 offset_t offset_to = 0; | |
1755 #if FAT_FAT32_SUPPORT | |
1756 uint8_t is_fat32 = (fs->partition->type == PARTITION_TYPE_FAT32); | |
1757 #endif | |
1758 | |
1759 if(cluster_num == 0) | |
1760 { | |
1761 #if FAT_FAT32_SUPPORT | |
1762 if(is_fat32) | |
1763 { | |
1764 cluster_num = fs->header.root_dir_cluster; | |
1765 } | |
1766 else | |
1767 #endif | |
1768 { | |
1769 /* we read/write from the root directory entry */ | |
1770 offset = fs->header.root_dir_offset; | |
1771 offset_to = fs->header.cluster_zero_offset; | |
1772 dir_entry_offset = offset; | |
1773 } | |
1774 } | |
1775 | |
1776 while(1) | |
1777 { | |
1778 if(offset == offset_to) | |
1779 { | |
1780 if(cluster_num == 0) | |
1781 /* We iterated through the whole root directory and | |
1782 * could not find enough space for the directory entry. | |
1783 */ | |
1784 return 0; | |
1785 | |
1786 if(offset) | |
1787 { | |
1788 /* We reached a cluster boundary and have to | |
1789 * switch to the next cluster. | |
1790 */ | |
1791 | |
1792 cluster_t cluster_next = fat_get_next_cluster(fs, cluster_num); | |
1793 if(!cluster_next) | |
1794 { | |
1795 cluster_next = fat_append_clusters(fs, cluster_num, 1); | |
1796 if(!cluster_next) | |
1797 return 0; | |
1798 | |
1799 /* we appended a new cluster and know it is free */ | |
1800 dir_entry_offset = fs->header.cluster_zero_offset + | |
1801 (offset_t) (cluster_next - 2) * fs->header.cluster_size; | |
1802 | |
1803 /* clear cluster to avoid garbage directory entries */ | |
1804 fat_clear_cluster(fs, cluster_next); | |
1805 | |
1806 break; | |
1807 } | |
1808 cluster_num = cluster_next; | |
1809 } | |
1810 | |
1811 offset = fat_cluster_offset(fs, cluster_num); | |
1812 offset_to = offset + fs->header.cluster_size; | |
1813 dir_entry_offset = offset; | |
1814 #if FAT_LFN_SUPPORT | |
1815 free_dir_entries_found = 0; | |
1816 #endif | |
1817 } | |
1818 | |
1819 /* read next lfn or 8.3 entry */ | |
1820 uint8_t first_char; | |
1821 if(!fs->partition->device_read(offset, &first_char, sizeof(first_char))) | |
1822 return 0; | |
1823 | |
1824 /* check if we found a free directory entry */ | |
1825 if(first_char == FAT_DIRENTRY_DELETED || !first_char) | |
1826 { | |
1827 /* check if we have the needed number of available entries */ | |
1828 #if FAT_LFN_SUPPORT | |
1829 ++free_dir_entries_found; | |
1830 if(free_dir_entries_found >= free_dir_entries_needed) | |
1831 #endif | |
1832 break; | |
1833 | |
1834 offset += 32; | |
1835 } | |
1836 else | |
1837 { | |
1838 offset += 32; | |
1839 dir_entry_offset = offset; | |
1840 #if FAT_LFN_SUPPORT | |
1841 free_dir_entries_found = 0; | |
1842 #endif | |
1843 } | |
1844 } | |
1845 | |
1846 return dir_entry_offset; | |
1847 } | |
1848 #endif | |
1849 | |
1850 #if DOXYGEN || FAT_WRITE_SUPPORT | |
1851 /** | |
1852 * \ingroup fat_fs | |
1853 * Writes a directory entry to disk. | |
1854 * | |
1855 * \note The file name is not checked for invalid characters. | |
1856 * | |
1857 * \note The generation of the short 8.3 file name is quite | |
1858 * simple. The first eight characters are used for the filename. | |
1859 * The extension, if any, is made up of the first three characters | |
1860 * following the last dot within the long filename. If the | |
1861 * filename (without the extension) is longer than eight characters, | |
1862 * the lower byte of the cluster number replaces the last two | |
1863 * characters to avoid name clashes. In any other case, it is your | |
1864 * responsibility to avoid name clashes. | |
1865 * | |
1866 * \param[in] fs The filesystem on which to operate. | |
1867 * \param[in] dir_entry The directory entry to write. | |
1868 * \returns 0 on failure, 1 on success. | |
1869 */ | |
1870 uint8_t fat_write_dir_entry(const struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry) | |
1871 { | |
1872 if(!fs || !dir_entry) | |
1873 return 0; | |
1874 | |
1875 #if FAT_DATETIME_SUPPORT | |
1876 { | |
1877 uint16_t year; | |
1878 uint8_t month; | |
1879 uint8_t day; | |
1880 uint8_t hour; | |
1881 uint8_t min; | |
1882 uint8_t sec; | |
1883 | |
1884 fat_get_datetime(&year, &month, &day, &hour, &min, &sec); | |
1885 fat_set_file_modification_date(dir_entry, year, month, day); | |
1886 fat_set_file_modification_time(dir_entry, hour, min, sec); | |
1887 } | |
1888 #endif | |
1889 | |
1890 device_write_t device_write = fs->partition->device_write; | |
1891 offset_t offset = dir_entry->entry_offset; | |
1892 const char* name = dir_entry->long_name; | |
1893 uint8_t name_len = strlen(name); | |
1894 #if FAT_LFN_SUPPORT | |
1895 uint8_t lfn_entry_count = (name_len + 12) / 13; | |
1896 #endif | |
1897 uint8_t buffer[32]; | |
1898 | |
1899 /* write 8.3 entry */ | |
1900 | |
1901 /* generate 8.3 file name */ | |
1902 memset(&buffer[0], ' ', 11); | |
1903 char* name_ext = strrchr(name, '.'); | |
1904 if(name_ext && *++name_ext) | |
1905 { | |
1906 uint8_t name_ext_len = strlen(name_ext); | |
1907 name_len -= name_ext_len + 1; | |
1908 | |
1909 if(name_ext_len > 3) | |
1910 #if FAT_LFN_SUPPORT | |
1911 name_ext_len = 3; | |
1912 #else | |
1913 return 0; | |
1914 #endif | |
1915 | |
1916 memcpy(&buffer[8], name_ext, name_ext_len); | |
1917 } | |
1918 | |
1919 if(name_len <= 8) | |
1920 { | |
1921 memcpy(buffer, name, name_len); | |
1922 | |
1923 #if FAT_LFN_SUPPORT | |
1924 /* For now, we create lfn entries for all files, | |
1925 * except the "." and ".." directory references. | |
1926 * This is to avoid difficulties with capitalization, | |
1927 * as 8.3 filenames allow uppercase letters only. | |
1928 * | |
1929 * Theoretically it would be possible to leave | |
1930 * the 8.3 entry alone if the basename and the | |
1931 * extension have no mixed capitalization. | |
1932 */ | |
1933 if(name[0] == '.' && | |
1934 ((name[1] == '.' && name[2] == '\0') || | |
1935 name[1] == '\0') | |
1936 ) | |
1937 lfn_entry_count = 0; | |
1938 #endif | |
1939 } | |
1940 else | |
1941 { | |
1942 #if FAT_LFN_SUPPORT | |
1943 memcpy(buffer, name, 8); | |
1944 | |
1945 /* Minimize 8.3 name clashes by appending | |
1946 * the lower byte of the cluster number. | |
1947 */ | |
1948 uint8_t num = dir_entry->cluster & 0xff; | |
1949 | |
1950 buffer[6] = (num < 0xa0) ? ('0' + (num >> 4)) : ('a' + (num >> 4)); | |
1951 num &= 0x0f; | |
1952 buffer[7] = (num < 0x0a) ? ('0' + num) : ('a' + num); | |
1953 #else | |
1954 return 0; | |
1955 #endif | |
1956 } | |
1957 if(buffer[0] == FAT_DIRENTRY_DELETED) | |
1958 buffer[0] = 0x05; | |
1959 | |
1960 /* fill directory entry buffer */ | |
1961 memset(&buffer[11], 0, sizeof(buffer) - 11); | |
1962 buffer[0x0b] = dir_entry->attributes; | |
1963 #if FAT_DATETIME_SUPPORT | |
1964 write16(&buffer[0x16], dir_entry->modification_time); | |
1965 write16(&buffer[0x18], dir_entry->modification_date); | |
1966 #endif | |
1967 #if FAT_FAT32_SUPPORT | |
1968 write16(&buffer[0x14], (uint16_t) (dir_entry->cluster >> 16)); | |
1969 #endif | |
1970 write16(&buffer[0x1a], dir_entry->cluster); | |
1971 write32(&buffer[0x1c], dir_entry->file_size); | |
1972 | |
1973 /* write to disk */ | |
1974 #if FAT_LFN_SUPPORT | |
1975 if(!device_write(offset + (uint16_t) lfn_entry_count * 32, buffer, sizeof(buffer))) | |
1976 #else | |
1977 if(!device_write(offset, buffer, sizeof(buffer))) | |
1978 #endif | |
1979 return 0; | |
1980 | |
1981 #if FAT_LFN_SUPPORT | |
1982 /* calculate checksum of 8.3 name */ | |
1983 uint8_t checksum = fat_calc_83_checksum(buffer); | |
1984 | |
1985 /* write lfn entries */ | |
1986 for(uint8_t lfn_entry = lfn_entry_count; lfn_entry > 0; --lfn_entry) | |
1987 { | |
1988 memset(buffer, 0xff, sizeof(buffer)); | |
1989 | |
1990 /* set file name */ | |
1991 const char* long_name_curr = name + (lfn_entry - 1) * 13; | |
1992 uint8_t i = 1; | |
1993 while(i < 0x1f) | |
1994 { | |
1995 buffer[i++] = *long_name_curr; | |
1996 buffer[i++] = 0; | |
1997 | |
1998 switch(i) | |
1999 { | |
2000 case 0x0b: | |
2001 i = 0x0e; | |
2002 break; | |
2003 case 0x1a: | |
2004 i = 0x1c; | |
2005 break; | |
2006 } | |
2007 | |
2008 if(!*long_name_curr++) | |
2009 break; | |
2010 } | |
2011 | |
2012 /* set index of lfn entry */ | |
2013 buffer[0x00] = lfn_entry; | |
2014 if(lfn_entry == lfn_entry_count) | |
2015 buffer[0x00] |= FAT_DIRENTRY_LFNLAST; | |
2016 | |
2017 /* mark as lfn entry */ | |
2018 buffer[0x0b] = 0x0f; | |
2019 | |
2020 /* set 8.3 checksum */ | |
2021 buffer[0x0d] = checksum; | |
2022 | |
2023 /* clear reserved bytes */ | |
2024 buffer[0x0c] = 0; | |
2025 buffer[0x1a] = 0; | |
2026 buffer[0x1b] = 0; | |
2027 | |
2028 /* write entry */ | |
2029 device_write(offset, buffer, sizeof(buffer)); | |
2030 | |
2031 offset += sizeof(buffer); | |
2032 } | |
2033 #endif | |
2034 | |
2035 return 1; | |
2036 } | |
2037 #endif | |
2038 | |
2039 #if DOXYGEN || FAT_WRITE_SUPPORT | |
2040 /** | |
2041 * \ingroup fat_file | |
2042 * Creates a file. | |
2043 * | |
2044 * Creates a file and obtains the directory entry of the | |
2045 * new file. If the file to create already exists, the | |
2046 * directory entry of the existing file will be returned | |
2047 * within the dir_entry parameter. | |
2048 * | |
2049 * \note The file name is not checked for invalid characters. | |
2050 * | |
2051 * \note The generation of the short 8.3 file name is quite | |
2052 * simple. The first eight characters are used for the filename. | |
2053 * The extension, if any, is made up of the first three characters | |
2054 * following the last dot within the long filename. If the | |
2055 * filename (without the extension) is longer than eight characters, | |
2056 * the lower byte of the cluster number replaces the last two | |
2057 * characters to avoid name clashes. In any other case, it is your | |
2058 * responsibility to avoid name clashes. | |
2059 * | |
2060 * \param[in] parent The handle of the directory in which to create the file. | |
2061 * \param[in] file The name of the file to create. | |
2062 * \param[out] dir_entry The directory entry to fill for the new (or existing) file. | |
2063 * \returns 0 on failure, 1 on success, 2 if the file already existed. | |
2064 * \see fat_delete_file | |
2065 */ | |
2066 uint8_t fat_create_file(struct fat_dir_struct* parent, const char* file, struct fat_dir_entry_struct* dir_entry) | |
2067 { | |
2068 if(!parent || !file || !file[0] || !dir_entry) | |
2069 return 0; | |
2070 | |
2071 /* check if the file already exists */ | |
2072 while(1) | |
2073 { | |
2074 if(!fat_read_dir(parent, dir_entry)) | |
2075 break; | |
2076 | |
2077 if(strcmp(file, dir_entry->long_name) == 0) | |
2078 { | |
2079 fat_reset_dir(parent); | |
2080 return 2; | |
2081 } | |
2082 } | |
2083 | |
2084 struct fat_fs_struct* fs = parent->fs; | |
2085 | |
2086 /* prepare directory entry with values already known */ | |
2087 memset(dir_entry, 0, sizeof(*dir_entry)); | |
2088 strncpy(dir_entry->long_name, file, sizeof(dir_entry->long_name) - 1); | |
2089 | |
2090 /* find place where to store directory entry */ | |
2091 if(!(dir_entry->entry_offset = fat_find_offset_for_dir_entry(fs, parent, dir_entry))) | |
2092 return 0; | |
2093 | |
2094 /* write directory entry to disk */ | |
2095 if(!fat_write_dir_entry(fs, dir_entry)) | |
2096 return 0; | |
2097 | |
2098 return 1; | |
2099 } | |
2100 #endif | |
2101 | |
2102 #if DOXYGEN || FAT_WRITE_SUPPORT | |
2103 /** | |
2104 * \ingroup fat_file | |
2105 * Deletes a file or directory. | |
2106 * | |
2107 * If a directory is deleted without first deleting its | |
2108 * subdirectories and files, disk space occupied by these | |
2109 * files will get wasted as there is no chance to release | |
2110 * it and mark it as free. | |
2111 * | |
2112 * \param[in] fs The filesystem on which to operate. | |
2113 * \param[in] dir_entry The directory entry of the file to delete. | |
2114 * \returns 0 on failure, 1 on success. | |
2115 * \see fat_create_file | |
2116 */ | |
2117 uint8_t fat_delete_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry) | |
2118 { | |
2119 if(!fs || !dir_entry) | |
2120 return 0; | |
2121 | |
2122 /* get offset of the file's directory entry */ | |
2123 offset_t dir_entry_offset = dir_entry->entry_offset; | |
2124 if(!dir_entry_offset) | |
2125 return 0; | |
2126 | |
2127 #if FAT_LFN_SUPPORT | |
2128 uint8_t buffer[12]; | |
2129 while(1) | |
2130 { | |
2131 /* read directory entry */ | |
2132 if(!fs->partition->device_read(dir_entry_offset, buffer, sizeof(buffer))) | |
2133 return 0; | |
2134 | |
2135 /* mark the directory entry as deleted */ | |
2136 buffer[0] = FAT_DIRENTRY_DELETED; | |
2137 | |
2138 /* write back entry */ | |
2139 if(!fs->partition->device_write(dir_entry_offset, buffer, sizeof(buffer))) | |
2140 return 0; | |
2141 | |
2142 /* check if we deleted the whole entry */ | |
2143 if(buffer[11] != 0x0f) | |
2144 break; | |
2145 | |
2146 dir_entry_offset += 32; | |
2147 } | |
2148 #else | |
2149 /* mark the directory entry as deleted */ | |
2150 uint8_t first_char = FAT_DIRENTRY_DELETED; | |
2151 if(!fs->partition->device_write(dir_entry_offset, &first_char, 1)) | |
2152 return 0; | |
2153 #endif | |
2154 | |
2155 /* We deleted the directory entry. The next thing to do is | |
2156 * marking all occupied clusters as free. | |
2157 */ | |
2158 return (dir_entry->cluster == 0 || fat_free_clusters(fs, dir_entry->cluster)); | |
2159 } | |
2160 #endif | |
2161 | |
2162 #if DOXYGEN || FAT_WRITE_SUPPORT | |
2163 /** | |
2164 * \ingroup fat_file | |
2165 * Moves or renames a file. | |
2166 * | |
2167 * Changes a file's name, optionally moving it into another | |
2168 * directory as well. Before calling this function, the | |
2169 * target file name must not exist. Moving a file to a | |
2170 * different filesystem (i.e. \a parent_new doesn't lie on | |
2171 * \a fs) is not supported. | |
2172 * | |
2173 * After successfully renaming (and moving) the file, the | |
2174 * given directory entry is updated such that it points to | |
2175 * the file's new location. | |
2176 * | |
2177 * \note The notes which apply to fat_create_file() also | |
2178 * apply to this function. | |
2179 * | |
2180 * \param[in] fs The filesystem on which to operate. | |
2181 * \param[in,out] dir_entry The directory entry of the file to move. | |
2182 * \param[in] parent_new The handle of the new parent directory of the file. | |
2183 * \param[in] file_new The file's new name. | |
2184 * \returns 0 on failure, 1 on success. | |
2185 * \see fat_create_file, fat_delete_file, fat_move_dir | |
2186 */ | |
2187 uint8_t fat_move_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry, struct fat_dir_struct* parent_new, const char* file_new) | |
2188 { | |
2189 if(!fs || !dir_entry || !parent_new || (file_new && !file_new[0])) | |
2190 return 0; | |
2191 if(fs != parent_new->fs) | |
2192 return 0; | |
2193 | |
2194 /* use existing file name if none has been specified */ | |
2195 if(!file_new) | |
2196 file_new = dir_entry->long_name; | |
2197 | |
2198 /* create file with new file name */ | |
2199 struct fat_dir_entry_struct dir_entry_new; | |
2200 if(!fat_create_file(parent_new, file_new, &dir_entry_new)) | |
2201 return 0; | |
2202 | |
2203 /* copy members of directory entry which do not change with rename */ | |
2204 dir_entry_new.attributes = dir_entry->attributes; | |
2205 #if FAT_DATETIME_SUPPORT | |
2206 dir_entry_new.modification_time = dir_entry->modification_time; | |
2207 dir_entry_new.modification_date = dir_entry->modification_date; | |
2208 #endif | |
2209 dir_entry_new.cluster = dir_entry->cluster; | |
2210 dir_entry_new.file_size = dir_entry->file_size; | |
2211 | |
2212 /* make the new file name point to the old file's content */ | |
2213 if(!fat_write_dir_entry(fs, &dir_entry_new)) | |
2214 { | |
2215 fat_delete_file(fs, &dir_entry_new); | |
2216 return 0; | |
2217 } | |
2218 | |
2219 /* delete the old file, but not its clusters, which have already been remapped above */ | |
2220 dir_entry->cluster = 0; | |
2221 if(!fat_delete_file(fs, dir_entry)) | |
2222 return 0; | |
2223 | |
2224 *dir_entry = dir_entry_new; | |
2225 return 1; | |
2226 } | |
2227 #endif | |
2228 | |
2229 #if DOXYGEN || FAT_WRITE_SUPPORT | |
2230 /** | |
2231 * \ingroup fat_dir | |
2232 * Creates a directory. | |
2233 * | |
2234 * Creates a directory and obtains its directory entry. | |
2235 * If the directory to create already exists, its | |
2236 * directory entry will be returned within the dir_entry | |
2237 * parameter. | |
2238 * | |
2239 * \note The notes which apply to fat_create_file() also | |
2240 * apply to this function. | |
2241 * | |
2242 * \param[in] parent The handle of the parent directory of the new directory. | |
2243 * \param[in] dir The name of the directory to create. | |
2244 * \param[out] dir_entry The directory entry to fill for the new directory. | |
2245 * \returns 0 on failure, 1 on success. | |
2246 * \see fat_delete_dir | |
2247 */ | |
2248 uint8_t fat_create_dir(struct fat_dir_struct* parent, const char* dir, struct fat_dir_entry_struct* dir_entry) | |
2249 { | |
2250 if(!parent || !dir || !dir[0] || !dir_entry) | |
2251 return 0; | |
2252 | |
2253 /* check if the file or directory already exists */ | |
2254 while(fat_read_dir(parent, dir_entry)) | |
2255 { | |
2256 if(strcmp(dir, dir_entry->long_name) == 0) | |
2257 { | |
2258 fat_reset_dir(parent); | |
2259 return 0; | |
2260 } | |
2261 } | |
2262 | |
2263 struct fat_fs_struct* fs = parent->fs; | |
2264 | |
2265 /* allocate cluster which will hold directory entries */ | |
2266 cluster_t dir_cluster = fat_append_clusters(fs, 0, 1); | |
2267 if(!dir_cluster) | |
2268 return 0; | |
2269 | |
2270 /* clear cluster to prevent bogus directory entries */ | |
2271 fat_clear_cluster(fs, dir_cluster); | |
2272 | |
2273 memset(dir_entry, 0, sizeof(*dir_entry)); | |
2274 dir_entry->attributes = FAT_ATTRIB_DIR; | |
2275 | |
2276 /* create "." directory self reference */ | |
2277 dir_entry->entry_offset = fs->header.cluster_zero_offset + | |
2278 (offset_t) (dir_cluster - 2) * fs->header.cluster_size; | |
2279 dir_entry->long_name[0] = '.'; | |
2280 dir_entry->cluster = dir_cluster; | |
2281 if(!fat_write_dir_entry(fs, dir_entry)) | |
2282 { | |
2283 fat_free_clusters(fs, dir_cluster); | |
2284 return 0; | |
2285 } | |
2286 | |
2287 /* create ".." parent directory reference */ | |
2288 dir_entry->entry_offset += 32; | |
2289 dir_entry->long_name[1] = '.'; | |
2290 dir_entry->cluster = parent->dir_entry.cluster; | |
2291 if(!fat_write_dir_entry(fs, dir_entry)) | |
2292 { | |
2293 fat_free_clusters(fs, dir_cluster); | |
2294 return 0; | |
2295 } | |
2296 | |
2297 /* fill directory entry */ | |
2298 strncpy(dir_entry->long_name, dir, sizeof(dir_entry->long_name) - 1); | |
2299 dir_entry->cluster = dir_cluster; | |
2300 | |
2301 /* find place where to store directory entry */ | |
2302 if(!(dir_entry->entry_offset = fat_find_offset_for_dir_entry(fs, parent, dir_entry))) | |
2303 { | |
2304 fat_free_clusters(fs, dir_cluster); | |
2305 return 0; | |
2306 } | |
2307 | |
2308 /* write directory to disk */ | |
2309 if(!fat_write_dir_entry(fs, dir_entry)) | |
2310 { | |
2311 fat_free_clusters(fs, dir_cluster); | |
2312 return 0; | |
2313 } | |
2314 | |
2315 return 1; | |
2316 } | |
2317 #endif | |
2318 | |
2319 /** | |
2320 * \ingroup fat_dir | |
2321 * Deletes a directory. | |
2322 * | |
2323 * This is just a synonym for fat_delete_file(). | |
2324 * If a directory is deleted without first deleting its | |
2325 * subdirectories and files, disk space occupied by these | |
2326 * files will get wasted as there is no chance to release | |
2327 * it and mark it as free. | |
2328 * | |
2329 * \param[in] fs The filesystem on which to operate. | |
2330 * \param[in] dir_entry The directory entry of the directory to delete. | |
2331 * \returns 0 on failure, 1 on success. | |
2332 * \see fat_create_dir | |
2333 */ | |
2334 #ifdef DOXYGEN | |
2335 uint8_t fat_delete_dir(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry); | |
2336 #endif | |
2337 | |
2338 /** | |
2339 * \ingroup fat_dir | |
2340 * Moves or renames a directory. | |
2341 * | |
2342 * This is just a synonym for fat_move_file(). | |
2343 * | |
2344 * \param[in] fs The filesystem on which to operate. | |
2345 * \param[in,out] dir_entry The directory entry of the directory to move. | |
2346 * \param[in] parent_new The handle of the new parent directory. | |
2347 * \param[in] dir_new The directory's new name. | |
2348 * \returns 0 on failure, 1 on success. | |
2349 * \see fat_create_dir, fat_delete_dir, fat_move_file | |
2350 */ | |
2351 #ifdef DOXYGEN | |
2352 uint8_t fat_move_dir(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry, struct fat_dir_struct* parent_new, const char* dir_new); | |
2353 #endif | |
2354 | |
2355 #if DOXYGEN || FAT_DATETIME_SUPPORT | |
2356 /** | |
2357 * \ingroup fat_file | |
2358 * Returns the modification date of a file. | |
2359 * | |
2360 * \param[in] dir_entry The directory entry of which to return the modification date. | |
2361 * \param[out] year The year the file was last modified. | |
2362 * \param[out] month The month the file was last modified. | |
2363 * \param[out] day The day the file was last modified. | |
2364 */ | |
2365 void fat_get_file_modification_date(const struct fat_dir_entry_struct* dir_entry, uint16_t* year, uint8_t* month, uint8_t* day) | |
2366 { | |
2367 if(!dir_entry) | |
2368 return; | |
2369 | |
2370 *year = 1980 + ((dir_entry->modification_date >> 9) & 0x7f); | |
2371 *month = (dir_entry->modification_date >> 5) & 0x0f; | |
2372 *day = (dir_entry->modification_date >> 0) & 0x1f; | |
2373 } | |
2374 #endif | |
2375 | |
2376 #if DOXYGEN || FAT_DATETIME_SUPPORT | |
2377 /** | |
2378 * \ingroup fat_file | |
2379 * Returns the modification time of a file. | |
2380 * | |
2381 * \param[in] dir_entry The directory entry of which to return the modification time. | |
2382 * \param[out] hour The hour the file was last modified. | |
2383 * \param[out] min The min the file was last modified. | |
2384 * \param[out] sec The sec the file was last modified. | |
2385 */ | |
2386 void fat_get_file_modification_time(const struct fat_dir_entry_struct* dir_entry, uint8_t* hour, uint8_t* min, uint8_t* sec) | |
2387 { | |
2388 if(!dir_entry) | |
2389 return; | |
2390 | |
2391 *hour = (dir_entry->modification_time >> 11) & 0x1f; | |
2392 *min = (dir_entry->modification_time >> 5) & 0x3f; | |
2393 *sec = ((dir_entry->modification_time >> 0) & 0x1f) * 2; | |
2394 } | |
2395 #endif | |
2396 | |
2397 #if DOXYGEN || (FAT_WRITE_SUPPORT && FAT_DATETIME_SUPPORT) | |
2398 /** | |
2399 * \ingroup fat_file | |
2400 * Sets the modification time of a date. | |
2401 * | |
2402 * \param[in] dir_entry The directory entry for which to set the modification date. | |
2403 * \param[in] year The year the file was last modified. | |
2404 * \param[in] month The month the file was last modified. | |
2405 * \param[in] day The day the file was last modified. | |
2406 */ | |
2407 void fat_set_file_modification_date(struct fat_dir_entry_struct* dir_entry, uint16_t year, uint8_t month, uint8_t day) | |
2408 { | |
2409 if(!dir_entry) | |
2410 return; | |
2411 | |
2412 dir_entry->modification_date = | |
2413 ((year - 1980) << 9) | | |
2414 ((uint16_t) month << 5) | | |
2415 ((uint16_t) day << 0); | |
2416 } | |
2417 #endif | |
2418 | |
2419 #if DOXYGEN || (FAT_WRITE_SUPPORT && FAT_DATETIME_SUPPORT) | |
2420 /** | |
2421 * \ingroup fat_file | |
2422 * Sets the modification time of a file. | |
2423 * | |
2424 * \param[in] dir_entry The directory entry for which to set the modification time. | |
2425 * \param[in] hour The year the file was last modified. | |
2426 * \param[in] min The month the file was last modified. | |
2427 * \param[in] sec The day the file was last modified. | |
2428 */ | |
2429 void fat_set_file_modification_time(struct fat_dir_entry_struct* dir_entry, uint8_t hour, uint8_t min, uint8_t sec) | |
2430 { | |
2431 if(!dir_entry) | |
2432 return; | |
2433 | |
2434 dir_entry->modification_time = | |
2435 ((uint16_t) hour << 11) | | |
2436 ((uint16_t) min << 5) | | |
2437 ((uint16_t) sec >> 1) ; | |
2438 } | |
2439 #endif | |
2440 | |
2441 /** | |
2442 * \ingroup fat_fs | |
2443 * Returns the amount of total storage capacity of the filesystem in bytes. | |
2444 * | |
2445 * \param[in] fs The filesystem on which to operate. | |
2446 * \returns 0 on failure, the filesystem size in bytes otherwise. | |
2447 */ | |
2448 offset_t fat_get_fs_size(const struct fat_fs_struct* fs) | |
2449 { | |
2450 if(!fs) | |
2451 return 0; | |
2452 | |
2453 #if FAT_FAT32_SUPPORT | |
2454 if(fs->partition->type == PARTITION_TYPE_FAT32) | |
2455 return (offset_t) (fs->header.fat_size / 4 - 2) * fs->header.cluster_size; | |
2456 else | |
2457 #endif | |
2458 return (offset_t) (fs->header.fat_size / 2 - 2) * fs->header.cluster_size; | |
2459 } | |
2460 | |
2461 /** | |
2462 * \ingroup fat_fs | |
2463 * Returns the amount of free storage capacity on the filesystem in bytes. | |
2464 * | |
2465 * \note As the FAT filesystem is cluster based, this function does not | |
2466 * return continuous values but multiples of the cluster size. | |
2467 * | |
2468 * \param[in] fs The filesystem on which to operate. | |
2469 * \returns 0 on failure, the free filesystem space in bytes otherwise. | |
2470 */ | |
2471 offset_t fat_get_fs_free(const struct fat_fs_struct* fs) | |
2472 { | |
2473 if(!fs) | |
2474 return 0; | |
2475 | |
2476 uint8_t fat[32]; | |
2477 struct fat_usage_count_callback_arg count_arg; | |
2478 count_arg.cluster_count = 0; | |
2479 count_arg.buffer_size = sizeof(fat); | |
2480 | |
2481 offset_t fat_offset = fs->header.fat_offset; | |
2482 uint32_t fat_size = fs->header.fat_size; | |
2483 while(fat_size > 0) | |
2484 { | |
2485 uintptr_t length = UINTPTR_MAX - 1; | |
2486 if(fat_size < length) | |
2487 length = fat_size; | |
2488 | |
2489 if(!fs->partition->device_read_interval(fat_offset, | |
2490 fat, | |
2491 sizeof(fat), | |
2492 length, | |
2493 #if FAT_FAT32_SUPPORT | |
2494 (fs->partition->type == PARTITION_TYPE_FAT16) ? | |
2495 fat_get_fs_free_16_callback : | |
2496 fat_get_fs_free_32_callback, | |
2497 #else | |
2498 fat_get_fs_free_16_callback, | |
2499 #endif | |
2500 &count_arg | |
2501 ) | |
2502 ) | |
2503 return 0; | |
2504 | |
2505 fat_offset += length; | |
2506 fat_size -= length; | |
2507 } | |
2508 | |
2509 return (offset_t) count_arg.cluster_count * fs->header.cluster_size; | |
2510 } | |
2511 | |
2512 /** | |
2513 * \ingroup fat_fs | |
2514 * Callback function used for counting free clusters in a FAT. | |
2515 */ | |
2516 uint8_t fat_get_fs_free_16_callback(uint8_t* buffer, offset_t offset, void* p) | |
2517 { | |
2518 struct fat_usage_count_callback_arg* count_arg = (struct fat_usage_count_callback_arg*) p; | |
2519 uintptr_t buffer_size = count_arg->buffer_size; | |
2520 | |
2521 for(uintptr_t i = 0; i < buffer_size; i += 2, buffer += 2) | |
2522 { | |
2523 uint16_t cluster = read16(buffer); | |
2524 if(cluster == HTOL16(FAT16_CLUSTER_FREE)) | |
2525 ++(count_arg->cluster_count); | |
2526 } | |
2527 | |
2528 return 1; | |
2529 } | |
2530 | |
2531 #if DOXYGEN || FAT_FAT32_SUPPORT | |
2532 /** | |
2533 * \ingroup fat_fs | |
2534 * Callback function used for counting free clusters in a FAT32. | |
2535 */ | |
2536 uint8_t fat_get_fs_free_32_callback(uint8_t* buffer, offset_t offset, void* p) | |
2537 { | |
2538 struct fat_usage_count_callback_arg* count_arg = (struct fat_usage_count_callback_arg*) p; | |
2539 uintptr_t buffer_size = count_arg->buffer_size; | |
2540 | |
2541 for(uintptr_t i = 0; i < buffer_size; i += 4, buffer += 4) | |
2542 { | |
2543 uint32_t cluster = read32(buffer); | |
2544 if(cluster == HTOL32(FAT32_CLUSTER_FREE)) | |
2545 ++(count_arg->cluster_count); | |
2546 } | |
2547 | |
2548 return 1; | |
2549 } | |
2550 #endif | |
2551 |