Experimental Linux GUI based on web browser

pull/767/head
longpanda 3 years ago
parent 7279ba9bc8
commit 43e8ec5785

@ -17,6 +17,10 @@ Ventoy2Disk.sh CMD [ OPTION ] /dev/sdX
Please refer https://www.ventoy.net/en/doc_start.html for details.
========== VentoyWeb.sh ===============
sudo sh VentoyWeb.sh
Normally, it will popup a web browser window.
If not you can open your browser and visit http://127.0.0.1:24680

@ -0,0 +1,182 @@
#!/bin/sh
print_usage() {
echo 'Usage: VentoyWeb.sh [ OPTION ]'
echo ' OPTION: (optional)'
echo ' -H x.x.x.x http server IP address (default is 127.0.0.1)'
echo ' -p PORT http server PORT (default is 24680)'
echo " -n don't start web browser"
echo ' -h print this help'
echo ''
}
print_err() {
echo ""
echo "$*"
echo ""
}
check_option() {
app="$1"
$app --help 2>&1 | grep -q "$2"
}
get_user() {
name=$(logname)
if [ -n "$name" -a "$name" != "root" ]; then
echo $name; return
fi
name=${HOME#/home/}
if [ -n "$name" -a "$name" != "root" ]; then
echo $name; return
fi
}
chromium_proc() {
app="$1"
url="http://${HOST}:${PORT}/index.html"
if check_option "$app" '[-][-]app='; then
su $VUSER -c "$app --app=$url >> $LOGFILE 2>&1"
elif check_option "$app" '[-][-]new[-]window='; then
su $VUSER -c "$app --new-window $url >> $LOGFILE 2>&1"
else
su $VUSER -c "$app $url >> $LOGFILE 2>&1"
fi
}
uid=$(id -u)
if [ $uid -ne 0 ]; then
print_err "Please use sudo or run the script as root."
exit 1
fi
OLDDIR=$(pwd)
if uname -a | egrep -q 'aarch64|arm64'; then
TOOLDIR=aarch64
elif uname -a | egrep -q 'x86_64|amd64'; then
TOOLDIR=x86_64
else
TOOLDIR=i386
fi
if [ ! -f ./tool/$TOOLDIR/V2DServer ]; then
if [ -f ${0%VentoyWeb.sh}/tool/$TOOLDIR/V2DServer ]; then
cd ${0%VentoyWeb.sh}
fi
fi
PATH=./tool/$TOOLDIR:$PATH
if [ ! -f ./boot/boot.img ]; then
if [ -d ./grub ]; then
echo "Don't run VentoyWeb.sh here, please download the released install package, and run the script in it."
else
echo "Please run under the correct directory!"
fi
exit 1
fi
HOST="127.0.0.1"
PORT=24680
while [ -n "$1" ]; do
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
print_usage
exit 0
elif [ "$1" = "-n" ]; then
NOWEB=1
elif [ "$1" = "-H" ]; then
shift
if echo $1 | grep -q '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*'; then
HOST="$1"
else
print_err "Invalid host $1"
exit 1
fi
elif [ "$1" = "-p" ]; then
shift
if [ $1 -gt 0 -a $1 -le 65535 ]; then
PORT="$1"
else
print_err "Invalid port $1"
exit 1
fi
fi
shift
done
if ps -ef | grep "V2DServer.*$HOST.*$PORT" | grep -q -v grep; then
print_err "Another ventoy server is running now, please close it first."
exit 1
fi
VUSER=$(get_user)
LOGFILE=log.txt
#delete the log.txt if it's more than 8MB
if [ -f $LOGFILE ]; then
logsize=$(stat -c '%s' $LOGFILE)
if [ $logsize -gt 8388608 ]; then
rm -f $LOGFILE
su $VUSER -c "touch $LOGFILE"
fi
else
su $VUSER -c "touch $LOGFILE"
fi
if [ -f ./tool/$TOOLDIR/V2DServer.xz ]; then
xz -d ./tool/$TOOLDIR/V2DServer.xz
chmod +x ./tool/$TOOLDIR/V2DServer
fi
V2DServer "$HOST" "$PORT" &
vtVer=$(cat ventoy/version)
echo ""
echo "=================================================================="
echo " Ventoy Server $vtVer is running at http://${HOST}:${PORT} ..."
echo "=================================================================="
echo ""
echo "################ Press Ctrl + C to exit ######################"
echo ""
if [ "$NOWEB" = "1" ]; then
echo "Please open your web browser and visit http://${HOST}:${PORT}"
else
if which -a google-chrome-stable >> $LOGFILE 2>&1; then
chromium_proc google-chrome-stable
elif which -a google-chrome >> $LOGFILE 2>&1; then
chromium_proc google-chrome
elif which -a chrome >> $LOGFILE 2>&1; then
chromium_proc chrome
elif which -a browser >> $LOGFILE 2>&1; then
chromium_proc browser
elif which -a firefox >> $LOGFILE 2>&1; then
su $VUSER -c "firefox --no-remote \"http://${HOST}:${PORT}/index.html\""
else
echo "Please open your web browser and visit http://${HOST}:${PORT}"
fi
fi
if ps -ef | grep "V2DServer.*$HOST.*$PORT" | grep -q -v grep; then
echo ""
else
print_err "Ventoy Server Error! Please check log.txt."
fi
wait $!
if [ -n "$OLDDIR" ]; then
CURDIR=$(pwd)
if [ "$CURDIR" != "$OLDDIR" ]; then
cd "$OLDDIR"
fi
fi

@ -25,6 +25,11 @@ sh mkcpio.sh
sh mkloopex.sh
cd -
cd ../LinuxGUI
sh language.sh || exit 1
sh build.sh
cd -
LOOP=$(losetup -f)
@ -88,12 +93,17 @@ xz --check=crc32 $tmpdir/boot/core.img
cp $OPT ./tool $tmpdir/
rm -f $tmpdir/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
cp $OPT Ventoy2Disk.sh $tmpdir/
cp $OPT VentoyWeb.sh $tmpdir/
cp $OPT README $tmpdir/
cp $OPT plugin $tmpdir/
cp $OPT CreatePersistentImg.sh $tmpdir/
dos2unix -q $tmpdir/Ventoy2Disk.sh
dos2unix -q $tmpdir/VentoyWeb.sh
dos2unix -q $tmpdir/CreatePersistentImg.sh
cp $OPT ../LinuxGUI/WebUI $tmpdir/
sed 's/.*SCRIPT_DEL_THIS \(.*\)/\1/g' -i $tmpdir/WebUI/index.html
#32MB disk img
dd status=none if=$LOOP of=$tmpdir/ventoy/ventoy.disk.img bs=512 count=$VENTOY_SECTOR_NUM skip=$part2_start_sector
xz --check=crc32 $tmpdir/ventoy/ventoy.disk.img
@ -119,6 +129,7 @@ done
find $tmpdir/ -type d -exec chmod 755 "{}" +
find $tmpdir/ -type f -exec chmod 644 "{}" +
chmod +x $tmpdir/Ventoy2Disk.sh
chmod +x $tmpdir/VentoyWeb.sh
chmod +x $tmpdir/CreatePersistentImg.sh
tar -czvf ventoy-${curver}-linux.tar.gz $tmpdir
@ -130,6 +141,7 @@ cp $OPT Ventoy2Disk*.exe $tmpdir/
cp $OPT $LANG_DIR/languages.ini $tmpdir/ventoy/
rm -rf $tmpdir/tool
rm -f $tmpdir/*.sh
rm -rf $tmpdir/WebUI
rm -f $tmpdir/README

@ -0,0 +1,299 @@
/******************************************************************************
* crc32.c ---- ventoy crc32
*
* 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 <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
static uint32_t 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_t ventoy_crc32(void *Buffer, uint32_t Length)
{
uint32_t i;
uint8_t *Ptr = Buffer;
uint32_t Crc = 0xFFFFFFFF;
for (i = 0; i < Length; i++, Ptr++)
{
Crc = (Crc >> 8) ^ g_crc_table[(uint8_t) Crc ^ *Ptr];
}
return Crc ^ 0xffffffff;
}

@ -0,0 +1,192 @@
/******************************************************************************
* ventoy_define.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 __VENTOY_DEFINE_H__
#define __VENTOY_DEFINE_H__
#define MAX_DISK_NUM 256
#define SIZE_1MB 1048576
#define SIZE_1GB 1073741824
#define VTOYIMG_PART_START_BYTES (1024 * 1024)
#define VTOYIMG_PART_START_SECTOR 2048
#define VTOYEFI_PART_BYTES (32 * 1024 * 1024)
#define VTOYEFI_PART_SECTORS 65536
#define VTOY_LOG_FILE "log.txt"
#pragma pack(1)
typedef struct vtoy_guid
{
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
}vtoy_guid;
typedef struct PART_TABLE
{
uint8_t Active; // 0x00 0x80
uint8_t StartHead;
uint16_t StartSector : 6;
uint16_t StartCylinder : 10;
uint8_t FsFlag;
uint8_t EndHead;
uint16_t EndSector : 6;
uint16_t EndCylinder : 10;
uint32_t StartSectorId;
uint32_t SectorCount;
}PART_TABLE;
typedef struct MBR_HEAD
{
uint8_t BootCode[446];
PART_TABLE PartTbl[4];
uint8_t Byte55;
uint8_t ByteAA;
}MBR_HEAD;
typedef struct VTOY_GPT_HDR
{
char Signature[8]; /* EFI PART */
uint8_t Version[4];
uint32_t Length;
uint32_t Crc;
uint8_t Reserved1[4];
uint64_t EfiStartLBA;
uint64_t EfiBackupLBA;
uint64_t PartAreaStartLBA;
uint64_t PartAreaEndLBA;
vtoy_guid DiskGuid;
uint64_t PartTblStartLBA;
uint32_t PartTblTotNum;
uint32_t PartTblEntryLen;
uint32_t PartTblCrc;
uint8_t Reserved2[420];
}VTOY_GPT_HDR;
typedef struct VTOY_GPT_PART_TBL
{
vtoy_guid PartType;
vtoy_guid PartGuid;
uint64_t StartLBA;
uint64_t LastLBA;
uint64_t Attr;
uint16_t Name[36];
}VTOY_GPT_PART_TBL;
typedef struct VTOY_GPT_INFO
{
MBR_HEAD MBR;
VTOY_GPT_HDR Head;
VTOY_GPT_PART_TBL PartTbl[128];
}VTOY_GPT_INFO;
#pragma pack()
#define MBR_PART_STYLE 0
#define GPT_PART_STYLE 1
typedef struct disk_ventoy_data
{
int ventoy_valid;
char ventoy_ver[32]; // 1.0.33 ...
int secure_boot_flag;
uint64_t preserved_space;
uint64_t part2_start_sector;
int partition_style; // MBR_PART_STYLE/GPT_PART_STYLE
VTOY_GPT_INFO gptinfo;
uint8_t rsvdata[4096];
}disk_ventoy_data;
typedef struct ventoy_disk
{
char disk_name[32]; // sda
char disk_path[64]; // /dev/sda
char part1_name[32]; // sda1
char part1_path[64]; // /dev/sda1
char part2_name[32]; // sda2
char part2_path[64]; // /dev/sda2
char disk_model[256]; // Sandisk/Kingston ...
char human_readable_size[32];
int major;
int minor;
int type;
int partstyle;
uint64_t size_in_byte;
disk_ventoy_data vtoydata;
}ventoy_disk;
#pragma pack(1)
typedef struct ventoy_guid
{
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
}ventoy_guid;
#pragma pack()
#ifndef O_BINARY
#define O_BINARY 0
#endif
#define VLOG_LOG 1
#define VLOG_DEBUG 2
#define ulong unsigned long
#define _ll long long
#define _ull unsigned long long
#define strlcpy(dst, src) strncpy(dst, src, sizeof(dst) - 1)
#define scnprintf(dst, fmt, args...) snprintf(dst, sizeof(dst) - 1, fmt, ##args)
#define vlog(fmt, args...) ventoy_syslog(VLOG_LOG, fmt, ##args)
#define vdebug(fmt, args...) ventoy_syslog(VLOG_DEBUG, fmt, ##args)
void ventoy_syslog(int level, const char *Fmt, ...);
void ventoy_set_loglevel(int level);
uint32_t ventoy_crc32(void *Buffer, uint32_t Length);
static inline void * zalloc(size_t n)
{
void *p = malloc(n);
if (p) memset(p, 0, n);
return p;
}
#endif /* __VENTOY_DEFINE_H__ */

@ -0,0 +1,735 @@
/******************************************************************************
* ventoy_disk.c ---- ventoy disk
* 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 <stdint.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/fs.h>
#include <dirent.h>
#include <time.h>
#include <ventoy_define.h>
#include <ventoy_disk.h>
#include <ventoy_util.h>
#include <fat_filelib.h>
int g_disk_num = 0;
static int g_fatlib_media_fd = 0;
static uint64_t g_fatlib_media_offset = 0;
ventoy_disk *g_disk_list = NULL;
static const char *g_ventoy_dev_type_str[VTOY_DEVICE_END] =
{
"unknown", "scsi", "USB", "ide", "dac960",
"cpqarray", "file", "ataraid", "i2o",
"ubd", "dasd", "viodasd", "sx8", "dm",
"xvd", "sd/mmc", "virtblk", "aoe",
"md", "loopback", "nvme", "brd", "pmem"
};
static const char * ventoy_get_dev_type_name(ventoy_dev_type type)
{
return (type < VTOY_DEVICE_END) ? g_ventoy_dev_type_str[type] : "unknown";
}
static int ventoy_check_blk_major(int major, const char *type)
{
int flag = 0;
int valid = 0;
int devnum = 0;
int len = 0;
char line[64];
char *pos = NULL;
FILE *fp = NULL;
fp = fopen("/proc/devices", "r");
if (!fp)
{
return 0;
}
len = (int)strlen(type);
while (fgets(line, sizeof(line), fp))
{
if (flag)
{
pos = strchr(line, ' ');
if (pos)
{
devnum = (int)strtol(line, NULL, 10);
if (devnum == major)
{
if (strncmp(pos + 1, type, len) == 0)
{
valid = 1;
}
break;
}
}
}
else if (strncmp(line, "Block devices:", 14) == 0)
{
flag = 1;
}
}
fclose(fp);
return valid;
}
static int ventoy_get_disk_devnum(const char *name, int *major, int* minor)
{
int rc;
char *pos;
char devnum[16] = {0};
rc = ventoy_get_sys_file_line(devnum, sizeof(devnum), "/sys/block/%s/dev", name);
if (rc)
{
return 1;
}
pos = strstr(devnum, ":");
if (!pos)
{
return 1;
}
*major = (int)strtol(devnum, NULL, 10);
*minor = (int)strtol(pos + 1, NULL, 10);
return 0;
}
static ventoy_dev_type ventoy_get_dev_type(const char *name, int major, int minor)
{
int rc;
char syspath[128];
char dstpath[256];
memset(syspath, 0, sizeof(syspath));
memset(dstpath, 0, sizeof(dstpath));
scnprintf(syspath, "/sys/block/%s", name);
rc = readlink(syspath, dstpath, sizeof(dstpath) - 1);
if (rc > 0 && strstr(dstpath, "/usb"))
{
return VTOY_DEVICE_USB;
}
if (SCSI_BLK_MAJOR(major) && (minor % 0x10 == 0))
{
return VTOY_DEVICE_SCSI;
}
else if (IDE_BLK_MAJOR(major) && (minor % 0x40 == 0))
{
return VTOY_DEVICE_IDE;
}
else if (major == DAC960_MAJOR && (minor % 0x8 == 0))
{
return VTOY_DEVICE_DAC960;
}
else if (major == ATARAID_MAJOR && (minor % 0x10 == 0))
{
return VTOY_DEVICE_ATARAID;
}
else if (major == AOE_MAJOR && (minor % 0x10 == 0))
{
return VTOY_DEVICE_AOE;
}
else if (major == DASD_MAJOR && (minor % 0x4 == 0))
{
return VTOY_DEVICE_DASD;
}
else if (major == VIODASD_MAJOR && (minor % 0x8 == 0))
{
return VTOY_DEVICE_VIODASD;
}
else if (SX8_BLK_MAJOR(major) && (minor % 0x20 == 0))
{
return VTOY_DEVICE_SX8;
}
else if (I2O_BLK_MAJOR(major) && (minor % 0x10 == 0))
{
return VTOY_DEVICE_I2O;
}
else if (CPQARRAY_BLK_MAJOR(major) && (minor % 0x10 == 0))
{
return VTOY_DEVICE_CPQARRAY;
}
else if (UBD_MAJOR == major && (minor % 0x10 == 0))
{
return VTOY_DEVICE_UBD;
}
else if (XVD_MAJOR == major && (minor % 0x10 == 0))
{
return VTOY_DEVICE_XVD;
}
else if (SDMMC_MAJOR == major && (minor % 0x8 == 0))
{
return VTOY_DEVICE_SDMMC;
}
else if (ventoy_check_blk_major(major, "virtblk"))
{
return VTOY_DEVICE_VIRTBLK;
}
else if (major == LOOP_MAJOR)
{
return VTOY_DEVICE_LOOP;
}
else if (major == MD_MAJOR)
{
return VTOY_DEVICE_MD;
}
else if (major == RAM_MAJOR)
{
return VTOY_DEVICE_RAM;
}
else if (strstr(name, "nvme") && ventoy_check_blk_major(major, "blkext"))
{
return VTOY_DEVICE_NVME;
}
else if (strstr(name, "pmem") && ventoy_check_blk_major(major, "blkext"))
{
return VTOY_DEVICE_PMEM;
}
return VTOY_DEVICE_END;
}
static int ventoy_is_possible_blkdev(const char *name)
{
if (name[0] == '.')
{
return 0;
}
/* /dev/ramX */
if (name[0] == 'r' && name[1] == 'a' && name[2] == 'm')
{
return 0;
}
/* /dev/loopX */
if (name[0] == 'l' && name[1] == 'o' && name[2] == 'o' && name[3] == 'p')
{
return 0;
}
/* /dev/dm-X */
if (name[0] == 'd' && name[1] == 'm' && name[2] == '-' && isdigit(name[3]))
{
return 0;
}
/* /dev/srX */
if (name[0] == 's' && name[1] == 'r' && isdigit(name[2]))
{
return 0;
}
return 1;
}
uint64_t ventoy_get_disk_size_in_byte(const char *disk)
{
int fd;
int rc;
unsigned long long size = 0;
char diskpath[256] = {0};
char sizebuf[64] = {0};
// Try 1: get size from sysfs
snprintf(diskpath, sizeof(diskpath) - 1, "/sys/block/%s/size", disk);
if (access(diskpath, F_OK) >= 0)
{
vdebug("get disk size from sysfs for %s\n", disk);
fd = open(diskpath, O_RDONLY | O_BINARY);
if (fd >= 0)
{
read(fd, sizebuf, sizeof(sizebuf));
size = strtoull(sizebuf, NULL, 10);
close(fd);
return (uint64_t)(size * 512);
}
}
else
{
vdebug("%s not exist \n", diskpath);
}
// Try 2: get size from ioctl
snprintf(diskpath, sizeof(diskpath) - 1, "/dev/%s", disk);
fd = open(diskpath, O_RDONLY);
if (fd >= 0)
{
vdebug("get disk size from ioctl for %s\n", disk);
rc = ioctl(fd, BLKGETSIZE64, &size);
if (rc == -1)
{
size = 0;
vdebug("failed to ioctl %d\n", rc);
}
close(fd);
}
else
{
vdebug("failed to open %s %d\n", diskpath, errno);
}
vdebug("disk %s size %llu bytes\n", disk, size);
return size;
}
int ventoy_get_disk_vendor(const char *name, char *vendorbuf, int bufsize)
{
return ventoy_get_sys_file_line(vendorbuf, bufsize, "/sys/block/%s/device/vendor", name);
}
int ventoy_get_disk_model(const char *name, char *modelbuf, int bufsize)
{
return ventoy_get_sys_file_line(modelbuf, bufsize, "/sys/block/%s/device/model", name);
}
static int fatlib_media_sector_read(uint32 sector, uint8 *buffer, uint32 sector_count)
{
lseek(g_fatlib_media_fd, (sector + g_fatlib_media_offset) * 512ULL, SEEK_SET);
read(g_fatlib_media_fd, buffer, sector_count * 512);
return 1;
}
static int fatlib_is_secure_boot_enable(void)
{
void *flfile = NULL;
flfile = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
if (flfile)
{
fl_fclose(flfile);
return 1;
}
else
{
vlog("/EFI/BOOT/grubx64_real.efi not exist\n");
}
return 0;
}
static int fatlib_get_ventoy_version(char *verbuf, int bufsize)
{
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;
snprintf(verbuf, bufsize - 1, "%s", pos);
rc = 0;
}
free(buf);
}
fl_fclose(flfile);
}
else
{
vdebug("No grub.cfg found\n");
}
return rc;
}
int ventoy_get_vtoy_data(ventoy_disk *info, int *ppartstyle)
{
int i;
int fd;
int len;
int rc = 1;
int ret = 1;
int part_style;
uint64_t part1_start_sector;
uint64_t part1_sector_count;
uint64_t part2_start_sector;
uint64_t part2_sector_count;
uint64_t preserved_space;
char name[64] = {0};
disk_ventoy_data *vtoy = NULL;
VTOY_GPT_INFO *gpt = NULL;
vtoy = &(info->vtoydata);
gpt = &(vtoy->gptinfo);
memset(vtoy, 0, sizeof(disk_ventoy_data));
vdebug("ventoy_get_vtoy_data %s\n", info->disk_path);
if (info->size_in_byte < (2 * VTOYEFI_PART_BYTES))
{
vdebug("disk %s is too small %llu\n", info->disk_path, (_ull)info->size_in_byte);
return 1;
}
fd = open(info->disk_path, O_RDONLY | O_BINARY);
if (fd < 0)
{
vdebug("failed to open %s %d\n", info->disk_path, errno);
return 1;
}
len = (int)read(fd, &(vtoy->gptinfo), sizeof(VTOY_GPT_INFO));
if (len != sizeof(VTOY_GPT_INFO))
{
vdebug("failed to read %s %d\n", info->disk_path, errno);
goto end;
}
if (gpt->MBR.Byte55 != 0x55 || gpt->MBR.ByteAA != 0xAA)
{
vdebug("Invalid mbr magic 0x%x 0x%x\n", gpt->MBR.Byte55, gpt->MBR.ByteAA);
goto end;
}
if (gpt->MBR.PartTbl[0].FsFlag == 0xEE && strncmp(gpt->Head.Signature, "EFI PART", 8) == 0)
{
part_style = GPT_PART_STYLE;
if (ppartstyle)
{
*ppartstyle = part_style;
}
if (gpt->PartTbl[0].StartLBA == 0 || gpt->PartTbl[1].StartLBA == 0)
{
vdebug("NO ventoy efi part layout <%llu %llu>\n",
(_ull)gpt->PartTbl[0].StartLBA,
(_ull)gpt->PartTbl[1].StartLBA);
goto end;
}
for (i = 0; i < 36; i++)
{
name[i] = (char)(gpt->PartTbl[1].Name[i]);
}
if (strcmp(name, "VTOYEFI"))
{
vdebug("Invalid efi part2 name <%s>\n", name);
goto end;
}
part1_start_sector = gpt->PartTbl[0].StartLBA;
part1_sector_count = gpt->PartTbl[0].LastLBA - part1_start_sector + 1;
part2_start_sector = gpt->PartTbl[1].StartLBA;
part2_sector_count = gpt->PartTbl[1].LastLBA - part2_start_sector + 1;
preserved_space = info->size_in_byte - (part2_start_sector + part2_sector_count + 33) * 512;
}
else
{
part_style = MBR_PART_STYLE;
if (ppartstyle)
{
*ppartstyle = part_style;
}
part1_start_sector = gpt->MBR.PartTbl[0].StartSectorId;
part1_sector_count = gpt->MBR.PartTbl[0].SectorCount;
part2_start_sector = gpt->MBR.PartTbl[1].StartSectorId;
part2_sector_count = gpt->MBR.PartTbl[1].SectorCount;
preserved_space = info->size_in_byte - (part2_start_sector + part2_sector_count) * 512;
}
if (part1_start_sector != VTOYIMG_PART_START_SECTOR ||
part2_sector_count != VTOYEFI_PART_SECTORS ||
(part1_start_sector + part1_sector_count) != part2_start_sector)
{
vdebug("Not valid ventoy partition layout [%llu %llu] [%llu %llu]\n",
part1_start_sector, part1_sector_count, part2_start_sector, part2_sector_count);
goto end;
}
vdebug("now check secure boot ...\n");
g_fatlib_media_fd = fd;
g_fatlib_media_offset = part2_start_sector;
fl_init();
if (0 == fl_attach_media(fatlib_media_sector_read, NULL))
{
ret = fatlib_get_ventoy_version(vtoy->ventoy_ver, sizeof(vtoy->ventoy_ver));
if (ret == 0 && vtoy->ventoy_ver[0])
{
vtoy->secure_boot_flag = fatlib_is_secure_boot_enable();
vtoy->ventoy_valid = 1;
}
else
{
vdebug("fatlib_get_ventoy_version failed %d\n", ret);
}
}
else
{
vdebug("fl_attach_media failed\n");
}
fl_shutdown();
g_fatlib_media_fd = -1;
g_fatlib_media_offset = 0;
if (0 == vtoy->ventoy_valid)
{
goto end;
}
lseek(fd, 2040 * 512, SEEK_SET);
read(fd, vtoy->rsvdata, sizeof(vtoy->rsvdata));
vtoy->preserved_space = preserved_space;
vtoy->partition_style = part_style;
vtoy->part2_start_sector = part2_start_sector;
rc = 0;
end:
close(fd);
return rc;
}
int ventoy_get_disk_info(const char *name, ventoy_disk *info)
{
char vendor[64] = {0};
char model[128] = {0};
vdebug("get disk info %s\n", name);
strlcpy(info->disk_name, name);
scnprintf(info->disk_path, "/dev/%s", name);
if (strstr(name, "nvme") || strstr(name, "mmc") || strstr(name, "nbd"))
{
scnprintf(info->part1_name, "%sp1", name);
scnprintf(info->part1_path, "/dev/%sp1", name);
scnprintf(info->part2_name, "%sp2", name);
scnprintf(info->part2_path, "/dev/%sp2", name);
}
else
{
scnprintf(info->part1_name, "%s1", name);
scnprintf(info->part1_path, "/dev/%s1", name);
scnprintf(info->part2_name, "%s2", name);
scnprintf(info->part2_path, "/dev/%s2", name);
}
info->size_in_byte = ventoy_get_disk_size_in_byte(name);
ventoy_get_disk_devnum(name, &info->major, &info->minor);
info->type = ventoy_get_dev_type(name, info->major, info->minor);
ventoy_get_disk_vendor(name, vendor, sizeof(vendor));
ventoy_get_disk_model(name, model, sizeof(model));
scnprintf(info->human_readable_size, "%llu GB", (_ull)ventoy_get_human_readable_gb(info->size_in_byte));
scnprintf(info->disk_model, "%s %s (%s)", vendor, model, ventoy_get_dev_type_name(info->type));
ventoy_get_vtoy_data(info, &(info->partstyle));
vdebug("disk:<%s %d:%d> model:<%s> size:%llu (%s)\n",
info->disk_path, info->major, info->minor, info->disk_model, info->size_in_byte, info->human_readable_size);
if (info->vtoydata.ventoy_valid)
{
vdebug("%s Ventoy:<%s> %s secureboot:%d preserve:%llu\n", info->disk_path, info->vtoydata.ventoy_ver,
info->vtoydata.partition_style == MBR_PART_STYLE ? "MBR" : "GPT",
info->vtoydata.secure_boot_flag, (_ull)(info->vtoydata.preserved_space));
}
else
{
vdebug("%s NO Ventoy detected\n", info->disk_path);
}
return 0;
}
static int ventoy_disk_compare(const ventoy_disk *disk1, const ventoy_disk *disk2)
{
if (disk1->type == VTOY_DEVICE_USB && disk2->type == VTOY_DEVICE_USB)
{
return strcmp(disk1->disk_name, disk2->disk_name);
}
else if (disk1->type == VTOY_DEVICE_USB)
{
return -1;
}
else if (disk2->type == VTOY_DEVICE_USB)
{
return 1;
}
else
{
return strcmp(disk1->disk_name, disk2->disk_name);
}
}
static int ventoy_disk_sort(void)
{
int i, j;
ventoy_disk *tmp;
tmp = malloc(sizeof(ventoy_disk));
if (!tmp)
{
return 1;
}
for (i = 0; i < g_disk_num; i++)
for (j = i + 1; j < g_disk_num; j++)
{
if (ventoy_disk_compare(g_disk_list + i, g_disk_list + j) > 0)
{
memcpy(tmp, g_disk_list + i, sizeof(ventoy_disk));
memcpy(g_disk_list + i, g_disk_list + j, sizeof(ventoy_disk));
memcpy(g_disk_list + j, tmp, sizeof(ventoy_disk));
}
}
free(tmp);
return 0;
}
int ventoy_disk_enumerate_all(void)
{
int rc = 0;
DIR* dir = NULL;
struct dirent* p = NULL;
vdebug("ventoy_disk_enumerate_all\n");
dir = opendir("/sys/block");
if (!dir)
{
vlog("Failed to open /sys/block %d\n", errno);
return 1;
}
while (((p = readdir(dir)) != NULL) && (g_disk_num < MAX_DISK_NUM))
{
if (ventoy_is_possible_blkdev(p->d_name))
{
memset(g_disk_list + g_disk_num, 0, sizeof(ventoy_disk));
if (0 == ventoy_get_disk_info(p->d_name, g_disk_list + g_disk_num))
{
g_disk_num++;
}
}
}
closedir(dir);
ventoy_disk_sort();
return rc;
}
void ventoy_disk_dump(ventoy_disk *cur)
{
if (cur->vtoydata.ventoy_valid)
{
vdebug("%s [%s] %s\tVentoy: %s %s secureboot:%d preserve:%llu\n",
cur->disk_path, cur->human_readable_size, cur->disk_model,
cur->vtoydata.ventoy_ver, cur->vtoydata.partition_style == MBR_PART_STYLE ? "MBR" : "GPT",
cur->vtoydata.secure_boot_flag, (_ull)(cur->vtoydata.preserved_space));
}
else
{
vdebug("%s [%s] %s\tVentoy: NA\n", cur->disk_path, cur->human_readable_size, cur->disk_model);
}
}
void ventoy_disk_dump_all(void)
{
int i;
vdebug("============= DISK DUMP ============\n");
for (i = 0; i < g_disk_num; i++)
{
ventoy_disk_dump(g_disk_list + i);
}
}
int ventoy_disk_install(ventoy_disk *disk, void *efipartimg)
{
return 0;
}
int ventoy_disk_init(void)
{
g_disk_list = malloc(sizeof(ventoy_disk) * MAX_DISK_NUM);
ventoy_disk_enumerate_all();
ventoy_disk_dump_all();
return 0;
}
void ventoy_disk_exit(void)
{
check_free(g_disk_list);
g_disk_list = NULL;
g_disk_num = 0;
}

