mirror of https://github.com/ventoy/Ventoy.git
Experimental Linux GUI based on web browser
This commit is contained in:
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
|
||||
|
Binary file not shown.
|
@ -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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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 @@
|
|||
2.6.11
|
|
@ -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);
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -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…
Reference in New Issue