attributes:
label: Ventoy Version
description: What version of ventoy are you running?
- placeholder: 1.0.59
+ placeholder: 1.0.60
validations:
required: true
- type: dropdown
fi
export PATH="./tool/$TOOLDIR:$PATH"
+rm -f ./log.txt
+
echo ''
echo '**********************************************'
#cd $VTOY_PATH/VtoyTool
#sh build.sh || exit 1
-#cd $VTOY_PATH/vtoyfat/fat_io_lib
+#cd $VTOY_PATH/vtoycli/fat_io_lib
#sh buildlib.sh
-#cd $VTOY_PATH/vtoyfat
-#sh build.sh || exit 1
-
-#cd $VTOY_PATH/vtoygpt
+#cd $VTOY_PATH/vtoycli
#sh build.sh || exit 1
#cd $VTOY_PATH/FUSEISO
echo ' -s/-S enable/disable secure boot support (default is disabled)'
echo ' -g use GPT partition style, default is MBR (only for install)'
echo ' -L Label of the 1st exfat partition (default is Ventoy)'
+ echo ' -n try non-destructive installation (only for install)'
echo ''
}
elif [ "$1" = "-I" ]; then
MODE="install"
FORCE="Y"
+ elif [ "$1" = "-n" ]; then
+ NONDESTRUCTIVE="Y"
elif [ "$1" = "-u" ]; then
MODE="update"
elif [ "$1" = "-l" ]; then
fi
-if [ "$MODE" = "install" ]; then
+if [ "$MODE" = "install" -a -z "$NONDESTRUCTIVE" ]; then
vtdebug "install Ventoy ..."
if [ -n "$VTGPT" ]; then
exit 1
fi
- if ! dd if=/dev/zero of=$DISK bs=1 count=512 status=none conv=fsync; then
+ # check and umount
+ check_umount_disk "$DISK"
+
+ if ! dd if=/dev/zero of=$DISK bs=64 count=512 status=none conv=fsync; then
vterr "Write data to $DISK failed, please check whether it's in use."
exit 1
fi
PART1=$(get_disk_part_name $DISK 1)
PART2=$(get_disk_part_name $DISK 2)
+ #clean part2
+ dd status=none conv=fsync if=/dev/zero of=$DISK bs=512 count=32 seek=$part2_start_sector
+
+ #format part1
+ vtinfo "Format partition 1 ..."
mkexfatfs -n "$VTNEW_LABEL" -s $cluster_sectors ${PART1}
- vtinfo "writing data to disk ..."
-
+ vtinfo "writing data to disk ..."
dd status=none conv=fsync if=./boot/boot.img of=$DISK bs=1 count=446
if [ -n "$VTGPT" ]; then
sync
vtinfo "esp partition processing ..."
+ if [ "$SECUREBOOT" != "YES" ]; then
+ sleep 2
+ check_umount_disk "$DISK"
+ vtoycli partresize -s $DISK $part2_start_sector
+ fi
+
+ echo ""
+ vtinfo "Install Ventoy to $DISK successfully finished."
+ echo ""
+
+elif [ "$MODE" = "install" -a -n "$NONDESTRUCTIVE" ]; then
+ vtdebug "non-destructive install Ventoy ..."
+
+ version=$(get_disk_ventoy_version $DISK)
+ if [ $? -eq 0 ]; then
+ if [ -z "$FORCE" ]; then
+ vtwarn "$DISK already contains a Ventoy with version $version."
+ vtwarn "You can not do and don not need non-destructive installation."
+ vtwarn ""
+ exit 1
+ fi
+ fi
- sleep 1
- check_umount_disk "$DISK"
+ disk_sector_num=$(cat /sys/block/${DISK#/dev/}/size)
+ disk_size_gb=$(expr $disk_sector_num / 2097152)
+
+ if vtoycli partresize -t $DISK; then
+ OldStyle="GPT"
+ else
+ OldStyle="MBR"
+ fi
+
+ #Print disk info
+ echo "Disk : $DISK"
+ parted -s $DISK p 2>&1 | grep Model
+ echo "Size : $disk_size_gb GB"
+ echo "Style: $OldStyle"
+ echo ''
+
+ vtwarn "Attention:"
+ vtwarn "Ventoy will try non-destructive installation on $DISK if possible."
+ echo ""
+
+ read -p 'Continue? (y/n) ' Answer
+ if [ "$Answer" != "y" ]; then
+ if [ "$Answer" != "Y" ]; then
+ exit 0
+ fi
+ fi
+
+ if [ $disk_sector_num -le $VENTOY_SECTOR_NUM ]; then
+ vterr "No enough space in disk $DISK"
+ exit 1
+ fi
+
+ PART1=$(get_disk_part_name $DISK 1)
+ PART2=$(get_disk_part_name $DISK 2)
+
+ #Part1 size in MB aligned with 4KB
+ PART1_SECTORS=$(cat /sys/class/block/${PART1#/dev/}/size)
+ PART1_4K=$(expr $PART1_SECTORS / 8)
+ PART1_MB=$(expr $PART1_4K / 256)
+ PART1_NEW_MB=$(expr $PART1_MB - 32)
+
+ echo "$PART1 is ${PART1_MB}MB"
+
+ #check partition layout
+ echo "check partition layout ..."
+ vtoycli partresize -c $DISK
+ vtRet=$?
+ if [ $vtRet -eq 0 ]; then
+ exit 1
+ else
+ # check and umount
+ check_umount_disk "$DISK"
+ sleep 1
+ check_umount_disk "$DISK"
- if [ "$SECUREBOOT" != "YES" ]; then
- mkdir ./tmp_mnt
-
- vtdebug "mounting part2 ...."
- for tt in 1 2 3 4 5; do
- if mount ${PART2} ./tmp_mnt > /dev/null 2>&1; then
- vtdebug "mounting part2 success"
- break
- fi
+ if [ $vtRet -eq 1 ]; then
+ echo "Free space enough, start install..."
+ part2_start_sector=$(expr $PART1_SECTORS + 2048)
+ elif [ $vtRet -eq 2 ]; then
+ echo "We need to shrink partition 1 firstly ..."
- check_umount_disk "$DISK"
- sleep 2
- done
-
- rm -f ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
- rm -f ./tmp_mnt/EFI/BOOT/grubx64.efi
- rm -f ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
- rm -f ./tmp_mnt/EFI/BOOT/grubia32.efi
- rm -f ./tmp_mnt/EFI/BOOT/MokManager.efi
- rm -f ./tmp_mnt/EFI/BOOT/mmia32.efi
- rm -f ./tmp_mnt/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
- mv ./tmp_mnt/EFI/BOOT/grubx64_real.efi ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
- mv ./tmp_mnt/EFI/BOOT/grubia32_real.efi ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
-
- sync
-
- for tt in 1 2 3; do
- if umount ./tmp_mnt; then
- vtdebug "umount part2 success"
- rm -rf ./tmp_mnt
- break
+ PART1_BLKID=$(blkid $PART1)
+ blkid $PART1
+
+ if echo $PART1_BLKID | egrep -q -i 'TYPE=ntfs|TYPE=.ntfs'; then
+ echo "Partition 1 contains NTFS filesystem"
+
+ which ntfsresize
+ if [ $? -ne 0 ]; then
+ echo "###[FAIL] ntfsresize not found. Please install ntfs-3g package."
+ exit 1
+ fi
+
+ echo "ntfsfix -b -d $PART1 ..."
+ ntfsfix -b -d $PART1
+
+ echo "ntfsresize --size ${PART1_NEW_MB}Mi $PART1 ..."
+ ntfsresize -f --size ${PART1_NEW_MB}Mi $PART1
+ if [ $? -ne 0 ]; then
+ echo "###[FAIL] ntfsresize failed."
+ exit 1
+ fi
+ elif echo $PART1_BLKID | egrep -q -i 'TYPE=ext[2-4]|TYPE=.ext[2-4]'; then
+ echo "Partition 1 contains EXT filesystem"
+
+ which resize2fs
+ if [ $? -ne 0 ]; then
+ echo "###[FAIL] resize2fs not found. Please install e2fsprogs package."
+ exit 1
+ fi
+
+ echo "e2fsck -f $PART1 ..."
+ e2fsck -f $PART1
+
+ echo "resize2fs $PART1 ${PART1_NEW_MB}M ..."
+ resize2fs $PART1 ${PART1_NEW_MB}M
+ if [ $? -ne 0 ]; then
+ echo "###[FAIL] resize2fs failed."
+ exit 1
+ fi
else
- vtdebug "umount part2 failed, now retry..."
- sleep 1
+ echo "###[FAIL] Unsupported filesystem in partition 1."
+ exit 1
fi
- done
+
+ sync
+ PART1_NEW_END_MB=$(expr $PART1_NEW_MB + 1)
+ part2_start_sector=$(expr $PART1_NEW_END_MB \* 2048)
+ fi
fi
- echo ""
- vtinfo "Install Ventoy to $DISK successfully finished."
- echo ""
+ vtinfo "writing data to disk part2_start=$part2_start_sector ..."
+
+ dd status=none conv=fsync if=./boot/boot.img of=$DISK bs=1 count=440
+
+ if [ "$OldStyle" = "GPT" ]; then
+ echo -en '\x22' | dd status=none of=$DISK conv=fsync bs=1 count=1 seek=92
+ xzcat ./boot/core.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=2014 seek=34
+ echo -en '\x23' | dd of=$DISK conv=fsync bs=1 count=1 seek=17908 status=none
+ else
+ xzcat ./boot/core.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=2047 seek=1
+ fi
+
+ xzcat ./ventoy/ventoy.disk.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start_sector
+
+ #test UUID
+ testUUIDStr=$(vtoy_gen_uuid | hexdump -C)
+ vtdebug "test uuid: $testUUIDStr"
+
+ #disk uuid
+ vtoy_gen_uuid | dd status=none conv=fsync of=${DISK} seek=384 bs=1 count=16
+
+ vtinfo "sync data ..."
+ sync
+
+ vtinfo "esp partition processing ..."
+ if [ "$SECUREBOOT" != "YES" ]; then
+ vtoycli partresize -s $DISK $part2_start_sector
+ fi
+
+ vtinfo "update partition table $DISK $part2_start_sector ..."
+ vtoycli partresize -p $DISK $part2_start_sector
+ if [ $? -eq 0 ]; then
+ sync
+ echo ""
+ vtinfo "Ventoy non-destructive installation on $DISK successfully finished."
+ echo ""
+ else
+ echo ""
+ vterr "Ventoy non-destructive installation on $DISK failed."
+ echo ""
+ fi
else
vtdebug "update Ventoy ..."
check_umount_disk "$DISK"
xzcat ./ventoy/ventoy.disk.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start
-
sync
+ vtinfo "esp partition processing ..."
if [ "$SECUREBOOT" != "YES" ]; then
- mkdir ./tmp_mnt
-
- vtdebug "mounting part2 ...."
- for tt in 1 2 3 4 5; do
- check_umount_disk "$DISK"
-
- if mount ${PART2} ./tmp_mnt > /dev/null 2>&1; then
- vtdebug "mounting part2 success"
- break
- else
- vtdebug "mounting part2 failed, now wait and retry..."
- fi
- sleep 2
- done
-
- rm -f ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
- rm -f ./tmp_mnt/EFI/BOOT/grubx64.efi
- rm -f ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
- rm -f ./tmp_mnt/EFI/BOOT/grubia32.efi
- rm -f ./tmp_mnt/EFI/BOOT/MokManager.efi
- rm -f ./tmp_mnt/EFI/BOOT/mmia32.efi
- rm -f ./tmp_mnt/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
- mv ./tmp_mnt/EFI/BOOT/grubx64_real.efi ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
- mv ./tmp_mnt/EFI/BOOT/grubia32_real.efi ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
-
- sync
-
- for tt in 1 2 3; do
- if umount ./tmp_mnt > /dev/null 2>&1; then
- vtdebug "umount part2 success"
- rm -rf ./tmp_mnt
- break
- else
- vtdebug "umount part2 failed, now retry..."
- sleep 1
- fi
- done
+ sleep 2
+ check_umount_disk "$DISK"
+ vtoycli partresize -s $DISK $part2_start
fi
echo ""
return
fi
- if vtoyfat -T; then
- vtdebug "vtoyfat test ok ..."
+ if vtoycli fat -T; then
+ vtdebug "vtoycli fat test ok ..."
else
- vtdebug "vtoyfat test fail ..."
+ vtdebug "vtoycli fat test fail ..."
ventoy_false
return
fi
PART2=$(get_disk_part_name $1 2)
- vtoyfat -s $PART2
+ vtoycli fat -s $PART2
}
get_disk_ventoy_version() {
PART2=$(get_disk_part_name $1 2)
- ParseVer=$(vtoyfat $PART2)
+ ParseVer=$(vtoycli fat $PART2)
if [ $? -eq 0 ]; then
vtdebug "Ventoy version in $PART2 is $ParseVer"
echo $ParseVer
sync
- vtoygpt -f $DISK
+ vtoycli gpt -f $DISK
sync
udevadm trigger --name-match=$DISK >/dev/null 2>&1
# cp $OPT ./tool/x86_64/mount.exfat-fuse $tmpmnt/tool/mount.exfat-fuse_x86_64
# cp $OPT ./tool/aarch64/mount.exfat-fuse $tmpmnt/tool/mount.exfat-fuse_aarch64
# to save space
-cp $OPT ./tool/i386/vtoygpt $tmpmnt/tool/mount.exfat-fuse_i386
-cp $OPT ./tool/x86_64/vtoygpt $tmpmnt/tool/mount.exfat-fuse_x86_64
-cp $OPT ./tool/aarch64/vtoygpt $tmpmnt/tool/mount.exfat-fuse_aarch64
+dd status=none bs=1024 count=16 if=./tool/i386/vtoycli of=$tmpmnt/tool/mount.exfat-fuse_i386
+dd status=none bs=1024 count=16 if=./tool/x86_64/vtoycli of=$tmpmnt/tool/mount.exfat-fuse_x86_64
+dd status=none bs=1024 count=16 if=./tool/aarch64/vtoycli of=$tmpmnt/tool/mount.exfat-fuse_aarch64
rm -f $tmpmnt/grub/i386-pc/*.img
--- /dev/null
+#!/bin/sh
+
+rm -f vtoycli_64
+rm -f vtoycli_32
+rm -f vtoycli_aa64
+rm -f vtoycli_m64e
+
+SRCS="vtoycli.c vtoyfat.c vtoygpt.c crc32.c partresize.c"
+
+gcc -specs "/usr/local/musl/lib/musl-gcc.specs" -Os -static -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_64.a -o vtoycli_64
+
+/opt/diet32/bin/diet -Os gcc -D_FILE_OFFSET_BITS=64 -m32 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_32.a -o vtoycli_32
+
+
+#gcc -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_64.a -o vtoycli_64
+#gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_32.a -o vtoycli_32
+
+aarch64-buildroot-linux-uclibc-gcc -static -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_aa64.a -o vtoycli_aa64
+mips64el-linux-musl-gcc -mips64r2 -mabi=64 -static -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_m64e.a -o vtoycli_m64e
+
+
+if [ -e vtoycli_64 ] && [ -e vtoycli_32 ] && [ -e vtoycli_aa64 ] && [ -e vtoycli_m64e ]; then
+ echo -e "\n===== success $name =======\n"
+
+ strip --strip-all vtoycli_32
+ strip --strip-all vtoycli_64
+ aarch64-buildroot-linux-uclibc-strip --strip-all vtoycli_aa64
+ mips64el-linux-musl-strip --strip-all vtoycli_m64e
+
+ [ -d ../INSTALL/tool/i386/ ] && mv vtoycli_32 ../INSTALL/tool/i386/vtoycli
+ [ -d ../INSTALL/tool/x86_64/ ] && mv vtoycli_64 ../INSTALL/tool/x86_64/vtoycli
+ [ -d ../INSTALL/tool/aarch64/ ] && mv vtoycli_aa64 ../INSTALL/tool/aarch64/vtoycli
+ [ -d ../INSTALL/tool/mips64el/ ] && mv vtoycli_m64e ../INSTALL/tool/mips64el/vtoycli
+else
+ echo -e "\n===== failed =======\n"
+ exit 1
+fi
--- /dev/null
+/******************************************************************************
+ * vtoygpt.c ---- ventoy gpt util
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define VOID void
+#define CHAR char
+#define UINT64 unsigned long long
+#define UINT32 unsigned int
+#define UINT16 unsigned short
+#define CHAR16 unsigned short
+#define UINT8 unsigned char
+
+UINT32 g_crc_table[256] = {
+ 0x00000000,
+ 0x77073096,
+ 0xEE0E612C,
+ 0x990951BA,
+ 0x076DC419,
+ 0x706AF48F,
+ 0xE963A535,
+ 0x9E6495A3,
+ 0x0EDB8832,
+ 0x79DCB8A4,
+ 0xE0D5E91E,
+ 0x97D2D988,
+ 0x09B64C2B,
+ 0x7EB17CBD,
+ 0xE7B82D07,
+ 0x90BF1D91,
+ 0x1DB71064,
+ 0x6AB020F2,
+ 0xF3B97148,
+ 0x84BE41DE,
+ 0x1ADAD47D,
+ 0x6DDDE4EB,
+ 0xF4D4B551,
+ 0x83D385C7,
+ 0x136C9856,
+ 0x646BA8C0,
+ 0xFD62F97A,
+ 0x8A65C9EC,
+ 0x14015C4F,
+ 0x63066CD9,
+ 0xFA0F3D63,
+ 0x8D080DF5,
+ 0x3B6E20C8,
+ 0x4C69105E,
+ 0xD56041E4,
+ 0xA2677172,
+ 0x3C03E4D1,
+ 0x4B04D447,
+ 0xD20D85FD,
+ 0xA50AB56B,
+ 0x35B5A8FA,
+ 0x42B2986C,
+ 0xDBBBC9D6,
+ 0xACBCF940,
+ 0x32D86CE3,
+ 0x45DF5C75,
+ 0xDCD60DCF,
+ 0xABD13D59,
+ 0x26D930AC,
+ 0x51DE003A,
+ 0xC8D75180,
+ 0xBFD06116,
+ 0x21B4F4B5,
+ 0x56B3C423,
+ 0xCFBA9599,
+ 0xB8BDA50F,
+ 0x2802B89E,
+ 0x5F058808,
+ 0xC60CD9B2,
+ 0xB10BE924,
+ 0x2F6F7C87,
+ 0x58684C11,
+ 0xC1611DAB,
+ 0xB6662D3D,
+ 0x76DC4190,
+ 0x01DB7106,
+ 0x98D220BC,
+ 0xEFD5102A,
+ 0x71B18589,
+ 0x06B6B51F,
+ 0x9FBFE4A5,
+ 0xE8B8D433,
+ 0x7807C9A2,
+ 0x0F00F934,
+ 0x9609A88E,
+ 0xE10E9818,
+ 0x7F6A0DBB,
+ 0x086D3D2D,
+ 0x91646C97,
+ 0xE6635C01,
+ 0x6B6B51F4,
+ 0x1C6C6162,
+ 0x856530D8,
+ 0xF262004E,
+ 0x6C0695ED,
+ 0x1B01A57B,
+ 0x8208F4C1,
+ 0xF50FC457,
+ 0x65B0D9C6,
+ 0x12B7E950,
+ 0x8BBEB8EA,
+ 0xFCB9887C,
+ 0x62DD1DDF,
+ 0x15DA2D49,
+ 0x8CD37CF3,
+ 0xFBD44C65,
+ 0x4DB26158,
+ 0x3AB551CE,
+ 0xA3BC0074,
+ 0xD4BB30E2,
+ 0x4ADFA541,
+ 0x3DD895D7,
+ 0xA4D1C46D,
+ 0xD3D6F4FB,
+ 0x4369E96A,
+ 0x346ED9FC,
+ 0xAD678846,
+ 0xDA60B8D0,
+ 0x44042D73,
+ 0x33031DE5,
+ 0xAA0A4C5F,
+ 0xDD0D7CC9,
+ 0x5005713C,
+ 0x270241AA,
+ 0xBE0B1010,
+ 0xC90C2086,
+ 0x5768B525,
+ 0x206F85B3,
+ 0xB966D409,
+ 0xCE61E49F,
+ 0x5EDEF90E,
+ 0x29D9C998,
+ 0xB0D09822,
+ 0xC7D7A8B4,
+ 0x59B33D17,
+ 0x2EB40D81,
+ 0xB7BD5C3B,
+ 0xC0BA6CAD,
+ 0xEDB88320,
+ 0x9ABFB3B6,
+ 0x03B6E20C,
+ 0x74B1D29A,
+ 0xEAD54739,
+ 0x9DD277AF,
+ 0x04DB2615,
+ 0x73DC1683,
+ 0xE3630B12,
+ 0x94643B84,
+ 0x0D6D6A3E,
+ 0x7A6A5AA8,
+ 0xE40ECF0B,
+ 0x9309FF9D,
+ 0x0A00AE27,
+ 0x7D079EB1,
+ 0xF00F9344,
+ 0x8708A3D2,
+ 0x1E01F268,
+ 0x6906C2FE,
+ 0xF762575D,
+ 0x806567CB,
+ 0x196C3671,
+ 0x6E6B06E7,
+ 0xFED41B76,
+ 0x89D32BE0,
+ 0x10DA7A5A,
+ 0x67DD4ACC,
+ 0xF9B9DF6F,
+ 0x8EBEEFF9,
+ 0x17B7BE43,
+ 0x60B08ED5,
+ 0xD6D6A3E8,
+ 0xA1D1937E,
+ 0x38D8C2C4,
+ 0x4FDFF252,
+ 0xD1BB67F1,
+ 0xA6BC5767,
+ 0x3FB506DD,
+ 0x48B2364B,
+ 0xD80D2BDA,
+ 0xAF0A1B4C,
+ 0x36034AF6,
+ 0x41047A60,
+ 0xDF60EFC3,
+ 0xA867DF55,
+ 0x316E8EEF,
+ 0x4669BE79,
+ 0xCB61B38C,
+ 0xBC66831A,
+ 0x256FD2A0,
+ 0x5268E236,
+ 0xCC0C7795,
+ 0xBB0B4703,
+ 0x220216B9,
+ 0x5505262F,
+ 0xC5BA3BBE,
+ 0xB2BD0B28,
+ 0x2BB45A92,
+ 0x5CB36A04,
+ 0xC2D7FFA7,
+ 0xB5D0CF31,
+ 0x2CD99E8B,
+ 0x5BDEAE1D,
+ 0x9B64C2B0,
+ 0xEC63F226,
+ 0x756AA39C,
+ 0x026D930A,
+ 0x9C0906A9,
+ 0xEB0E363F,
+ 0x72076785,
+ 0x05005713,
+ 0x95BF4A82,
+ 0xE2B87A14,
+ 0x7BB12BAE,
+ 0x0CB61B38,
+ 0x92D28E9B,
+ 0xE5D5BE0D,
+ 0x7CDCEFB7,
+ 0x0BDBDF21,
+ 0x86D3D2D4,
+ 0xF1D4E242,
+ 0x68DDB3F8,
+ 0x1FDA836E,
+ 0x81BE16CD,
+ 0xF6B9265B,
+ 0x6FB077E1,
+ 0x18B74777,
+ 0x88085AE6,
+ 0xFF0F6A70,
+ 0x66063BCA,
+ 0x11010B5C,
+ 0x8F659EFF,
+ 0xF862AE69,
+ 0x616BFFD3,
+ 0x166CCF45,
+ 0xA00AE278,
+ 0xD70DD2EE,
+ 0x4E048354,
+ 0x3903B3C2,
+ 0xA7672661,
+ 0xD06016F7,
+ 0x4969474D,
+ 0x3E6E77DB,
+ 0xAED16A4A,
+ 0xD9D65ADC,
+ 0x40DF0B66,
+ 0x37D83BF0,
+ 0xA9BCAE53,
+ 0xDEBB9EC5,
+ 0x47B2CF7F,
+ 0x30B5FFE9,
+ 0xBDBDF21C,
+ 0xCABAC28A,
+ 0x53B39330,
+ 0x24B4A3A6,
+ 0xBAD03605,
+ 0xCDD70693,
+ 0x54DE5729,
+ 0x23D967BF,
+ 0xB3667A2E,
+ 0xC4614AB8,
+ 0x5D681B02,
+ 0x2A6F2B94,
+ 0xB40BBE37,
+ 0xC30C8EA1,
+ 0x5A05DF1B,
+ 0x2D02EF8D
+};
+
+UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length)
+{
+ UINT32 i;
+ UINT8 *Ptr = Buffer;
+ UINT32 Crc = 0xFFFFFFFF;
+
+ for (i = 0; i < Length; i++, Ptr++)
+ {
+ Crc = (Crc >> 8) ^ g_crc_table[(UINT8) Crc ^ *Ptr];
+ }
+
+ return Crc ^ 0xffffffff;
+}
+
--- /dev/null
+#!/bin/sh
+
+rm -rf include
+rm -rf lib
+
+cd release
+#/opt/diet64/bin/diet -Os gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+gcc -specs "/usr/local/musl/lib/musl-gcc.specs" -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_64.a *.o
+rm -f *.o
+
+
+gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_32.a *.o
+rm -f *.o
+
+
+aarch64-linux-gnu-gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_aa64.a *.o
+rm -f *.o
+
+
+mips64el-linux-musl-gcc -mips64r2 -mabi=64 -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_m64e.a *.o
+rm -f *.o
+
+cd -
+
+
+mkdir lib
+mkdir include
+
+mv release/*.a lib/
+cp -a release/*.h include/
+
--- /dev/null
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK 0
+#define FAT_INIT_MEDIA_ACCESS_ERROR (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE (-2)
+#define FAT_INIT_INVALID_SIGNATURE (-3)
+#define FAT_INIT_ENDIAN_ERROR (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE (-6)
+#define FAT_INIT_STRUCT_PACKING (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+ // User supplied function pointers for disk IO
+ fn_diskio_read read_media;
+ fn_diskio_write write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+ uint8 sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+ uint32 address;
+ int dirty;
+ uint8 * ptr;
+
+ // Next in chain of sector buffers
+ struct fat_buffer *next;
+};
+
+typedef enum eFatType
+{
+ FAT_TYPE_16,
+ FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+ // Filesystem globals
+ uint8 sectors_per_cluster;
+ uint32 cluster_begin_lba;
+ uint32 rootdir_first_cluster;
+ uint32 rootdir_first_sector;
+ uint32 rootdir_sectors;
+ uint32 fat_begin_lba;
+ uint16 fs_info_sector;
+ uint32 lba_begin;
+ uint32 fat_sectors;
+ uint32 next_free_cluster;
+ uint16 root_entry_count;
+ uint16 reserved_sectors;
+ uint8 num_of_fats;
+ tFatType fat_type;
+
+ // Disk/Media API
+ struct disk_if disk_io;
+
+ // [Optional] Thread Safety
+ void (*fl_lock)(void);
+ void (*fl_unlock)(void);
+
+ // Working buffer
+ struct fat_buffer currentsector;
+
+ // FAT Buffer
+ struct fat_buffer *fat_buffer_head;
+ struct fat_buffer fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+ uint32 sector;
+ uint32 cluster;
+ uint8 offset;
+};
+
+struct fs_dir_ent
+{
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 is_dir;
+ uint32 cluster;
+ uint32 size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ uint16 access_date;
+ uint16 write_time;
+ uint16 write_date;
+ uint16 create_date;
+ uint16 create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs);
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void fatfs_show_details(struct fatfs *fs);
+uint32 fatfs_get_root_cluster(struct fatfs *fs);
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
--- /dev/null
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
--- /dev/null
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+// FAT32 Offsets
+// Name Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT 0 // Length = 3
+#define BS_OEMNAME 3 // Length = 8
+#define BPB_BYTSPERSEC 11 // Length = 2
+#define BPB_SECPERCLUS 13 // Length = 1
+#define BPB_RSVDSECCNT 14 // Length = 2
+#define BPB_NUMFATS 16 // Length = 1
+#define BPB_ROOTENTCNT 17 // Length = 2
+#define BPB_TOTSEC16 19 // Length = 2
+#define BPB_MEDIA 21 // Length = 1
+#define BPB_FATSZ16 22 // Length = 2
+#define BPB_SECPERTRK 24 // Length = 2
+#define BPB_NUMHEADS 26 // Length = 2
+#define BPB_HIDDSEC 28 // Length = 4
+#define BPB_TOTSEC32 32 // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM 36 // Length = 1
+#define BS_FAT_BOOTSIG 38 // Length = 1
+#define BS_FAT_VOLID 39 // Length = 4
+#define BS_FAT_VOLLAB 43 // Length = 11
+#define BS_FAT_FILSYSTYPE 54 // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32 36 // Length = 4
+#define BPB_FAT32_EXTFLAGS 40 // Length = 2
+#define BPB_FAT32_FSVER 42 // Length = 2
+#define BPB_FAT32_ROOTCLUS 44 // Length = 4
+#define BPB_FAT32_FSINFO 48 // Length = 2
+#define BPB_FAT32_BKBOOTSEC 50 // Length = 2
+#define BS_FAT32_DRVNUM 64 // Length = 1
+#define BS_FAT32_BOOTSIG 66 // Length = 1
+#define BS_FAT32_VOLID 67 // Length = 4
+#define BS_FAT32_VOLLAB 71 // Length = 11
+#define BS_FAT32_FILSYSTYPE 82 // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12 1
+#define FAT_TYPE_FAT16 2
+#define FAT_TYPE_FAT32 3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION 510
+#define SIGNATURE_VALUE 0xAA55
+#define PARTITION1_TYPECODE_LOCATION 450
+#define FAT32_TYPECODE1 0x0B
+#define FAT32_TYPECODE2 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION 454
+#define PARTITION1_SIZE_LOCATION 458
+
+#define FAT_DIR_ENTRY_SIZE 32
+#define FAT_SFN_SIZE_FULL 11
+#define FAT_SFN_SIZE_PARTIAL 8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY 0x01
+#define FILE_ATTR_HIDDEN 0x02
+#define FILE_ATTR_SYSTEM 0x04
+#define FILE_ATTR_SYSHID 0x06
+#define FILE_ATTR_VOLUME_ID 0x08
+#define FILE_ATTR_DIRECTORY 0x10
+#define FILE_ATTR_ARCHIVE 0x20
+#define FILE_ATTR_LFN_TEXT 0x0F
+#define FILE_HEADER_BLANK 0x00
+#define FILE_HEADER_DELETED 0xE5
+#define FILE_TYPE_DIR 0x10
+#define FILE_TYPE_FILE 0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT 11
+#define FAT_TIME_HOURS_MASK 0x1F
+#define FAT_TIME_MINUTES_SHIFT 5
+#define FAT_TIME_MINUTES_MASK 0x3F
+#define FAT_TIME_SECONDS_SHIFT 0
+#define FAT_TIME_SECONDS_MASK 0x1F
+#define FAT_TIME_SECONDS_SCALE 2
+#define FAT_DATE_YEAR_SHIFT 9
+#define FAT_DATE_YEAR_MASK 0x7F
+#define FAT_DATE_MONTH_SHIFT 5
+#define FAT_DATE_MONTH_MASK 0xF
+#define FAT_DATE_DAY_SHIFT 0
+#define FAT_DATE_DAY_MASK 0x1F
+#define FAT_DATE_YEAR_OFFSET 1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER 0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER 0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+ uint8 Name[11];
+ uint8 Attr;
+ uint8 NTRes;
+ uint8 CrtTimeTenth;
+ uint8 CrtTime[2];
+ uint8 CrtDate[2];
+ uint8 LstAccDate[2];
+ uint16 FstClusHI;
+ uint8 WrtTime[2];
+ uint8 WrtDate[2];
+ uint16 FstClusLO;
+ uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
--- /dev/null
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+ #define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+ #define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+ #define SEEK_SET 0
+#endif
+
+#ifndef EOF
+ #define EOF (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+ uint32 ClusterIdx;
+ uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+ uint32 parentcluster;
+ uint32 startcluster;
+ uint32 bytenum;
+ uint32 filelength;
+ int filelength_changed;
+ char path[FATFS_MAX_LONG_FILENAME];
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+ uint32 cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+ // Cluster Lookup
+ struct cluster_lookup last_fat_lookup;
+
+ // Read/Write sector buffer
+ uint8 file_data_sector[FAT_SECTOR_SIZE];
+ uint32 file_data_address;
+ int file_data_dirty;
+
+ // File fopen flags
+ uint8 flags;
+#define FILE_READ (1 << 0)
+#define FILE_WRITE (1 << 1)
+#define FILE_APPEND (1 << 2)
+#define FILE_BINARY (1 << 3)
+#define FILE_ERASE (1 << 4)
+#define FILE_CREATE (1 << 5)
+
+ struct fat_node list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void fl_init(void);
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void fl_shutdown(void);
+
+// Standard API
+void* fl_fopen(const char *path, const char *modifiers);
+void fl_fclose(void *file);
+int fl_fflush(void *file);
+int fl_fgetc(void *file);
+char * fl_fgets(char *s, int n, void *f);
+int fl_fputc(int c, void *file);
+int fl_fputs(const char * str, void *file);
+int fl_fwrite(const void * data, int size, int count, void *file );
+int fl_fread(void * data, int size, int count, void *file );
+int fl_fseek(void *file , long offset , int origin );
+int fl_fgetpos(void *file , uint32 * position);
+long fl_ftell(void *f);
+int fl_feof(void *f);
+int fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status FL_DIR;
+typedef struct fs_dir_ent fl_dirent;
+
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir);
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int fl_closedir(FL_DIR* dir);
+
+// Extensions
+void fl_listdirectory(const char *path);
+int fl_createdirectory(const char *path);
+int fl_is_dir(const char *path);
+
+int fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE FL_FILE
+
+#define fopen(a,b) fl_fopen(a, b)
+#define fclose(a) fl_fclose(a)
+#define fflush(a) fl_fflush(a)
+#define fgetc(a) fl_fgetc(a)
+#define fgets(a,b,c) fl_fgets(a, b, c)
+#define fputc(a,b) fl_fputc(a, b)
+#define fputs(a,b) fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d) fl_fread(a, b, c, d)
+#define fseek(a,b,c) fl_fseek(a, b, c)
+#define fgetpos(a,b) fl_fgetpos(a, b)
+#define ftell(a) fl_ftell(a)
+#define feof(a) fl_feof(a)
+#define remove(a) fl_remove(a)
+#define mkdir(a) fl_createdirectory(a)
+#define rmdir(a) 0
+
+#endif
+
+#endif
--- /dev/null
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
--- /dev/null
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+ #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+ #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+ struct fat_node *previous;
+ struct fat_node *next;
+};
+
+struct fat_list
+{
+ struct fat_node *head;
+ struct fat_node *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m) p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p) (p)->next
+#define fat_list_prev(l, p) (p)->previous
+#define fat_list_first(l) (l)->head
+#define fat_list_last(l) (l)->tail
+#define fat_list_for_each(l, p) for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if(!node->previous)
+ list->head = node->next;
+ else
+ node->previous->next = node->next;
+
+ if(!node->next)
+ list->tail = node->previous;
+ else
+ node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node;
+ new_node->next = node->next;
+ if (!node->next)
+ list->tail = new_node;
+ else
+ node->next->previous = new_node;
+ node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node->previous;
+ new_node->next = node;
+ if (!node->previous)
+ list->head = new_node;
+ else
+ node->previous->next = new_node;
+ node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ node->previous = 0;
+ node->next = 0;
+ }
+ else
+ fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->tail)
+ fat_list_insert_first(list, node);
+ else
+ fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+ struct fat_node * node;
+
+ FAT_ASSERT(list);
+
+ node = fat_list_first(list);
+ if (node)
+ fat_list_remove(list, node);
+
+ return node;
+}
+
+#endif
+
--- /dev/null
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES 20
+#define MAX_LFN_ENTRY_LENGTH 13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location) ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location) ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); \
+ buffer[location+2] = (uint8)((value>>16)&0xFF); \
+ buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Structure (max 260 LFN length)
+ uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+ uint8 Null;
+#endif
+ uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int fatfs_entry_is_file(struct fat_dir_entry *entry);
+int fatfs_lfn_entries_required(char *filename);
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16 fatfs_convert_to_fat_date(int day, int month, int year);
+void fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
--- /dev/null
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+ #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+ #define FATFS_IS_LITTLE_ENDIAN 1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+ #define FATFS_MAX_LONG_FILENAME 260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+ #define FATFS_MAX_OPEN_FILES 2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES 128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+ #define FATFS_INC_WRITE_SUPPORT 1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+ #define FATFS_INC_LFN_SUPPORT 1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+ #define FATFS_DIR_LIST_SUPPORT 1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+ #define FATFS_INC_TIME_DATE_SUPPORT 0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+ #define FATFS_INC_FORMAT_SUPPORT 1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE 512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+ // Don't include stdio, but there is a printf function available
+ #ifdef FAT_PRINTF_NOINC_STDIO
+ extern int printf(const char* ctrl1, ... );
+ #define FAT_PRINTF(a) printf a
+ // Include stdio to use printf
+ #else
+ #include <stdio.h>
+ #define FAT_PRINTF(a) printf a
+ #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+ #include <time.h>
+#endif
+
+#endif
--- /dev/null
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#endif
--- /dev/null
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs);
+int fatfs_fat_purge(struct fatfs *fs);
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32 fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
--- /dev/null
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+ #if __SIZEOF_LONG__ == 8
+ #define FATFS_DEF_UINT32_AS_INT
+ #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+
+ // If compiling on a 64-bit machine, use int as 32-bits
+ #ifdef FATFS_DEF_UINT32_AS_INT
+ typedef unsigned int uint32;
+ // Else for 32-bit machines & embedded systems, use long...
+ #else
+ typedef unsigned long uint32;
+ #endif
+#endif
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+ #define FAT_HTONS(n) (n)
+ #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+ #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+ #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+ ((((uint32)(n) & 0xFF00)) << 8) | \
+ ((((uint32)(n) & 0xFF0000)) >> 8) | \
+ ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED __attribute__ ((packed))
+#else
+ // Other compilers may require other methods of packing structures
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED
+#endif
+
+#endif
--- /dev/null
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
--- /dev/null
+File IO Lib API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+void fl_init(void)\r
+\r
+ Called to initialize FAT IO library.\r
+ This should be called prior to any other functions.\r
+\r
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))\r
+\r
+ [Optional] File system thread safety locking functions.\r
+ For thread safe operation, you should provide lock() and unlock() functions.\r
+ Note that locking primitive used must support recursive locking, i.e lock() called within an already \91locked\92 region.\r
+\r
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+ This function is used to attach system specific disk/media access functions. \r
+ This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).\r
+\r
+void fl_shutdown(void)\r
+\r
+ Shutdown the FAT IO library. This purges any un-saved data back to disk.\r
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE\r
+ Version 2, June 1991\r
+\r
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.\r
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+ Preamble\r
+\r
+ The licenses for most software are designed to take away your\r
+freedom to share and change it. By contrast, the GNU General Public\r
+License is intended to guarantee your freedom to share and change free\r
+software--to make sure the software is free for all its users. This\r
+General Public License applies to most of the Free Software\r
+Foundation's software and to any other program whose authors commit to\r
+using it. (Some other Free Software Foundation software is covered by\r
+the GNU Library General Public License instead.) You can apply it to\r
+your programs, too.\r
+\r
+ When we speak of free software, we are referring to freedom, not\r
+price. Our General Public Licenses are designed to make sure that you\r
+have the freedom to distribute copies of free software (and charge for\r
+this service if you wish), that you receive source code or can get it\r
+if you want it, that you can change the software or use pieces of it\r
+in new free programs; and that you know you can do these things.\r
+\r
+ To protect your rights, we need to make restrictions that forbid\r
+anyone to deny you these rights or to ask you to surrender the rights.\r
+These restrictions translate to certain responsibilities for you if you\r
+distribute copies of the software, or if you modify it.\r
+\r
+ For example, if you distribute copies of such a program, whether\r
+gratis or for a fee, you must give the recipients all the rights that\r
+you have. You must make sure that they, too, receive or can get the\r
+source code. And you must show them these terms so they know their\r
+rights.\r
+\r
+ We protect your rights with two steps: (1) copyright the software, and\r
+(2) offer you this license which gives you legal permission to copy,\r
+distribute and/or modify the software.\r
+\r
+ Also, for each author's protection and ours, we want to make certain\r
+that everyone understands that there is no warranty for this free\r
+software. If the software is modified by someone else and passed on, we\r
+want its recipients to know that what they have is not the original, so\r
+that any problems introduced by others will not reflect on the original\r
+authors' reputations.\r
+\r
+ Finally, any free program is threatened constantly by software\r
+patents. We wish to avoid the danger that redistributors of a free\r
+program will individually obtain patent licenses, in effect making the\r
+program proprietary. To prevent this, we have made it clear that any\r
+patent must be licensed for everyone's free use or not licensed at all.\r
+\r
+ The precise terms and conditions for copying, distribution and\r
+modification follow.\r
+\r
+\r
+ GNU GENERAL PUBLIC LICENSE\r
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
+\r
+ 0. This License applies to any program or other work which contains\r
+a notice placed by the copyright holder saying it may be distributed\r
+under the terms of this General Public License. The "Program", below,\r
+refers to any such program or work, and a "work based on the Program"\r
+means either the Program or any derivative work under copyright law:\r
+that is to say, a work containing the Program or a portion of it,\r
+either verbatim or with modifications and/or translated into another\r
+language. (Hereinafter, translation is included without limitation in\r
+the term "modification".) Each licensee is addressed as "you".\r
+\r
+Activities other than copying, distribution and modification are not\r
+covered by this License; they are outside its scope. The act of\r
+running the Program is not restricted, and the output from the Program\r
+is covered only if its contents constitute a work based on the\r
+Program (independent of having been made by running the Program).\r
+Whether that is true depends on what the Program does.\r
+\r
+ 1. You may copy and distribute verbatim copies of the Program's\r
+source code as you receive it, in any medium, provided that you\r
+conspicuously and appropriately publish on each copy an appropriate\r
+copyright notice and disclaimer of warranty; keep intact all the\r
+notices that refer to this License and to the absence of any warranty;\r
+and give any other recipients of the Program a copy of this License\r
+along with the Program.\r
+\r
+You may charge a fee for the physical act of transferring a copy, and\r
+you may at your option offer warranty protection in exchange for a fee.\r
+\r
+ 2. You may modify your copy or copies of the Program or any portion\r
+of it, thus forming a work based on the Program, and copy and\r
+distribute such modifications or work under the terms of Section 1\r
+above, provided that you also meet all of these conditions:\r
+\r
+ a) You must cause the modified files to carry prominent notices\r
+ stating that you changed the files and the date of any change.\r
+\r
+ b) You must cause any work that you distribute or publish, that in\r
+ whole or in part contains or is derived from the Program or any\r
+ part thereof, to be licensed as a whole at no charge to all third\r
+ parties under the terms of this License.\r
+\r
+ c) If the modified program normally reads commands interactively\r
+ when run, you must cause it, when started running for such\r
+ interactive use in the most ordinary way, to print or display an\r
+ announcement including an appropriate copyright notice and a\r
+ notice that there is no warranty (or else, saying that you provide\r
+ a warranty) and that users may redistribute the program under\r
+ these conditions, and telling the user how to view a copy of this\r
+ License. (Exception: if the Program itself is interactive but\r
+ does not normally print such an announcement, your work based on\r
+ the Program is not required to print an announcement.)\r
+\r
+\r
+These requirements apply to the modified work as a whole. If\r
+identifiable sections of that work are not derived from the Program,\r
+and can be reasonably considered independent and separate works in\r
+themselves, then this License, and its terms, do not apply to those\r
+sections when you distribute them as separate works. But when you\r
+distribute the same sections as part of a whole which is a work based\r
+on the Program, the distribution of the whole must be on the terms of\r
+this License, whose permissions for other licensees extend to the\r
+entire whole, and thus to each and every part regardless of who wrote it.\r
+\r
+Thus, it is not the intent of this section to claim rights or contest\r
+your rights to work written entirely by you; rather, the intent is to\r
+exercise the right to control the distribution of derivative or\r
+collective works based on the Program.\r
+\r
+In addition, mere aggregation of another work not based on the Program\r
+with the Program (or with a work based on the Program) on a volume of\r
+a storage or distribution medium does not bring the other work under\r
+the scope of this License.\r
+\r
+ 3. You may copy and distribute the Program (or a work based on it,\r
+under Section 2) in object code or executable form under the terms of\r
+Sections 1 and 2 above provided that you also do one of the following:\r
+\r
+ a) Accompany it with the complete corresponding machine-readable\r
+ source code, which must be distributed under the terms of Sections\r
+ 1 and 2 above on a medium customarily used for software interchange; or,\r
+\r
+ b) Accompany it with a written offer, valid for at least three\r
+ years, to give any third party, for a charge no more than your\r
+ cost of physically performing source distribution, a complete\r
+ machine-readable copy of the corresponding source code, to be\r
+ distributed under the terms of Sections 1 and 2 above on a medium\r
+ customarily used for software interchange; or,\r
+\r
+ c) Accompany it with the information you received as to the offer\r
+ to distribute corresponding source code. (This alternative is\r
+ allowed only for noncommercial distribution and only if you\r
+ received the program in object code or executable form with such\r
+ an offer, in accord with Subsection b above.)\r
+\r
+The source code for a work means the preferred form of the work for\r
+making modifications to it. For an executable work, complete source\r
+code means all the source code for all modules it contains, plus any\r
+associated interface definition files, plus the scripts used to\r
+control compilation and installation of the executable. However, as a\r
+special exception, the source code distributed need not include\r
+anything that is normally distributed (in either source or binary\r
+form) with the major components (compiler, kernel, and so on) of the\r
+operating system on which the executable runs, unless that component\r
+itself accompanies the executable.\r
+\r
+If distribution of executable or object code is made by offering\r
+access to copy from a designated place, then offering equivalent\r
+access to copy the source code from the same place counts as\r
+distribution of the source code, even though third parties are not\r
+compelled to copy the source along with the object code.\r
+\r
+\r
+ 4. You may not copy, modify, sublicense, or distribute the Program\r
+except as expressly provided under this License. Any attempt\r
+otherwise to copy, modify, sublicense or distribute the Program is\r
+void, and will automatically terminate your rights under this License.\r
+However, parties who have received copies, or rights, from you under\r
+this License will not have their licenses terminated so long as such\r
+parties remain in full compliance.\r
+\r
+ 5. You are not required to accept this License, since you have not\r
+signed it. However, nothing else grants you permission to modify or\r
+distribute the Program or its derivative works. These actions are\r
+prohibited by law if you do not accept this License. Therefore, by\r
+modifying or distributing the Program (or any work based on the\r
+Program), you indicate your acceptance of this License to do so, and\r
+all its terms and conditions for copying, distributing or modifying\r
+the Program or works based on it.\r
+\r
+ 6. Each time you redistribute the Program (or any work based on the\r
+Program), the recipient automatically receives a license from the\r
+original licensor to copy, distribute or modify the Program subject to\r
+these terms and conditions. You may not impose any further\r
+restrictions on the recipients' exercise of the rights granted herein.\r
+You are not responsible for enforcing compliance by third parties to\r
+this License.\r
+\r
+ 7. If, as a consequence of a court judgment or allegation of patent\r
+infringement or for any other reason (not limited to patent issues),\r
+conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License. If you cannot\r
+distribute so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you\r
+may not distribute the Program at all. For example, if a patent\r
+license would not permit royalty-free redistribution of the Program by\r
+all those who receive copies directly or indirectly through you, then\r
+the only way you could satisfy both it and this License would be to\r
+refrain entirely from distribution of the Program.\r
+\r
+If any portion of this section is held invalid or unenforceable under\r
+any particular circumstance, the balance of the section is intended to\r
+apply and the section as a whole is intended to apply in other\r
+circumstances.\r
+\r
+It is not the purpose of this section to induce you to infringe any\r
+patents or other property right claims or to contest validity of any\r
+such claims; this section has the sole purpose of protecting the\r
+integrity of the free software distribution system, which is\r
+implemented by public license practices. Many people have made\r
+generous contributions to the wide range of software distributed\r
+through that system in reliance on consistent application of that\r
+system; it is up to the author/donor to decide if he or she is willing\r
+to distribute software through any other system and a licensee cannot\r
+impose that choice.\r
+\r
+This section is intended to make thoroughly clear what is believed to\r
+be a consequence of the rest of this License.\r
+\r
+\r
+ 8. If the distribution and/or use of the Program is restricted in\r
+certain countries either by patents or by copyrighted interfaces, the\r
+original copyright holder who places the Program under this License\r
+may add an explicit geographical distribution limitation excluding\r
+those countries, so that distribution is permitted only in or among\r
+countries not thus excluded. In such case, this License incorporates\r
+the limitation as if written in the body of this License.\r
+\r
+ 9. The Free Software Foundation may publish revised and/or new versions\r
+of the General Public License from time to time. Such new versions will\r
+be similar in spirit to the present version, but may differ in detail to\r
+address new problems or concerns.\r
+\r
+Each version is given a distinguishing version number. If the Program\r
+specifies a version number of this License which applies to it and "any\r
+later version", you have the option of following the terms and conditions\r
+either of that version or of any later version published by the Free\r
+Software Foundation. If the Program does not specify a version number of\r
+this License, you may choose any version ever published by the Free Software\r
+Foundation.\r
+\r
+ 10. If you wish to incorporate parts of the Program into other free\r
+programs whose distribution conditions are different, write to the author\r
+to ask for permission. For software which is copyrighted by the Free\r
+Software Foundation, write to the Free Software Foundation; we sometimes\r
+make exceptions for this. Our decision will be guided by the two goals\r
+of preserving the free status of all derivatives of our free software and\r
+of promoting the sharing and reuse of software generally.\r
+\r
+ NO WARRANTY\r
+\r
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\r
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\r
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\r
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r
+REPAIR OR CORRECTION.\r
+\r
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r
+POSSIBILITY OF SUCH DAMAGES.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+\r
+ How to Apply These Terms to Your New Programs\r
+\r
+ If you develop a new program, and you want it to be of the greatest\r
+possible use to the public, the best way to achieve this is to make it\r
+free software which everyone can redistribute and change under these terms.\r
+\r
+ To do so, attach the following notices to the program. It is safest\r
+to attach them to the start of each source file to most effectively\r
+convey the exclusion of warranty; and each file should have at least\r
+the "copyright" line and a pointer to where the full notice is found.\r
+\r
+ <one line to give the program's name and a brief idea of what it does.>\r
+ Copyright (C) <year> <name of author>\r
+\r
+ This program is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU General Public License as published by\r
+ the Free Software Foundation; either version 2 of the License, or\r
+ (at your option) any later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with this program; if not, write to the Free Software\r
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+If the program is interactive, make it output a short notice like this\r
+when it starts in an interactive mode:\r
+\r
+ Gnomovision version 69, Copyright (C) year name of author\r
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
+ This is free software, and you are welcome to redistribute it\r
+ under certain conditions; type `show c' for details.\r
+\r
+The hypothetical commands `show w' and `show c' should show the appropriate\r
+parts of the General Public License. Of course, the commands you use may\r
+be called something other than `show w' and `show c'; they could even be\r
+mouse-clicks or menu items--whatever suits your program.\r
+\r
+You should also get your employer (if you work as a programmer) or your\r
+school, if any, to sign a "copyright disclaimer" for the program, if\r
+necessary. Here is a sample; alter the names:\r
+\r
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program\r
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.\r
+\r
+ <signature of Ty Coon>, 1 April 1989\r
+ Ty Coon, President of Vice\r
+\r
+This General Public License does not permit incorporating your program into\r
+proprietary programs. If your program is a subroutine library, you may\r
+consider it more useful to permit linking proprietary applications with the\r
+library. If this is what you want to do, use the GNU Library General\r
+Public License instead of this License.\r
--- /dev/null
+File IO Lib Options\r
+-=-=-=-=-=-=-=-=-=-\r
+\r
+See defines in fat_opts.h:\r
+\r
+FATFS_IS_LITTLE_ENDIAN [1/0]\r
+ Which endian is your system? Set to 1 for little endian, 0 for big endian.\r
+\r
+FATFS_MAX_LONG_FILENAME [260]\r
+ By default, 260 characters (max LFN length). Increase this to support greater path depths.\r
+\r
+FATFS_MAX_OPEN_FILES \r
+ The more files you wish to have concurrently open, the greater this number should be.\r
+ This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).\r
+\r
+FAT_BUFFER_SECTORS\r
+ Minimum is 1, more increases performance.\r
+ This defines how many FAT sectors can be buffered per FAT_BUFFER entry.\r
+\r
+FAT_BUFFERS\r
+ Minimum is 1, more increases performance.\r
+ This defines how many FAT buffer entries are available.\r
+ Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE\r
+\r
+FATFS_INC_WRITE_SUPPORT\r
+ Support file write functionality.\r
+\r
+FAT_SECTOR_SIZE\r
+ Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).\r
+\r
+FAT_PRINTF\r
+ A define that allows the File IO library to print to console/stdout. \r
+ Provide your own printf function if printf not available.\r
+\r
+FAT_CLUSTER_CACHE_ENTRIES\r
+ Size of cluster chain cache (can be undefined if not required).\r
+ Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2\r
+ Improves access speed considerably.\r
+\r
+FATFS_INC_LFN_SUPPORT [1/0]\r
+ Enable/Disable support for long filenames.\r
+\r
+FATFS_DIR_LIST_SUPPORT [1/0]\r
+ Include support for directory listing.\r
+\r
+FATFS_INC_TIME_DATE_SUPPORT [1/0]\r
+ Use time/date functions provided by time.h to update creation & modification timestamps.\r
+\r
+FATFS_INC_FORMAT_SUPPORT\r
+ Include support for formatting disks (FAT16 only).\r
+\r
+FAT_PRINTF_NOINC_STDIO\r
+ Disable use of printf & inclusion of stdio.h\r
--- /dev/null
+Revision History\r
+-=-=-=-=-=-=-=-=-\r
+v2.6.11 - Fix compilation with GCC on 64-bit machines\r
+v2.6.10 - Added support for FAT32 format.\r
+V2.6.9 - Added support for time & date handling.\r
+V2.6.8 - Fixed error with FSINFO sector write.\r
+V2.6.7 - Added fgets().\r
+ Fixed C warnings, removed dependancy on some string.h functions.\r
+V2.6.6 \96 Massive read + write performance improvements.\r
+V2.6.5 \96 Bug fixes for big endian systems.\r
+V2.6.4 \96 Further bug fixes and performance improvements for write operations.\r
+V2.6.3 \96 Peformance improvements, FAT16 formatting support. Various bug fixes.\r
+V2.6 - Basic support for FAT16 added (18-04-10).\r
+V2.5 - Code cleaned up. Many bugs fixed. Thread safety functions added.\r
+V2.x - Write support added as well as better stdio like API.\r
+V1.0 - Rewrite of all code to enable multiple files to be opened and provides a \r
+ better file API.\r
+ Also better string matching, and generally better C code than origonal \r
+ version.\r
+V0.1c - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping \r
+ the drive reads from addressing a sector that is out of range.\r
+V0.1b - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32 \r
+ access.\r
+V0.1a - First release (27/12/03); fopen(), fgetc() unbuffered reads.\r
--- /dev/null
+FAT File IO Library License\r
+-=-=-=-=-=-=-=-=-=-=-=-=-=-\r
+\r
+This versions license: GPL\r
+\r
+If you include GPL software in your project, you must release the source code of that project too.\r
+\r
+If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.\r
+\r
+Email: admin@ultra-embedded.com\r
--- /dev/null
+Media Access API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+ Sector: 32-bit sector number\r
+ Buffer: Target buffer to read n sectors of data into.\r
+ Sector_count: Number of sectors to read.\r
+\r
+Return: \r
+ int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+ Application/target specific disk/media read function.\r
+ Sector number (sectors are usually 512 byte pages) to read.\r
+\r
+Media Write API\r
+\r
+int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+ Sector: 32-bit sector number\r
+ Buffer: Target buffer to write n sectors of data from.\r
+ Sector_count: Number of sectors to write.\r
+\r
+Return: \r
+ int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+ Application/target specific disk/media write function.\r
+ Sector number (sectors are usually 512 byte pages) to write to.\r
+\r
+File IO Library Linkage\r
+ Use the following API to attach the media IO functions to the File IO library.\r
+\r
+ int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+\r
+\r
--- /dev/null
+#include <stdio.h>\r
+#include "fat_filelib.h"\r
+\r
+int media_init()\r
+{\r
+ // ...\r
+ return 1;\r
+}\r
+\r
+int media_read(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+ unsigned long i;\r
+\r
+ for (i=0;i<sector_count;i++)\r
+ {\r
+ // ...\r
+ // Add platform specific sector (512 bytes) read code here\r
+ //..\r
+\r
+ sector ++;\r
+ buffer += 512;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+int media_write(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+ unsigned long i;\r
+\r
+ for (i=0;i<sector_count;i++)\r
+ {\r
+ // ...\r
+ // Add platform specific sector (512 bytes) write code here\r
+ //..\r
+\r
+ sector ++;\r
+ buffer += 512;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+void main()\r
+{\r
+ FL_FILE *file;\r
+\r
+ // Initialise media\r
+ media_init();\r
+\r
+ // Initialise File IO Library\r
+ fl_init();\r
+\r
+ // Attach media access functions to library\r
+ if (fl_attach_media(media_read, media_write) != FAT_INIT_OK)\r
+ {\r
+ printf("ERROR: Media attach failed\n");\r
+ return; \r
+ }\r
+\r
+ // List root directory\r
+ fl_listdirectory("/");\r
+\r
+ // Create File\r
+ file = fl_fopen("/file.bin", "w");\r
+ if (file)\r
+ {\r
+ // Write some data\r
+ unsigned char data[] = { 1, 2, 3, 4 };\r
+ if (fl_fwrite(data, 1, sizeof(data), file) != sizeof(data))\r
+ printf("ERROR: Write file failed\n");\r
+ }\r
+ else\r
+ printf("ERROR: Create file failed\n");\r
+\r
+ // Close file\r
+ fl_fclose(file);\r
+\r
+ // Delete File\r
+ if (fl_remove("/file.bin") < 0)\r
+ printf("ERROR: Delete file failed\n");\r
+\r
+ // List root directory\r
+ fl_listdirectory("/");\r
+\r
+ fl_shutdown();\r
+}\r
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_init: Load FAT Parameters
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs)
+{
+ uint8 num_of_fats;
+ uint16 reserved_sectors;
+ uint32 FATSz;
+ uint32 root_dir_sectors;
+ uint32 total_sectors;
+ uint32 data_sectors;
+ uint32 count_of_clusters;
+ uint8 valid_partition = 0;
+
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have a read function (write function is optional)
+ if (!fs->disk_io.read_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // MBR: Sector 0 on the disk
+ // NOTE: Some removeable media does not have this.
+
+ // Load MBR (LBA 0) into the 512 byte buffer
+ if (!fs->disk_io.read_media(0, fs->currentsector.sector, 1))
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Make Sure 0x55 and 0xAA are at end of sector
+ // (this should be the case regardless of the MBR or boot sector)
+ if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
+ return FAT_INIT_INVALID_SIGNATURE;
+
+ // Now check again using the access function to prove endian conversion function
+ if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
+ return FAT_INIT_ENDIAN_ERROR;
+
+ // Verify packed structures
+ if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
+ return FAT_INIT_STRUCT_PACKING;
+
+ // Check the partition type code
+ switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
+ {
+ case 0x0B:
+ case 0x06:
+ case 0x0C:
+ case 0x0E:
+ case 0x0F:
+ case 0x05:
+ valid_partition = 1;
+ break;
+ case 0x00:
+ valid_partition = 0;
+ break;
+ default:
+ if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
+ valid_partition = 1;
+ break;
+ }
+
+ // Read LBA Begin for the file system
+ if (valid_partition)
+ fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
+ // Else possibly MBR less disk
+ else
+ fs->lba_begin = 0;\r
+\r
+ // Load Volume 1 table into sector buffer
+ // (We may already have this in the buffer if MBR less drive!)
+ if (!fs->disk_io.read_media(fs->lba_begin, fs->currentsector.sector, 1))
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Make sure there are 512 bytes per cluster
+ if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
+ return FAT_INIT_INVALID_SECTOR_SIZE;\r
+
+ // Load Parameters of FAT partition
+ fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
+ reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
+ num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
+ fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+ fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+ else
+ fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+ // For FAT32 (which this may be)
+ fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
+ fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
+
+ // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+ fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
+ fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
+
+ if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
+ return FAT_INIT_INVALID_SIGNATURE;
+
+ // Calculate the root dir sectors
+ root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+ FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+ else
+ FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
+ total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
+ else
+ total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
+
+ data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
+
+ // Find out which version of FAT this is...
+ if (fs->sectors_per_cluster != 0)
+ {
+ count_of_clusters = data_sectors / fs->sectors_per_cluster;
+
+ if(count_of_clusters < 4085)
+ // Volume is FAT12
+ return FAT_INIT_WRONG_FILESYS_TYPE;
+ else if(count_of_clusters < 65525)
+ {
+ // Clear this FAT32 specific param
+ fs->rootdir_first_cluster = 0;
+
+ // Volume is FAT16
+ fs->fat_type = FAT_TYPE_16;
+ return FAT_INIT_OK;
+ }
+ else
+ {
+ // Volume is FAT32
+ fs->fat_type = FAT_TYPE_32;
+ return FAT_INIT_OK;
+ }
+ }
+ else
+ return FAT_INIT_WRONG_FILESYS_TYPE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
+// LBA number.
+//-----------------------------------------------------------------------------
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
+{
+ if (fs->fat_type == FAT_TYPE_16)
+ return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
+ else
+ return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_read:
+//-----------------------------------------------------------------------------
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+ return fs->disk_io.read_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_write:
+//-----------------------------------------------------------------------------
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+ return fs->disk_io.write_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_reader: From the provided startcluster and sector offset
+// Returns True if success, returns False if not (including if read out of range)
+//-----------------------------------------------------------------------------
+int fatfs_sector_reader(struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
+{
+ uint32 sector_to_read = 0;
+ uint32 cluster_to_read = 0;
+ uint32 cluster_chain = 0;
+ uint32 i;
+ uint32 lba;
+
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
+ {
+ if (offset < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + offset;
+ else
+ return 0;
+ }
+ // FAT16/32 Other
+ else
+ {
+ // Set start of cluster chain to initial value
+ cluster_chain = start_cluster;
+
+ // Find parameters
+ cluster_to_read = offset / fs->sectors_per_cluster;
+ sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
+
+ // Follow chain to find cluster to read
+ for (i=0; i<cluster_to_read; i++)
+ cluster_chain = fatfs_find_next_cluster(fs, cluster_chain);
+
+ // If end of cluster chain then return false
+ if (cluster_chain == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Calculate sector address
+ lba = fatfs_lba_of_cluster(fs, cluster_chain)+sector_to_read;
+ }
+
+ // User provided target array
+ if (target)
+ return fs->disk_io.read_media(lba, target, 1);
+ // Else read sector if not already loaded
+ else if (lba != fs->currentsector.address)
+ {
+ fs->currentsector.address = lba;
+ return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ else
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_read_sector: Read from the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+ {
+ uint32 lba;
+
+ // In FAT16, there are a limited amount of sectors in root dir!
+ if (sector < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+ else
+ return 0;
+
+ // User target buffer passed in
+ if (target)
+ {
+ // Read from disk
+ return fs->disk_io.read_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate read address
+ fs->currentsector.address = lba;
+
+ // Read from disk
+ return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ // FAT16/32 Other
+ else
+ {
+ // User target buffer passed in
+ if (target)
+ {
+ // Calculate read address
+ uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+ // Read from disk
+ return fs->disk_io.read_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+ // Read from disk
+ return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_write_sector: Write to the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+ {
+ uint32 lba;
+
+ // In FAT16 we cannot extend the root dir!
+ if (sector < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+ else
+ return 0;
+
+ // User target buffer passed in
+ if (target)
+ {
+ // Write to disk
+ return fs->disk_io.write_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = lba;
+
+ // Write to disk
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ // FAT16/32 Other
+ else
+ {
+ // User target buffer passed in
+ if (target)
+ {
+ // Calculate write address
+ uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+ // Write to disk
+ return fs->disk_io.write_media(lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+ // Write to disk
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_show_details: Show the details about the filesystem
+//-----------------------------------------------------------------------------
+void fatfs_show_details(struct fatfs *fs)
+{
+ FAT_PRINTF(("FAT details:\r\n"));
+ FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
+ FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
+ FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
+ FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
+ FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_root_cluster: Get the root dir cluster
+//-----------------------------------------------------------------------------
+uint32 fatfs_get_root_cluster(struct fatfs *fs)
+{
+ // NOTE: On FAT16 this will be 0 which has a special meaning...
+ return fs->rootdir_first_cluster;
+}
+//-------------------------------------------------------------
+// fatfs_get_file_entry: Find the file entry for a filename
+//-------------------------------------------------------------
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ int x=0;
+ char *long_filename = NULL;
+ char short_filename[13];
+ struct lfn_cache lfn;
+ int dotRequired = 0;
+ struct fat_dir_entry *directoryEntry;
+
+ fatfs_lfn_cache_init(&lfn, 1);
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ // Normal SFN Entry and Long text exists
+ else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+ {
+ long_filename = fatfs_lfn_cache_get(&lfn);
+
+ // Compare names to see if they match
+ if (fatfs_compare_names(long_filename, name_to_find))
+ {
+ memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+ return 1;
+ }
+
+ fatfs_lfn_cache_init(&lfn, 0);
+ }
+ else
+#endif
+ // Normal Entry, only 8.3 Text
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ memset(short_filename, 0, sizeof(short_filename));
+
+ // Copy name to string
+ for (i=0; i<8; i++)
+ short_filename[i] = directoryEntry->Name[i];
+
+ // Extension
+ dotRequired = 0;
+ for (i=8; i<11; i++)
+ {
+ short_filename[i+1] = directoryEntry->Name[i];
+ if (directoryEntry->Name[i] != ' ')
+ dotRequired = 1;
+ }
+
+ // Dot only required if extension present
+ if (dotRequired)
+ {
+ // If not . or .. entry
+ if (short_filename[0]!='.')
+ short_filename[8] = '.';
+ else
+ short_filename[8] = ' ';
+ }
+ else
+ short_filename[8] = ' ';
+
+ // Compare names to see if they match
+ if (fatfs_compare_names(short_filename, name_to_find))
+ {
+ memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+ return 1;
+ }
+
+ fatfs_lfn_cache_init(&lfn, 0);
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+//-------------------------------------------------------------
+// fatfs_sfn_exists: Check if a short filename exists.
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+ else
+#endif
+ // Normal Entry, only 8.3 Text
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+ return 1;
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_timestamps: Update date/time details
+//-------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
+{
+ time_t time_now;
+ struct tm * time_info;
+ uint16 fat_time;
+ uint16 fat_date;
+
+ // Get system time
+ time(&time_now);
+
+ // Convert to local time
+ time_info = localtime(&time_now);
+
+ // Convert time to FAT format
+ fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+
+ // Convert date to FAT format
+ fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
+
+ // Update requested fields
+ if (create)
+ {
+ directoryEntry->CrtTime[1] = fat_time >> 8;
+ directoryEntry->CrtTime[0] = fat_time >> 0;
+ directoryEntry->CrtDate[1] = fat_date >> 8;
+ directoryEntry->CrtDate[0] = fat_date >> 0;
+ }
+
+ if (modify)
+ {
+ directoryEntry->WrtTime[1] = fat_time >> 8;
+ directoryEntry->WrtTime[0] = fat_time >> 0;
+ directoryEntry->WrtDate[1] = fat_date >> 8;
+ directoryEntry->WrtDate[0] = fat_date >> 0;
+ }
+
+ if (access)
+ {
+ directoryEntry->LstAccDate[1] = fat_time >> 8;
+ directoryEntry->LstAccDate[0] = fat_time >> 0;
+ directoryEntry->LstAccDate[1] = fat_date >> 8;
+ directoryEntry->LstAccDate[0] = fat_date >> 0;
+ }
+
+ return 1;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_file_length: Find a SFN entry and update it
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+ {
+ directoryEntry->FileSize = FAT_HTONL(fileLength);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update access / modify time & date
+ fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+ // Update sfn entry
+ memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+ // Write sector back
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
+ {
+ // Mark as deleted
+ directoryEntry->Name[0] = FILE_HEADER_DELETED;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update access / modify time & date
+ fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+ // Update sfn entry
+ memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+ // Write sector back
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_start: Initialise a directory listing procedure
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
+{
+ dirls->cluster = StartCluster;
+ dirls->sector = 0;
+ dirls->offset = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_next: Get the next entry in the directory.
+// Returns: 1 = found, 0 = end of listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
+{
+ uint8 i,item;
+ uint16 recordoffset;
+ struct fat_dir_entry *directoryEntry;
+ char *long_filename = NULL;
+ char short_filename[13];
+ struct lfn_cache lfn;
+ int dotRequired = 0;
+ int result = 0;
+
+ // Initialise LFN cache first
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ while (1)
+ {
+ // If data read OK
+ if (fatfs_sector_reader(fs, dirls->cluster, dirls->sector, 0))
+ {
+ // Maximum of 16 directory entries
+ for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Increase directory offset
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if ( fatfs_entry_lfn_text(directoryEntry) )
+ fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+ // If Invalid record found delete any long file name information collated
+ else if ( fatfs_entry_lfn_invalid(directoryEntry) )
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ // Normal SFN Entry and Long text exists
+ else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+ {
+ // Get text
+ long_filename = fatfs_lfn_cache_get(&lfn);
+ strncpy(entry->filename, long_filename, FATFS_MAX_LONG_FILENAME-1);
+
+ if (fatfs_entry_is_dir(directoryEntry))
+ entry->is_dir = 1;
+ else
+ entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Get time / dates
+ entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+ entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+ entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+ entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+ entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+ entry->size = FAT_HTONL(directoryEntry->FileSize);
+ entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+ // Next starting position
+ dirls->offset = item + 1;
+ result = 1;
+ return 1;
+ }
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if ( fatfs_entry_sfn_only(directoryEntry) )
+ {
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ memset(short_filename, 0, sizeof(short_filename));
+
+ // Copy name to string
+ for (i=0; i<8; i++)
+ short_filename[i] = directoryEntry->Name[i];
+
+ // Extension
+ dotRequired = 0;
+ for (i=8; i<11; i++)
+ {
+ short_filename[i+1] = directoryEntry->Name[i];
+ if (directoryEntry->Name[i] != ' ')
+ dotRequired = 1;
+ }
+
+ // Dot only required if extension present
+ if (dotRequired)
+ {
+ // If not . or .. entry
+ if (short_filename[0]!='.')
+ short_filename[8] = '.';
+ else
+ short_filename[8] = ' ';
+ }
+ else
+ short_filename[8] = ' ';
+
+ fatfs_get_sfn_display_name(entry->filename, short_filename);
+
+ if (fatfs_entry_is_dir(directoryEntry))
+ entry->is_dir = 1;
+ else
+ entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Get time / dates
+ entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+ entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+ entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+ entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+ entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+ entry->size = FAT_HTONL(directoryEntry->FileSize);
+ entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+ // Next starting position
+ dirls->offset = item + 1;
+ result = 1;
+ return 1;
+ }
+ }// end of for
+
+ // If reached end of the dir move onto next sector
+ dirls->sector++;
+ dirls->offset = 0;
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+#endif
--- /dev/null
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK 0
+#define FAT_INIT_MEDIA_ACCESS_ERROR (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE (-2)
+#define FAT_INIT_INVALID_SIGNATURE (-3)
+#define FAT_INIT_ENDIAN_ERROR (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE (-6)
+#define FAT_INIT_STRUCT_PACKING (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+ // User supplied function pointers for disk IO
+ fn_diskio_read read_media;
+ fn_diskio_write write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+ uint8 sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+ uint32 address;
+ int dirty;
+ uint8 * ptr;
+
+ // Next in chain of sector buffers
+ struct fat_buffer *next;
+};
+
+typedef enum eFatType
+{
+ FAT_TYPE_16,
+ FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+ // Filesystem globals
+ uint8 sectors_per_cluster;
+ uint32 cluster_begin_lba;
+ uint32 rootdir_first_cluster;
+ uint32 rootdir_first_sector;
+ uint32 rootdir_sectors;
+ uint32 fat_begin_lba;
+ uint16 fs_info_sector;
+ uint32 lba_begin;
+ uint32 fat_sectors;
+ uint32 next_free_cluster;
+ uint16 root_entry_count;
+ uint16 reserved_sectors;
+ uint8 num_of_fats;
+ tFatType fat_type;
+
+ // Disk/Media API
+ struct disk_if disk_io;
+
+ // [Optional] Thread Safety
+ void (*fl_lock)(void);
+ void (*fl_unlock)(void);
+
+ // Working buffer
+ struct fat_buffer currentsector;
+
+ // FAT Buffer
+ struct fat_buffer *fat_buffer_head;
+ struct fat_buffer fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+ uint32 sector;
+ uint32 cluster;
+ uint8 offset;
+};
+
+struct fs_dir_ent
+{
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 is_dir;
+ uint32 cluster;
+ uint32 size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ uint16 access_date;
+ uint16 write_time;
+ uint16 write_date;
+ uint16 create_date;
+ uint16 create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs);
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void fatfs_show_details(struct fatfs *fs);
+uint32 fatfs_get_root_cluster(struct fatfs *fs);
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_cache.h"
+
+// Per file cluster chain caching used to improve performance.
+// This does not have to be enabled for architectures with low
+// memory space.
+
+//-----------------------------------------------------------------------------
+// fatfs_cache_init:
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ int i;
+
+ for (i=0;i<FAT_CLUSTER_CACHE_ENTRIES;i++)
+ {
+ file->cluster_cache_idx[i] = 0xFFFFFFFF; // Not used
+ file->cluster_cache_data[i] = 0;
+ }
+#endif
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_get_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+ if (file->cluster_cache_idx[slot] == clusterIdx)
+ {
+ *pNextCluster = file->cluster_cache_data[slot];
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_set_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+ if (file->cluster_cache_idx[slot] == clusterIdx)
+ file->cluster_cache_data[slot] = nextCluster;
+ else
+ {
+ file->cluster_cache_idx[slot] = clusterIdx;
+ file->cluster_cache_data[slot] = nextCluster;
+ }
+#endif
+
+ return 1;
+}
--- /dev/null
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
--- /dev/null
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+// FAT32 Offsets
+// Name Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT 0 // Length = 3
+#define BS_OEMNAME 3 // Length = 8
+#define BPB_BYTSPERSEC 11 // Length = 2
+#define BPB_SECPERCLUS 13 // Length = 1
+#define BPB_RSVDSECCNT 14 // Length = 2
+#define BPB_NUMFATS 16 // Length = 1
+#define BPB_ROOTENTCNT 17 // Length = 2
+#define BPB_TOTSEC16 19 // Length = 2
+#define BPB_MEDIA 21 // Length = 1
+#define BPB_FATSZ16 22 // Length = 2
+#define BPB_SECPERTRK 24 // Length = 2
+#define BPB_NUMHEADS 26 // Length = 2
+#define BPB_HIDDSEC 28 // Length = 4
+#define BPB_TOTSEC32 32 // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM 36 // Length = 1
+#define BS_FAT_BOOTSIG 38 // Length = 1
+#define BS_FAT_VOLID 39 // Length = 4
+#define BS_FAT_VOLLAB 43 // Length = 11
+#define BS_FAT_FILSYSTYPE 54 // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32 36 // Length = 4
+#define BPB_FAT32_EXTFLAGS 40 // Length = 2
+#define BPB_FAT32_FSVER 42 // Length = 2
+#define BPB_FAT32_ROOTCLUS 44 // Length = 4
+#define BPB_FAT32_FSINFO 48 // Length = 2
+#define BPB_FAT32_BKBOOTSEC 50 // Length = 2
+#define BS_FAT32_DRVNUM 64 // Length = 1
+#define BS_FAT32_BOOTSIG 66 // Length = 1
+#define BS_FAT32_VOLID 67 // Length = 4
+#define BS_FAT32_VOLLAB 71 // Length = 11
+#define BS_FAT32_FILSYSTYPE 82 // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12 1
+#define FAT_TYPE_FAT16 2
+#define FAT_TYPE_FAT32 3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION 510
+#define SIGNATURE_VALUE 0xAA55
+#define PARTITION1_TYPECODE_LOCATION 450
+#define FAT32_TYPECODE1 0x0B
+#define FAT32_TYPECODE2 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION 454
+#define PARTITION1_SIZE_LOCATION 458
+
+#define FAT_DIR_ENTRY_SIZE 32
+#define FAT_SFN_SIZE_FULL 11
+#define FAT_SFN_SIZE_PARTIAL 8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY 0x01
+#define FILE_ATTR_HIDDEN 0x02
+#define FILE_ATTR_SYSTEM 0x04
+#define FILE_ATTR_SYSHID 0x06
+#define FILE_ATTR_VOLUME_ID 0x08
+#define FILE_ATTR_DIRECTORY 0x10
+#define FILE_ATTR_ARCHIVE 0x20
+#define FILE_ATTR_LFN_TEXT 0x0F
+#define FILE_HEADER_BLANK 0x00
+#define FILE_HEADER_DELETED 0xE5
+#define FILE_TYPE_DIR 0x10
+#define FILE_TYPE_FILE 0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT 11
+#define FAT_TIME_HOURS_MASK 0x1F
+#define FAT_TIME_MINUTES_SHIFT 5
+#define FAT_TIME_MINUTES_MASK 0x3F
+#define FAT_TIME_SECONDS_SHIFT 0
+#define FAT_TIME_SECONDS_MASK 0x1F
+#define FAT_TIME_SECONDS_SCALE 2
+#define FAT_DATE_YEAR_SHIFT 9
+#define FAT_DATE_YEAR_MASK 0x7F
+#define FAT_DATE_MONTH_SHIFT 5
+#define FAT_DATE_MONTH_MASK 0xF
+#define FAT_DATE_DAY_SHIFT 0
+#define FAT_DATE_DAY_MASK 0x1F
+#define FAT_DATE_YEAR_OFFSET 1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER 0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER 0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+ uint8 Name[11];
+ uint8 Attr;
+ uint8 NTRes;
+ uint8 CrtTimeTenth;
+ uint8 CrtTime[2];
+ uint8 CrtDate[2];
+ uint8 LstAccDate[2];
+ uint16 FstClusHI;
+ uint8 WrtTime[2];
+ uint8 WrtDate[2];
+ uint16 FstClusLO;
+ uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_misc.h"
+#include "fat_string.h"
+#include "fat_filelib.h"
+#include "fat_cache.h"
+
+//-----------------------------------------------------------------------------
+// Locals
+//-----------------------------------------------------------------------------
+static FL_FILE _files[FATFS_MAX_OPEN_FILES];
+static int _filelib_init = 0;
+static int _filelib_valid = 0;
+static struct fatfs _fs;
+static struct fat_list _open_file_list;
+static struct fat_list _free_file_list;
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+
+// Macro for checking if file lib is initialised
+#define CHECK_FL_INIT() { if (_filelib_init==0) fl_init(); }
+
+#define FL_LOCK(a) do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
+#define FL_UNLOCK(a) do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
+
+//-----------------------------------------------------------------------------
+// Local Functions
+//-----------------------------------------------------------------------------
+static void _fl_init();
+
+//-----------------------------------------------------------------------------
+// _allocate_file: Find a slot in the open files buffer for a new file
+//-----------------------------------------------------------------------------
+static FL_FILE* _allocate_file(void)
+{
+ // Allocate free file
+ struct fat_node *node = fat_list_pop_head(&_free_file_list);
+
+ // Add to open list
+ if (node)
+ fat_list_insert_last(&_open_file_list, node);
+
+ return fat_list_entry(node, FL_FILE, list_node);
+}
+//-----------------------------------------------------------------------------
+// _check_file_open: Returns true if the file is already open
+//-----------------------------------------------------------------------------
+static int _check_file_open(FL_FILE* file)
+{
+ struct fat_node *node;
+
+ // Compare open files
+ fat_list_for_each(&_open_file_list, node)
+ {
+ FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
+
+ // If not the current file
+ if (openFile != file)
+ {
+ // Compare path and name
+ if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
+ return 1;
+ }
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// _free_file: Free open file handle
+//-----------------------------------------------------------------------------
+static void _free_file(FL_FILE* file)
+{
+ // Remove from open list
+ fat_list_remove(&_open_file_list, &file->list_node);
+
+ // Add to free list
+ fat_list_insert_last(&_free_file_list, &file->list_node);
+}
+
+//-----------------------------------------------------------------------------
+// Low Level
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// _open_directory: Cycle through path string to find the start cluster
+// address of the highest subdir.
+//-----------------------------------------------------------------------------
+static int _open_directory(char *path, uint32 *pathCluster)
+{
+ int levels;
+ int sublevel;
+ char currentfolder[FATFS_MAX_LONG_FILENAME];
+ struct fat_dir_entry sfEntry;
+ uint32 startcluster;
+
+ // Set starting cluster to root cluster
+ startcluster = fatfs_get_root_cluster(&_fs);
+
+ // Find number of levels
+ levels = fatfs_total_path_levels(path);
+
+ // Cycle through each level and get the start sector
+ for (sublevel=0;sublevel<(levels+1);sublevel++)
+ {
+ if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
+ return 0;
+
+ // Find clusteraddress for folder (currentfolder)
+ if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
+ {
+ // Check entry is folder
+ if (fatfs_entry_is_dir(&sfEntry))
+ startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ *pathCluster = startcluster;
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// _create_directory: Cycle through path string and create the end directory
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static int _create_directory(char *path)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+ char shortFilename[FAT_SFN_SIZE_FULL];
+ int tailNum = 0;
+ int i;
+
+ // Allocate a new file handle
+ file = _allocate_file();
+ if (!file)
+ return 0;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ // Check if file already open
+ if (_check_file_open(file))
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ // If file is in the root dir
+ if (file->path[0] == 0)
+ file->parentcluster = fatfs_get_root_cluster(&_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(file->path, &file->parentcluster))
+ {
+ _free_file(file);
+ return 0;
+ }
+ }
+
+ // Check if same filename exists in directory
+ if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ file->startcluster = 0;
+
+ // Create the file space for the folder (at least one clusters worth!)
+ if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+ {
+ _free_file(file);
+ return 0;
+ }
+
+ // Erase new directory cluster
+ memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+ for (i=0;i<_fs.sectors_per_cluster;i++)
+ {
+ if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
+ {
+ _free_file(file);
+ return 0;
+ }
+ }
+
+#if FATFS_INC_LFN_SUPPORT
+
+ // Generate a short filename & tail
+ tailNum = 0;
+ do
+ {
+ // Create a standard short filename (without tail)
+ fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+ // If second hit or more, generate a ~n tail
+ if (tailNum != 0)
+ fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+ // Try with no tail if first entry
+ else
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already or not
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+ break;
+
+ tailNum++;
+ }
+ while (tailNum < 9999);
+
+ // We reached the max number of duplicate short file names (unlikely!)
+ if (tailNum == 9999)
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+#else
+ // Create a standard short filename (without tail)
+ if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+
+ // Copy to SFN space
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+#endif
+
+ // Add file to disk
+ if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return 0;
+ }
+
+ // General
+ file->filelength = 0;
+ file->bytenum = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_fat_purge(&_fs);
+
+ _free_file(file);
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _open_file: Open a file for reading
+//-----------------------------------------------------------------------------
+static FL_FILE* _open_file(const char *path)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+
+ // Allocate a new file handle
+ file = _allocate_file();
+ if (!file)
+ return NULL;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // Check if file already open
+ if (_check_file_open(file))
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // If file is in the root dir
+ if (file->path[0]==0)
+ file->parentcluster = fatfs_get_root_cluster(&_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(file->path, &file->parentcluster))
+ {
+ _free_file(file);
+ return NULL;
+ }
+ }
+
+ // Using dir cluster address search for filename
+ if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
+ // Make sure entry is file not dir!
+ if (fatfs_entry_is_file(&sfEntry))
+ {
+ // Initialise file details
+ memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
+ file->filelength = FAT_HTONL(sfEntry.FileSize);
+ file->bytenum = 0;
+ file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_cache_init(&_fs, file);
+
+ fatfs_fat_purge(&_fs);
+
+ return file;
+ }
+
+ _free_file(file);
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+// _create_file: Create a new file
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static FL_FILE* _create_file(const char *filename)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+ char shortFilename[FAT_SFN_SIZE_FULL];
+ int tailNum = 0;
+
+ // No write access?
+ if (!_fs.disk_io.write_media)
+ return NULL;
+
+ // Allocate a new file handle
+ file = _allocate_file();
+ if (!file)
+ return NULL;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // Check if file already open
+ if (_check_file_open(file))
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ // If file is in the root dir
+ if (file->path[0] == 0)
+ file->parentcluster = fatfs_get_root_cluster(&_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(file->path, &file->parentcluster))
+ {
+ _free_file(file);
+ return NULL;
+ }
+ }
+
+ // Check if same filename exists in directory
+ if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+ file->startcluster = 0;
+
+ // Create the file space for the file (at least one clusters worth!)
+ if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+ {
+ _free_file(file);
+ return NULL;
+ }
+
+#if FATFS_INC_LFN_SUPPORT
+ // Generate a short filename & tail
+ tailNum = 0;
+ do
+ {
+ // Create a standard short filename (without tail)
+ fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+ // If second hit or more, generate a ~n tail
+ if (tailNum != 0)
+ fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+ // Try with no tail if first entry
+ else
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already or not
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+ break;
+
+ tailNum++;
+ }
+ while (tailNum < 9999);
+
+ // We reached the max number of duplicate short file names (unlikely!)
+ if (tailNum == 9999)
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+#else
+ // Create a standard short filename (without tail)
+ if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+
+ // Copy to SFN space
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already
+ if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+#endif
+
+ // Add file to disk
+ if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+ _free_file(file);
+ return NULL;
+ }
+
+ // General
+ file->filelength = 0;
+ file->bytenum = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_cache_init(&_fs, file);
+
+ fatfs_fat_purge(&_fs);
+
+ return file;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _read_sectors: Read sector(s) from disk to file
+//-----------------------------------------------------------------------------
+static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
+{
+ uint32 Sector = 0;
+ uint32 ClusterIdx = 0;
+ uint32 Cluster = 0;
+ uint32 i;
+ uint32 lba;
+
+ // Find cluster index within file & sector with cluster
+ ClusterIdx = offset / _fs.sectors_per_cluster;
+ Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+ // Limit number of sectors read to the number remaining in this cluster
+ if ((Sector + count) > _fs.sectors_per_cluster)
+ count = _fs.sectors_per_cluster - Sector;
+
+ // Quick lookup for next link in the chain
+ if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ // Else walk the chain
+ else
+ {
+ // Starting from last recorded cluster?
+ if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+ {
+ i = file->last_fat_lookup.ClusterIdx;
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ }
+ // Start searching from the beginning..
+ else
+ {
+ // Set start of cluster chain to initial value
+ i = 0;
+ Cluster = file->startcluster;
+ }
+
+ // Follow chain to find cluster to read
+ for ( ;i<ClusterIdx; i++)
+ {
+ uint32 nextCluster;
+
+ // Does the entry exist in the cache?
+ if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+ {
+ // Scan file linked list to find next entry
+ nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+ // Push entry into cache
+ fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+ }
+
+ Cluster = nextCluster;
+ }
+
+ // Record current cluster lookup details (if valid)
+ if (Cluster != FAT32_LAST_CLUSTER)
+ {
+ file->last_fat_lookup.CurrentCluster = Cluster;
+ file->last_fat_lookup.ClusterIdx = ClusterIdx;
+ }
+ }
+
+ // If end of cluster chain then return false
+ if (Cluster == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Calculate sector address
+ lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
+
+ // Read sector of file
+ if (fatfs_sector_read(&_fs, lba, buffer, count))
+ return count;
+ else
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// External API
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fl_init: Initialise library
+//-----------------------------------------------------------------------------
+void fl_init(void)
+{
+ int i;
+
+ fat_list_init(&_free_file_list);
+ fat_list_init(&_open_file_list);
+
+ // Add all file objects to free list
+ for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
+ fat_list_insert_last(&_free_file_list, &_files[i].list_node);
+
+ _filelib_init = 1;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_locks:
+//-----------------------------------------------------------------------------
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+{
+ _fs.fl_lock = lock;
+ _fs.fl_unlock = unlock;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_media:
+//-----------------------------------------------------------------------------
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+{
+ int res;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ _fs.disk_io.read_media = rd;
+ _fs.disk_io.write_media = wr;
+
+ // Initialise FAT parameters
+ if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
+ {
+ FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
+ return res;
+ }
+
+ _filelib_valid = 1;
+ return FAT_INIT_OK;
+}
+//-----------------------------------------------------------------------------
+// fl_shutdown: Call before shutting down system
+//-----------------------------------------------------------------------------
+void fl_shutdown(void)
+{
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+ fatfs_fat_purge(&_fs);
+ FL_UNLOCK(&_fs);
+}
+//-----------------------------------------------------------------------------
+// fopen: Open or Create a file for reading or writing
+//-----------------------------------------------------------------------------
+void* fl_fopen(const char *path, const char *mode)
+{
+ int i;
+ FL_FILE* file;
+ uint8 flags = 0;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (!_filelib_valid)
+ return NULL;
+
+ if (!path || !mode)
+ return NULL;
+
+ // Supported Modes:
+ // "r" Open a file for reading.
+ // The file must exist.
+ // "w" Create an empty file for writing.
+ // If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+ // "a" Append to a file.
+ // Writing operations append data at the end of the file.
+ // The file is created if it does not exist.
+ // "r+" Open a file for update both reading and writing.
+ // The file must exist.
+ // "w+" Create an empty file for both reading and writing.
+ // If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+ // "a+" Open a file for reading and appending.
+ // All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
+ // You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
+ // will move it back to the end of file.
+ // The file is created if it does not exist.
+
+ for (i=0;i<(int)strlen(mode);i++)
+ {
+ switch (mode[i])
+ {
+ case 'r':
+ case 'R':
+ flags |= FILE_READ;
+ break;
+ case 'w':
+ case 'W':
+ flags |= FILE_WRITE;
+ flags |= FILE_ERASE;
+ flags |= FILE_CREATE;
+ break;
+ case 'a':
+ case 'A':
+ flags |= FILE_WRITE;
+ flags |= FILE_APPEND;
+ flags |= FILE_CREATE;
+ break;
+ case '+':
+ if (flags & FILE_READ)
+ flags |= FILE_WRITE;
+ else if (flags & FILE_WRITE)
+ {
+ flags |= FILE_READ;
+ flags |= FILE_ERASE;
+ flags |= FILE_CREATE;
+ }
+ else if (flags & FILE_APPEND)
+ {
+ flags |= FILE_READ;
+ flags |= FILE_WRITE;
+ flags |= FILE_APPEND;
+ flags |= FILE_CREATE;
+ }
+ break;
+ case 'b':
+ case 'B':
+ flags |= FILE_BINARY;
+ break;
+ }
+ }
+
+ file = NULL;
+
+#if FATFS_INC_WRITE_SUPPORT == 0
+ // No write support!
+ flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+#endif
+
+ // No write access - remove write/modify flags
+ if (!_fs.disk_io.write_media)
+ flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+
+ FL_LOCK(&_fs);
+
+ // Read
+ if (flags & FILE_READ)
+ file = _open_file(path);
+
+ // Create New
+#if FATFS_INC_WRITE_SUPPORT
+ if (!file && (flags & FILE_CREATE))
+ file = _create_file(path);
+#endif
+
+ // Write Existing (and not open due to read or create)
+ if (!(flags & FILE_READ))
+ if ((flags & FILE_CREATE) && !file)
+ if (flags & (FILE_WRITE | FILE_APPEND))
+ file = _open_file(path);
+
+ if (file)
+ file->flags = flags;
+
+ FL_UNLOCK(&_fs);
+ return file;
+}
+//-----------------------------------------------------------------------------
+// _write_sectors: Write sector(s) to disk
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
+{
+ uint32 SectorNumber = 0;
+ uint32 ClusterIdx = 0;
+ uint32 Cluster = 0;
+ uint32 LastCluster = FAT32_LAST_CLUSTER;
+ uint32 i;
+ uint32 lba;
+ uint32 TotalWriteCount = count;
+
+ // Find values for Cluster index & sector within cluster
+ ClusterIdx = offset / _fs.sectors_per_cluster;
+ SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+ // Limit number of sectors written to the number remaining in this cluster
+ if ((SectorNumber + count) > _fs.sectors_per_cluster)
+ count = _fs.sectors_per_cluster - SectorNumber;
+
+ // Quick lookup for next link in the chain
+ if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ // Else walk the chain
+ else
+ {
+ // Starting from last recorded cluster?
+ if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+ {
+ i = file->last_fat_lookup.ClusterIdx;
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ }
+ // Start searching from the beginning..
+ else
+ {
+ // Set start of cluster chain to initial value
+ i = 0;
+ Cluster = file->startcluster;
+ }
+
+ // Follow chain to find cluster to read
+ for ( ;i<ClusterIdx; i++)
+ {
+ uint32 nextCluster;
+
+ // Does the entry exist in the cache?
+ if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+ {
+ // Scan file linked list to find next entry
+ nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+ // Push entry into cache
+ fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+ }
+
+ LastCluster = Cluster;
+ Cluster = nextCluster;
+
+ // Dont keep following a dead end
+ if (Cluster == FAT32_LAST_CLUSTER)
+ break;
+ }
+
+ // If we have reached the end of the chain, allocate more!
+ if (Cluster == FAT32_LAST_CLUSTER)
+ {
+ // Add some more cluster(s) to the last good cluster chain
+ if (!fatfs_add_free_space(&_fs, &LastCluster, (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
+ return 0;
+
+ Cluster = LastCluster;
+ }
+
+ // Record current cluster lookup details
+ file->last_fat_lookup.CurrentCluster = Cluster;
+ file->last_fat_lookup.ClusterIdx = ClusterIdx;
+ }
+
+ // Calculate write address
+ lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
+
+ if (fatfs_sector_write(&_fs, lba, buf, count))
+ return count;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fflush: Flush un-written data to the file
+//-----------------------------------------------------------------------------
+int fl_fflush(void *f)
+{
+#if FATFS_INC_WRITE_SUPPORT
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (file)
+ {
+ FL_LOCK(&_fs);
+
+ // If some write data still in buffer
+ if (file->file_data_dirty)
+ {
+ // Write back current sector before loading next
+ if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
+ file->file_data_dirty = 0;
+ }
+
+ FL_UNLOCK(&_fs);
+ }
+#endif
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fclose: Close an open file
+//-----------------------------------------------------------------------------
+void fl_fclose(void *f)
+{
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (file)
+ {
+ FL_LOCK(&_fs);
+
+ // Flush un-written data to file
+ fl_fflush(f);
+
+ // File size changed?
+ if (file->filelength_changed)
+ {
+#if FATFS_INC_WRITE_SUPPORT
+ // Update filesize in directory
+ fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
+#endif
+ file->filelength_changed = 0;
+ }
+
+ file->bytenum = 0;
+ file->filelength = 0;
+ file->startcluster = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Free file handle
+ _free_file(file);
+
+ fatfs_fat_purge(&_fs);
+
+ FL_UNLOCK(&_fs);
+ }
+}
+//-----------------------------------------------------------------------------
+// fl_fgetc: Get a character in the stream
+//-----------------------------------------------------------------------------
+int fl_fgetc(void *f)
+{
+ int res;
+ uint8 data = 0;
+
+ res = fl_fread(&data, 1, 1, f);
+ if (res == 1)
+ return (int)data;
+ else
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgets: Get a string from a stream
+//-----------------------------------------------------------------------------
+char *fl_fgets(char *s, int n, void *f)
+{
+ int idx = 0;
+
+ // Space for null terminator?
+ if (n > 0)
+ {
+ // While space (+space for null terminator)
+ while (idx < (n-1))
+ {
+ int ch = fl_fgetc(f);
+
+ // EOF / Error?
+ if (ch < 0)
+ break;
+
+ // Store character read from stream
+ s[idx++] = (char)ch;
+
+ // End of line?
+ if (ch == '\n')
+ break;
+ }
+
+ if (idx > 0)
+ s[idx] = '\0';
+ }
+
+ return (idx > 0) ? s : 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fread: Read a block of data from the file
+//-----------------------------------------------------------------------------
+int fl_fread(void * buffer, int size, int length, void *f )
+{
+ uint32 sector;
+ uint32 offset;
+ int copyCount;
+ int count = size * length;
+ int bytesRead = 0;
+
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (buffer==NULL || file==NULL)
+ return -1;
+
+ // No read permissions
+ if (!(file->flags & FILE_READ))
+ return -1;
+
+ // Nothing to be done
+ if (!count)
+ return 0;
+
+ // Check if read starts past end of file
+ if (file->bytenum >= file->filelength)
+ return -1;
+
+ // Limit to file size
+ if ( (file->bytenum + count) > file->filelength )
+ count = file->filelength - file->bytenum;
+
+ // Calculate start sector
+ sector = file->bytenum / FAT_SECTOR_SIZE;
+
+ // Offset to start copying data from first sector
+ offset = file->bytenum % FAT_SECTOR_SIZE;
+
+ while (bytesRead < count)
+ {
+ // Read whole sector, read from media directly into target buffer
+ if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
+ {
+ // Read as many sectors as possible into target buffer
+ uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
+ if (sectorsRead)
+ {
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE * sectorsRead;
+
+ // Move onto next sector and reset copy offset
+ sector+= sectorsRead;
+ offset = 0;
+ }
+ else
+ break;
+ }
+ else
+ {
+ // Do we need to re-read the sector?
+ if (file->file_data_address != sector)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(file);
+
+ // Get LBA of sector offset within file
+ if (!_read_sectors(file, sector, file->file_data_sector, 1))
+ // Read failed - out of range (probably)
+ break;
+
+ file->file_data_address = sector;
+ file->file_data_dirty = 0;
+ }
+
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE - offset;
+
+ // Only require some of this sector?
+ if (copyCount > (count - bytesRead))
+ copyCount = (count - bytesRead);
+
+ // Copy to application buffer
+ memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
+
+ // Move onto next sector and reset copy offset
+ sector++;
+ offset = 0;
+ }
+
+ // Increase total read count
+ bytesRead += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+ }
+
+ return bytesRead;
+}
+//-----------------------------------------------------------------------------
+// fl_fseek: Seek to a specific place in the file
+//-----------------------------------------------------------------------------
+int fl_fseek( void *f, long offset, int origin )
+{
+ FL_FILE *file = (FL_FILE *)f;
+ int res = -1;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (!file)
+ return -1;
+
+ if (origin == SEEK_END && offset != 0)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ // Invalidate file buffer
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+
+ if (origin == SEEK_SET)
+ {
+ file->bytenum = (uint32)offset;
+
+ if (file->bytenum > file->filelength)
+ file->bytenum = file->filelength;
+
+ res = 0;
+ }
+ else if (origin == SEEK_CUR)
+ {
+ // Positive shift
+ if (offset >= 0)
+ {
+ file->bytenum += offset;
+
+ if (file->bytenum > file->filelength)
+ file->bytenum = file->filelength;
+ }
+ // Negative shift
+ else
+ {
+ // Make shift positive
+ offset = -offset;
+
+ // Limit to negative shift to start of file
+ if ((uint32)offset > file->bytenum)
+ file->bytenum = 0;
+ else
+ file->bytenum-= offset;
+ }
+
+ res = 0;
+ }
+ else if (origin == SEEK_END)
+ {
+ file->bytenum = file->filelength;
+ res = 0;
+ }
+ else
+ res = -1;
+
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgetpos: Get the current file position
+//-----------------------------------------------------------------------------
+int fl_fgetpos(void *f , uint32 * position)
+{
+ FL_FILE *file = (FL_FILE *)f;
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ // Get position
+ *position = file->bytenum;
+
+ FL_UNLOCK(&_fs);
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_ftell: Get the current file position
+//-----------------------------------------------------------------------------
+long fl_ftell(void *f)
+{
+ uint32 pos = 0;
+
+ fl_fgetpos(f, &pos);
+
+ return (long)pos;
+}
+//-----------------------------------------------------------------------------
+// fl_feof: Is the file pointer at the end of the stream?
+//-----------------------------------------------------------------------------
+int fl_feof(void *f)
+{
+ FL_FILE *file = (FL_FILE *)f;
+ int res;
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ if (file->bytenum == file->filelength)
+ res = EOF;
+ else
+ res = 0;
+
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fputc: Write a character to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputc(int c, void *f)
+{
+ uint8 data = (uint8)c;
+ int res;
+
+ res = fl_fwrite(&data, 1, 1, f);
+ if (res == 1)
+ return c;
+ else
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fwrite: Write a block of data to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fwrite(const void * data, int size, int count, void *f )
+{
+ FL_FILE *file = (FL_FILE *)f;
+ uint32 sector;
+ uint32 offset;
+ uint32 length = (size*count);
+ uint8 *buffer = (uint8 *)data;
+ uint32 bytesWritten = 0;
+ uint32 copyCount;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&_fs);
+
+ // No write permissions
+ if (!(file->flags & FILE_WRITE))
+ {
+ FL_UNLOCK(&_fs);
+ return -1;
+ }
+
+ // Append writes to end of file
+ if (file->flags & FILE_APPEND)
+ file->bytenum = file->filelength;
+ // Else write to current position
+
+ // Calculate start sector
+ sector = file->bytenum / FAT_SECTOR_SIZE;
+
+ // Offset to start copying data from first sector
+ offset = file->bytenum % FAT_SECTOR_SIZE;
+
+ while (bytesWritten < length)
+ {
+ // Whole sector or more to be written?
+ if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
+ {
+ uint32 sectorsWrote;
+
+ // Buffered sector, flush back to disk
+ if (file->file_data_address != 0xFFFFFFFF)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(file);
+
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ }
+
+ // Write as many sectors as possible
+ sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
+ copyCount = FAT_SECTOR_SIZE * sectorsWrote;
+
+ // Increase total read count
+ bytesWritten += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+
+ // Move onto next sector and reset copy offset
+ sector+= sectorsWrote;
+ offset = 0;
+
+ if (!sectorsWrote)
+ break;
+ }
+ else
+ {
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE - offset;
+
+ // Only require some of this sector?
+ if (copyCount > (length - bytesWritten))
+ copyCount = (length - bytesWritten);
+
+ // Do we need to read a new sector?
+ if (file->file_data_address != sector)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(file);
+
+ // If we plan to overwrite the whole sector, we don't need to read it first!
+ if (copyCount != FAT_SECTOR_SIZE)
+ {
+ // NOTE: This does not have succeed; if last sector of file
+ // reached, no valid data will be read in, but write will
+ // allocate some more space for new data.
+
+ // Get LBA of sector offset within file
+ if (!_read_sectors(file, sector, file->file_data_sector, 1))
+ memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+ }
+
+ file->file_data_address = sector;
+ file->file_data_dirty = 0;
+ }
+
+ // Copy from application buffer into sector buffer
+ memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
+
+ // Mark buffer as dirty
+ file->file_data_dirty = 1;
+
+ // Increase total read count
+ bytesWritten += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+
+ // Move onto next sector and reset copy offset
+ sector++;
+ offset = 0;
+ }
+ }
+
+ // Write increased extent of the file?
+ if (file->bytenum > file->filelength)
+ {
+ // Increase file size to new point
+ file->filelength = file->bytenum;
+
+ // We are changing the file length and this
+ // will need to be writen back at some point
+ file->filelength_changed = 1;
+ }
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // If time & date support is enabled, always force directory entry to be
+ // written in-order to update file modify / access time & date.
+ file->filelength_changed = 1;
+#endif
+
+ FL_UNLOCK(&_fs);
+
+ return (size*count);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fputs: Write a character string to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputs(const char * str, void *f)
+{
+ int len = (int)strlen(str);
+ int res = fl_fwrite(str, 1, len, f);
+
+ if (res == len)
+ return len;
+ else
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_remove: Remove a file from the filesystem
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_remove( const char * filename )
+{
+ FL_FILE* file;
+ int res = -1;
+
+ FL_LOCK(&_fs);
+
+ // Use read_file as this will check if the file is already open!
+ file = fl_fopen((char*)filename, "r");
+ if (file)
+ {
+ // Delete allocated space
+ if (fatfs_free_cluster_chain(&_fs, file->startcluster))
+ {
+ // Remove directory entries
+ if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Close the file handle (this should not write anything to the file
+ // as we have not changed the file since opening it!)
+ fl_fclose(file);
+
+ res = 0;
+ }
+ }
+ }
+
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_createdirectory: Create a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_createdirectory(const char *path)
+{
+ int res;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+ res =_create_directory((char*)path);
+ FL_UNLOCK(&_fs);
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_listdirectory: List a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fl_listdirectory(const char *path)
+{
+ FL_DIR dirstat;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+
+ FAT_PRINTF(("\r\nDirectory %s\r\n", path));
+
+ if (fl_opendir(path, &dirstat))
+ {
+ struct fs_dir_ent dirent;
+
+ while (fl_readdir(&dirstat, &dirent) == 0)
+ {
+#if FATFS_INC_TIME_DATE_SUPPORT
+ int d,m,y,h,mn,s;
+ fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
+ fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
+ FAT_PRINTF(("%02d/%02d/%04d %02d:%02d ", d,mn,y,h,m));
+#endif
+
+ if (dirent.is_dir)
+ {
+ FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
+ }
+ else
+ {
+ FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
+ }
+ }
+
+ fl_closedir(&dirstat);
+ }
+
+ FL_UNLOCK(&_fs);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_opendir: Opens a directory for listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
+{
+ int levels;
+ int res = 1;
+ uint32 cluster = FAT32_INVALID_CLUSTER;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+
+ levels = fatfs_total_path_levels((char*)path) + 1;
+
+ // If path is in the root dir
+ if (levels == 0)
+ cluster = fatfs_get_root_cluster(&_fs);
+ // Find parent directory start cluster
+ else
+ res = _open_directory((char*)path, &cluster);
+
+ if (res)
+ fatfs_list_directory_start(&_fs, dir, cluster);
+
+ FL_UNLOCK(&_fs);
+
+ return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_readdir: Get next item in directory
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
+{
+ int res = 0;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT();
+
+ FL_LOCK(&_fs);
+
+ res = fatfs_list_directory_next(&_fs, dirls, entry);
+
+ FL_UNLOCK(&_fs);
+
+ return res ? 0 : -1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_closedir: Close directory after listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_closedir(FL_DIR* dir)
+{
+ // Not used
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_is_dir: Is this a directory?
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_is_dir(const char *path)
+{
+ int res = 0;
+ FL_DIR dir;
+
+ if (fl_opendir(path, &dir))
+ {
+ res = 1;
+ fl_closedir(&dir);
+ }
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+#if FATFS_INC_FORMAT_SUPPORT
+int fl_format(uint32 volume_sectors, const char *name)
+{
+ return fatfs_format(&_fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
+//-----------------------------------------------------------------------------
+// fl_get_fs:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void)
+{
+ return &_fs;
+}
+#endif
--- /dev/null
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+ #define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+ #define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+ #define SEEK_SET 0
+#endif
+
+#ifndef EOF
+ #define EOF (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+ uint32 ClusterIdx;
+ uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+ uint32 parentcluster;
+ uint32 startcluster;
+ uint32 bytenum;
+ uint32 filelength;
+ int filelength_changed;
+ char path[FATFS_MAX_LONG_FILENAME];
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+ uint32 cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+ // Cluster Lookup
+ struct cluster_lookup last_fat_lookup;
+
+ // Read/Write sector buffer
+ uint8 file_data_sector[FAT_SECTOR_SIZE];
+ uint32 file_data_address;
+ int file_data_dirty;
+
+ // File fopen flags
+ uint8 flags;
+#define FILE_READ (1 << 0)
+#define FILE_WRITE (1 << 1)
+#define FILE_APPEND (1 << 2)
+#define FILE_BINARY (1 << 3)
+#define FILE_ERASE (1 << 4)
+#define FILE_CREATE (1 << 5)
+
+ struct fat_node list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void fl_init(void);
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void fl_shutdown(void);
+
+// Standard API
+void* fl_fopen(const char *path, const char *modifiers);
+void fl_fclose(void *file);
+int fl_fflush(void *file);
+int fl_fgetc(void *file);
+char * fl_fgets(char *s, int n, void *f);
+int fl_fputc(int c, void *file);
+int fl_fputs(const char * str, void *file);
+int fl_fwrite(const void * data, int size, int count, void *file );
+int fl_fread(void * data, int size, int count, void *file );
+int fl_fseek(void *file , long offset , int origin );
+int fl_fgetpos(void *file , uint32 * position);
+long fl_ftell(void *f);
+int fl_feof(void *f);
+int fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status FL_DIR;
+typedef struct fs_dir_ent fl_dirent;
+
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir);
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int fl_closedir(FL_DIR* dir);
+
+// Extensions
+void fl_listdirectory(const char *path);
+int fl_createdirectory(const char *path);
+int fl_is_dir(const char *path);
+
+int fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE FL_FILE
+
+#define fopen(a,b) fl_fopen(a, b)
+#define fclose(a) fl_fclose(a)
+#define fflush(a) fl_fflush(a)
+#define fgetc(a) fl_fgetc(a)
+#define fgets(a,b,c) fl_fgets(a, b, c)
+#define fputc(a,b) fl_fputc(a, b)
+#define fputs(a,b) fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d) fl_fread(a, b, c, d)
+#define fseek(a,b,c) fl_fseek(a, b, c)
+#define fgetpos(a,b) fl_fgetpos(a, b)
+#define ftell(a) fl_ftell(a)
+#define feof(a) fl_feof(a)
+#define remove(a) fl_remove(a)
+#define mkdir(a) fl_createdirectory(a)
+#define rmdir(a) 0
+
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_format.h"
+
+#if FATFS_INC_FORMAT_SUPPORT
+
+//-----------------------------------------------------------------------------
+// Tables
+//-----------------------------------------------------------------------------
+struct sec_per_clus_table
+{
+ uint32 sectors;
+ uint8 sectors_per_cluster;
+};
+
+struct sec_per_clus_table _cluster_size_table16[] =
+{
+ { 32680, 2}, // 16MB - 1K
+ { 262144, 4}, // 128MB - 2K
+ { 524288, 8}, // 256MB - 4K
+ { 1048576, 16}, // 512MB - 8K
+ { 2097152, 32}, // 1GB - 16K
+ { 4194304, 64}, // 2GB - 32K
+ { 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
+ { 0 , 0 } // Invalid
+};
+
+struct sec_per_clus_table _cluster_size_table32[] =
+{
+ { 532480, 1}, // 260MB - 512b
+ { 16777216, 8}, // 8GB - 4K
+ { 33554432, 16}, // 16GB - 8K
+ { 67108864, 32}, // 32GB - 16K
+ { 0xFFFFFFFF, 64},// >32GB - 32K
+ { 0 , 0 } // Invalid
+};
+
+//-----------------------------------------------------------------------------
+// fatfs_calc_cluster_size: Calculate what cluster size should be used
+//-----------------------------------------------------------------------------
+static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
+{
+ int i;
+
+ if (!is_fat32)
+ {
+ for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
+ if (sectors <= _cluster_size_table16[i].sectors)
+ return _cluster_size_table16[i].sectors_per_cluster;
+ }
+ else
+ {
+ for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
+ if (sectors <= _cluster_size_table32[i].sectors)
+ return _cluster_size_table32[i].sectors_per_cluster;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_sectors: Erase a number of sectors
+//-----------------------------------------------------------------------------
+static int fatfs_erase_sectors(struct fatfs *fs, uint32 lba, int count)
+{
+ int i;
+
+ // Zero sector first
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ for (i=0;i<count;i++)
+ if (!fs->disk_io.write_media(lba + i, fs->currentsector.sector, 1))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_boot_sector: Create the boot sector
+//-----------------------------------------------------------------------------
+static int fatfs_create_boot_sector(struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
+{
+ uint32 total_clusters;
+ int i;
+
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // OEM Name & Jump Code
+ fs->currentsector.sector[0] = 0xEB;
+ fs->currentsector.sector[1] = 0x3C;
+ fs->currentsector.sector[2] = 0x90;
+ fs->currentsector.sector[3] = 0x4D;
+ fs->currentsector.sector[4] = 0x53;
+ fs->currentsector.sector[5] = 0x44;
+ fs->currentsector.sector[6] = 0x4F;
+ fs->currentsector.sector[7] = 0x53;
+ fs->currentsector.sector[8] = 0x35;
+ fs->currentsector.sector[9] = 0x2E;
+ fs->currentsector.sector[10] = 0x30;
+
+ // Bytes per sector
+ fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
+ fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
+
+ // Get sectors per cluster size for the disk
+ fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
+ if (!fs->sectors_per_cluster)
+ return 0; // Invalid disk size
+
+ // Sectors per cluster
+ fs->currentsector.sector[13] = fs->sectors_per_cluster;
+
+ // Reserved Sectors
+ if (!is_fat32)
+ fs->reserved_sectors = 8;
+ else
+ fs->reserved_sectors = 32;
+ fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
+ fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
+
+ // Number of FATS
+ fs->num_of_fats = 2;
+ fs->currentsector.sector[16] = fs->num_of_fats;
+
+ // Max entries in root dir (FAT16 only)
+ if (!is_fat32)
+ {
+ fs->root_entry_count = 512;
+ fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
+ fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
+ }
+ else
+ {
+ fs->root_entry_count = 0;
+ fs->currentsector.sector[17] = 0;
+ fs->currentsector.sector[18] = 0;
+ }
+
+ // [FAT16] Total sectors (use FAT32 count instead)
+ fs->currentsector.sector[19] = 0x00;
+ fs->currentsector.sector[20] = 0x00;
+
+ // Media type
+ fs->currentsector.sector[21] = 0xF8;
+
+
+ // FAT16 BS Details
+ if (!is_fat32)
+ {
+ // Count of sectors used by the FAT table (FAT16 only)
+ total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+ fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
+ fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
+ fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
+
+ // Sectors per track
+ fs->currentsector.sector[24] = 0x00;
+ fs->currentsector.sector[25] = 0x00;
+
+ // Heads
+ fs->currentsector.sector[26] = 0x00;
+ fs->currentsector.sector[27] = 0x00;
+
+ // Hidden sectors
+ fs->currentsector.sector[28] = 0x20;
+ fs->currentsector.sector[29] = 0x00;
+ fs->currentsector.sector[30] = 0x00;
+ fs->currentsector.sector[31] = 0x00;
+
+ // Total sectors for this volume
+ fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+ fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+ fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+ fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+ // Drive number
+ fs->currentsector.sector[36] = 0x00;
+
+ // Reserved
+ fs->currentsector.sector[37] = 0x00;
+
+ // Boot signature
+ fs->currentsector.sector[38] = 0x29;
+
+ // Volume ID
+ fs->currentsector.sector[39] = 0x12;
+ fs->currentsector.sector[40] = 0x34;
+ fs->currentsector.sector[41] = 0x56;
+ fs->currentsector.sector[42] = 0x78;
+
+ // Volume name
+ for (i=0;i<11;i++)
+ {
+ if (i < (int)strlen(name))
+ fs->currentsector.sector[i+43] = name[i];
+ else
+ fs->currentsector.sector[i+43] = ' ';
+ }
+
+ // File sys type
+ fs->currentsector.sector[54] = 'F';
+ fs->currentsector.sector[55] = 'A';
+ fs->currentsector.sector[56] = 'T';
+ fs->currentsector.sector[57] = '1';
+ fs->currentsector.sector[58] = '6';
+ fs->currentsector.sector[59] = ' ';
+ fs->currentsector.sector[60] = ' ';
+ fs->currentsector.sector[61] = ' ';
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+ }
+ // FAT32 BS Details
+ else
+ {
+ // Count of sectors used by the FAT table (FAT16 only)
+ fs->currentsector.sector[22] = 0;
+ fs->currentsector.sector[23] = 0;
+
+ // Sectors per track (default)
+ fs->currentsector.sector[24] = 0x3F;
+ fs->currentsector.sector[25] = 0x00;
+
+ // Heads (default)
+ fs->currentsector.sector[26] = 0xFF;
+ fs->currentsector.sector[27] = 0x00;
+
+ // Hidden sectors
+ fs->currentsector.sector[28] = 0x00;
+ fs->currentsector.sector[29] = 0x00;
+ fs->currentsector.sector[30] = 0x00;
+ fs->currentsector.sector[31] = 0x00;
+
+ // Total sectors for this volume
+ fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+ fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+ fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+ fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+ total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+ fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
+
+ // BPB_FATSz32
+ fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
+ fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
+ fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
+ fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
+
+ // BPB_ExtFlags
+ fs->currentsector.sector[40] = 0;
+ fs->currentsector.sector[41] = 0;
+
+ // BPB_FSVer
+ fs->currentsector.sector[42] = 0;
+ fs->currentsector.sector[43] = 0;
+
+ // BPB_RootClus
+ fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
+ fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
+ fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
+ fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
+
+ // BPB_FSInfo
+ fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
+ fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
+
+ // BPB_BkBootSec
+ fs->currentsector.sector[50] = 6;
+ fs->currentsector.sector[51] = 0;
+
+ // Drive number
+ fs->currentsector.sector[64] = 0x00;
+
+ // Boot signature
+ fs->currentsector.sector[66] = 0x29;
+
+ // Volume ID
+ fs->currentsector.sector[67] = 0x12;
+ fs->currentsector.sector[68] = 0x34;
+ fs->currentsector.sector[69] = 0x56;
+ fs->currentsector.sector[70] = 0x78;
+
+ // Volume name
+ for (i=0;i<11;i++)
+ {
+ if (i < (int)strlen(name))
+ fs->currentsector.sector[i+71] = name[i];
+ else
+ fs->currentsector.sector[i+71] = ' ';
+ }
+
+ // File sys type
+ fs->currentsector.sector[82] = 'F';
+ fs->currentsector.sector[83] = 'A';
+ fs->currentsector.sector[84] = 'T';
+ fs->currentsector.sector[85] = '3';
+ fs->currentsector.sector[86] = '2';
+ fs->currentsector.sector[87] = ' ';
+ fs->currentsector.sector[88] = ' ';
+ fs->currentsector.sector[89] = ' ';
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+ }
+
+ if (fs->disk_io.write_media(boot_sector_lba, fs->currentsector.sector, 1))
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
+//-----------------------------------------------------------------------------
+static int fatfs_create_fsinfo_sector(struct fatfs *fs, uint32 sector_lba)
+{
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // FSI_LeadSig
+ fs->currentsector.sector[0] = 0x52;
+ fs->currentsector.sector[1] = 0x52;
+ fs->currentsector.sector[2] = 0x61;
+ fs->currentsector.sector[3] = 0x41;
+
+ // FSI_StrucSig
+ fs->currentsector.sector[484] = 0x72;
+ fs->currentsector.sector[485] = 0x72;
+ fs->currentsector.sector[486] = 0x41;
+ fs->currentsector.sector[487] = 0x61;
+
+ // FSI_Free_Count
+ fs->currentsector.sector[488] = 0xFF;
+ fs->currentsector.sector[489] = 0xFF;
+ fs->currentsector.sector[490] = 0xFF;
+ fs->currentsector.sector[491] = 0xFF;
+
+ // FSI_Nxt_Free
+ fs->currentsector.sector[492] = 0xFF;
+ fs->currentsector.sector[493] = 0xFF;
+ fs->currentsector.sector[494] = 0xFF;
+ fs->currentsector.sector[495] = 0xFF;
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+
+ if (fs->disk_io.write_media(sector_lba, fs->currentsector.sector, 1))
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_fat: Erase FAT table using fs details in fs struct
+//-----------------------------------------------------------------------------
+static int fatfs_erase_fat(struct fatfs *fs, int is_fat32)
+{
+ uint32 i;
+
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // Initialise default allocate / reserved clusters
+ if (!is_fat32)
+ {
+ SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
+ SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
+ }
+ else
+ {
+ SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
+ SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
+ SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
+ }
+
+ if (!fs->disk_io.write_media(fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
+ return 0;
+
+ // Zero remaining FAT sectors
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+ for (i=1;i<fs->fat_sectors*fs->num_of_fats;i++)
+ if (!fs->disk_io.write_media(fs->fat_begin_lba + i, fs->currentsector.sector, 1))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat16: Format a FAT16 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have read + write functions
+ if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Volume is FAT16
+ fs->fat_type = FAT_TYPE_16;
+
+ // Not valid for FAT16
+ fs->fs_info_sector = 0;
+ fs->rootdir_first_cluster = 0;
+
+ // Sector 0: Boot sector
+ // NOTE: We don't need an MBR, it is a waste of a good sector!
+ fs->lba_begin = 0;
+ if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 0))
+ return 0;
+
+ // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+ fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
+ fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+ // Initialise FAT sectors
+ if (!fatfs_erase_fat(fs, 0))
+ return 0;
+
+ // Erase Root directory
+ if (!fatfs_erase_sectors(fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat32: Format a FAT32 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have read + write functions
+ if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Volume is FAT32
+ fs->fat_type = FAT_TYPE_32;
+
+ // Basic defaults for normal FAT32 partitions
+ fs->fs_info_sector = 1;
+ fs->rootdir_first_cluster = 2;
+
+ // Sector 0: Boot sector
+ // NOTE: We don't need an MBR, it is a waste of a good sector!
+ fs->lba_begin = 0;
+ if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 1))
+ return 0;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+ // Initialise FSInfo sector
+ if (!fatfs_create_fsinfo_sector(fs, fs->fs_info_sector))
+ return 0;
+
+ // Initialise FAT sectors
+ if (!fatfs_erase_fat(fs, 1))
+ return 0;
+
+ // Erase Root directory
+ if (!fatfs_erase_sectors(fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ // 2GB - 32K limit for safe behaviour for FAT16
+ if (volume_sectors <= 4194304)
+ return fatfs_format_fat16(fs, volume_sectors, name);
+ else
+ return fatfs_format_fat32(fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
--- /dev/null
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
--- /dev/null
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+ #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+ #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+ struct fat_node *previous;
+ struct fat_node *next;
+};
+
+struct fat_list
+{
+ struct fat_node *head;
+ struct fat_node *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m) p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p) (p)->next
+#define fat_list_prev(l, p) (p)->previous
+#define fat_list_first(l) (l)->head
+#define fat_list_last(l) (l)->tail
+#define fat_list_for_each(l, p) for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if(!node->previous)
+ list->head = node->next;
+ else
+ node->previous->next = node->next;
+
+ if(!node->next)
+ list->tail = node->previous;
+ else
+ node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node;
+ new_node->next = node->next;
+ if (!node->next)
+ list->tail = new_node;
+ else
+ node->next->previous = new_node;
+ node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node->previous;
+ new_node->next = node;
+ if (!node->previous)
+ list->head = new_node;
+ else
+ node->previous->next = new_node;
+ node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ node->previous = 0;
+ node->next = 0;
+ }
+ else
+ fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->tail)
+ fat_list_insert_first(list, node);
+ else
+ fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+ struct fat_node * node;
+
+ FAT_ASSERT(list);
+
+ node = fat_list_first(list);
+ if (node)
+ fat_list_remove(list, node);
+
+ return node;
+}
+
+#endif
+
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_init: Clear long file name cache
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
+{
+ int i = 0;
+
+ lfn->no_of_strings = 0;
+
+#if FATFS_INC_LFN_SUPPORT
+
+ // Zero out buffer also
+ if (wipeTable)
+ for (i=0;i<MAX_LONGFILENAME_ENTRIES;i++)
+ memset(lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
+#endif
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_entry - Function extracts long file name text from sector
+// at a specific offset
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
+{
+ uint8 LFNIndex, i;
+ LFNIndex = entryBuffer[0] & 0x1F;
+
+ // Limit file name to cache size!
+ if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
+ return ;
+
+ // This is an error condition
+ if (LFNIndex == 0)
+ return ;
+
+ if (lfn->no_of_strings == 0)
+ lfn->no_of_strings = LFNIndex;
+
+ lfn->String[LFNIndex-1][0] = entryBuffer[1];
+ lfn->String[LFNIndex-1][1] = entryBuffer[3];
+ lfn->String[LFNIndex-1][2] = entryBuffer[5];
+ lfn->String[LFNIndex-1][3] = entryBuffer[7];
+ lfn->String[LFNIndex-1][4] = entryBuffer[9];
+ lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
+ lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
+ lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
+ lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
+ lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
+ lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
+ lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
+ lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
+
+ for (i=0; i<MAX_LFN_ENTRY_LENGTH; i++)
+ if (lfn->String[LFNIndex-1][i]==0xFF)
+ lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_get: Get a reference to the long filename
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
+{
+ // Null terminate long filename
+ if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
+ lfn->Null = '\0';
+ else if (lfn->no_of_strings)
+ lfn->String[lfn->no_of_strings][0] = '\0';
+ else
+ lfn->String[0][0] = '\0';
+
+ return (char*)&lfn->String[0][0];
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_text: If LFN text entry found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
+{
+ if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
+{
+ if ( (entry->Name[0]==FILE_HEADER_BLANK) ||
+ (entry->Name[0]==FILE_HEADER_DELETED)||
+ (entry->Attr==FILE_ATTR_VOLUME_ID) ||
+ (entry->Attr & FILE_ATTR_SYSHID) )
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
+{
+ if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+ (entry->Name[0]!=FILE_HEADER_BLANK) &&
+ (entry->Name[0]!=FILE_HEADER_DELETED) &&
+ (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+ (!(entry->Attr&FILE_ATTR_SYSHID)) &&
+ (lfn->no_of_strings) )
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_sfn_only: If SFN only exists
+//-----------------------------------------------------------------------------
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
+{
+ if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+ (entry->Name[0]!=FILE_HEADER_BLANK) &&
+ (entry->Name[0]!=FILE_HEADER_DELETED) &&
+ (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+ (!(entry->Attr&FILE_ATTR_SYSHID)) )
+ return 1;
+ else
+ return 0;
+}
+// TODO: FILE_ATTR_SYSHID ?!?!??!
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_dir: Returns 1 if a directory
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_dir(struct fat_dir_entry *entry)
+{
+ if (entry->Attr & FILE_TYPE_DIR)
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_file: Returns 1 is a file entry
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_file(struct fat_dir_entry *entry)
+{
+ if (entry->Attr & FILE_TYPE_FILE)
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_entries_required: Calculate number of 13 characters entries
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_lfn_entries_required(char *filename)
+{
+ int length = (int)strlen(filename);
+
+ if (length)
+ return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_filename_to_lfn:
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
+{
+ int i;
+ int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+ // 13 characters entries
+ int length = (int)strlen(filename);
+ int entriesRequired = fatfs_lfn_entries_required(filename);
+
+ // Filename offset
+ int start = entry * MAX_LFN_ENTRY_LENGTH;
+
+ // Initialise to zeros
+ memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
+
+ // LFN entry number
+ buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
+
+ // LFN flag
+ buffer[11] = 0x0F;
+
+ // Checksum of short filename
+ buffer[13] = sfnChk;
+
+ // Copy to buffer
+ for (i=0;i<MAX_LFN_ENTRY_LENGTH;i++)
+ {
+ if ( (start+i) < length )
+ buffer[nameIndexes[i]] = filename[start+i];
+ else if ( (start+i) == length )
+ buffer[nameIndexes[i]] = 0x00;
+ else
+ {
+ buffer[nameIndexes[i]] = 0xFF;
+ buffer[nameIndexes[i]+1] = 0xFF;
+ }
+ }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_sfn_create_entry: Create the short filename directory entry
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir)
+{
+ int i;
+
+ // Copy short filename
+ for (i=0;i<FAT_SFN_SIZE_FULL;i++)
+ entry->Name[i] = shortfilename[i];
+
+ // Unless we have a RTC we might as well set these to 1980
+ entry->CrtTimeTenth = 0x00;
+ entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
+ entry->CrtDate[1] = 0x00;
+ entry->CrtDate[0] = 0x20;
+ entry->LstAccDate[1] = 0x00;
+ entry->LstAccDate[0] = 0x20;
+ entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
+ entry->WrtDate[1] = 0x00;
+ entry->WrtDate[0] = 0x20;
+
+ if (!dir)
+ entry->Attr = FILE_TYPE_FILE;
+ else
+ entry->Attr = FILE_TYPE_DIR;
+
+ entry->NTRes = 0x00;
+
+ entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
+ entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
+ entry->FileSize = FAT_HTONL(size);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_create_sfn: Create a padded SFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
+{
+ int i;
+ int dotPos = -1;
+ char ext[3];
+ int pos;
+ int len = (int)strlen(filename);
+
+ // Invalid to start with .
+ if (filename[0]=='.')
+ return 0;
+
+ memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
+ memset(ext, ' ', 3);
+
+ // Find dot seperator
+ for (i = 0; i< len; i++)
+ {
+ if (filename[i]=='.')
+ dotPos = i;
+ }
+
+ // Extract extensions
+ if (dotPos!=-1)
+ {
+ // Copy first three chars of extension
+ for (i = (dotPos+1); i < (dotPos+1+3); i++)
+ if (i<len)
+ ext[i-(dotPos+1)] = filename[i];
+
+ // Shorten the length to the dot position
+ len = dotPos;
+ }
+
+ // Add filename part
+ pos = 0;
+ for (i=0;i<len;i++)
+ {
+ if ( (filename[i]!=' ') && (filename[i]!='.') )
+ {
+ if (filename[i] >= 'a' && filename[i] <= 'z')
+ sfn_output[pos++] = filename[i] - 'a' + 'A';
+ else
+ sfn_output[pos++] = filename[i];
+ }
+
+ // Fill upto 8 characters
+ if (pos==FAT_SFN_SIZE_PARTIAL)
+ break;
+ }
+
+ // Add extension part
+ for (i=FAT_SFN_SIZE_PARTIAL;i<FAT_SFN_SIZE_FULL;i++)
+ {
+ if (ext[i-FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
+ sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
+ else
+ sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
+ }
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_itoa:
+//-----------------------------------------------------------------------------
+static void fatfs_itoa(uint32 num, char *s)
+{
+ char* cp;
+ char outbuf[12];
+ const char digits[] = "0123456789ABCDEF";
+
+ // Build string backwards
+ cp = outbuf;
+ do
+ {
+ *cp++ = digits[(int)(num % 10)];
+ }
+ while ((num /= 10) > 0);
+
+ *cp-- = 0;
+
+ // Copy in forwards
+ while (cp >= outbuf)
+ *s++ = *cp--;
+
+ *s = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_generate_tail:
+// sfn_input = Input short filename, spaced format & in upper case
+// sfn_output = Output short filename with tail
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
+{
+ int tail_chars;
+ char tail_str[12];
+
+ if (tailNum > 99999)
+ return 0;
+
+ // Convert to number
+ memset(tail_str, 0x00, sizeof(tail_str));
+ tail_str[0] = '~';
+ fatfs_itoa(tailNum, tail_str+1);
+
+ // Copy in base filename
+ memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
+
+ // Overwrite with tail
+ tail_chars = (int)strlen(tail_str);
+ memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
+
+ return 1;
+}
+#endif
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
+//-----------------------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
+{
+ *hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
+ *minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
+ *seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
+ *seconds = *seconds * FAT_TIME_SECONDS_SCALE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
+//-----------------------------------------------------------------------------
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
+{
+ *day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
+ *month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
+ *year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
+ *year = *year + FAT_DATE_YEAR_OFFSET;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
+{
+ uint16 fat_time = 0;
+
+ // Most FAT times are to a resolution of 2 seconds
+ seconds /= FAT_TIME_SECONDS_SCALE;
+
+ fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
+ fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
+ fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
+
+ return fat_time;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_date(int day, int month, int year)
+{
+ uint16 fat_date = 0;
+
+ // FAT dates are relative to 1980
+ if (year >= FAT_DATE_YEAR_OFFSET)
+ year -= FAT_DATE_YEAR_OFFSET;
+
+ fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
+ fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
+ fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
+
+ return fat_date;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_print_sector:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_DEBUG
+void fatfs_print_sector(uint32 sector, uint8 *data)
+{
+ int i;
+ int j;
+
+ FAT_PRINTF(("Sector %d:\n", sector));
+
+ for (i=0;i<FAT_SECTOR_SIZE;i++)
+ {
+ if (!((i) % 16))
+ {
+ FAT_PRINTF((" %04d: ", i));
+ }
+
+ FAT_PRINTF(("%02x", data[i]));
+ if (!((i+1) % 4))
+ {
+ FAT_PRINTF((" "));
+ }
+
+ if (!((i+1) % 16))
+ {
+ FAT_PRINTF((" "));
+ for (j=0;j<16;j++)
+ {
+ char ch = data[i-15+j];
+
+ // Is printable?
+ if (ch > 31 && ch < 127)
+ {
+ FAT_PRINTF(("%c", ch));
+ }
+ else
+ {
+ FAT_PRINTF(("."));
+ }
+ }
+
+ FAT_PRINTF(("\n"));
+ }
+ }
+}
+#endif
--- /dev/null
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES 20
+#define MAX_LFN_ENTRY_LENGTH 13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location) ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location) ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); \
+ buffer[location+2] = (uint8)((value>>16)&0xFF); \
+ buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Structure (max 260 LFN length)
+ uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+ uint8 Null;
+#endif
+ uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int fatfs_entry_is_file(struct fat_dir_entry *entry);
+int fatfs_lfn_entries_required(char *filename);
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16 fatfs_convert_to_fat_date(int day, int month, int year);
+void fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
--- /dev/null
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+ #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+ #define FATFS_IS_LITTLE_ENDIAN 1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+ #define FATFS_MAX_LONG_FILENAME 260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+ #define FATFS_MAX_OPEN_FILES 2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES 128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+ #define FATFS_INC_WRITE_SUPPORT 1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+ #define FATFS_INC_LFN_SUPPORT 1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+ #define FATFS_DIR_LIST_SUPPORT 1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+ #define FATFS_INC_TIME_DATE_SUPPORT 0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+ #define FATFS_INC_FORMAT_SUPPORT 1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE 512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+ // Don't include stdio, but there is a printf function available
+ #ifdef FAT_PRINTF_NOINC_STDIO
+ extern int printf(const char* ctrl1, ... );
+ #define FAT_PRINTF(a) printf a
+ // Include stdio to use printf
+ #else
+ #include <stdio.h>
+ #define FAT_PRINTF(a) printf a
+ #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+ #include <time.h>
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include <assert.h>
+#include "fat_string.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_total_path_levels: Take a filename and path and count the sub levels
+// of folders. E.g. C:\folder\file.zip = 1 level
+// Acceptable input formats are:
+// c:\folder\file.zip
+// /dev/etc/samba.conf
+// Returns: -1 = Error, 0 or more = Ok
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path)
+{
+ int levels = 0;
+ char expectedchar;
+
+ if (!path)
+ return -1;
+
+ // Acceptable formats:
+ // c:\folder\file.zip
+ // /dev/etc/samba.conf
+ if (*path == '/')
+ {
+ expectedchar = '/';
+ path++;
+ }
+ else if (path[1] == ':' || path[2] == '\\')
+ {
+ expectedchar = '\\';
+ path += 3;
+ }
+ else
+ return -1;
+
+ // Count levels in path string
+ while (*path)
+ {
+ // Fast forward through actual subdir text to next slash
+ for (; *path; )
+ {
+ // If slash detected escape from for loop
+ if (*path == expectedchar) { path++; break; }
+ path++;
+ }
+
+ // Increase number of subdirs founds
+ levels++;
+ }
+
+ // Subtract the file itself
+ return levels-1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_substring: Get a substring from 'path' which contains the folder
+// (or file) at the specified level.
+// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
+// Returns: -1 = Error, 0 = Ok
+//-----------------------------------------------------------------------------
+int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
+{
+ int i;
+ int pathlen=0;
+ int levels=0;
+ int copypnt=0;
+ char expectedchar;
+
+ if (!path || max_len <= 0)
+ return -1;
+
+ // Acceptable formats:
+ // c:\folder\file.zip
+ // /dev/etc/samba.conf
+ if (*path == '/')
+ {
+ expectedchar = '/';
+ path++;
+ }
+ else if (path[1] == ':' || path[2] == '\\')
+ {
+ expectedchar = '\\';
+ path += 3;
+ }
+ else
+ return -1;
+
+ // Get string length of path
+ pathlen = (int)strlen (path);
+
+ // Loop through the number of times as characters in 'path'
+ for (i = 0; i<pathlen; i++)
+ {
+ // If a '\' is found then increase level
+ if (*path == expectedchar) levels++;
+
+ // If correct level and the character is not a '\' or '/' then copy text to 'output'
+ if ( (levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len-1)))
+ output[copypnt++] = *path;
+
+ // Increment through path string
+ path++;
+ }
+
+ // Null Terminate
+ output[copypnt] = '\0';
+
+ // If a string was copied return 0 else return 1
+ if (output[0] != '\0')
+ return 0; // OK
+ else
+ return -1; // Error
+}
+//-----------------------------------------------------------------------------
+// fatfs_split_path: Full path contains the passed in string.
+// Returned is the path string and file Name string
+// E.g. C:\folder\file.zip -> path = C:\folder filename = file.zip
+// E.g. C:\file.zip -> path = [blank] filename = file.zip
+//-----------------------------------------------------------------------------
+int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
+{
+ int strindex;
+
+ // Count the levels to the filepath
+ int levels = fatfs_total_path_levels(full_path);
+ if (levels == -1)
+ return -1;
+
+ // Get filename part of string
+ if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
+ return -1;
+
+ // If root file
+ if (levels == 0)
+ path[0] = '\0';
+ else
+ {
+ strindex = (int)strlen(full_path) - (int)strlen(filename);
+ if (strindex > max_path)
+ strindex = max_path;
+
+ memcpy(path, full_path, strindex);
+ path[strindex-1] = '\0';
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
+//-----------------------------------------------------------------------------
+static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
+{
+ int diff;
+ char a,b;
+
+ while (n--)
+ {
+ a = *s1;
+ b = *s2;
+
+ // Make lower case if uppercase
+ if ((a>='A') && (a<='Z'))
+ a+= 32;
+ if ((b>='A') && (b<='Z'))
+ b+= 32;
+
+ diff = a - b;
+
+ // If different
+ if (diff)
+ return diff;
+
+ // If run out of strings
+ if ( (*s1 == 0) || (*s2 == 0) )
+ break;
+
+ s1++;
+ s2++;
+ }
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_GetExtension: Get index to extension within filename
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_GetExtension(char *str)
+{
+ int dotPos = -1;
+ char *strSrc = str;
+
+ // Find last '.' in string (if at all)
+ while (*strSrc)
+ {
+ if (*strSrc=='.')
+ dotPos = (int)(strSrc-str);
+
+ strSrc++;
+ }
+
+ return dotPos;
+}
+//-----------------------------------------------------------------------------
+// FileString_TrimLength: Get length of string excluding trailing spaces
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_TrimLength(char *str, int strLen)
+{
+ int length = strLen;
+ char *strSrc = str+strLen-1;
+
+ // Find last non white space
+ while (strLen != 0)
+ {
+ if (*strSrc == ' ')
+ length = (int)(strSrc - str);
+ else
+ break;
+
+ strSrc--;
+ strLen--;
+ }
+
+ return length;
+}
+//-----------------------------------------------------------------------------
+// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
+// Returns 1 if match, 0 if not
+//-----------------------------------------------------------------------------
+int fatfs_compare_names(char* strA, char* strB)
+{
+ char *ext1 = NULL;
+ char *ext2 = NULL;
+ int ext1Pos, ext2Pos;
+ int file1Len, file2Len;
+
+ // Get both files extension
+ ext1Pos = FileString_GetExtension(strA);
+ ext2Pos = FileString_GetExtension(strB);
+
+ // NOTE: Extension position can be different for matching
+ // filename if trailing space are present before it!
+ // Check that if one has an extension, so does the other
+ if ((ext1Pos==-1) && (ext2Pos!=-1))
+ return 0;
+ if ((ext2Pos==-1) && (ext1Pos!=-1))
+ return 0;
+
+ // If they both have extensions, compare them
+ if (ext1Pos!=-1)
+ {
+ // Set pointer to start of extension
+ ext1 = strA+ext1Pos+1;
+ ext2 = strB+ext2Pos+1;
+
+ // Verify that the file extension lengths match!
+ if (strlen(ext1) != strlen(ext2))
+ return 0;
+
+ // If they dont match
+ if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
+ return 0;
+
+ // Filelength is upto extensions
+ file1Len = ext1Pos;
+ file2Len = ext2Pos;
+ }
+ // No extensions
+ else
+ {
+ // Filelength is actual filelength
+ file1Len = (int)strlen(strA);
+ file2Len = (int)strlen(strB);
+ }
+
+ // Find length without trailing spaces (before ext)
+ file1Len = FileString_TrimLength(strA, file1Len);
+ file2Len = FileString_TrimLength(strB, file2Len);
+
+ // Check the file lengths match
+ if (file1Len!=file2Len)
+ return 0;
+
+ // Compare main part of filenames
+ if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
+ return 0;
+ else
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
+//-----------------------------------------------------------------------------
+int fatfs_string_ends_with_slash(char *path)
+{
+ if (path)
+ {
+ while (*path)
+ {
+ // Last character?
+ if (!(*(path+1)))
+ {
+ if (*path == '\\' || *path == '/')
+ return 1;
+ }
+
+ path++;
+ }
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_sfn_display_name: Get display name for SFN entry
+//-----------------------------------------------------------------------------
+int fatfs_get_sfn_display_name(char* out, char* in)
+{
+ int len = 0;
+ while (*in && len <= 11)
+ {
+ char a = *in++;
+
+ if (a == ' ')
+ continue;
+ // Make lower case if uppercase
+ else if ((a>='A') && (a<='Z'))
+ a+= 32;
+
+ *out++ = a;
+ len++;
+ }
+
+ *out = '\0';
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_extension: Get extension of filename passed in 'filename'.
+// Returned extension is always lower case.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_get_extension(char* filename, char* out, int maxlen)
+{
+ int len = 0;
+
+ // Get files extension offset
+ int ext_pos = FileString_GetExtension(filename);
+
+ if (ext_pos > 0 && out && maxlen)
+ {
+ filename += ext_pos + 1;
+
+ while (*filename && len < (maxlen-1))
+ {
+ char a = *filename++;
+
+ // Make lowercase if uppercase
+ if ((a>='A') && (a<='Z'))
+ a+= 32;
+
+ *out++ = a;
+ len++;
+ }
+
+ *out = '\0';
+ return 1;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_path_string: Append path & filename to create file path string.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
+{
+ int len = 0;
+ char last = 0;
+ char seperator = '/';
+
+ if (path && filename && out && maxlen > 0)
+ {
+ while (*path && len < (maxlen-2))
+ {
+ last = *path++;
+ if (last == '\\')
+ seperator = '\\';
+ *out++ = last;
+ len++;
+ }
+
+ // Add a seperator if trailing one not found
+ if (last != '\\' && last != '/')
+ *out++ = seperator;
+
+ while (*filename && len < (maxlen-1))
+ {
+ *out++ = *filename++;
+ len++;
+ }
+
+ *out = '\0';
+
+ return 1;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// Test Bench
+//-----------------------------------------------------------------------------
+#ifdef FAT_STRING_TESTBENCH
+void main(void)
+{
+ char output[255];
+ char output2[255];
+
+ assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
+ assert(fatfs_total_path_levels("C:\\file.zip") == 0);
+ assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
+ assert(fatfs_total_path_levels("C:\\") == -1);
+ assert(fatfs_total_path_levels("") == -1);
+ assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
+ assert(fatfs_total_path_levels("/dev/file.zip") == 1);
+
+ assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
+ assert(strcmp(output, "folder") == 0);
+
+ assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
+ assert(strcmp(output, "file.zip") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
+ assert(strcmp(output, "dev") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
+ assert(strcmp(output, "etc") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
+ assert(strcmp(output, "file.zip") == 0);
+
+ assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(strcmp(output, "C:\\folder") == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(output[0] == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(strcmp(output, "/dev/etc") == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
+ assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
+ assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
+
+ assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
+ assert(FileString_TrimLength("C:\\file.zip ", strlen("C:\\file.zip ")) == strlen("C:\\file.zip"));
+ assert(FileString_TrimLength(" ", strlen(" ")) == 0);
+
+ assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
+ assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
+ assert(fatfs_compare_names("C:\\file .ext", "C:\\file.ext") == 1);
+ assert(fatfs_compare_names("C:\\file .ext", "C:\\file2.ext") == 0);
+
+ assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
+ assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
+ assert(fatfs_string_ends_with_slash("/path") == 0);
+ assert(fatfs_string_ends_with_slash("/path/a") == 0);
+ assert(fatfs_string_ends_with_slash("/path/") == 1);
+
+ assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
+ assert(strcmp(output, "wav") == 0);
+ assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
+ assert(strcmp(output, "wav") == 0);
+ assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
+ assert(strcmp(output, "ext") != 0);
+
+ assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "/mydir1/myfile.txt") == 0);
+ assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
+ assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
+}
+#endif
--- /dev/null
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
+ #error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
+#endif
+
+//-----------------------------------------------------------------------------
+// FAT Sector Buffer
+//-----------------------------------------------------------------------------
+#define FAT32_GET_32BIT_WORD(pbuf, location) ( GET_32BIT_WORD(pbuf->ptr, location) )
+#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+#define FAT16_GET_16BIT_WORD(pbuf, location) ( GET_16BIT_WORD(pbuf->ptr, location) )
+#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+
+//-----------------------------------------------------------------------------
+// fatfs_fat_init:
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs)
+{
+ int i;
+
+ // FAT buffer chain head
+ fs->fat_buffer_head = NULL;
+
+ for (i=0;i<FAT_BUFFERS;i++)
+ {
+ // Initialise buffers to invalid
+ fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
+ fs->fat_buffers[i].dirty = 0;
+ memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
+ fs->fat_buffers[i].ptr = NULL;
+
+ // Add to head of queue
+ fs->fat_buffers[i].next = fs->fat_buffer_head;
+ fs->fat_buffer_head = &fs->fat_buffers[i];
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+static int fatfs_fat_writeback(struct fatfs *fs, struct fat_buffer *pcur)
+{
+ if (pcur)
+ {
+ // Writeback sector if changed
+ if (pcur->dirty)
+ {
+ if (fs->disk_io.write_media)
+ {
+ uint32 sectors = FAT_BUFFER_SECTORS;
+ uint32 offset = pcur->address - fs->fat_begin_lba;
+
+ // Limit to sectors used for the FAT
+ if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
+ sectors = FAT_BUFFER_SECTORS;
+ else
+ sectors = fs->fat_sectors - offset;
+
+ if (!fs->disk_io.write_media(pcur->address, pcur->sector, sectors))
+ return 0;
+ }
+
+ pcur->dirty = 0;
+ }
+
+ return 1;
+ }
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_read_sector: Read a FAT sector
+//-----------------------------------------------------------------------------
+static struct fat_buffer *fatfs_fat_read_sector(struct fatfs *fs, uint32 sector)
+{
+ struct fat_buffer *last = NULL;
+ struct fat_buffer *pcur = fs->fat_buffer_head;
+
+ // Itterate through sector buffer list
+ while (pcur)
+ {
+ // Sector within this buffer?
+ if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
+ break;
+
+ // End of list?
+ if (pcur->next == NULL)
+ {
+ // Remove buffer from list
+ if (last)
+ last->next = NULL;
+ // We the first and last buffer in the chain?
+ else
+ fs->fat_buffer_head = NULL;
+ }
+
+ last = pcur;
+ pcur = pcur->next;
+ }
+
+ // We found the sector already in FAT buffer chain
+ if (pcur)
+ {
+ pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
+ return pcur;
+ }
+
+ // Else, we removed the last item from the list
+ pcur = last;
+
+ // Add to start of sector buffer list (now newest sector)
+ pcur->next = fs->fat_buffer_head;
+ fs->fat_buffer_head = pcur;
+
+ // Writeback sector if changed
+ if (pcur->dirty)
+ if (!fatfs_fat_writeback(fs, pcur))
+ return 0;
+
+ // Address is now new sector
+ pcur->address = sector;
+
+ // Read next sector
+ if (!fs->disk_io.read_media(pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
+ {
+ // Read failed, invalidate buffer address
+ pcur->address = FAT32_INVALID_CLUSTER;
+ return NULL;
+ }
+
+ pcur->ptr = pcur->sector;
+ return pcur;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+int fatfs_fat_purge(struct fatfs *fs)
+{
+ struct fat_buffer *pcur = fs->fat_buffer_head;
+
+ // Itterate through sector buffer list
+ while (pcur)
+ {
+ // Writeback sector if changed
+ if (pcur->dirty)
+ if (!fatfs_fat_writeback(fs, pcur))
+ return 0;
+
+ pcur = pcur->next;
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// General FAT Table Operations
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
+// reading FAT table and traversing it. Return 0xffffffff for end of chain.
+//-----------------------------------------------------------------------------
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster)
+{
+ uint32 fat_sector_offset, position;
+ uint32 nextcluster;
+ struct fat_buffer *pbuf;
+
+ // Why is '..' labelled with cluster 0 when it should be 2 ??
+ if (current_cluster == 0)
+ current_cluster = 2;
+
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = current_cluster / 256;
+ else
+ fat_sector_offset = current_cluster / 128;
+
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return (FAT32_LAST_CLUSTER);
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+
+ // If end of chain found
+ if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
+ return (FAT32_LAST_CLUSTER);
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+ // Mask out MS 4 bits (its 28bit addressing)
+ nextcluster = nextcluster & 0x0FFFFFFF;
+
+ // If end of chain found
+ if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
+ return (FAT32_LAST_CLUSTER);
+ }
+
+ // Else return next cluster
+ return (nextcluster);
+}
+//-----------------------------------------------------------------------------
+// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
+//-----------------------------------------------------------------------------
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue)
+{
+ if (fs->fat_type == FAT_TYPE_16)
+ ;
+ else
+ {
+ // Load sector to change it
+ struct fat_buffer *pbuf = fatfs_fat_read_sector(fs, fs->lba_begin+fs->fs_info_sector);
+ if (!pbuf)
+ return ;
+
+ // Change
+ FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
+ fs->next_free_cluster = newValue;
+
+ // Write back FSINFO sector to disk
+ if (fs->disk_io.write_media)
+ fs->disk_io.write_media(pbuf->address, pbuf->sector, 1);
+
+ // Invalidate cache entry
+ pbuf->address = FAT32_INVALID_CLUSTER;
+ pbuf->dirty = 0;
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
+{
+ uint32 fat_sector_offset, position;
+ uint32 nextcluster;
+ uint32 current_cluster = start_cluster;
+ struct fat_buffer *pbuf;
+
+ do
+ {
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = current_cluster / 256;
+ else
+ fat_sector_offset = current_cluster / 128;
+
+ if ( fat_sector_offset < fs->fat_sectors)
+ {
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return 0;
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+ // Mask out MS 4 bits (its 28bit addressing)
+ nextcluster = nextcluster & 0x0FFFFFFF;
+ }
+
+ if (nextcluster !=0 )
+ current_cluster++;
+ }
+ else
+ // Otherwise, run out of FAT sectors to check...
+ return 0;
+ }
+ while (nextcluster != 0x0);
+
+ // Found blank entry
+ *free_cluster = current_cluster;
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
+// write (slow).
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster)
+{
+ struct fat_buffer *pbuf;
+ uint32 fat_sector_offset, position;
+
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = cluster / 256;
+ else
+ fat_sector_offset = cluster / 128;
+
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return 0;
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 16 bit entry of current sector relating to cluster number
+ position = (cluster - (fat_sector_offset * 256)) * 2;
+
+ // Write Next Clusters value to Sector Buffer
+ FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (cluster - (fat_sector_offset * 128)) * 4;
+
+ // Write Next Clusters value to Sector Buffer
+ FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
+ }
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_free_cluster_chain: Follow a chain marking each element as free
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster)
+{
+ uint32 last_cluster;
+ uint32 next_cluster = start_cluster;
+
+ // Loop until end of chain
+ while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
+ {
+ last_cluster = next_cluster;
+
+ // Find next link
+ next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+
+ // Clear last link
+ fatfs_fat_set_cluster(fs, last_cluster, 0x00000000);
+ }
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
+// to the current tail.
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
+{
+ uint32 last_cluster = FAT32_LAST_CLUSTER;
+ uint32 next_cluster = start_cluster;
+
+ if (start_cluster == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Loop until end of chain
+ while ( next_cluster != FAT32_LAST_CLUSTER )
+ {
+ last_cluster = next_cluster;
+
+ // Find next link
+ next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+ if (!next_cluster)
+ return 0;
+ }
+
+ // Add link in for new cluster
+ fatfs_fat_set_cluster(fs, last_cluster, newEntry);
+
+ // Mark new cluster as end of chain
+ fatfs_fat_set_cluster(fs, newEntry, FAT32_LAST_CLUSTER);
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_count_free_clusters:
+//-----------------------------------------------------------------------------
+uint32 fatfs_count_free_clusters(struct fatfs *fs)
+{
+ uint32 i,j;
+ uint32 count = 0;
+ struct fat_buffer *pbuf;
+
+ for (i = 0; i < fs->fat_sectors; i++)
+ {
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba + i);
+ if (!pbuf)
+ break;
+
+ for (j = 0; j < FAT_SECTOR_SIZE; )
+ {
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
+ count++;
+
+ j += 2;
+ }
+ else
+ {
+ if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
+ count++;
+
+ j += 4;
+ }
+ }
+ }
+
+ return count;
+}
--- /dev/null
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs);
+int fatfs_fat_purge(struct fatfs *fs);
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32 fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
--- /dev/null
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+ #if __SIZEOF_LONG__ == 8
+ #define FATFS_DEF_UINT32_AS_INT
+ #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+
+ // If compiling on a 64-bit machine, use int as 32-bits
+ #ifdef FATFS_DEF_UINT32_AS_INT
+ typedef unsigned int uint32;
+ // Else for 32-bit machines & embedded systems, use long...
+ #else
+ typedef unsigned long uint32;
+ #endif
+#endif
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+ #define FAT_HTONS(n) (n)
+ #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+ #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+ #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+ ((((uint32)(n) & 0xFF00)) << 8) | \
+ ((((uint32)(n) & 0xFF0000)) >> 8) | \
+ ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED __attribute__ ((packed))
+#else
+ // Other compilers may require other methods of packing structures
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED
+#endif
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+// FAT16/32 File IO Library\r
+// V2.6\r
+// Ultra-Embedded.com\r
+// Copyright 2003 - 2012\r
+//\r
+// Email: admin@ultra-embedded.com\r
+//\r
+// License: GPL\r
+// If you would like a version with a more permissive license for use in\r
+// closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+#if FATFS_INC_WRITE_SUPPORT
+//-----------------------------------------------------------------------------
+// fatfs_add_free_space: Allocate another cluster of free space to the end
+// of a files cluster chain.
+//-----------------------------------------------------------------------------
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters)
+{
+ uint32 i;
+ uint32 nextcluster;
+ uint32 start = *startCluster;
+
+ // Set the next free cluster hint to unknown
+ if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+ fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+ for (i=0;i<clusters;i++)
+ {
+ // Start looking for free clusters from the beginning
+ if (fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+ {
+ // Point last to this
+ fatfs_fat_set_cluster(fs, start, nextcluster);
+
+ // Point this to end of file
+ fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+
+ // Adjust argument reference
+ start = nextcluster;
+ if (i == 0)
+ *startCluster = nextcluster;
+ }
+ else
+ return 0;
+ }
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_allocate_free_space: Add an ammount of free space to a file either from
+// 'startCluster' if newFile = false, or allocating a new start to the chain if
+// newFile = true.
+//-----------------------------------------------------------------------------
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
+{
+ uint32 clusterSize;
+ uint32 clusterCount;
+ uint32 nextcluster;
+
+ if (size==0)
+ return 0;
+
+ // Set the next free cluster hint to unknown
+ if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+ fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+ // Work out size and clusters
+ clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
+ clusterCount = (size / clusterSize);
+
+ // If any left over
+ if (size-(clusterSize*clusterCount))
+ clusterCount++;
+
+ // Allocated first link in the chain if a new file
+ if (newFile)
+ {
+ if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+ return 0;
+
+ // If this is all that is needed then all done
+ if (clusterCount==1)
+ {
+ fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+ *startCluster = nextcluster;
+ return 1;
+ }
+ }
+ // Allocate from end of current chain (startCluster is end of chain)
+ else
+ nextcluster = *startCluster;
+
+ if (!fatfs_add_free_space(fs, &nextcluster, clusterCount))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
+// which takes up 'entryCount' blocks (or allocate some more)
+//-----------------------------------------------------------------------------
+static int fatfs_find_free_dir_offset(struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
+{
+ struct fat_dir_entry *directoryEntry;
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ int x=0;
+ int possible_spaces = 0;
+ int start_recorded = 0;
+
+ // No entries required?
+ if (entryCount == 0)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+ // LFN Entry
+ if (fatfs_entry_lfn_text(directoryEntry))
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ // Increment the count in-case the file turns
+ // out to be deleted...
+ possible_spaces++;
+ }
+ // SFN Entry
+ else
+ {
+ // Has file been deleted?
+ if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ possible_spaces++;
+
+ // We have found enough space?
+ if (possible_spaces >= entryCount)
+ return 1;
+
+ // Else continue counting until we find a valid entry!
+ }
+ // Is the file entry empty?
+ else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ // Increment the blank entries count
+ possible_spaces++;
+
+ // We have found enough space?
+ if (possible_spaces >= entryCount)
+ return 1;
+ }
+ // File entry is valid
+ else
+ {
+ // Reset all flags
+ possible_spaces = 0;
+ start_recorded = 0;
+ }
+ }
+ } // End of for
+ } // End of if
+ // Run out of free space in the directory, allocate some more
+ else
+ {
+ uint32 newCluster;
+
+ // Get a new cluster for directory
+ if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &newCluster))
+ return 0;
+
+ // Add cluster to end of directory tree
+ if (!fatfs_fat_add_cluster_to_chain(fs, dirCluster, newCluster))
+ return 0;
+
+ // Erase new directory cluster
+ memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
+ for (i=0;i<fs->sectors_per_cluster;i++)
+ {
+ if (!fatfs_write_sector(fs, newCluster, i, 0))
+ return 0;
+ }
+
+ // If non of the name fitted on previous sectors
+ if (!start_recorded)
+ {
+ // Store start
+ *pSector = (x-1);
+ *pOffset = 0;
+ start_recorded = 1;
+ }
+
+ return 1;
+ }
+ } // End of while loop
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ uint32 x=0;
+ int entryCount;
+ struct fat_dir_entry shortEntry;
+ int dirtySector = 0;
+
+ uint32 dirSector = 0;
+ uint8 dirOffset = 0;
+ int foundEnd = 0;
+
+ uint8 checksum;
+ uint8 *pSname;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+#if FATFS_INC_LFN_SUPPORT
+ // How many LFN entries are required?
+ // NOTE: We always request one LFN even if it would fit in a SFN!
+ entryCount = fatfs_lfn_entries_required(filename);
+ if (!entryCount)
+ return 0;
+#else
+ entryCount = 0;
+#endif
+
+ // Find space in the directory for this filename (or allocate some more)
+ // NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
+ if (!fatfs_find_free_dir_offset(fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
+ return 0;
+
+ // Generate checksum of short filename
+ pSname = (uint8*)shortfilename;
+ checksum = 0;
+ for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
+
+ // Start from current sector where space was found!
+ x = dirSector;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // If the start position for the entry has been found
+ if (foundEnd==0)
+ if ( (dirSector==(x-1)) && (dirOffset==item) )
+ foundEnd = 1;
+
+ // Start adding filename
+ if (foundEnd)
+ {
+ if (entryCount==0)
+ {
+ // Short filename
+ fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update create, access & modify time & date
+ fatfs_update_timestamps(&shortEntry, 1, 1, 1);
+#endif
+
+ memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
+
+ // Writeback
+ return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+#if FATFS_INC_LFN_SUPPORT
+ else
+ {
+ entryCount--;
+
+ // Copy entry to directory buffer
+ fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
+ dirtySector = 1;
+ }
+#endif
+ }
+ } // End of if
+
+ // Write back to disk before loading another sector
+ if (dirtySector)
+ {
+ if (!fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1))
+ return 0;
+
+ dirtySector = 0;
+ }
+ }
+ else
+ return 0;
+ } // End of while loop
+
+ return 0;
+}
+#endif
--- /dev/null
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
--- /dev/null
+/******************************************************************************
+ * partresize.c ---- ventoy part resize util
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fat_filelib.h>
+#include "vtoycli.h"
+
+static int g_disk_fd = 0;
+static UINT64 g_disk_offset = 0;
+static GUID g_ZeroGuid = {0};
+static GUID g_WindowsDataPartGuid = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
+
+static int vtoy_disk_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+ UINT64 offset = sector * 512ULL;
+
+ lseek(g_disk_fd, g_disk_offset + offset, SEEK_SET);
+ read(g_disk_fd, buffer, sector_count * 512);
+
+ return 1;
+}
+
+static int vtoy_disk_write(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+ UINT64 offset = sector * 512ULL;
+
+ lseek(g_disk_fd, g_disk_offset + offset, SEEK_SET);
+ write(g_disk_fd, buffer, sector_count * 512);
+
+ return 1;
+}
+
+
+static int gpt_check(const char *disk)
+{
+ int fd = -1;
+ int rc = 1;
+ VTOY_GPT_INFO *pGPT = NULL;
+
+ fd = open(disk, O_RDONLY);
+ if (fd < 0)
+ {
+ printf("Failed to open %s\n", disk);
+ goto out;
+ }
+
+ pGPT = malloc(sizeof(VTOY_GPT_INFO));
+ if (NULL == pGPT)
+ {
+ goto out;
+ }
+ memset(pGPT, 0, sizeof(VTOY_GPT_INFO));
+
+ read(fd, pGPT, sizeof(VTOY_GPT_INFO));
+
+ if (pGPT->MBR.PartTbl[0].FsFlag == 0xEE && memcmp(pGPT->Head.Signature, "EFI PART", 8) == 0)
+ {
+ rc = 0;
+ }
+
+out:
+ check_close(fd);
+ check_free(pGPT);
+ return rc;
+}
+
+static int part_check(const char *disk)
+{
+ int i;
+ int fd = -1;
+ int rc = 0;
+ int Index = 0;
+ int Count = 0;
+ int PartStyle = 0;
+ UINT64 Part1Start;
+ UINT64 Part1End;
+ UINT64 NextPartStart;
+ UINT64 DiskSizeInBytes;
+ VTOY_GPT_INFO *pGPT = NULL;
+
+ DiskSizeInBytes = get_disk_size_in_byte(disk);
+ if (DiskSizeInBytes == 0)
+ {
+ printf("Failed to get disk size of %s\n", disk);
+ goto out;
+ }
+
+ fd = open(disk, O_RDONLY);
+ if (fd < 0)
+ {
+ printf("Failed to open %s\n", disk);
+ goto out;
+ }
+
+ pGPT = malloc(sizeof(VTOY_GPT_INFO));
+ if (NULL == pGPT)
+ {
+ goto out;
+ }
+ memset(pGPT, 0, sizeof(VTOY_GPT_INFO));
+
+ read(fd, pGPT, sizeof(VTOY_GPT_INFO));
+
+ if (pGPT->MBR.PartTbl[0].FsFlag == 0xEE && memcmp(pGPT->Head.Signature, "EFI PART", 8) == 0)
+ {
+ PartStyle = 1;
+ }
+ else
+ {
+ PartStyle = 0;
+ }
+
+ if (PartStyle == 0)
+ {
+ PART_TABLE *PartTbl = pGPT->MBR.PartTbl;
+
+ for (Count = 0, i = 0; i < 4; i++)
+ {
+ if (PartTbl[i].SectorCount > 0)
+ {
+ printf("MBR Part%d SectorStart:%u SectorCount:%u\n", i + 1, PartTbl[i].StartSectorId, PartTbl[i].SectorCount);
+ Count++;
+ }
+ }
+
+ //We must have a free partition table for VTOYEFI partition
+ if (Count >= 4)
+ {
+ printf("###[FAIL] 4 MBR partition tables are all used.\n");
+ goto out;
+ }
+
+ if (PartTbl[0].SectorCount > 0)
+ {
+ Part1Start = PartTbl[0].StartSectorId;
+ Part1End = PartTbl[0].SectorCount + Part1Start;
+ }
+ else
+ {
+ printf("###[FAIL] MBR Partition 1 is invalid\n");
+ goto out;
+ }
+
+ Index = -1;
+ NextPartStart = DiskSizeInBytes / 512ULL;
+ for (i = 1; i < 4; i++)
+ {
+ if (PartTbl[i].SectorCount > 0 && NextPartStart > PartTbl[i].StartSectorId)
+ {
+ Index = i;
+ NextPartStart = PartTbl[i].StartSectorId;
+ }
+ }
+
+ NextPartStart *= 512ULL;
+ printf("DiskSize:%llu NextPartStart:%llu(LBA:%llu) Index:%d\n",
+ DiskSizeInBytes, NextPartStart, NextPartStart / 512ULL, Index);
+ }
+ else
+ {
+ VTOY_GPT_PART_TBL *PartTbl = pGPT->PartTbl;
+
+ for (Count = 0, i = 0; i < 128; i++)
+ {
+ if (memcmp(&(PartTbl[i].PartGuid), &g_ZeroGuid, sizeof(GUID)))
+ {
+ printf("GPT Part%d StartLBA:%llu LastLBA:%llu\n", i + 1, PartTbl[i].StartLBA, PartTbl[i].LastLBA);
+ Count++;
+ }
+ }
+
+ if (Count >= 128)
+ {
+ printf("###[FAIL] 128 GPT partition tables are all used.\n");
+ goto out;
+ }
+
+ if (memcmp(&(PartTbl[0].PartGuid), &g_ZeroGuid, sizeof(GUID)))
+ {
+ Part1Start = PartTbl[0].StartLBA;
+ Part1End = PartTbl[0].LastLBA + 1;
+ }
+ else
+ {
+ printf("###[FAIL] GPT Partition 1 is invalid\n");
+ goto out;
+ }
+
+ Index = -1;
+ NextPartStart = (pGPT->Head.PartAreaEndLBA + 1);
+ for (i = 1; i < 128; i++)
+ {
+ if (memcmp(&(PartTbl[i].PartGuid), &g_ZeroGuid, sizeof(GUID)) && NextPartStart > PartTbl[i].StartLBA)
+ {
+ Index = i;
+ NextPartStart = PartTbl[i].StartLBA;
+ }
+ }
+
+ NextPartStart *= 512ULL;
+ printf("DiskSize:%llu NextPartStart:%llu(LBA:%llu) Index:%d\n",
+ DiskSizeInBytes, NextPartStart, NextPartStart / 512ULL, Index);
+ }
+
+ printf("Valid partition table (%s): Valid partition count:%d\n", (PartStyle == 0) ? "MBR" : "GPT", Count);
+
+ //Partition 1 MUST start at 1MB
+ Part1Start *= 512ULL;
+ Part1End *= 512ULL;
+
+ printf("Partition 1 start at: %llu %lluKB, end:%llu, NextPartStart:%llu\n",
+ Part1Start, Part1Start / 1024, Part1End, NextPartStart);
+ if (Part1Start != SIZE_1MB)
+ {
+ printf("###[FAIL] Partition 1 is not start at 1MB\n");
+ goto out;
+ }
+
+
+ //If we have free space after partition 1
+ if (NextPartStart - Part1End >= VENTOY_EFI_PART_SIZE)
+ {
+ printf("Free space after partition 1 (%llu) is enough for VTOYEFI part\n", NextPartStart - Part1End);
+ rc = 1;
+ }
+ else if (NextPartStart == Part1End)
+ {
+ printf("There is no free space after partition 1\n");
+ rc = 2;
+ }
+ else
+ {
+ printf("The free space after partition 1 is not enough\n");
+ rc = 2;
+ }
+
+out:
+ check_close(fd);
+ check_free(pGPT);
+ return rc;
+}
+
+static int secureboot_proc(char *disk, UINT64 part2start)
+{
+ int rc = 0;
+ int size;
+ int fd = -1;
+ char *filebuf = NULL;
+ void *file = NULL;
+
+ fd = open(disk, O_RDWR);
+ if (fd < 0)
+ {
+ printf("Failed to open %s\n", disk);
+ return 1;
+ }
+
+ g_disk_fd = fd;
+ g_disk_offset = part2start * 512ULL;
+
+ fl_init();
+
+ if (0 == fl_attach_media(vtoy_disk_read, vtoy_disk_write))
+ {
+ file = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+ printf("Open ventoy efi file %p\n", file);
+ if (file)
+ {
+ fl_fseek(file, 0, SEEK_END);
+ size = (int)fl_ftell(file);
+ fl_fseek(file, 0, SEEK_SET);
+
+ printf("ventoy x64 efi file size %d ...\n", size);
+
+ filebuf = (char *)malloc(size);
+ if (filebuf)
+ {
+ fl_fread(filebuf, 1, size, file);
+ }
+
+ fl_fclose(file);
+
+ fl_remove("/EFI/BOOT/BOOTX64.EFI");
+ fl_remove("/EFI/BOOT/grubx64.efi");
+ fl_remove("/EFI/BOOT/grubx64_real.efi");
+ fl_remove("/EFI/BOOT/MokManager.efi");
+ fl_remove("/ENROLL_THIS_KEY_IN_MOKMANAGER.cer");
+
+ file = fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb");
+ printf("Open bootx64 efi file %p\n", file);
+ if (file)
+ {
+ if (filebuf)
+ {
+ fl_fwrite(filebuf, 1, size, file);
+ }
+
+ fl_fflush(file);
+ fl_fclose(file);
+ }
+
+ if (filebuf)
+ {
+ free(filebuf);
+ }
+ }
+
+ file = fl_fopen("/EFI/BOOT/grubia32_real.efi", "rb");
+ printf("Open ventoy ia32 efi file %p\n", file);
+ if (file)
+ {
+ fl_fseek(file, 0, SEEK_END);
+ size = (int)fl_ftell(file);
+ fl_fseek(file, 0, SEEK_SET);
+
+ printf("ventoy efi file size %d ...\n", size);
+
+ filebuf = (char *)malloc(size);
+ if (filebuf)
+ {
+ fl_fread(filebuf, 1, size, file);
+ }
+
+ fl_fclose(file);
+
+ fl_remove("/EFI/BOOT/BOOTIA32.EFI");
+ fl_remove("/EFI/BOOT/grubia32.efi");
+ fl_remove("/EFI/BOOT/grubia32_real.efi");
+ fl_remove("/EFI/BOOT/mmia32.efi");
+
+ file = fl_fopen("/EFI/BOOT/BOOTIA32.EFI", "wb");
+ printf("Open bootia32 efi file %p\n", file);
+ if (file)
+ {
+ if (filebuf)
+ {
+ fl_fwrite(filebuf, 1, size, file);
+ }
+
+ fl_fflush(file);
+ fl_fclose(file);
+ }
+
+ if (filebuf)
+ {
+ free(filebuf);
+ }
+ }
+
+ }
+ else
+ {
+ rc = 1;
+ }
+
+ fl_shutdown();
+ fsync(fd);
+
+ return rc;
+}
+
+static int VentoyFillMBRLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)
+{
+ UINT8 Head;
+ UINT8 Sector;
+ UINT8 nSector = 63;
+ UINT8 nHead = 8;
+ UINT32 Cylinder;
+ UINT32 EndSectorId;
+
+ while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
+ {
+ nHead = (UINT8)nHead * 2;
+ }
+
+ if (nHead == 0)
+ {
+ nHead = 255;
+ }
+
+ Cylinder = StartSectorId / nSector / nHead;
+ Head = StartSectorId / nSector % nHead;
+ Sector = StartSectorId % nSector + 1;
+
+ Table->StartHead = Head;
+ Table->StartSector = Sector;
+ Table->StartCylinder = Cylinder;
+
+ EndSectorId = StartSectorId + SectorCount - 1;
+ Cylinder = EndSectorId / nSector / nHead;
+ Head = EndSectorId / nSector % nHead;
+ Sector = EndSectorId % nSector + 1;
+
+ Table->EndHead = Head;
+ Table->EndSector = Sector;
+ Table->EndCylinder = Cylinder;
+
+ Table->StartSectorId = StartSectorId;
+ Table->SectorCount = SectorCount;
+
+ return 0;
+}
+
+
+static int WriteDataToPhyDisk(int fd, UINT64 offset, void *buffer, int len)
+{
+ ssize_t wrlen;
+ off_t newseek;
+
+ newseek = lseek(fd, offset, SEEK_SET);
+ if (newseek != offset)
+ {
+ printf("Failed to lseek %llu %lld %d\n", offset, (long long)newseek, errno);
+ return 0;
+ }
+
+ wrlen = write(fd, buffer, len);
+ if ((int)wrlen != len)
+ {
+ printf("Failed to write %d %d %d\n", len, (int)wrlen, errno);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead)
+{
+ UINT64 LBA;
+ UINT64 BackupLBA;
+
+ memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR));
+
+ LBA = pHead->EfiStartLBA;
+ BackupLBA = pHead->EfiBackupLBA;
+
+ pHead->EfiStartLBA = BackupLBA;
+ pHead->EfiBackupLBA = LBA;
+ pHead->PartTblStartLBA = BackupLBA + 1 - 33;
+
+ pHead->Crc = 0;
+ pHead->Crc = VtoyCrc32(pHead, pHead->Length);
+
+ return 0;
+}
+
+static int update_part_table(char *disk, UINT64 part2start)
+{
+ int i;
+ int j;
+ int fd = -1;
+ int rc = 1;
+ int PartStyle = 0;
+ ssize_t len = 0;
+ UINT64 DiskSizeInBytes;
+ VTOY_GPT_INFO *pGPT = NULL;
+ VTOY_GPT_HDR *pBack = NULL;
+
+ DiskSizeInBytes = get_disk_size_in_byte(disk);
+ if (DiskSizeInBytes == 0)
+ {
+ printf("Failed to get disk size of %s\n", disk);
+ goto out;
+ }
+
+ fd = open(disk, O_RDWR);
+ if (fd < 0)
+ {
+ printf("Failed to open %s\n", disk);
+ goto out;
+ }
+
+ pGPT = malloc(sizeof(VTOY_GPT_INFO) + sizeof(VTOY_GPT_HDR));
+ if (NULL == pGPT)
+ {
+ goto out;
+ }
+ memset(pGPT, 0, sizeof(VTOY_GPT_INFO) + sizeof(VTOY_GPT_HDR));
+
+ pBack = (VTOY_GPT_HDR *)(pGPT + 1);
+
+ len = read(fd, pGPT, sizeof(VTOY_GPT_INFO));
+ if (len != (ssize_t)sizeof(VTOY_GPT_INFO))
+ {
+ printf("Failed to read partition table %d err:%d\n", (int)len, errno);
+ goto out;
+ }
+
+ if (pGPT->MBR.PartTbl[0].FsFlag == 0xEE && memcmp(pGPT->Head.Signature, "EFI PART", 8) == 0)
+ {
+ PartStyle = 1;
+ }
+ else
+ {
+ PartStyle = 0;
+ }
+
+ if (PartStyle == 0)
+ {
+ PART_TABLE *PartTbl = pGPT->MBR.PartTbl;
+
+ for (i = 1; i < 4; i++)
+ {
+ if (PartTbl[i].SectorCount == 0)
+ {
+ break;
+ }
+ }
+
+ if (i >= 4)
+ {
+ printf("###[FAIL] Can not find a free MBR partition table.\n");
+ goto out;
+ }
+
+ for (j = i - 1; j > 0; j--)
+ {
+ printf("Move MBR partition table %d --> %d\n", j + 1, j + 2);
+ memcpy(PartTbl + (j + 1), PartTbl + j, sizeof(PART_TABLE));
+ }
+
+ memset(PartTbl + 1, 0, sizeof(PART_TABLE));
+ VentoyFillMBRLocation(DiskSizeInBytes, (UINT32)part2start, VENTOY_EFI_PART_SIZE / 512, PartTbl + 1);
+ PartTbl[1].Active = 0x00;
+ PartTbl[1].FsFlag = 0xEF; // EFI System Partition
+
+ PartTbl[0].Active = 0x80; // bootable
+ PartTbl[0].SectorCount = (UINT32)part2start - 2048;
+
+ if (!WriteDataToPhyDisk(fd, 0, &(pGPT->MBR), 512))
+ {
+ printf("MBR write MBR failed\n");
+ goto out;
+ }
+
+ fsync(fd);
+ printf("MBR update partition table success.\n");
+ rc = 0;
+ }
+ else
+ {
+ VTOY_GPT_PART_TBL *PartTbl = pGPT->PartTbl;
+
+ for (i = 1; i < 128; i++)
+ {
+ if (memcmp(&(PartTbl[i].PartGuid), &g_ZeroGuid, sizeof(GUID)) == 0)
+ {
+ break;
+ }
+ }
+
+ if (i >= 128)
+ {
+ printf("###[FAIL] Can not find a free GPT partition table.\n");
+ goto out;
+ }
+
+ for (j = i - 1; j > 0; j--)
+ {
+ printf("Move GPT partition table %d --> %d\n", j + 1, j + 2);
+ memcpy(PartTbl + (j + 1), PartTbl + j, sizeof(VTOY_GPT_PART_TBL));
+ }
+
+ // to fix windows issue
+ memset(PartTbl + 1, 0, sizeof(VTOY_GPT_PART_TBL));
+ memcpy(&(PartTbl[1].PartType), &g_WindowsDataPartGuid, sizeof(GUID));
+ ventoy_gen_preudo_uuid(&(PartTbl[1].PartGuid));
+
+ PartTbl[0].LastLBA = part2start - 1;
+
+ PartTbl[1].StartLBA = PartTbl[0].LastLBA + 1;
+ PartTbl[1].LastLBA = PartTbl[1].StartLBA + VENTOY_EFI_PART_SIZE / 512 - 1;
+ PartTbl[1].Attr = 0xC000000000000001ULL;
+ PartTbl[1].Name[0] = 'V';
+ PartTbl[1].Name[1] = 'T';
+ PartTbl[1].Name[2] = 'O';
+ PartTbl[1].Name[3] = 'Y';
+ PartTbl[1].Name[4] = 'E';
+ PartTbl[1].Name[5] = 'F';
+ PartTbl[1].Name[6] = 'I';
+ PartTbl[1].Name[7] = 0;
+
+ //Update CRC
+ pGPT->Head.PartTblCrc = VtoyCrc32(pGPT->PartTbl, sizeof(pGPT->PartTbl));
+ pGPT->Head.Crc = 0;
+ pGPT->Head.Crc = VtoyCrc32(&(pGPT->Head), pGPT->Head.Length);
+
+ printf("pGPT->Head.EfiStartLBA=%llu\n", pGPT->Head.EfiStartLBA);
+ printf("pGPT->Head.EfiBackupLBA=%llu\n", pGPT->Head.EfiBackupLBA);
+
+ VentoyFillBackupGptHead(pGPT, pBack);
+ if (!WriteDataToPhyDisk(fd, pGPT->Head.EfiBackupLBA * 512, pBack, 512))
+ {
+ printf("GPT write backup head failed\n");
+ goto out;
+ }
+
+ if (!WriteDataToPhyDisk(fd, (pGPT->Head.EfiBackupLBA - 32) * 512, pGPT->PartTbl, 512 * 32))
+ {
+ printf("GPT write backup partition table failed\n");
+ goto out;
+ }
+
+ if (!WriteDataToPhyDisk(fd, 0, pGPT, 512 * 34))
+ {
+ printf("GPT write MBR & Main partition table failed\n");
+ goto out;
+ }
+
+ fsync(fd);
+ printf("GPT update partition table success.\n");
+ rc = 0;
+ }
+
+out:
+ check_close(fd);
+ check_free(pGPT);
+ return rc;
+}
+
+int partresize_main(int argc, char **argv)
+{
+ UINT64 sector;
+
+ if (argc != 3 && argc != 4)
+ {
+ printf("usage: partresize -c/-f /dev/sdb\n");
+ return 1;
+ }
+
+ if (strcmp(argv[1], "-c") == 0)
+ {
+ return part_check(argv[2]);
+ }
+ else if (strcmp(argv[1], "-s") == 0)
+ {
+ sector = strtoull(argv[3], NULL, 10);
+ return secureboot_proc(argv[2], sector);
+ }
+ else if (strcmp(argv[1], "-p") == 0)
+ {
+ sector = strtoull(argv[3], NULL, 10);
+ return update_part_table(argv[2], sector);
+ }
+ else if (strcmp(argv[1], "-t") == 0)
+ {
+ return gpt_check(argv[2]);
+ }
+ else
+ {
+ return 1;
+ }
+}
+
--- /dev/null
+/******************************************************************************
+ * vtoycli.c
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "vtoycli.h"
+
+void ventoy_gen_preudo_uuid(void *uuid)
+{
+ int i;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ {
+ srand(time(NULL));
+ for (i = 0; i < 8; i++)
+ {
+ *((uint16_t *)uuid + i) = (uint16_t)(rand() & 0xFFFF);
+ }
+ }
+ else
+ {
+ read(fd, uuid, 16);
+ close(fd);
+ }
+}
+
+UINT64 get_disk_size_in_byte(const char *disk)
+{
+ int fd;
+ int rc;
+ const char *pos = disk;
+ unsigned long long size = 0;
+ char diskpath[256] = {0};
+ char sizebuf[64] = {0};
+
+ if (strncmp(disk, "/dev/", 5) == 0)
+ {
+ pos = disk + 5;
+ }
+
+ // Try 1: get size from sysfs
+ snprintf(diskpath, sizeof(diskpath) - 1, "/sys/block/%s/size", pos);
+ if (access(diskpath, F_OK) >= 0)
+ {
+ fd = open(diskpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, sizebuf, sizeof(sizebuf));
+ size = strtoull(sizebuf, NULL, 10);
+ close(fd);
+ return (size * 512);
+ }
+ }
+ else
+ {
+ printf("%s not exist \n", diskpath);
+ }
+
+ printf("disk %s size %llu bytes\n", disk, (unsigned long long)size);
+ return size;
+}
+
+
+int main(int argc, char **argv)
+{
+ if (argc < 2)
+ {
+ return 1;
+ }
+ else if (strcmp(argv[1], "fat") == 0)
+ {
+ return vtoyfat_main(argc - 1, argv + 1);
+ }
+ else if (strcmp(argv[1], "gpt") == 0)
+ {
+ return vtoygpt_main(argc - 1, argv + 1);
+ }
+ else if (strcmp(argv[1], "partresize") == 0)
+ {
+ return partresize_main(argc - 1, argv + 1);
+ }
+ else
+ {
+ return 1;
+ }
+}
+
--- /dev/null
+/******************************************************************************
+ * vtoycli.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VTOYCLI_H__
+#define __VTOYCLI_H__
+
+#define VENTOY_EFI_PART_ATTR 0xC000000000000001ULL
+
+#define SIZE_1MB (1024 * 1024)
+#define VENTOY_EFI_PART_SIZE (32 * SIZE_1MB)
+
+#define check_free(p) if (p) free(p)
+#define check_close(fd) if (fd >= 0) close(fd)
+
+#define VOID void
+#define CHAR char
+#define UINT64 unsigned long long
+#define UINT32 unsigned int
+#define UINT16 unsigned short
+#define CHAR16 unsigned short
+#define UINT8 unsigned char
+
+UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length);
+
+#define COMPILE_ASSERT(expr) extern char __compile_assert[(expr) ? 1 : -1]
+
+#pragma pack(1)
+
+typedef struct PART_TABLE
+{
+ UINT8 Active;
+
+ UINT8 StartHead;
+ UINT16 StartSector : 6;
+ UINT16 StartCylinder : 10;
+
+ UINT8 FsFlag;
+
+ UINT8 EndHead;
+ UINT16 EndSector : 6;
+ UINT16 EndCylinder : 10;
+
+ UINT32 StartSectorId;
+ UINT32 SectorCount;
+}PART_TABLE;
+
+typedef struct MBR_HEAD
+{
+ UINT8 BootCode[446];
+ PART_TABLE PartTbl[4];
+ UINT8 Byte55;
+ UINT8 ByteAA;
+}MBR_HEAD;
+
+typedef struct GUID
+{
+ UINT32 data1;
+ UINT16 data2;
+ UINT16 data3;
+ UINT8 data4[8];
+}GUID;
+
+typedef struct VTOY_GPT_HDR
+{
+ CHAR Signature[8]; /* EFI PART */
+ UINT8 Version[4];
+ UINT32 Length;
+ UINT32 Crc;
+ UINT8 Reserved1[4];
+ UINT64 EfiStartLBA;
+ UINT64 EfiBackupLBA;
+ UINT64 PartAreaStartLBA;
+ UINT64 PartAreaEndLBA;
+ GUID DiskGuid;
+ UINT64 PartTblStartLBA;
+ UINT32 PartTblTotNum;
+ UINT32 PartTblEntryLen;
+ UINT32 PartTblCrc;
+ UINT8 Reserved2[420];
+}VTOY_GPT_HDR;
+
+COMPILE_ASSERT(sizeof(VTOY_GPT_HDR) == 512);
+
+typedef struct VTOY_GPT_PART_TBL
+{
+ GUID PartType;
+ GUID PartGuid;
+ UINT64 StartLBA;
+ UINT64 LastLBA;
+ UINT64 Attr;
+ CHAR16 Name[36];
+}VTOY_GPT_PART_TBL;
+COMPILE_ASSERT(sizeof(VTOY_GPT_PART_TBL) == 128);
+
+typedef struct VTOY_GPT_INFO
+{
+ MBR_HEAD MBR;
+ VTOY_GPT_HDR Head;
+ VTOY_GPT_PART_TBL PartTbl[128];
+}VTOY_GPT_INFO;
+
+typedef struct VTOY_BK_GPT_INFO
+{
+ VTOY_GPT_PART_TBL PartTbl[128];
+ VTOY_GPT_HDR Head;
+}VTOY_BK_GPT_INFO;
+
+COMPILE_ASSERT(sizeof(VTOY_GPT_INFO) == 512 * 34);
+COMPILE_ASSERT(sizeof(VTOY_BK_GPT_INFO) == 512 * 33);
+
+#pragma pack()
+
+UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length);
+int vtoygpt_main(int argc, char **argv);
+int vtoyfat_main(int argc, char **argv);
+int partresize_main(int argc, char **argv);
+void ventoy_gen_preudo_uuid(void *uuid);
+UINT64 get_disk_size_in_byte(const char *disk);
+
+#endif /* __VTOYCLI_H__ */
+
--- /dev/null
+/******************************************************************************
+ * vtoyfat.c ---- Parse fat file system
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <fat_filelib.h>
+
+static int g_disk_fd = 0;
+
+static int vtoy_disk_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+ lseek(g_disk_fd, sector * 512, SEEK_SET);
+ read(g_disk_fd, buffer, sector_count * 512);
+
+ return 1;
+}
+
+static int check_secure_boot(void)
+{
+ void *flfile = NULL;
+
+ flfile = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+ if (flfile)
+ {
+ fl_fclose(flfile);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int get_ventoy_version(void)
+{
+ int rc = 1;
+ int size = 0;
+ char *buf = NULL;
+ char *pos = NULL;
+ char *end = NULL;
+ void *flfile = NULL;
+
+ flfile = fl_fopen("/grub/grub.cfg", "rb");
+ if (flfile)
+ {
+ fl_fseek(flfile, 0, SEEK_END);
+ size = (int)fl_ftell(flfile);
+
+ fl_fseek(flfile, 0, SEEK_SET);
+
+ buf = malloc(size + 1);
+ if (buf)
+ {
+ fl_fread(buf, 1, size, flfile);
+ buf[size] = 0;
+
+ pos = strstr(buf, "VENTOY_VERSION=");
+ if (pos)
+ {
+ pos += strlen("VENTOY_VERSION=");
+ if (*pos == '"')
+ {
+ pos++;
+ }
+
+ end = pos;
+ while (*end != 0 && *end != '"' && *end != '\r' && *end != '\n')
+ {
+ end++;
+ }
+
+ *end = 0;
+
+ printf("%s\n", pos);
+ rc = 0;
+ }
+ free(buf);
+ }
+
+ fl_fclose(flfile);
+ }
+
+ return rc;
+}
+
+int vtoyfat_main(int argc, char **argv)
+{
+ int op = 0;
+ int rc = 1;
+ char *disk;
+
+ if (argc != 2 && argc != 3)
+ {
+ printf("Usage: vtoyfat /dev/sdbs \n");
+ printf("Usage: vtoyfat -s /dev/sdbs \n");
+ return 1;
+ }
+
+ if (argv[1][0] == '-' && argv[1][1] == 'T')
+ {
+ return 0;
+ }
+
+ disk = argv[1];
+ if (argv[1][0] == '-' && argv[1][1] == 's')
+ {
+ op = 1;
+ disk = argv[2];
+ }
+
+ g_disk_fd = open(disk, O_RDONLY);
+ if (g_disk_fd < 0)
+ {
+ printf("Failed to open %s\n", disk);
+ return 1;
+ }
+
+ fl_init();
+
+ if (0 == fl_attach_media(vtoy_disk_read, NULL))
+ {
+ if (op == 0)
+ {
+ rc = get_ventoy_version();
+ }
+ else
+ {
+ rc = check_secure_boot();
+ }
+ }
+
+ fl_shutdown();
+
+ close(g_disk_fd);
+
+ return rc;
+}
+
--- /dev/null
+/******************************************************************************
+ * vtoygpt.c ---- ventoy gpt util
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include "vtoycli.h"
+
+void DumpGuid(const char *prefix, GUID *guid)
+{
+ printf("%s: %08x-%04x-%04x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+ prefix,
+ guid->data1, guid->data2, guid->data3,
+ guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3],
+ guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]
+ );
+}
+
+void DumpHead(VTOY_GPT_HDR *pHead)
+{
+ UINT32 CrcRead;
+ UINT32 CrcCalc;
+
+ printf("Signature:<%s>\n", pHead->Signature);
+ printf("Version:<%02x %02x %02x %02x>\n", pHead->Version[0], pHead->Version[1], pHead->Version[2], pHead->Version[3]);
+ printf("Length:%u\n", pHead->Length);
+ printf("Crc:0x%08x\n", pHead->Crc);
+ printf("EfiStartLBA:%lu\n", pHead->EfiStartLBA);
+ printf("EfiBackupLBA:%lu\n", pHead->EfiBackupLBA);
+ printf("PartAreaStartLBA:%lu\n", pHead->PartAreaStartLBA);
+ printf("PartAreaEndLBA:%lu\n", pHead->PartAreaEndLBA);
+ DumpGuid("DiskGuid", &pHead->DiskGuid);
+
+ printf("PartTblStartLBA:%lu\n", pHead->PartTblStartLBA);
+ printf("PartTblTotNum:%u\n", pHead->PartTblTotNum);
+ printf("PartTblEntryLen:%u\n", pHead->PartTblEntryLen);
+ printf("PartTblCrc:0x%08x\n", pHead->PartTblCrc);
+
+ CrcRead = pHead->Crc;
+ pHead->Crc = 0;
+ CrcCalc = VtoyCrc32(pHead, pHead->Length);
+
+ if (CrcCalc != CrcRead)
+ {
+ printf("Head CRC Check Failed\n");
+ }
+ else
+ {
+ printf("Head CRC Check SUCCESS [%x] [%x]\n", CrcCalc, CrcRead);
+ }
+
+ CrcRead = pHead->PartTblCrc;
+ CrcCalc = VtoyCrc32(pHead + 1, pHead->PartTblEntryLen * pHead->PartTblTotNum);
+ if (CrcCalc != CrcRead)
+ {
+ printf("Part Table CRC Check Failed\n");
+ }
+ else
+ {
+ printf("Part Table CRC Check SUCCESS [%x] [%x]\n", CrcCalc, CrcRead);
+ }
+}
+
+void DumpPartTable(VTOY_GPT_PART_TBL *Tbl)
+{
+ int i;
+
+ DumpGuid("PartType", &Tbl->PartType);
+ DumpGuid("PartGuid", &Tbl->PartGuid);
+ printf("StartLBA:%lu\n", Tbl->StartLBA);
+ printf("LastLBA:%lu\n", Tbl->LastLBA);
+ printf("Attr:0x%lx\n", Tbl->Attr);
+ printf("Name:");
+
+ for (i = 0; i < 36 && Tbl->Name[i]; i++)
+ {
+ printf("%c", (CHAR)(Tbl->Name[i]));
+ }
+ printf("\n");
+}
+
+void DumpMBR(MBR_HEAD *pMBR)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ printf("=========== Partition Table %d ============\n", i + 1);
+ printf("PartTbl.Active = 0x%x\n", pMBR->PartTbl[i].Active);
+ printf("PartTbl.FsFlag = 0x%x\n", pMBR->PartTbl[i].FsFlag);
+ printf("PartTbl.StartSectorId = %u\n", pMBR->PartTbl[i].StartSectorId);
+ printf("PartTbl.SectorCount = %u\n", pMBR->PartTbl[i].SectorCount);
+ printf("PartTbl.StartHead = %u\n", pMBR->PartTbl[i].StartHead);
+ printf("PartTbl.StartSector = %u\n", pMBR->PartTbl[i].StartSector);
+ printf("PartTbl.StartCylinder = %u\n", pMBR->PartTbl[i].StartCylinder);
+ printf("PartTbl.EndHead = %u\n", pMBR->PartTbl[i].EndHead);
+ printf("PartTbl.EndSector = %u\n", pMBR->PartTbl[i].EndSector);
+ printf("PartTbl.EndCylinder = %u\n", pMBR->PartTbl[i].EndCylinder);
+ }
+}
+
+int DumpGptInfo(VTOY_GPT_INFO *pGptInfo)
+{
+ int i;
+
+ DumpMBR(&pGptInfo->MBR);
+ DumpHead(&pGptInfo->Head);
+
+ for (i = 0; i < 128; i++)
+ {
+ if (pGptInfo->PartTbl[i].StartLBA == 0)
+ {
+ break;
+ }
+
+ printf("=====Part %d=====\n", i);
+ DumpPartTable(pGptInfo->PartTbl + i);
+ }
+
+ return 0;
+}
+
+int vtoygpt_main(int argc, char **argv)
+{
+ int i;
+ int fd;
+ UINT64 DiskSize;
+ CHAR16 *Name = NULL;
+ VTOY_GPT_INFO *pMainGptInfo = NULL;
+ VTOY_BK_GPT_INFO *pBackGptInfo = NULL;
+
+ if (argc != 3)
+ {
+ printf("usage: vtoygpt -f /dev/sdb\n");
+ return 1;
+ }
+
+ fd = open(argv[2], O_RDWR);
+ if (fd < 0)
+ {
+ printf("Failed to open %s\n", argv[2]);
+ return 1;
+ }
+
+ pMainGptInfo = malloc(sizeof(VTOY_GPT_INFO));
+ pBackGptInfo = malloc(sizeof(VTOY_BK_GPT_INFO));
+ if (NULL == pMainGptInfo || NULL == pBackGptInfo)
+ {
+ close(fd);
+ return 1;
+ }
+
+ read(fd, pMainGptInfo, sizeof(VTOY_GPT_INFO));
+
+ if (argv[1][0] == '-' && argv[1][1] == 'd')
+ {
+ DumpGptInfo(pMainGptInfo);
+ }
+ else
+ {
+ DiskSize = lseek(fd, 0, SEEK_END);
+ lseek(fd, DiskSize - 33 * 512, SEEK_SET);
+ read(fd, pBackGptInfo, sizeof(VTOY_BK_GPT_INFO));
+
+ Name = pMainGptInfo->PartTbl[1].Name;
+ if (Name[0] == 'V' && Name[1] == 'T' && Name[2] == 'O' && Name[3] == 'Y')
+ {
+ pMainGptInfo->PartTbl[1].Attr = VENTOY_EFI_PART_ATTR;
+ pMainGptInfo->Head.PartTblCrc = VtoyCrc32(pMainGptInfo->PartTbl, sizeof(pMainGptInfo->PartTbl));
+ pMainGptInfo->Head.Crc = 0;
+ pMainGptInfo->Head.Crc = VtoyCrc32(&pMainGptInfo->Head, pMainGptInfo->Head.Length);
+
+ pBackGptInfo->PartTbl[1].Attr = VENTOY_EFI_PART_ATTR;
+ pBackGptInfo->Head.PartTblCrc = VtoyCrc32(pBackGptInfo->PartTbl, sizeof(pBackGptInfo->PartTbl));
+ pBackGptInfo->Head.Crc = 0;
+ pBackGptInfo->Head.Crc = VtoyCrc32(&pBackGptInfo->Head, pBackGptInfo->Head.Length);
+
+ lseek(fd, 512, SEEK_SET);
+ write(fd, (UINT8 *)pMainGptInfo + 512, sizeof(VTOY_GPT_INFO) - 512);
+
+ lseek(fd, DiskSize - 33 * 512, SEEK_SET);
+ write(fd, pBackGptInfo, sizeof(VTOY_BK_GPT_INFO));
+
+ fsync(fd);
+ }
+ }
+
+ free(pMainGptInfo);
+ free(pBackGptInfo);
+ close(fd);
+
+ return 0;
+}
+