@ -0,0 +1,145 @@
/******************************************************************************
* ventoy_disk.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 __VENTOY_DISK_H__
#define __VENTOY_DISK_H__
typedef enum
{
VTOY_DEVICE_UNKNOWN = 0,
VTOY_DEVICE_SCSI,
VTOY_DEVICE_USB,
VTOY_DEVICE_IDE,
VTOY_DEVICE_DAC960,
VTOY_DEVICE_CPQARRAY,
VTOY_DEVICE_FILE,
VTOY_DEVICE_ATARAID,
VTOY_DEVICE_I2O,
VTOY_DEVICE_UBD,
VTOY_DEVICE_DASD,
VTOY_DEVICE_VIODASD,
VTOY_DEVICE_SX8,
VTOY_DEVICE_DM,
VTOY_DEVICE_XVD,
VTOY_DEVICE_SDMMC,
VTOY_DEVICE_VIRTBLK,
VTOY_DEVICE_AOE,
VTOY_DEVICE_MD,
VTOY_DEVICE_LOOP,
VTOY_DEVICE_NVME,
VTOY_DEVICE_RAM,
VTOY_DEVICE_PMEM,
VTOY_DEVICE_END
}ventoy_dev_type;
/* from <linux/major.h> */
#define IDE0_MAJOR 3
#define IDE1_MAJOR 22
#define IDE2_MAJOR 33
#define IDE3_MAJOR 34
#define IDE4_MAJOR 56
#define IDE5_MAJOR 57
#define SCSI_CDROM_MAJOR 11
#define SCSI_DISK0_MAJOR 8
#define SCSI_DISK1_MAJOR 65
#define SCSI_DISK2_MAJOR 66
#define SCSI_DISK3_MAJOR 67
#define SCSI_DISK4_MAJOR 68
#define SCSI_DISK5_MAJOR 69
#define SCSI_DISK6_MAJOR 70
#define SCSI_DISK7_MAJOR 71
#define SCSI_DISK8_MAJOR 128
#define SCSI_DISK9_MAJOR 129
#define SCSI_DISK10_MAJOR 130
#define SCSI_DISK11_MAJOR 131
#define SCSI_DISK12_MAJOR 132
#define SCSI_DISK13_MAJOR 133
#define SCSI_DISK14_MAJOR 134
#define SCSI_DISK15_MAJOR 135
#define COMPAQ_SMART2_MAJOR 72
#define COMPAQ_SMART2_MAJOR1 73
#define COMPAQ_SMART2_MAJOR2 74
#define COMPAQ_SMART2_MAJOR3 75
#define COMPAQ_SMART2_MAJOR4 76
#define COMPAQ_SMART2_MAJOR5 77
#define COMPAQ_SMART2_MAJOR6 78
#define COMPAQ_SMART2_MAJOR7 79
#define COMPAQ_SMART_MAJOR 104
#define COMPAQ_SMART_MAJOR1 105
#define COMPAQ_SMART_MAJOR2 106
#define COMPAQ_SMART_MAJOR3 107
#define COMPAQ_SMART_MAJOR4 108
#define COMPAQ_SMART_MAJOR5 109
#define COMPAQ_SMART_MAJOR6 110
#define COMPAQ_SMART_MAJOR7 111
#define DAC960_MAJOR 48
#define ATARAID_MAJOR 114
#define I2O_MAJOR1 80
#define I2O_MAJOR2 81
#define I2O_MAJOR3 82
#define I2O_MAJOR4 83
#define I2O_MAJOR5 84
#define I2O_MAJOR6 85
#define I2O_MAJOR7 86
#define I2O_MAJOR8 87
#define UBD_MAJOR 98
#define DASD_MAJOR 94
#define VIODASD_MAJOR 112
#define AOE_MAJOR 152
#define SX8_MAJOR1 160
#define SX8_MAJOR2 161
#define XVD_MAJOR 202
#define SDMMC_MAJOR 179
#define LOOP_MAJOR 7
#define MD_MAJOR 9
#define BLKEXT_MAJOR 259
#define RAM_MAJOR 1
#define SCSI_BLK_MAJOR(M) ( \
(M) == SCSI_DISK0_MAJOR \
|| (M) == SCSI_CDROM_MAJOR \
|| ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) \
|| ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
#define IDE_BLK_MAJOR(M) \
((M) == IDE0_MAJOR || \
(M) == IDE1_MAJOR || \
(M) == IDE2_MAJOR || \
(M) == IDE3_MAJOR || \
(M) == IDE4_MAJOR || \
(M) == IDE5_MAJOR)
#define SX8_BLK_MAJOR(M) ((M) >= SX8_MAJOR1 && (M) <= SX8_MAJOR2)
#define I2O_BLK_MAJOR(M) ((M) >= I2O_MAJOR1 && (M) <= I2O_MAJOR8)
#define CPQARRAY_BLK_MAJOR(M) \
(((M) >= COMPAQ_SMART2_MAJOR && (M) <= COMPAQ_SMART2_MAJOR7) || \
(COMPAQ_SMART_MAJOR <= (M) && (M) <= COMPAQ_SMART_MAJOR7))
#define VENTOY_FILE_STG1_IMG "boot/core.img.xz"
#define VENTOY_FILE_DISK_IMG "ventoy/ventoy.disk.img.xz"
extern int g_disk_num;
extern ventoy_disk *g_disk_list;
int ventoy_disk_enumerate_all(void);
int ventoy_disk_init(void);
void ventoy_disk_exit(void);
#endif /* __VENTOY_DISK_H__ */

@ -0,0 +1,716 @@
/******************************************************************************
* ventoy_json.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 <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <ventoy_define.h>
#include <ventoy_util.h>
#include <ventoy_json.h>
static void vtoy_json_free(VTOY_JSON *pstJsonHead)
{
VTOY_JSON *pstNext = NULL;
while (NULL != pstJsonHead)
{
pstNext = pstJsonHead->pstNext;
if ((pstJsonHead->enDataType < JSON_TYPE_BUTT) && (NULL != pstJsonHead->pstChild))
{
vtoy_json_free(pstJsonHead->pstChild);
}
free(pstJsonHead);
pstJsonHead = pstNext;
}
return;
}
static char *vtoy_json_skip(const char *pcData)
{
while ((NULL != pcData) && ('\0' != *pcData) && (*pcData <= 32))
{
pcData++;
}
return (char *)pcData;
}
VTOY_JSON *vtoy_json_find_item
(
VTOY_JSON *pstJson,
JSON_TYPE enDataType,
const char *szKey
)
{
while (NULL != pstJson)
{
if ((enDataType == pstJson->enDataType) &&
(0 == strcmp(szKey, pstJson->pcName)))
{
return pstJson;
}
pstJson = pstJson->pstNext;
}
return NULL;
}
static int vtoy_json_parse_number
(
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
unsigned long Value;
Value = strtoul(pcData, (char **)ppcEnd, 10);
if (*ppcEnd == pcData)
{
vdebug("Failed to parse json number %s.\n", pcData);
return JSON_FAILED;
}
pstJson->enDataType = JSON_TYPE_NUMBER;
pstJson->unData.lValue = Value;
return JSON_SUCCESS;
}
static int vtoy_json_parse_string
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
uint32_t uiLen = 0;
const char *pcPos = NULL;
const char *pcTmp = pcData + 1;
*ppcEnd = pcData;
if ('\"' != *pcData)
{
return JSON_FAILED;
}
pcPos = strchr(pcTmp, '\"');
if ((NULL == pcPos) || (pcPos < pcTmp))
{
vdebug("Invalid string %s.\n", pcData);
return JSON_FAILED;
}
*ppcEnd = pcPos + 1;
uiLen = (uint32_t)(unsigned long)(pcPos - pcTmp);
pstJson->enDataType = JSON_TYPE_STRING;
pstJson->unData.pcStrVal = pcNewStart + (pcTmp - pcRawStart);
pstJson->unData.pcStrVal[uiLen] = '\0';
return JSON_SUCCESS;
}
static int vtoy_json_parse_array
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
int Ret = JSON_SUCCESS;
VTOY_JSON *pstJsonChild = NULL;
VTOY_JSON *pstJsonItem = NULL;
const char *pcTmp = pcData + 1;
*ppcEnd = pcData;
pstJson->enDataType = JSON_TYPE_ARRAY;
if ('[' != *pcData)
{
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(pcTmp);
if (']' == *pcTmp)
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
if (JSON_SUCCESS != Ret)
{
vdebug("Failed to parse array child.\n");
return JSON_FAILED;
}
pstJsonChild = pstJson->pstChild;
pcTmp = vtoy_json_skip(*ppcEnd);
while ((NULL != pcTmp) && (',' == *pcTmp))
{
JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
pstJsonChild->pstNext = pstJsonItem;
pstJsonItem->pstPrev = pstJsonChild;
pstJsonChild = pstJsonItem;
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
vdebug("Failed to parse array child.\n");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
}
if ((NULL != pcTmp) && (']' == *pcTmp))
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
else
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
}
static int vtoy_json_parse_object
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
int Ret = JSON_SUCCESS;
VTOY_JSON *pstJsonChild = NULL;
VTOY_JSON *pstJsonItem = NULL;
const char *pcTmp = pcData + 1;
*ppcEnd = pcData;
pstJson->enDataType = JSON_TYPE_OBJECT;
if ('{' != *pcData)
{
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(pcTmp);
if ('}' == *pcTmp)
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
if (JSON_SUCCESS != Ret)
{
vdebug("Failed to parse array child.\n");
return JSON_FAILED;
}
pstJsonChild = pstJson->pstChild;
pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
pstJsonChild->unData.pcStrVal = NULL;
pcTmp = vtoy_json_skip(*ppcEnd);
if ((NULL == pcTmp) || (':' != *pcTmp))
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
vdebug("Failed to parse array child.\n");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
while ((NULL != pcTmp) && (',' == *pcTmp))
{
JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
pstJsonChild->pstNext = pstJsonItem;
pstJsonItem->pstPrev = pstJsonChild;
pstJsonChild = pstJsonItem;
Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
vdebug("Failed to parse array child.\n");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
pstJsonChild->unData.pcStrVal = NULL;
if ((NULL == pcTmp) || (':' != *pcTmp))
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
vdebug("Failed to parse array child.\n");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
}
if ((NULL != pcTmp) && ('}' == *pcTmp))
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
else
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
}
int vtoy_json_parse_value
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
pcData = vtoy_json_skip(pcData);
switch (*pcData)
{
case 'n':
{
if (0 == strncmp(pcData, "null", 4))
{
pstJson->enDataType = JSON_TYPE_NULL;
*ppcEnd = pcData + 4;
return JSON_SUCCESS;
}
break;
}
case 'f':
{
if (0 == strncmp(pcData, "false", 5))
{
pstJson->enDataType = JSON_TYPE_BOOL;
pstJson->unData.lValue = 0;
*ppcEnd = pcData + 5;
return JSON_SUCCESS;
}
break;
}
case 't':
{
if (0 == strncmp(pcData, "true", 4))
{
pstJson->enDataType = JSON_TYPE_BOOL;
pstJson->unData.lValue = 1;
*ppcEnd = pcData + 4;
return JSON_SUCCESS;
}
break;
}
case '\"':
{
return vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
}
case '[':
{
return vtoy_json_parse_array(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
}
case '{':
{
return vtoy_json_parse_object(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
}
case '-':
{
return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
}
default :
{
if (*pcData >= '0' && *pcData <= '9')
{
return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
}
}
}
*ppcEnd = pcData;
vdebug("Invalid json data %u.\n", (uint8_t)(*pcData));
return JSON_FAILED;
}
VTOY_JSON * vtoy_json_create(void)
{
VTOY_JSON *pstJson = NULL;
pstJson = (VTOY_JSON *)zalloc(sizeof(VTOY_JSON));
if (NULL == pstJson)
{
return NULL;
}
return pstJson;
}
int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData)
{
uint32_t uiMemSize = 0;
int Ret = JSON_SUCCESS;
char *pcNewBuf = NULL;
const char *pcEnd = NULL;
uiMemSize = strlen(szJsonData) + 1;
pcNewBuf = (char *)malloc(uiMemSize);
if (NULL == pcNewBuf)
{
vdebug("Failed to alloc new buf.\n");
return JSON_FAILED;
}
memcpy(pcNewBuf, szJsonData, uiMemSize);
pcNewBuf[uiMemSize - 1] = 0;
Ret = vtoy_json_parse_value(pcNewBuf, (char *)szJsonData, pstJson, szJsonData, &pcEnd);
if (JSON_SUCCESS != Ret)
{
vdebug("Failed to parse json data %s start=%p, end=%p:%s.\n",
szJsonData, szJsonData, pcEnd, pcEnd);
return JSON_FAILED;
}
return JSON_SUCCESS;
}
int vtoy_json_scan_parse
(
const VTOY_JSON *pstJson,
uint32_t uiParseNum,
VTOY_JSON_PARSE_S *pstJsonParse
)
{
uint32_t i = 0;
const VTOY_JSON *pstJsonCur = NULL;
VTOY_JSON_PARSE_S *pstCurParse = NULL;
for (pstJsonCur = pstJson; NULL != pstJsonCur; pstJsonCur = pstJsonCur->pstNext)
{
if ((JSON_TYPE_OBJECT == pstJsonCur->enDataType) ||
(JSON_TYPE_ARRAY == pstJsonCur->enDataType))
{
continue;
}
for (i = 0, pstCurParse = NULL; i < uiParseNum; i++)
{
if (0 == strcmp(pstJsonParse[i].pcKey, pstJsonCur->pcName))
{
pstCurParse = pstJsonParse + i;
break;
}
}
if (NULL == pstCurParse)
{
continue;
}
switch (pstJsonCur->enDataType)
{
case JSON_TYPE_NUMBER:
{
if (sizeof(uint32_t) == pstCurParse->uiBufSize)
{
*(uint32_t *)(pstCurParse->pDataBuf) = (uint32_t)pstJsonCur->unData.lValue;
}
else if (sizeof(uint16_t) == pstCurParse->uiBufSize)
{
*(uint16_t *)(pstCurParse->pDataBuf) = (uint16_t)pstJsonCur->unData.lValue;
}
else if (sizeof(uint8_t) == pstCurParse->uiBufSize)
{
*(uint8_t *)(pstCurParse->pDataBuf) = (uint8_t)pstJsonCur->unData.lValue;
}
else if ((pstCurParse->uiBufSize > sizeof(uint64_t)))
{
snprintf((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, "%llu",
(unsigned long long)(pstJsonCur->unData.lValue));
}
else
{
vdebug("Invalid number data buf size %u.\n", pstCurParse->uiBufSize);
}
break;
}
case JSON_TYPE_STRING:
{
strncpy((char *)pstCurParse->pDataBuf, pstJsonCur->unData.pcStrVal, pstCurParse->uiBufSize);
break;
}
case JSON_TYPE_BOOL:
{
*(uint8_t *)(pstCurParse->pDataBuf) = (pstJsonCur->unData.lValue) > 0 ? 1 : 0;
break;
}
default :
{
break;
}
}
}
return JSON_SUCCESS;
}
int vtoy_json_scan_array
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstArrayItem
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
*ppstArrayItem = pstJsonItem;
return JSON_SUCCESS;
}
int vtoy_json_scan_array_ex
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstArrayItem
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
*ppstArrayItem = pstJsonItem->pstChild;
return JSON_SUCCESS;
}
int vtoy_json_scan_object
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstObjectItem
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_OBJECT, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
*ppstObjectItem = pstJsonItem;
return JSON_SUCCESS;
}
int vtoy_json_get_int
(
VTOY_JSON *pstJson,
const char *szKey,
int *piValue
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
*piValue = (int)pstJsonItem->unData.lValue;
return JSON_SUCCESS;
}
int vtoy_json_get_uint
(
VTOY_JSON *pstJson,
const char *szKey,
uint32_t *puiValue
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
*puiValue = (uint32_t)pstJsonItem->unData.lValue;
return JSON_SUCCESS;
}
int vtoy_json_get_uint64
(
VTOY_JSON *pstJson,
const char *szKey,
uint64_t *pui64Value
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
*pui64Value = (uint64_t)pstJsonItem->unData.lValue;
return JSON_SUCCESS;
}
int vtoy_json_get_bool
(
VTOY_JSON *pstJson,
const char *szKey,
uint8_t *pbValue
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_BOOL, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
*pbValue = pstJsonItem->unData.lValue > 0 ? 1 : 0;
return JSON_SUCCESS;
}
int vtoy_json_get_string
(
VTOY_JSON *pstJson,
const char *szKey,
uint32_t uiBufLen,
char *pcBuf
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return JSON_NOT_FOUND;
}
strncpy(pcBuf, pstJsonItem->unData.pcStrVal, uiBufLen);
return JSON_SUCCESS;
}
const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey)
{
VTOY_JSON *pstJsonItem = NULL;
if ((NULL == pstJson) || (NULL == szKey))
{
return NULL;
}
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
if (NULL == pstJsonItem)
{
vdebug("Key %s is not found in json data.\n", szKey);
return NULL;
}
return pstJsonItem->unData.pcStrVal;
}
int vtoy_json_destroy(VTOY_JSON *pstJson)
{
if (NULL == pstJson)
{
return JSON_SUCCESS;
}
if (NULL != pstJson->pstChild)
{
vtoy_json_free(pstJson->pstChild);
}
if (NULL != pstJson->pstNext)
{
vtoy_json_free(pstJson->pstNext);
}
free(pstJson);
return JSON_SUCCESS;
}

@ -0,0 +1,267 @@
/******************************************************************************
* ventoy_json.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 __VENTOY_JSON_H__
#define __VENTOY_JSON_H__
#define JSON_NEW_ITEM(pstJson, ret) \
{ \
(pstJson) = (VTOY_JSON *)zalloc(sizeof(VTOY_JSON)); \
if (NULL == (pstJson)) \
{ \
vdebug("Failed to alloc memory for json."); \
return (ret); \
} \
}
#define ssprintf(curpos, buf, len, fmt, args...) \
curpos += snprintf(buf + curpos, len - curpos, fmt, ##args)
#define VTOY_JSON_IS_SKIPABLE(c) (((c) <= 32) ? 1 : 0)
#define VTOY_JSON_PRINT_PREFIX(uiDepth, args...) \
{ \
uint32_t _uiLoop = 0; \
for (_uiLoop = 0; _uiLoop < (uiDepth); _uiLoop++) \
{ \
ssprintf(uiCurPos, pcBuf, uiBufLen, " "); \
} \
ssprintf(uiCurPos, pcBuf, uiBufLen, ##args); \
}
#define VTOY_JSON_SUCCESS_RET "{ \"result\" : \"success\" }"
#define VTOY_JSON_FAILED_RET "{ \"result\" : \"failed\" }"
#define VTOY_JSON_INVALID_RET "{ \"result\" : \"invalidfmt\" }"
#define VTOY_JSON_TOKEN_ERR_RET "{ \"result\" : \"tokenerror\" }"
#define VTOY_JSON_EXIST_RET "{ \"result\" : \"exist\" }"
#define VTOY_JSON_TIMEOUT_RET "{ \"result\" : \"timeout\" }"
#define VTOY_JSON_BUSY_RET "{ \"result\" : \"busy\" }"
#define VTOY_JSON_INUSE_RET "{ \"result\" : \"inuse\" }"
#define VTOY_JSON_NOTFOUND_RET "{ \"result\" : \"notfound\" }"
#define VTOY_JSON_NOTRUNNING_RET "{ \"result\" : \"notrunning\" }"
#define VTOY_JSON_NOT_READY_RET "{ \"result\" : \"notready\" }"
#define VTOY_JSON_NOT_SUPPORT_RET "{ \"result\" : \"notsupport\" }"
#define VTOY_JSON_MBR_2TB_RET "{ \"result\" : \"mbr2tb\" }"
#define VTOY_JSON_INVALID_RSV_RET "{ \"result\" : \"reserve_invalid\" }"
#define VTOY_JSON_FILE_NOT_FOUND_RET "{ \"result\" : \"file_not_found\" }"
typedef enum tagJSON_TYPE
{
JSON_TYPE_NUMBER = 0,
JSON_TYPE_STRING,
JSON_TYPE_BOOL,
JSON_TYPE_ARRAY,
JSON_TYPE_OBJECT,
JSON_TYPE_NULL,
JSON_TYPE_BUTT
}JSON_TYPE;
typedef struct tagVTOY_JSON
{
struct tagVTOY_JSON *pstPrev;
struct tagVTOY_JSON *pstNext;
struct tagVTOY_JSON *pstChild;
JSON_TYPE enDataType;
union
{
char *pcStrVal;
int iNumVal;
uint64_t lValue;
}unData;
char *pcName;
}VTOY_JSON;
#define VTOY_JSON_FMT_BEGIN(uiCurPos, pcBuf, uiBufLen) \
{\
uint32_t __uiCurPos = (uiCurPos);\
uint32_t __uiBufLen = (uiBufLen);\
char *__pcBuf = (pcBuf);
#define VTOY_JSON_FMT_END(uiCurPos) \
(uiCurPos) = __uiCurPos;\
}
#define VTOY_JSON_FMT_OBJ_BEGIN() ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "{")
#define VTOY_JSON_FMT_OBJ_END() \
{\
if (',' == *(__pcBuf + (__uiCurPos - 1)))\
{\
__uiCurPos -= 1;\
}\
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
}
#define VTOY_JSON_FMT_OBJ_ENDEX() \
{\
if (',' == *(__pcBuf + (__uiCurPos - 1)))\
{\
__uiCurPos -= 1;\
}\
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "},");\
}
#define VTOY_JSON_FMT_KEY(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":", (Key))
#define VTOY_JSON_FMT_ITEM(Item) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\",", (Item))
#define VTOY_JSON_FMT_COMA() ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",");
#define VTOY_JSON_FMT_APPEND_BEGIN() \
{ \
if ('}' == *(__pcBuf + (__uiCurPos - 1)))\
{\
__uiCurPos -= 1;\
}\
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",");\
}
#define VTOY_JSON_FMT_APPEND_END() \
{ \
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
}
#define VTOY_JSON_FMT_ARY_BEGIN() ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "[")
#define VTOY_JSON_FMT_ARY_END() \
{\
if (',' == *(__pcBuf + (__uiCurPos - 1)))\
{\
__uiCurPos -= 1;\
}\
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "]");\
}
#define VTOY_JSON_FMT_ARY_ENDEX() \
{\
if (',' == *(__pcBuf + (__uiCurPos - 1)))\
{\
__uiCurPos -= 1;\
}\
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "],");\
}
#define VTOY_JSON_FMT_UINT64(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%llu,", Key, (_ull)Val)
#define VTOY_JSON_FMT_ULONG(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%lu,", Key, Val)
#define VTOY_JSON_FMT_LONG(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%ld,", Key, Val)
#define VTOY_JSON_FMT_UINT(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%u,", Key, Val)
#define VTOY_JSON_FMT_STRINT(Key, Val) \
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%u\",", Key, Val)
#define VTOY_JSON_FMT_STRINT64(Key, Val) \
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%llu\",", Key, Val)
#define VTOY_JSON_FMT_SINT(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%d,", Key, Val)
#define VTOY_JSON_FMT_DUBL(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%.1lf,", Key, Val)
#define VTOY_JSON_FMT_DUBL2(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":%10.02lf,", Key, Val)
#define VTOY_JSON_FMT_STRN(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":\"%s\",", Key, Val)
#define VTOY_JSON_FMT_NULL(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":null,", Key)
#define VTOY_JSON_FMT_TRUE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":true,", (Key))
#define VTOY_JSON_FMT_FALSE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":false,", (Key))
#define VTOY_JSON_FMT_BOOL(Key, Val) \
{\
if (0 == (Val))\
{\
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":false,", (Key));\
}\
else \
{\
ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":true,", (Key));\
}\
}
typedef struct tagVTOY_JSON_PARSE
{
char *pcKey;
void *pDataBuf;
uint32_t uiBufSize;
}VTOY_JSON_PARSE_S;
#define JSON_SUCCESS 0
#define JSON_FAILED 1
#define JSON_NOT_FOUND 2
int vtoy_json_parse_value
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
);
VTOY_JSON * vtoy_json_create(void);
int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData);
int vtoy_json_destroy(VTOY_JSON *pstJson);
VTOY_JSON *vtoy_json_find_item
(
VTOY_JSON *pstJson,
JSON_TYPE enDataType,
const char *szKey
);
int vtoy_json_scan_parse
(
const VTOY_JSON *pstJson,
uint32_t uiParseNum,
VTOY_JSON_PARSE_S *pstJsonParse
);
int vtoy_json_get_int
(
VTOY_JSON *pstJson,
const char *szKey,
int *piValue
);
int vtoy_json_get_uint
(
VTOY_JSON *pstJson,
const char *szKey,
uint32_t *puiValue
);
int vtoy_json_get_uint64
(
VTOY_JSON *pstJson,
const char *szKey,
uint64_t *pui64Value
);
int vtoy_json_get_bool
(
VTOY_JSON *pstJson,
const char *szKey,
uint8_t *pbValue
);
int vtoy_json_get_string
(
VTOY_JSON *pstJson,
const char *szKey,
uint32_t uiBufLen,
char *pcBuf
);
const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey);
#endif /* __VENTOY_JSON_H__ */

@ -0,0 +1,115 @@
/******************************************************************************
* ventoy_log.c ---- ventoy log
*
* 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 <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <ventoy_define.h>
static int g_ventoy_log_level = VLOG_DEBUG;
static pthread_mutex_t g_log_mutex;
int ventoy_log_init(void)
{
pthread_mutex_init(&g_log_mutex, NULL);
return 0;
}
void ventoy_log_exit(void)
{
pthread_mutex_destroy(&g_log_mutex);
}
void ventoy_set_loglevel(int level)
{
g_ventoy_log_level = level;
}
void ventoy_syslog_newline(int level, const char *Fmt, ...)
{
char log[512];
va_list arg;
time_t stamp;
struct tm ttm;
FILE *fp;
if (level > g_ventoy_log_level)
{
return;
}
time(&stamp);
localtime_r(&stamp, &ttm);
va_start(arg, Fmt);
vsnprintf(log, 512, Fmt, arg);
va_end(arg);
pthread_mutex_lock(&g_log_mutex);
fp = fopen(VTOY_LOG_FILE, "a+");
if (fp)
{
fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s\n",
ttm.tm_year, ttm.tm_mon, ttm.tm_mday,
ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
log);
fclose(fp);
}
pthread_mutex_unlock(&g_log_mutex);
}
void ventoy_syslog(int level, const char *Fmt, ...)
{
char log[512];
va_list arg;
time_t stamp;
struct tm ttm;
FILE *fp;
if (level > g_ventoy_log_level)
{
return;
}
time(&stamp);
localtime_r(&stamp, &ttm);
va_start(arg, Fmt);
vsnprintf(log, 512, Fmt, arg);
va_end(arg);
pthread_mutex_lock(&g_log_mutex);
fp = fopen(VTOY_LOG_FILE, "a+");
if (fp)
{
fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s",
ttm.tm_year, ttm.tm_mon, ttm.tm_mday,
ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
log);
fclose(fp);
}
pthread_mutex_unlock(&g_log_mutex);
}

@ -0,0 +1,167 @@
/******************************************************************************
* ventoy_md5.c ---- ventoy md5
*
* 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 <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
const static uint32_t k[64] =
{
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
const static uint32_t r[] =
{
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
};
#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
#define to_bytes(val, bytes) *((uint32_t *)(bytes)) = (val)
#define ROTATE_CALC() \
{\
temp = d; \
d = c; \
c = b; \
b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); \
a = temp; \
}
void ventoy_md5(const void *data, uint32_t len, uint8_t *md5)
{
uint32_t h0, h1, h2, h3;
uint32_t w[16];
uint32_t a, b, c, d, i, f, g, temp;
uint32_t offset, mod, delta;
uint8_t postbuf[128] = {0};
// Initialize variables - simple count in nibbles:
h0 = 0x67452301;
h1 = 0xefcdab89;
h2 = 0x98badcfe;
h3 = 0x10325476;
//Pre-processing:
//append "1" bit to message
//append "0" bits until message length in bits ≡ 448 (mod 512)
//append length mod (2^64) to message
mod = len % 64;
if (mod)
{
memcpy(postbuf, (const uint8_t *)data + len - mod, mod);
}
postbuf[mod] = 0x80;
if (mod < 56)
{
to_bytes(len * 8, postbuf + 56);
to_bytes(len >> 29, postbuf + 60);
delta = 64;
}
else
{
to_bytes(len * 8, postbuf + 120);
to_bytes(len >> 29, postbuf + 124);
delta = 128;
}
len -= mod;
for (offset = 0; offset < len + delta; offset += 64)
{
if (offset < len)
{
memcpy(w, (const uint8_t *)data + offset, 64);
}
else
{
memcpy(w, postbuf + offset - len, 64);
}
// Initialize hash value for this chunk:
a = h0;
b = h1;
c = h2;
d = h3;
// Main loop:
for (i = 0; i < 16; i++)
{
f = (b & c) | ((~b) & d);
g = i;
ROTATE_CALC();
}
for (i = 16; i < 32; i++)
{
f = (d & b) | ((~d) & c);
g = (5 * i + 1) % 16;
ROTATE_CALC();
}
for (i = 32; i < 48; i++)
{
f = b ^ c ^ d;
g = (3 * i + 5) % 16;
ROTATE_CALC();
}
for (i = 48; i < 64; i++)
{
f = c ^ (b | (~d));
g = (7 * i) % 16;
ROTATE_CALC();
}
// Add this chunk's hash to result so far:
h0 += a;
h1 += b;
h2 += c;
h3 += d;
}
//var char md5[16] := h0 append h1 append h2 append h3 //(Output is in little-endian)
to_bytes(h0, md5);
to_bytes(h1, md5 + 4);
to_bytes(h2, md5 + 8);
to_bytes(h3, md5 + 12);
}

@ -0,0 +1,541 @@
/******************************************************************************
* ventoy_util.c ---- ventoy 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 <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <linux/fs.h>
#include <dirent.h>
#include <time.h>
#include <ventoy_define.h>
#include <ventoy_util.h>
uint8_t g_mbr_template[512];
void ventoy_gen_preudo_uuid(void *uuid)
{
int i;
int fd;
fd = open("/dev/urandom", O_RDONLY | O_BINARY);
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_t ventoy_get_human_readable_gb(uint64_t SizeBytes)
{
int i;
int Pow2 = 1;
double Delta;
double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
if ((SizeBytes % SIZE_1GB) == 0)
{
return (uint64_t)(SizeBytes / SIZE_1GB);
}
for (i = 0; i < 12; i++)
{
if (Pow2 > GB)
{
Delta = (Pow2 - GB) / Pow2;
}
else
{
Delta = (GB - Pow2) / Pow2;
}
if (Delta < 0.05)
{
return Pow2;
}
Pow2 <<= 1;
}
return (uint64_t)GB;
}
int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...)
{
int len;
char c;
char path[256];
va_list arg;
va_start(arg, fmt);
vsnprintf(path, 256, fmt, arg);
va_end(arg);
if (access(path, F_OK) >= 0)
{
FILE *fp = fopen(path, "r");
memset(buffer, 0, buflen);
len = (int)fread(buffer, 1, buflen - 1, fp);
fclose(fp);
while (len > 0)
{
c = buffer[len - 1];
if (c == '\r' || c == '\n' || c == ' ' || c == '\t')
{
buffer[len - 1] = 0;
len--;
}
else
{
break;
}
}
return 0;
}
else
{
vdebug("%s not exist \n", path);
return 1;
}
}
int ventoy_is_disk_mounted(const char *devpath)
{
int len;
int mount = 0;
char line[512];
FILE *fp = NULL;
fp = fopen("/proc/mounts", "r");
if (!fp)
{
return 0;
}
len = (int)strlen(devpath);
while (fgets(line, sizeof(line), fp))
{
if (strncmp(line, devpath, len) == 0)
{
mount = 1;
vdebug("%s mounted <%s>\n", devpath, line);
goto end;
}
}
end:
fclose(fp);
return mount;
}
int ventoy_try_umount_disk(const char *devpath)
{
int rc;
int len;
char line[512];
char *pos1 = NULL;
char *pos2 = NULL;
FILE *fp = NULL;
fp = fopen("/proc/mounts", "r");
if (!fp)
{
return 0;
}
len = (int)strlen(devpath);
while (fgets(line, sizeof(line), fp))
{
if (strncmp(line, devpath, len) == 0)
{
pos1 = strchr(line, ' ');
if (pos1)
{
pos2 = strchr(pos1 + 1, ' ');
if (pos2)
{
*pos2 = 0;
}
rc = umount(pos1 + 1);
if (rc)
{
vdebug("umount %s %s [ failed ] error:%d\n", devpath, pos1 + 1, errno);
}
else
{
vdebug("umount %s %s [ success ]\n", devpath, pos1 + 1);
}
}
}
}
fclose(fp);
return 0;
}
int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen)
{
int FileSize;
FILE *fp = NULL;
void *Data = NULL;
fp = fopen(FileName, "rb");
if (fp == NULL)
{
vlog("Failed to open file %s", FileName);
return 1;
}
fseek(fp, 0, SEEK_END);
FileSize = (int)ftell(fp);
Data = malloc(FileSize + ExtLen);
if (!Data)
{
fclose(fp);
return 1;
}
fseek(fp, 0, SEEK_SET);
fread(Data, 1, FileSize, fp);
fclose(fp);
*Bufer = Data;
*BufLen = FileSize;
return 0;
}
const char * ventoy_get_local_version(void)
{
int rc;
int FileSize;
char *Pos = NULL;
char *Buf = NULL;
static char LocalVersion[64] = { 0 };
if (LocalVersion[0] == 0)
{
rc = ventoy_read_file_to_buf("ventoy/version", 1, (void **)&Buf, &FileSize);
if (rc)
{
return "";
}
Buf[FileSize] = 0;
for (Pos = Buf; *Pos; Pos++)
{
if (*Pos == '\r' || *Pos == '\n')
{
*Pos = 0;
break;
}
}
scnprintf(LocalVersion, "%s", Buf);
free(Buf);
}
return LocalVersion;
}
int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
{
memcpy(pMBR, g_mbr_template, 512);
return 0;
}
static int VentoyFillProtectMBR(uint64_t DiskSizeBytes, MBR_HEAD *pMBR)
{
ventoy_guid Guid;
uint32_t DiskSignature;
uint64_t DiskSectorCount;
VentoyGetLocalBootImg(pMBR);
ventoy_gen_preudo_uuid(&Guid);
memcpy(&DiskSignature, &Guid, sizeof(uint32_t));
vdebug("Disk signature: 0x%08x\n", DiskSignature);
memcpy(pMBR->BootCode + 0x1B8, &DiskSignature, 4);
DiskSectorCount = DiskSizeBytes / 512 - 1;
if (DiskSectorCount > 0xFFFFFFFF)
{
DiskSectorCount = 0xFFFFFFFF;
}
memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl));
pMBR->PartTbl[0].Active = 0x00;
pMBR->PartTbl[0].FsFlag = 0xee; // EE
pMBR->PartTbl[0].StartHead = 0;
pMBR->PartTbl[0].StartSector = 1;
pMBR->PartTbl[0].StartCylinder = 0;
pMBR->PartTbl[0].EndHead = 254;
pMBR->PartTbl[0].EndSector = 63;
pMBR->PartTbl[0].EndCylinder = 1023;
pMBR->PartTbl[0].StartSectorId = 1;
pMBR->PartTbl[0].SectorCount = (uint32_t)DiskSectorCount;
pMBR->Byte55 = 0x55;
pMBR->ByteAA = 0xAA;
pMBR->BootCode[92] = 0x22;
return 0;
}
static int ventoy_fill_gpt_partname(uint16_t Name[36], const char *asciiName)
{
int i;
int len;
memset(Name, 0, 36 * sizeof(uint16_t));
len = (int)strlen(asciiName);
for (i = 0; i < 36 && i < len; i++)
{
Name[i] = asciiName[i];
}
return 0;
}
int ventoy_fill_gpt(uint64_t size, uint64_t reserve, int align4k, VTOY_GPT_INFO *gpt)
{
uint64_t ReservedSector = 33;
uint64_t Part1SectorCount = 0;
uint64_t DiskSectorCount = size / 512;
VTOY_GPT_HDR *Head = &gpt->Head;
VTOY_GPT_PART_TBL *Table = gpt->PartTbl;
ventoy_guid WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
//ventoy_guid EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
//ventoy_guid BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } };
VentoyFillProtectMBR(size, &gpt->MBR);
if (reserve > 0)
{
ReservedSector += reserve / 512;
}
// check aligned with 4KB
if (align4k)
{
if (DiskSectorCount % 8)
{
vdebug("Disk need to align with 4KB %u\n", (uint32_t)(DiskSectorCount % 8));
ReservedSector += (DiskSectorCount % 8);
}
}
Part1SectorCount = DiskSectorCount - ReservedSector - (VTOYEFI_PART_BYTES / 512) - 2048;
memcpy(Head->Signature, "EFI PART", 8);
Head->Version[2] = 0x01;
Head->Length = 92;
Head->Crc = 0;
Head->EfiStartLBA = 1;
Head->EfiBackupLBA = DiskSectorCount - 1;
Head->PartAreaStartLBA = 34;
Head->PartAreaEndLBA = DiskSectorCount - 34;
ventoy_gen_preudo_uuid(&Head->DiskGuid);
Head->PartTblStartLBA = 2;
Head->PartTblTotNum = 128;
Head->PartTblEntryLen = 128;
memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(ventoy_guid));
ventoy_gen_preudo_uuid(&(Table[0].PartGuid));
Table[0].StartLBA = 2048;
Table[0].LastLBA = 2048 + Part1SectorCount - 1;
Table[0].Attr = 0;
ventoy_fill_gpt_partname(Table[0].Name, "Ventoy");
// to fix windows issue
//memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID));
memcpy(&(Table[1].PartType), &WindowsDataPartType, sizeof(ventoy_guid));
ventoy_gen_preudo_uuid(&(Table[1].PartGuid));
Table[1].StartLBA = Table[0].LastLBA + 1;
Table[1].LastLBA = Table[1].StartLBA + VTOYEFI_PART_BYTES / 512 - 1;
Table[1].Attr = 0x8000000000000001ULL;
ventoy_fill_gpt_partname(Table[1].Name, "VTOYEFI");
#if 0
memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(ventoy_guid));
ventoy_gen_preudo_uuid(&(Table[2].PartGuid));
Table[2].StartLBA = 34;
Table[2].LastLBA = 2047;
Table[2].Attr = 0;
#endif
//Update CRC
Head->PartTblCrc = ventoy_crc32(Table, sizeof(gpt->PartTbl));
Head->Crc = ventoy_crc32(Head, Head->Length);
return 0;
}
int VentoyFillMBRLocation(uint64_t DiskSizeInBytes, uint32_t StartSectorId, uint32_t SectorCount, PART_TABLE *Table)
{
uint8_t Head;
uint8_t Sector;
uint8_t nSector = 63;
uint8_t nHead = 8;
uint32_t Cylinder;
uint32_t EndSectorId;
while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
{
nHead = (uint8_t)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;
}
int ventoy_fill_mbr(uint64_t size, uint64_t reserve, int align4k, int PartStyle, MBR_HEAD *pMBR)
{
ventoy_guid Guid;
uint32_t DiskSignature;
uint32_t DiskSectorCount;
uint32_t PartSectorCount;
uint32_t PartStartSector;
uint32_t ReservedSector;
VentoyGetLocalBootImg(pMBR);
ventoy_gen_preudo_uuid(&Guid);
memcpy(&DiskSignature, &Guid, sizeof(uint32_t));
vdebug("Disk signature: 0x%08x\n", DiskSignature);
memcpy(pMBR->BootCode + 0x1B8, &DiskSignature, 4);
if (size / 512 > 0xFFFFFFFF)
{
DiskSectorCount = 0xFFFFFFFF;
}
else
{
DiskSectorCount = (uint32_t)(size / 512);
}
if (reserve <= 0)
{
ReservedSector = 0;
}
else
{
ReservedSector = (uint32_t)(reserve / 512);
}
if (PartStyle)
{
ReservedSector += 33; // backup GPT part table
}
// check aligned with 4KB
if (align4k)
{
uint64_t sectors = size / 512;
if (sectors % 8)
{
vlog("Disk need to align with 4KB %u\n", (uint32_t)(sectors % 8));
ReservedSector += (uint32_t)(sectors % 8);
}
}
vlog("ReservedSector: %u\n", ReservedSector);
//Part1
PartStartSector = VTOYIMG_PART_START_SECTOR;
PartSectorCount = DiskSectorCount - ReservedSector - VTOYEFI_PART_BYTES / 512 - PartStartSector;
VentoyFillMBRLocation(size, PartStartSector, PartSectorCount, pMBR->PartTbl);
pMBR->PartTbl[0].Active = 0x80; // bootable
pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS
//Part2
PartStartSector += PartSectorCount;
PartSectorCount = VTOYEFI_PART_BYTES / 512;
VentoyFillMBRLocation(size, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
pMBR->PartTbl[1].Active = 0x00;
pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
pMBR->Byte55 = 0x55;
pMBR->ByteAA = 0xAA;
return 0;
}

@ -0,0 +1,52 @@
/******************************************************************************
* ventoy_util.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 __VENTOY_UTIL_H__
#define __VENTOY_UTIL_H__
#define check_free(p) if (p) free(p)
#define vtoy_safe_close_fd(fd) \
{\
if ((fd) >= 0) \
{ \
close(fd); \
(fd) = -1; \
}\
}
extern uint8_t g_mbr_template[512];
void ventoy_gen_preudo_uuid(void *uuid);
int ventoy_get_disk_part_name(const char *dev, int part, char *partbuf, int bufsize);
int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...);
uint64_t ventoy_get_human_readable_gb(uint64_t SizeBytes);
void ventoy_md5(const void *data, uint32_t len, uint8_t *md5);
int ventoy_is_disk_mounted(const char *devpath);
int ventoy_try_umount_disk(const char *devpath);
int unxz(unsigned char *in, int in_size,
int (*fill)(void *dest, unsigned int size),
int (*flush)(void *src, unsigned int size),
unsigned char *out, int *in_used,
void (*error)(char *x));
int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen);
const char * ventoy_get_local_version(void);
int ventoy_fill_gpt(uint64_t size, uint64_t reserve, int align4k, VTOY_GPT_INFO *gpt);
int ventoy_fill_mbr(uint64_t size, uint64_t reserve, int align4k, int PartStyle, MBR_HEAD *pMBR);
#endif /* __VENTOY_UTIL_H__ */

