|
Functions | |
| struct fat_fs_struct * | fat_open (struct partition_struct *partition) |
| void | fat_close (struct fat_fs_struct *fs) |
| static uint8_t | fat_read_header (struct fat_fs_struct *fs) |
| static cluster_t | fat_get_next_cluster (const struct fat_fs_struct *fs, cluster_t cluster_num) |
| static cluster_t | fat_append_clusters (struct fat_fs_struct *fs, cluster_t cluster_num, cluster_t count) |
| static uint8_t | fat_free_clusters (struct fat_fs_struct *fs, cluster_t cluster_num) |
| static uint8_t | fat_terminate_clusters (struct fat_fs_struct *fs, cluster_t cluster_num) |
| static uint8_t | fat_clear_cluster (const struct fat_fs_struct *fs, cluster_t cluster_num) |
| static uintptr_t | fat_clear_cluster_callback (uint8_t *buffer, offset_t offset, void *p) |
| static offset_t | fat_cluster_offset (const struct fat_fs_struct *fs, cluster_t cluster_num) |
| static uint8_t | fat_dir_entry_read_callback (uint8_t *buffer, offset_t offset, void *p) |
| static uint8_t | fat_calc_83_checksum (const uint8_t *file_name_83) |
| 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) |
| static uint8_t | fat_write_dir_entry (const struct fat_fs_struct *fs, struct fat_dir_entry_struct *dir_entry) |
| offset_t | fat_get_fs_size (const struct fat_fs_struct *fs) |
| offset_t | fat_get_fs_free (const struct fat_fs_struct *fs) |
| static uint8_t | fat_get_fs_free_16_callback (uint8_t *buffer, offset_t offset, void *p) |
| cluster_t fat_append_clusters | ( | struct fat_fs_struct * | fs, | |
| cluster_t | cluster_num, | |||
| cluster_t | count | |||
| ) | [static] |
Appends a new cluster chain to an existing one.
Set cluster_num to zero to create a completely new one.
| [in] | fs | The file system on which to operate. |
| [in] | cluster_num | The cluster to which to append the new chain. |
| [in] | count | The number of clusters to allocate. |
Definition at line 493 of file fat.c.
References fat_fs_struct::cluster_free, partition_struct::device_read, partition_struct::device_write, FAT16_CLUSTER_FREE, FAT16_CLUSTER_LAST_MAX, FAT32_CLUSTER_FREE, FAT32_CLUSTER_LAST_MAX, fat_free_clusters(), fat_header_struct::fat_offset, fat_header_struct::fat_size, fat_fs_struct::header, htol16(), htol32(), fat_fs_struct::partition, PARTITION_TYPE_FAT32, and partition_struct::type.
Referenced by fat_create_dir(), fat_find_offset_for_dir_entry(), fat_resize_file(), and fat_write_file().
00494 { 00495 if(!fs) 00496 return 0; 00497 00498 device_read_t device_read = fs->partition->device_read; 00499 device_write_t device_write = fs->partition->device_write; 00500 offset_t fat_offset = fs->header.fat_offset; 00501 cluster_t count_left = count; 00502 cluster_t cluster_current = fs->cluster_free; 00503 cluster_t cluster_next = 0; 00504 cluster_t cluster_count; 00505 uint16_t fat_entry16; 00506 #if FAT_FAT32_SUPPORT 00507 uint32_t fat_entry32; 00508 uint8_t is_fat32 = (fs->partition->type == PARTITION_TYPE_FAT32); 00509 00510 if(is_fat32) 00511 cluster_count = fs->header.fat_size / sizeof(fat_entry32); 00512 else 00513 #endif 00514 cluster_count = fs->header.fat_size / sizeof(fat_entry16); 00515 00516 fs->cluster_free = 0; 00517 cluster_t cluster_left = cluster_count; 00518 for( cluster_left = cluster_count; cluster_left > 0; --cluster_left, ++cluster_current) 00519 { 00520 if(cluster_current < 2 || cluster_current >= cluster_count) 00521 cluster_current = 2; 00522 00523 #if FAT_FAT32_SUPPORT 00524 if(is_fat32) 00525 { 00526 if(!device_read(fat_offset + cluster_current * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32))) 00527 return 0; 00528 } 00529 else 00530 #endif 00531 { 00532 if(!device_read(fat_offset + cluster_current * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16))) 00533 return 0; 00534 } 00535 00536 #if FAT_FAT32_SUPPORT 00537 if(is_fat32) 00538 { 00539 /* check if this is a free cluster */ 00540 if(fat_entry32 != HTOL32(FAT32_CLUSTER_FREE)) 00541 continue; 00542 00543 /* If we don't need this free cluster for the 00544 * current allocation, we keep it in mind for 00545 * the next time. 00546 */ 00547 if(count_left == 0) 00548 { 00549 fs->cluster_free = cluster_current; 00550 break; 00551 } 00552 00553 /* allocate cluster */ 00554 if(cluster_next == 0) 00555 fat_entry32 = HTOL32(FAT32_CLUSTER_LAST_MAX); 00556 else 00557 fat_entry32 = htol32(cluster_next); 00558 00559 if(!device_write(fat_offset + cluster_current * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32))) 00560 break; 00561 } 00562 else 00563 #endif 00564 { 00565 /* check if this is a free cluster */ 00566 if(fat_entry16 != HTOL16(FAT16_CLUSTER_FREE)) 00567 continue; 00568 00569 /* If we don't need this free cluster for the 00570 * current allocation, we keep it in mind for 00571 * the next time. 00572 */ 00573 if(count_left == 0) 00574 { 00575 fs->cluster_free = cluster_current; 00576 break; 00577 } 00578 00579 /* allocate cluster */ 00580 if(cluster_next == 0) 00581 fat_entry16 = HTOL16(FAT16_CLUSTER_LAST_MAX); 00582 else 00583 fat_entry16 = htol16((uint16_t) cluster_next); 00584 00585 if(!device_write(fat_offset + cluster_current * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16))) 00586 break; 00587 } 00588 00589 cluster_next = cluster_current; 00590 --count_left; 00591 } 00592 00593 do 00594 { 00595 if(count_left > 0) 00596 break; 00597 00598 /* We allocated a new cluster chain. Now join 00599 * it with the existing one (if any). 00600 */ 00601 if(cluster_num >= 2) 00602 { 00603 #if FAT_FAT32_SUPPORT 00604 if(is_fat32) 00605 { 00606 fat_entry32 = htol32(cluster_next); 00607 00608 if(!device_write(fat_offset + cluster_num * sizeof(fat_entry32), (uint8_t*) &fat_entry32, sizeof(fat_entry32))) 00609 break; 00610 } 00611 else 00612 #endif 00613 { 00614 fat_entry16 = htol16((uint16_t) cluster_next); 00615 00616 if(!device_write(fat_offset + cluster_num * sizeof(fat_entry16), (uint8_t*) &fat_entry16, sizeof(fat_entry16))) 00617 break; 00618 } 00619 } 00620 00621 return cluster_next; 00622 00623 } while(0); 00624 00625 /* No space left on device or writing error. 00626 * Free up all clusters already allocated. 00627 */ 00628 fat_free_clusters(fs, cluster_next); 00629 00630 return 0; 00631 }


| uint8_t fat_calc_83_checksum | ( | const uint8_t * | file_name_83 | ) | [static] |
Calculates the checksum for 8.3 names used within the corresponding lfn directory entries.
| [in] | file_name_83 | The 11-byte file name buffer. |
Definition at line 1708 of file fat.c.
Referenced by fat_dir_entry_read_callback(), and fat_write_dir_entry().
01709 { 01710 uint8_t checksum = file_name_83[0]; 01711 uint16_t i=0; 01712 for( i = 1; i < 11; ++i) 01713 checksum = ((checksum >> 1) | (checksum << 7)) + file_name_83[i]; 01714 01715 return checksum; 01716 }

| uint8_t fat_clear_cluster | ( | const struct fat_fs_struct * | fs, | |
| cluster_t | cluster_num | |||
| ) | [static] |
Clears a single cluster.
The complete cluster is filled with zeros.
| [in] | fs | The filesystem on which to operate. |
| [in] | cluster_num | The cluster to clear. |
Definition at line 793 of file fat.c.
References fat_header_struct::cluster_size, partition_struct::device_write_interval, fat_clear_cluster_callback(), fat_cluster_offset(), fat_fs_struct::header, and fat_fs_struct::partition.
Referenced by fat_create_dir(), and fat_find_offset_for_dir_entry().
00794 { 00795 if(cluster_num < 2) 00796 return 0; 00797 00798 offset_t cluster_offset = fat_cluster_offset(fs, cluster_num); 00799 00800 uint8_t zero[16]; 00801 memset(zero, 0, sizeof(zero)); 00802 return fs->partition->device_write_interval(cluster_offset, 00803 zero, 00804 fs->header.cluster_size, 00805 fat_clear_cluster_callback, 00806 0 00807 ); 00808 }


| uintptr_t fat_clear_cluster_callback | ( | uint8_t * | buffer, | |
| offset_t | offset, | |||
| void * | p | |||
| ) | [static] |
Callback function for clearing a cluster.
Definition at line 816 of file fat.c.
Referenced by fat_clear_cluster().

| void fat_close | ( | struct fat_fs_struct * | fs | ) |
Closes a FAT filesystem.
When this function returns, the given filesystem descriptor will be invalid.
| [in] | fs | The filesystem to close. |
Definition at line 291 of file fat.c.
References fat_fs_struct::partition.
Referenced by WaspSD::close().
00292 { 00293 if(!fs) 00294 return; 00295 00296 #if USE_DYNAMIC_MEMORY 00297 free(fs); 00298 #else 00299 fs->partition = 0; 00300 #endif 00301 }

| offset_t fat_cluster_offset | ( | const struct fat_fs_struct * | fs, | |
| cluster_t | cluster_num | |||
| ) | [static] |
Calculates the offset of the specified cluster.
| [in] | fs | The filesystem on which to operate. |
| [in] | cluster_num | The cluster whose offset to calculate. |
Definition at line 830 of file fat.c.
References fat_header_struct::cluster_size, fat_header_struct::cluster_zero_offset, and fat_fs_struct::header.
Referenced by fat_clear_cluster(), fat_find_offset_for_dir_entry(), fat_read_dir(), fat_read_file(), and fat_write_file().
00831 { 00832 if(!fs || cluster_num < 2) 00833 return 0; 00834 00835 return fs->header.cluster_zero_offset + (offset_t) (cluster_num - 2) * fs->header.cluster_size; 00836 }

| uint8_t fat_dir_entry_read_callback | ( | uint8_t * | buffer, | |
| offset_t | offset, | |||
| void * | p | |||
| ) | [static] |
Callback function for reading a directory entry.
Interprets a raw directory entry and puts the contained information into a fat_dir_entry_struct structure.
For a single file there may exist multiple directory entries. All except the last one are lfn entries, which contain parts of the long filename. The last directory entry is a traditional 8.3 style one. It contains all other information like size, cluster, date and time.
| [in] | buffer | A pointer to 32 bytes of raw data. |
| [in] | offset | The absolute offset of the raw data. |
| [in,out] | p | An argument structure controlling operation. |
Definition at line 1579 of file fat.c.
References fat_dir_entry_struct::attributes, fat_read_dir_callback_arg::bytes_read, fat_read_dir_callback_arg::checksum, fat_dir_entry_struct::cluster, fat_read_dir_callback_arg::dir_entry, fat_dir_entry_struct::entry_offset, fat_calc_83_checksum(), FAT_DIRENTRY_DELETED, fat_dir_entry_struct::file_size, fat_read_dir_callback_arg::finished, fat_dir_entry_struct::long_name, ltoh16, and ltoh32.
Referenced by fat_read_dir().
01580 { 01581 struct fat_read_dir_callback_arg* arg = p; 01582 struct fat_dir_entry_struct* dir_entry = arg->dir_entry; 01583 01584 arg->bytes_read += 32; 01585 01586 /* skip deleted or empty entries */ 01587 if(buffer[0] == FAT_DIRENTRY_DELETED || !buffer[0]) 01588 { 01589 #if FAT_LFN_SUPPORT 01590 arg->checksum = 0; 01591 #endif 01592 return 1; 01593 } 01594 01595 #if !FAT_LFN_SUPPORT 01596 /* skip lfn entries */ 01597 if(buffer[11] == 0x0f) 01598 return 1; 01599 #endif 01600 01601 char* long_name = dir_entry->long_name; 01602 #if FAT_LFN_SUPPORT 01603 if(buffer[11] == 0x0f) 01604 { 01605 /* checksum validation */ 01606 if(arg->checksum == 0 || arg->checksum != buffer[13]) 01607 { 01608 /* reset directory entry */ 01609 memset(dir_entry, 0, sizeof(*dir_entry)); 01610 01611 arg->checksum = buffer[13]; 01612 dir_entry->entry_offset = offset; 01613 } 01614 01615 /* lfn supports unicode, but we do not, for now. 01616 * So we assume pure ascii and read only every 01617 * second byte. 01618 */ 01619 uint16_t char_offset = ((buffer[0] & 0x3f) - 1) * 13; 01620 const uint8_t char_mapping[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; 01621 uint16_t i=0; 01622 for( i = 0; i <= 12 && char_offset + i < sizeof(dir_entry->long_name) - 1; ++i) 01623 long_name[char_offset + i] = buffer[char_mapping[i]]; 01624 01625 return 1; 01626 } 01627 else 01628 #endif 01629 { 01630 #if FAT_LFN_SUPPORT 01631 /* if we do not have a long name or the previous lfn does not match, take the 8.3 name */ 01632 if(long_name[0] == '\0' || arg->checksum != fat_calc_83_checksum(buffer)) 01633 #endif 01634 { 01635 /* reset directory entry */ 01636 memset(dir_entry, 0, sizeof(*dir_entry)); 01637 dir_entry->entry_offset = offset; 01638 01639 uint8_t i; 01640 for(i = 0; i < 8; ++i) 01641 { 01642 if(buffer[i] == ' ') 01643 break; 01644 long_name[i] = buffer[i]; 01645 01646 /* Windows NT and later versions do not store lfn entries 01647 * for 8.3 names which have a lowercase basename, extension 01648 * or both when everything else is uppercase. They use two 01649 * extra bits to signal a lowercase basename or extension. 01650 */ 01651 if((buffer[12] & 0x08) && buffer[i] >= 'A' && buffer[i] <= 'Z') 01652 long_name[i] += 'a' - 'A'; 01653 } 01654 if(long_name[0] == 0x05) 01655 long_name[0] = (char) FAT_DIRENTRY_DELETED; 01656 01657 if(buffer[8] != ' ') 01658 { 01659 long_name[i++] = '.'; 01660 01661 uint8_t j = 8; 01662 for(; j < 11; ++j) 01663 { 01664 if(buffer[j] == ' ') 01665 break; 01666 long_name[i] = buffer[j]; 01667 01668 /* See above for the lowercase 8.3 name handling of 01669 * Windows NT and later. 01670 */ 01671 if((buffer[12] & 0x10) && buffer[j] >= 'A' && buffer[j] <= 'Z') 01672 long_name[i] += 'a' - 'A'; 01673 01674 ++i; 01675 } 01676 } 01677 01678 long_name[i] = '\0'; 01679 } 01680 01681 /* extract properties of file and store them within the structure */ 01682 dir_entry->attributes = buffer[11]; 01683 dir_entry->cluster = ltoh16(*((uint16_t*) &buffer[26])); 01684 #if FAT_FAT32_SUPPORT 01685 dir_entry->cluster |= ((cluster_t) ltoh16(*((uint16_t*) &buffer[20]))) << 16; 01686 #endif 01687 dir_entry->file_size = ltoh32(*((uint32_t*) &buffer[28])); 01688 01689 #if FAT_DATETIME_SUPPORT 01690 dir_entry->modification_time = ltoh16(*((uint16_t*) &buffer[22])); 01691 dir_entry->modification_date = ltoh16(*((uint16_t*) &buffer[24])); 01692 #endif 01693 01694 arg->finished = 1; 01695 return 0; 01696 } 01697 }


| 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 | |||
| ) | [static] |
Searches for space where to store a directory entry.
| [in] | fs | The filesystem on which to operate. |
| [in] | parent | The directory in which to search. |
| [in] | dir_entry | The directory entry for which to search space. |
Definition at line 1729 of file fat.c.
References fat_dir_entry_struct::cluster, fat_header_struct::cluster_size, fat_header_struct::cluster_zero_offset, partition_struct::device_read, fat_dir_struct::dir_entry, fat_append_clusters(), fat_clear_cluster(), fat_cluster_offset(), FAT_DIRENTRY_DELETED, fat_get_next_cluster(), fat_fs_struct::header, fat_dir_entry_struct::long_name, fat_fs_struct::partition, PARTITION_TYPE_FAT32, fat_header_struct::root_dir_offset, and partition_struct::type.
Referenced by fat_create_dir(), and fat_create_file().
01730 { 01731 if(!fs || !dir_entry) 01732 return 0; 01733 01734 /* search for a place where to write the directory entry to disk */ 01735 #if FAT_LFN_SUPPORT 01736 uint8_t free_dir_entries_needed = (strlen(dir_entry->long_name) + 12) / 13 + 1; 01737 uint8_t free_dir_entries_found = 0; 01738 #endif 01739 cluster_t cluster_num = parent->dir_entry.cluster; 01740 offset_t dir_entry_offset = 0; 01741 offset_t offset = 0; 01742 offset_t offset_to = 0; 01743 #if FAT_FAT32_SUPPORT 01744 uint8_t is_fat32 = (fs->partition->type == PARTITION_TYPE_FAT32); 01745 #endif 01746 01747 if(cluster_num == 0) 01748 { 01749 #if FAT_FAT32_SUPPORT 01750 if(is_fat32) 01751 { 01752 cluster_num = fs->header.root_dir_cluster; 01753 } 01754 else 01755 #endif 01756 { 01757 /* we read/write from the root directory entry */ 01758 offset = fs->header.root_dir_offset; 01759 offset_to = fs->header.cluster_zero_offset; 01760 dir_entry_offset = offset; 01761 } 01762 } 01763 01764 while(1) 01765 { 01766 if(offset == offset_to) 01767 { 01768 if(cluster_num == 0) 01769 /* We iterated through the whole root directory and 01770 * could not find enough space for the directory entry. 01771 */ 01772 return 0; 01773 01774 if(offset) 01775 { 01776 /* We reached a cluster boundary and have to 01777 * switch to the next cluster. 01778 */ 01779 01780 cluster_t cluster_next = fat_get_next_cluster(fs, cluster_num); 01781 if(!cluster_next) 01782 { 01783 cluster_next = fat_append_clusters(fs, cluster_num, 1); 01784 if(!cluster_next) 01785 return 0; 01786 01787 /* we appended a new cluster and know it is free */ 01788 dir_entry_offset = fs->header.cluster_zero_offset + 01789 (offset_t) (cluster_next - 2) * fs->header.cluster_size; 01790 01791 /* clear cluster to avoid garbage directory entries */ 01792 fat_clear_cluster(fs, cluster_next); 01793 01794 break; 01795 } 01796 cluster_num = cluster_next; 01797 } 01798 01799 offset = fat_cluster_offset(fs, cluster_num); 01800 offset_to = offset + fs->header.cluster_size; 01801 dir_entry_offset = offset; 01802 #if FAT_LFN_SUPPORT 01803 free_dir_entries_found = 0; 01804 #endif 01805 } 01806 01807 /* read next lfn or 8.3 entry */ 01808 uint8_t first_char; 01809 if(!fs->partition->device_read(offset, &first_char, sizeof(first_char))) 01810 return 0; 01811 01812 /* check if we found a free directory entry */ 01813 if(first_char == FAT_DIRENTRY_DELETED || !first_char) 01814 { 01815 /* check if we have the needed number of available entries */ 01816 #if FAT_LFN_SUPPORT 01817 ++free_dir_entries_found; 01818 if(free_dir_entries_found >= free_dir_entries_needed) 01819 #endif 01820 break; 01821 01822 offset += 32; 01823 } 01824 else 01825 { 01826 offset += 32; 01827 dir_entry_offset = offset; 01828 #if FAT_LFN_SUPPORT 01829 free_dir_entries_found = 0; 01830 #endif 01831 } 01832 } 01833 01834 return dir_entry_offset; 01835 }


| uint8_t fat_free_clusters | ( | struct fat_fs_struct * | fs, | |
| cluster_t | cluster_num | |||
| ) | [static] |
Frees a cluster chain, or a part thereof.
Marks the specified cluster and all clusters which are sequentially referenced by it as free. They may then be used again for future file allocations.
| [in] | fs | The filesystem on which to operate. |
| [in] | cluster_num | The starting cluster of the chain which to free. |
Definition at line 652 of file fat.c.
References fat_fs_struct::cluster_free, partition_struct::device_read, partition_struct::device_write, FAT16_CLUSTER_BAD, FAT16_CLUSTER_FREE, FAT16_CLUSTER_LAST_MAX, FAT16_CLUSTER_LAST_MIN, FAT16_CLUSTER_RESERVED_MAX, FAT16_CLUSTER_RESERVED_MIN, FAT32_CLUSTER_BAD, FAT32_CLUSTER_FREE, FAT32_CLUSTER_LAST_MAX, FAT32_CLUSTER_LAST_MIN, FAT32_CLUSTER_RESERVED_MAX, FAT32_CLUSTER_RESERVED_MIN, fat_header_struct::fat_offset, fat_fs_struct::header, ltoh16, ltoh32, fat_fs_struct::partition, PARTITION_TYPE_FAT32, and partition_struct::type.
Referenced by fat_append_clusters(), fat_create_dir(), fat_delete_file(), fat_resize_file(), and fat_terminate_clusters().
00653 { 00654 if(!fs || cluster_num < 2) 00655 return 0; 00656 00657 offset_t fat_offset = fs->header.fat_offset; 00658 #if FAT_FAT32_SUPPORT 00659 if(fs->partition->type == PARTITION_TYPE_FAT32) 00660 { 00661 uint32_t fat_entry; 00662 while(cluster_num) 00663 { 00664 if(!fs->partition->device_read(fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) 00665 return 0; 00666 00667 /* get next cluster of current cluster before freeing current cluster */ 00668 uint32_t cluster_num_next = ltoh32(fat_entry); 00669 00670 if(cluster_num_next == FAT32_CLUSTER_FREE) 00671 return 1; 00672 if(cluster_num_next == FAT32_CLUSTER_BAD || 00673 (cluster_num_next >= FAT32_CLUSTER_RESERVED_MIN && 00674 cluster_num_next <= FAT32_CLUSTER_RESERVED_MAX 00675 ) 00676 ) 00677 return 0; 00678 if(cluster_num_next >= FAT32_CLUSTER_LAST_MIN && cluster_num_next <= FAT32_CLUSTER_LAST_MAX) 00679 cluster_num_next = 0; 00680 00681 /* We know we will free the cluster, so remember it as 00682 * free for the next allocation. 00683 */ 00684 if(!fs->cluster_free) 00685 fs->cluster_free = cluster_num; 00686 00687 /* free cluster */ 00688 fat_entry = HTOL32(FAT32_CLUSTER_FREE); 00689 fs->partition->device_write(fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)); 00690 00691 /* We continue in any case here, even if freeing the cluster failed. 00692 * The cluster is lost, but maybe we can still free up some later ones. 00693 */ 00694 00695 cluster_num = cluster_num_next; 00696 } 00697 } 00698 else 00699 #endif 00700 { 00701 uint16_t fat_entry; 00702 while(cluster_num) 00703 { 00704 if(!fs->partition->device_read(fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) 00705 return 0; 00706 00707 /* get next cluster of current cluster before freeing current cluster */ 00708 uint16_t cluster_num_next = ltoh16(fat_entry); 00709 00710 if(cluster_num_next == FAT16_CLUSTER_FREE) 00711 return 1; 00712 if(cluster_num_next == FAT16_CLUSTER_BAD || 00713 (cluster_num_next >= FAT16_CLUSTER_RESERVED_MIN && 00714 cluster_num_next <= FAT16_CLUSTER_RESERVED_MAX 00715 ) 00716 ) 00717 return 0; 00718 if(cluster_num_next >= FAT16_CLUSTER_LAST_MIN && cluster_num_next <= FAT16_CLUSTER_LAST_MAX) 00719 cluster_num_next = 0; 00720 00721 /* free cluster */ 00722 fat_entry = HTOL16(FAT16_CLUSTER_FREE); 00723 fs->partition->device_write(fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry)); 00724 00725 /* We continue in any case here, even if freeing the cluster failed. 00726 * The cluster is lost, but maybe we can still free up some later ones. 00727 */ 00728 00729 cluster_num = cluster_num_next; 00730 } 00731 } 00732 00733 return 1; 00734 }

| offset_t fat_get_fs_free | ( | const struct fat_fs_struct * | fs | ) |
Returns the amount of free storage capacity on the filesystem in bytes.
| [in] | fs | The filesystem on which to operate. |
Definition at line 2376 of file fat.c.
References fat_usage_count_callback_arg::buffer_size, fat_usage_count_callback_arg::cluster_count, fat_header_struct::cluster_size, partition_struct::device_read_interval, FAT_FAT32_SUPPORT, fat_get_fs_free_16_callback(), fat_header_struct::fat_offset, fat_header_struct::fat_size, fat_fs_struct::header, fat_fs_struct::partition, PARTITION_TYPE_FAT16, and partition_struct::type.
Referenced by WaspSD::getDiskFree(), and WaspSD::print_disk_info().
02377 { 02378 if(!fs) 02379 return 0; 02380 02381 uint8_t fat[32]; 02382 struct fat_usage_count_callback_arg count_arg; 02383 count_arg.cluster_count = 0; 02384 count_arg.buffer_size = sizeof(fat); 02385 02386 offset_t fat_offset = fs->header.fat_offset; 02387 uint32_t fat_size = fs->header.fat_size; 02388 while(fat_size > 0) 02389 { 02390 uintptr_t length = UINTPTR_MAX - 1; 02391 if(fat_size < length) 02392 length = fat_size; 02393 02394 if(!fs->partition->device_read_interval(fat_offset, 02395 fat, 02396 sizeof(fat), 02397 length, 02398 #if FAT_FAT32_SUPPORT 02399 (fs->partition->type == PARTITION_TYPE_FAT16) ? 02400 fat_get_fs_free_16_callback : 02401 fat_get_fs_free_32_callback, 02402 #else 02403 fat_get_fs_free_16_callback, 02404 #endif 02405 &count_arg 02406 ) 02407 ) 02408 return 0; 02409 02410 fat_offset += length; 02411 fat_size -= length; 02412 } 02413 02414 return (offset_t) count_arg.cluster_count * fs->header.cluster_size; 02415 }


| uint8_t fat_get_fs_free_16_callback | ( | uint8_t * | buffer, | |
| offset_t | offset, | |||
| void * | p | |||
| ) | [static] |
Callback function used for counting free clusters in a FAT.
Definition at line 2421 of file fat.c.
References fat_usage_count_callback_arg::buffer_size, fat_usage_count_callback_arg::cluster_count, and FAT16_CLUSTER_FREE.
Referenced by fat_get_fs_free().
02422 { 02423 struct fat_usage_count_callback_arg* count_arg = (struct fat_usage_count_callback_arg*) p; 02424 uintptr_t buffer_size = count_arg->buffer_size; 02425 02426 uintptr_t i = 0; 02427 for( i = 0; i < buffer_size; i += 2, buffer += 2) 02428 { 02429 uint16_t cluster = *((uint16_t*) &buffer[0]); 02430 if(cluster == HTOL16(FAT16_CLUSTER_FREE)) 02431 ++(count_arg->cluster_count); 02432 } 02433 02434 return 1; 02435 }

| offset_t fat_get_fs_size | ( | const struct fat_fs_struct * | fs | ) |
Deletes a directory.
This is just a synonym for fat_delete_file(). If a directory is deleted without first deleting its subdirectories and files, disk space occupied by these files will get wasted as there is no chance to release it and mark it as free.
| [in] | fs | The filesystem on which to operate. |
| [in] | dir_entry | The directory entry of the directory to delete. |
| [in] | fs | The filesystem on which to operate. |
Definition at line 2353 of file fat.c.
References fat_header_struct::cluster_size, fat_header_struct::fat_size, fat_fs_struct::header, fat_fs_struct::partition, PARTITION_TYPE_FAT32, and partition_struct::type.
Referenced by WaspSD::getDiskSize(), and WaspSD::print_disk_info().
02354 { 02355 if(!fs) 02356 return 0; 02357 02358 #if FAT_FAT32_SUPPORT 02359 if(fs->partition->type == PARTITION_TYPE_FAT32) 02360 return (offset_t) (fs->header.fat_size / 4 - 2) * fs->header.cluster_size; 02361 else 02362 #endif 02363 return (offset_t) (fs->header.fat_size / 2 - 2) * fs->header.cluster_size; 02364 }

| cluster_t fat_get_next_cluster | ( | const struct fat_fs_struct * | fs, | |
| cluster_t | cluster_num | |||
| ) | [static] |
Retrieves the next following cluster of a given cluster.
Using the filesystem file allocation table, this function returns the number of the cluster containing the data directly following the data within the cluster with the given number.
| [in] | fs | The filesystem for which to determine the next cluster. |
| [in] | cluster_num | The number of the cluster for which to determine its successor. |
Definition at line 438 of file fat.c.
References partition_struct::device_read, FAT16_CLUSTER_BAD, FAT16_CLUSTER_FREE, FAT16_CLUSTER_LAST_MAX, FAT16_CLUSTER_LAST_MIN, FAT16_CLUSTER_RESERVED_MAX, FAT16_CLUSTER_RESERVED_MIN, FAT32_CLUSTER_BAD, FAT32_CLUSTER_FREE, FAT32_CLUSTER_LAST_MAX, FAT32_CLUSTER_LAST_MIN, FAT32_CLUSTER_RESERVED_MAX, FAT32_CLUSTER_RESERVED_MIN, fat_header_struct::fat_offset, fat_fs_struct::header, ltoh16, ltoh32, fat_fs_struct::partition, PARTITION_TYPE_FAT32, and partition_struct::type.
Referenced by fat_find_offset_for_dir_entry(), fat_read_dir(), fat_read_file(), fat_resize_file(), fat_terminate_clusters(), and fat_write_file().
00439 { 00440 if(!fs || cluster_num < 2) 00441 return 0; 00442 00443 #if FAT_FAT32_SUPPORT 00444 if(fs->partition->type == PARTITION_TYPE_FAT32) 00445 { 00446 /* read appropriate fat entry */ 00447 uint32_t fat_entry; 00448 if(!fs->partition->device_read(fs->header.fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) 00449 return 0; 00450 00451 /* determine next cluster from fat */ 00452 cluster_num = ltoh32(fat_entry); 00453 00454 if(cluster_num == FAT32_CLUSTER_FREE || 00455 cluster_num == FAT32_CLUSTER_BAD || 00456 (cluster_num >= FAT32_CLUSTER_RESERVED_MIN && cluster_num <= FAT32_CLUSTER_RESERVED_MAX) || 00457 (cluster_num >= FAT32_CLUSTER_LAST_MIN && cluster_num <= FAT32_CLUSTER_LAST_MAX)) 00458 return 0; 00459 } 00460 else 00461 #endif 00462 { 00463 /* read appropriate fat entry */ 00464 uint16_t fat_entry; 00465 if(!fs->partition->device_read(fs->header.fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) 00466 return 0; 00467 00468 /* determine next cluster from fat */ 00469 cluster_num = ltoh16(fat_entry); 00470 00471 if(cluster_num == FAT16_CLUSTER_FREE || 00472 cluster_num == FAT16_CLUSTER_BAD || 00473 (cluster_num >= FAT16_CLUSTER_RESERVED_MIN && cluster_num <= FAT16_CLUSTER_RESERVED_MAX) || 00474 (cluster_num >= FAT16_CLUSTER_LAST_MIN && cluster_num <= FAT16_CLUSTER_LAST_MAX)) 00475 return 0; 00476 } 00477 00478 return cluster_num; 00479 }

| struct fat_fs_struct* fat_open | ( | struct partition_struct * | partition | ) | [read] |
Opens a FAT filesystem.
| [in] | partition | Discriptor of partition on which the filesystem resides. |
Definition at line 235 of file fat.c.
References partition_struct::device_write, partition_struct::device_write_interval, FAT_FS_COUNT, fat_fs_handles, fat_read_header(), FAT_WRITE_SUPPORT, and fat_fs_struct::partition.
Referenced by WaspSD::init().
00236 { 00237 if(!partition || 00238 #if FAT_WRITE_SUPPORT 00239 !partition->device_write || 00240 !partition->device_write_interval 00241 #else 00242 0 00243 #endif 00244 ) 00245 return 0; 00246 00247 #if USE_DYNAMIC_MEMORY 00248 struct fat_fs_struct* fs = malloc(sizeof(*fs)); 00249 if(!fs) 00250 return 0; 00251 #else 00252 struct fat_fs_struct* fs = fat_fs_handles; 00253 uint8_t i; 00254 for(i = 0; i < FAT_FS_COUNT; ++i) 00255 { 00256 if(!fs->partition) 00257 break; 00258 00259 ++fs; 00260 } 00261 if(i >= FAT_FS_COUNT) 00262 return 0; 00263 #endif 00264 00265 memset(fs, 0, sizeof(*fs)); 00266 00267 fs->partition = partition; 00268 if(!fat_read_header(fs)) 00269 { 00270 #if USE_DYNAMIC_MEMORY 00271 free(fs); 00272 #else 00273 fs->partition = 0; 00274 #endif 00275 return 0; 00276 } 00277 00278 return fs; 00279 }


| uint8_t fat_read_header | ( | struct fat_fs_struct * | fs | ) | [static] |
Reads and parses the header of a FAT filesystem.
| inout] | fs The filesystem for which to parse the header. |
Definition at line 310 of file fat.c.
References fat_header_struct::cluster_size, fat_header_struct::cluster_zero_offset, partition_struct::device_read, fat_header_struct::fat_offset, fat_header_struct::fat_size, fat_fs_struct::header, ltoh16, ltoh32, partition_struct::offset, fat_fs_struct::partition, PARTITION_TYPE_FAT16, PARTITION_TYPE_FAT32, fat_header_struct::root_dir_offset, fat_header_struct::sector_size, fat_header_struct::size, and partition_struct::type.
Referenced by fat_open().
00311 { 00312 if(!fs) 00313 return 0; 00314 00315 struct partition_struct* partition = fs->partition; 00316 if(!partition) 00317 return 0; 00318 00319 /* read fat parameters */ 00320 #if FAT_FAT32_SUPPORT 00321 uint8_t buffer[37]; 00322 #else 00323 uint8_t buffer[25]; 00324 #endif 00325 offset_t partition_offset = (offset_t) partition->offset * 512; 00326 if(!partition->device_read(partition_offset + 0x0b, buffer, sizeof(buffer))) 00327 return 0; 00328 00329 uint16_t bytes_per_sector = ltoh16(*((uint16_t*) &buffer[0x00])); 00330 uint16_t reserved_sectors = ltoh16(*((uint16_t*) &buffer[0x03])); 00331 uint8_t sectors_per_cluster = buffer[0x02]; 00332 uint8_t fat_copies = buffer[0x05]; 00333 uint16_t max_root_entries = ltoh16(*((uint16_t*) &buffer[0x06])); 00334 uint16_t sector_count_16 = ltoh16(*((uint16_t*) &buffer[0x08])); 00335 uint16_t sectors_per_fat = ltoh16(*((uint16_t*) &buffer[0x0b])); 00336 uint32_t sector_count = ltoh32(*((uint32_t*) &buffer[0x15])); 00337 #if FAT_FAT32_SUPPORT 00338 uint32_t sectors_per_fat32 = ltoh32(*((uint32_t*) &buffer[0x19])); 00339 uint32_t cluster_root_dir = ltoh32(*((uint32_t*) &buffer[0x21])); 00340 #endif 00341 00342 if(sector_count == 0) 00343 { 00344 if(sector_count_16 == 0) 00345 /* illegal volume size */ 00346 return 0; 00347 else 00348 sector_count = sector_count_16; 00349 } 00350 #if FAT_FAT32_SUPPORT 00351 if(sectors_per_fat != 0) 00352 sectors_per_fat32 = sectors_per_fat; 00353 else if(sectors_per_fat32 == 0) 00354 /* this is neither FAT16 nor FAT32 */ 00355 return 0; 00356 #else 00357 if(sectors_per_fat == 0) 00358 /* this is not a FAT16 */ 00359 return 0; 00360 #endif 00361 00362 /* determine the type of FAT we have here */ 00363 uint32_t data_sector_count = sector_count 00364 - reserved_sectors 00365 #if FAT_FAT32_SUPPORT 00366 - sectors_per_fat32 * fat_copies 00367 #else 00368 - (uint32_t) sectors_per_fat * fat_copies 00369 #endif 00370 - ((max_root_entries * 32 + bytes_per_sector - 1) / bytes_per_sector); 00371 uint32_t data_cluster_count = data_sector_count / sectors_per_cluster; 00372 if(data_cluster_count < 4085) 00373 /* this is a FAT12, not supported */ 00374 return 0; 00375 else if(data_cluster_count < 65525) 00376 /* this is a FAT16 */ 00377 partition->type = PARTITION_TYPE_FAT16; 00378 else 00379 /* this is a FAT32 */ 00380 partition->type = PARTITION_TYPE_FAT32; 00381 00382 /* fill header information */ 00383 struct fat_header_struct* header = &fs->header; 00384 memset(header, 0, sizeof(*header)); 00385 00386 header->size = (offset_t) sector_count * bytes_per_sector; 00387 00388 header->fat_offset = /* jump to partition */ 00389 partition_offset + 00390 /* jump to fat */ 00391 (offset_t) reserved_sectors * bytes_per_sector; 00392 header->fat_size = (data_cluster_count + 2) * (partition->type == PARTITION_TYPE_FAT16 ? 2 : 4); 00393 00394 header->sector_size = bytes_per_sector; 00395 header->cluster_size = (uint16_t) bytes_per_sector * sectors_per_cluster; 00396 00397 #if FAT_FAT32_SUPPORT 00398 if(partition->type == PARTITION_TYPE_FAT16) 00399 #endif 00400 { 00401 header->root_dir_offset = /* jump to fats */ 00402 header->fat_offset + 00403 /* jump to root directory entries */ 00404 (offset_t) fat_copies * sectors_per_fat * bytes_per_sector; 00405 00406 header->cluster_zero_offset = /* jump to root directory entries */ 00407 header->root_dir_offset + 00408 /* skip root directory entries */ 00409 (offset_t) max_root_entries * 32; 00410 } 00411 #if FAT_FAT32_SUPPORT 00412 else 00413 { 00414 header->cluster_zero_offset = /* jump to fats */ 00415 header->fat_offset + 00416 /* skip fats */ 00417 (offset_t) fat_copies * sectors_per_fat32 * bytes_per_sector; 00418 00419 header->root_dir_cluster = cluster_root_dir; 00420 } 00421 #endif 00422 00423 return 1; 00424 }

| uint8_t fat_terminate_clusters | ( | struct fat_fs_struct * | fs, | |
| cluster_t | cluster_num | |||
| ) | [static] |
Frees a part of a cluster chain and correctly terminates the rest.
Marks the specified cluster as the new end of a cluster chain and frees all following clusters.
| [in] | fs | The filesystem on which to operate. |
| [in] | cluster_num | The new end of the cluster chain. |
Definition at line 750 of file fat.c.
References partition_struct::device_write, FAT16_CLUSTER_LAST_MAX, FAT32_CLUSTER_LAST_MAX, fat_free_clusters(), fat_get_next_cluster(), fat_header_struct::fat_offset, fat_fs_struct::header, fat_fs_struct::partition, PARTITION_TYPE_FAT32, and partition_struct::type.
Referenced by fat_resize_file().
00751 { 00752 if(!fs || cluster_num < 2) 00753 return 0; 00754 00755 /* fetch next cluster before overwriting the cluster entry */ 00756 cluster_t cluster_num_next = fat_get_next_cluster(fs, cluster_num); 00757 00758 /* mark cluster as the last one */ 00759 #if FAT_FAT32_SUPPORT 00760 if(fs->partition->type == PARTITION_TYPE_FAT32) 00761 { 00762 uint32_t fat_entry = HTOL32(FAT32_CLUSTER_LAST_MAX); 00763 if(!fs->partition->device_write(fs->header.fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) 00764 return 0; 00765 } 00766 else 00767 #endif 00768 { 00769 uint16_t fat_entry = HTOL16(FAT16_CLUSTER_LAST_MAX); 00770 if(!fs->partition->device_write(fs->header.fat_offset + cluster_num * sizeof(fat_entry), (uint8_t*) &fat_entry, sizeof(fat_entry))) 00771 return 0; 00772 } 00773 00774 /* free remaining clusters */ 00775 if(cluster_num_next) 00776 return fat_free_clusters(fs, cluster_num_next); 00777 else 00778 return 1; 00779 }


| uint8_t fat_write_dir_entry | ( | const struct fat_fs_struct * | fs, | |
| struct fat_dir_entry_struct * | dir_entry | |||
| ) | [static] |
Writes a directory entry to disk.
The generation of the short 8.3 file name is quite simple. The first eight characters are used for the filename. The extension, if any, is made up of the first three characters following the last dot within the long filename. If the filename (without the extension) is longer than eight characters, the lower byte of the cluster number replaces the last two characters to avoid name clashes. In any other case, it is your responsibility to avoid name clashes.
| [in] | fs | The filesystem on which to operate. |
| [in] | dir_entry | The directory entry to write. |
Definition at line 1858 of file fat.c.
References fat_dir_entry_struct::attributes, fat_dir_entry_struct::cluster, partition_struct::device_write, fat_dir_entry_struct::entry_offset, fat_calc_83_checksum(), FAT_DIRENTRY_DELETED, FAT_DIRENTRY_LFNLAST, fat_get_datetime, fat_dir_entry_struct::file_size, htol16(), htol32(), fat_dir_entry_struct::long_name, min, and fat_fs_struct::partition.
Referenced by fat_close_file(), fat_create_dir(), fat_create_file(), fat_resize_file(), and fat_write_file().
01859 { 01860 if(!fs || !dir_entry) 01861 return 0; 01862 01863 #if FAT_DATETIME_SUPPORT 01864 { 01865 uint16_t year; 01866 uint8_t month; 01867 uint8_t day; 01868 uint8_t hour; 01869 uint8_t min; 01870 uint8_t sec; 01871 01872 fat_get_datetime(&year, &month, &day, &hour, &min, &sec); 01873 fat_set_file_modification_date(dir_entry, year, month, day); 01874 fat_set_file_modification_time(dir_entry, hour, min, sec); 01875 } 01876 #endif 01877 01878 device_write_t device_write = fs->partition->device_write; 01879 offset_t offset = dir_entry->entry_offset; 01880 const char* name = dir_entry->long_name; 01881 uint8_t name_len = strlen(name); 01882 #if FAT_LFN_SUPPORT 01883 uint8_t lfn_entry_count = (name_len + 12) / 13; 01884 #endif 01885 uint8_t buffer[32]; 01886 01887 /* write 8.3 entry */ 01888 01889 /* generate 8.3 file name */ 01890 memset(&buffer[0], ' ', 11); 01891 char* name_ext = strrchr(name, '.'); 01892 if(name_ext && *++name_ext) 01893 { 01894 uint8_t name_ext_len = strlen(name_ext); 01895 name_len -= name_ext_len + 1; 01896 01897 if(name_ext_len > 3) 01898 #if FAT_LFN_SUPPORT 01899 name_ext_len = 3; 01900 #else 01901 return 0; 01902 #endif 01903 01904 memcpy(&buffer[8], name_ext, name_ext_len); 01905 } 01906 01907 if(name_len <= 8) 01908 { 01909 memcpy(buffer, name, name_len); 01910 01911 #if FAT_LFN_SUPPORT 01912 /* For now, we create lfn entries for all files, 01913 * except the "." and ".." directory references. 01914 * This is to avoid difficulties with capitalization, 01915 * as 8.3 filenames allow uppercase letters only. 01916 * 01917 * Theoretically it would be possible to leave 01918 * the 8.3 entry alone if the basename and the 01919 * extension have no mixed capitalization. 01920 */ 01921 if(name[0] == '.' && 01922 ((name[1] == '.' && name[2] == '\0') || 01923 name[1] == '\0') 01924 ) 01925 lfn_entry_count = 0; 01926 #endif 01927 } 01928 else 01929 { 01930 #if FAT_LFN_SUPPORT 01931 memcpy(buffer, name, 8); 01932 01933 /* Minimize 8.3 name clashes by appending 01934 * the lower byte of the cluster number. 01935 */ 01936 uint8_t num = dir_entry->cluster & 0xff; 01937 01938 buffer[6] = (num < 0xa0) ? ('0' + (num >> 4)) : ('a' + (num >> 4)); 01939 num &= 0x0f; 01940 buffer[7] = (num < 0x0a) ? ('0' + num) : ('a' + num); 01941 #else 01942 return 0; 01943 #endif 01944 } 01945 if(buffer[0] == FAT_DIRENTRY_DELETED) 01946 buffer[0] = 0x05; 01947 01948 /* fill directory entry buffer */ 01949 memset(&buffer[11], 0, sizeof(buffer) - 11); 01950 buffer[0x0b] = dir_entry->attributes; 01951 #if FAT_DATETIME_SUPPORT 01952 *((uint16_t*) &buffer[0x16]) = htol16(dir_entry->modification_time); 01953 *((uint16_t*) &buffer[0x18]) = htol16(dir_entry->modification_date); 01954 #endif 01955 #if FAT_FAT32_SUPPORT 01956 *((uint16_t*) &buffer[0x14]) = htol16((uint16_t) (dir_entry->cluster >> 16)); 01957 #endif 01958 *((uint16_t*) &buffer[0x1a]) = htol16(dir_entry->cluster); 01959 *((uint32_t*) &buffer[0x1c]) = htol32(dir_entry->file_size); 01960 01961 /* write to disk */ 01962 #if FAT_LFN_SUPPORT 01963 if(!device_write(offset + (uint16_t) lfn_entry_count * 32, buffer, sizeof(buffer))) 01964 #else 01965 if(!device_write(offset, buffer, sizeof(buffer))) 01966 #endif 01967 return 0; 01968 01969 #if FAT_LFN_SUPPORT 01970 /* calculate checksum of 8.3 name */ 01971 uint8_t checksum = fat_calc_83_checksum(buffer); 01972 01973 /* write lfn entries */ 01974 uint8_t lfn_entry =0; 01975 for( lfn_entry = lfn_entry_count; lfn_entry > 0; --lfn_entry) 01976 { 01977 memset(buffer, 0xff, sizeof(buffer)); 01978 01979 /* set file name */ 01980 const char* long_name_curr = name + (lfn_entry - 1) * 13; 01981 uint8_t i = 1; 01982 while(i < 0x1f) 01983 { 01984 buffer[i++] = *long_name_curr; 01985 buffer[i++] = 0; 01986 01987 switch(i) 01988 { 01989 case 0x0b: 01990 i = 0x0e; 01991 break; 01992 case 0x1a: 01993 i = 0x1c; 01994 break; 01995 } 01996 01997 if(!*long_name_curr++) 01998 break; 01999 } 02000 02001 /* set index of lfn entry */ 02002 buffer[0x00] = lfn_entry; 02003 if(lfn_entry == lfn_entry_count) 02004 buffer[0x00] |= FAT_DIRENTRY_LFNLAST; 02005 02006 /* mark as lfn entry */ 02007 buffer[0x0b] = 0x0f; 02008 02009 /* set 8.3 checksum */ 02010 buffer[0x0d] = checksum; 02011 02012 /* clear reserved bytes */ 02013 buffer[0x0c] = 0; 02014 buffer[0x1a] = 0; 02015 buffer[0x1b] = 0; 02016 02017 /* write entry */ 02018 device_write(offset, buffer, sizeof(buffer)); 02019 02020 offset += sizeof(buffer); 02021 } 02022 #endif 02023 02024 return 1; 02025 }


1.5.6