@ -0,0 +1,26 @@
/******************************************************************************
* Ventoy2Disk.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 __VENTOY2DISK_H__
#define __VENTOY2DISK_H__
#endif /* __VENTOY2DISK_H__ */

@ -0,0 +1,30 @@
#!/bin/sh
CUR="$PWD"
rm -rf src
mkdir -p src/libexfat
mkdir -p src/mkfs
rm -rf exfat-1.3.0
unzip exfat-1.3.0.zip
cd exfat-1.3.0
autoreconf --install
./configure --prefix="$CUR" CFLAGS='-O2 -D_FILE_OFFSET_BITS=64'
make
cp -a libexfat/*.c ../src/libexfat/
cp -a libexfat/*.h ../src/libexfat/
cp -a mkfs/*.c ../src/mkfs/
cp -a mkfs/*.h ../src/mkfs/
rm -f ../src/libexfat/log.c
cd ..
rm -rf exfat-1.3.0
mv src/mkfs/main.c src/mkfs/mkexfat_main.c
sed 's/<exfat.h>/"exfat.h"/g' -i src/mkfs/mkexfat_main.c
sed 's/<exfat.h>/"exfat.h"/g' -i src/mkfs/mkexfat.h
sed 's/int main/int mkexfat_main/g' -i src/mkfs/mkexfat_main.c

@ -0,0 +1,68 @@
/*
byteorder.h (12.01.10)
Endianness stuff. exFAT uses little-endian byte order.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef BYTEORDER_H_INCLUDED
#define BYTEORDER_H_INCLUDED
#include "platform.h"
#include <stdint.h>
#include <stddef.h>
typedef struct { uint16_t __u16; } le16_t;
typedef struct { uint32_t __u32; } le32_t;
typedef struct { uint64_t __u64; } le64_t;
#if EXFAT_BYTE_ORDER == EXFAT_LITTLE_ENDIAN
static inline uint16_t le16_to_cpu(le16_t v) { return v.__u16; }
static inline uint32_t le32_to_cpu(le32_t v) { return v.__u32; }
static inline uint64_t le64_to_cpu(le64_t v) { return v.__u64; }
static inline le16_t cpu_to_le16(uint16_t v) { le16_t t = {v}; return t; }
static inline le32_t cpu_to_le32(uint32_t v) { le32_t t = {v}; return t; }
static inline le64_t cpu_to_le64(uint64_t v) { le64_t t = {v}; return t; }
typedef size_t bitmap_t;
#elif EXFAT_BYTE_ORDER == EXFAT_BIG_ENDIAN
static inline uint16_t le16_to_cpu(le16_t v)
{ return exfat_bswap16(v.__u16); }
static inline uint32_t le32_to_cpu(le32_t v)
{ return exfat_bswap32(v.__u32); }
static inline uint64_t le64_to_cpu(le64_t v)
{ return exfat_bswap64(v.__u64); }
static inline le16_t cpu_to_le16(uint16_t v)
{ le16_t t = {exfat_bswap16(v)}; return t; }
static inline le32_t cpu_to_le32(uint32_t v)
{ le32_t t = {exfat_bswap32(v)}; return t; }
static inline le64_t cpu_to_le64(uint64_t v)
{ le64_t t = {exfat_bswap64(v)}; return t; }
typedef unsigned char bitmap_t;
#else
#error Wow! You have a PDP machine?!
#endif
#endif /* ifndef BYTEORDER_H_INCLUDED */

@ -0,0 +1,491 @@
/*
cluster.c (03.09.09)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
#include <errno.h>
#include <string.h>
#include <inttypes.h>
/*
* Sector to absolute offset.
*/
static off_t s2o(const struct exfat* ef, off_t sector)
{
return sector << ef->sb->sector_bits;
}
/*
* Cluster to sector.
*/
static off_t c2s(const struct exfat* ef, cluster_t cluster)
{
if (cluster < EXFAT_FIRST_DATA_CLUSTER)
exfat_bug("invalid cluster number %u", cluster);
return le32_to_cpu(ef->sb->cluster_sector_start) +
((off_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) << ef->sb->spc_bits);
}
/*
* Cluster to absolute offset.
*/
off_t exfat_c2o(const struct exfat* ef, cluster_t cluster)
{
return s2o(ef, c2s(ef, cluster));
}
/*
* Sector to cluster.
*/
static cluster_t s2c(const struct exfat* ef, off_t sector)
{
return ((sector - le32_to_cpu(ef->sb->cluster_sector_start)) >>
ef->sb->spc_bits) + EXFAT_FIRST_DATA_CLUSTER;
}
/*
* Size in bytes to size in clusters (rounded upwards).
*/
static uint32_t bytes2clusters(const struct exfat* ef, uint64_t bytes)
{
uint64_t cluster_size = CLUSTER_SIZE(*ef->sb);
return DIV_ROUND_UP(bytes, cluster_size);
}
cluster_t exfat_next_cluster(const struct exfat* ef,
const struct exfat_node* node, cluster_t cluster)
{
le32_t next;
off_t fat_offset;
if (cluster < EXFAT_FIRST_DATA_CLUSTER)
exfat_bug("bad cluster 0x%x", cluster);
if (node->is_contiguous)
return cluster + 1;
fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+ cluster * sizeof(cluster_t);
if (exfat_pread(ef->dev, &next, sizeof(next), fat_offset) < 0)
return EXFAT_CLUSTER_BAD; /* the caller should handle this and print
appropriate error message */
return le32_to_cpu(next);
}
cluster_t exfat_advance_cluster(const struct exfat* ef,
struct exfat_node* node, uint32_t count)
{
uint32_t i;
if (node->fptr_index > count)
{
node->fptr_index = 0;
node->fptr_cluster = node->start_cluster;
}
for (i = node->fptr_index; i < count; i++)
{
node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster);
if (CLUSTER_INVALID(*ef->sb, node->fptr_cluster))
break; /* the caller should handle this and print appropriate
error message */
}
node->fptr_index = count;
return node->fptr_cluster;
}
static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end)
{
const size_t start_index = start / sizeof(bitmap_t) / 8;
const size_t end_index = DIV_ROUND_UP(end, sizeof(bitmap_t) * 8);
size_t i;
size_t start_bitindex;
size_t end_bitindex;
size_t c;
for (i = start_index; i < end_index; i++)
{
if (bitmap[i] == ~((bitmap_t) 0))
continue;
start_bitindex = MAX(i * sizeof(bitmap_t) * 8, start);
end_bitindex = MIN((i + 1) * sizeof(bitmap_t) * 8, end);
for (c = start_bitindex; c < end_bitindex; c++)
if (BMAP_GET(bitmap, c) == 0)
{
BMAP_SET(bitmap, c);
return c + EXFAT_FIRST_DATA_CLUSTER;
}
}
return EXFAT_CLUSTER_END;
}
static int flush_nodes(struct exfat* ef, struct exfat_node* node)
{
struct exfat_node* p;
for (p = node->child; p != NULL; p = p->next)
{
int rc = flush_nodes(ef, p);
if (rc != 0)
return rc;
}
return exfat_flush_node(ef, node);
}
int exfat_flush_nodes(struct exfat* ef)
{
return flush_nodes(ef, ef->root);
}
int exfat_flush(struct exfat* ef)
{
if (ef->cmap.dirty)
{
if (exfat_pwrite(ef->dev, ef->cmap.chunk,
BMAP_SIZE(ef->cmap.chunk_size),
exfat_c2o(ef, ef->cmap.start_cluster)) < 0)
{
exfat_error("failed to write clusters bitmap");
return -EIO;
}
ef->cmap.dirty = false;
}
return 0;
}
static bool set_next_cluster(const struct exfat* ef, bool contiguous,
cluster_t current, cluster_t next)
{
off_t fat_offset;
le32_t next_le32;
if (contiguous)
return true;
fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+ current * sizeof(cluster_t);
next_le32 = cpu_to_le32(next);
if (exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset) < 0)
{
exfat_error("failed to write the next cluster %#x after %#x", next,
current);
return false;
}
return true;
}
static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint)
{
cluster_t cluster;
hint -= EXFAT_FIRST_DATA_CLUSTER;
if (hint >= ef->cmap.chunk_size)
hint = 0;
cluster = find_bit_and_set(ef->cmap.chunk, hint, ef->cmap.chunk_size);
if (cluster == EXFAT_CLUSTER_END)
cluster = find_bit_and_set(ef->cmap.chunk, 0, hint);
if (cluster == EXFAT_CLUSTER_END)
{
exfat_error("no free space left");
return EXFAT_CLUSTER_END;
}
ef->cmap.dirty = true;
return cluster;
}
static void free_cluster(struct exfat* ef, cluster_t cluster)
{
if (cluster - EXFAT_FIRST_DATA_CLUSTER >= ef->cmap.size)
exfat_bug("caller must check cluster validity (%#x, %#x)", cluster,
ef->cmap.size);
BMAP_CLR(ef->cmap.chunk, cluster - EXFAT_FIRST_DATA_CLUSTER);
ef->cmap.dirty = true;
}
static bool make_noncontiguous(const struct exfat* ef, cluster_t first,
cluster_t last)
{
cluster_t c;
for (c = first; c < last; c++)
if (!set_next_cluster(ef, false, c, c + 1))
return false;
return true;
}
static int shrink_file(struct exfat* ef, struct exfat_node* node,
uint32_t current, uint32_t difference);
static int grow_file(struct exfat* ef, struct exfat_node* node,
uint32_t current, uint32_t difference)
{
cluster_t previous;
cluster_t next;
uint32_t allocated = 0;
if (difference == 0)
exfat_bug("zero clusters count passed");
if (node->start_cluster != EXFAT_CLUSTER_FREE)
{
/* get the last cluster of the file */
previous = exfat_advance_cluster(ef, node, current - 1);
if (CLUSTER_INVALID(*ef->sb, previous))
{
exfat_error("invalid cluster 0x%x while growing", previous);
return -EIO;
}
}
else
{
if (node->fptr_index != 0)
exfat_bug("non-zero pointer index (%u)", node->fptr_index);
/* file does not have clusters (i.e. is empty), allocate
the first one for it */
previous = allocate_cluster(ef, 0);
if (CLUSTER_INVALID(*ef->sb, previous))
return -ENOSPC;
node->fptr_cluster = node->start_cluster = previous;
allocated = 1;
/* file consists of only one cluster, so it's contiguous */
node->is_contiguous = true;
}
while (allocated < difference)
{
next = allocate_cluster(ef, previous + 1);
if (CLUSTER_INVALID(*ef->sb, next))
{
if (allocated != 0)
shrink_file(ef, node, current + allocated, allocated);
return -ENOSPC;
}
if (next != previous - 1 && node->is_contiguous)
{
/* it's a pity, but we are not able to keep the file contiguous
anymore */
if (!make_noncontiguous(ef, node->start_cluster, previous))
return -EIO;
node->is_contiguous = false;
node->is_dirty = true;
}
if (!set_next_cluster(ef, node->is_contiguous, previous, next))
return -EIO;
previous = next;
allocated++;
}
if (!set_next_cluster(ef, node->is_contiguous, previous,
EXFAT_CLUSTER_END))
return -EIO;
return 0;
}
static int shrink_file(struct exfat* ef, struct exfat_node* node,
uint32_t current, uint32_t difference)
{
cluster_t previous;
cluster_t next;
if (difference == 0)
exfat_bug("zero difference passed");
if (node->start_cluster == EXFAT_CLUSTER_FREE)
exfat_bug("unable to shrink empty file (%u clusters)", current);
if (current < difference)
exfat_bug("file underflow (%u < %u)", current, difference);
/* crop the file */
if (current > difference)
{
cluster_t last = exfat_advance_cluster(ef, node,
current - difference - 1);
if (CLUSTER_INVALID(*ef->sb, last))
{
exfat_error("invalid cluster 0x%x while shrinking", last);
return -EIO;
}
previous = exfat_next_cluster(ef, node, last);
if (!set_next_cluster(ef, node->is_contiguous, last,
EXFAT_CLUSTER_END))
return -EIO;
}
else
{
previous = node->start_cluster;
node->start_cluster = EXFAT_CLUSTER_FREE;
node->is_dirty = true;
}
node->fptr_index = 0;
node->fptr_cluster = node->start_cluster;
/* free remaining clusters */
while (difference--)
{
if (CLUSTER_INVALID(*ef->sb, previous))
{
exfat_error("invalid cluster 0x%x while freeing after shrink",
previous);
return -EIO;
}
next = exfat_next_cluster(ef, node, previous);
if (!set_next_cluster(ef, node->is_contiguous, previous,
EXFAT_CLUSTER_FREE))
return -EIO;
free_cluster(ef, previous);
previous = next;
}
return 0;
}
static bool erase_raw(struct exfat* ef, size_t size, off_t offset)
{
if (exfat_pwrite(ef->dev, ef->zero_cluster, size, offset) < 0)
{
exfat_error("failed to erase %zu bytes at %"PRId64, size, offset);
return false;
}
return true;
}
static int erase_range(struct exfat* ef, struct exfat_node* node,
uint64_t begin, uint64_t end)
{
uint64_t cluster_boundary;
cluster_t cluster;
if (begin >= end)
return 0;
cluster_boundary = (begin | (CLUSTER_SIZE(*ef->sb) - 1)) + 1;
cluster = exfat_advance_cluster(ef, node,
begin / CLUSTER_SIZE(*ef->sb));
if (CLUSTER_INVALID(*ef->sb, cluster))
{
exfat_error("invalid cluster 0x%x while erasing", cluster);
return -EIO;
}
/* erase from the beginning to the closest cluster boundary */
if (!erase_raw(ef, MIN(cluster_boundary, end) - begin,
exfat_c2o(ef, cluster) + begin % CLUSTER_SIZE(*ef->sb)))
return -EIO;
/* erase whole clusters */
while (cluster_boundary < end)
{
cluster = exfat_next_cluster(ef, node, cluster);
/* the cluster cannot be invalid because we have just allocated it */
if (CLUSTER_INVALID(*ef->sb, cluster))
exfat_bug("invalid cluster 0x%x after allocation", cluster);
if (!erase_raw(ef, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, cluster)))
return -EIO;
cluster_boundary += CLUSTER_SIZE(*ef->sb);
}
return 0;
}
int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
bool erase)
{
uint32_t c1 = bytes2clusters(ef, node->size);
uint32_t c2 = bytes2clusters(ef, size);
int rc = 0;
if (node->references == 0 && node->parent)
exfat_bug("no references, node changes can be lost");
if (node->size == size)
return 0;
if (c1 < c2)
rc = grow_file(ef, node, c1, c2 - c1);
else if (c1 > c2)
rc = shrink_file(ef, node, c1, c1 - c2);
if (rc != 0)
return rc;
if (erase)
{
rc = erase_range(ef, node, node->size, size);
if (rc != 0)
return rc;
}
exfat_update_mtime(node);
node->size = size;
node->is_dirty = true;
return 0;
}
uint32_t exfat_count_free_clusters(const struct exfat* ef)
{
uint32_t free_clusters = 0;
uint32_t i;
for (i = 0; i < ef->cmap.size; i++)
if (BMAP_GET(ef->cmap.chunk, i) == 0)
free_clusters++;
return free_clusters;
}
static int find_used_clusters(const struct exfat* ef,
cluster_t* a, cluster_t* b)
{
const cluster_t end = le32_to_cpu(ef->sb->cluster_count);
/* find first used cluster */
for (*a = *b + 1; *a < end; (*a)++)
if (BMAP_GET(ef->cmap.chunk, *a - EXFAT_FIRST_DATA_CLUSTER))
break;
if (*a >= end)
return 1;
/* find last contiguous used cluster */
for (*b = *a; *b < end; (*b)++)
if (BMAP_GET(ef->cmap.chunk, *b - EXFAT_FIRST_DATA_CLUSTER) == 0)
{
(*b)--;
break;
}
return 0;
}
int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b)
{
cluster_t ca, cb;
if (*a == 0 && *b == 0)
ca = cb = EXFAT_FIRST_DATA_CLUSTER - 1;
else
{
ca = s2c(ef, *a);
cb = s2c(ef, *b);
}
if (find_used_clusters(ef, &ca, &cb) != 0)
return 1;
if (*a != 0 || *b != 0)
*a = c2s(ef, ca);
*b = c2s(ef, cb) + (CLUSTER_SIZE(*ef->sb) - 1) / SECTOR_SIZE(*ef->sb);
return 0;
}

@ -0,0 +1,66 @@
/*
compiler.h (09.06.13)
Compiler-specific definitions. Note that unknown compiler is not a
showstopper.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef COMPILER_H_INCLUDED
#define COMPILER_H_INCLUDED
#if __STDC_VERSION__ < 199901L
#error C99-compliant compiler is required
#endif
#if defined(__clang__)
#define PRINTF __attribute__((format(printf, 1, 2)))
#define NORETURN __attribute__((noreturn))
#define PACKED __attribute__((packed))
#if __has_extension(c_static_assert)
#define USE_C11_STATIC_ASSERT
#endif
#elif defined(__GNUC__)
#define PRINTF __attribute__((format(printf, 1, 2)))
#define NORETURN __attribute__((noreturn))
#define PACKED __attribute__((packed))
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#define USE_C11_STATIC_ASSERT
#endif
#else
#define PRINTF
#define NORETURN
#define PACKED
#endif
#ifdef USE_C11_STATIC_ASSERT
#define STATIC_ASSERT(cond) _Static_assert(cond, #cond)
#else
#define CONCAT2(a, b) a ## b
#define CONCAT1(a, b) CONCAT2(a, b)
#define STATIC_ASSERT(cond) \
extern void CONCAT1(static_assert, __LINE__)(int x[(cond) ? 1 : -1])
#endif
#endif /* ifndef COMPILER_H_INCLUDED */

@ -0,0 +1,40 @@
/* libexfat/config.h. Generated from config.h.in by configure. */
/* libexfat/config.h.in. Generated from configure.ac by autoheader. */
/* Name of package */
#define PACKAGE "exfat"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "relan@users.noreply.github.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "Free exFAT implementation"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "Free exFAT implementation 1.3.0"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "exfat"
/* Define to the home page for this package. */
#define PACKAGE_URL "https://github.com/relan/exfat"
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.3.0"
/* Define if block devices are not supported. */
/* #undef USE_UBLIO */
/* Version number of package */
#define VERSION "1.3.0"
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */

@ -0,0 +1,255 @@
/*
exfat.h (29.08.09)
Definitions of structures and constants used in exFAT file system
implementation.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef EXFAT_H_INCLUDED
#define EXFAT_H_INCLUDED
#ifndef ANDROID
/* Android.bp is used instead of autotools when targeting Android */
#include "config.h"
#endif
#include "compiler.h"
#include "exfatfs.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#define EXFAT_NAME_MAX 255
/* UTF-16 encodes code points up to U+FFFF as single 16-bit code units.
UTF-8 uses up to 3 bytes (i.e. 8-bit code units) to encode code points
up to U+FFFF. One additional character is for null terminator. */
#define EXFAT_UTF8_NAME_BUFFER_MAX (EXFAT_NAME_MAX * 3 + 1)
#define EXFAT_UTF8_ENAME_BUFFER_MAX (EXFAT_ENAME_MAX * 3 + 1)
#define SECTOR_SIZE(sb) (1 << (sb).sector_bits)
#define CLUSTER_SIZE(sb) (SECTOR_SIZE(sb) << (sb).spc_bits)
#define CLUSTER_INVALID(sb, c) ((c) < EXFAT_FIRST_DATA_CLUSTER || \
(c) - EXFAT_FIRST_DATA_CLUSTER >= le32_to_cpu((sb).cluster_count))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define DIV_ROUND_UP(x, d) (((x) + (d) - 1) / (d))
#define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
#define BMAP_SIZE(count) (ROUND_UP(count, sizeof(bitmap_t) * 8) / 8)
#define BMAP_BLOCK(index) ((index) / sizeof(bitmap_t) / 8)
#define BMAP_MASK(index) ((bitmap_t) 1 << ((index) % (sizeof(bitmap_t) * 8)))
#define BMAP_GET(bitmap, index) \
((bitmap)[BMAP_BLOCK(index)] & BMAP_MASK(index))
#define BMAP_SET(bitmap, index) \
((bitmap)[BMAP_BLOCK(index)] |= BMAP_MASK(index))
#define BMAP_CLR(bitmap, index) \
((bitmap)[BMAP_BLOCK(index)] &= ~BMAP_MASK(index))
#define EXFAT_REPAIR(hook, ef, ...) \
(exfat_ask_to_fix(ef) && exfat_fix_ ## hook(ef, __VA_ARGS__))
/* The size of off_t type must be 64 bits. File systems larger than 2 GB will
be corrupted with 32-bit off_t. */
STATIC_ASSERT(sizeof(off_t) == 8);
struct exfat_node
{
struct exfat_node* parent;
struct exfat_node* child;
struct exfat_node* next;
struct exfat_node* prev;
int references;
uint32_t fptr_index;
cluster_t fptr_cluster;
off_t entry_offset;
cluster_t start_cluster;
uint16_t attrib;
uint8_t continuations;
bool is_contiguous : 1;
bool is_cached : 1;
bool is_dirty : 1;
bool is_unlinked : 1;
uint64_t size;
time_t mtime, atime;
le16_t name[EXFAT_NAME_MAX + 1];
};
enum exfat_mode
{
EXFAT_MODE_RO,
EXFAT_MODE_RW,
EXFAT_MODE_ANY,
};
struct exfat_dev;
struct exfat
{
struct exfat_dev* dev;
struct exfat_super_block* sb;
uint16_t* upcase;
struct exfat_node* root;
struct
{
cluster_t start_cluster;
uint32_t size; /* in bits */
bitmap_t* chunk;
uint32_t chunk_size; /* in bits */
bool dirty;
}
cmap;
char label[EXFAT_UTF8_ENAME_BUFFER_MAX];
void* zero_cluster;
int dmask, fmask;
uid_t uid;
gid_t gid;
int ro;
bool noatime;
enum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair;
};
/* in-core nodes iterator */
struct exfat_iterator
{
struct exfat_node* parent;
struct exfat_node* current;
};
struct exfat_human_bytes
{
uint64_t value;
const char* unit;
};
extern int exfat_errors;
extern int exfat_errors_fixed;
#define VLOG_LOG 1
#define VLOG_DEBUG 2
void ventoy_syslog_newline(int level, const char *Fmt, ...);
#define exfat_bug(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
#define exfat_error(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
#define exfat_error(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
#define exfat_warn(fmt, args...) ventoy_syslog_newline(VLOG_LOG, fmt, ##args)
#define exfat_debug(fmt, args...) ventoy_syslog_newline(VLOG_DEBUG, fmt, ##args)
#if 0
void exfat_bug(const char* format, ...) PRINTF NORETURN;
void exfat_error(const char* format, ...) PRINTF;
void exfat_warn(const char* format, ...) PRINTF;
void exfat_debug(const char* format, ...) PRINTF;
#endif /* #if 0 */
struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode);
int exfat_close(struct exfat_dev* dev);
int exfat_fsync(struct exfat_dev* dev);
enum exfat_mode exfat_get_mode(const struct exfat_dev* dev);
off_t exfat_get_size(const struct exfat_dev* dev);
off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence);
ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size);
ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size);
ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
off_t offset);
ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
off_t offset);
ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
void* buffer, size_t size, off_t offset);
ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
const void* buffer, size_t size, off_t offset);
int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
struct exfat_iterator* it);
void exfat_closedir(struct exfat* ef, struct exfat_iterator* it);
struct exfat_node* exfat_readdir(struct exfat_iterator* it);
int exfat_lookup(struct exfat* ef, struct exfat_node** node,
const char* path);
int exfat_split(struct exfat* ef, struct exfat_node** parent,
struct exfat_node** node, le16_t* name, const char* path);
off_t exfat_c2o(const struct exfat* ef, cluster_t cluster);
cluster_t exfat_next_cluster(const struct exfat* ef,
const struct exfat_node* node, cluster_t cluster);
cluster_t exfat_advance_cluster(const struct exfat* ef,
struct exfat_node* node, uint32_t count);
int exfat_flush_nodes(struct exfat* ef);
int exfat_flush(struct exfat* ef);
int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
bool erase);
uint32_t exfat_count_free_clusters(const struct exfat* ef);
int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b);
void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
struct stat* stbuf);
void exfat_get_name(const struct exfat_node* node,
char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]);
uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry);
uint16_t exfat_add_checksum(const void* entry, uint16_t sum);
le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n);
uint32_t exfat_vbr_start_checksum(const void* sector, size_t size);
uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum);
le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name,
size_t length);
void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb);
void exfat_print_info(const struct exfat_super_block* sb,
uint32_t free_clusters);
int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
size_t insize);
int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
size_t insize);
size_t utf16_length(const le16_t* str);
struct exfat_node* exfat_get_node(struct exfat_node* node);
void exfat_put_node(struct exfat* ef, struct exfat_node* node);
int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node);
int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir);
void exfat_reset_cache(struct exfat* ef);
int exfat_flush_node(struct exfat* ef, struct exfat_node* node);
int exfat_unlink(struct exfat* ef, struct exfat_node* node);
int exfat_rmdir(struct exfat* ef, struct exfat_node* node);
int exfat_mknod(struct exfat* ef, const char* path);
int exfat_mkdir(struct exfat* ef, const char* path);
int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path);
void exfat_utimes(struct exfat_node* node, const struct timespec tv[2]);
void exfat_update_atime(struct exfat_node* node);
void exfat_update_mtime(struct exfat_node* node);
const char* exfat_get_label(struct exfat* ef);
int exfat_set_label(struct exfat* ef, const char* label);
int exfat_mount(struct exfat* ef, const char* spec, const char* options);
void exfat_unmount(struct exfat* ef);
time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec);
void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
uint8_t* centisec);
void exfat_tzset(void);
bool exfat_ask_to_fix(const struct exfat* ef);
bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector,
uint32_t vbr_checksum);
bool exfat_fix_invalid_node_checksum(const struct exfat* ef,
struct exfat_node* node);
bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir,
const struct exfat_entry* entry, off_t offset);
#endif /* ifndef EXFAT_H_INCLUDED */

@ -0,0 +1,180 @@
/*
exfatfs.h (29.08.09)
Definitions of structures and constants used in exFAT file system.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef EXFATFS_H_INCLUDED
#define EXFATFS_H_INCLUDED
#include "byteorder.h"
#include "compiler.h"
typedef uint32_t cluster_t; /* cluster number */
#define EXFAT_FIRST_DATA_CLUSTER 2
#define EXFAT_LAST_DATA_CLUSTER 0xfffffff6
#define EXFAT_CLUSTER_FREE 0 /* free cluster */
#define EXFAT_CLUSTER_BAD 0xfffffff7 /* cluster contains bad sector */
#define EXFAT_CLUSTER_END 0xffffffff /* final cluster of file or directory */
#define EXFAT_STATE_MOUNTED 2
struct exfat_super_block
{
uint8_t jump[3]; /* 0x00 jmp and nop instructions */
uint8_t oem_name[8]; /* 0x03 "EXFAT " */
uint8_t __unused1[53]; /* 0x0B always 0 */
le64_t sector_start; /* 0x40 partition first sector */
le64_t sector_count; /* 0x48 partition sectors count */
le32_t fat_sector_start; /* 0x50 FAT first sector */
le32_t fat_sector_count; /* 0x54 FAT sectors count */
le32_t cluster_sector_start; /* 0x58 first cluster sector */
le32_t cluster_count; /* 0x5C total clusters count */
le32_t rootdir_cluster; /* 0x60 first cluster of the root dir */
le32_t volume_serial; /* 0x64 volume serial number */
struct /* 0x68 FS version */
{
uint8_t minor;
uint8_t major;
}
version;
le16_t volume_state; /* 0x6A volume state flags */
uint8_t sector_bits; /* 0x6C sector size as (1 << n) */
uint8_t spc_bits; /* 0x6D sectors per cluster as (1 << n) */
uint8_t fat_count; /* 0x6E always 1 */
uint8_t drive_no; /* 0x6F always 0x80 */
uint8_t allocated_percent; /* 0x70 percentage of allocated space */
uint8_t __unused2[397]; /* 0x71 always 0 */
le16_t boot_signature; /* the value of 0xAA55 */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_super_block) == 512);
#define EXFAT_ENTRY_VALID 0x80
#define EXFAT_ENTRY_CONTINUED 0x40
#define EXFAT_ENTRY_OPTIONAL 0x20
#define EXFAT_ENTRY_BITMAP (0x01 | EXFAT_ENTRY_VALID)
#define EXFAT_ENTRY_UPCASE (0x02 | EXFAT_ENTRY_VALID)
#define EXFAT_ENTRY_LABEL (0x03 | EXFAT_ENTRY_VALID)
#define EXFAT_ENTRY_FILE (0x05 | EXFAT_ENTRY_VALID)
#define EXFAT_ENTRY_FILE_INFO (0x00 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
#define EXFAT_ENTRY_FILE_NAME (0x01 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
#define EXFAT_ENTRY_FILE_TAIL (0x00 | EXFAT_ENTRY_VALID \
| EXFAT_ENTRY_CONTINUED \
| EXFAT_ENTRY_OPTIONAL)
struct exfat_entry /* common container for all entries */
{
uint8_t type; /* any of EXFAT_ENTRY_xxx */
uint8_t data[31];
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry) == 32);
#define EXFAT_ENAME_MAX 15
struct exfat_entry_bitmap /* allocated clusters bitmap */
{
uint8_t type; /* EXFAT_ENTRY_BITMAP */
uint8_t __unknown1[19];
le32_t start_cluster;
le64_t size; /* in bytes */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_bitmap) == 32);
#define EXFAT_UPCASE_CHARS 0x10000
struct exfat_entry_upcase /* upper case translation table */
{
uint8_t type; /* EXFAT_ENTRY_UPCASE */
uint8_t __unknown1[3];
le32_t checksum;
uint8_t __unknown2[12];
le32_t start_cluster;
le64_t size; /* in bytes */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_upcase) == 32);
struct exfat_entry_label /* volume label */
{
uint8_t type; /* EXFAT_ENTRY_LABEL */
uint8_t length; /* number of characters */
le16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_label) == 32);
#define EXFAT_ATTRIB_RO 0x01
#define EXFAT_ATTRIB_HIDDEN 0x02
#define EXFAT_ATTRIB_SYSTEM 0x04
#define EXFAT_ATTRIB_VOLUME 0x08
#define EXFAT_ATTRIB_DIR 0x10
#define EXFAT_ATTRIB_ARCH 0x20
struct exfat_entry_meta1 /* file or directory info (part 1) */
{
uint8_t type; /* EXFAT_ENTRY_FILE */
uint8_t continuations;
le16_t checksum;
le16_t attrib; /* combination of EXFAT_ATTRIB_xxx */
le16_t __unknown1;
le16_t crtime, crdate; /* creation date and time */
le16_t mtime, mdate; /* latest modification date and time */
le16_t atime, adate; /* latest access date and time */
uint8_t crtime_cs; /* creation time in cs (centiseconds) */
uint8_t mtime_cs; /* latest modification time in cs */
uint8_t __unknown2[10];
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32);
#define EXFAT_FLAG_ALWAYS1 (1u << 0)
#define EXFAT_FLAG_CONTIGUOUS (1u << 1)
struct exfat_entry_meta2 /* file or directory info (part 2) */
{
uint8_t type; /* EXFAT_ENTRY_FILE_INFO */
uint8_t flags; /* combination of EXFAT_FLAG_xxx */
uint8_t __unknown1;
uint8_t name_length;
le16_t name_hash;
le16_t __unknown2;
le64_t valid_size; /* in bytes, less or equal to size */
uint8_t __unknown3[4];
le32_t start_cluster;
le64_t size; /* in bytes */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_meta2) == 32);
struct exfat_entry_name /* file or directory name */
{
uint8_t type; /* EXFAT_ENTRY_FILE_NAME */
uint8_t __unknown;
le16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_name) == 32);
#endif /* ifndef EXFATFS_H_INCLUDED */

@ -0,0 +1,511 @@
/*
io.c (02.09.09)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#if defined(__APPLE__)
#include <sys/disk.h>
#elif defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#include <sys/ioctl.h>
#elif __linux__
#include <sys/mount.h>
#endif
#ifdef USE_UBLIO
#include <sys/uio.h>
#include <ublio.h>
#endif
struct exfat_dev
{
int fd;
enum exfat_mode mode;
off_t size; /* in bytes */
#ifdef USE_UBLIO
off_t pos;
ublio_filehandle_t ufh;
#endif
};
int g_vtoy_exfat_disk_fd = -1;
uint64_t g_vtoy_exfat_part_size = 0;
static bool is_open(int fd)
{
return fcntl(fd, F_GETFD) != -1;
}
static int open_ro(const char* spec)
{
return open(spec, O_RDONLY);
}
static int open_rw(const char* spec)
{
int fd = open(spec, O_RDWR);
#ifdef __linux__
int ro = 0;
/*
This ioctl is needed because after "blockdev --setro" kernel still
allows to open the device in read-write mode but fails writes.
*/
if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
{
close(fd);
errno = EROFS;
return -1;
}
#endif
return fd;
}
struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
{
struct exfat_dev* dev;
struct stat stbuf;
#ifdef USE_UBLIO
struct ublio_param up;
#endif
/* The system allocates file descriptors sequentially. If we have been
started with stdin (0), stdout (1) or stderr (2) closed, the system
will give us descriptor 0, 1 or 2 later when we open block device,
FUSE communication pipe, etc. As a result, functions using stdin,
stdout or stderr will actually work with a different thing and can
corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */
while (!is_open(STDIN_FILENO)
|| !is_open(STDOUT_FILENO)
|| !is_open(STDERR_FILENO))
{
/* we don't need those descriptors, let them leak */
if (open("/dev/null", O_RDWR) == -1)
{
exfat_error("failed to open /dev/null");
return NULL;
}
}
dev = malloc(sizeof(struct exfat_dev));
if (dev == NULL)
{
exfat_error("failed to allocate memory for device structure");
return NULL;
}
switch (mode)
{
case EXFAT_MODE_RO:
dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
if (dev->fd == -1)
{
free(dev);
exfat_error("failed to open '%s' in read-only mode: %s", spec,
strerror(errno));
return NULL;
}
dev->mode = EXFAT_MODE_RO;
break;
case EXFAT_MODE_RW:
dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
if (dev->fd == -1)
{
free(dev);
exfat_error("failed to open '%s' in read-write mode: %s", spec,
strerror(errno));
return NULL;
}
dev->mode = EXFAT_MODE_RW;
break;
case EXFAT_MODE_ANY:
dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
if (dev->fd != -1)
{
dev->mode = EXFAT_MODE_RW;
break;
}
dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
if (dev->fd != -1)
{
dev->mode = EXFAT_MODE_RO;
exfat_warn("'%s' is write-protected, mounting read-only", spec);
break;
}
free(dev);
exfat_error("failed to open '%s': %s", spec, strerror(errno));
return NULL;
}
if (fstat(dev->fd, &stbuf) != 0)
{
close(dev->fd);
free(dev);
exfat_error("failed to fstat '%s'", spec);
return NULL;
}
if (!S_ISBLK(stbuf.st_mode) &&
!S_ISCHR(stbuf.st_mode) &&
!S_ISREG(stbuf.st_mode))
{
close(dev->fd);
free(dev);
exfat_error("'%s' is neither a device, nor a regular file", spec);
return NULL;
}
#if defined(__APPLE__)
if (!S_ISREG(stbuf.st_mode))
{
uint32_t block_size = 0;
uint64_t blocks = 0;
if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
{
close(dev->fd);
free(dev);
exfat_error("failed to get block size");
return NULL;
}
if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
{
close(dev->fd);
free(dev);
exfat_error("failed to get blocks count");
return NULL;
}
dev->size = blocks * block_size;
}
else
#elif defined(__OpenBSD__)
if (!S_ISREG(stbuf.st_mode))
{
struct disklabel lab;
struct partition* pp;
char* partition;
if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1)
{
close(dev->fd);
free(dev);
exfat_error("failed to get disklabel");
return NULL;
}
/* Don't need to check that partition letter is valid as we won't get
this far otherwise. */
partition = strchr(spec, '\0') - 1;
pp = &(lab.d_partitions[*partition - 'a']);
dev->size = DL_GETPSIZE(pp) * lab.d_secsize;
if (pp->p_fstype != FS_NTFS)
exfat_warn("partition type is not 0x07 (NTFS/exFAT); "
"you can fix this with fdisk(8)");
}
else
#endif
{
/* works for Linux, FreeBSD, Solaris */
dev->size = exfat_seek(dev, 0, SEEK_END);
if (dev->size <= 0)
{
close(dev->fd);
free(dev);
exfat_error("failed to get size of '%s'", spec);
return NULL;
}
if (exfat_seek(dev, 0, SEEK_SET) == -1)
{
close(dev->fd);
free(dev);
exfat_error("failed to seek to the beginning of '%s'", spec);
return NULL;
}
}
#ifdef USE_UBLIO
memset(&up, 0, sizeof(struct ublio_param));
up.up_blocksize = 256 * 1024;
up.up_items = 64;
up.up_grace = 32;
up.up_priv = &dev->fd;
dev->pos = 0;
dev->ufh = ublio_open(&up);
if (dev->ufh == NULL)
{
close(dev->fd);
free(dev);
exfat_error("failed to initialize ublio");
return NULL;
}
#endif
return dev;
}
int exfat_close(struct exfat_dev* dev)
{
int rc = 0;
#ifdef USE_UBLIO
if (ublio_close(dev->ufh) != 0)
{
exfat_error("failed to close ublio");
rc = -EIO;
}
#endif
if (dev->fd != g_vtoy_exfat_disk_fd)
{
if (close(dev->fd) != 0)
{
exfat_error("failed to close device: %s", strerror(errno));
rc = -EIO;
}
}
free(dev);
return rc;
}
int exfat_fsync(struct exfat_dev* dev)
{
int rc = 0;
#ifdef USE_UBLIO
if (ublio_fsync(dev->ufh) != 0)
{
exfat_error("ublio fsync failed");
rc = -EIO;
}
#endif
if (fsync(dev->fd) != 0)
{
exfat_error("fsync failed: %s", strerror(errno));
rc = -EIO;
}
return rc;
}
enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
{
return dev->mode;
}
off_t exfat_get_size(const struct exfat_dev* dev)
{
return dev->size;
}
off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
{
#ifdef USE_UBLIO
/* XXX SEEK_CUR will be handled incorrectly */
return dev->pos = lseek(dev->fd, offset, whence);
#else
if (SEEK_SET == whence)
{
if (offset > g_vtoy_exfat_part_size)
{
return -1;
}
lseek(dev->fd, 512 * 2048 + offset, SEEK_SET);
return offset;
}
else if (SEEK_END == whence)
{
if (offset == 0)
{
offset = 512 * 2048 + g_vtoy_exfat_part_size;
lseek(dev->fd, offset, SEEK_SET);
return (off_t)g_vtoy_exfat_part_size;
}
else
{
exfat_error("Invalid SEEK_END offset %llu", (unsigned long long)offset);
return -1;
}
}
else
{
exfat_error("Invalid seek whence %d", whence);
return lseek(dev->fd, offset, whence);
}
#endif
}
ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
{
#ifdef USE_UBLIO
ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
if (result >= 0)
dev->pos += size;
return result;
#else
return read(dev->fd, buffer, size);
#endif
}
ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
{
#ifdef USE_UBLIO
ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
if (result >= 0)
dev->pos += size;
return result;
#else
return write(dev->fd, buffer, size);
#endif
}
ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
off_t offset)
{
#ifdef USE_UBLIO
return ublio_pread(dev->ufh, buffer, size, offset);
#else
return pread(dev->fd, buffer, size, offset);
#endif
}
ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
off_t offset)
{
#ifdef USE_UBLIO
return ublio_pwrite(dev->ufh, buffer, size, offset);
#else
return pwrite(dev->fd, buffer, size, offset);
#endif
}
ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
void* buffer, size_t size, off_t offset)
{
cluster_t cluster;
char* bufp = buffer;
off_t lsize, loffset, remainder;
if (offset >= node->size)
return 0;
if (size == 0)
return 0;
cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
if (CLUSTER_INVALID(*ef->sb, cluster))
{
exfat_error("invalid cluster 0x%x while reading", cluster);
return -EIO;
}
loffset = offset % CLUSTER_SIZE(*ef->sb);
remainder = MIN(size, node->size - offset);
while (remainder > 0)
{
if (CLUSTER_INVALID(*ef->sb, cluster))
{
exfat_error("invalid cluster 0x%x while reading", cluster);
return -EIO;
}
lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
if (exfat_pread(ef->dev, bufp, lsize,
exfat_c2o(ef, cluster) + loffset) < 0)
{
exfat_error("failed to read cluster %#x", cluster);
return -EIO;
}
bufp += lsize;
loffset = 0;
remainder -= lsize;
cluster = exfat_next_cluster(ef, node, cluster);
}
if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime)
exfat_update_atime(node);
return MIN(size, node->size - offset) - remainder;
}
ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
const void* buffer, size_t size, off_t offset)
{
int rc;
cluster_t cluster;
const char* bufp = buffer;
off_t lsize, loffset, remainder;
if (offset > node->size)
{
rc = exfat_truncate(ef, node, offset, true);
if (rc != 0)
return rc;
}
if (offset + size > node->size)
{
rc = exfat_truncate(ef, node, offset + size, false);
if (rc != 0)
return rc;
}
if (size == 0)
return 0;
cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
if (CLUSTER_INVALID(*ef->sb, cluster))
{
exfat_error("invalid cluster 0x%x while writing", cluster);
return -EIO;
}
loffset = offset % CLUSTER_SIZE(*ef->sb);
remainder = size;
while (remainder > 0)
{
if (CLUSTER_INVALID(*ef->sb, cluster))
{
exfat_error("invalid cluster 0x%x while writing", cluster);
return -EIO;
}
lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
if (exfat_pwrite(ef->dev, bufp, lsize,
exfat_c2o(ef, cluster) + loffset) < 0)
{
exfat_error("failed to write cluster %#x", cluster);
return -EIO;
}
bufp += lsize;
loffset = 0;
remainder -= lsize;
cluster = exfat_next_cluster(ef, node, cluster);
}
if (!(node->attrib & EXFAT_ATTRIB_DIR))
/* directory's mtime should be updated by the caller only when it
creates or removes something in this directory */
exfat_update_mtime(node);
return size - remainder;
}

@ -0,0 +1,224 @@
/*
lookup.c (02.09.09)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
#include <string.h>
#include <errno.h>
#include <inttypes.h>
int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
struct exfat_iterator* it)
{
int rc;
exfat_get_node(dir);
it->parent = dir;
it->current = NULL;
rc = exfat_cache_directory(ef, dir);
if (rc != 0)
exfat_put_node(ef, dir);
return rc;
}
void exfat_closedir(struct exfat* ef, struct exfat_iterator* it)
{
exfat_put_node(ef, it->parent);
it->parent = NULL;
it->current = NULL;
}
struct exfat_node* exfat_readdir(struct exfat_iterator* it)
{
if (it->current == NULL)
it->current = it->parent->child;
else
it->current = it->current->next;
if (it->current != NULL)
return exfat_get_node(it->current);
else
return NULL;
}
static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
{
return (int) ef->upcase[a] - (int) ef->upcase[b];
}
static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
{
while (le16_to_cpu(*a) && le16_to_cpu(*b))
{
int rc = compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
if (rc != 0)
return rc;
a++;
b++;
}
return compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
}
static int lookup_name(struct exfat* ef, struct exfat_node* parent,
struct exfat_node** node, const char* name, size_t n)
{
struct exfat_iterator it;
le16_t buffer[EXFAT_NAME_MAX + 1];
int rc;
*node = NULL;
rc = utf8_to_utf16(buffer, name, EXFAT_NAME_MAX + 1, n);
if (rc != 0)
return rc;
rc = exfat_opendir(ef, parent, &it);
if (rc != 0)
return rc;
while ((*node = exfat_readdir(&it)))
{
if (compare_name(ef, buffer, (*node)->name) == 0)
{
exfat_closedir(ef, &it);
return 0;
}
exfat_put_node(ef, *node);
}
exfat_closedir(ef, &it);
return -ENOENT;
}
static size_t get_comp(const char* path, const char** comp)
{
const char* end;
*comp = path + strspn(path, "/"); /* skip leading slashes */
end = strchr(*comp, '/');
if (end == NULL)
return strlen(*comp);
else
return end - *comp;
}
int exfat_lookup(struct exfat* ef, struct exfat_node** node,
const char* path)
{
struct exfat_node* parent;
const char* p;
size_t n;
int rc;
/* start from the root directory */
parent = *node = exfat_get_node(ef->root);
for (p = path; (n = get_comp(p, &p)); p += n)
{
if (n == 1 && *p == '.') /* skip "." component */
continue;
rc = lookup_name(ef, parent, node, p, n);
if (rc != 0)
{
exfat_put_node(ef, parent);
return rc;
}
exfat_put_node(ef, parent);
parent = *node;
}
return 0;
}
static bool is_last_comp(const char* comp, size_t length)
{
const char* p = comp + length;
return get_comp(p, &p) == 0;
}
static bool is_allowed(const char* comp, size_t length)
{
size_t i;
for (i = 0; i < length; i++)
switch (comp[i])
{
case 0x01 ... 0x1f:
case '/':
case '\\':
case ':':
case '*':
case '?':
case '"':
case '<':
case '>':
case '|':
return false;
}
return true;
}
int exfat_split(struct exfat* ef, struct exfat_node** parent,
struct exfat_node** node, le16_t* name, const char* path)
{
const char* p;
size_t n;
int rc;
memset(name, 0, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
*parent = *node = exfat_get_node(ef->root);
for (p = path; (n = get_comp(p, &p)); p += n)
{
if (n == 1 && *p == '.')
continue;
if (is_last_comp(p, n))
{
if (!is_allowed(p, n))
{
/* contains characters that are not allowed */
exfat_put_node(ef, *parent);
return -ENOENT;
}
rc = utf8_to_utf16(name, p, EXFAT_NAME_MAX + 1, n);
if (rc != 0)
{
exfat_put_node(ef, *parent);
return rc;
}
rc = lookup_name(ef, *parent, node, p, n);
if (rc != 0 && rc != -ENOENT)
{
exfat_put_node(ef, *parent);
return rc;
}
return 0;
}
rc = lookup_name(ef, *parent, node, p, n);
if (rc != 0)
{
exfat_put_node(ef, *parent);
return rc;
}
exfat_put_node(ef, *parent);
*parent = *node;
}
exfat_bug("impossible");
return 1;
}

@ -0,0 +1,389 @@
/*
mount.c (22.10.09)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/types.h>
static uint64_t rootdir_size(const struct exfat* ef)
{
uint32_t clusters = 0;
uint32_t clusters_max = le32_to_cpu(ef->sb->cluster_count);
cluster_t rootdir_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
/* Iterate all clusters of the root directory to calculate its size.
It can't be contiguous because there is no flag to indicate this. */
do
{
if (clusters == clusters_max) /* infinite loop detected */
{
exfat_error("root directory cannot occupy all %d clusters",
clusters);
return 0;
}
if (CLUSTER_INVALID(*ef->sb, rootdir_cluster))
{
exfat_error("bad cluster %#x while reading root directory",
rootdir_cluster);
return 0;
}
rootdir_cluster = exfat_next_cluster(ef, ef->root, rootdir_cluster);
clusters++;
}
while (rootdir_cluster != EXFAT_CLUSTER_END);
return (uint64_t) clusters * CLUSTER_SIZE(*ef->sb);
}
static const char* get_option(const char* options, const char* option_name)
{
const char* p;
size_t length = strlen(option_name);
for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
if ((p == options || p[-1] == ',') && p[length] == '=')
return p + length + 1;
return NULL;
}
static int get_int_option(const char* options, const char* option_name,
int base, int default_value)
{
const char* p = get_option(options, option_name);
if (p == NULL)
return default_value;
return strtol(p, NULL, base);
}
static bool match_option(const char* options, const char* option_name)
{
const char* p;
size_t length = strlen(option_name);
for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
if ((p == options || p[-1] == ',') &&
(p[length] == ',' || p[length] == '\0'))
return true;
return false;
}
static void parse_options(struct exfat* ef, const char* options)
{
int opt_umask;
opt_umask = get_int_option(options, "umask", 8, 0);
ef->dmask = get_int_option(options, "dmask", 8, opt_umask);
ef->fmask = get_int_option(options, "fmask", 8, opt_umask);
ef->uid = get_int_option(options, "uid", 10, geteuid());
ef->gid = get_int_option(options, "gid", 10, getegid());
ef->noatime = match_option(options, "noatime");
switch (get_int_option(options, "repair", 10, 0))
{
case 1:
ef->repair = EXFAT_REPAIR_ASK;
break;
case 2:
ef->repair = EXFAT_REPAIR_YES;
break;
default:
ef->repair = EXFAT_REPAIR_NO;
break;
}
}
static bool verify_vbr_checksum(const struct exfat* ef, void* sector)
{
off_t sector_size = SECTOR_SIZE(*ef->sb);
uint32_t vbr_checksum;
int i;
if (exfat_pread(ef->dev, sector, sector_size, 0) < 0)
{
exfat_error("failed to read boot sector");
return false;
}
vbr_checksum = exfat_vbr_start_checksum(sector, sector_size);
for (i = 1; i < 11; i++)
{
if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0)
{
exfat_error("failed to read VBR sector");
return false;
}
vbr_checksum = exfat_vbr_add_checksum(sector, sector_size,
vbr_checksum);
}
if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0)
{
exfat_error("failed to read VBR checksum sector");
return false;
}
for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum)
{
exfat_error("invalid VBR checksum 0x%x (expected 0x%x)",
le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum);
if (!EXFAT_REPAIR(invalid_vbr_checksum, ef, sector, vbr_checksum))
return false;
}
return true;
}
static int commit_super_block(const struct exfat* ef)
{
if (exfat_pwrite(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
{
exfat_error("failed to write super block");
return 1;
}
return exfat_fsync(ef->dev);
}
static int prepare_super_block(const struct exfat* ef)
{
if (le16_to_cpu(ef->sb->volume_state) & EXFAT_STATE_MOUNTED)
exfat_warn("volume was not unmounted cleanly");
if (ef->ro)
return 0;
ef->sb->volume_state = cpu_to_le16(
le16_to_cpu(ef->sb->volume_state) | EXFAT_STATE_MOUNTED);
return commit_super_block(ef);
}
static void exfat_free(struct exfat* ef)
{
exfat_close(ef->dev); /* first of all, close the descriptor */
ef->dev = NULL; /* struct exfat_dev is freed by exfat_close() */
free(ef->root);
ef->root = NULL;
free(ef->zero_cluster);
ef->zero_cluster = NULL;
free(ef->cmap.chunk);
ef->cmap.chunk = NULL;
free(ef->upcase);
ef->upcase = NULL;
free(ef->sb);
ef->sb = NULL;
}
int exfat_mount(struct exfat* ef, const char* spec, const char* options)
{
int rc;
enum exfat_mode mode;
exfat_tzset();
memset(ef, 0, sizeof(struct exfat));
parse_options(ef, options);
if (match_option(options, "ro"))
mode = EXFAT_MODE_RO;
else if (match_option(options, "ro_fallback"))
mode = EXFAT_MODE_ANY;
else
mode = EXFAT_MODE_RW;
ef->dev = exfat_open(spec, mode);
if (ef->dev == NULL)
return -EIO;
if (exfat_get_mode(ef->dev) == EXFAT_MODE_RO)
{
if (mode == EXFAT_MODE_ANY)
ef->ro = -1;
else
ef->ro = 1;
}
ef->sb = malloc(sizeof(struct exfat_super_block));
if (ef->sb == NULL)
{
exfat_error("failed to allocate memory for the super block");
exfat_free(ef);
return -ENOMEM;
}
memset(ef->sb, 0, sizeof(struct exfat_super_block));
if (exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
{
exfat_error("failed to read boot sector");
exfat_free(ef);
return -EIO;
}
if (memcmp(ef->sb->oem_name, "EXFAT ", 8) != 0)
{
exfat_error("exFAT file system is not found");
exfat_free(ef);
return -EIO;
}
/* sector cannot be smaller than 512 bytes */
if (ef->sb->sector_bits < 9)
{
exfat_error("too small sector size: 2^%hhd", ef->sb->sector_bits);
exfat_free(ef);
return -EIO;
}
/* officially exFAT supports cluster size up to 32 MB */
if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25)
{
exfat_error("too big cluster size: 2^(%hhd+%hhd)",
ef->sb->sector_bits, ef->sb->spc_bits);
exfat_free(ef);
return -EIO;
}
ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb));
if (ef->zero_cluster == NULL)
{
exfat_error("failed to allocate zero sector");
exfat_free(ef);
return -ENOMEM;
}
/* use zero_cluster as a temporary buffer for VBR checksum verification */
if (!verify_vbr_checksum(ef, ef->zero_cluster))
{
exfat_free(ef);
return -EIO;
}
memset(ef->zero_cluster, 0, CLUSTER_SIZE(*ef->sb));
if (ef->sb->version.major != 1 || ef->sb->version.minor != 0)
{
exfat_error("unsupported exFAT version: %hhu.%hhu",
ef->sb->version.major, ef->sb->version.minor);
exfat_free(ef);
return -EIO;
}
if (ef->sb->fat_count != 1)
{
exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count);
exfat_free(ef);
return -EIO;
}
if (le64_to_cpu(ef->sb->sector_count) * SECTOR_SIZE(*ef->sb) >
exfat_get_size(ef->dev))
{
/* this can cause I/O errors later but we don't fail mounting to let
user rescue data */
exfat_warn("file system in sectors is larger than device: "
"%"PRIu64" * %d > %"PRIu64,
le64_to_cpu(ef->sb->sector_count), SECTOR_SIZE(*ef->sb),
exfat_get_size(ef->dev));
}
if ((off_t) le32_to_cpu(ef->sb->cluster_count) * CLUSTER_SIZE(*ef->sb) >
exfat_get_size(ef->dev))
{
exfat_error("file system in clusters is larger than device: "
"%u * %d > %"PRIu64,
le32_to_cpu(ef->sb->cluster_count), CLUSTER_SIZE(*ef->sb),
exfat_get_size(ef->dev));
exfat_free(ef);
return -EIO;
}
ef->root = malloc(sizeof(struct exfat_node));
if (ef->root == NULL)
{
exfat_error("failed to allocate root node");
exfat_free(ef);
return -ENOMEM;
}
memset(ef->root, 0, sizeof(struct exfat_node));
ef->root->attrib = EXFAT_ATTRIB_DIR;
ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
ef->root->fptr_cluster = ef->root->start_cluster;
ef->root->name[0] = cpu_to_le16('\0');
ef->root->size = rootdir_size(ef);
if (ef->root->size == 0)
{
exfat_free(ef);
return -EIO;
}
/* exFAT does not have time attributes for the root directory */
ef->root->mtime = 0;
ef->root->atime = 0;
/* always keep at least 1 reference to the root node */
exfat_get_node(ef->root);
rc = exfat_cache_directory(ef, ef->root);
if (rc != 0)
goto error;
if (ef->upcase == NULL)
{
exfat_error("upcase table is not found");
goto error;
}
if (ef->cmap.chunk == NULL)
{
exfat_error("clusters bitmap is not found");
goto error;
}
if (prepare_super_block(ef) != 0)
goto error;
return 0;
error:
exfat_put_node(ef, ef->root);
exfat_reset_cache(ef);
exfat_free(ef);
return -EIO;
}
static void finalize_super_block(struct exfat* ef)
{
if (ef->ro)
return;
ef->sb->volume_state = cpu_to_le16(
le16_to_cpu(ef->sb->volume_state) & ~EXFAT_STATE_MOUNTED);
/* Some implementations set the percentage of allocated space to 0xff
on FS creation and never update it. In this case leave it as is. */
if (ef->sb->allocated_percent != 0xff)
{
uint32_t free, total;
free = exfat_count_free_clusters(ef);
total = le32_to_cpu(ef->sb->cluster_count);
ef->sb->allocated_percent = ((total - free) * 100 + total / 2) / total;
}
commit_super_block(ef); /* ignore return code */
}
void exfat_unmount(struct exfat* ef)
{
exfat_flush_nodes(ef); /* ignore return code */
exfat_flush(ef); /* ignore return code */
exfat_put_node(ef, ef->root);
exfat_reset_cache(ef);
finalize_super_block(ef);
exfat_free(ef); /* will close the descriptor */
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,63 @@
/*
platform.h (14.05.13)
OS-specific code (libc-specific in fact). Note that systems with the
same kernel can use different libc implementations.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef PLATFORM_H_INCLUDED
#define PLATFORM_H_INCLUDED
#if defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)
#include <endian.h>
#include <byteswap.h>
#define exfat_bswap16(x) bswap_16(x)
#define exfat_bswap32(x) bswap_32(x)
#define exfat_bswap64(x) bswap_64(x)
#define EXFAT_BYTE_ORDER __BYTE_ORDER
#define EXFAT_LITTLE_ENDIAN __LITTLE_ENDIAN
#define EXFAT_BIG_ENDIAN __BIG_ENDIAN
#elif defined(__APPLE__)
#include <machine/endian.h>
#include <libkern/OSByteOrder.h>
#define exfat_bswap16(x) OSSwapInt16(x)
#define exfat_bswap32(x) OSSwapInt32(x)
#define exfat_bswap64(x) OSSwapInt64(x)
#define EXFAT_BYTE_ORDER BYTE_ORDER
#define EXFAT_LITTLE_ENDIAN LITTLE_ENDIAN
#define EXFAT_BIG_ENDIAN BIG_ENDIAN
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/endian.h>
#define exfat_bswap16(x) bswap16(x)
#define exfat_bswap32(x) bswap32(x)
#define exfat_bswap64(x) bswap64(x)
#define EXFAT_BYTE_ORDER _BYTE_ORDER
#define EXFAT_LITTLE_ENDIAN _LITTLE_ENDIAN
#define EXFAT_BIG_ENDIAN _BIG_ENDIAN
#else
#error Unknown platform
#endif
#endif /* ifndef PLATFORM_H_INCLUDED */

@ -0,0 +1,103 @@
/*
repair.c (09.03.17)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
#include <strings.h>
int exfat_errors_fixed;
bool exfat_ask_to_fix(const struct exfat* ef)
{
const char* question = "Fix (Y/N)?";
char answer[8];
bool yeah, nope;
switch (ef->repair)
{
case EXFAT_REPAIR_NO:
return false;
case EXFAT_REPAIR_YES:
printf("%s %s", question, "Y\n");
return true;
case EXFAT_REPAIR_ASK:
do
{
printf("%s ", question);
fflush(stdout);
if (fgets(answer, sizeof(answer), stdin))
{
yeah = strcasecmp(answer, "Y\n") == 0;
nope = strcasecmp(answer, "N\n") == 0;
}
else
{
yeah = false;
nope = true;
}
}
while (!yeah && !nope);
return yeah;
}
exfat_bug("invalid repair option value: %d", ef->repair);
return false;
}
bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector,
uint32_t vbr_checksum)
{
size_t i;
off_t sector_size = SECTOR_SIZE(*ef->sb);
for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
((le32_t*) sector)[i] = cpu_to_le32(vbr_checksum);
if (exfat_pwrite(ef->dev, sector, sector_size, 11 * sector_size) < 0)
{
exfat_error("failed to write correct VBR checksum");
return false;
}
exfat_errors_fixed++;
return true;
}
bool exfat_fix_invalid_node_checksum(const struct exfat* ef,
struct exfat_node* node)
{
/* checksum will be rewritten by exfat_flush_node() */
node->is_dirty = true;
exfat_errors_fixed++;
return true;
}
bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir,
const struct exfat_entry* entry, off_t offset)
{
struct exfat_entry deleted = *entry;
deleted.type &= ~EXFAT_ENTRY_VALID;
if (exfat_generic_pwrite(ef, dir, &deleted, sizeof(struct exfat_entry),
offset) != sizeof(struct exfat_entry))
return false;
exfat_errors_fixed++;
return true;
}

@ -0,0 +1,164 @@
/*
time.c (03.02.12)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
/* timezone offset from UTC in seconds; positive for western timezones,
negative for eastern ones */
static long exfat_timezone;
#define SEC_IN_MIN 60ll
#define SEC_IN_HOUR (60 * SEC_IN_MIN)
#define SEC_IN_DAY (24 * SEC_IN_HOUR)
#define SEC_IN_YEAR (365 * SEC_IN_DAY) /* not leap year */
/* Unix epoch started at 0:00:00 UTC 1 January 1970 */
#define UNIX_EPOCH_YEAR 1970
/* exFAT epoch started at 0:00:00 UTC 1 January 1980 */
#define EXFAT_EPOCH_YEAR 1980
/* number of years from Unix epoch to exFAT epoch */
#define EPOCH_DIFF_YEAR (EXFAT_EPOCH_YEAR - UNIX_EPOCH_YEAR)
/* number of days from Unix epoch to exFAT epoch (considering leap days) */
#define EPOCH_DIFF_DAYS (EPOCH_DIFF_YEAR * 365 + EPOCH_DIFF_YEAR / 4)
/* number of seconds from Unix epoch to exFAT epoch (considering leap days) */
#define EPOCH_DIFF_SEC (EPOCH_DIFF_DAYS * SEC_IN_DAY)
/* number of leap years passed from exFAT epoch to the specified year
(excluding the specified year itself) */
#define LEAP_YEARS(year) ((EXFAT_EPOCH_YEAR + (year) - 1) / 4 \
- (EXFAT_EPOCH_YEAR - 1) / 4)
/* checks whether the specified year is leap */
#define IS_LEAP_YEAR(year) ((EXFAT_EPOCH_YEAR + (year)) % 4 == 0)
static const time_t days_in_year[] =
{
/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
{
time_t unix_time = EPOCH_DIFF_SEC;
uint16_t ndate = le16_to_cpu(date);
uint16_t ntime = le16_to_cpu(time);
uint16_t day = ndate & 0x1f; /* 5 bits, 1-31 */
uint16_t month = ndate >> 5 & 0xf; /* 4 bits, 1-12 */
uint16_t year = ndate >> 9; /* 7 bits, 1-127 (+1980) */
uint16_t twosec = ntime & 0x1f; /* 5 bits, 0-29 (2 sec granularity) */
uint16_t min = ntime >> 5 & 0x3f; /* 6 bits, 0-59 */
uint16_t hour = ntime >> 11; /* 5 bits, 0-23 */
if (day == 0 || month == 0 || month > 12)
{
exfat_error("bad date %u-%02hu-%02hu",
year + EXFAT_EPOCH_YEAR, month, day);
return 0;
}
if (hour > 23 || min > 59 || twosec > 29)
{
exfat_error("bad time %hu:%02hu:%02u",
hour, min, twosec * 2);
return 0;
}
if (centisec > 199)
{
exfat_error("bad centiseconds count %hhu", centisec);
return 0;
}
/* every 4th year between 1904 and 2096 is leap */
unix_time += year * SEC_IN_YEAR + LEAP_YEARS(year) * SEC_IN_DAY;
unix_time += days_in_year[month] * SEC_IN_DAY;
/* if it's leap year and February has passed we should add 1 day */
if ((EXFAT_EPOCH_YEAR + year) % 4 == 0 && month > 2)
unix_time += SEC_IN_DAY;
unix_time += (day - 1) * SEC_IN_DAY;
unix_time += hour * SEC_IN_HOUR;
unix_time += min * SEC_IN_MIN;
/* exFAT represents time with 2 sec granularity */
unix_time += twosec * 2;
unix_time += centisec / 100;
/* exFAT stores timestamps in local time, so we correct it to UTC */
unix_time += exfat_timezone;
return unix_time;
}
void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
uint8_t* centisec)
{
time_t shift = EPOCH_DIFF_SEC + exfat_timezone;
uint16_t day, month, year;
uint16_t twosec, min, hour;
int days;
int i;
/* time before exFAT epoch cannot be represented */
if (unix_time < shift)
unix_time = shift;
unix_time -= shift;
days = unix_time / SEC_IN_DAY;
year = (4 * days) / (4 * 365 + 1);
days -= year * 365 + LEAP_YEARS(year);
month = 0;
for (i = 1; i <= 12; i++)
{
int leap_day = (IS_LEAP_YEAR(year) && i == 2);
int leap_sub = (IS_LEAP_YEAR(year) && i >= 3);
if (i == 12 || days - leap_sub < days_in_year[i + 1] + leap_day)
{
month = i;
days -= days_in_year[i] + leap_sub;
break;
}
}
day = days + 1;
hour = (unix_time % SEC_IN_DAY) / SEC_IN_HOUR;
min = (unix_time % SEC_IN_HOUR) / SEC_IN_MIN;
twosec = (unix_time % SEC_IN_MIN) / 2;
*date = cpu_to_le16(day | (month << 5) | (year << 9));
*time = cpu_to_le16(twosec | (min << 5) | (hour << 11));
if (centisec)
*centisec = (unix_time % 2) * 100;
}
void exfat_tzset(void)
{
time_t now;
struct tm* utc;
tzset();
now = time(NULL);
utc = gmtime(&now);
/* gmtime() always sets tm_isdst to 0 because daylight savings never
affect UTC. Setting tm_isdst to -1 makes mktime() to determine whether
summer time is in effect. */
utc->tm_isdst = -1;
exfat_timezone = mktime(utc) - now;
}

@ -0,0 +1,245 @@
/*
utf.c (13.09.09)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
#include <errno.h>
static char* wchar_to_utf8(char* output, wchar_t wc, size_t outsize)
{
if (wc <= 0x7f)
{
if (outsize < 1)
return NULL;
*output++ = (char) wc;
}
else if (wc <= 0x7ff)
{
if (outsize < 2)
return NULL;
*output++ = 0xc0 | (wc >> 6);
*output++ = 0x80 | (wc & 0x3f);
}
else if (wc <= 0xffff)
{
if (outsize < 3)
return NULL;
*output++ = 0xe0 | (wc >> 12);
*output++ = 0x80 | ((wc >> 6) & 0x3f);
*output++ = 0x80 | (wc & 0x3f);
}
else if (wc <= 0x1fffff)
{
if (outsize < 4)
return NULL;
*output++ = 0xf0 | (wc >> 18);
*output++ = 0x80 | ((wc >> 12) & 0x3f);
*output++ = 0x80 | ((wc >> 6) & 0x3f);
*output++ = 0x80 | (wc & 0x3f);
}
else if (wc <= 0x3ffffff)
{
if (outsize < 5)
return NULL;
*output++ = 0xf8 | (wc >> 24);
*output++ = 0x80 | ((wc >> 18) & 0x3f);
*output++ = 0x80 | ((wc >> 12) & 0x3f);
*output++ = 0x80 | ((wc >> 6) & 0x3f);
*output++ = 0x80 | (wc & 0x3f);
}
else if (wc <= 0x7fffffff)
{
if (outsize < 6)
return NULL;
*output++ = 0xfc | (wc >> 30);
*output++ = 0x80 | ((wc >> 24) & 0x3f);
*output++ = 0x80 | ((wc >> 18) & 0x3f);
*output++ = 0x80 | ((wc >> 12) & 0x3f);
*output++ = 0x80 | ((wc >> 6) & 0x3f);
*output++ = 0x80 | (wc & 0x3f);
}
else
return NULL;
return output;
}
static const le16_t* utf16_to_wchar(const le16_t* input, wchar_t* wc,
size_t insize)
{
if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800)
{
if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00)
return NULL;
*wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10);
*wc |= (le16_to_cpu(input[1]) & 0x3ff);
*wc += 0x10000;
return input + 2;
}
else
{
*wc = le16_to_cpu(*input);
return input + 1;
}
}
int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
size_t insize)
{
const le16_t* inp = input;
char* outp = output;
wchar_t wc;
while (inp - input < insize)
{
inp = utf16_to_wchar(inp, &wc, insize - (inp - input));
if (inp == NULL)
{
exfat_error("illegal UTF-16 sequence");
return -EILSEQ;
}
outp = wchar_to_utf8(outp, wc, outsize - (outp - output));
if (outp == NULL)
{
exfat_error("name is too long");
return -ENAMETOOLONG;
}
if (wc == 0)
return 0;
}
if (outp - output >= outsize)
{
exfat_error("name is too long");
return -ENAMETOOLONG;
}
*outp = '\0';
return 0;
}
static const char* utf8_to_wchar(const char* input, wchar_t* wc,
size_t insize)
{
if ((input[0] & 0x80) == 0 && insize >= 1)
{
*wc = (wchar_t) input[0];
return input + 1;
}
if ((input[0] & 0xe0) == 0xc0 && insize >= 2)
{
*wc = (((wchar_t) input[0] & 0x1f) << 6) |
((wchar_t) input[1] & 0x3f);
return input + 2;
}
if ((input[0] & 0xf0) == 0xe0 && insize >= 3)
{
*wc = (((wchar_t) input[0] & 0x0f) << 12) |
(((wchar_t) input[1] & 0x3f) << 6) |
((wchar_t) input[2] & 0x3f);
return input + 3;
}
if ((input[0] & 0xf8) == 0xf0 && insize >= 4)
{
*wc = (((wchar_t) input[0] & 0x07) << 18) |
(((wchar_t) input[1] & 0x3f) << 12) |
(((wchar_t) input[2] & 0x3f) << 6) |
((wchar_t) input[3] & 0x3f);
return input + 4;
}
if ((input[0] & 0xfc) == 0xf8 && insize >= 5)
{
*wc = (((wchar_t) input[0] & 0x03) << 24) |
(((wchar_t) input[1] & 0x3f) << 18) |
(((wchar_t) input[2] & 0x3f) << 12) |
(((wchar_t) input[3] & 0x3f) << 6) |
((wchar_t) input[4] & 0x3f);
return input + 5;
}
if ((input[0] & 0xfe) == 0xfc && insize >= 6)
{
*wc = (((wchar_t) input[0] & 0x01) << 30) |
(((wchar_t) input[1] & 0x3f) << 24) |
(((wchar_t) input[2] & 0x3f) << 18) |
(((wchar_t) input[3] & 0x3f) << 12) |
(((wchar_t) input[4] & 0x3f) << 6) |
((wchar_t) input[5] & 0x3f);
return input + 6;
}
return NULL;
}
static le16_t* wchar_to_utf16(le16_t* output, wchar_t wc, size_t outsize)
{
if (wc <= 0xffff) /* if character is from BMP */
{
if (outsize == 0)
return NULL;
output[0] = cpu_to_le16(wc);
return output + 1;
}
if (outsize < 2)
return NULL;
wc -= 0x10000;
output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff));
output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff));
return output + 2;
}
int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
size_t insize)
{
const char* inp = input;
le16_t* outp = output;
wchar_t wc;
while (inp - input < insize)
{
inp = utf8_to_wchar(inp, &wc, insize - (inp - input));
if (inp == NULL)
{
exfat_error("illegal UTF-8 sequence");
return -EILSEQ;
}
outp = wchar_to_utf16(outp, wc, outsize - (outp - output));
if (outp == NULL)
{
exfat_error("name is too long");
return -ENAMETOOLONG;
}
if (wc == 0)
break;
}
if (outp - output >= outsize)
{
exfat_error("name is too long");
return -ENAMETOOLONG;
}
*outp = cpu_to_le16(0);
return 0;
}
size_t utf16_length(const le16_t* str)
{
size_t i = 0;
while (le16_to_cpu(str[i]))
i++;
return i;
}

@ -0,0 +1,180 @@
/*
utils.c (04.09.09)
exFAT file system implementation library.
Free exFAT implementation.
Copyright (C) 2010-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "exfat.h"
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
struct stat* stbuf)
{
memset(stbuf, 0, sizeof(struct stat));
if (node->attrib & EXFAT_ATTRIB_DIR)
stbuf->st_mode = S_IFDIR | (0777 & ~ef->dmask);
else
stbuf->st_mode = S_IFREG | (0777 & ~ef->fmask);
stbuf->st_nlink = 1;
stbuf->st_uid = ef->uid;
stbuf->st_gid = ef->gid;
stbuf->st_size = node->size;
stbuf->st_blocks = ROUND_UP(node->size, CLUSTER_SIZE(*ef->sb)) / 512;
stbuf->st_mtime = node->mtime;
stbuf->st_atime = node->atime;
/* set ctime to mtime to ensure we don't break programs that rely on ctime
(e.g. rsync) */
stbuf->st_ctime = node->mtime;
}
void exfat_get_name(const struct exfat_node* node,
char buffer[EXFAT_UTF8_NAME_BUFFER_MAX])
{
if (utf16_to_utf8(buffer, node->name, EXFAT_UTF8_NAME_BUFFER_MAX,
EXFAT_NAME_MAX) != 0)
exfat_bug("failed to convert name to UTF-8");
}
static uint16_t add_checksum_byte(uint16_t sum, uint8_t byte)
{
return ((sum << 15) | (sum >> 1)) + byte;
}
static uint16_t add_checksum_bytes(uint16_t sum, const void* buffer, size_t n)
{
int i;
for (i = 0; i < n; i++)
sum = add_checksum_byte(sum, ((const uint8_t*) buffer)[i]);
return sum;
}
uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry)
{
uint16_t sum = 0;
int i;
for (i = 0; i < sizeof(struct exfat_entry); i++)
if (i != 2 && i != 3) /* skip checksum field itself */
sum = add_checksum_byte(sum, ((const uint8_t*) entry)[i]);
return sum;
}
uint16_t exfat_add_checksum(const void* entry, uint16_t sum)
{
return add_checksum_bytes(sum, entry, sizeof(struct exfat_entry));
}
le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n)
{
uint16_t checksum;
int i;
checksum = exfat_start_checksum((const struct exfat_entry_meta1*) entries);
for (i = 1; i < n; i++)
checksum = exfat_add_checksum(entries + i, checksum);
return cpu_to_le16(checksum);
}
uint32_t exfat_vbr_start_checksum(const void* sector, size_t size)
{
size_t i;
uint32_t sum = 0;
for (i = 0; i < size; i++)
/* skip volume_state and allocated_percent fields */
if (i != 0x6a && i != 0x6b && i != 0x70)
sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
return sum;
}
uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum)
{
size_t i;
for (i = 0; i < size; i++)
sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
return sum;
}
le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name,
size_t length)
{
size_t i;
uint16_t hash = 0;
for (i = 0; i < length; i++)
{
uint16_t c = le16_to_cpu(name[i]);
/* convert to upper case */
c = ef->upcase[c];
hash = ((hash << 15) | (hash >> 1)) + (c & 0xff);
hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
}
return cpu_to_le16(hash);
}
void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb)
{
size_t i;
/* 16 EB (minus 1 byte) is the largest size that can be represented by
uint64_t */
const char* units[] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"};
uint64_t divisor = 1;
uint64_t temp = 0;
for (i = 0; ; i++, divisor *= 1024)
{
temp = (value + divisor / 2) / divisor;
if (temp == 0)
break;
if (temp / 1024 * 1024 == temp)
continue;
if (temp < 10240)
break;
}
hb->value = temp;
hb->unit = units[i];
}
void exfat_print_info(const struct exfat_super_block* sb,
uint32_t free_clusters)
{
struct exfat_human_bytes hb;
off_t total_space = le64_to_cpu(sb->sector_count) * SECTOR_SIZE(*sb);
off_t avail_space = (off_t) free_clusters * CLUSTER_SIZE(*sb);
printf("File system version %hhu.%hhu\n",
sb->version.major, sb->version.minor);
exfat_humanize_bytes(SECTOR_SIZE(*sb), &hb);
printf("Sector size %10"PRIu64" %s\n", hb.value, hb.unit);
exfat_humanize_bytes(CLUSTER_SIZE(*sb), &hb);
printf("Cluster size %10"PRIu64" %s\n", hb.value, hb.unit);
exfat_humanize_bytes(total_space, &hb);
printf("Volume size %10"PRIu64" %s\n", hb.value, hb.unit);
exfat_humanize_bytes(total_space - avail_space, &hb);
printf("Used space %10"PRIu64" %s\n", hb.value, hb.unit);
exfat_humanize_bytes(avail_space, &hb);
printf("Available space %10"PRIu64" %s\n", hb.value, hb.unit);
}

@ -0,0 +1,79 @@
/*
cbm.c (09.11.10)
Clusters Bitmap creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "cbm.h"
#include "fat.h"
#include "uct.h"
#include "rootdir.h"
#include <limits.h>
#include <string.h>
static off_t cbm_alignment(void)
{
return get_cluster_size();
}
static off_t cbm_size(void)
{
return DIV_ROUND_UP(
(get_volume_size() - get_position(&cbm)) / get_cluster_size(),
CHAR_BIT);
}
static int cbm_write(struct exfat_dev* dev)
{
uint32_t allocated_clusters =
DIV_ROUND_UP(cbm.get_size(), get_cluster_size()) +
DIV_ROUND_UP(uct.get_size(), get_cluster_size()) +
DIV_ROUND_UP(rootdir.get_size(), get_cluster_size());
size_t bitmap_size = ROUND_UP(allocated_clusters, CHAR_BIT);
bitmap_t* bitmap = malloc(BMAP_SIZE(bitmap_size));
size_t i;
if (bitmap == NULL)
{
exfat_error("failed to allocate bitmap of %zu bytes",
BMAP_SIZE(bitmap_size));
return 1;
}
memset(bitmap, 0, BMAP_SIZE(bitmap_size));
for (i = 0; i < bitmap_size; i++)
if (i < allocated_clusters)
BMAP_SET(bitmap, i);
if (exfat_write(dev, bitmap, bitmap_size / CHAR_BIT) < 0)
{
free(bitmap);
exfat_error("failed to write bitmap of %zu bytes",
bitmap_size / CHAR_BIT);
return 1;
}
free(bitmap);
return 0;
}
const struct fs_object cbm =
{
.get_alignment = cbm_alignment,
.get_size = cbm_size,
.write = cbm_write,
};

@ -0,0 +1,30 @@
/*
cbm.h (09.11.10)
Clusters Bitmap creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MKFS_CBM_H_INCLUDED
#define MKFS_CBM_H_INCLUDED
#include "mkexfat.h"
extern const struct fs_object cbm;
#endif /* ifndef MKFS_CBM_H_INCLUDED */

@ -0,0 +1,88 @@
/*
fat.c (09.11.10)
File Allocation Table creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "fat.h"
#include "cbm.h"
#include "uct.h"
#include "rootdir.h"
#include <unistd.h>
static off_t fat_alignment(void)
{
return (off_t) 128 * get_sector_size();
}
static off_t fat_size(void)
{
return get_volume_size() / get_cluster_size() * sizeof(cluster_t);
}
static cluster_t fat_write_entry(struct exfat_dev* dev, cluster_t cluster,
cluster_t value)
{
le32_t fat_entry = cpu_to_le32(value);
if (exfat_write(dev, &fat_entry, sizeof(fat_entry)) < 0)
{
exfat_error("failed to write FAT entry 0x%x", value);
return 0;
}
return cluster + 1;
}
static cluster_t fat_write_entries(struct exfat_dev* dev, cluster_t cluster,
uint64_t length)
{
cluster_t end = cluster + DIV_ROUND_UP(length, get_cluster_size());
while (cluster < end - 1)
{
cluster = fat_write_entry(dev, cluster, cluster + 1);
if (cluster == 0)
return 0;
}
return fat_write_entry(dev, cluster, EXFAT_CLUSTER_END);
}
static int fat_write(struct exfat_dev* dev)
{
cluster_t c = 0;
if (!(c = fat_write_entry(dev, c, 0xfffffff8))) /* media type */
return 1;
if (!(c = fat_write_entry(dev, c, 0xffffffff))) /* some weird constant */
return 1;
if (!(c = fat_write_entries(dev, c, cbm.get_size())))
return 1;
if (!(c = fat_write_entries(dev, c, uct.get_size())))
return 1;
if (!(c = fat_write_entries(dev, c, rootdir.get_size())))
return 1;
return 0;
}
const struct fs_object fat =
{
.get_alignment = fat_alignment,
.get_size = fat_size,
.write = fat_write,
};

@ -0,0 +1,30 @@
/*
fat.h (09.11.10)
File Allocation Table creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MKFS_FAT_H_INCLUDED
#define MKFS_FAT_H_INCLUDED
#include "mkexfat.h"
extern const struct fs_object fat;
#endif /* ifndef MKFS_FAT_H_INCLUDED */

@ -0,0 +1,167 @@
/*
mkexfat.c (22.04.12)
FS creation engine.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "mkexfat.h"
#include <sys/types.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
static int check_size(off_t volume_size)
{
const struct fs_object** pp;
off_t position = 0;
for (pp = objects; *pp; pp++)
{
position = ROUND_UP(position, (*pp)->get_alignment());
position += (*pp)->get_size();
}
if (position > volume_size)
{
struct exfat_human_bytes vhb;
exfat_humanize_bytes(volume_size, &vhb);
exfat_error("too small device (%"PRIu64" %s)", vhb.value, vhb.unit);
return 1;
}
return 0;
}
static int erase_object(struct exfat_dev* dev, const void* block,
size_t block_size, off_t start, off_t size)
{
const off_t block_count = DIV_ROUND_UP(size, block_size);
off_t i;
if (exfat_seek(dev, start, SEEK_SET) == (off_t) -1)
{
exfat_error("seek to 0x%"PRIx64" failed", start);
return 1;
}
for (i = 0; i < size; i += block_size)
{
if (exfat_write(dev, block, MIN(size - i, block_size)) < 0)
{
exfat_error("failed to erase block %"PRIu64"/%"PRIu64
" at 0x%"PRIx64, i + 1, block_count, start);
return 1;
}
}
return 0;
}
static int erase(struct exfat_dev* dev)
{
const struct fs_object** pp;
off_t position = 0;
const size_t block_size = 1024 * 1024;
void* block = malloc(block_size);
if (block == NULL)
{
exfat_error("failed to allocate erase block of %zu bytes", block_size);
return 1;
}
memset(block, 0, block_size);
for (pp = objects; *pp; pp++)
{
position = ROUND_UP(position, (*pp)->get_alignment());
if (erase_object(dev, block, block_size, position,
(*pp)->get_size()) != 0)
{
free(block);
return 1;
}
position += (*pp)->get_size();
}
free(block);
return 0;
}
static int create(struct exfat_dev* dev)
{
const struct fs_object** pp;
off_t position = 0;
for (pp = objects; *pp; pp++)
{
position = ROUND_UP(position, (*pp)->get_alignment());
if (exfat_seek(dev, position, SEEK_SET) == (off_t) -1)
{
exfat_error("seek to 0x%"PRIx64" failed", position);
return 1;
}
if ((*pp)->write(dev) != 0)
return 1;
position += (*pp)->get_size();
}
return 0;
}
int mkfs(struct exfat_dev* dev, off_t volume_size)
{
if (check_size(volume_size) != 0)
return 1;
exfat_debug("Creating... ");
//fputs("Creating... ", stdout);
//fflush(stdout);
if (erase(dev) != 0)
return 1;
if (create(dev) != 0)
return 1;
//puts("done.");
//fputs("Flushing... ", stdout);
//fflush(stdout);
exfat_debug("Flushing... ");
if (exfat_fsync(dev) != 0)
return 1;
//puts("done.");
return 0;
}
off_t get_position(const struct fs_object* object)
{
const struct fs_object** pp;
off_t position = 0;
for (pp = objects; *pp; pp++)
{
position = ROUND_UP(position, (*pp)->get_alignment());
if (*pp == object)
return position;
position += (*pp)->get_size();
}
exfat_bug("unknown object");
return 0;
}

@ -0,0 +1,49 @@
/*
mkexfat.h (09.11.10)
FS creation engine.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MKFS_MKEXFAT_H_INCLUDED
#define MKFS_MKEXFAT_H_INCLUDED
#include "exfat.h"
struct fs_object
{
off_t (*get_alignment)(void);
off_t (*get_size)(void);
int (*write)(struct exfat_dev* dev);
};
extern const struct fs_object* objects[];
int get_sector_bits(void);
int get_spc_bits(void);
off_t get_volume_size(void);
const le16_t* get_volume_label(void);
uint32_t get_volume_serial(void);
uint64_t get_first_sector(void);
int get_sector_size(void);
int get_cluster_size(void);
int mkfs(struct exfat_dev* dev, off_t volume_size);
off_t get_position(const struct fs_object* object);
#endif /* ifndef MKFS_MKEXFAT_H_INCLUDED */

@ -0,0 +1,268 @@
/*
main.c (15.08.10)
Creates exFAT file system.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "mkexfat.h"
#include "vbr.h"
#include "fat.h"
#include "cbm.h"
#include "uct.h"
#include "rootdir.h"
#include "exfat.h"
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
const struct fs_object* objects[] =
{
&vbr,
&vbr,
&fat,
/* clusters heap */
&cbm,
&uct,
&rootdir,
NULL,
};
static struct
{
int sector_bits;
int spc_bits;
off_t volume_size;
le16_t volume_label[EXFAT_ENAME_MAX + 1];
uint32_t volume_serial;
uint64_t first_sector;
}
param;
extern int g_vtoy_exfat_disk_fd;
extern uint64_t g_vtoy_exfat_part_size;
int get_sector_bits(void)
{
return param.sector_bits;
}
int get_spc_bits(void)
{
return param.spc_bits;
}
off_t get_volume_size(void)
{
return param.volume_size;
}
const le16_t* get_volume_label(void)
{
return param.volume_label;
}
uint32_t get_volume_serial(void)
{
return param.volume_serial;
}
uint64_t get_first_sector(void)
{
return param.first_sector;
}
int get_sector_size(void)
{
return 1 << get_sector_bits();
}
int get_cluster_size(void)
{
return get_sector_size() << get_spc_bits();
}
static int setup_spc_bits(int sector_bits, int user_defined, off_t volume_size)
{
int i;
if (user_defined != -1)
{
off_t cluster_size = 1 << sector_bits << user_defined;
if (volume_size / cluster_size > EXFAT_LAST_DATA_CLUSTER)
{
struct exfat_human_bytes chb, vhb;
exfat_humanize_bytes(cluster_size, &chb);
exfat_humanize_bytes(volume_size, &vhb);
exfat_error("cluster size %"PRIu64" %s is too small for "
"%"PRIu64" %s volume, try -s %d",
chb.value, chb.unit,
vhb.value, vhb.unit,
1 << setup_spc_bits(sector_bits, -1, volume_size));
return -1;
}
return user_defined;
}
if (volume_size < 256ull * 1024 * 1024)
return MAX(0, 12 - sector_bits); /* 4 KB */
if (volume_size < 32ull * 1024 * 1024 * 1024)
return MAX(0, 15 - sector_bits); /* 32 KB */
for (i = 17; ; i++) /* 128 KB or more */
if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER)
return MAX(0, i - sector_bits);
}
static int setup_volume_label(le16_t label[EXFAT_ENAME_MAX + 1], const char* s)
{
memset(label, 0, (EXFAT_ENAME_MAX + 1) * sizeof(le16_t));
if (s == NULL)
return 0;
return utf8_to_utf16(label, s, EXFAT_ENAME_MAX + 1, strlen(s));
}
static uint32_t setup_volume_serial(uint32_t user_defined)
{
struct timeval now;
if (user_defined != 0)
return user_defined;
if (gettimeofday(&now, NULL) != 0)
{
exfat_error("failed to form volume id");
return 0;
}
return (now.tv_sec << 20) | now.tv_usec;
}
static int setup(struct exfat_dev* dev, int sector_bits, int spc_bits,
const char* volume_label, uint32_t volume_serial,
uint64_t first_sector)
{
param.sector_bits = sector_bits;
param.first_sector = first_sector;
param.volume_size = exfat_get_size(dev);
param.spc_bits = setup_spc_bits(sector_bits, spc_bits, param.volume_size);
if (param.spc_bits == -1)
return 1;
if (setup_volume_label(param.volume_label, volume_label) != 0)
return 1;
param.volume_serial = setup_volume_serial(volume_serial);
if (param.volume_serial == 0)
return 1;
return mkfs(dev, param.volume_size);
}
static int logarithm2(int n)
{
int i;
for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
if ((1 << i) == n)
return i;
return -1;
}
static void usage(const char* prog)
{
fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
"[-p partition-first-sector] "
"[-s sectors-per-cluster] [-V] <device>\n", prog);
exit(1);
}
int mkexfat_main(const char *devpath, int fd, uint64_t part_sector_count)
{
int spc_bits = -1;
uint32_t volume_serial = 0;
uint64_t first_sector = 0;
struct exfat_dev* dev;
#if 0
while ((opt = getopt(argc, argv, "i:n:p:s:V")) != -1)
{
switch (opt)
{
case 'i':
volume_serial = strtol(optarg, NULL, 16);
break;
case 'n':
volume_label = optarg;
break;
case 'p':
first_sector = strtoll(optarg, NULL, 10);
break;
case 's':
spc_bits = logarithm2(atoi(optarg));
if (spc_bits < 0)
{
exfat_error("invalid option value: '%s'", optarg);
return 1;
}
break;
case 'V':
puts("Copyright (C) 2011-2018 Andrew Nayenko");
return 0;
default:
usage(argv[0]);
break;
}
}
#endif /* #if 0 */
/*
* DiskSize > 32GB Cluster Size use 128KB
* DiskSize < 32GB Cluster Size use 32KB
*/
if ((part_sector_count / 2097152) > 32)
{
spc_bits = logarithm2(256);
}
else
{
spc_bits = logarithm2(64);
}
g_vtoy_exfat_disk_fd = fd;
g_vtoy_exfat_part_size = part_sector_count * 512;
dev = exfat_open(devpath, EXFAT_MODE_RW);
if (dev == NULL)
return 1;
if (setup(dev, 9, spc_bits, "Ventoy", volume_serial, first_sector) != 0)
{
exfat_close(dev);
return 1;
}
if (exfat_close(dev) != 0)
return 1;
return 0;
}

@ -0,0 +1,102 @@
/*
rootdir.c (09.11.10)
Root directory creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "rootdir.h"
#include "uct.h"
#include "cbm.h"
#include "uctc.h"
#include <string.h>
static off_t rootdir_alignment(void)
{
return get_cluster_size();
}
static off_t rootdir_size(void)
{
return get_cluster_size();
}
static void init_label_entry(struct exfat_entry_label* label_entry)
{
memset(label_entry, 0, sizeof(struct exfat_entry_label));
label_entry->type = EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID;
if (utf16_length(get_volume_label()) == 0)
return;
memcpy(label_entry->name, get_volume_label(),
EXFAT_ENAME_MAX * sizeof(le16_t));
label_entry->length = utf16_length(get_volume_label());
label_entry->type |= EXFAT_ENTRY_VALID;
}
static void init_bitmap_entry(struct exfat_entry_bitmap* bitmap_entry)
{
memset(bitmap_entry, 0, sizeof(struct exfat_entry_bitmap));
bitmap_entry->type = EXFAT_ENTRY_BITMAP;
bitmap_entry->start_cluster = cpu_to_le32(EXFAT_FIRST_DATA_CLUSTER);
bitmap_entry->size = cpu_to_le64(cbm.get_size());
}
static void init_upcase_entry(struct exfat_entry_upcase* upcase_entry)
{
size_t i;
uint32_t sum = 0;
for (i = 0; i < sizeof(upcase_table); i++)
sum = ((sum << 31) | (sum >> 1)) + upcase_table[i];
memset(upcase_entry, 0, sizeof(struct exfat_entry_upcase));
upcase_entry->type = EXFAT_ENTRY_UPCASE;
upcase_entry->checksum = cpu_to_le32(sum);
upcase_entry->start_cluster = cpu_to_le32(
(get_position(&uct) - get_position(&cbm)) / get_cluster_size() +
EXFAT_FIRST_DATA_CLUSTER);
upcase_entry->size = cpu_to_le64(sizeof(upcase_table));
}
static int rootdir_write(struct exfat_dev* dev)
{
struct exfat_entry_label label_entry;
struct exfat_entry_bitmap bitmap_entry;
struct exfat_entry_upcase upcase_entry;
init_label_entry(&label_entry);
init_bitmap_entry(&bitmap_entry);
init_upcase_entry(&upcase_entry);
if (exfat_write(dev, &label_entry, sizeof(struct exfat_entry)) < 0)
return 1;
if (exfat_write(dev, &bitmap_entry, sizeof(struct exfat_entry)) < 0)
return 1;
if (exfat_write(dev, &upcase_entry, sizeof(struct exfat_entry)) < 0)
return 1;
return 0;
}
const struct fs_object rootdir =
{
.get_alignment = rootdir_alignment,
.get_size = rootdir_size,
.write = rootdir_write,
};

@ -0,0 +1,30 @@
/*
rootdir.h (09.11.10)
Root directory creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MKFS_ROOTDIR_H_INCLUDED
#define MKFS_ROOTDIR_H_INCLUDED
#include "mkexfat.h"
extern const struct fs_object rootdir;
#endif /* ifndef MKFS_ROOTDIR_H_INCLUDED */

@ -0,0 +1,52 @@
/*
uct.c (09.11.10)
Upper Case Table creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "uct.h"
#include "uctc.h"
static off_t uct_alignment(void)
{
return get_cluster_size();
}
static off_t uct_size(void)
{
return sizeof(upcase_table);
}
static int uct_write(struct exfat_dev* dev)
{
if (exfat_write(dev, upcase_table, sizeof(upcase_table)) < 0)
{
exfat_error("failed to write upcase table of %zu bytes",
sizeof(upcase_table));
return 1;
}
return 0;
}
const struct fs_object uct =
{
.get_alignment = uct_alignment,
.get_size = uct_size,
.write = uct_write,
};

@ -0,0 +1,30 @@
/*
uct.h (09.11.10)
Upper Case Table creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MKFS_UCT_H_INCLUDED
#define MKFS_UCT_H_INCLUDED
#include "mkexfat.h"
extern const struct fs_object uct;
#endif /* ifndef MKFS_UCT_H_INCLUDED */

@ -0,0 +1,757 @@
/*
uctc.c (30.04.12)
Upper Case Table contents.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "uctc.h"
uint8_t upcase_table[5836] =
{
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00,
0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00,
0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00,
0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00,
0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00,
0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00,
0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00,
0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00,
0x28, 0x00, 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00,
0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00,
0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00,
0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00,
0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00,
0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00,
0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5f, 0x00,
0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x7b, 0x00,
0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7f, 0x00,
0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00,
0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00,
0x88, 0x00, 0x89, 0x00, 0x8a, 0x00, 0x8b, 0x00,
0x8c, 0x00, 0x8d, 0x00, 0x8e, 0x00, 0x8f, 0x00,
0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00,
0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00,
0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00,
0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00,
0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00,
0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00,
0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00,
0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00,
0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00,
0xb4, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00,
0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00,
0xbc, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, 0x00,
0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00,
0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xdf, 0x00,
0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xf7, 0x00,
0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0x78, 0x01,
0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01,
0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01,
0x08, 0x01, 0x08, 0x01, 0x0a, 0x01, 0x0a, 0x01,
0x0c, 0x01, 0x0c, 0x01, 0x0e, 0x01, 0x0e, 0x01,
0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01,
0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01,
0x18, 0x01, 0x18, 0x01, 0x1a, 0x01, 0x1a, 0x01,
0x1c, 0x01, 0x1c, 0x01, 0x1e, 0x01, 0x1e, 0x01,
0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01,
0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01,
0x28, 0x01, 0x28, 0x01, 0x2a, 0x01, 0x2a, 0x01,
0x2c, 0x01, 0x2c, 0x01, 0x2e, 0x01, 0x2e, 0x01,
0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01,
0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01,
0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3b, 0x01,
0x3b, 0x01, 0x3d, 0x01, 0x3d, 0x01, 0x3f, 0x01,
0x3f, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01,
0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01,
0x47, 0x01, 0x49, 0x01, 0x4a, 0x01, 0x4a, 0x01,
0x4c, 0x01, 0x4c, 0x01, 0x4e, 0x01, 0x4e, 0x01,
0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01,
0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01,
0x58, 0x01, 0x58, 0x01, 0x5a, 0x01, 0x5a, 0x01,
0x5c, 0x01, 0x5c, 0x01, 0x5e, 0x01, 0x5e, 0x01,
0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01,
0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01,
0x68, 0x01, 0x68, 0x01, 0x6a, 0x01, 0x6a, 0x01,
0x6c, 0x01, 0x6c, 0x01, 0x6e, 0x01, 0x6e, 0x01,
0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01,
0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01,
0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7b, 0x01,
0x7b, 0x01, 0x7d, 0x01, 0x7d, 0x01, 0x7f, 0x01,
0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01,
0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01,
0x87, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, 0x01,
0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01,
0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01,
0x94, 0x01, 0xf6, 0x01, 0x96, 0x01, 0x97, 0x01,
0x98, 0x01, 0x98, 0x01, 0x3d, 0x02, 0x9b, 0x01,
0x9c, 0x01, 0x9d, 0x01, 0x20, 0x02, 0x9f, 0x01,
0xa0, 0x01, 0xa0, 0x01, 0xa2, 0x01, 0xa2, 0x01,
0xa4, 0x01, 0xa4, 0x01, 0xa6, 0x01, 0xa7, 0x01,
0xa7, 0x01, 0xa9, 0x01, 0xaa, 0x01, 0xab, 0x01,
0xac, 0x01, 0xac, 0x01, 0xae, 0x01, 0xaf, 0x01,
0xaf, 0x01, 0xb1, 0x01, 0xb2, 0x01, 0xb3, 0x01,
0xb3, 0x01, 0xb5, 0x01, 0xb5, 0x01, 0xb7, 0x01,
0xb8, 0x01, 0xb8, 0x01, 0xba, 0x01, 0xbb, 0x01,
0xbc, 0x01, 0xbc, 0x01, 0xbe, 0x01, 0xf7, 0x01,
0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, 0xc3, 0x01,
0xc4, 0x01, 0xc5, 0x01, 0xc4, 0x01, 0xc7, 0x01,
0xc8, 0x01, 0xc7, 0x01, 0xca, 0x01, 0xcb, 0x01,
0xca, 0x01, 0xcd, 0x01, 0xcd, 0x01, 0xcf, 0x01,
0xcf, 0x01, 0xd1, 0x01, 0xd1, 0x01, 0xd3, 0x01,
0xd3, 0x01, 0xd5, 0x01, 0xd5, 0x01, 0xd7, 0x01,
0xd7, 0x01, 0xd9, 0x01, 0xd9, 0x01, 0xdb, 0x01,
0xdb, 0x01, 0x8e, 0x01, 0xde, 0x01, 0xde, 0x01,
0xe0, 0x01, 0xe0, 0x01, 0xe2, 0x01, 0xe2, 0x01,
0xe4, 0x01, 0xe4, 0x01, 0xe6, 0x01, 0xe6, 0x01,
0xe8, 0x01, 0xe8, 0x01, 0xea, 0x01, 0xea, 0x01,
0xec, 0x01, 0xec, 0x01, 0xee, 0x01, 0xee, 0x01,
0xf0, 0x01, 0xf1, 0x01, 0xf2, 0x01, 0xf1, 0x01,
0xf4, 0x01, 0xf4, 0x01, 0xf6, 0x01, 0xf7, 0x01,
0xf8, 0x01, 0xf8, 0x01, 0xfa, 0x01, 0xfa, 0x01,
0xfc, 0x01, 0xfc, 0x01, 0xfe, 0x01, 0xfe, 0x01,
0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02,
0x08, 0x02, 0x08, 0x02, 0x0a, 0x02, 0x0a, 0x02,
0x0c, 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x0e, 0x02,
0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02,
0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02,
0x18, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1a, 0x02,
0x1c, 0x02, 0x1c, 0x02, 0x1e, 0x02, 0x1e, 0x02,
0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02,
0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02,
0x28, 0x02, 0x28, 0x02, 0x2a, 0x02, 0x2a, 0x02,
0x2c, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x2e, 0x02,
0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02,
0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02,
0x38, 0x02, 0x39, 0x02, 0x65, 0x2c, 0x3b, 0x02,
0x3b, 0x02, 0x3d, 0x02, 0x66, 0x2c, 0x3f, 0x02,
0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02,
0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02,
0x48, 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4a, 0x02,
0x4c, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x4e, 0x02,
0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01,
0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8a, 0x01,
0x58, 0x02, 0x8f, 0x01, 0x5a, 0x02, 0x90, 0x01,
0x5c, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x5f, 0x02,
0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01,
0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02,
0x97, 0x01, 0x96, 0x01, 0x6a, 0x02, 0x62, 0x2c,
0x6c, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x9c, 0x01,
0x70, 0x02, 0x71, 0x02, 0x9d, 0x01, 0x73, 0x02,
0x74, 0x02, 0x9f, 0x01, 0x76, 0x02, 0x77, 0x02,
0x78, 0x02, 0x79, 0x02, 0x7a, 0x02, 0x7b, 0x02,
0x7c, 0x02, 0x64, 0x2c, 0x7e, 0x02, 0x7f, 0x02,
0xa6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xa9, 0x01,
0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02,
0xae, 0x01, 0x44, 0x02, 0xb1, 0x01, 0xb2, 0x01,
0x45, 0x02, 0x8d, 0x02, 0x8e, 0x02, 0x8f, 0x02,
0x90, 0x02, 0x91, 0x02, 0xb7, 0x01, 0x93, 0x02,
0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02,
0x98, 0x02, 0x99, 0x02, 0x9a, 0x02, 0x9b, 0x02,
0x9c, 0x02, 0x9d, 0x02, 0x9e, 0x02, 0x9f, 0x02,
0xa0, 0x02, 0xa1, 0x02, 0xa2, 0x02, 0xa3, 0x02,
0xa4, 0x02, 0xa5, 0x02, 0xa6, 0x02, 0xa7, 0x02,
0xa8, 0x02, 0xa9, 0x02, 0xaa, 0x02, 0xab, 0x02,
0xac, 0x02, 0xad, 0x02, 0xae, 0x02, 0xaf, 0x02,
0xb0, 0x02, 0xb1, 0x02, 0xb2, 0x02, 0xb3, 0x02,
0xb4, 0x02, 0xb5, 0x02, 0xb6, 0x02, 0xb7, 0x02,
0xb8, 0x02, 0xb9, 0x02, 0xba, 0x02, 0xbb, 0x02,
0xbc, 0x02, 0xbd, 0x02, 0xbe, 0x02, 0xbf, 0x02,
0xc0, 0x02, 0xc1, 0x02, 0xc2, 0x02, 0xc3, 0x02,
0xc4, 0x02, 0xc5, 0x02, 0xc6, 0x02, 0xc7, 0x02,
0xc8, 0x02, 0xc9, 0x02, 0xca, 0x02, 0xcb, 0x02,
0xcc, 0x02, 0xcd, 0x02, 0xce, 0x02, 0xcf, 0x02,
0xd0, 0x02, 0xd1, 0x02, 0xd2, 0x02, 0xd3, 0x02,
0xd4, 0x02, 0xd5, 0x02, 0xd6, 0x02, 0xd7, 0x02,
0xd8, 0x02, 0xd9, 0x02, 0xda, 0x02, 0xdb, 0x02,
0xdc, 0x02, 0xdd, 0x02, 0xde, 0x02, 0xdf, 0x02,
0xe0, 0x02, 0xe1, 0x02, 0xe2, 0x02, 0xe3, 0x02,
0xe4, 0x02, 0xe5, 0x02, 0xe6, 0x02, 0xe7, 0x02,
0xe8, 0x02, 0xe9, 0x02, 0xea, 0x02, 0xeb, 0x02,
0xec, 0x02, 0xed, 0x02, 0xee, 0x02, 0xef, 0x02,
0xf0, 0x02, 0xf1, 0x02, 0xf2, 0x02, 0xf3, 0x02,
0xf4, 0x02, 0xf5, 0x02, 0xf6, 0x02, 0xf7, 0x02,
0xf8, 0x02, 0xf9, 0x02, 0xfa, 0x02, 0xfb, 0x02,
0xfc, 0x02, 0xfd, 0x02, 0xfe, 0x02, 0xff, 0x02,
0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03,
0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03,
0x08, 0x03, 0x09, 0x03, 0x0a, 0x03, 0x0b, 0x03,
0x0c, 0x03, 0x0d, 0x03, 0x0e, 0x03, 0x0f, 0x03,
0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03,
0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03,
0x18, 0x03, 0x19, 0x03, 0x1a, 0x03, 0x1b, 0x03,
0x1c, 0x03, 0x1d, 0x03, 0x1e, 0x03, 0x1f, 0x03,
0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03,
0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03,
0x28, 0x03, 0x29, 0x03, 0x2a, 0x03, 0x2b, 0x03,
0x2c, 0x03, 0x2d, 0x03, 0x2e, 0x03, 0x2f, 0x03,
0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03,
0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03,
0x38, 0x03, 0x39, 0x03, 0x3a, 0x03, 0x3b, 0x03,
0x3c, 0x03, 0x3d, 0x03, 0x3e, 0x03, 0x3f, 0x03,
0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03,
0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03,
0x48, 0x03, 0x49, 0x03, 0x4a, 0x03, 0x4b, 0x03,
0x4c, 0x03, 0x4d, 0x03, 0x4e, 0x03, 0x4f, 0x03,
0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03,
0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03,
0x58, 0x03, 0x59, 0x03, 0x5a, 0x03, 0x5b, 0x03,
0x5c, 0x03, 0x5d, 0x03, 0x5e, 0x03, 0x5f, 0x03,
0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03,
0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03,
0x68, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x03,
0x6c, 0x03, 0x6d, 0x03, 0x6e, 0x03, 0x6f, 0x03,
0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03,
0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03,
0x78, 0x03, 0x79, 0x03, 0x7a, 0x03, 0xfd, 0x03,
0xfe, 0x03, 0xff, 0x03, 0x7e, 0x03, 0x7f, 0x03,
0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03,
0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03,
0x88, 0x03, 0x89, 0x03, 0x8a, 0x03, 0x8b, 0x03,
0x8c, 0x03, 0x8d, 0x03, 0x8e, 0x03, 0x8f, 0x03,
0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
0xa0, 0x03, 0xa1, 0x03, 0xa2, 0x03, 0xa3, 0x03,
0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8a, 0x03,
0xb0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
0xa0, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa3, 0x03,
0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
0x8c, 0x03, 0x8e, 0x03, 0x8f, 0x03, 0xcf, 0x03,
0xd0, 0x03, 0xd1, 0x03, 0xd2, 0x03, 0xd3, 0x03,
0xd4, 0x03, 0xd5, 0x03, 0xd6, 0x03, 0xd7, 0x03,
0xd8, 0x03, 0xd8, 0x03, 0xda, 0x03, 0xda, 0x03,
0xdc, 0x03, 0xdc, 0x03, 0xde, 0x03, 0xde, 0x03,
0xe0, 0x03, 0xe0, 0x03, 0xe2, 0x03, 0xe2, 0x03,
0xe4, 0x03, 0xe4, 0x03, 0xe6, 0x03, 0xe6, 0x03,
0xe8, 0x03, 0xe8, 0x03, 0xea, 0x03, 0xea, 0x03,
0xec, 0x03, 0xec, 0x03, 0xee, 0x03, 0xee, 0x03,
0xf0, 0x03, 0xf1, 0x03, 0xf9, 0x03, 0xf3, 0x03,
0xf4, 0x03, 0xf5, 0x03, 0xf6, 0x03, 0xf7, 0x03,
0xf7, 0x03, 0xf9, 0x03, 0xfa, 0x03, 0xfa, 0x03,
0xfc, 0x03, 0xfd, 0x03, 0xfe, 0x03, 0xff, 0x03,
0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04,
0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04,
0x68, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6a, 0x04,
0x6c, 0x04, 0x6c, 0x04, 0x6e, 0x04, 0x6e, 0x04,
0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04,
0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04,
0x78, 0x04, 0x78, 0x04, 0x7a, 0x04, 0x7a, 0x04,
0x7c, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x7e, 0x04,
0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04,
0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04,
0x88, 0x04, 0x89, 0x04, 0x8a, 0x04, 0x8a, 0x04,
0x8c, 0x04, 0x8c, 0x04, 0x8e, 0x04, 0x8e, 0x04,
0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04,
0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04,
0x98, 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9a, 0x04,
0x9c, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0x9e, 0x04,
0xa0, 0x04, 0xa0, 0x04, 0xa2, 0x04, 0xa2, 0x04,
0xa4, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa6, 0x04,
0xa8, 0x04, 0xa8, 0x04, 0xaa, 0x04, 0xaa, 0x04,
0xac, 0x04, 0xac, 0x04, 0xae, 0x04, 0xae, 0x04,
0xb0, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb2, 0x04,
0xb4, 0x04, 0xb4, 0x04, 0xb6, 0x04, 0xb6, 0x04,
0xb8, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xba, 0x04,
0xbc, 0x04, 0xbc, 0x04, 0xbe, 0x04, 0xbe, 0x04,
0xc0, 0x04, 0xc1, 0x04, 0xc1, 0x04, 0xc3, 0x04,
0xc3, 0x04, 0xc5, 0x04, 0xc5, 0x04, 0xc7, 0x04,
0xc7, 0x04, 0xc9, 0x04, 0xc9, 0x04, 0xcb, 0x04,
0xcb, 0x04, 0xcd, 0x04, 0xcd, 0x04, 0xc0, 0x04,
0xd0, 0x04, 0xd0, 0x04, 0xd2, 0x04, 0xd2, 0x04,
0xd4, 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd6, 0x04,
0xd8, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xda, 0x04,
0xdc, 0x04, 0xdc, 0x04, 0xde, 0x04, 0xde, 0x04,
0xe0, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe2, 0x04,
0xe4, 0x04, 0xe4, 0x04, 0xe6, 0x04, 0xe6, 0x04,
0xe8, 0x04, 0xe8, 0x04, 0xea, 0x04, 0xea, 0x04,
0xec, 0x04, 0xec, 0x04, 0xee, 0x04, 0xee, 0x04,
0xf0, 0x04, 0xf0, 0x04, 0xf2, 0x04, 0xf2, 0x04,
0xf4, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf6, 0x04,
0xf8, 0x04, 0xf8, 0x04, 0xfa, 0x04, 0xfa, 0x04,
0xfc, 0x04, 0xfc, 0x04, 0xfe, 0x04, 0xfe, 0x04,
0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05,
0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05,
0x08, 0x05, 0x08, 0x05, 0x0a, 0x05, 0x0a, 0x05,
0x0c, 0x05, 0x0c, 0x05, 0x0e, 0x05, 0x0e, 0x05,
0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05,
0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05,
0x18, 0x05, 0x19, 0x05, 0x1a, 0x05, 0x1b, 0x05,
0x1c, 0x05, 0x1d, 0x05, 0x1e, 0x05, 0x1f, 0x05,
0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05,
0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05,
0x28, 0x05, 0x29, 0x05, 0x2a, 0x05, 0x2b, 0x05,
0x2c, 0x05, 0x2d, 0x05, 0x2e, 0x05, 0x2f, 0x05,
0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05,
0x58, 0x05, 0x59, 0x05, 0x5a, 0x05, 0x5b, 0x05,
0x5c, 0x05, 0x5d, 0x05, 0x5e, 0x05, 0x5f, 0x05,
0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xff, 0xff,
0xf6, 0x17, 0x63, 0x2c, 0x7e, 0x1d, 0x7f, 0x1d,
0x80, 0x1d, 0x81, 0x1d, 0x82, 0x1d, 0x83, 0x1d,
0x84, 0x1d, 0x85, 0x1d, 0x86, 0x1d, 0x87, 0x1d,
0x88, 0x1d, 0x89, 0x1d, 0x8a, 0x1d, 0x8b, 0x1d,
0x8c, 0x1d, 0x8d, 0x1d, 0x8e, 0x1d, 0x8f, 0x1d,
0x90, 0x1d, 0x91, 0x1d, 0x92, 0x1d, 0x93, 0x1d,
0x94, 0x1d, 0x95, 0x1d, 0x96, 0x1d, 0x97, 0x1d,
0x98, 0x1d, 0x99, 0x1d, 0x9a, 0x1d, 0x9b, 0x1d,
0x9c, 0x1d, 0x9d, 0x1d, 0x9e, 0x1d, 0x9f, 0x1d,
0xa0, 0x1d, 0xa1, 0x1d, 0xa2, 0x1d, 0xa3, 0x1d,
0xa4, 0x1d, 0xa5, 0x1d, 0xa6, 0x1d, 0xa7, 0x1d,
0xa8, 0x1d, 0xa9, 0x1d, 0xaa, 0x1d, 0xab, 0x1d,
0xac, 0x1d, 0xad, 0x1d, 0xae, 0x1d, 0xaf, 0x1d,
0xb0, 0x1d, 0xb1, 0x1d, 0xb2, 0x1d, 0xb3, 0x1d,
0xb4, 0x1d, 0xb5, 0x1d, 0xb6, 0x1d, 0xb7, 0x1d,
0xb8, 0x1d, 0xb9, 0x1d, 0xba, 0x1d, 0xbb, 0x1d,
0xbc, 0x1d, 0xbd, 0x1d, 0xbe, 0x1d, 0xbf, 0x1d,
0xc0, 0x1d, 0xc1, 0x1d, 0xc2, 0x1d, 0xc3, 0x1d,
0xc4, 0x1d, 0xc5, 0x1d, 0xc6, 0x1d, 0xc7, 0x1d,
0xc8, 0x1d, 0xc9, 0x1d, 0xca, 0x1d, 0xcb, 0x1d,
0xcc, 0x1d, 0xcd, 0x1d, 0xce, 0x1d, 0xcf, 0x1d,
0xd0, 0x1d, 0xd1, 0x1d, 0xd2, 0x1d, 0xd3, 0x1d,
0xd4, 0x1d, 0xd5, 0x1d, 0xd6, 0x1d, 0xd7, 0x1d,
0xd8, 0x1d, 0xd9, 0x1d, 0xda, 0x1d, 0xdb, 0x1d,
0xdc, 0x1d, 0xdd, 0x1d, 0xde, 0x1d, 0xdf, 0x1d,
0xe0, 0x1d, 0xe1, 0x1d, 0xe2, 0x1d, 0xe3, 0x1d,
0xe4, 0x1d, 0xe5, 0x1d, 0xe6, 0x1d, 0xe7, 0x1d,
0xe8, 0x1d, 0xe9, 0x1d, 0xea, 0x1d, 0xeb, 0x1d,
0xec, 0x1d, 0xed, 0x1d, 0xee, 0x1d, 0xef, 0x1d,
0xf0, 0x1d, 0xf1, 0x1d, 0xf2, 0x1d, 0xf3, 0x1d,
0xf4, 0x1d, 0xf5, 0x1d, 0xf6, 0x1d, 0xf7, 0x1d,
0xf8, 0x1d, 0xf9, 0x1d, 0xfa, 0x1d, 0xfb, 0x1d,
0xfc, 0x1d, 0xfd, 0x1d, 0xfe, 0x1d, 0xff, 0x1d,
0x00, 0x1e, 0x00, 0x1e, 0x02, 0x1e, 0x02, 0x1e,
0x04, 0x1e, 0x04, 0x1e, 0x06, 0x1e, 0x06, 0x1e,
0x08, 0x1e, 0x08, 0x1e, 0x0a, 0x1e, 0x0a, 0x1e,
0x0c, 0x1e, 0x0c, 0x1e, 0x0e, 0x1e, 0x0e, 0x1e,
0x10, 0x1e, 0x10, 0x1e, 0x12, 0x1e, 0x12, 0x1e,
0x14, 0x1e, 0x14, 0x1e, 0x16, 0x1e, 0x16, 0x1e,
0x18, 0x1e, 0x18, 0x1e, 0x1a, 0x1e, 0x1a, 0x1e,
0x1c, 0x1e, 0x1c, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
0x20, 0x1e, 0x20, 0x1e, 0x22, 0x1e, 0x22, 0x1e,
0x24, 0x1e, 0x24, 0x1e, 0x26, 0x1e, 0x26, 0x1e,
0x28, 0x1e, 0x28, 0x1e, 0x2a, 0x1e, 0x2a, 0x1e,
0x2c, 0x1e, 0x2c, 0x1e, 0x2e, 0x1e, 0x2e, 0x1e,
0x30, 0x1e, 0x30, 0x1e, 0x32, 0x1e, 0x32, 0x1e,
0x34, 0x1e, 0x34, 0x1e, 0x36, 0x1e, 0x36, 0x1e,
0x38, 0x1e, 0x38, 0x1e, 0x3a, 0x1e, 0x3a, 0x1e,
0x3c, 0x1e, 0x3c, 0x1e, 0x3e, 0x1e, 0x3e, 0x1e,
0x40, 0x1e, 0x40, 0x1e, 0x42, 0x1e, 0x42, 0x1e,
0x44, 0x1e, 0x44, 0x1e, 0x46, 0x1e, 0x46, 0x1e,
0x48, 0x1e, 0x48, 0x1e, 0x4a, 0x1e, 0x4a, 0x1e,
0x4c, 0x1e, 0x4c, 0x1e, 0x4e, 0x1e, 0x4e, 0x1e,
0x50, 0x1e, 0x50, 0x1e, 0x52, 0x1e, 0x52, 0x1e,
0x54, 0x1e, 0x54, 0x1e, 0x56, 0x1e, 0x56, 0x1e,
0x58, 0x1e, 0x58, 0x1e, 0x5a, 0x1e, 0x5a, 0x1e,
0x5c, 0x1e, 0x5c, 0x1e, 0x5e, 0x1e, 0x5e, 0x1e,
0x60, 0x1e, 0x60, 0x1e, 0x62, 0x1e, 0x62, 0x1e,
0x64, 0x1e, 0x64, 0x1e, 0x66, 0x1e, 0x66, 0x1e,
0x68, 0x1e, 0x68, 0x1e, 0x6a, 0x1e, 0x6a, 0x1e,
0x6c, 0x1e, 0x6c, 0x1e, 0x6e, 0x1e, 0x6e, 0x1e,
0x70, 0x1e, 0x70, 0x1e, 0x72, 0x1e, 0x72, 0x1e,
0x74, 0x1e, 0x74, 0x1e, 0x76, 0x1e, 0x76, 0x1e,
0x78, 0x1e, 0x78, 0x1e, 0x7a, 0x1e, 0x7a, 0x1e,
0x7c, 0x1e, 0x7c, 0x1e, 0x7e, 0x1e, 0x7e, 0x1e,
0x80, 0x1e, 0x80, 0x1e, 0x82, 0x1e, 0x82, 0x1e,
0x84, 0x1e, 0x84, 0x1e, 0x86, 0x1e, 0x86, 0x1e,
0x88, 0x1e, 0x88, 0x1e, 0x8a, 0x1e, 0x8a, 0x1e,
0x8c, 0x1e, 0x8c, 0x1e, 0x8e, 0x1e, 0x8e, 0x1e,
0x90, 0x1e, 0x90, 0x1e, 0x92, 0x1e, 0x92, 0x1e,
0x94, 0x1e, 0x94, 0x1e, 0x96, 0x1e, 0x97, 0x1e,
0x98, 0x1e, 0x99, 0x1e, 0x9a, 0x1e, 0x9b, 0x1e,
0x9c, 0x1e, 0x9d, 0x1e, 0x9e, 0x1e, 0x9f, 0x1e,
0xa0, 0x1e, 0xa0, 0x1e, 0xa2, 0x1e, 0xa2, 0x1e,
0xa4, 0x1e, 0xa4, 0x1e, 0xa6, 0x1e, 0xa6, 0x1e,
0xa8, 0x1e, 0xa8, 0x1e, 0xaa, 0x1e, 0xaa, 0x1e,
0xac, 0x1e, 0xac, 0x1e, 0xae, 0x1e, 0xae, 0x1e,
0xb0, 0x1e, 0xb0, 0x1e, 0xb2, 0x1e, 0xb2, 0x1e,
0xb4, 0x1e, 0xb4, 0x1e, 0xb6, 0x1e, 0xb6, 0x1e,
0xb8, 0x1e, 0xb8, 0x1e, 0xba, 0x1e, 0xba, 0x1e,
0xbc, 0x1e, 0xbc, 0x1e, 0xbe, 0x1e, 0xbe, 0x1e,
0xc0, 0x1e, 0xc0, 0x1e, 0xc2, 0x1e, 0xc2, 0x1e,
0xc4, 0x1e, 0xc4, 0x1e, 0xc6, 0x1e, 0xc6, 0x1e,
0xc8, 0x1e, 0xc8, 0x1e, 0xca, 0x1e, 0xca, 0x1e,
0xcc, 0x1e, 0xcc, 0x1e, 0xce, 0x1e, 0xce, 0x1e,
0xd0, 0x1e, 0xd0, 0x1e, 0xd2, 0x1e, 0xd2, 0x1e,
0xd4, 0x1e, 0xd4, 0x1e, 0xd6, 0x1e, 0xd6, 0x1e,
0xd8, 0x1e, 0xd8, 0x1e, 0xda, 0x1e, 0xda, 0x1e,
0xdc, 0x1e, 0xdc, 0x1e, 0xde, 0x1e, 0xde, 0x1e,
0xe0, 0x1e, 0xe0, 0x1e, 0xe2, 0x1e, 0xe2, 0x1e,
0xe4, 0x1e, 0xe4, 0x1e, 0xe6, 0x1e, 0xe6, 0x1e,
0xe8, 0x1e, 0xe8, 0x1e, 0xea, 0x1e, 0xea, 0x1e,
0xec, 0x1e, 0xec, 0x1e, 0xee, 0x1e, 0xee, 0x1e,
0xf0, 0x1e, 0xf0, 0x1e, 0xf2, 0x1e, 0xf2, 0x1e,
0xf4, 0x1e, 0xf4, 0x1e, 0xf6, 0x1e, 0xf6, 0x1e,
0xf8, 0x1e, 0xf8, 0x1e, 0xfa, 0x1e, 0xfb, 0x1e,
0xfc, 0x1e, 0xfd, 0x1e, 0xfe, 0x1e, 0xff, 0x1e,
0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
0x1c, 0x1f, 0x1d, 0x1f, 0x16, 0x1f, 0x17, 0x1f,
0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
0x1c, 0x1f, 0x1d, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f,
0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
0x4c, 0x1f, 0x4d, 0x1f, 0x46, 0x1f, 0x47, 0x1f,
0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
0x4c, 0x1f, 0x4d, 0x1f, 0x4e, 0x1f, 0x4f, 0x1f,
0x50, 0x1f, 0x59, 0x1f, 0x52, 0x1f, 0x5b, 0x1f,
0x54, 0x1f, 0x5d, 0x1f, 0x56, 0x1f, 0x5f, 0x1f,
0x58, 0x1f, 0x59, 0x1f, 0x5a, 0x1f, 0x5b, 0x1f,
0x5c, 0x1f, 0x5d, 0x1f, 0x5e, 0x1f, 0x5f, 0x1f,
0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
0xba, 0x1f, 0xbb, 0x1f, 0xc8, 0x1f, 0xc9, 0x1f,
0xca, 0x1f, 0xcb, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
0xf8, 0x1f, 0xf9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
0xfa, 0x1f, 0xfb, 0x1f, 0x7e, 0x1f, 0x7f, 0x1f,
0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
0xb8, 0x1f, 0xb9, 0x1f, 0xb2, 0x1f, 0xbc, 0x1f,
0xb4, 0x1f, 0xb5, 0x1f, 0xb6, 0x1f, 0xb7, 0x1f,
0xb8, 0x1f, 0xb9, 0x1f, 0xba, 0x1f, 0xbb, 0x1f,
0xbc, 0x1f, 0xbd, 0x1f, 0xbe, 0x1f, 0xbf, 0x1f,
0xc0, 0x1f, 0xc1, 0x1f, 0xc2, 0x1f, 0xc3, 0x1f,
0xc4, 0x1f, 0xc5, 0x1f, 0xc6, 0x1f, 0xc7, 0x1f,
0xc8, 0x1f, 0xc9, 0x1f, 0xca, 0x1f, 0xcb, 0x1f,
0xc3, 0x1f, 0xcd, 0x1f, 0xce, 0x1f, 0xcf, 0x1f,
0xd8, 0x1f, 0xd9, 0x1f, 0xd2, 0x1f, 0xd3, 0x1f,
0xd4, 0x1f, 0xd5, 0x1f, 0xd6, 0x1f, 0xd7, 0x1f,
0xd8, 0x1f, 0xd9, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
0xdc, 0x1f, 0xdd, 0x1f, 0xde, 0x1f, 0xdf, 0x1f,
0xe8, 0x1f, 0xe9, 0x1f, 0xe2, 0x1f, 0xe3, 0x1f,
0xe4, 0x1f, 0xec, 0x1f, 0xe6, 0x1f, 0xe7, 0x1f,
0xe8, 0x1f, 0xe9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
0xec, 0x1f, 0xed, 0x1f, 0xee, 0x1f, 0xef, 0x1f,
0xf0, 0x1f, 0xf1, 0x1f, 0xf2, 0x1f, 0xf3, 0x1f,
0xf4, 0x1f, 0xf5, 0x1f, 0xf6, 0x1f, 0xf7, 0x1f,
0xf8, 0x1f, 0xf9, 0x1f, 0xfa, 0x1f, 0xfb, 0x1f,
0xf3, 0x1f, 0xfd, 0x1f, 0xfe, 0x1f, 0xff, 0x1f,
0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20,
0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20,
0x08, 0x20, 0x09, 0x20, 0x0a, 0x20, 0x0b, 0x20,
0x0c, 0x20, 0x0d, 0x20, 0x0e, 0x20, 0x0f, 0x20,
0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20,
0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20,
0x18, 0x20, 0x19, 0x20, 0x1a, 0x20, 0x1b, 0x20,
0x1c, 0x20, 0x1d, 0x20, 0x1e, 0x20, 0x1f, 0x20,
0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20,
0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20,
0x28, 0x20, 0x29, 0x20, 0x2a, 0x20, 0x2b, 0x20,
0x2c, 0x20, 0x2d, 0x20, 0x2e, 0x20, 0x2f, 0x20,
0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20,
0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20,
0x38, 0x20, 0x39, 0x20, 0x3a, 0x20, 0x3b, 0x20,
0x3c, 0x20, 0x3d, 0x20, 0x3e, 0x20, 0x3f, 0x20,
0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20,
0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20,
0x48, 0x20, 0x49, 0x20, 0x4a, 0x20, 0x4b, 0x20,
0x4c, 0x20, 0x4d, 0x20, 0x4e, 0x20, 0x4f, 0x20,
0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20,
0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20,
0x58, 0x20, 0x59, 0x20, 0x5a, 0x20, 0x5b, 0x20,
0x5c, 0x20, 0x5d, 0x20, 0x5e, 0x20, 0x5f, 0x20,
0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20,
0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20,
0x68, 0x20, 0x69, 0x20, 0x6a, 0x20, 0x6b, 0x20,
0x6c, 0x20, 0x6d, 0x20, 0x6e, 0x20, 0x6f, 0x20,
0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20,
0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20,
0x78, 0x20, 0x79, 0x20, 0x7a, 0x20, 0x7b, 0x20,
0x7c, 0x20, 0x7d, 0x20, 0x7e, 0x20, 0x7f, 0x20,
0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20,
0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20,
0x88, 0x20, 0x89, 0x20, 0x8a, 0x20, 0x8b, 0x20,
0x8c, 0x20, 0x8d, 0x20, 0x8e, 0x20, 0x8f, 0x20,
0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20,
0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20,
0x98, 0x20, 0x99, 0x20, 0x9a, 0x20, 0x9b, 0x20,
0x9c, 0x20, 0x9d, 0x20, 0x9e, 0x20, 0x9f, 0x20,
0xa0, 0x20, 0xa1, 0x20, 0xa2, 0x20, 0xa3, 0x20,
0xa4, 0x20, 0xa5, 0x20, 0xa6, 0x20, 0xa7, 0x20,
0xa8, 0x20, 0xa9, 0x20, 0xaa, 0x20, 0xab, 0x20,
0xac, 0x20, 0xad, 0x20, 0xae, 0x20, 0xaf, 0x20,
0xb0, 0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20,
0xb4, 0x20, 0xb5, 0x20, 0xb6, 0x20, 0xb7, 0x20,
0xb8, 0x20, 0xb9, 0x20, 0xba, 0x20, 0xbb, 0x20,
0xbc, 0x20, 0xbd, 0x20, 0xbe, 0x20, 0xbf, 0x20,
0xc0, 0x20, 0xc1, 0x20, 0xc2, 0x20, 0xc3, 0x20,
0xc4, 0x20, 0xc5, 0x20, 0xc6, 0x20, 0xc7, 0x20,
0xc8, 0x20, 0xc9, 0x20, 0xca, 0x20, 0xcb, 0x20,
0xcc, 0x20, 0xcd, 0x20, 0xce, 0x20, 0xcf, 0x20,
0xd0, 0x20, 0xd1, 0x20, 0xd2, 0x20, 0xd3, 0x20,
0xd4, 0x20, 0xd5, 0x20, 0xd6, 0x20, 0xd7, 0x20,
0xd8, 0x20, 0xd9, 0x20, 0xda, 0x20, 0xdb, 0x20,
0xdc, 0x20, 0xdd, 0x20, 0xde, 0x20, 0xdf, 0x20,
0xe0, 0x20, 0xe1, 0x20, 0xe2, 0x20, 0xe3, 0x20,
0xe4, 0x20, 0xe5, 0x20, 0xe6, 0x20, 0xe7, 0x20,
0xe8, 0x20, 0xe9, 0x20, 0xea, 0x20, 0xeb, 0x20,
0xec, 0x20, 0xed, 0x20, 0xee, 0x20, 0xef, 0x20,
0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, 0x20,
0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20,
0xf8, 0x20, 0xf9, 0x20, 0xfa, 0x20, 0xfb, 0x20,
0xfc, 0x20, 0xfd, 0x20, 0xfe, 0x20, 0xff, 0x20,
0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21,
0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21,
0x08, 0x21, 0x09, 0x21, 0x0a, 0x21, 0x0b, 0x21,
0x0c, 0x21, 0x0d, 0x21, 0x0e, 0x21, 0x0f, 0x21,
0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21,
0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21,
0x18, 0x21, 0x19, 0x21, 0x1a, 0x21, 0x1b, 0x21,
0x1c, 0x21, 0x1d, 0x21, 0x1e, 0x21, 0x1f, 0x21,
0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21,
0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21,
0x28, 0x21, 0x29, 0x21, 0x2a, 0x21, 0x2b, 0x21,
0x2c, 0x21, 0x2d, 0x21, 0x2e, 0x21, 0x2f, 0x21,
0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21,
0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21,
0x38, 0x21, 0x39, 0x21, 0x3a, 0x21, 0x3b, 0x21,
0x3c, 0x21, 0x3d, 0x21, 0x3e, 0x21, 0x3f, 0x21,
0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21,
0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21,
0x48, 0x21, 0x49, 0x21, 0x4a, 0x21, 0x4b, 0x21,
0x4c, 0x21, 0x4d, 0x21, 0x32, 0x21, 0x4f, 0x21,
0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21,
0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21,
0x58, 0x21, 0x59, 0x21, 0x5a, 0x21, 0x5b, 0x21,
0x5c, 0x21, 0x5d, 0x21, 0x5e, 0x21, 0x5f, 0x21,
0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21,
0x83, 0x21, 0xff, 0xff, 0x4b, 0x03, 0xb6, 0x24,
0xb7, 0x24, 0xb8, 0x24, 0xb9, 0x24, 0xba, 0x24,
0xbb, 0x24, 0xbc, 0x24, 0xbd, 0x24, 0xbe, 0x24,
0xbf, 0x24, 0xc0, 0x24, 0xc1, 0x24, 0xc2, 0x24,
0xc3, 0x24, 0xc4, 0x24, 0xc5, 0x24, 0xc6, 0x24,
0xc7, 0x24, 0xc8, 0x24, 0xc9, 0x24, 0xca, 0x24,
0xcb, 0x24, 0xcc, 0x24, 0xcd, 0x24, 0xce, 0x24,
0xcf, 0x24, 0xff, 0xff, 0x46, 0x07, 0x00, 0x2c,
0x01, 0x2c, 0x02, 0x2c, 0x03, 0x2c, 0x04, 0x2c,
0x05, 0x2c, 0x06, 0x2c, 0x07, 0x2c, 0x08, 0x2c,
0x09, 0x2c, 0x0a, 0x2c, 0x0b, 0x2c, 0x0c, 0x2c,
0x0d, 0x2c, 0x0e, 0x2c, 0x0f, 0x2c, 0x10, 0x2c,
0x11, 0x2c, 0x12, 0x2c, 0x13, 0x2c, 0x14, 0x2c,
0x15, 0x2c, 0x16, 0x2c, 0x17, 0x2c, 0x18, 0x2c,
0x19, 0x2c, 0x1a, 0x2c, 0x1b, 0x2c, 0x1c, 0x2c,
0x1d, 0x2c, 0x1e, 0x2c, 0x1f, 0x2c, 0x20, 0x2c,
0x21, 0x2c, 0x22, 0x2c, 0x23, 0x2c, 0x24, 0x2c,
0x25, 0x2c, 0x26, 0x2c, 0x27, 0x2c, 0x28, 0x2c,
0x29, 0x2c, 0x2a, 0x2c, 0x2b, 0x2c, 0x2c, 0x2c,
0x2d, 0x2c, 0x2e, 0x2c, 0x5f, 0x2c, 0x60, 0x2c,
0x60, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x2c,
0x65, 0x2c, 0x66, 0x2c, 0x67, 0x2c, 0x67, 0x2c,
0x69, 0x2c, 0x69, 0x2c, 0x6b, 0x2c, 0x6b, 0x2c,
0x6d, 0x2c, 0x6e, 0x2c, 0x6f, 0x2c, 0x70, 0x2c,
0x71, 0x2c, 0x72, 0x2c, 0x73, 0x2c, 0x74, 0x2c,
0x75, 0x2c, 0x75, 0x2c, 0x77, 0x2c, 0x78, 0x2c,
0x79, 0x2c, 0x7a, 0x2c, 0x7b, 0x2c, 0x7c, 0x2c,
0x7d, 0x2c, 0x7e, 0x2c, 0x7f, 0x2c, 0x80, 0x2c,
0x80, 0x2c, 0x82, 0x2c, 0x82, 0x2c, 0x84, 0x2c,
0x84, 0x2c, 0x86, 0x2c, 0x86, 0x2c, 0x88, 0x2c,
0x88, 0x2c, 0x8a, 0x2c, 0x8a, 0x2c, 0x8c, 0x2c,
0x8c, 0x2c, 0x8e, 0x2c, 0x8e, 0x2c, 0x90, 0x2c,
0x90, 0x2c, 0x92, 0x2c, 0x92, 0x2c, 0x94, 0x2c,
0x94, 0x2c, 0x96, 0x2c, 0x96, 0x2c, 0x98, 0x2c,
0x98, 0x2c, 0x9a, 0x2c, 0x9a, 0x2c, 0x9c, 0x2c,
0x9c, 0x2c, 0x9e, 0x2c, 0x9e, 0x2c, 0xa0, 0x2c,
0xa0, 0x2c, 0xa2, 0x2c, 0xa2, 0x2c, 0xa4, 0x2c,
0xa4, 0x2c, 0xa6, 0x2c, 0xa6, 0x2c, 0xa8, 0x2c,
0xa8, 0x2c, 0xaa, 0x2c, 0xaa, 0x2c, 0xac, 0x2c,
0xac, 0x2c, 0xae, 0x2c, 0xae, 0x2c, 0xb0, 0x2c,
0xb0, 0x2c, 0xb2, 0x2c, 0xb2, 0x2c, 0xb4, 0x2c,
0xb4, 0x2c, 0xb6, 0x2c, 0xb6, 0x2c, 0xb8, 0x2c,
0xb8, 0x2c, 0xba, 0x2c, 0xba, 0x2c, 0xbc, 0x2c,
0xbc, 0x2c, 0xbe, 0x2c, 0xbe, 0x2c, 0xc0, 0x2c,
0xc0, 0x2c, 0xc2, 0x2c, 0xc2, 0x2c, 0xc4, 0x2c,
0xc4, 0x2c, 0xc6, 0x2c, 0xc6, 0x2c, 0xc8, 0x2c,
0xc8, 0x2c, 0xca, 0x2c, 0xca, 0x2c, 0xcc, 0x2c,
0xcc, 0x2c, 0xce, 0x2c, 0xce, 0x2c, 0xd0, 0x2c,
0xd0, 0x2c, 0xd2, 0x2c, 0xd2, 0x2c, 0xd4, 0x2c,
0xd4, 0x2c, 0xd6, 0x2c, 0xd6, 0x2c, 0xd8, 0x2c,
0xd8, 0x2c, 0xda, 0x2c, 0xda, 0x2c, 0xdc, 0x2c,
0xdc, 0x2c, 0xde, 0x2c, 0xde, 0x2c, 0xe0, 0x2c,
0xe0, 0x2c, 0xe2, 0x2c, 0xe2, 0x2c, 0xe4, 0x2c,
0xe5, 0x2c, 0xe6, 0x2c, 0xe7, 0x2c, 0xe8, 0x2c,
0xe9, 0x2c, 0xea, 0x2c, 0xeb, 0x2c, 0xec, 0x2c,
0xed, 0x2c, 0xee, 0x2c, 0xef, 0x2c, 0xf0, 0x2c,
0xf1, 0x2c, 0xf2, 0x2c, 0xf3, 0x2c, 0xf4, 0x2c,
0xf5, 0x2c, 0xf6, 0x2c, 0xf7, 0x2c, 0xf8, 0x2c,
0xf9, 0x2c, 0xfa, 0x2c, 0xfb, 0x2c, 0xfc, 0x2c,
0xfd, 0x2c, 0xfe, 0x2c, 0xff, 0x2c, 0xa0, 0x10,
0xa1, 0x10, 0xa2, 0x10, 0xa3, 0x10, 0xa4, 0x10,
0xa5, 0x10, 0xa6, 0x10, 0xa7, 0x10, 0xa8, 0x10,
0xa9, 0x10, 0xaa, 0x10, 0xab, 0x10, 0xac, 0x10,
0xad, 0x10, 0xae, 0x10, 0xaf, 0x10, 0xb0, 0x10,
0xb1, 0x10, 0xb2, 0x10, 0xb3, 0x10, 0xb4, 0x10,
0xb5, 0x10, 0xb6, 0x10, 0xb7, 0x10, 0xb8, 0x10,
0xb9, 0x10, 0xba, 0x10, 0xbb, 0x10, 0xbc, 0x10,
0xbd, 0x10, 0xbe, 0x10, 0xbf, 0x10, 0xc0, 0x10,
0xc1, 0x10, 0xc2, 0x10, 0xc3, 0x10, 0xc4, 0x10,
0xc5, 0x10, 0xff, 0xff, 0x1b, 0xd2, 0x21, 0xff,
0x22, 0xff, 0x23, 0xff, 0x24, 0xff, 0x25, 0xff,
0x26, 0xff, 0x27, 0xff, 0x28, 0xff, 0x29, 0xff,
0x2a, 0xff, 0x2b, 0xff, 0x2c, 0xff, 0x2d, 0xff,
0x2e, 0xff, 0x2f, 0xff, 0x30, 0xff, 0x31, 0xff,
0x32, 0xff, 0x33, 0xff, 0x34, 0xff, 0x35, 0xff,
0x36, 0xff, 0x37, 0xff, 0x38, 0xff, 0x39, 0xff,
0x3a, 0xff, 0x5b, 0xff, 0x5c, 0xff, 0x5d, 0xff,
0x5e, 0xff, 0x5f, 0xff, 0x60, 0xff, 0x61, 0xff,
0x62, 0xff, 0x63, 0xff, 0x64, 0xff, 0x65, 0xff,
0x66, 0xff, 0x67, 0xff, 0x68, 0xff, 0x69, 0xff,
0x6a, 0xff, 0x6b, 0xff, 0x6c, 0xff, 0x6d, 0xff,
0x6e, 0xff, 0x6f, 0xff, 0x70, 0xff, 0x71, 0xff,
0x72, 0xff, 0x73, 0xff, 0x74, 0xff, 0x75, 0xff,
0x76, 0xff, 0x77, 0xff, 0x78, 0xff, 0x79, 0xff,
0x7a, 0xff, 0x7b, 0xff, 0x7c, 0xff, 0x7d, 0xff,
0x7e, 0xff, 0x7f, 0xff, 0x80, 0xff, 0x81, 0xff,
0x82, 0xff, 0x83, 0xff, 0x84, 0xff, 0x85, 0xff,
0x86, 0xff, 0x87, 0xff, 0x88, 0xff, 0x89, 0xff,
0x8a, 0xff, 0x8b, 0xff, 0x8c, 0xff, 0x8d, 0xff,
0x8e, 0xff, 0x8f, 0xff, 0x90, 0xff, 0x91, 0xff,
0x92, 0xff, 0x93, 0xff, 0x94, 0xff, 0x95, 0xff,
0x96, 0xff, 0x97, 0xff, 0x98, 0xff, 0x99, 0xff,
0x9a, 0xff, 0x9b, 0xff, 0x9c, 0xff, 0x9d, 0xff,
0x9e, 0xff, 0x9f, 0xff, 0xa0, 0xff, 0xa1, 0xff,
0xa2, 0xff, 0xa3, 0xff, 0xa4, 0xff, 0xa5, 0xff,
0xa6, 0xff, 0xa7, 0xff, 0xa8, 0xff, 0xa9, 0xff,
0xaa, 0xff, 0xab, 0xff, 0xac, 0xff, 0xad, 0xff,
0xae, 0xff, 0xaf, 0xff, 0xb0, 0xff, 0xb1, 0xff,
0xb2, 0xff, 0xb3, 0xff, 0xb4, 0xff, 0xb5, 0xff,
0xb6, 0xff, 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xff,
0xba, 0xff, 0xbb, 0xff, 0xbc, 0xff, 0xbd, 0xff,
0xbe, 0xff, 0xbf, 0xff, 0xc0, 0xff, 0xc1, 0xff,
0xc2, 0xff, 0xc3, 0xff, 0xc4, 0xff, 0xc5, 0xff,
0xc6, 0xff, 0xc7, 0xff, 0xc8, 0xff, 0xc9, 0xff,
0xca, 0xff, 0xcb, 0xff, 0xcc, 0xff, 0xcd, 0xff,
0xce, 0xff, 0xcf, 0xff, 0xd0, 0xff, 0xd1, 0xff,
0xd2, 0xff, 0xd3, 0xff, 0xd4, 0xff, 0xd5, 0xff,
0xd6, 0xff, 0xd7, 0xff, 0xd8, 0xff, 0xd9, 0xff,
0xda, 0xff, 0xdb, 0xff, 0xdc, 0xff, 0xdd, 0xff,
0xde, 0xff, 0xdf, 0xff, 0xe0, 0xff, 0xe1, 0xff,
0xe2, 0xff, 0xe3, 0xff, 0xe4, 0xff, 0xe5, 0xff,
0xe6, 0xff, 0xe7, 0xff, 0xe8, 0xff, 0xe9, 0xff,
0xea, 0xff, 0xeb, 0xff, 0xec, 0xff, 0xed, 0xff,
0xee, 0xff, 0xef, 0xff, 0xf0, 0xff, 0xf1, 0xff,
0xf2, 0xff, 0xf3, 0xff, 0xf4, 0xff, 0xf5, 0xff,
0xf6, 0xff, 0xf7, 0xff, 0xf8, 0xff, 0xf9, 0xff,
0xfa, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xfd, 0xff,
0xfe, 0xff, 0xff, 0xff
};

@ -0,0 +1,30 @@
/*
uctc.h (30.10.10)
Upper Case Table declaration.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MKFS_UCTC_H_INCLUDED
#define MKFS_UCTC_H_INCLUDED
#include <stdint.h>
extern uint8_t upcase_table[5836];
#endif /* ifndef MKFS_UCTC_H_INCLUDED */

@ -0,0 +1,148 @@
/*
vbr.c (09.11.10)
Volume Boot Record creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vbr.h"
#include "fat.h"
#include "cbm.h"
#include "uct.h"
#include "rootdir.h"
#include <string.h>
static off_t vbr_alignment(void)
{
return get_sector_size();
}
static off_t vbr_size(void)
{
return 12 * get_sector_size();
}
static void init_sb(struct exfat_super_block* sb)
{
uint32_t clusters_max;
uint32_t fat_sectors;
clusters_max = get_volume_size() / get_cluster_size();
fat_sectors = DIV_ROUND_UP((off_t) clusters_max * sizeof(cluster_t),
get_sector_size());
memset(sb, 0, sizeof(struct exfat_super_block));
sb->jump[0] = 0xeb;
sb->jump[1] = 0x76;
sb->jump[2] = 0x90;
memcpy(sb->oem_name, "EXFAT ", sizeof(sb->oem_name));
sb->sector_start = cpu_to_le64(get_first_sector());
sb->sector_count = cpu_to_le64(get_volume_size() / get_sector_size());
sb->fat_sector_start = cpu_to_le32(
fat.get_alignment() / get_sector_size());
sb->fat_sector_count = cpu_to_le32(ROUND_UP(
le32_to_cpu(sb->fat_sector_start) + fat_sectors,
1 << get_spc_bits()) -
le32_to_cpu(sb->fat_sector_start));
sb->cluster_sector_start = cpu_to_le32(
get_position(&cbm) / get_sector_size());
sb->cluster_count = cpu_to_le32(clusters_max -
((le32_to_cpu(sb->fat_sector_start) +
le32_to_cpu(sb->fat_sector_count)) >> get_spc_bits()));
sb->rootdir_cluster = cpu_to_le32(
(get_position(&rootdir) - get_position(&cbm)) / get_cluster_size()
+ EXFAT_FIRST_DATA_CLUSTER);
sb->volume_serial = cpu_to_le32(get_volume_serial());
sb->version.major = 1;
sb->version.minor = 0;
sb->volume_state = cpu_to_le16(0);
sb->sector_bits = get_sector_bits();
sb->spc_bits = get_spc_bits();
sb->fat_count = 1;
sb->drive_no = 0x80;
sb->allocated_percent = 0;
sb->boot_signature = cpu_to_le16(0xaa55);
}
static int vbr_write(struct exfat_dev* dev)
{
struct exfat_super_block sb;
uint32_t checksum;
le32_t* sector = malloc(get_sector_size());
size_t i;
if (sector == NULL)
{
exfat_error("failed to allocate sector-sized block of memory");
return 1;
}
init_sb(&sb);
if (exfat_write(dev, &sb, sizeof(struct exfat_super_block)) < 0)
{
free(sector);
exfat_error("failed to write super block sector");
return 1;
}
checksum = exfat_vbr_start_checksum(&sb, sizeof(struct exfat_super_block));
memset(sector, 0, get_sector_size());
sector[get_sector_size() / sizeof(sector[0]) - 1] =
cpu_to_le32(0xaa550000);
for (i = 0; i < 8; i++)
{
if (exfat_write(dev, sector, get_sector_size()) < 0)
{
free(sector);
exfat_error("failed to write a sector with boot signature");
return 1;
}
checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
}
memset(sector, 0, get_sector_size());
for (i = 0; i < 2; i++)
{
if (exfat_write(dev, sector, get_sector_size()) < 0)
{
free(sector);
exfat_error("failed to write an empty sector");
return 1;
}
checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
}
for (i = 0; i < get_sector_size() / sizeof(sector[0]); i++)
sector[i] = cpu_to_le32(checksum);
if (exfat_write(dev, sector, get_sector_size()) < 0)
{
free(sector);
exfat_error("failed to write checksum sector");
return 1;
}
free(sector);
return 0;
}
const struct fs_object vbr =
{
.get_alignment = vbr_alignment,
.get_size = vbr_size,
.write = vbr_write,
};

@ -0,0 +1,30 @@
/*
vbr.h (09.11.10)
Volume Boot Record creation code.
Free exFAT implementation.
Copyright (C) 2011-2018 Andrew Nayenko
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MKFS_VBR_H_INCLUDED
#define MKFS_VBR_H_INCLUDED
#include "mkexfat.h"
extern const struct fs_object vbr;
#endif /* ifndef MKFS_VBR_H_INCLUDED */

@ -0,0 +1,30 @@
#!/bin/sh
rm -rf include
rm -rf lib
cd release
gcc -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
cd -
mkdir lib
mkdir include
mv release/*.a lib/
cp -a release/*.h include/

@ -0,0 +1,133 @@
#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

@ -0,0 +1,13 @@
#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

@ -0,0 +1,128 @@
#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

@ -0,0 +1,146 @@
#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

@ -0,0 +1,15 @@
#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

@ -0,0 +1,161 @@
#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

@ -0,0 +1,63 @@
#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

@ -0,0 +1,90 @@
#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

@ -0,0 +1,20 @@
#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

@ -0,0 +1,20 @@
#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

@ -0,0 +1,69 @@
#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

@ -0,0 +1,14 @@
#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

@ -0,0 +1,22 @@
File IO Lib API
-=-=-=-=-=-=-=-=-
void fl_init(void)
Called to initialize FAT IO library.
This should be called prior to any other functions.
void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
[Optional] File system thread safety locking functions.
For thread safe operation, you should provide lock() and unlock() functions.
Note that locking primitive used must support recursive locking, i.e lock() called within an already locked region.
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
This function is used to attach system specific disk/media access functions.
This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).
void fl_shutdown(void)
Shutdown the FAT IO library. This purges any un-saved data back to disk.

@ -0,0 +1,345 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

@ -0,0 +1,53 @@
File IO Lib Options
-=-=-=-=-=-=-=-=-=-
See defines in fat_opts.h:
FATFS_IS_LITTLE_ENDIAN [1/0]
Which endian is your system? Set to 1 for little endian, 0 for big endian.
FATFS_MAX_LONG_FILENAME [260]
By default, 260 characters (max LFN length). Increase this to support greater path depths.
FATFS_MAX_OPEN_FILES
The more files you wish to have concurrently open, the greater this number should be.
This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).
FAT_BUFFER_SECTORS
Minimum is 1, more increases performance.
This defines how many FAT sectors can be buffered per FAT_BUFFER entry.
FAT_BUFFERS
Minimum is 1, more increases performance.
This defines how many FAT buffer entries are available.
Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE
FATFS_INC_WRITE_SUPPORT
Support file write functionality.
FAT_SECTOR_SIZE
Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).
FAT_PRINTF
A define that allows the File IO library to print to console/stdout.
Provide your own printf function if printf not available.
FAT_CLUSTER_CACHE_ENTRIES
Size of cluster chain cache (can be undefined if not required).
Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
Improves access speed considerably.
FATFS_INC_LFN_SUPPORT [1/0]
Enable/Disable support for long filenames.
FATFS_DIR_LIST_SUPPORT [1/0]
Include support for directory listing.
FATFS_INC_TIME_DATE_SUPPORT [1/0]
Use time/date functions provided by time.h to update creation & modification timestamps.
FATFS_INC_FORMAT_SUPPORT
Include support for formatting disks (FAT16 only).
FAT_PRINTF_NOINC_STDIO
Disable use of printf & inclusion of stdio.h

@ -0,0 +1,24 @@
Revision History
-=-=-=-=-=-=-=-=-
v2.6.11 - Fix compilation with GCC on 64-bit machines
v2.6.10 - Added support for FAT32 format.
V2.6.9 - Added support for time & date handling.
V2.6.8 - Fixed error with FSINFO sector write.
V2.6.7 - Added fgets().
Fixed C warnings, removed dependancy on some string.h functions.
V2.6.6 Massive read + write performance improvements.
V2.6.5 Bug fixes for big endian systems.
V2.6.4 Further bug fixes and performance improvements for write operations.
V2.6.3 Peformance improvements, FAT16 formatting support. Various bug fixes.
V2.6 - Basic support for FAT16 added (18-04-10).
V2.5 - Code cleaned up. Many bugs fixed. Thread safety functions added.
V2.x - Write support added as well as better stdio like API.
V1.0 - Rewrite of all code to enable multiple files to be opened and provides a
better file API.
Also better string matching, and generally better C code than origonal
version.
V0.1c - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping
the drive reads from addressing a sector that is out of range.
V0.1b - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32
access.
V0.1a - First release (27/12/03); fopen(), fgetc() unbuffered reads.

@ -0,0 +1,10 @@
FAT File IO Library License
-=-=-=-=-=-=-=-=-=-=-=-=-=-
This versions license: GPL
If you include GPL software in your project, you must release the source code of that project too.
If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.
Email: admin@ultra-embedded.com

@ -0,0 +1,40 @@
Media Access API
-=-=-=-=-=-=-=-=-
int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)
Params:
Sector: 32-bit sector number
Buffer: Target buffer to read n sectors of data into.
Sector_count: Number of sectors to read.
Return:
int, 1 = success, 0 = failure.
Description:
Application/target specific disk/media read function.
Sector number (sectors are usually 512 byte pages) to read.
Media Write API
int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)
Params:
Sector: 32-bit sector number
Buffer: Target buffer to write n sectors of data from.
Sector_count: Number of sectors to write.
Return:
int, 1 = success, 0 = failure.
Description:
Application/target specific disk/media write function.
Sector number (sectors are usually 512 byte pages) to write to.
File IO Library Linkage
Use the following API to attach the media IO functions to the File IO library.
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)

@ -0,0 +1,87 @@
#include <stdio.h>
#include "fat_filelib.h"
int media_init()
{
// ...
return 1;
}
int media_read(unsigned long sector, unsigned char *buffer, unsigned long sector_count)
{
unsigned long i;
for (i=0;i<sector_count;i++)
{
// ...
// Add platform specific sector (512 bytes) read code here
//..
sector ++;
buffer += 512;
}
return 1;
}
int media_write(unsigned long sector, unsigned char *buffer, unsigned long sector_count)
{
unsigned long i;
for (i=0;i<sector_count;i++)
{
// ...
// Add platform specific sector (512 bytes) write code here
//..
sector ++;
buffer += 512;
}
return 1;
}
void main()
{
FL_FILE *file;
// Initialise media
media_init();
// Initialise File IO Library
fl_init();
// Attach media access functions to library
if (fl_attach_media(media_read, media_write) != FAT_INIT_OK)
{
printf("ERROR: Media attach failed\n");
return;
}
// List root directory
fl_listdirectory("/");
// Create File
file = fl_fopen("/file.bin", "w");
if (file)
{
// Write some data
unsigned char data[] = { 1, 2, 3, 4 };
if (fl_fwrite(data, 1, sizeof(data), file) != sizeof(data))
printf("ERROR: Write file failed\n");
}
else
printf("ERROR: Create file failed\n");
// Close file
fl_fclose(file);
// Delete File
if (fl_remove("/file.bin") < 0)
printf("ERROR: Delete file failed\n");
// List root directory
fl_listdirectory("/");
fl_shutdown();
}

@ -0,0 +1,904 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library 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 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#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;
// 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;
// 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

@ -0,0 +1,133 @@
#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

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library 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 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#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;
}

@ -0,0 +1,13 @@
#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

@ -0,0 +1,128 @@
#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

File diff suppressed because it is too large Load Diff

@ -0,0 +1,146 @@
#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

@ -0,0 +1,532 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library 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 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#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*/

@ -0,0 +1,15 @@
#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

@ -0,0 +1,161 @@
#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

@ -0,0 +1,505 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library 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 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#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

@ -0,0 +1,63 @@
#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

@ -0,0 +1,90 @@
#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

@ -0,0 +1,514 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library 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 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#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

@ -0,0 +1,20 @@
#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

@ -0,0 +1,478 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library 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 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#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;
}

@ -0,0 +1,20 @@
#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

@ -0,0 +1,69 @@
#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

@ -0,0 +1,373 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library 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 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#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

@ -0,0 +1,14 @@
#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

@ -0,0 +1,46 @@
#!/bin/sh
# https://github.com/lammertb/libhttp/archive/v1.8.tar.gz
# rm -rf include
# rm -rf lib
# mkdir include
# mkdir lib
# rm -rf libhttp-1.8
# tar xf libhttp-1.8.tar.gz
# cd libhttp-1.8
# cp -a include/civetweb.h ../include/
# cd ..
# rm -rf libhttp-1.8
# tar xf libhttp-1.8.tar.gz
# cd libhttp-1.8
# make lib COPT="-DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
# cp -a libcivetweb.a ../lib/libcivetweb_64.a
# cd ..
# rm -rf libhttp-1.8
# tar xf libhttp-1.8.tar.gz
# cd libhttp-1.8
# make lib COPT="-m32 -DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
# cp -a libcivetweb.a ../lib/libcivetweb_32.a
# cd ..
# rm -rf libhttp-1.8
# tar xf libhttp-1.8.tar.gz
# cd libhttp-1.8
# make lib CC=aarch64-linux-gnu-gcc COPT="-DNDEBUG -DNO_CGI -DNO_CACHING -DNO_SSL -DSQLITE_DISABLE_LFS -DSSL_ALREADY_INITIALIZED"
# cp -a libcivetweb.a ../lib/libcivetweb_aa64.a
# cd ..
# rm -rf libhttp-1.8

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,793 @@
/* Copyright (c) 2016 the Civetweb developers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
static int
url_encoded_field_found(const struct mg_connection *conn,
const char *key,
size_t key_len,
const char *filename,
size_t filename_len,
char *path,
size_t path_len,
struct mg_form_data_handler *fdh)
{
char key_dec[1024];
char filename_dec[1024];
int key_dec_len;
int filename_dec_len;
int ret;
key_dec_len =
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
return FORM_FIELD_STORAGE_SKIP;
}
if (filename) {
filename_dec_len = mg_url_decode(filename,
(int)filename_len,
filename_dec,
(int)sizeof(filename_dec),
1);
if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
|| (filename_dec_len < 0)) {
/* Log error message and skip this field. */
mg_cry(conn, "%s: Cannot decode filename", __func__);
return FORM_FIELD_STORAGE_SKIP;
}
} else {
filename_dec[0] = 0;
}
ret =
fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
if (fdh->field_get == NULL) {
mg_cry(conn, "%s: Function \"Get\" not available", __func__);
return FORM_FIELD_STORAGE_SKIP;
}
}
if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
if (fdh->field_store == NULL) {
mg_cry(conn, "%s: Function \"Store\" not available", __func__);
return FORM_FIELD_STORAGE_SKIP;
}
}
return ret;
}
static int
url_encoded_field_get(const struct mg_connection *conn,
const char *key,
size_t key_len,
const char *value,
size_t value_len,
struct mg_form_data_handler *fdh)
{
char key_dec[1024];
char *value_dec = mg_malloc(value_len + 1);
int value_dec_len;
if (!value_dec) {
/* Log error message and stop parsing the form data. */
mg_cry(conn,
"%s: Not enough memory (required: %lu)",
__func__,
(unsigned long)(value_len + 1));
return FORM_FIELD_STORAGE_ABORT;
}
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
value_dec_len =
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
return fdh->field_get(key_dec,
value_dec,
(size_t)value_dec_len,
fdh->user_data);
}
static int
field_stored(const struct mg_connection *conn,
const char *path,
long long file_size,
struct mg_form_data_handler *fdh)
{
/* Equivalent to "upload" callback of "mg_upload". */
(void)conn; /* we do not need mg_cry here, so conn is currently unused */
return fdh->field_store(path, file_size, fdh->user_data);
}
static const char *
search_boundary(const char *buf,
size_t buf_len,
const char *boundary,
size_t boundary_len)
{
/* We must do a binary search here, not a string search, since the buffer
* may contain '\x00' bytes, if binary data is transferred. */
int clen = (int)buf_len - (int)boundary_len - 4;
int i;
for (i = 0; i <= clen; i++) {
if (!memcmp(buf + i, "\r\n--", 4)) {
if (!memcmp(buf + i + 4, boundary, boundary_len)) {
return buf + i;
}
}
}
return NULL;
}
int
mg_handle_form_request(struct mg_connection *conn,
struct mg_form_data_handler *fdh)
{
const char *content_type;
char path[512];
char buf[1024];
int field_storage;
int buf_fill = 0;
int r;
int field_count = 0;
struct file fstore = STRUCT_FILE_INITIALIZER;
int64_t file_size = 0; /* init here, to a avoid a false positive
"uninitialized variable used" warning */
int has_body_data =
(conn->request_info.content_length > 0) || (conn->is_chunked);
/* There are three ways to encode data from a HTML form:
* 1) method: GET (default)
* The form data is in the HTTP query string.
* 2) method: POST, enctype: "application/x-www-form-urlencoded"
* The form data is in the request body.
* The body is url encoded (the default encoding for POST).
* 3) method: POST, enctype: "multipart/form-data".
* The form data is in the request body of a multipart message.
* This is the typical way to handle file upload from a form.
*/
if (!has_body_data) {
const char *data;
if (strcmp(conn->request_info.request_method, "GET")) {
/* No body data, but not a GET request.
* This is not a valid form request. */
return -1;
}
/* GET request: form data is in the query string. */
/* The entire data has already been loaded, so there is no nead to
* call mg_read. We just need to split the query string into key-value
* pairs. */
data = conn->request_info.query_string;
if (!data) {
/* No query string. */
return -1;
}
/* Split data in a=1&b=xy&c=3&c=4 ... */
while (*data) {
const char *val = strchr(data, '=');
const char *next;
ptrdiff_t keylen, vallen;
if (!val) {
break;
}
keylen = val - data;
/* In every "field_found" callback we ask what to do with the
* data ("field_storage"). This could be:
* FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
* FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
* callback function
* FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
* FORM_FIELD_STORAGE_READ (3) ... let the user read the data
* (for parsing long data on the fly)
* (currently not implemented)
* FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
*/
memset(path, 0, sizeof(path));
field_count++;
field_storage = url_encoded_field_found(conn,
data,
(size_t)keylen,
NULL,
0,
path,
sizeof(path) - 1,
fdh);
val++;
next = strchr(val, '&');
if (next) {
vallen = next - val;
next++;
} else {
vallen = (ptrdiff_t)strlen(val);
next = val + vallen;
}
if (field_storage == FORM_FIELD_STORAGE_GET) {
/* Call callback */
url_encoded_field_get(
conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
/* Store the content to a file */
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
fstore.fp = NULL;
}
file_size = 0;
if (fstore.fp != NULL) {
size_t n =
(size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
if (fstore.fp) {
r = fclose(fstore.fp);
if (r == 0) {
/* stored successfully */
field_stored(conn, path, file_size, fdh);
} else {
mg_cry(conn,
"%s: Error saving file %s",
__func__,
path);
remove_bad_file(conn, path);
}
fstore.fp = NULL;
}
} else {
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
}
}
/* if (field_storage == FORM_FIELD_STORAGE_READ) { */
/* The idea of "field_storage=read" is to let the API user read
* data chunk by chunk and to some data processing on the fly.
* This should avoid the need to store data in the server:
* It should neither be stored in memory, like
* "field_storage=get" does, nor in a file like
* "field_storage=store".
* However, for a "GET" request this does not make any much
* sense, since the data is already stored in memory, as it is
* part of the query string.
*/
/* } */
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
== FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
break;
}
/* Proceed to next entry */
data = next;
}
return field_count;
}
content_type = mg_get_header(conn, "Content-Type");
if (!content_type
|| !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
|| !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
/* The form data is in the request body data, encoded in key/value
* pairs. */
int all_data_read = 0;
/* Read body data and split it in keys and values.
* The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
* Here we use "POST", and read the data from the request body.
* The data read on the fly, so it is not required to buffer the
* entire request in memory before processing it. */
for (;;) {
const char *val;
const char *next;
ptrdiff_t keylen, vallen;
ptrdiff_t used;
int end_of_key_value_pair_found = 0;
int get_block;
if ((size_t)buf_fill < (sizeof(buf) - 1)) {
size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
r = mg_read(conn, buf + (size_t)buf_fill, to_read);
if (r < 0) {
/* read error */
return -1;
}
if (r != (int)to_read) {
/* TODO: Create a function to get "all_data_read" from
* the conn object. All data is read if the Content-Length
* has been reached, or if chunked encoding is used and
* the end marker has been read, or if the connection has
* been closed. */
all_data_read = 1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
break;
}
}
val = strchr(buf, '=');
if (!val) {
break;
}
keylen = val - buf;
val++;
/* Call callback */
memset(path, 0, sizeof(path));
field_count++;
field_storage = url_encoded_field_found(conn,
buf,
(size_t)keylen,
NULL,
0,
path,
sizeof(path) - 1,
fdh);
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
== FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
break;
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
fstore.fp = NULL;
}
file_size = 0;
if (!fstore.fp) {
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
}
}
get_block = 0;
/* Loop to read values larger than sizeof(buf)-keylen-2 */
do {
next = strchr(val, '&');
if (next) {
vallen = next - val;
next++;
end_of_key_value_pair_found = 1;
} else {
vallen = (ptrdiff_t)strlen(val);
next = val + vallen;
}
if (field_storage == FORM_FIELD_STORAGE_GET) {
#if 0
if (!end_of_key_value_pair_found && !all_data_read) {
/* This callback will deliver partial contents */
}
#else
(void)all_data_read; /* avoid warning */
#endif
/* Call callback */
url_encoded_field_get(conn,
((get_block > 0) ? NULL : buf),
((get_block > 0) ? 0
: (size_t)keylen),
val,
(size_t)vallen,
fdh);
get_block++;
}
if (fstore.fp) {
size_t n =
(size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
}
if (!end_of_key_value_pair_found) {
used = next - buf;
memmove(buf,
buf + (size_t)used,
sizeof(buf) - (size_t)used);
buf_fill -= (int)used;
if ((size_t)buf_fill < (sizeof(buf) - 1)) {
size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
r = mg_read(conn, buf + (size_t)buf_fill, to_read);
if (r < 0) {
/* read error */
return -1;
}
if (r != (int)to_read) {
/* TODO: Create a function to get "all_data_read"
* from the conn object. All data is read if the
* Content-Length has been reached, or if chunked
* encoding is used and the end marker has been
* read, or if the connection has been closed. */
all_data_read = 1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
break;
}
val = buf;
}
}
} while (!end_of_key_value_pair_found);
if (fstore.fp) {
r = fclose(fstore.fp);
if (r == 0) {
/* stored successfully */
field_stored(conn, path, file_size, fdh);
} else {
mg_cry(conn, "%s: Error saving file %s", __func__, path);
remove_bad_file(conn, path);
}
fstore.fp = NULL;
}
/* Proceed to next entry */
used = next - buf;
memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
buf_fill -= (int)used;
}
return field_count;
}
if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
/* The form data is in the request body data, encoded as multipart
* content (see https://www.ietf.org/rfc/rfc1867.txt,
* https://www.ietf.org/rfc/rfc2388.txt). */
const char *boundary;
size_t bl;
ptrdiff_t used;
struct mg_request_info part_header;
char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
const char *content_disp;
const char *next;
memset(&part_header, 0, sizeof(part_header));
/* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
bl = 20;
while (content_type[bl] == ' ') {
bl++;
}
/* There has to be a BOUNDARY definition in the Content-Type header */
if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
/* Malformed request */
return -1;
}
boundary = content_type + bl + 9;
bl = strlen(boundary);
if (bl + 800 > sizeof(buf)) {
/* Sanity check: The algorithm can not work if bl >= sizeof(buf),
* and it will not work effectively, if the buf is only a few byte
* larger than bl, or it buf can not hold the multipart header
* plus the boundary.
* Check some reasonable number here, that should be fulfilled by
* any reasonable request from every browser. If it is not
* fulfilled, it might be a hand-made request, intended to
* interfere with the algorithm. */
return -1;
}
for (;;) {
size_t towrite, n;
int get_block;
r = mg_read(conn,
buf + (size_t)buf_fill,
sizeof(buf) - 1 - (size_t)buf_fill);
if (r < 0) {
/* read error */
return -1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
/* No data */
return -1;
}
if (buf[0] != '-' || buf[1] != '-') {
/* Malformed request */
return -1;
}
if (strncmp(buf + 2, boundary, bl)) {
/* Malformed request */
return -1;
}
if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
/* Every part must end with \r\n, if there is another part.
* The end of the request has an extra -- */
if (((size_t)buf_fill != (size_t)(bl + 6))
|| (strncmp(buf + bl + 2, "--\r\n", 4))) {
/* Malformed request */
return -1;
}
/* End of the request */
break;
}
/* Next, we need to get the part header: Read until \r\n\r\n */
hbuf = buf + bl + 4;
hend = strstr(hbuf, "\r\n\r\n");
if (!hend) {
/* Malformed request */
return -1;
}
parse_http_headers(&hbuf, &part_header);
if ((hend + 2) != hbuf) {
/* Malformed request */
return -1;
}
/* Skip \r\n\r\n */
hend += 4;
/* According to the RFC, every part has to have a header field like:
* Content-Disposition: form-data; name="..." */
content_disp = get_header(&part_header, "Content-Disposition");
if (!content_disp) {
/* Malformed request */
return -1;
}
/* Get the mandatory name="..." part of the Content-Disposition
* header. */
nbeg = strstr(content_disp, "name=\"");
if (!nbeg) {
/* Malformed request */
return -1;
}
nbeg += 6;
nend = strchr(nbeg, '\"');
if (!nend) {
/* Malformed request */
return -1;
}
/* Get the optional filename="..." part of the Content-Disposition
* header. */
fbeg = strstr(content_disp, "filename=\"");
if (fbeg) {
fbeg += 10;
fend = strchr(fbeg, '\"');
if (!fend) {
/* Malformed request (the filename field is optional, but if
* it exists, it needs to be terminated correctly). */
return -1;
}
/* TODO: check Content-Type */
/* Content-Type: application/octet-stream */
} else {
fend = fbeg;
}
memset(path, 0, sizeof(path));
field_count++;
field_storage = url_encoded_field_found(conn,
nbeg,
(size_t)(nend - nbeg),
fbeg,
(size_t)(fend - fbeg),
path,
sizeof(path) - 1,
fdh);
/* If the boundary is already in the buffer, get the address,
* otherwise next will be NULL. */
next = search_boundary(hbuf,
(size_t)((buf - hbuf) + buf_fill),
boundary,
bl);
if (field_storage == FORM_FIELD_STORAGE_STORE) {
/* Store the content to a file */
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
fstore.fp = NULL;
}
file_size = 0;
if (!fstore.fp) {
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
}
}
get_block = 0;
while (!next) {
/* Set "towrite" to the number of bytes available
* in the buffer */
towrite = (size_t)(buf - hend + buf_fill);
/* Subtract the boundary length, to deal with
* cases the boundary is only partially stored
* in the buffer. */
towrite -= bl + 4;
if (field_storage == FORM_FIELD_STORAGE_GET) {
url_encoded_field_get(conn,
((get_block > 0) ? NULL : nbeg),
((get_block > 0)
? 0
: (size_t)(nend - nbeg)),
hend,
towrite,
fdh);
get_block++;
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (fstore.fp) {
/* Store the content of the buffer. */
n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
if ((n != towrite) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
}
}
memmove(buf, hend + towrite, bl + 4);
buf_fill = (int)(bl + 4);
hend = buf;
/* Read new data */
r = mg_read(conn,
buf + (size_t)buf_fill,
sizeof(buf) - 1 - (size_t)buf_fill);
if (r < 0) {
/* read error */
return -1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
/* No data */
return -1;
}
/* Find boundary */
next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
}
towrite = (size_t)(next - hend);
if (field_storage == FORM_FIELD_STORAGE_GET) {
/* Call callback */
url_encoded_field_get(conn,
((get_block > 0) ? NULL : nbeg),
((get_block > 0) ? 0
: (size_t)(nend - nbeg)),
hend,
towrite,
fdh);
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (fstore.fp) {
n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
if ((n != towrite) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
}
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (fstore.fp) {
r = fclose(fstore.fp);
if (r == 0) {
/* stored successfully */
field_stored(conn, path, file_size, fdh);
} else {
mg_cry(conn,
"%s: Error saving file %s",
__func__,
path);
remove_bad_file(conn, path);
}
fstore.fp = NULL;
}
}
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
== FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
break;
}
/* Remove from the buffer */
used = next - buf + 2;
memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
buf_fill -= (int)used;
}
/* All parts handled */
return field_count;
}
/* Unknown Content-Type */
return -1;
}

@ -0,0 +1,468 @@
/*
* This an amalgamation of md5.c and md5.h into a single file
* with all static declaration to reduce linker conflicts
* in Civetweb.
*
* The MD5_STATIC declaration was added to facilitate static
* inclusion.
* No Face Press, LLC
*/
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.h is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version.
*/
#ifndef md5_INCLUDED
#define md5_INCLUDED
/*
* This package supports both compile-time and run-time determination of CPU
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
* defined as non-zero, the code will be compiled to run only on big-endian
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
* run on either big- or little-endian CPUs, but will run slightly less
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
*/
typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
#ifdef __cplusplus
extern "C" {
#endif
/* Initialize the algorithm. */
MD5_STATIC void md5_init(md5_state_t *pms);
/* Append a string to the message. */
MD5_STATIC void
md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes);
/* Finish the message and return the digest. */
MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
#ifdef __cplusplus
} /* end extern "C" */
#endif
#endif /* md5_INCLUDED */
/*
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
either statically or dynamically; added missing #include <string.h>
in library.
2002-03-11 lpd Corrected argument list for main(), and added int return
type, in test program and T value program.
2002-02-21 lpd Added missing #include <stdio.h> in test program.
2000-07-03 lpd Patched to eliminate warnings about "constant is
unsigned in ANSI C, signed in traditional"; made test program
self-checking.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#ifndef MD5_STATIC
#include <string.h>
#endif
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#ifdef ARCH_IS_BIG_ENDIAN
#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
#else
#define BYTE_ORDER (0)
#endif
#define T_MASK ((md5_word_t)~0)
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
#define T3 (0x242070db)
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
#define T6 (0x4787c62a)
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
#define T9 (0x698098d8)
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
#define T13 (0x6b901122)
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
#define T16 (0x49b40821)
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
#define T19 (0x265e5a51)
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
#define T22 (0x02441453)
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
#define T25 (0x21e1cde6)
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
#define T28 (0x455a14ed)
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
#define T31 (0x676f02d9)
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
#define T35 (0x6d9d6122)
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
#define T38 (0x4bdecfa9)
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
#define T41 (0x289b7ec6)
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
#define T44 (0x04881d05)
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
#define T47 (0x1fa27cf8)
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
#define T50 (0x432aff97)
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
#define T53 (0x655b59c3)
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
#define T57 (0x6fa87e4f)
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
#define T60 (0x4e0811a1)
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
#define T63 (0x2ad7d2bb)
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
static void
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
{
md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2],
d = pms->abcd[3];
md5_word_t t;
#if BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */
md5_word_t X[16];
#else
/* Define storage for little-endian or both types of CPUs. */
md5_word_t xbuf[16];
const md5_word_t *X;
#endif
{
#if BYTE_ORDER == 0
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static const int w = 1;
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
#endif
#if BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (const md5_byte_t *)0) & 3)) {
/* data are properly aligned, a direct assignment is possible */
/* cast through a (void *) should avoid a compiler warning,
see
https://github.com/bel2125/civetweb/issues/94#issuecomment-98112861
*/
X = (const md5_word_t *)(const void *)data;
} else {
/* not aligned */
memcpy(xbuf, data, 64);
X = xbuf;
}
}
#endif
#if BYTE_ORDER == 0
else /* dynamic big-endian */
#endif
#if BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t *xp = data;
int i;
#if BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */
#else
#define xbuf X /* (static only) */
#endif
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = (md5_word_t)(xp[0]) + (md5_word_t)(xp[1] << 8)
+ (md5_word_t)(xp[2] << 16)
+ (md5_word_t)(xp[3] << 24);
}
#endif
}
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti) \
t = a + F(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti) \
t = a + G(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti) \
t = a + H(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti) \
t = a + I(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
MD5_STATIC void
md5_init(md5_state_t *pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
pms->abcd[3] = 0x10325476;
}
MD5_STATIC void
md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
{
const md5_byte_t *p = data;
size_t left = nbytes;
size_t offset = (pms->count[0] >> 3) & 63;
md5_word_t nbits = (md5_word_t)(nbytes << 3);
if (nbytes <= 0)
return;
/* Update the message length. */
pms->count[1] += (md5_word_t)(nbytes >> 29);
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy(pms->buf, p, left);
}
MD5_STATIC void
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
{
static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
md5_byte_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
md5_append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}

@ -0,0 +1,250 @@
/* This file is part of the CivetWeb web server.
* See https://github.com/civetweb/civetweb/
* (C) 2015 by the CivetWeb authors, MIT license.
*/
#include "duktape.h"
/* TODO: the mg context should be added to duktape as well */
/* Alternative: redefine a new, clean API from scratch (instead of using mg),
* or at least do not add problematic functions. */
/* For evaluation purposes, currently only "send" is supported.
* All other ~50 functions will be added later. */
/* Note: This is only experimental support, so the API may still change. */
static const char *civetweb_conn_id = "\xFF"
"civetweb_conn";
static const char *civetweb_ctx_id = "\xFF"
"civetweb_ctx";
static void *
mg_duk_mem_alloc(void *udata, duk_size_t size)
{
return mg_malloc(size);
}
static void *
mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize)
{
return mg_realloc(ptr, newsize);
}
static void
mg_duk_mem_free(void *udata, void *ptr)
{
mg_free(ptr);
}
static void
mg_duk_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)
{
/* Script is called "protected" (duk_peval_file), so script errors should
* never yield in a call to this function. Maybe calls prior to executing
* the script could raise a fatal error. */
struct mg_connection *conn;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
mg_cry(conn, "%s", msg);
}
static duk_ret_t
duk_itf_write(duk_context *ctx)
{
struct mg_connection *conn;
duk_double_t ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
/*
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
*/
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_write(conn, val, len);
duk_push_number(ctx, ret);
return 1;
}
static duk_ret_t
duk_itf_read(duk_context *ctx)
{
struct mg_connection *conn;
char buf[1024];
int len;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
len = mg_read(conn, buf, sizeof(buf));
duk_push_lstring(ctx, buf, len);
return 1;
}
static duk_ret_t
duk_itf_getoption(duk_context *ctx)
{
struct mg_context *cv_ctx;
const char *ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_ctx_id);
cv_ctx = (struct mg_context *)duk_to_pointer(ctx, -1);
if (!cv_ctx) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_get_option(cv_ctx, val);
if (ret) {
duk_push_string(ctx, ret);
} else {
duk_push_null(ctx);
}
return 1;
}
static void
mg_exec_duktape_script(struct mg_connection *conn, const char *script_name)
{
int i;
duk_context *ctx = NULL;
conn->must_close = 1;
/* Create Duktape interpreter state */
ctx = duk_create_heap(mg_duk_mem_alloc,
mg_duk_mem_realloc,
mg_duk_mem_free,
NULL,
mg_duk_fatal_handler);
if (!ctx) {
mg_cry(conn, "Failed to create a Duktape heap.");
goto exec_duktape_finished;
}
/* Add "conn" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_c_function(ctx, duk_itf_write, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "write"); /* add function conn.write */
duk_push_c_function(ctx, duk_itf_read, 0 /* 0 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "read"); /* add function conn.read */
duk_push_string(ctx, conn->request_info.request_method);
duk_put_prop_string(ctx, -2, "request_method"); /* add string conn.r... */
duk_push_string(ctx, conn->request_info.request_uri);
duk_put_prop_string(ctx, -2, "request_uri");
duk_push_string(ctx, conn->request_info.local_uri);
duk_put_prop_string(ctx, -2, "uri");
duk_push_string(ctx, conn->request_info.http_version);
duk_put_prop_string(ctx, -2, "http_version");
duk_push_string(ctx, conn->request_info.query_string);
duk_put_prop_string(ctx, -2, "query_string");
duk_push_string(ctx, conn->request_info.remote_addr);
duk_put_prop_string(ctx, -2, "remote_addr");
duk_push_int(ctx, conn->request_info.remote_port);
duk_put_prop_string(ctx, -2, "remote_port");
duk_push_int(ctx, ntohs(conn->client.lsa.sin.sin_port));
duk_put_prop_string(ctx, -2, "server_port");
duk_push_object(ctx); /* subfolder "conn.http_headers" */
for (i = 0; i < conn->request_info.num_headers; i++) {
duk_push_string(ctx, conn->request_info.http_headers[i].value);
duk_put_prop_string(ctx, -2, conn->request_info.http_headers[i].name);
}
duk_put_prop_string(ctx, -2, "http_headers");
duk_put_prop_string(ctx, -2, "conn"); /* call the table "conn" */
/* Add "civetweb" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_string(ctx, CIVETWEB_VERSION);
duk_put_prop_string(ctx, -2, "version");
duk_push_string(ctx, script_name);
duk_put_prop_string(ctx, -2, "script_name");
if (conn->ctx != NULL) {
duk_push_c_function(ctx, duk_itf_getoption, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)(conn->ctx));
duk_put_prop_string(ctx, -2, civetweb_ctx_id);
duk_put_prop_string(ctx, -2, "getoption"); /* add function conn.write */
if (conn->ctx->systemName != NULL) {
duk_push_string(ctx, conn->ctx->systemName);
duk_put_prop_string(ctx, -2, "system");
}
}
duk_put_prop_string(ctx, -2, "civetweb"); /* call the table "civetweb" */
duk_push_global_stash(ctx);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
if (duk_peval_file(ctx, script_name) != 0) {
mg_cry(conn, "%s", duk_safe_to_string(ctx, -1));
goto exec_duktape_finished;
}
duk_pop(ctx); /* ignore result */
exec_duktape_finished:
duk_destroy_heap(ctx);
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,150 @@
#if !defined(MAX_TIMERS)
#define MAX_TIMERS MAX_WORKER_THREADS
#endif
typedef int (*taction)(void *arg);
struct ttimer {
double time;
double period;
taction action;
void *arg;
};
struct ttimers {
pthread_t threadid; /* Timer thread ID */
pthread_mutex_t mutex; /* Protects timer lists */
struct ttimer timers[MAX_TIMERS]; /* List of timers */
unsigned timer_count; /* Current size of timer list */
};
static int
timer_add(struct mg_context *ctx,
double next_time,
double period,
int is_relative,
taction action,
void *arg)
{
unsigned u, v;
int error = 0;
struct timespec now;
if (ctx->stop_flag) {
return 0;
}
if (is_relative) {
clock_gettime(CLOCK_MONOTONIC, &now);
next_time += now.tv_sec;
next_time += now.tv_nsec * 1.0E-9;
}
pthread_mutex_lock(&ctx->timers->mutex);
if (ctx->timers->timer_count == MAX_TIMERS) {
error = 1;
} else {
for (u = 0; u < ctx->timers->timer_count; u++) {
if (ctx->timers->timers[u].time < next_time) {
for (v = ctx->timers->timer_count; v > u; v--) {
ctx->timers->timers[v] = ctx->timers->timers[v - 1];
}
break;
}
}
ctx->timers->timers[u].time = next_time;
ctx->timers->timers[u].period = period;
ctx->timers->timers[u].action = action;
ctx->timers->timers[u].arg = arg;
ctx->timers->timer_count++;
}
pthread_mutex_unlock(&ctx->timers->mutex);
return error;
}
static void
timer_thread_run(void *thread_func_param)
{
struct mg_context *ctx = (struct mg_context *)thread_func_param;
struct timespec now;
double d;
unsigned u;
int re_schedule;
struct ttimer t;
mg_set_thread_name("timer");
if (ctx->callbacks.init_thread) {
/* Timer thread */
ctx->callbacks.init_thread(ctx, 2);
}
#if defined(HAVE_CLOCK_NANOSLEEP) /* Linux with librt */
/* TODO */
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &request, &request)
== EINTR) { /*nop*/
;
}
#else
clock_gettime(CLOCK_MONOTONIC, &now);
d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
while (ctx->stop_flag == 0) {
pthread_mutex_lock(&ctx->timers->mutex);
if (ctx->timers->timer_count > 0 && d >= ctx->timers->timers[0].time) {
t = ctx->timers->timers[0];
for (u = 1; u < ctx->timers->timer_count; u++) {
ctx->timers->timers[u - 1] = ctx->timers->timers[u];
}
ctx->timers->timer_count--;
pthread_mutex_unlock(&ctx->timers->mutex);
re_schedule = t.action(t.arg);
if (re_schedule && (t.period > 0)) {
timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
}
continue;
} else {
pthread_mutex_unlock(&ctx->timers->mutex);
}
mg_sleep(1);
clock_gettime(CLOCK_MONOTONIC, &now);
d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
}
#endif
}
#ifdef _WIN32
static unsigned __stdcall timer_thread(void *thread_func_param)
{
timer_thread_run(thread_func_param);
return 0;
}
#else
static void *
timer_thread(void *thread_func_param)
{
timer_thread_run(thread_func_param);
return NULL;
}
#endif /* _WIN32 */
static int
timers_init(struct mg_context *ctx)
{
ctx->timers = (struct ttimers *)mg_calloc(sizeof(struct ttimers), 1);
(void)pthread_mutex_init(&ctx->timers->mutex, NULL);
/* Start timer thread */
mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid);
return 0;
}
static void
timers_exit(struct mg_context *ctx)
{
if (ctx->timers) {
(void)pthread_mutex_destroy(&ctx->timers->mutex);
mg_free(ctx->timers);
}
}

@ -0,0 +1,10 @@
Licensing of XZ Embedded
========================
All the files in this package have been written by Lasse Collin
and/or Igor Pavlov. All these files have been put into the
public domain. You can do whatever you want with these files.
As usual, this software is provided "as is", without any warranty.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save