diff --git a/INSTALL/README b/INSTALL/README index 61f28d56..2535f490 100644 --- a/INSTALL/README +++ b/INSTALL/README @@ -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 diff --git a/INSTALL/VentoyWeb.sh b/INSTALL/VentoyWeb.sh new file mode 100644 index 00000000..3bc9469e --- /dev/null +++ b/INSTALL/VentoyWeb.sh @@ -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 diff --git a/INSTALL/ventoy_pack.sh b/INSTALL/ventoy_pack.sh index e9cfe9e9..8c2655c3 100644 --- a/INSTALL/ventoy_pack.sh +++ b/INSTALL/ventoy_pack.sh @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_crc32.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_crc32.c new file mode 100644 index 00000000..9f80c61b --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_crc32.c @@ -0,0 +1,299 @@ +/****************************************************************************** + * crc32.c ---- ventoy crc32 + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include + +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; +} + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_define.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_define.h new file mode 100644 index 00000000..14b2cf41 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_define.h @@ -0,0 +1,192 @@ +/****************************************************************************** + * ventoy_define.h + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#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__ */ + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.c new file mode 100644 index 00000000..4d0871b8 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.c @@ -0,0 +1,735 @@ +/****************************************************************************** + * ventoy_disk.c ---- ventoy disk + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.h new file mode 100644 index 00000000..50618dce --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_disk.h @@ -0,0 +1,145 @@ +/****************************************************************************** + * ventoy_disk.h + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#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 */ +#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__ */ + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_json.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_json.c new file mode 100644 index 00000000..f65a4583 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_json.c @@ -0,0 +1,716 @@ +/****************************************************************************** + * ventoy_json.c + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_json.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_json.h new file mode 100644 index 00000000..12e6f192 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_json.h @@ -0,0 +1,267 @@ +/****************************************************************************** + * ventoy_json.h + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#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__ */ + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_log.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_log.c new file mode 100644 index 00000000..469f843e --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_log.c @@ -0,0 +1,115 @@ +/****************************************************************************** + * ventoy_log.c ---- ventoy log + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_md5.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_md5.c new file mode 100644 index 00000000..ba79d545 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_md5.c @@ -0,0 +1,167 @@ +/****************************************************************************** + * ventoy_md5.c ---- ventoy md5 + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include + +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); +} + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_util.c b/LinuxGUI/Ventoy2Disk/Core/ventoy_util.c new file mode 100644 index 00000000..562c3a09 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_util.c @@ -0,0 +1,541 @@ +/****************************************************************************** + * ventoy_util.c ---- ventoy util + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + diff --git a/LinuxGUI/Ventoy2Disk/Core/ventoy_util.h b/LinuxGUI/Ventoy2Disk/Core/ventoy_util.h new file mode 100644 index 00000000..eb093106 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Core/ventoy_util.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * ventoy_util.h + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#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__ */ + diff --git a/LinuxGUI/Ventoy2Disk/Include/Ventoy2Disk.h b/LinuxGUI/Ventoy2Disk/Include/Ventoy2Disk.h new file mode 100644 index 00000000..32f452cb --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Include/Ventoy2Disk.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * Ventoy2Disk.h + * + * Copyright (c) 2021, longpanda + * + * 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 . + * + */ +#ifndef __VENTOY2DISK_H__ +#define __VENTOY2DISK_H__ + + + +#endif /* __VENTOY2DISK_H__ */ + diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/buidexfat.sh b/LinuxGUI/Ventoy2Disk/Lib/exfat/buidexfat.sh new file mode 100644 index 00000000..00a83c64 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/buidexfat.sh @@ -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"/g' -i src/mkfs/mkexfat_main.c +sed 's//"exfat.h"/g' -i src/mkfs/mkexfat.h +sed 's/int main/int mkexfat_main/g' -i src/mkfs/mkexfat_main.c + diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/exfat-1.3.0.zip b/LinuxGUI/Ventoy2Disk/Lib/exfat/exfat-1.3.0.zip new file mode 100644 index 00000000..aa1d5f39 Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/exfat/exfat-1.3.0.zip differ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/byteorder.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/byteorder.h new file mode 100644 index 00000000..ba544109 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/byteorder.h @@ -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 +#include + +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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/cluster.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/cluster.c new file mode 100644 index 00000000..15cd9aa3 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/cluster.c @@ -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 +#include +#include + +/* + * 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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/compiler.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/compiler.h new file mode 100644 index 00000000..7eb686c7 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/compiler.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/config.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/config.h new file mode 100644 index 00000000..73059a72 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/config.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfat.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfat.h new file mode 100644 index 00000000..b3ff78da --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfat.h @@ -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 +#include +#include +#include +#include +#include + +#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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfatfs.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfatfs.h new file mode 100644 index 00000000..b7b6cac5 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/exfatfs.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c new file mode 100644 index 00000000..4dee5365 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c @@ -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 +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#elif defined(__OpenBSD__) +#include +#include +#include +#include +#elif __linux__ +#include +#endif +#ifdef USE_UBLIO +#include +#include +#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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/lookup.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/lookup.c new file mode 100644 index 00000000..8fa8e0f3 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/lookup.c @@ -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 +#include +#include + +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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/mount.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/mount.c new file mode 100644 index 00000000..4284aee6 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/mount.c @@ -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 +#include +#include +#include +#include +#include + +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 */ +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/node.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/node.c new file mode 100644 index 00000000..ab1d7d6d --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/node.c @@ -0,0 +1,1226 @@ +/* + node.c (09.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 +#include +#include + +#define EXFAT_ENTRY_NONE (-1) + +struct exfat_node* exfat_get_node(struct exfat_node* node) +{ + /* if we switch to multi-threaded mode we will need atomic + increment here and atomic decrement in exfat_put_node() */ + node->references++; + return node; +} + +void exfat_put_node(struct exfat* ef, struct exfat_node* node) +{ + char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]; + + --node->references; + if (node->references < 0) + { + exfat_get_name(node, buffer); + exfat_bug("reference counter of '%s' is below zero", buffer); + } + else if (node->references == 0 && node != ef->root) + { + if (node->is_dirty) + { + exfat_get_name(node, buffer); + exfat_warn("dirty node '%s' with zero references", buffer); + } + } +} + +/** + * This function must be called on rmdir and unlink (after the last + * exfat_put_node()) to free clusters. + */ +int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node) +{ + int rc = 0; + + if (node->references != 0) + exfat_bug("unable to cleanup a node with %d references", + node->references); + + if (node->is_unlinked) + { + /* free all clusters and node structure itself */ + rc = exfat_truncate(ef, node, 0, true); + /* free the node even in case of error or its memory will be lost */ + free(node); + } + return rc; +} + +static int read_entries(struct exfat* ef, struct exfat_node* dir, + struct exfat_entry* entries, int n, off_t offset) +{ + ssize_t size; + + if (!(dir->attrib & EXFAT_ATTRIB_DIR)) + exfat_bug("attempted to read entries from a file"); + + size = exfat_generic_pread(ef, dir, entries, + sizeof(struct exfat_entry[n]), offset); + if (size == sizeof(struct exfat_entry[n])) + return 0; /* success */ + if (size == 0) + return -ENOENT; + if (size < 0) + return -EIO; + exfat_error("read %zd bytes instead of %zu bytes", size, + sizeof(struct exfat_entry[n])); + return -EIO; +} + +static int write_entries(struct exfat* ef, struct exfat_node* dir, + const struct exfat_entry* entries, int n, off_t offset) +{ + ssize_t size; + + if (!(dir->attrib & EXFAT_ATTRIB_DIR)) + exfat_bug("attempted to write entries into a file"); + + size = exfat_generic_pwrite(ef, dir, entries, + sizeof(struct exfat_entry[n]), offset); + if (size == sizeof(struct exfat_entry[n])) + return 0; /* success */ + if (size < 0) + return -EIO; + exfat_error("wrote %zd bytes instead of %zu bytes", size, + sizeof(struct exfat_entry[n])); + return -EIO; +} + +static struct exfat_node* allocate_node(void) +{ + struct exfat_node* node = malloc(sizeof(struct exfat_node)); + if (node == NULL) + { + exfat_error("failed to allocate node"); + return NULL; + } + memset(node, 0, sizeof(struct exfat_node)); + return node; +} + +static void init_node_meta1(struct exfat_node* node, + const struct exfat_entry_meta1* meta1) +{ + node->attrib = le16_to_cpu(meta1->attrib); + node->continuations = meta1->continuations; + node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime, + meta1->mtime_cs); + /* there is no centiseconds field for atime */ + node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, 0); +} + +static void init_node_meta2(struct exfat_node* node, + const struct exfat_entry_meta2* meta2) +{ + node->size = le64_to_cpu(meta2->size); + node->start_cluster = le32_to_cpu(meta2->start_cluster); + node->fptr_cluster = node->start_cluster; + node->is_contiguous = ((meta2->flags & EXFAT_FLAG_CONTIGUOUS) != 0); +} + +static void init_node_name(struct exfat_node* node, + const struct exfat_entry* entries, int n) +{ + int i; + + for (i = 0; i < n; i++) + memcpy(node->name + i * EXFAT_ENAME_MAX, + ((const struct exfat_entry_name*) &entries[i])->name, + EXFAT_ENAME_MAX * sizeof(le16_t)); +} + +static bool check_entries(const struct exfat_entry* entry, int n) +{ + int previous = EXFAT_ENTRY_NONE; + int current; + int i; + + /* check transitions between entries types */ + for (i = 0; i < n + 1; previous = current, i++) + { + bool valid = false; + + current = (i < n) ? entry[i].type : EXFAT_ENTRY_NONE; + switch (previous) + { + case EXFAT_ENTRY_NONE: + valid = (current == EXFAT_ENTRY_FILE); + break; + case EXFAT_ENTRY_FILE: + valid = (current == EXFAT_ENTRY_FILE_INFO); + break; + case EXFAT_ENTRY_FILE_INFO: + valid = (current == EXFAT_ENTRY_FILE_NAME); + break; + case EXFAT_ENTRY_FILE_NAME: + valid = (current == EXFAT_ENTRY_FILE_NAME || + current == EXFAT_ENTRY_NONE || + current >= EXFAT_ENTRY_FILE_TAIL); + break; + case EXFAT_ENTRY_FILE_TAIL ... 0xff: + valid = (current >= EXFAT_ENTRY_FILE_TAIL || + current == EXFAT_ENTRY_NONE); + break; + } + + if (!valid) + { + exfat_error("unexpected entry type %#x after %#x at %d/%d", + current, previous, i, n); + return false; + } + } + return true; +} + +static bool check_node(const struct exfat* ef, struct exfat_node* node, + le16_t actual_checksum, const struct exfat_entry_meta1* meta1, + const struct exfat_entry_meta2* meta2) +{ + int cluster_size = CLUSTER_SIZE(*ef->sb); + uint64_t clusters_heap_size = + (uint64_t) le32_to_cpu(ef->sb->cluster_count) * cluster_size; + char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]; + bool ret = true; + + /* + Validate checksum first. If it's invalid all other fields probably + contain just garbage. + */ + if (le16_to_cpu(actual_checksum) != le16_to_cpu(meta1->checksum)) + { + exfat_get_name(node, buffer); + exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer, + le16_to_cpu(actual_checksum), le16_to_cpu(meta1->checksum)); + if (!EXFAT_REPAIR(invalid_node_checksum, ef, node)) + ret = false; + } + + /* + exFAT does not support sparse files but allows files with uninitialized + clusters. For such files valid_size means initialized data size and + cannot be greater than file size. See SetFileValidData() function + description in MSDN. + */ + if (le64_to_cpu(meta2->valid_size) > node->size) + { + exfat_get_name(node, buffer); + exfat_error("'%s' has valid size (%"PRIu64") greater than size " + "(%"PRIu64")", buffer, le64_to_cpu(meta2->valid_size), + node->size); + ret = false; + } + + /* + Empty file must have zero start cluster. Non-empty file must start + with a valid cluster. Directories cannot be empty (i.e. must always + have a valid start cluster), but we will check this later while + reading that directory to give user a chance to read this directory. + */ + if (node->size == 0 && node->start_cluster != EXFAT_CLUSTER_FREE) + { + exfat_get_name(node, buffer); + exfat_error("'%s' is empty but start cluster is %#x", buffer, + node->start_cluster); + ret = false; + } + if (node->size > 0 && CLUSTER_INVALID(*ef->sb, node->start_cluster)) + { + exfat_get_name(node, buffer); + exfat_error("'%s' points to invalid cluster %#x", buffer, + node->start_cluster); + ret = false; + } + + /* File or directory cannot be larger than clusters heap. */ + if (node->size > clusters_heap_size) + { + exfat_get_name(node, buffer); + exfat_error("'%s' is larger than clusters heap: %"PRIu64" > %"PRIu64, + buffer, node->size, clusters_heap_size); + ret = false; + } + + /* Empty file or directory must be marked as non-contiguous. */ + if (node->size == 0 && node->is_contiguous) + { + exfat_get_name(node, buffer); + exfat_error("'%s' is empty but marked as contiguous (%#hx)", buffer, + node->attrib); + ret = false; + } + + /* Directory size must be aligned on at cluster boundary. */ + if ((node->attrib & EXFAT_ATTRIB_DIR) && node->size % cluster_size != 0) + { + exfat_get_name(node, buffer); + exfat_error("'%s' directory size %"PRIu64" is not divisible by %d", buffer, + node->size, cluster_size); + ret = false; + } + + return ret; +} + +static int parse_file_entries(struct exfat* ef, struct exfat_node* node, + const struct exfat_entry* entries, int n) +{ + const struct exfat_entry_meta1* meta1; + const struct exfat_entry_meta2* meta2; + int mandatory_entries; + + if (!check_entries(entries, n)) + return -EIO; + + meta1 = (const struct exfat_entry_meta1*) &entries[0]; + if (meta1->continuations < 2) + { + exfat_error("too few continuations (%hhu)", meta1->continuations); + return -EIO; + } + meta2 = (const struct exfat_entry_meta2*) &entries[1]; + if (meta2->flags & ~(EXFAT_FLAG_ALWAYS1 | EXFAT_FLAG_CONTIGUOUS)) + { + exfat_error("unknown flags in meta2 (%#hhx)", meta2->flags); + return -EIO; + } + mandatory_entries = 2 + DIV_ROUND_UP(meta2->name_length, EXFAT_ENAME_MAX); + if (meta1->continuations < mandatory_entries - 1) + { + exfat_error("too few continuations (%hhu < %d)", + meta1->continuations, mandatory_entries - 1); + return -EIO; + } + + init_node_meta1(node, meta1); + init_node_meta2(node, meta2); + init_node_name(node, entries + 2, mandatory_entries - 2); + + if (!check_node(ef, node, exfat_calc_checksum(entries, n), meta1, meta2)) + return -EIO; + + return 0; +} + +static int parse_file_entry(struct exfat* ef, struct exfat_node* parent, + struct exfat_node** node, off_t* offset, int n) +{ + struct exfat_entry entries[n]; + int rc; + + rc = read_entries(ef, parent, entries, n, *offset); + if (rc != 0) + return rc; + + /* a new node has zero references */ + *node = allocate_node(); + if (*node == NULL) + return -ENOMEM; + (*node)->entry_offset = *offset; + + rc = parse_file_entries(ef, *node, entries, n); + if (rc != 0) + { + free(*node); + return rc; + } + + *offset += sizeof(struct exfat_entry[n]); + return 0; +} + +static void decompress_upcase(uint16_t* output, const le16_t* source, + size_t size) +{ + size_t si; + size_t oi; + + for (oi = 0; oi < EXFAT_UPCASE_CHARS; oi++) + output[oi] = oi; + + for (si = 0, oi = 0; si < size && oi < EXFAT_UPCASE_CHARS; si++) + { + uint16_t ch = le16_to_cpu(source[si]); + + if (ch == 0xffff && si + 1 < size) /* indicates a run */ + oi += le16_to_cpu(source[++si]); + else + output[oi++] = ch; + } +} + +/* + * Read one entry in a directory at offset position and build a new node + * structure. + */ +static int readdir(struct exfat* ef, struct exfat_node* parent, + struct exfat_node** node, off_t* offset) +{ + int rc; + struct exfat_entry entry; + const struct exfat_entry_meta1* meta1; + const struct exfat_entry_upcase* upcase; + const struct exfat_entry_bitmap* bitmap; + const struct exfat_entry_label* label; + uint64_t upcase_size = 0; + le16_t* upcase_comp = NULL; + + for (;;) + { + rc = read_entries(ef, parent, &entry, 1, *offset); + if (rc != 0) + return rc; + + switch (entry.type) + { + case EXFAT_ENTRY_FILE: + meta1 = (const struct exfat_entry_meta1*) &entry; + return parse_file_entry(ef, parent, node, offset, + 1 + meta1->continuations); + + case EXFAT_ENTRY_UPCASE: + if (ef->upcase != NULL) + break; + upcase = (const struct exfat_entry_upcase*) &entry; + if (CLUSTER_INVALID(*ef->sb, le32_to_cpu(upcase->start_cluster))) + { + exfat_error("invalid cluster 0x%x in upcase table", + le32_to_cpu(upcase->start_cluster)); + return -EIO; + } + upcase_size = le64_to_cpu(upcase->size); + if (upcase_size == 0 || + upcase_size > EXFAT_UPCASE_CHARS * sizeof(uint16_t) || + upcase_size % sizeof(uint16_t) != 0) + { + exfat_error("bad upcase table size (%"PRIu64" bytes)", + upcase_size); + return -EIO; + } + upcase_comp = malloc(upcase_size); + if (upcase_comp == NULL) + { + exfat_error("failed to allocate upcase table (%"PRIu64" bytes)", + upcase_size); + return -ENOMEM; + } + + /* read compressed upcase table */ + if (exfat_pread(ef->dev, upcase_comp, upcase_size, + exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))) < 0) + { + free(upcase_comp); + exfat_error("failed to read upper case table " + "(%"PRIu64" bytes starting at cluster %#x)", + upcase_size, + le32_to_cpu(upcase->start_cluster)); + return -EIO; + } + + /* decompress upcase table */ + ef->upcase = calloc(EXFAT_UPCASE_CHARS, sizeof(uint16_t)); + if (ef->upcase == NULL) + { + free(upcase_comp); + exfat_error("failed to allocate decompressed upcase table"); + return -ENOMEM; + } + decompress_upcase(ef->upcase, upcase_comp, + upcase_size / sizeof(uint16_t)); + free(upcase_comp); + break; + + case EXFAT_ENTRY_BITMAP: + bitmap = (const struct exfat_entry_bitmap*) &entry; + ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster); + if (CLUSTER_INVALID(*ef->sb, ef->cmap.start_cluster)) + { + exfat_error("invalid cluster 0x%x in clusters bitmap", + ef->cmap.start_cluster); + return -EIO; + } + ef->cmap.size = le32_to_cpu(ef->sb->cluster_count); + if (le64_to_cpu(bitmap->size) < DIV_ROUND_UP(ef->cmap.size, 8)) + { + exfat_error("invalid clusters bitmap size: %"PRIu64 + " (expected at least %u)", + le64_to_cpu(bitmap->size), + DIV_ROUND_UP(ef->cmap.size, 8)); + return -EIO; + } + /* FIXME bitmap can be rather big, up to 512 MB */ + ef->cmap.chunk_size = ef->cmap.size; + ef->cmap.chunk = malloc(BMAP_SIZE(ef->cmap.chunk_size)); + if (ef->cmap.chunk == NULL) + { + exfat_error("failed to allocate clusters bitmap chunk " + "(%"PRIu64" bytes)", le64_to_cpu(bitmap->size)); + return -ENOMEM; + } + + if (exfat_pread(ef->dev, ef->cmap.chunk, + BMAP_SIZE(ef->cmap.chunk_size), + exfat_c2o(ef, ef->cmap.start_cluster)) < 0) + { + exfat_error("failed to read clusters bitmap " + "(%"PRIu64" bytes starting at cluster %#x)", + le64_to_cpu(bitmap->size), ef->cmap.start_cluster); + return -EIO; + } + break; + + case EXFAT_ENTRY_LABEL: + label = (const struct exfat_entry_label*) &entry; + if (label->length > EXFAT_ENAME_MAX) + { + exfat_error("too long label (%hhu chars)", label->length); + return -EIO; + } + if (utf16_to_utf8(ef->label, label->name, + sizeof(ef->label), EXFAT_ENAME_MAX) != 0) + return -EIO; + break; + + default: + if (!(entry.type & EXFAT_ENTRY_VALID)) + break; /* deleted entry, ignore it */ + + exfat_error("unknown entry type %#hhx", entry.type); + if (!EXFAT_REPAIR(unknown_entry, ef, parent, &entry, *offset)) + return -EIO; + } + *offset += sizeof(entry); + } + /* we never reach here */ +} + +int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir) +{ + off_t offset = 0; + int rc; + struct exfat_node* node; + struct exfat_node* current = NULL; + + if (dir->is_cached) + return 0; /* already cached */ + + while ((rc = readdir(ef, dir, &node, &offset)) == 0) + { + node->parent = dir; + if (current != NULL) + { + current->next = node; + node->prev = current; + } + else + dir->child = node; + + current = node; + } + + if (rc != -ENOENT) + { + /* rollback */ + for (current = dir->child; current; current = node) + { + node = current->next; + free(current); + } + dir->child = NULL; + return rc; + } + + dir->is_cached = true; + return 0; +} + +static void tree_attach(struct exfat_node* dir, struct exfat_node* node) +{ + node->parent = dir; + if (dir->child) + { + dir->child->prev = node; + node->next = dir->child; + } + dir->child = node; +} + +static void tree_detach(struct exfat_node* node) +{ + if (node->prev) + node->prev->next = node->next; + else /* this is the first node in the list */ + node->parent->child = node->next; + if (node->next) + node->next->prev = node->prev; + node->parent = NULL; + node->prev = NULL; + node->next = NULL; +} + +static void reset_cache(struct exfat* ef, struct exfat_node* node) +{ + char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]; + + while (node->child) + { + struct exfat_node* p = node->child; + reset_cache(ef, p); + tree_detach(p); + free(p); + } + node->is_cached = false; + if (node->references != 0) + { + exfat_get_name(node, buffer); + exfat_warn("non-zero reference counter (%d) for '%s'", + node->references, buffer); + } + if (node != ef->root && node->is_dirty) + { + exfat_get_name(node, buffer); + exfat_bug("node '%s' is dirty", buffer); + } + while (node->references) + exfat_put_node(ef, node); +} + +void exfat_reset_cache(struct exfat* ef) +{ + reset_cache(ef, ef->root); +} + +int exfat_flush_node(struct exfat* ef, struct exfat_node* node) +{ + struct exfat_entry entries[1 + node->continuations]; + struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0]; + struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1]; + int rc; + + if (!node->is_dirty) + return 0; /* no need to flush */ + + if (ef->ro) + exfat_bug("unable to flush node to read-only FS"); + + if (node->parent == NULL) + return 0; /* do not flush unlinked node */ + + rc = read_entries(ef, node->parent, entries, 1 + node->continuations, + node->entry_offset); + if (rc != 0) + return rc; + if (!check_entries(entries, 1 + node->continuations)) + return -EIO; + + meta1->attrib = cpu_to_le16(node->attrib); + exfat_unix2exfat(node->mtime, &meta1->mdate, &meta1->mtime, + &meta1->mtime_cs); + exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime, NULL); + meta2->size = meta2->valid_size = cpu_to_le64(node->size); + meta2->start_cluster = cpu_to_le32(node->start_cluster); + meta2->flags = EXFAT_FLAG_ALWAYS1; + /* empty files must not be marked as contiguous */ + if (node->size != 0 && node->is_contiguous) + meta2->flags |= EXFAT_FLAG_CONTIGUOUS; + /* name hash remains unchanged, no need to recalculate it */ + + meta1->checksum = exfat_calc_checksum(entries, 1 + node->continuations); + rc = write_entries(ef, node->parent, entries, 1 + node->continuations, + node->entry_offset); + if (rc != 0) + return rc; + + node->is_dirty = false; + return exfat_flush(ef); +} + +static int erase_entries(struct exfat* ef, struct exfat_node* dir, int n, + off_t offset) +{ + struct exfat_entry entries[n]; + int rc; + int i; + + rc = read_entries(ef, dir, entries, n, offset); + if (rc != 0) + return rc; + for (i = 0; i < n; i++) + entries[i].type &= ~EXFAT_ENTRY_VALID; + return write_entries(ef, dir, entries, n, offset); +} + +static int erase_node(struct exfat* ef, struct exfat_node* node) +{ + int rc; + + exfat_get_node(node->parent); + rc = erase_entries(ef, node->parent, 1 + node->continuations, + node->entry_offset); + if (rc != 0) + { + exfat_put_node(ef, node->parent); + return rc; + } + rc = exfat_flush_node(ef, node->parent); + exfat_put_node(ef, node->parent); + return rc; +} + +static int shrink_directory(struct exfat* ef, struct exfat_node* dir, + off_t deleted_offset) +{ + const struct exfat_node* node; + const struct exfat_node* last_node; + uint64_t entries = 0; + uint64_t new_size; + + if (!(dir->attrib & EXFAT_ATTRIB_DIR)) + exfat_bug("attempted to shrink a file"); + if (!dir->is_cached) + exfat_bug("attempted to shrink uncached directory"); + + for (last_node = node = dir->child; node; node = node->next) + { + if (deleted_offset < node->entry_offset) + { + /* there are other entries after the removed one, no way to shrink + this directory */ + return 0; + } + if (last_node->entry_offset < node->entry_offset) + last_node = node; + } + + if (last_node) + { + /* offset of the last entry */ + entries += last_node->entry_offset / sizeof(struct exfat_entry); + /* two subentries with meta info */ + entries += 2; + /* subentries with file name */ + entries += DIV_ROUND_UP(utf16_length(last_node->name), + EXFAT_ENAME_MAX); + } + + new_size = DIV_ROUND_UP(entries * sizeof(struct exfat_entry), + CLUSTER_SIZE(*ef->sb)) * CLUSTER_SIZE(*ef->sb); + if (new_size == 0) /* directory always has at least 1 cluster */ + new_size = CLUSTER_SIZE(*ef->sb); + if (new_size == dir->size) + return 0; + return exfat_truncate(ef, dir, new_size, true); +} + +static int delete(struct exfat* ef, struct exfat_node* node) +{ + struct exfat_node* parent = node->parent; + off_t deleted_offset = node->entry_offset; + int rc; + + exfat_get_node(parent); + rc = erase_node(ef, node); + if (rc != 0) + { + exfat_put_node(ef, parent); + return rc; + } + tree_detach(node); + rc = shrink_directory(ef, parent, deleted_offset); + node->is_unlinked = true; + if (rc != 0) + { + exfat_flush_node(ef, parent); + exfat_put_node(ef, parent); + return rc; + } + exfat_update_mtime(parent); + rc = exfat_flush_node(ef, parent); + exfat_put_node(ef, parent); + return rc; +} + +int exfat_unlink(struct exfat* ef, struct exfat_node* node) +{ + if (node->attrib & EXFAT_ATTRIB_DIR) + return -EISDIR; + return delete(ef, node); +} + +int exfat_rmdir(struct exfat* ef, struct exfat_node* node) +{ + int rc; + + if (!(node->attrib & EXFAT_ATTRIB_DIR)) + return -ENOTDIR; + /* check that directory is empty */ + rc = exfat_cache_directory(ef, node); + if (rc != 0) + return rc; + if (node->child) + return -ENOTEMPTY; + return delete(ef, node); +} + +static int check_slot(struct exfat* ef, struct exfat_node* dir, off_t offset, + int n) +{ + struct exfat_entry entries[n]; + int rc; + size_t i; + + /* Root directory contains entries, that don't have any nodes associated + with them (clusters bitmap, upper case table, label). We need to be + careful not to overwrite them. */ + if (dir != ef->root) + return 0; + + rc = read_entries(ef, dir, entries, n, offset); + if (rc != 0) + return rc; + for (i = 0; i < n; i++) + if (entries[i].type & EXFAT_ENTRY_VALID) + return -EINVAL; + return 0; +} + +static int find_slot(struct exfat* ef, struct exfat_node* dir, + off_t* offset, int n) +{ + bitmap_t* dmap; + struct exfat_node* p; + size_t i; + int contiguous = 0; + + if (!dir->is_cached) + exfat_bug("directory is not cached"); + + /* build a bitmap of valid entries in the directory */ + dmap = calloc(BMAP_SIZE(dir->size / sizeof(struct exfat_entry)), + sizeof(bitmap_t)); + if (dmap == NULL) + { + exfat_error("failed to allocate directory bitmap (%"PRIu64")", + dir->size / sizeof(struct exfat_entry)); + return -ENOMEM; + } + for (p = dir->child; p != NULL; p = p->next) + for (i = 0; i < 1 + p->continuations; i++) + BMAP_SET(dmap, p->entry_offset / sizeof(struct exfat_entry) + i); + + /* find a slot in the directory entries bitmap */ + for (i = 0; i < dir->size / sizeof(struct exfat_entry); i++) + { + if (BMAP_GET(dmap, i) == 0) + { + if (contiguous++ == 0) + *offset = (off_t) i * sizeof(struct exfat_entry); + if (contiguous == n) + /* suitable slot is found, check that it's not occupied */ + switch (check_slot(ef, dir, *offset, n)) + { + case 0: + free(dmap); + return 0; + case -EIO: + free(dmap); + return -EIO; + case -EINVAL: + /* slot at (i-n) is occupied, go back and check (i-n+1) */ + i -= contiguous - 1; + contiguous = 0; + break; + } + } + else + contiguous = 0; + } + free(dmap); + + /* no suitable slots found, extend the directory */ + if (contiguous == 0) + *offset = dir->size; + return exfat_truncate(ef, dir, + ROUND_UP(dir->size + sizeof(struct exfat_entry[n - contiguous]), + CLUSTER_SIZE(*ef->sb)), + true); +} + +static int commit_entry(struct exfat* ef, struct exfat_node* dir, + const le16_t* name, off_t offset, uint16_t attrib) +{ + struct exfat_node* node; + const size_t name_length = utf16_length(name); + const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX); + struct exfat_entry entries[2 + name_entries]; + struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0]; + struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1]; + int i; + int rc; + + memset(entries, 0, sizeof(struct exfat_entry[2])); + + meta1->type = EXFAT_ENTRY_FILE; + meta1->continuations = 1 + name_entries; + meta1->attrib = cpu_to_le16(attrib); + exfat_unix2exfat(time(NULL), &meta1->crdate, &meta1->crtime, + &meta1->crtime_cs); + meta1->adate = meta1->mdate = meta1->crdate; + meta1->atime = meta1->mtime = meta1->crtime; + meta1->mtime_cs = meta1->crtime_cs; /* there is no atime_cs */ + + meta2->type = EXFAT_ENTRY_FILE_INFO; + meta2->flags = EXFAT_FLAG_ALWAYS1; + meta2->name_length = name_length; + meta2->name_hash = exfat_calc_name_hash(ef, name, name_length); + meta2->start_cluster = cpu_to_le32(EXFAT_CLUSTER_FREE); + + for (i = 0; i < name_entries; i++) + { + struct exfat_entry_name* name_entry; + + name_entry = (struct exfat_entry_name*) &entries[2 + i]; + name_entry->type = EXFAT_ENTRY_FILE_NAME; + name_entry->__unknown = 0; + memcpy(name_entry->name, name + i * EXFAT_ENAME_MAX, + EXFAT_ENAME_MAX * sizeof(le16_t)); + } + + meta1->checksum = exfat_calc_checksum(entries, 2 + name_entries); + rc = write_entries(ef, dir, entries, 2 + name_entries, offset); + if (rc != 0) + return rc; + + node = allocate_node(); + if (node == NULL) + return -ENOMEM; + node->entry_offset = offset; + memcpy(node->name, name, name_length * sizeof(le16_t)); + init_node_meta1(node, meta1); + init_node_meta2(node, meta2); + + tree_attach(dir, node); + return 0; +} + +static int create(struct exfat* ef, const char* path, uint16_t attrib) +{ + struct exfat_node* dir; + struct exfat_node* existing; + off_t offset = -1; + le16_t name[EXFAT_NAME_MAX + 1]; + int rc; + + rc = exfat_split(ef, &dir, &existing, name, path); + if (rc != 0) + return rc; + if (existing != NULL) + { + exfat_put_node(ef, existing); + exfat_put_node(ef, dir); + return -EEXIST; + } + + rc = find_slot(ef, dir, &offset, + 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX)); + if (rc != 0) + { + exfat_put_node(ef, dir); + return rc; + } + rc = commit_entry(ef, dir, name, offset, attrib); + if (rc != 0) + { + exfat_put_node(ef, dir); + return rc; + } + exfat_update_mtime(dir); + rc = exfat_flush_node(ef, dir); + exfat_put_node(ef, dir); + return rc; +} + +int exfat_mknod(struct exfat* ef, const char* path) +{ + return create(ef, path, EXFAT_ATTRIB_ARCH); +} + +int exfat_mkdir(struct exfat* ef, const char* path) +{ + int rc; + struct exfat_node* node; + + rc = create(ef, path, EXFAT_ATTRIB_DIR); + if (rc != 0) + return rc; + rc = exfat_lookup(ef, &node, path); + if (rc != 0) + return 0; + /* directories always have at least one cluster */ + rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb), true); + if (rc != 0) + { + delete(ef, node); + exfat_put_node(ef, node); + return rc; + } + rc = exfat_flush_node(ef, node); + if (rc != 0) + { + delete(ef, node); + exfat_put_node(ef, node); + return rc; + } + exfat_put_node(ef, node); + return 0; +} + +static int rename_entry(struct exfat* ef, struct exfat_node* dir, + struct exfat_node* node, const le16_t* name, off_t new_offset) +{ + const size_t name_length = utf16_length(name); + const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX); + struct exfat_entry entries[2 + name_entries]; + struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0]; + struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1]; + int rc; + int i; + + rc = read_entries(ef, node->parent, entries, 2, node->entry_offset); + if (rc != 0) + return rc; + + meta1->continuations = 1 + name_entries; + meta2->name_length = name_length; + meta2->name_hash = exfat_calc_name_hash(ef, name, name_length); + + rc = erase_node(ef, node); + if (rc != 0) + return rc; + + node->entry_offset = new_offset; + node->continuations = 1 + name_entries; + + for (i = 0; i < name_entries; i++) + { + struct exfat_entry_name* name_entry; + + name_entry = (struct exfat_entry_name*) &entries[2 + i]; + name_entry->type = EXFAT_ENTRY_FILE_NAME; + name_entry->__unknown = 0; + memcpy(name_entry->name, name + i * EXFAT_ENAME_MAX, + EXFAT_ENAME_MAX * sizeof(le16_t)); + } + + meta1->checksum = exfat_calc_checksum(entries, 2 + name_entries); + rc = write_entries(ef, dir, entries, 2 + name_entries, new_offset); + if (rc != 0) + return rc; + + memcpy(node->name, name, (EXFAT_NAME_MAX + 1) * sizeof(le16_t)); + tree_detach(node); + tree_attach(dir, node); + return 0; +} + +int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path) +{ + struct exfat_node* node; + struct exfat_node* existing; + struct exfat_node* dir; + off_t offset = -1; + le16_t name[EXFAT_NAME_MAX + 1]; + int rc; + + rc = exfat_lookup(ef, &node, old_path); + if (rc != 0) + return rc; + + rc = exfat_split(ef, &dir, &existing, name, new_path); + if (rc != 0) + { + exfat_put_node(ef, node); + return rc; + } + + /* check that target is not a subdirectory of the source */ + if (node->attrib & EXFAT_ATTRIB_DIR) + { + struct exfat_node* p; + + for (p = dir; p; p = p->parent) + if (node == p) + { + if (existing != NULL) + exfat_put_node(ef, existing); + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + return -EINVAL; + } + } + + if (existing != NULL) + { + /* remove target if it's not the same node as source */ + if (existing != node) + { + if (existing->attrib & EXFAT_ATTRIB_DIR) + { + if (node->attrib & EXFAT_ATTRIB_DIR) + rc = exfat_rmdir(ef, existing); + else + rc = -ENOTDIR; + } + else + { + if (!(node->attrib & EXFAT_ATTRIB_DIR)) + rc = exfat_unlink(ef, existing); + else + rc = -EISDIR; + } + exfat_put_node(ef, existing); + if (rc != 0) + { + /* free clusters even if something went wrong; overwise they + will be just lost */ + exfat_cleanup_node(ef, existing); + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + return rc; + } + rc = exfat_cleanup_node(ef, existing); + if (rc != 0) + { + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + return rc; + } + } + else + exfat_put_node(ef, existing); + } + + rc = find_slot(ef, dir, &offset, + 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX)); + if (rc != 0) + { + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + return rc; + } + rc = rename_entry(ef, dir, node, name, offset); + if (rc != 0) + { + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + return rc; + } + rc = exfat_flush_node(ef, dir); + exfat_put_node(ef, dir); + exfat_put_node(ef, node); + /* node itself is not marked as dirty, no need to flush it */ + return rc; +} + +void exfat_utimes(struct exfat_node* node, const struct timespec tv[2]) +{ + node->atime = tv[0].tv_sec; + node->mtime = tv[1].tv_sec; + node->is_dirty = true; +} + +void exfat_update_atime(struct exfat_node* node) +{ + node->atime = time(NULL); + node->is_dirty = true; +} + +void exfat_update_mtime(struct exfat_node* node) +{ + node->mtime = time(NULL); + node->is_dirty = true; +} + +const char* exfat_get_label(struct exfat* ef) +{ + return ef->label; +} + +static int find_label(struct exfat* ef, off_t* offset) +{ + struct exfat_entry entry; + int rc; + + for (*offset = 0; ; *offset += sizeof(entry)) + { + rc = read_entries(ef, ef->root, &entry, 1, *offset); + if (rc != 0) + return rc; + + if (entry.type == EXFAT_ENTRY_LABEL) + return 0; + } +} + +int exfat_set_label(struct exfat* ef, const char* label) +{ + le16_t label_utf16[EXFAT_ENAME_MAX + 1]; + int rc; + off_t offset; + struct exfat_entry_label entry; + + memset(label_utf16, 0, sizeof(label_utf16)); + rc = utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX + 1, strlen(label)); + if (rc != 0) + return rc; + + rc = find_label(ef, &offset); + if (rc == -ENOENT) + rc = find_slot(ef, ef->root, &offset, 1); + if (rc != 0) + return rc; + + entry.type = EXFAT_ENTRY_LABEL; + entry.length = utf16_length(label_utf16); + memcpy(entry.name, label_utf16, sizeof(entry.name)); + if (entry.length == 0) + entry.type ^= EXFAT_ENTRY_VALID; + + rc = write_entries(ef, ef->root, (struct exfat_entry*) &entry, 1, offset); + if (rc != 0) + return rc; + + strcpy(ef->label, label); + return 0; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/platform.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/platform.h new file mode 100644 index 00000000..9ab3155d --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/platform.h @@ -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 +#include +#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 +#include +#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 +#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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/repair.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/repair.c new file mode 100644 index 00000000..620ad52b --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/repair.c @@ -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 + +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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/time.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/time.c new file mode 100644 index 00000000..31ae5a26 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/time.c @@ -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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utf.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utf.c new file mode 100644 index 00000000..0d0018c0 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utf.c @@ -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 + +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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utils.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utils.c new file mode 100644 index 00000000..7baa6631 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/utils.c @@ -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 +#include +#include + +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); +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.c new file mode 100644 index 00000000..ad5accc2 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.c @@ -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 +#include + +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, +}; diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.h new file mode 100644 index 00000000..3803b792 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/cbm.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.c new file mode 100644 index 00000000..c5174a7a --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.c @@ -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 + +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, +}; diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.h new file mode 100644 index 00000000..fbecaa22 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/fat.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.c new file mode 100644 index 00000000..10ecf23a --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.c @@ -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 +#include +#include +#include +#include + +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; +} + diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.h new file mode 100644 index 00000000..54816afe --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat_main.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat_main.c new file mode 100644 index 00000000..c65035fa --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/mkexfat_main.c @@ -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 +#include +#include +#include +#include +#include +#include + +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] \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; +} + diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.c new file mode 100644 index 00000000..323da8aa --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.c @@ -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 + +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, +}; diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.h new file mode 100644 index 00000000..87f437f1 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/rootdir.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.c new file mode 100644 index 00000000..98cdce8f --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.c @@ -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, +}; diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.h new file mode 100644 index 00000000..ce619ecf --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uct.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.c new file mode 100644 index 00000000..518219cd --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.c @@ -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 +}; diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.h new file mode 100644 index 00000000..bd5ebb29 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/uctc.h @@ -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 + +extern uint8_t upcase_table[5836]; + +#endif /* ifndef MKFS_UCTC_H_INCLUDED */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.c b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.c new file mode 100644 index 00000000..e40081fa --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.c @@ -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 + +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, +}; diff --git a/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.h b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.h new file mode 100644 index 00000000..c1f59a60 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/exfat/src/mkfs/vbr.h @@ -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 */ diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/buildlib.sh b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/buildlib.sh new file mode 100644 index 00000000..dec432f7 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/buildlib.sh @@ -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/ + diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_access.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_access.h new file mode 100644 index 00000000..17523878 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_access.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_cache.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_cache.h new file mode 100644 index 00000000..348d5d38 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_cache.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_defs.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_defs.h new file mode 100644 index 00000000..5fe8d6a4 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_defs.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_filelib.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_filelib.h new file mode 100644 index 00000000..a40a28ff --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_filelib.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_format.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_format.h new file mode 100644 index 00000000..a8a6bbaf --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_format.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_list.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_list.h new file mode 100644 index 00000000..bd386ef6 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_list.h @@ -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 + diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_misc.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_misc.h new file mode 100644 index 00000000..0c026343 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_misc.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_opts.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_opts.h new file mode 100644 index 00000000..ac4dc860 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_opts.h @@ -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 + #define FAT_PRINTF(a) printf a + #endif +#endif + +// Time/Date support requires time.h +#if FATFS_INC_TIME_DATE_SUPPORT + #include +#endif + +#endif diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_string.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_string.h new file mode 100644 index 00000000..90ca8e05 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_string.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_table.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_table.h new file mode 100644 index 00000000..ead75f32 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_table.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_types.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_types.h new file mode 100644 index 00000000..5e2cca8a --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_types.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_write.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_write.h new file mode 100644 index 00000000..5558a86e --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/include/fat_write.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_32.a b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_32.a new file mode 100644 index 00000000..e68fa386 Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_32.a differ diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_64.a b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_64.a new file mode 100644 index 00000000..8598a87c Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_64.a differ diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_aa64.a b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_aa64.a new file mode 100644 index 00000000..7b9b66c6 Binary files /dev/null and b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/lib/libfat_io_aa64.a differ diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/API.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/API.txt new file mode 100644 index 00000000..61fc1647 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/API.txt @@ -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. diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/COPYRIGHT.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/COPYRIGHT.txt new file mode 100644 index 00000000..4335f7fb --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/COPYRIGHT.txt @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Configuration.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Configuration.txt new file mode 100644 index 00000000..9ec576e4 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Configuration.txt @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/History.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/History.txt new file mode 100644 index 00000000..58958f41 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/History.txt @@ -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. diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/License.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/License.txt new file mode 100644 index 00000000..c7fb0cc7 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/License.txt @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Media Access API.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Media Access API.txt new file mode 100644 index 00000000..45eede0f --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/Media Access API.txt @@ -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) + + + diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/example.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/example.c new file mode 100644 index 00000000..5d30e5b6 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/example.c @@ -0,0 +1,87 @@ +#include +#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 +#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; idisk_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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.h new file mode 100644 index 00000000..17523878 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_access.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.c new file mode 100644 index 00000000..de77e6a0 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.c @@ -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 +#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;icluster_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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.h new file mode 100644 index 00000000..348d5d38 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_cache.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_defs.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_defs.h new file mode 100644 index 00000000..5fe8d6a4 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_defs.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.c new file mode 100644 index 00000000..2c4a236f --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.c @@ -0,0 +1,1603 @@ +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// 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 +#include +#include "fat_defs.h" +#include "fat_access.h" +#include "fat_table.h" +#include "fat_write.h" +#include "fat_misc.h" +#include "fat_string.h" +#include "fat_filelib.h" +#include "fat_cache.h" + +//----------------------------------------------------------------------------- +// Locals +//----------------------------------------------------------------------------- +static FL_FILE _files[FATFS_MAX_OPEN_FILES]; +static int _filelib_init = 0; +static int _filelib_valid = 0; +static struct fatfs _fs; +static struct fat_list _open_file_list; +static struct fat_list _free_file_list; + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +// Macro for checking if file lib is initialised +#define CHECK_FL_INIT() { if (_filelib_init==0) fl_init(); } + +#define FL_LOCK(a) do { if ((a)->fl_lock) (a)->fl_lock(); } while (0) +#define FL_UNLOCK(a) do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0) + +//----------------------------------------------------------------------------- +// Local Functions +//----------------------------------------------------------------------------- +static void _fl_init(); + +//----------------------------------------------------------------------------- +// _allocate_file: Find a slot in the open files buffer for a new file +//----------------------------------------------------------------------------- +static FL_FILE* _allocate_file(void) +{ + // Allocate free file + struct fat_node *node = fat_list_pop_head(&_free_file_list); + + // Add to open list + if (node) + fat_list_insert_last(&_open_file_list, node); + + return fat_list_entry(node, FL_FILE, list_node); +} +//----------------------------------------------------------------------------- +// _check_file_open: Returns true if the file is already open +//----------------------------------------------------------------------------- +static int _check_file_open(FL_FILE* file) +{ + struct fat_node *node; + + // Compare open files + fat_list_for_each(&_open_file_list, node) + { + FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node); + + // If not the current file + if (openFile != file) + { + // Compare path and name + if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) ) + return 1; + } + } + + return 0; +} +//----------------------------------------------------------------------------- +// _free_file: Free open file handle +//----------------------------------------------------------------------------- +static void _free_file(FL_FILE* file) +{ + // Remove from open list + fat_list_remove(&_open_file_list, &file->list_node); + + // Add to free list + fat_list_insert_last(&_free_file_list, &file->list_node); +} + +//----------------------------------------------------------------------------- +// Low Level +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// _open_directory: Cycle through path string to find the start cluster +// address of the highest subdir. +//----------------------------------------------------------------------------- +static int _open_directory(char *path, uint32 *pathCluster) +{ + int levels; + int sublevel; + char currentfolder[FATFS_MAX_LONG_FILENAME]; + struct fat_dir_entry sfEntry; + uint32 startcluster; + + // Set starting cluster to root cluster + startcluster = fatfs_get_root_cluster(&_fs); + + // Find number of levels + levels = fatfs_total_path_levels(path); + + // Cycle through each level and get the start sector + for (sublevel=0;sublevel<(levels+1);sublevel++) + { + if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1) + return 0; + + // Find clusteraddress for folder (currentfolder) + if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry)) + { + // Check entry is folder + if (fatfs_entry_is_dir(&sfEntry)) + startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO); + else + return 0; + } + else + return 0; + } + + *pathCluster = startcluster; + return 1; +} +//----------------------------------------------------------------------------- +// _create_directory: Cycle through path string and create the end directory +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +static int _create_directory(char *path) +{ + FL_FILE* file; + struct fat_dir_entry sfEntry; + char shortFilename[FAT_SFN_SIZE_FULL]; + int tailNum = 0; + int i; + + // Allocate a new file handle + file = _allocate_file(); + if (!file) + return 0; + + // Clear filename + memset(file->path, '\0', sizeof(file->path)); + memset(file->filename, '\0', sizeof(file->filename)); + + // Split full path into filename and directory path + if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1) + { + _free_file(file); + return 0; + } + + // Check if file already open + if (_check_file_open(file)) + { + _free_file(file); + return 0; + } + + // If file is in the root dir + if (file->path[0] == 0) + file->parentcluster = fatfs_get_root_cluster(&_fs); + else + { + // Find parent directory start cluster + if (!_open_directory(file->path, &file->parentcluster)) + { + _free_file(file); + return 0; + } + } + + // Check if same filename exists in directory + if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1) + { + _free_file(file); + return 0; + } + + file->startcluster = 0; + + // Create the file space for the folder (at least one clusters worth!) + if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1)) + { + _free_file(file); + return 0; + } + + // Erase new directory cluster + memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE); + for (i=0;i<_fs.sectors_per_cluster;i++) + { + if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector)) + { + _free_file(file); + return 0; + } + } + +#if FATFS_INC_LFN_SUPPORT + + // Generate a short filename & tail + tailNum = 0; + do + { + // Create a standard short filename (without tail) + fatfs_lfn_create_sfn(shortFilename, file->filename); + + // If second hit or more, generate a ~n tail + if (tailNum != 0) + fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum); + // Try with no tail if first entry + else + memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL); + + // Check if entry exists already or not + if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0) + break; + + tailNum++; + } + while (tailNum < 9999); + + // We reached the max number of duplicate short file names (unlikely!) + if (tailNum == 9999) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return 0; + } +#else + // Create a standard short filename (without tail) + if (!fatfs_lfn_create_sfn(shortFilename, file->filename)) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return 0; + } + + // Copy to SFN space + memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL); + + // Check if entry exists already + if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename)) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return 0; + } +#endif + + // Add file to disk + if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1)) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return 0; + } + + // General + file->filelength = 0; + file->bytenum = 0; + file->file_data_address = 0xFFFFFFFF; + file->file_data_dirty = 0; + file->filelength_changed = 0; + + // Quick lookup for next link in the chain + file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF; + file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF; + + fatfs_fat_purge(&_fs); + + _free_file(file); + return 1; +} +#endif +//----------------------------------------------------------------------------- +// _open_file: Open a file for reading +//----------------------------------------------------------------------------- +static FL_FILE* _open_file(const char *path) +{ + FL_FILE* file; + struct fat_dir_entry sfEntry; + + // Allocate a new file handle + file = _allocate_file(); + if (!file) + return NULL; + + // Clear filename + memset(file->path, '\0', sizeof(file->path)); + memset(file->filename, '\0', sizeof(file->filename)); + + // Split full path into filename and directory path + if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1) + { + _free_file(file); + return NULL; + } + + // Check if file already open + if (_check_file_open(file)) + { + _free_file(file); + return NULL; + } + + // If file is in the root dir + if (file->path[0]==0) + file->parentcluster = fatfs_get_root_cluster(&_fs); + else + { + // Find parent directory start cluster + if (!_open_directory(file->path, &file->parentcluster)) + { + _free_file(file); + return NULL; + } + } + + // Using dir cluster address search for filename + if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry)) + // Make sure entry is file not dir! + if (fatfs_entry_is_file(&sfEntry)) + { + // Initialise file details + memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL); + file->filelength = FAT_HTONL(sfEntry.FileSize); + file->bytenum = 0; + file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO); + file->file_data_address = 0xFFFFFFFF; + file->file_data_dirty = 0; + file->filelength_changed = 0; + + // Quick lookup for next link in the chain + file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF; + file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF; + + fatfs_cache_init(&_fs, file); + + fatfs_fat_purge(&_fs); + + return file; + } + + _free_file(file); + return NULL; +} +//----------------------------------------------------------------------------- +// _create_file: Create a new file +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +static FL_FILE* _create_file(const char *filename) +{ + FL_FILE* file; + struct fat_dir_entry sfEntry; + char shortFilename[FAT_SFN_SIZE_FULL]; + int tailNum = 0; + + // No write access? + if (!_fs.disk_io.write_media) + return NULL; + + // Allocate a new file handle + file = _allocate_file(); + if (!file) + return NULL; + + // Clear filename + memset(file->path, '\0', sizeof(file->path)); + memset(file->filename, '\0', sizeof(file->filename)); + + // Split full path into filename and directory path + if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1) + { + _free_file(file); + return NULL; + } + + // Check if file already open + if (_check_file_open(file)) + { + _free_file(file); + return NULL; + } + + // If file is in the root dir + if (file->path[0] == 0) + file->parentcluster = fatfs_get_root_cluster(&_fs); + else + { + // Find parent directory start cluster + if (!_open_directory(file->path, &file->parentcluster)) + { + _free_file(file); + return NULL; + } + } + + // Check if same filename exists in directory + if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1) + { + _free_file(file); + return NULL; + } + + file->startcluster = 0; + + // Create the file space for the file (at least one clusters worth!) + if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1)) + { + _free_file(file); + return NULL; + } + +#if FATFS_INC_LFN_SUPPORT + // Generate a short filename & tail + tailNum = 0; + do + { + // Create a standard short filename (without tail) + fatfs_lfn_create_sfn(shortFilename, file->filename); + + // If second hit or more, generate a ~n tail + if (tailNum != 0) + fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum); + // Try with no tail if first entry + else + memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL); + + // Check if entry exists already or not + if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0) + break; + + tailNum++; + } + while (tailNum < 9999); + + // We reached the max number of duplicate short file names (unlikely!) + if (tailNum == 9999) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return NULL; + } +#else + // Create a standard short filename (without tail) + if (!fatfs_lfn_create_sfn(shortFilename, file->filename)) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return NULL; + } + + // Copy to SFN space + memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL); + + // Check if entry exists already + if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename)) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return NULL; + } +#endif + + // Add file to disk + if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0)) + { + // Delete allocated space + fatfs_free_cluster_chain(&_fs, file->startcluster); + + _free_file(file); + return NULL; + } + + // General + file->filelength = 0; + file->bytenum = 0; + file->file_data_address = 0xFFFFFFFF; + file->file_data_dirty = 0; + file->filelength_changed = 0; + + // Quick lookup for next link in the chain + file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF; + file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF; + + fatfs_cache_init(&_fs, file); + + fatfs_fat_purge(&_fs); + + return file; +} +#endif +//----------------------------------------------------------------------------- +// _read_sectors: Read sector(s) from disk to file +//----------------------------------------------------------------------------- +static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count) +{ + uint32 Sector = 0; + uint32 ClusterIdx = 0; + uint32 Cluster = 0; + uint32 i; + uint32 lba; + + // Find cluster index within file & sector with cluster + ClusterIdx = offset / _fs.sectors_per_cluster; + Sector = offset - (ClusterIdx * _fs.sectors_per_cluster); + + // Limit number of sectors read to the number remaining in this cluster + if ((Sector + count) > _fs.sectors_per_cluster) + count = _fs.sectors_per_cluster - Sector; + + // Quick lookup for next link in the chain + if (ClusterIdx == file->last_fat_lookup.ClusterIdx) + Cluster = file->last_fat_lookup.CurrentCluster; + // Else walk the chain + else + { + // Starting from last recorded cluster? + if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1) + { + i = file->last_fat_lookup.ClusterIdx; + Cluster = file->last_fat_lookup.CurrentCluster; + } + // Start searching from the beginning.. + else + { + // Set start of cluster chain to initial value + i = 0; + Cluster = file->startcluster; + } + + // Follow chain to find cluster to read + for ( ;ilast_fat_lookup.CurrentCluster = Cluster; + file->last_fat_lookup.ClusterIdx = ClusterIdx; + } + } + + // If end of cluster chain then return false + if (Cluster == FAT32_LAST_CLUSTER) + return 0; + + // Calculate sector address + lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector; + + // Read sector of file + if (fatfs_sector_read(&_fs, lba, buffer, count)) + return count; + else + return 0; +} + +//----------------------------------------------------------------------------- +// External API +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// fl_init: Initialise library +//----------------------------------------------------------------------------- +void fl_init(void) +{ + int i; + + fat_list_init(&_free_file_list); + fat_list_init(&_open_file_list); + + // Add all file objects to free list + for (i=0;iflags = flags; + + FL_UNLOCK(&_fs); + return file; +} +//----------------------------------------------------------------------------- +// _write_sectors: Write sector(s) to disk +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count) +{ + uint32 SectorNumber = 0; + uint32 ClusterIdx = 0; + uint32 Cluster = 0; + uint32 LastCluster = FAT32_LAST_CLUSTER; + uint32 i; + uint32 lba; + uint32 TotalWriteCount = count; + + // Find values for Cluster index & sector within cluster + ClusterIdx = offset / _fs.sectors_per_cluster; + SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster); + + // Limit number of sectors written to the number remaining in this cluster + if ((SectorNumber + count) > _fs.sectors_per_cluster) + count = _fs.sectors_per_cluster - SectorNumber; + + // Quick lookup for next link in the chain + if (ClusterIdx == file->last_fat_lookup.ClusterIdx) + Cluster = file->last_fat_lookup.CurrentCluster; + // Else walk the chain + else + { + // Starting from last recorded cluster? + if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1) + { + i = file->last_fat_lookup.ClusterIdx; + Cluster = file->last_fat_lookup.CurrentCluster; + } + // Start searching from the beginning.. + else + { + // Set start of cluster chain to initial value + i = 0; + Cluster = file->startcluster; + } + + // Follow chain to find cluster to read + for ( ;ilast_fat_lookup.CurrentCluster = Cluster; + file->last_fat_lookup.ClusterIdx = ClusterIdx; + } + + // Calculate write address + lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber; + + if (fatfs_sector_write(&_fs, lba, buf, count)) + return count; + else + return 0; +} +#endif +//----------------------------------------------------------------------------- +// fl_fflush: Flush un-written data to the file +//----------------------------------------------------------------------------- +int fl_fflush(void *f) +{ +#if FATFS_INC_WRITE_SUPPORT + FL_FILE *file = (FL_FILE *)f; + + // If first call to library, initialise + CHECK_FL_INIT(); + + if (file) + { + FL_LOCK(&_fs); + + // If some write data still in buffer + if (file->file_data_dirty) + { + // Write back current sector before loading next + if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1)) + file->file_data_dirty = 0; + } + + FL_UNLOCK(&_fs); + } +#endif + return 0; +} +//----------------------------------------------------------------------------- +// fl_fclose: Close an open file +//----------------------------------------------------------------------------- +void fl_fclose(void *f) +{ + FL_FILE *file = (FL_FILE *)f; + + // If first call to library, initialise + CHECK_FL_INIT(); + + if (file) + { + FL_LOCK(&_fs); + + // Flush un-written data to file + fl_fflush(f); + + // File size changed? + if (file->filelength_changed) + { +#if FATFS_INC_WRITE_SUPPORT + // Update filesize in directory + fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength); +#endif + file->filelength_changed = 0; + } + + file->bytenum = 0; + file->filelength = 0; + file->startcluster = 0; + file->file_data_address = 0xFFFFFFFF; + file->file_data_dirty = 0; + file->filelength_changed = 0; + + // Free file handle + _free_file(file); + + fatfs_fat_purge(&_fs); + + FL_UNLOCK(&_fs); + } +} +//----------------------------------------------------------------------------- +// fl_fgetc: Get a character in the stream +//----------------------------------------------------------------------------- +int fl_fgetc(void *f) +{ + int res; + uint8 data = 0; + + res = fl_fread(&data, 1, 1, f); + if (res == 1) + return (int)data; + else + return res; +} +//----------------------------------------------------------------------------- +// fl_fgets: Get a string from a stream +//----------------------------------------------------------------------------- +char *fl_fgets(char *s, int n, void *f) +{ + int idx = 0; + + // Space for null terminator? + if (n > 0) + { + // While space (+space for null terminator) + while (idx < (n-1)) + { + int ch = fl_fgetc(f); + + // EOF / Error? + if (ch < 0) + break; + + // Store character read from stream + s[idx++] = (char)ch; + + // End of line? + if (ch == '\n') + break; + } + + if (idx > 0) + s[idx] = '\0'; + } + + return (idx > 0) ? s : 0; +} +//----------------------------------------------------------------------------- +// fl_fread: Read a block of data from the file +//----------------------------------------------------------------------------- +int fl_fread(void * buffer, int size, int length, void *f ) +{ + uint32 sector; + uint32 offset; + int copyCount; + int count = size * length; + int bytesRead = 0; + + FL_FILE *file = (FL_FILE *)f; + + // If first call to library, initialise + CHECK_FL_INIT(); + + if (buffer==NULL || file==NULL) + return -1; + + // No read permissions + if (!(file->flags & FILE_READ)) + return -1; + + // Nothing to be done + if (!count) + return 0; + + // Check if read starts past end of file + if (file->bytenum >= file->filelength) + return -1; + + // Limit to file size + if ( (file->bytenum + count) > file->filelength ) + count = file->filelength - file->bytenum; + + // Calculate start sector + sector = file->bytenum / FAT_SECTOR_SIZE; + + // Offset to start copying data from first sector + offset = file->bytenum % FAT_SECTOR_SIZE; + + while (bytesRead < count) + { + // Read whole sector, read from media directly into target buffer + if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE)) + { + // Read as many sectors as possible into target buffer + uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE); + if (sectorsRead) + { + // We have upto one sector to copy + copyCount = FAT_SECTOR_SIZE * sectorsRead; + + // Move onto next sector and reset copy offset + sector+= sectorsRead; + offset = 0; + } + else + break; + } + else + { + // Do we need to re-read the sector? + if (file->file_data_address != sector) + { + // Flush un-written data to file + if (file->file_data_dirty) + fl_fflush(file); + + // Get LBA of sector offset within file + if (!_read_sectors(file, sector, file->file_data_sector, 1)) + // Read failed - out of range (probably) + break; + + file->file_data_address = sector; + file->file_data_dirty = 0; + } + + // We have upto one sector to copy + copyCount = FAT_SECTOR_SIZE - offset; + + // Only require some of this sector? + if (copyCount > (count - bytesRead)) + copyCount = (count - bytesRead); + + // Copy to application buffer + memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount); + + // Move onto next sector and reset copy offset + sector++; + offset = 0; + } + + // Increase total read count + bytesRead += copyCount; + + // Increment file pointer + file->bytenum += copyCount; + } + + return bytesRead; +} +//----------------------------------------------------------------------------- +// fl_fseek: Seek to a specific place in the file +//----------------------------------------------------------------------------- +int fl_fseek( void *f, long offset, int origin ) +{ + FL_FILE *file = (FL_FILE *)f; + int res = -1; + + // If first call to library, initialise + CHECK_FL_INIT(); + + if (!file) + return -1; + + if (origin == SEEK_END && offset != 0) + return -1; + + FL_LOCK(&_fs); + + // Invalidate file buffer + file->file_data_address = 0xFFFFFFFF; + file->file_data_dirty = 0; + + if (origin == SEEK_SET) + { + file->bytenum = (uint32)offset; + + if (file->bytenum > file->filelength) + file->bytenum = file->filelength; + + res = 0; + } + else if (origin == SEEK_CUR) + { + // Positive shift + if (offset >= 0) + { + file->bytenum += offset; + + if (file->bytenum > file->filelength) + file->bytenum = file->filelength; + } + // Negative shift + else + { + // Make shift positive + offset = -offset; + + // Limit to negative shift to start of file + if ((uint32)offset > file->bytenum) + file->bytenum = 0; + else + file->bytenum-= offset; + } + + res = 0; + } + else if (origin == SEEK_END) + { + file->bytenum = file->filelength; + res = 0; + } + else + res = -1; + + FL_UNLOCK(&_fs); + + return res; +} +//----------------------------------------------------------------------------- +// fl_fgetpos: Get the current file position +//----------------------------------------------------------------------------- +int fl_fgetpos(void *f , uint32 * position) +{ + FL_FILE *file = (FL_FILE *)f; + + if (!file) + return -1; + + FL_LOCK(&_fs); + + // Get position + *position = file->bytenum; + + FL_UNLOCK(&_fs); + + return 0; +} +//----------------------------------------------------------------------------- +// fl_ftell: Get the current file position +//----------------------------------------------------------------------------- +long fl_ftell(void *f) +{ + uint32 pos = 0; + + fl_fgetpos(f, &pos); + + return (long)pos; +} +//----------------------------------------------------------------------------- +// fl_feof: Is the file pointer at the end of the stream? +//----------------------------------------------------------------------------- +int fl_feof(void *f) +{ + FL_FILE *file = (FL_FILE *)f; + int res; + + if (!file) + return -1; + + FL_LOCK(&_fs); + + if (file->bytenum == file->filelength) + res = EOF; + else + res = 0; + + FL_UNLOCK(&_fs); + + return res; +} +//----------------------------------------------------------------------------- +// fl_fputc: Write a character to the stream +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +int fl_fputc(int c, void *f) +{ + uint8 data = (uint8)c; + int res; + + res = fl_fwrite(&data, 1, 1, f); + if (res == 1) + return c; + else + return res; +} +#endif +//----------------------------------------------------------------------------- +// fl_fwrite: Write a block of data to the stream +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +int fl_fwrite(const void * data, int size, int count, void *f ) +{ + FL_FILE *file = (FL_FILE *)f; + uint32 sector; + uint32 offset; + uint32 length = (size*count); + uint8 *buffer = (uint8 *)data; + uint32 bytesWritten = 0; + uint32 copyCount; + + // If first call to library, initialise + CHECK_FL_INIT(); + + if (!file) + return -1; + + FL_LOCK(&_fs); + + // No write permissions + if (!(file->flags & FILE_WRITE)) + { + FL_UNLOCK(&_fs); + return -1; + } + + // Append writes to end of file + if (file->flags & FILE_APPEND) + file->bytenum = file->filelength; + // Else write to current position + + // Calculate start sector + sector = file->bytenum / FAT_SECTOR_SIZE; + + // Offset to start copying data from first sector + offset = file->bytenum % FAT_SECTOR_SIZE; + + while (bytesWritten < length) + { + // Whole sector or more to be written? + if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE)) + { + uint32 sectorsWrote; + + // Buffered sector, flush back to disk + if (file->file_data_address != 0xFFFFFFFF) + { + // Flush un-written data to file + if (file->file_data_dirty) + fl_fflush(file); + + file->file_data_address = 0xFFFFFFFF; + file->file_data_dirty = 0; + } + + // Write as many sectors as possible + sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE); + copyCount = FAT_SECTOR_SIZE * sectorsWrote; + + // Increase total read count + bytesWritten += copyCount; + + // Increment file pointer + file->bytenum += copyCount; + + // Move onto next sector and reset copy offset + sector+= sectorsWrote; + offset = 0; + + if (!sectorsWrote) + break; + } + else + { + // We have upto one sector to copy + copyCount = FAT_SECTOR_SIZE - offset; + + // Only require some of this sector? + if (copyCount > (length - bytesWritten)) + copyCount = (length - bytesWritten); + + // Do we need to read a new sector? + if (file->file_data_address != sector) + { + // Flush un-written data to file + if (file->file_data_dirty) + fl_fflush(file); + + // If we plan to overwrite the whole sector, we don't need to read it first! + if (copyCount != FAT_SECTOR_SIZE) + { + // NOTE: This does not have succeed; if last sector of file + // reached, no valid data will be read in, but write will + // allocate some more space for new data. + + // Get LBA of sector offset within file + if (!_read_sectors(file, sector, file->file_data_sector, 1)) + memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE); + } + + file->file_data_address = sector; + file->file_data_dirty = 0; + } + + // Copy from application buffer into sector buffer + memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount); + + // Mark buffer as dirty + file->file_data_dirty = 1; + + // Increase total read count + bytesWritten += copyCount; + + // Increment file pointer + file->bytenum += copyCount; + + // Move onto next sector and reset copy offset + sector++; + offset = 0; + } + } + + // Write increased extent of the file? + if (file->bytenum > file->filelength) + { + // Increase file size to new point + file->filelength = file->bytenum; + + // We are changing the file length and this + // will need to be writen back at some point + file->filelength_changed = 1; + } + +#if FATFS_INC_TIME_DATE_SUPPORT + // If time & date support is enabled, always force directory entry to be + // written in-order to update file modify / access time & date. + file->filelength_changed = 1; +#endif + + FL_UNLOCK(&_fs); + + return (size*count); +} +#endif +//----------------------------------------------------------------------------- +// fl_fputs: Write a character string to the stream +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +int fl_fputs(const char * str, void *f) +{ + int len = (int)strlen(str); + int res = fl_fwrite(str, 1, len, f); + + if (res == len) + return len; + else + return res; +} +#endif +//----------------------------------------------------------------------------- +// fl_remove: Remove a file from the filesystem +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +int fl_remove( const char * filename ) +{ + FL_FILE* file; + int res = -1; + + FL_LOCK(&_fs); + + // Use read_file as this will check if the file is already open! + file = fl_fopen((char*)filename, "r"); + if (file) + { + // Delete allocated space + if (fatfs_free_cluster_chain(&_fs, file->startcluster)) + { + // Remove directory entries + if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename)) + { + // Close the file handle (this should not write anything to the file + // as we have not changed the file since opening it!) + fl_fclose(file); + + res = 0; + } + } + } + + FL_UNLOCK(&_fs); + + return res; +} +#endif +//----------------------------------------------------------------------------- +// fl_createdirectory: Create a directory based on a path +//----------------------------------------------------------------------------- +#if FATFS_INC_WRITE_SUPPORT +int fl_createdirectory(const char *path) +{ + int res; + + // If first call to library, initialise + CHECK_FL_INIT(); + + FL_LOCK(&_fs); + res =_create_directory((char*)path); + FL_UNLOCK(&_fs); + + return res; +} +#endif +//----------------------------------------------------------------------------- +// fl_listdirectory: List a directory based on a path +//----------------------------------------------------------------------------- +#if FATFS_DIR_LIST_SUPPORT +void fl_listdirectory(const char *path) +{ + FL_DIR dirstat; + + // If first call to library, initialise + CHECK_FL_INIT(); + + FL_LOCK(&_fs); + + FAT_PRINTF(("\r\nDirectory %s\r\n", path)); + + if (fl_opendir(path, &dirstat)) + { + struct fs_dir_ent dirent; + + while (fl_readdir(&dirstat, &dirent) == 0) + { +#if FATFS_INC_TIME_DATE_SUPPORT + int d,m,y,h,mn,s; + fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s); + fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y); + FAT_PRINTF(("%02d/%02d/%04d %02d:%02d ", d,mn,y,h,m)); +#endif + + if (dirent.is_dir) + { + FAT_PRINTF(("%s \r\n", dirent.filename)); + } + else + { + FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size)); + } + } + + fl_closedir(&dirstat); + } + + FL_UNLOCK(&_fs); +} +#endif +//----------------------------------------------------------------------------- +// fl_opendir: Opens a directory for listing +//----------------------------------------------------------------------------- +#if FATFS_DIR_LIST_SUPPORT +FL_DIR* fl_opendir(const char* path, FL_DIR *dir) +{ + int levels; + int res = 1; + uint32 cluster = FAT32_INVALID_CLUSTER; + + // If first call to library, initialise + CHECK_FL_INIT(); + + FL_LOCK(&_fs); + + levels = fatfs_total_path_levels((char*)path) + 1; + + // If path is in the root dir + if (levels == 0) + cluster = fatfs_get_root_cluster(&_fs); + // Find parent directory start cluster + else + res = _open_directory((char*)path, &cluster); + + if (res) + fatfs_list_directory_start(&_fs, dir, cluster); + + FL_UNLOCK(&_fs); + + return cluster != FAT32_INVALID_CLUSTER ? dir : 0; +} +#endif +//----------------------------------------------------------------------------- +// fl_readdir: Get next item in directory +//----------------------------------------------------------------------------- +#if FATFS_DIR_LIST_SUPPORT +int fl_readdir(FL_DIR *dirls, fl_dirent *entry) +{ + int res = 0; + + // If first call to library, initialise + CHECK_FL_INIT(); + + FL_LOCK(&_fs); + + res = fatfs_list_directory_next(&_fs, dirls, entry); + + FL_UNLOCK(&_fs); + + return res ? 0 : -1; +} +#endif +//----------------------------------------------------------------------------- +// fl_closedir: Close directory after listing +//----------------------------------------------------------------------------- +#if FATFS_DIR_LIST_SUPPORT +int fl_closedir(FL_DIR* dir) +{ + // Not used + return 0; +} +#endif +//----------------------------------------------------------------------------- +// fl_is_dir: Is this a directory? +//----------------------------------------------------------------------------- +#if FATFS_DIR_LIST_SUPPORT +int fl_is_dir(const char *path) +{ + int res = 0; + FL_DIR dir; + + if (fl_opendir(path, &dir)) + { + res = 1; + fl_closedir(&dir); + } + + return res; +} +#endif +//----------------------------------------------------------------------------- +// fl_format: Format a partition with either FAT16 or FAT32 based on size +//----------------------------------------------------------------------------- +#if FATFS_INC_FORMAT_SUPPORT +int fl_format(uint32 volume_sectors, const char *name) +{ + return fatfs_format(&_fs, volume_sectors, name); +} +#endif /*FATFS_INC_FORMAT_SUPPORT*/ +//----------------------------------------------------------------------------- +// fl_get_fs: +//----------------------------------------------------------------------------- +#ifdef FATFS_INC_TEST_HOOKS +struct fatfs* fl_get_fs(void) +{ + return &_fs; +} +#endif diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.h new file mode 100644 index 00000000..a40a28ff --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_filelib.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.c new file mode 100644 index 00000000..d067f377 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.c @@ -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 +#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;idisk_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;ifat_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*/ diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.h new file mode 100644 index 00000000..a8a6bbaf --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_format.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_list.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_list.h new file mode 100644 index 00000000..bd386ef6 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_list.h @@ -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 + diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.c new file mode 100644 index 00000000..cbf6f08d --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.c @@ -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 +#include +#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;iString[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; iString[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;iName[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= '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= '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 31 && ch < 127) + { + FAT_PRINTF(("%c", ch)); + } + else + { + FAT_PRINTF((".")); + } + } + + FAT_PRINTF(("\n")); + } + } +} +#endif diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.h new file mode 100644 index 00000000..0c026343 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_misc.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_opts.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_opts.h new file mode 100644 index 00000000..ac4dc860 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_opts.h @@ -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 + #define FAT_PRINTF(a) printf a + #endif +#endif + +// Time/Date support requires time.h +#if FATFS_INC_TIME_DATE_SUPPORT + #include +#endif + +#endif diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.c new file mode 100644 index 00000000..f7206ce7 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.c @@ -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 +#include +#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 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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.h new file mode 100644 index 00000000..90ca8e05 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_string.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.c new file mode 100644 index 00000000..8d05d3bf --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.c @@ -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 +#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;ifat_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; +} diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.h new file mode 100644 index 00000000..ead75f32 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_table.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_types.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_types.h new file mode 100644 index 00000000..5e2cca8a --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_types.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.c b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.c new file mode 100644 index 00000000..0718cb13 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.c @@ -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 +#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;irootdir_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;isectors_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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.h b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.h new file mode 100644 index 00000000..5558a86e --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/fat_write.h @@ -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 diff --git a/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/version.txt b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/version.txt new file mode 100644 index 00000000..5a00a98c --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/fat_io_lib/release/version.txt @@ -0,0 +1 @@ +2.6.11 diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/buildlib.sh b/LinuxGUI/Ventoy2Disk/Lib/libhttp/buildlib.sh new file mode 100644 index 00000000..07c542f4 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/libhttp/buildlib.sh @@ -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 + + diff --git a/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.c b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.c new file mode 100644 index 00000000..98130f53 --- /dev/null +++ b/LinuxGUI/Ventoy2Disk/Lib/libhttp/include/civetweb.c @@ -0,0 +1,13145 @@ +/* Copyright (c) 2013-2016 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka + * + * 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. + */ + + +#if defined(_WIN32) +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ +#endif +#ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */ +#define _WIN32_WINNT 0x0501 +#endif +#else +#if defined(__GNUC__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE /* for setgroups() */ +#endif +#if defined(__linux__) && !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ +#endif +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ +#endif +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS /* wants this for C++ */ +#endif +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ +#endif +#ifdef __sun +#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ +#define __inline inline /* not recognized on older compiler versions */ +#endif +#endif + +#if defined(USE_LUA) && defined(USE_WEBSOCKET) +#define USE_TIMERS +#endif + +#if defined(_MSC_VER) +/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */ +#pragma warning(disable : 4306) +/* conditional expression is constant: introduced by FD_SET(..) */ +#pragma warning(disable : 4127) +/* non-constant aggregate initializer: issued due to missing C99 support */ +#pragma warning(disable : 4204) +/* padding added after data member */ +#pragma warning(disable : 4820) +/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */ +#pragma warning(disable : 4668) +/* no function prototype given: converting '()' to '(void)' */ +#pragma warning(disable : 4255) +/* function has been selected for automatic inline expansion */ +#pragma warning(disable : 4711) +#endif + + +/* This code uses static_assert to check some conditions. + * Unfortunately some compilers still do not support it, so we have a + * replacement function here. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +#define mg_static_assert static_assert +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +#define mg_static_assert static_assert +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#define mg_static_assert _Static_assert +#else +char static_assert_replacement[1]; +#define mg_static_assert(cond, txt) \ + extern char static_assert_replacement[(cond) ? 1 : -1] +#endif + +mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8, + "int data type size check"); +mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8, + "pointer data type size check"); +mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check"); +/* mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t data + * type size check"); */ + +/* DTL -- including winsock2.h works better if lean and mean */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#if defined(__SYMBIAN32__) +#define NO_SSL /* SSL is not supported */ +#define NO_CGI /* CGI is not supported */ +#define PATH_MAX FILENAME_MAX +#endif /* __SYMBIAN32__ */ + + +/* Include the header file here, so the CivetWeb interface is defined for the + * entire implementation, including the following forward definitions. */ +#include "civetweb.h" + + +#ifndef IGNORE_UNUSED_RESULT +#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1)) +#endif + +#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */ +#include +#include +#include +#include +#include +#endif /* !_WIN32_WCE */ + +#ifdef __MACH__ + +#define CLOCK_MONOTONIC (1) +#define CLOCK_REALTIME (2) + +#include +#include +#include +#include +#include + + +/* clock_gettime is not implemented on OSX */ +int clock_gettime(int clk_id, struct timespec *t); + +int +clock_gettime(int clk_id, struct timespec *t) +{ + memset(t, 0, sizeof(*t)); + if (clk_id == CLOCK_REALTIME) { + struct timeval now; + int rv = gettimeofday(&now, NULL); + if (rv) { + return rv; + } + t->tv_sec = now.tv_sec; + t->tv_nsec = now.tv_usec * 1000; + return 0; + + } else if (clk_id == CLOCK_MONOTONIC) { + static uint64_t clock_start_time = 0; + static mach_timebase_info_data_t timebase_ifo = {0, 0}; + + uint64_t now = mach_absolute_time(); + + if (clock_start_time == 0) { + kern_return_t mach_status = mach_timebase_info(&timebase_ifo); +#if defined(DEBUG) + assert(mach_status == KERN_SUCCESS); +#else + /* appease "unused variable" warning for release builds */ + (void)mach_status; +#endif + clock_start_time = now; + } + + now = (uint64_t)((double)(now - clock_start_time) + * (double)timebase_ifo.numer + / (double)timebase_ifo.denom); + + t->tv_sec = now / 1000000000; + t->tv_nsec = now % 1000000000; + return 0; + } + return -1; /* EINVAL - Clock ID is unknown */ +} +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef MAX_WORKER_THREADS +#define MAX_WORKER_THREADS (1024 * 64) +#endif +#ifndef SOCKET_TIMEOUT_QUANTUM +#define SOCKET_TIMEOUT_QUANTUM (10000) +#endif + +mg_static_assert(MAX_WORKER_THREADS >= 1, + "worker threads must be a positive number"); + +#if defined(_WIN32) \ + && !defined(__SYMBIAN32__) /* WINDOWS / UNIX include block */ +#include +#include /* DTL add for SO_EXCLUSIVE */ +#include + +typedef const char *SOCK_OPT_TYPE; + +#if !defined(PATH_MAX) +#define PATH_MAX (MAX_PATH) +#endif + +#if !defined(PATH_MAX) +#define PATH_MAX (4096) +#endif + +mg_static_assert(PATH_MAX >= 1, "path length must be a positive number"); + +#ifndef _IN_PORT_T +#ifndef in_port_t +#define in_port_t u_short +#endif +#endif + +#ifndef _WIN32_WCE +#include +#include +#include +#else /* _WIN32_WCE */ +#define NO_CGI /* WinCE has no pipes */ + +typedef long off_t; + +#define errno ((int)(GetLastError())) +#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10)) +#endif /* _WIN32_WCE */ + +#define MAKEUQUAD(lo, hi) \ + ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32)) +#define RATE_DIFF (10000000) /* 100 nsecs */ +#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de)) +#define SYS2UNIX_TIME(lo, hi) \ + ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)) + +/* Visual Studio 6 does not know __func__ or __FUNCTION__ + * The rest of MS compilers use __FUNCTION__, not C99 __func__ + * Also use _strtoui64 on modern M$ compilers */ +#if defined(_MSC_VER) +#if (_MSC_VER < 1300) +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x)) +#define strtoll(x, y, z) (_atoi64(x)) +#else +#define __func__ __FUNCTION__ +#define strtoull(x, y, z) (_strtoui64(x, y, z)) +#define strtoll(x, y, z) (_strtoi64(x, y, z)) +#endif +#endif /* _MSC_VER */ + +#define ERRNO ((int)(GetLastError())) +#define NO_SOCKLEN_T + +#if defined(_WIN64) || defined(__MINGW64__) +#define SSL_LIB "ssleay64.dll" +#define CRYPTO_LIB "libeay64.dll" +#else +#define SSL_LIB "ssleay32.dll" +#define CRYPTO_LIB "libeay32.dll" +#endif + +#define O_NONBLOCK (0) +#ifndef W_OK +#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */ +#endif +#if !defined(EWOULDBLOCK) +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif /* !EWOULDBLOCK */ +#define _POSIX_ +#define INT64_FMT "I64d" +#define UINT64_FMT "I64u" + +#define WINCDECL __cdecl +#define SHUT_RD (0) +#define SHUT_WR (1) +#define SHUT_BOTH (2) +#define vsnprintf_impl _vsnprintf +#define access _access +#define mg_sleep(x) (Sleep(x)) + +#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY) +#ifndef popen +#define popen(x, y) (_popen(x, y)) +#endif +#ifndef pclose +#define pclose(x) (_pclose(x)) +#endif +#define close(x) (_close(x)) +#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y))) +#define RTLD_LAZY (0) +#define fseeko(x, y, z) (_lseeki64(_fileno(x), (y), (z)) == -1 ? -1 : 0) +#define fdopen(x, y) (_fdopen((x), (y))) +#define write(x, y, z) (_write((x), (y), (unsigned)z)) +#define read(x, y, z) (_read((x), (y), (unsigned)z)) +#define flockfile(x) (EnterCriticalSection(&global_log_file_lock)) +#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock)) +#define sleep(x) (Sleep((x)*1000)) +#define rmdir(x) (_rmdir(x)) +#define timegm(x) (_mkgmtime(x)) + +#if !defined(fileno) +#define fileno(x) (_fileno(x)) +#endif /* !fileno MINGW #defines fileno */ + +typedef HANDLE pthread_mutex_t; +typedef DWORD pthread_key_t; +typedef HANDLE pthread_t; +typedef struct { + CRITICAL_SECTION threadIdSec; + int waitingthreadcount; /* The number of threads queued. */ + pthread_t *waitingthreadhdls; /* The thread handles. */ +} pthread_cond_t; + +#ifndef __clockid_t_defined +typedef DWORD clockid_t; +#endif +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC (1) +#endif +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME (2) +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1900) +#define _TIMESPEC_DEFINED +#endif +#ifndef _TIMESPEC_DEFINED +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; +#endif + +#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ + +static int pthread_mutex_lock(pthread_mutex_t *); +static int pthread_mutex_unlock(pthread_mutex_t *); +static void path_to_unicode(const struct mg_connection *conn, + const char *path, + wchar_t *wbuf, + size_t wbuf_len); +struct file; +static const char * +mg_fgets(char *buf, size_t size, struct file *filep, char **p); + + +#if defined(HAVE_STDINT) +#include +#else +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; +#define INT64_MAX (9223372036854775807) +#endif /* HAVE_STDINT */ + +/* POSIX dirent interface */ +struct dirent { + char d_name[PATH_MAX]; +}; + +typedef struct DIR { + HANDLE handle; + WIN32_FIND_DATAW info; + struct dirent result; +} DIR; + +#if defined(_WIN32) && !defined(POLLIN) +#ifndef HAVE_POLL +struct pollfd { + SOCKET fd; + short events; + short revents; +}; +#define POLLIN (0x0300) +#endif +#endif + +/* Mark required libraries */ +#if defined(_MSC_VER) +#pragma comment(lib, "Ws2_32.lib") +#endif + +#else /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include \ + block */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef const void *SOCK_OPT_TYPE; + +#if defined(ANDROID) +typedef unsigned short int in_port_t; +#endif + +#include +#include +#include +#include +#define vsnprintf_impl vsnprintf + +#if !defined(NO_SSL_DL) && !defined(NO_SSL) +#include +#endif +#include +#if defined(__MACH__) +#define SSL_LIB "libssl.dylib" +#define CRYPTO_LIB "libcrypto.dylib" +#else +#if !defined(SSL_LIB) +#define SSL_LIB "libssl.so" +#endif +#if !defined(CRYPTO_LIB) +#define CRYPTO_LIB "libcrypto.so" +#endif +#endif +#ifndef O_BINARY +#define O_BINARY (0) +#endif /* O_BINARY */ +#define closesocket(a) (close(a)) +#define mg_mkdir(conn, path, mode) (mkdir(path, mode)) +#define mg_remove(conn, x) (remove(x)) +#define mg_sleep(x) (usleep((x)*1000)) +#define mg_opendir(conn, x) (opendir(x)) +#define mg_closedir(x) (closedir(x)) +#define mg_readdir(x) (readdir(x)) +#define ERRNO (errno) +#define INVALID_SOCKET (-1) +#define INT64_FMT PRId64 +#define UINT64_FMT PRIu64 +typedef int SOCKET; +#define WINCDECL + +#if defined(__hpux) +/* HPUX 11 does not have monotonic, fall back to realtime */ +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +/* HPUX defines socklen_t incorrectly as size_t which is 64bit on + * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED + * the prototypes use int* rather than socklen_t* which matches the + * actual library expectation. When called with the wrong size arg + * accept() returns a zero client inet addr and check_acl() always + * fails. Since socklen_t is widely used below, just force replace + * their typedef with int. - DTL + */ +#define socklen_t int +#endif /* hpux */ + +#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include \ + block */ + +/* va_copy should always be a macro, C99 and C++11 - DTL */ +#ifndef va_copy +#define va_copy(x, y) ((x) = (y)) +#endif + +#ifdef _WIN32 +/* Create substitutes for POSIX functions in Win32. */ + +#if defined(__MINGW32__) +/* Show no warning in case system functions are not used. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + + +static CRITICAL_SECTION global_log_file_lock; +static DWORD +pthread_self(void) +{ + return GetCurrentThreadId(); +} + + +static int +pthread_key_create( + pthread_key_t *key, + void (*_ignored)(void *) /* destructor not supported for Windows */ + ) +{ + (void)_ignored; + + if ((key != 0)) { + *key = TlsAlloc(); + return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1; + } + return -2; +} + + +static int +pthread_key_delete(pthread_key_t key) +{ + return TlsFree(key) ? 0 : 1; +} + + +static int +pthread_setspecific(pthread_key_t key, void *value) +{ + return TlsSetValue(key, value) ? 0 : 1; +} + + +static void * +pthread_getspecific(pthread_key_t key) +{ + return TlsGetValue(key); +} + +#if defined(__MINGW32__) +/* Enable unused function warning again */ +#pragma GCC diagnostic pop +#endif + +static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL; +#else +static pthread_mutexattr_t pthread_mutex_attr; +#endif /* _WIN32 */ + + +#define PASSWORDS_FILE_NAME ".htpasswd" +#define CGI_ENVIRONMENT_SIZE (4096) +#define MAX_CGI_ENVIR_VARS (256) +#define MG_BUF_LEN (8192) + +#ifndef MAX_REQUEST_SIZE +#define MAX_REQUEST_SIZE (16384) +#endif + +mg_static_assert(MAX_REQUEST_SIZE >= 256, + "request size length must be a positive number"); + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +#if !defined(DEBUG_TRACE) +#if defined(DEBUG) + + +static void DEBUG_TRACE_FUNC(const char *func, + unsigned line, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(3, 4); + +static void +DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...) +{ + va_list args; + flockfile(stdout); + printf("*** %lu.%p.%s.%u: ", + (unsigned long)time(NULL), + (void *)pthread_self(), + func, + line); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putchar('\n'); + fflush(stdout); + funlockfile(stdout); +} + +#define DEBUG_TRACE(fmt, ...) \ + DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) + +#else +#define DEBUG_TRACE(fmt, ...) \ + do { \ + } while (0) +#endif /* DEBUG */ +#endif /* DEBUG_TRACE */ + +#if defined(MEMORY_DEBUGGING) +unsigned long mg_memory_debug_blockCount = 0; +unsigned long mg_memory_debug_totalMemUsed = 0; + + +static void * +mg_malloc_ex(size_t size, const char *file, unsigned line) +{ + void *data = malloc(size + sizeof(size_t)); + void *memory = 0; + char mallocStr[256]; + + if (data) { + *(size_t *)data = size; + mg_memory_debug_totalMemUsed += size; + mg_memory_debug_blockCount++; + memory = (void *)(((char *)data) + sizeof(size_t)); + } + + sprintf(mallocStr, + "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)size, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); +#if defined(_WIN32) + OutputDebugStringA(mallocStr); +#else + DEBUG_TRACE("%s", mallocStr); +#endif + + return memory; +} + + +static void * +mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line) +{ + void *data = mg_malloc_ex(size * count, file, line); + if (data) { + memset(data, 0, size); + } + return data; +} + + +static void +mg_free_ex(void *memory, const char *file, unsigned line) +{ + char mallocStr[256]; + void *data = (void *)(((char *)memory) - sizeof(size_t)); + size_t size; + + if (memory) { + size = *(size_t *)data; + mg_memory_debug_totalMemUsed -= size; + mg_memory_debug_blockCount--; + sprintf(mallocStr, + "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)size, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); +#if defined(_WIN32) + OutputDebugStringA(mallocStr); +#else + DEBUG_TRACE("%s", mallocStr); +#endif + + free(data); + } +} + + +static void * +mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line) +{ + char mallocStr[256]; + void *data; + void *_realloc; + size_t oldsize; + + if (newsize) { + if (memory) { + data = (void *)(((char *)memory) - sizeof(size_t)); + oldsize = *(size_t *)data; + _realloc = realloc(data, newsize + sizeof(size_t)); + if (_realloc) { + data = _realloc; + mg_memory_debug_totalMemUsed -= oldsize; + sprintf(mallocStr, + "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)oldsize, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); +#if defined(_WIN32) + OutputDebugStringA(mallocStr); +#else + DEBUG_TRACE("%s", mallocStr); +#endif + mg_memory_debug_totalMemUsed += newsize; + sprintf(mallocStr, + "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)newsize, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); +#if defined(_WIN32) + OutputDebugStringA(mallocStr); +#else + DEBUG_TRACE("%s", mallocStr); +#endif + *(size_t *)data = newsize; + data = (void *)(((char *)data) + sizeof(size_t)); + } else { +#if defined(_WIN32) + OutputDebugStringA("MEM: realloc failed\n"); +#else + DEBUG_TRACE("%s", "MEM: realloc failed\n"); +#endif + return _realloc; + } + } else { + data = mg_malloc_ex(newsize, file, line); + } + } else { + data = 0; + mg_free_ex(memory, file, line); + } + + return data; +} + +#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) +#define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__) +#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__) +#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) + +#else + +static __inline void * +mg_malloc(size_t a) +{ + return malloc(a); +} + +static __inline void * +mg_calloc(size_t a, size_t b) +{ + return calloc(a, b); +} + +static __inline void * +mg_realloc(void *a, size_t b) +{ + return realloc(a, b); +} + +static __inline void +mg_free(void *a) +{ + free(a); +} + +#endif + + +static void mg_vsnprintf(const struct mg_connection *conn, + int *truncated, + char *buf, + size_t buflen, + const char *fmt, + va_list ap); + +static void mg_snprintf(const struct mg_connection *conn, + int *truncated, + char *buf, + size_t buflen, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(5, 6); + +/* This following lines are just meant as a reminder to use the mg-functions + * for memory management */ +#ifdef malloc +#undef malloc +#endif +#ifdef calloc +#undef calloc +#endif +#ifdef realloc +#undef realloc +#endif +#ifdef free +#undef free +#endif +#ifdef snprintf +#undef snprintf +#endif +#ifdef vsnprintf +#undef vsnprintf +#endif +#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc +#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc +#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc +#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free +#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf +#ifdef _WIN32 /* vsnprintf must not be used in any system, * \ + * but this define only works well for Windows. */ +#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf +#endif + +#define MD5_STATIC static +#include "md5.inl" + +/* Darwin prior to 7.0 and Win32 do not have socklen_t */ +#ifdef NO_SOCKLEN_T +typedef int socklen_t; +#endif /* NO_SOCKLEN_T */ +#define _DARWIN_UNLIMITED_SELECT + +#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */ + +#if !defined(MSG_NOSIGNAL) +#define MSG_NOSIGNAL (0) +#endif + +#if !defined(SOMAXCONN) +#define SOMAXCONN (100) +#endif + +/* Size of the accepted socket queue */ +#if !defined(MGSQLEN) +#define MGSQLEN (20) +#endif + +#if defined(NO_SSL_DL) +#include +#include +#include +#include +#include +#else +/* SSL loaded dynamically from DLL. + * I put the prototypes here to be independent from OpenSSL source + * installation. */ + +typedef struct ssl_st SSL; +typedef struct ssl_method_st SSL_METHOD; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct x509_store_ctx_st X509_STORE_CTX; + +#define SSL_CTRL_OPTIONS (32) +#define SSL_CTRL_CLEAR_OPTIONS (77) +#define SSL_CTRL_SET_ECDH_AUTO (94) + +#define SSL_VERIFY_NONE (0) +#define SSL_VERIFY_PEER (1) +#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2) +#define SSL_VERIFY_CLIENT_ONCE (4) +#define SSL_OP_ALL ((long)(0x80000BFFUL)) +#define SSL_OP_NO_SSLv2 (0x01000000L) +#define SSL_OP_NO_SSLv3 (0x02000000L) +#define SSL_OP_NO_TLSv1 (0x04000000L) +#define SSL_OP_NO_TLSv1_2 (0x08000000L) +#define SSL_OP_NO_TLSv1_1 (0x10000000L) +#define SSL_OP_SINGLE_DH_USE (0x00100000L) + +struct ssl_func { + const char *name; /* SSL function name */ + void (*ptr)(void); /* Function pointer */ +}; + +#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr) +#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr) +#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr) +#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr) +#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr) +#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr) +#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr) +#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr) +#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr) +#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr) +#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr) +#define SSL_CTX_use_PrivateKey_file \ + (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr) +#define SSL_CTX_use_certificate_file \ + (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr) +#define SSL_CTX_set_default_passwd_cb \ + (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr) +#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr) +#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr) +#define SSL_CTX_use_certificate_chain_file \ + (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr) +#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr) +#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr) +#define SSL_CTX_set_verify \ + (*(void (*)(SSL_CTX *, \ + int, \ + int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19].ptr) +#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr) +#define SSL_CTX_load_verify_locations \ + (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr) +#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr) +#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr) +#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr) +#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr) +#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr) +#define SSL_CIPHER_get_name \ + (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr) +#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr) +#define SSL_CTX_set_session_id_context \ + (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr) +#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr) +#define SSL_CTX_set_cipher_list \ + (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr) +#define SSL_CTX_set_options(ctx, op) \ + SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL) +#define SSL_CTX_clear_options(ctx, op) \ + SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL) +#define SSL_CTX_set_ecdh_auto(ctx, onoff) \ + SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL) + +#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr) +#define CRYPTO_set_locking_callback \ + (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr) +#define CRYPTO_set_id_callback \ + (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr) +#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr) +#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr) +#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr) +#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr) +#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr) +#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr) +#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr) +#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr) + + +/* set_ssl_option() function updates this array. + * It loads SSL library dynamically and changes NULLs to the actual addresses + * of respective functions. The macros above (like SSL_connect()) are really + * just calling these functions indirectly via the pointer. */ +static struct ssl_func ssl_sw[] = {{"SSL_free", NULL}, + {"SSL_accept", NULL}, + {"SSL_connect", NULL}, + {"SSL_read", NULL}, + {"SSL_write", NULL}, + {"SSL_get_error", NULL}, + {"SSL_set_fd", NULL}, + {"SSL_new", NULL}, + {"SSL_CTX_new", NULL}, + {"SSLv23_server_method", NULL}, + {"SSL_library_init", NULL}, + {"SSL_CTX_use_PrivateKey_file", NULL}, + {"SSL_CTX_use_certificate_file", NULL}, + {"SSL_CTX_set_default_passwd_cb", NULL}, + {"SSL_CTX_free", NULL}, + {"SSL_load_error_strings", NULL}, + {"SSL_CTX_use_certificate_chain_file", NULL}, + {"SSLv23_client_method", NULL}, + {"SSL_pending", NULL}, + {"SSL_CTX_set_verify", NULL}, + {"SSL_shutdown", NULL}, + {"SSL_CTX_load_verify_locations", NULL}, + {"SSL_CTX_set_default_verify_paths", NULL}, + {"SSL_CTX_set_verify_depth", NULL}, + {"SSL_get_peer_certificate", NULL}, + {"SSL_get_version", NULL}, + {"SSL_get_current_cipher", NULL}, + {"SSL_CIPHER_get_name", NULL}, + {"SSL_CTX_check_private_key", NULL}, + {"SSL_CTX_set_session_id_context", NULL}, + {"SSL_CTX_ctrl", NULL}, + {"SSL_CTX_set_cipher_list", NULL}, + {NULL, NULL}}; + + +/* Similar array as ssl_sw. These functions could be located in different + * lib. */ +#if !defined(NO_SSL) +static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL}, + {"CRYPTO_set_locking_callback", NULL}, + {"CRYPTO_set_id_callback", NULL}, + {"ERR_get_error", NULL}, + {"ERR_error_string", NULL}, + {"ERR_remove_state", NULL}, + {"ERR_free_strings", NULL}, + {"ENGINE_cleanup", NULL}, + {"CONF_modules_unload", NULL}, + {"CRYPTO_cleanup_all_ex_data", NULL}, + {"EVP_cleanup", NULL}, + {NULL, NULL}}; +#endif /* NO_SSL */ +#endif /* NO_SSL_DL */ + + +#if !defined(NO_CACHING) +static const char *month_names[] = {"Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"}; +#endif /* !NO_CACHING */ + +/* Unified socket address. For IPv6 support, add IPv6 address structure in the + * union u. */ +union usa { + struct sockaddr sa; + struct sockaddr_in sin; +#if defined(USE_IPV6) + struct sockaddr_in6 sin6; +#endif +}; + +/* Describes a string (chunk of memory). */ +struct vec { + const char *ptr; + size_t len; +}; + +struct file { + uint64_t size; + time_t last_modified; + FILE *fp; + const char *membuf; /* Non-NULL if file data is in memory */ + int is_directory; + int gzipped; /* set to 1 if the content is gzipped + * in which case we need a content-encoding: gzip header */ +}; + +#define STRUCT_FILE_INITIALIZER \ + { \ + (uint64_t)0, (time_t)0, (FILE *)NULL, (const char *)NULL, 0, 0 \ + } + +/* Describes listening socket, or socket which was accept()-ed by the master + * thread and queued for future handling by the worker thread. */ +struct socket { + SOCKET sock; /* Listening socket */ + union usa lsa; /* Local socket address */ + union usa rsa; /* Remote socket address */ + unsigned char is_ssl; /* Is port SSL-ed */ + unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL + * port */ +}; + +/* NOTE(lsm): this enum shoulds be in sync with the config_options below. */ +enum { + CGI_EXTENSIONS, + CGI_ENVIRONMENT, + PUT_DELETE_PASSWORDS_FILE, + CGI_INTERPRETER, + PROTECT_URI, + AUTHENTICATION_DOMAIN, + SSI_EXTENSIONS, + THROTTLE, + ACCESS_LOG_FILE, + ENABLE_DIRECTORY_LISTING, + ERROR_LOG_FILE, + GLOBAL_PASSWORDS_FILE, + INDEX_FILES, + ENABLE_KEEP_ALIVE, + ACCESS_CONTROL_LIST, + EXTRA_MIME_TYPES, + LISTENING_PORTS, + DOCUMENT_ROOT, + SSL_CERTIFICATE, + NUM_THREADS, + RUN_AS_USER, + REWRITE, + HIDE_FILES, + REQUEST_TIMEOUT, + SSL_DO_VERIFY_PEER, + SSL_CA_PATH, + SSL_CA_FILE, + SSL_VERIFY_DEPTH, + SSL_DEFAULT_VERIFY_PATHS, + SSL_CIPHER_LIST, + SSL_PROTOCOL_VERSION, + SSL_SHORT_TRUST, +#if defined(USE_WEBSOCKET) + WEBSOCKET_TIMEOUT, +#endif + DECODE_URL, + +#if defined(USE_LUA) + LUA_PRELOAD_FILE, + LUA_SCRIPT_EXTENSIONS, + LUA_SERVER_PAGE_EXTENSIONS, +#endif +#if defined(USE_DUKTAPE) + DUKTAPE_SCRIPT_EXTENSIONS, +#endif + +#if defined(USE_WEBSOCKET) + WEBSOCKET_ROOT, +#endif +#if defined(USE_LUA) && defined(USE_WEBSOCKET) + LUA_WEBSOCKET_EXTENSIONS, +#endif + ACCESS_CONTROL_ALLOW_ORIGIN, + ERROR_PAGES, + CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the + * socket option typedef TCP_NODELAY. */ +#if !defined(NO_CACHING) + STATIC_FILE_MAX_AGE, +#endif + + NUM_OPTIONS +}; + + +/* Config option name, config types, default value */ +static struct mg_option config_options[] = { + {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, + {"cgi_environment", CONFIG_TYPE_STRING, NULL}, + {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL}, + {"cgi_interpreter", CONFIG_TYPE_FILE, NULL}, + {"protect_uri", CONFIG_TYPE_STRING, NULL}, + {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"}, + {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"}, + {"throttle", CONFIG_TYPE_STRING, NULL}, + {"access_log_file", CONFIG_TYPE_FILE, NULL}, + {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"}, + {"error_log_file", CONFIG_TYPE_FILE, NULL}, + {"global_auth_file", CONFIG_TYPE_FILE, NULL}, + {"index_files", + CONFIG_TYPE_STRING, +#ifdef USE_LUA + "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi," + "index.shtml,index.php"}, +#else + "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"}, +#endif + {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"}, + {"access_control_list", CONFIG_TYPE_STRING, NULL}, + {"extra_mime_types", CONFIG_TYPE_STRING, NULL}, + {"listening_ports", CONFIG_TYPE_STRING, "8080"}, + {"document_root", CONFIG_TYPE_DIRECTORY, NULL}, + {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, + {"num_threads", CONFIG_TYPE_NUMBER, "50"}, + {"run_as_user", CONFIG_TYPE_STRING, NULL}, + {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL}, + {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL}, + {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, + {"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"}, + {"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL}, + {"ssl_ca_file", CONFIG_TYPE_FILE, NULL}, + {"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"}, + {"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"}, + {"ssl_cipher_list", CONFIG_TYPE_STRING, NULL}, + {"ssl_protocol_version", CONFIG_TYPE_NUMBER, "0"}, + {"ssl_short_trust", CONFIG_TYPE_BOOLEAN, "no"}, +#if defined(USE_WEBSOCKET) + {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, +#endif + {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"}, + +#if defined(USE_LUA) + {"lua_preload_file", CONFIG_TYPE_FILE, NULL}, + {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, + {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, +#endif +#if defined(USE_DUKTAPE) + /* The support for duktape is still in alpha version state. + * The name of this config option might change. */ + {"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"}, +#endif + +#if defined(USE_WEBSOCKET) + {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL}, +#endif +#if defined(USE_LUA) && defined(USE_WEBSOCKET) + {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, +#endif + {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"}, + {"error_pages", CONFIG_TYPE_DIRECTORY, NULL}, + {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"}, +#if !defined(NO_CACHING) + {"static_file_max_age", CONFIG_TYPE_NUMBER, "3600"}, +#endif + + {NULL, CONFIG_TYPE_UNKNOWN, NULL}}; + +/* Check if the config_options and the corresponding enum have compatible + * sizes. */ +mg_static_assert((sizeof(config_options) / sizeof(config_options[0])) + == (NUM_OPTIONS + 1), + "config_options and enum not sync"); + +enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER }; + +struct mg_handler_info { + /* Name/Pattern of the URI. */ + char *uri; + size_t uri_len; + + /* handler type */ + int handler_type; + + /* Handler for http/https or authorization requests. */ + mg_request_handler handler; + + /* Handler for ws/wss (websocket) requests. */ + mg_websocket_connect_handler connect_handler; + mg_websocket_ready_handler ready_handler; + mg_websocket_data_handler data_handler; + mg_websocket_close_handler close_handler; + + /* Handler for authorization requests */ + mg_authorization_handler auth_handler; + + /* User supplied argument for the handler function. */ + void *cbdata; + + /* next handler in a linked list */ + struct mg_handler_info *next; +}; + +struct mg_context { + volatile int stop_flag; /* Should we stop event loop */ + SSL_CTX *ssl_ctx; /* SSL context */ + char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */ + struct mg_callbacks callbacks; /* User-defined callback function */ + void *user_data; /* User-defined data */ + int context_type; /* 1 = server context, 2 = client context */ + + struct socket *listening_sockets; + in_port_t *listening_ports; + unsigned int num_listening_sockets; + + volatile int + running_worker_threads; /* Number of currently running worker threads */ + pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */ + pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */ + + struct socket queue[MGSQLEN]; /* Accepted sockets */ + volatile int sq_head; /* Head of the socket queue */ + volatile int sq_tail; /* Tail of the socket queue */ + pthread_cond_t sq_full; /* Signaled when socket is produced */ + pthread_cond_t sq_empty; /* Signaled when socket is consumed */ + pthread_t masterthreadid; /* The master thread ID */ + unsigned int + cfg_worker_threads; /* The number of configured worker threads. */ + pthread_t *workerthreadids; /* The worker thread IDs */ + + time_t start_time; /* Server start time, used for authentication */ + uint64_t auth_nonce_mask; /* Mask for all nonce values */ + pthread_mutex_t nonce_mutex; /* Protects nonce_count */ + unsigned long nonce_count; /* Used nonces, used for authentication */ + + char *systemName; /* What operating system is running */ + + /* linked list of uri handlers */ + struct mg_handler_info *handlers; + +#if defined(USE_LUA) && defined(USE_WEBSOCKET) + /* linked list of shared lua websockets */ + struct mg_shared_lua_websocket_list *shared_lua_websockets; +#endif + +#ifdef USE_TIMERS + struct ttimers *timers; +#endif +}; + + +struct mg_connection { + struct mg_request_info request_info; + struct mg_context *ctx; + SSL *ssl; /* SSL descriptor */ + SSL_CTX *client_ssl_ctx; /* SSL context for client connections */ + struct socket client; /* Connected client */ + time_t conn_birth_time; /* Time (wall clock) when connection was + * established */ + struct timespec req_time; /* Time (since system start) when the request + * was received */ + int64_t num_bytes_sent; /* Total bytes sent to client */ + int64_t content_len; /* Content-Length header value */ + int64_t consumed_content; /* How many bytes of content have been read */ + int is_chunked; /* Transfer-Encoding is chunked: 0=no, 1=yes: + * data available, 2: all data read */ + size_t chunk_remainder; /* Unread data from the last chunk */ + char *buf; /* Buffer for received data */ + char *path_info; /* PATH_INFO part of the URL */ + + int must_close; /* 1 if connection must be closed */ + int in_error_handler; /* 1 if in handler for user defined error + * pages */ + int internal_error; /* 1 if an error occured while processing the + * request */ + + int buf_size; /* Buffer size */ + int request_len; /* Size of the request + headers in a buffer */ + int data_len; /* Total size of data in a buffer */ + int status_code; /* HTTP reply status code, e.g. 200 */ + int throttle; /* Throttling, bytes/sec. <= 0 means no + * throttle */ + time_t last_throttle_time; /* Last time throttled data was sent */ + int64_t last_throttle_bytes; /* Bytes sent this second */ + pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure + * atomic transmissions for websockets */ +#if defined(USE_LUA) && defined(USE_WEBSOCKET) + void *lua_websocket_state; /* Lua_State for a websocket connection */ +#endif +}; + + +static pthread_key_t sTlsKey; /* Thread local storage index */ +static int sTlsInit = 0; +static int thread_idx_max = 0; + + +struct mg_workerTLS { + int is_master; + unsigned long thread_idx; +#if defined(_WIN32) && !defined(__SYMBIAN32__) + HANDLE pthread_cond_helper_mutex; +#endif +}; + +/* Directory entry */ +struct de { + struct mg_connection *conn; + char *file_name; + struct file file; +}; + + +#if defined(USE_WEBSOCKET) +static int is_websocket_protocol(const struct mg_connection *conn); +#else +#define is_websocket_protocol(conn) (0) +#endif + + +static int +mg_atomic_inc(volatile int *addr) +{ + int ret; +#if defined(_WIN32) && !defined(__SYMBIAN32__) + /* Depending on the SDK, this function uses either + * (volatile unsigned int *) or (volatile LONG *), + * so whatever you use, the other SDK is likely to raise a warning. */ + ret = InterlockedIncrement((volatile long *)addr); +#elif defined(__GNUC__) \ + && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) + ret = __sync_add_and_fetch(addr, 1); +#else + ret = (++(*addr)); +#endif + return ret; +} + + +static int +mg_atomic_dec(volatile int *addr) +{ + int ret; +#if defined(_WIN32) && !defined(__SYMBIAN32__) + /* Depending on the SDK, this function uses either + * (volatile unsigned int *) or (volatile LONG *), + * so whatever you use, the other SDK is likely to raise a warning. */ + ret = InterlockedDecrement((volatile long *)addr); +#elif defined(__GNUC__) \ + && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) + ret = __sync_sub_and_fetch(addr, 1); +#else + ret = (--(*addr)); +#endif + return ret; +} + +#if !defined(NO_THREAD_NAME) +#if defined(_WIN32) && defined(_MSC_VER) +/* Set the thread name for debugging purposes in Visual Studio + * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx + */ +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO { + DWORD dwType; /* Must be 0x1000. */ + LPCSTR szName; /* Pointer to name (in user addr space). */ + DWORD dwThreadID; /* Thread ID (-1=caller thread). */ + DWORD dwFlags; /* Reserved for future use, must be zero. */ +} THREADNAME_INFO; +#pragma pack(pop) +#elif defined(__linux__) +#include +#include +#endif + + +static void +mg_set_thread_name(const char *name) +{ + char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */ + + mg_snprintf( + NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name); + +#if defined(_WIN32) +#if defined(_MSC_VER) + /* Windows and Visual Studio Compiler */ + __try + { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = ~0U; + info.dwFlags = 0; + + RaiseException(0x406D1388, + 0, + sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR *)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#elif defined(__MINGW32__) +/* No option known to set thread name for MinGW */ +#endif +#elif defined(__GLIBC__) \ + && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12))) + /* pthread_setname_np first appeared in glibc in version 2.12*/ + (void)pthread_setname_np(pthread_self(), threadName); +#elif defined(__linux__) + /* on linux we can use the old prctl function */ + (void)prctl(PR_SET_NAME, threadName, 0, 0, 0); +#endif +} +#else /* !defined(NO_THREAD_NAME) */ +void +mg_set_thread_name(const char *threadName) +{ +} +#endif + + +#if defined(MG_LEGACY_INTERFACE) +const char ** +mg_get_valid_option_names(void) +{ + /* This function is deprecated. Use mg_get_valid_options instead. */ + static const char * + data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; + int i; + + for (i = 0; config_options[i].name != NULL; i++) { + data[i * 2] = config_options[i].name; + data[i * 2 + 1] = config_options[i].default_value; + } + + return data; +} +#endif + + +const struct mg_option * +mg_get_valid_options(void) +{ + return config_options; +} + + +static int +is_file_in_memory(const struct mg_connection *conn, + const char *path, + struct file *filep) +{ + size_t size = 0; + if (!conn || !filep) { + return 0; + } + + if (conn->ctx->callbacks.open_file) { + filep->membuf = conn->ctx->callbacks.open_file(conn, path, &size); + if (filep->membuf != NULL) { + /* NOTE: override filep->size only on success. Otherwise, it might + * break constructs like if (!mg_stat() || !mg_fopen()) ... */ + filep->size = size; + } + } + + return filep->membuf != NULL; +} + + +static int +is_file_opened(const struct file *filep) +{ + if (!filep) { + return 0; + } + + return filep->membuf != NULL || filep->fp != NULL; +} + + +/* mg_fopen will open a file either in memory or on the disk. + * The input parameter path is a string in UTF-8 encoding. + * The input parameter mode is the same as for fopen. + * Either fp or membuf will be set in the output struct filep. + * The function returns 1 on success, 0 on error. */ +static int +mg_fopen(const struct mg_connection *conn, + const char *path, + const char *mode, + struct file *filep) +{ + struct stat st; + + if (!filep) { + return 0; + } + + /* TODO (high): mg_fopen should only open a file, while mg_stat should + * only get the file status. They should not work on different members of + * the same structure (bad cohesion). */ + memset(filep, 0, sizeof(*filep)); + + if (stat(path, &st) == 0) { + filep->size = (uint64_t)(st.st_size); + } + + if (!is_file_in_memory(conn, path, filep)) { +#ifdef _WIN32 + wchar_t wbuf[PATH_MAX], wmode[20]; + path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); + MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); + filep->fp = _wfopen(wbuf, wmode); +#else + /* Linux et al already use unicode. No need to convert. */ + filep->fp = fopen(path, mode); +#endif + } + + return is_file_opened(filep); +} + + +static void +mg_fclose(struct file *filep) +{ + if (filep != NULL && filep->fp != NULL) { + fclose(filep->fp); + } +} + + +static void +mg_strlcpy(register char *dst, register const char *src, size_t n) +{ + for (; *src != '\0' && n > 1; n--) { + *dst++ = *src++; + } + *dst = '\0'; +} + + +static int +lowercase(const char *s) +{ + return tolower(*(const unsigned char *)s); +} + + +int +mg_strncasecmp(const char *s1, const char *s2, size_t len) +{ + int diff = 0; + + if (len > 0) { + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); + } + + return diff; +} + + +int +mg_strcasecmp(const char *s1, const char *s2) +{ + int diff; + + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0'); + + return diff; +} + + +static char * +mg_strndup(const char *ptr, size_t len) +{ + char *p; + + if ((p = (char *)mg_malloc(len + 1)) != NULL) { + mg_strlcpy(p, ptr, len + 1); + } + + return p; +} + + +static char * +mg_strdup(const char *str) +{ + return mg_strndup(str, strlen(str)); +} + + +static const char * +mg_strcasestr(const char *big_str, const char *small_str) +{ + size_t i, big_len = strlen(big_str), small_len = strlen(small_str); + + if (big_len >= small_len) { + for (i = 0; i <= (big_len - small_len); i++) { + if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { + return big_str + i; + } + } + } + + return NULL; +} + + +/* Return null terminated string of given maximum length. + * Report errors if length is exceeded. */ +static void +mg_vsnprintf(const struct mg_connection *conn, + int *truncated, + char *buf, + size_t buflen, + const char *fmt, + va_list ap) +{ + int n, ok; + + if (buflen == 0) { + return; + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +/* Using fmt as a non-literal is intended here, since it is mostly called + * indirectly by mg_snprintf */ +#endif + + n = (int)vsnprintf_impl(buf, buflen, fmt, ap); + ok = (n >= 0) && ((size_t)n < buflen); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + if (ok) { + if (truncated) { + *truncated = 0; + } + } else { + if (truncated) { + *truncated = 1; + } + mg_cry(conn, + "truncating vsnprintf buffer: [%.*s]", + (int)((buflen > 200) ? 200 : (buflen - 1)), + buf); + n = (int)buflen - 1; + } + buf[n] = '\0'; +} + + +static void +mg_snprintf(const struct mg_connection *conn, + int *truncated, + char *buf, + size_t buflen, + const char *fmt, + ...) +{ + va_list ap; + + va_start(ap, fmt); + mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap); + va_end(ap); +} + + +static int +get_option_index(const char *name) +{ + int i; + + for (i = 0; config_options[i].name != NULL; i++) { + if (strcmp(config_options[i].name, name) == 0) { + return i; + } + } + return -1; +} + + +const char * +mg_get_option(const struct mg_context *ctx, const char *name) +{ + int i; + if ((i = get_option_index(name)) == -1) { + return NULL; + } else if (!ctx || ctx->config[i] == NULL) { + return ""; + } else { + return ctx->config[i]; + } +} + + +struct mg_context * +mg_get_context(const struct mg_connection *conn) +{ + return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx); +} + + +void * +mg_get_user_data(const struct mg_context *ctx) +{ + return (ctx == NULL) ? NULL : ctx->user_data; +} + + +void +mg_set_user_connection_data(struct mg_connection *conn, void *data) +{ + if (conn != NULL) { + conn->request_info.conn_data = data; + } +} + + +void * +mg_get_user_connection_data(const struct mg_connection *conn) +{ + if (conn != NULL) { + return conn->request_info.conn_data; + } + return NULL; +} + + +size_t +mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl) +{ + size_t i; + if (!ctx) { + return 0; + } + for (i = 0; i < size && i < ctx->num_listening_sockets; i++) { + ssl[i] = ctx->listening_sockets[i].is_ssl; + ports[i] = ctx->listening_ports[i]; + } + return i; +} + + +int +mg_get_server_ports(const struct mg_context *ctx, + int size, + struct mg_server_ports *ports) +{ + int i, cnt = 0; + + if (size <= 0) { + return -1; + } + memset(ports, 0, sizeof(*ports) * (size_t)size); + if (!ctx) { + return -1; + } + if (!ctx->listening_sockets || !ctx->listening_ports) { + return -1; + } + + for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) { + + ports[cnt].port = ctx->listening_ports[i]; + ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl; + ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir; + + if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) { + /* IPv4 */ + ports[cnt].protocol = 1; + cnt++; + } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) { + /* IPv6 */ + ports[cnt].protocol = 3; + cnt++; + } + } + + return cnt; +} + + +static void +sockaddr_to_string(char *buf, size_t len, const union usa *usa) +{ + buf[0] = '\0'; + + if (!usa) { + return; + } + + if (usa->sa.sa_family == AF_INET) { + getnameinfo(&usa->sa, + sizeof(usa->sin), + buf, + (unsigned)len, + NULL, + 0, + NI_NUMERICHOST); + } +#if defined(USE_IPV6) + else if (usa->sa.sa_family == AF_INET6) { + getnameinfo(&usa->sa, + sizeof(usa->sin6), + buf, + (unsigned)len, + NULL, + 0, + NI_NUMERICHOST); + } +#endif +} + + +/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be + * included in all responses other than 100, 101, 5xx. */ +static void +gmt_time_string(char *buf, size_t buf_len, time_t *t) +{ + struct tm *tm; + + tm = ((t != NULL) ? gmtime(t) : NULL); + if (tm != NULL) { + strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); + } else { + mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); + buf[buf_len - 1] = '\0'; + } +} + + +/* difftime for struct timespec. Return value is in seconds. */ +static double +mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before) +{ + return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9 + + (double)(ts_now->tv_sec - ts_before->tv_sec); +} + + +/* Print error message to the opened error log stream. */ +void +mg_cry(const struct mg_connection *conn, const char *fmt, ...) +{ + char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; + va_list ap; + struct file fi; + time_t timestamp; + + va_start(ap, fmt); + IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap)); + va_end(ap); + buf[sizeof(buf) - 1] = 0; + + if (!conn) { + puts(buf); + return; + } + + /* Do not lock when getting the callback value, here and below. + * I suppose this is fine, since function cannot disappear in the + * same way string option can. */ + if ((conn->ctx->callbacks.log_message == NULL) + || (conn->ctx->callbacks.log_message(conn, buf) == 0)) { + + if (conn->ctx->config[ERROR_LOG_FILE] != NULL) { + if (mg_fopen(conn, conn->ctx->config[ERROR_LOG_FILE], "a+", &fi) + == 0) { + fi.fp = NULL; + } + } else { + fi.fp = NULL; + } + + if (fi.fp != NULL) { + flockfile(fi.fp); + timestamp = time(NULL); + + sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); + fprintf(fi.fp, + "[%010lu] [error] [client %s] ", + (unsigned long)timestamp, + src_addr); + + if (conn->request_info.request_method != NULL) { + fprintf(fi.fp, + "%s %s: ", + conn->request_info.request_method, + conn->request_info.request_uri); + } + + fprintf(fi.fp, "%s", buf); + fputc('\n', fi.fp); + fflush(fi.fp); + funlockfile(fi.fp); + mg_fclose(&fi); + } + } +} + + +/* Return fake connection structure. Used for logging, if connection + * is not applicable at the moment of logging. */ +static struct mg_connection * +fc(struct mg_context *ctx) +{ + static struct mg_connection fake_connection; + fake_connection.ctx = ctx; + return &fake_connection; +} + + +const char * +mg_version(void) +{ + return CIVETWEB_VERSION; +} + + +const struct mg_request_info * +mg_get_request_info(const struct mg_connection *conn) +{ + if (!conn) { + return NULL; + } + return &conn->request_info; +} + + +/* Skip the characters until one of the delimiters characters found. + * 0-terminate resulting word. Skip the delimiter and following whitespaces. + * Advance pointer to buffer to the next word. Return found 0-terminated word. + * Delimiters can be quoted with quotechar. */ +static char * +skip_quoted(char **buf, + const char *delimiters, + const char *whitespace, + char quotechar) +{ + char *p, *begin_word, *end_word, *end_whitespace; + + begin_word = *buf; + end_word = begin_word + strcspn(begin_word, delimiters); + + /* Check for quotechar */ + if (end_word > begin_word) { + p = end_word - 1; + while (*p == quotechar) { + /* While the delimiter is quoted, look for the next delimiter. */ + /* This happens, e.g., in calls from parse_auth_header, + * if the user name contains a " character. */ + + /* If there is anything beyond end_word, copy it. */ + if (*end_word != '\0') { + size_t end_off = strcspn(end_word + 1, delimiters); + memmove(p, end_word, end_off + 1); + p += end_off; /* p must correspond to end_word - 1 */ + end_word += end_off + 1; + } else { + *p = '\0'; + break; + } + } + for (p++; p < end_word; p++) { + *p = '\0'; + } + } + + if (*end_word == '\0') { + *buf = end_word; + } else { + end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); + + for (p = end_word; p < end_whitespace; p++) { + *p = '\0'; + } + + *buf = end_whitespace; + } + + return begin_word; +} + + +/* Simplified version of skip_quoted without quote char + * and whitespace == delimiters */ +static char * +skip(char **buf, const char *delimiters) +{ + return skip_quoted(buf, delimiters, delimiters, 0); +} + + +/* Return HTTP header value, or NULL if not found. */ +static const char * +get_header(const struct mg_request_info *ri, const char *name) +{ + int i; + if (ri) { + for (i = 0; i < ri->num_headers; i++) { + if (!mg_strcasecmp(name, ri->http_headers[i].name)) { + return ri->http_headers[i].value; + } + } + } + + return NULL; +} + + +const char * +mg_get_header(const struct mg_connection *conn, const char *name) +{ + if (!conn) { + return NULL; + } + + return get_header(&conn->request_info, name); +} + + +/* A helper function for traversing a comma separated list of values. + * It returns a list pointer shifted to the next value, or NULL if the end + * of the list found. + * Value is stored in val vector. If value has form "x=y", then eq_val + * vector is initialized to point to the "y" part, and val vector length + * is adjusted to point only to "x". */ +static const char * +next_option(const char *list, struct vec *val, struct vec *eq_val) +{ + int end; + +reparse: + if (val == NULL || list == NULL || *list == '\0') { + /* End of the list */ + list = NULL; + } else { + /* Skip over leading LWS */ + while (*list == ' ' || *list == '\t') + list++; + + val->ptr = list; + if ((list = strchr(val->ptr, ',')) != NULL) { + /* Comma found. Store length and shift the list ptr */ + val->len = ((size_t)(list - val->ptr)); + list++; + } else { + /* This value is the last one */ + list = val->ptr + strlen(val->ptr); + val->len = ((size_t)(list - val->ptr)); + } + + /* Adjust length for trailing LWS */ + end = (int)val->len - 1; + while (end >= 0 && (val->ptr[end] == ' ' || val->ptr[end] == '\t')) + end--; + val->len = (size_t)(end + 1); + + if (val->len == 0) { + /* Ignore any empty entries. */ + goto reparse; + } + + if (eq_val != NULL) { + /* Value has form "x=y", adjust pointers and lengths + * so that val points to "x", and eq_val points to "y". */ + eq_val->len = 0; + eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len); + if (eq_val->ptr != NULL) { + eq_val->ptr++; /* Skip over '=' character */ + eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len; + val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1; + } + } + } + + return list; +} + +/* A helper function for checking if a comma separated list of values contains + * the given option (case insensitvely). + * 'header' can be NULL, in which case false is returned. */ +static int +header_has_option(const char *header, const char *option) +{ + struct vec opt_vec; + struct vec eq_vec; + + assert(option != NULL); + assert(option[0] != '\0'); + + while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) { + if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0) + return 1; + } + + return 0; +} + +/* Perform case-insensitive match of string against pattern */ +static int +match_prefix(const char *pattern, size_t pattern_len, const char *str) +{ + const char *or_str; + size_t i; + int j, len, res; + + if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) { + res = match_prefix(pattern, (size_t)(or_str - pattern), str); + return res > 0 ? res : match_prefix(or_str + 1, + (size_t)((pattern + pattern_len) + - (or_str + 1)), + str); + } + + for (i = 0, j = 0; i < pattern_len; i++, j++) { + if (pattern[i] == '?' && str[j] != '\0') { + continue; + } else if (pattern[i] == '$') { + return str[j] == '\0' ? j : -1; + } else if (pattern[i] == '*') { + i++; + if (pattern[i] == '*') { + i++; + len = (int)strlen(str + j); + } else { + len = (int)strcspn(str + j, "/"); + } + if (i == pattern_len) { + return j + len; + } + do { + res = match_prefix(pattern + i, pattern_len - i, str + j + len); + } while (res == -1 && len-- > 0); + return res == -1 ? -1 : j + res + len; + } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { + return -1; + } + } + return j; +} + + +/* HTTP 1.1 assumes keep alive if "Connection:" header is not set + * This function must tolerate situations when connection info is not + * set up, for example if request parsing failed. */ +static int +should_keep_alive(const struct mg_connection *conn) +{ + if (conn != NULL) { + const char *http_version = conn->request_info.http_version; + const char *header = mg_get_header(conn, "Connection"); + if (conn->must_close || conn->internal_error || conn->status_code == 401 + || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 + || (header != NULL && !header_has_option(header, "keep-alive")) + || (header == NULL && http_version + && 0 != strcmp(http_version, "1.1"))) { + return 0; + } + return 1; + } + return 0; +} + + +static int +should_decode_url(const struct mg_connection *conn) +{ + if (!conn || !conn->ctx) { + return 0; + } + + return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0); +} + + +static const char * +suggest_connection_header(const struct mg_connection *conn) +{ + return should_keep_alive(conn) ? "keep-alive" : "close"; +} + + +static int +send_no_cache_header(struct mg_connection *conn) +{ + /* Send all current and obsolete cache opt-out directives. */ + return mg_printf(conn, + "Cache-Control: no-cache, no-store, " + "must-revalidate, private, max-age=0\r\n" + "Pragma: no-cache\r\n" + "Expires: 0\r\n"); +} + + +static int +send_static_cache_header(struct mg_connection *conn) +{ +#if !defined(NO_CACHING) + /* Read the server config to check how long a file may be cached. + * The configuration is in seconds. */ + int max_age = atoi(conn->ctx->config[STATIC_FILE_MAX_AGE]); + if (max_age <= 0) { + /* 0 means "do not cache". All values <0 are reserved + * and may be used differently in the future. */ + /* If a file should not be cached, do not only send + * max-age=0, but also pragmas and Expires headers. */ + return send_no_cache_header(conn); + } + + /* Use "Cache-Control: max-age" instead of "Expires" header. + * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */ + /* See also https://www.mnot.net/cache_docs/ */ + /* According to RFC 2616, Section 14.21, caching times should not exceed + * one year. A year with 365 days corresponds to 31536000 seconds, a leap + * year to 31622400 seconds. For the moment, we just send whatever has + * been configured, still the behavior for >1 year should be considered + * as undefined. */ + return mg_printf(conn, "Cache-Control: max-age=%u\r\n", (unsigned)max_age); +#else /* NO_CACHING */ + return send_no_cache_header(conn); +#endif /* !NO_CACHING */ +} + + +static void handle_file_based_request(struct mg_connection *conn, + const char *path, + struct file *filep); + +static int +mg_stat(struct mg_connection *conn, const char *path, struct file *filep); + + +const char * +mg_get_response_code_text(struct mg_connection *conn, int response_code) +{ + /* See IANA HTTP status code assignment: + * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + */ + + switch (response_code) { + /* RFC2616 Section 10.1 - Informational 1xx */ + case 100: + return "Continue"; /* RFC2616 Section 10.1.1 */ + case 101: + return "Switching Protocols"; /* RFC2616 Section 10.1.2 */ + case 102: + return "Processing"; /* RFC2518 Section 10.1 */ + + /* RFC2616 Section 10.2 - Successful 2xx */ + case 200: + return "OK"; /* RFC2616 Section 10.2.1 */ + case 201: + return "Created"; /* RFC2616 Section 10.2.2 */ + case 202: + return "Accepted"; /* RFC2616 Section 10.2.3 */ + case 203: + return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */ + case 204: + return "No Content"; /* RFC2616 Section 10.2.5 */ + case 205: + return "Reset Content"; /* RFC2616 Section 10.2.6 */ + case 206: + return "Partial Content"; /* RFC2616 Section 10.2.7 */ + case 207: + return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */ + case 208: + return "Already Reported"; /* RFC5842 Section 7.1 */ + + case 226: + return "IM used"; /* RFC3229 Section 10.4.1 */ + + /* RFC2616 Section 10.3 - Redirection 3xx */ + case 300: + return "Multiple Choices"; /* RFC2616 Section 10.3.1 */ + case 301: + return "Moved Permanently"; /* RFC2616 Section 10.3.2 */ + case 302: + return "Found"; /* RFC2616 Section 10.3.3 */ + case 303: + return "See Other"; /* RFC2616 Section 10.3.4 */ + case 304: + return "Not Modified"; /* RFC2616 Section 10.3.5 */ + case 305: + return "Use Proxy"; /* RFC2616 Section 10.3.6 */ + case 307: + return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */ + case 308: + return "Permanent Redirect"; /* RFC7238 Section 3 */ + + /* RFC2616 Section 10.4 - Client Error 4xx */ + case 400: + return "Bad Request"; /* RFC2616 Section 10.4.1 */ + case 401: + return "Unauthorized"; /* RFC2616 Section 10.4.2 */ + case 402: + return "Payment Required"; /* RFC2616 Section 10.4.3 */ + case 403: + return "Forbidden"; /* RFC2616 Section 10.4.4 */ + case 404: + return "Not Found"; /* RFC2616 Section 10.4.5 */ + case 405: + return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */ + case 406: + return "Not Acceptable"; /* RFC2616 Section 10.4.7 */ + case 407: + return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */ + case 408: + return "Request Time-out"; /* RFC2616 Section 10.4.9 */ + case 409: + return "Conflict"; /* RFC2616 Section 10.4.10 */ + case 410: + return "Gone"; /* RFC2616 Section 10.4.11 */ + case 411: + return "Length Required"; /* RFC2616 Section 10.4.12 */ + case 412: + return "Precondition Failed"; /* RFC2616 Section 10.4.13 */ + case 413: + return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */ + case 414: + return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */ + case 415: + return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */ + case 416: + return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 */ + case 417: + return "Expectation Failed"; /* RFC2616 Section 10.4.18 */ + + case 421: + return "Misdirected Request"; /* RFC7540 Section 9.1.2 */ + case 422: + return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918 + * Section 11.2 */ + case 423: + return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */ + case 424: + return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918 + * Section 11.4 */ + + case 426: + return "Upgrade Required"; /* RFC 2817 Section 4 */ + + case 428: + return "Precondition Required"; /* RFC 6585, Section 3 */ + case 429: + return "Too Many Requests"; /* RFC 6585, Section 4 */ + + case 431: + return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */ + + case 451: + return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05, + * Section 3 */ + + /* RFC2616 Section 10.5 - Server Error 5xx */ + case 500: + return "Internal Server Error"; /* RFC2616 Section 10.5.1 */ + case 501: + return "Not Implemented"; /* RFC2616 Section 10.5.2 */ + case 502: + return "Bad Gateway"; /* RFC2616 Section 10.5.3 */ + case 503: + return "Service Unavailable"; /* RFC2616 Section 10.5.4 */ + case 504: + return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */ + case 505: + return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */ + case 506: + return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */ + case 507: + return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918 + * Section 11.5 */ + case 508: + return "Loop Detected"; /* RFC5842 Section 7.1 */ + + case 510: + return "Not Extended"; /* RFC 2774, Section 7 */ + case 511: + return "Network Authentication Required"; /* RFC 6585, Section 6 */ + + /* Other status codes, not shown in the IANA HTTP status code assignment. + * E.g., "de facto" standards due to common use, ... */ + case 418: + return "I am a teapot"; /* RFC2324 Section 2.3.2 */ + case 419: + return "Authentication Timeout"; /* common use */ + case 420: + return "Enhance Your Calm"; /* common use */ + case 440: + return "Login Timeout"; /* common use */ + case 509: + return "Bandwidth Limit Exceeded"; /* common use */ + + default: + /* This error code is unknown. This should not happen. */ + if (conn) { + mg_cry(conn, "Unknown HTTP response code: %u", response_code); + } + + /* Return at least a category according to RFC 2616 Section 10. */ + if (response_code >= 100 && response_code < 200) { + /* Unknown informational status code */ + return "Information"; + } + if (response_code >= 200 && response_code < 300) { + /* Unknown success code */ + return "Success"; + } + if (response_code >= 300 && response_code < 400) { + /* Unknown redirection code */ + return "Redirection"; + } + if (response_code >= 400 && response_code < 500) { + /* Unknown request error code */ + return "Client Error"; + } + if (response_code >= 500 && response_code < 600) { + /* Unknown server error code */ + return "Server Error"; + } + + /* Response code not even within reasonable range */ + return ""; + } +} + + +static void send_http_error(struct mg_connection *, + int, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(3, 4); + +static void +send_http_error(struct mg_connection *conn, int status, const char *fmt, ...) +{ + char buf[MG_BUF_LEN]; + va_list ap; + int len, i, page_handler_found, scope, truncated; + char date[64]; + time_t curtime = time(NULL); + const char *error_handler = NULL; + struct file error_page_file = STRUCT_FILE_INITIALIZER; + const char *error_page_file_ext, *tstr; + + const char *status_text = mg_get_response_code_text(conn, status); + + if (conn == NULL) { + return; + } + + conn->status_code = status; + if (conn->in_error_handler || conn->ctx->callbacks.http_error == NULL + || conn->ctx->callbacks.http_error(conn, status)) { + if (!conn->in_error_handler) { + /* Send user defined error pages, if defined */ + error_handler = conn->ctx->config[ERROR_PAGES]; + error_page_file_ext = conn->ctx->config[INDEX_FILES]; + page_handler_found = 0; + if (error_handler != NULL) { + for (scope = 1; (scope <= 3) && !page_handler_found; scope++) { + switch (scope) { + case 1: /* Handler for specific error, e.g. 404 error */ + mg_snprintf(conn, + &truncated, + buf, + sizeof(buf) - 32, + "%serror%03u.", + error_handler, + status); + break; + case 2: /* Handler for error group, e.g., 5xx error handler + * for all server errors (500-599) */ + mg_snprintf(conn, + &truncated, + buf, + sizeof(buf) - 32, + "%serror%01uxx.", + error_handler, + status / 100); + break; + default: /* Handler for all errors */ + mg_snprintf(conn, + &truncated, + buf, + sizeof(buf) - 32, + "%serror.", + error_handler); + break; + } + + /* String truncation in buf may only occur if error_handler + * is too long. This string is from the config, not from a + * client. */ + (void)truncated; + + len = (int)strlen(buf); + + tstr = strchr(error_page_file_ext, '.'); + + while (tstr) { + for (i = 1; i < 32 && tstr[i] != 0 && tstr[i] != ','; + i++) + buf[len + i - 1] = tstr[i]; + buf[len + i - 1] = 0; + if (mg_stat(conn, buf, &error_page_file)) { + page_handler_found = 1; + break; + } + tstr = strchr(tstr + i, '.'); + } + } + } + + if (page_handler_found) { + conn->in_error_handler = 1; + handle_file_based_request(conn, buf, &error_page_file); + conn->in_error_handler = 0; + return; + } + } + + /* No custom error page. Send default error page. */ + gmt_time_string(date, sizeof(date), &curtime); + + conn->must_close = 1; + mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text); + send_no_cache_header(conn); + mg_printf(conn, + "Date: %s\r\n" + "Connection: close\r\n\r\n", + date); + + /* Errors 1xx, 204 and 304 MUST NOT send a body */ + if (status > 199 && status != 204 && status != 304) { + + mg_printf(conn, "Error %d: %s\n", status, status_text); + + if (fmt != NULL) { + va_start(ap, fmt); + mg_vsnprintf(conn, NULL, buf, sizeof(buf), fmt, ap); + va_end(ap); + mg_write(conn, buf, strlen(buf)); + DEBUG_TRACE("Error %i - [%s]", status, buf); + } + + } else { + /* No body allowed. Close the connection. */ + DEBUG_TRACE("Error %i", status); + } + } +} + +#if defined(_WIN32) && !defined(__SYMBIAN32__) +/* Create substitutes for POSIX functions in Win32. */ + +#if defined(__MINGW32__) +/* Show no warning in case system functions are not used. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + + +static int +pthread_mutex_init(pthread_mutex_t *mutex, void *unused) +{ + (void)unused; + *mutex = CreateMutex(NULL, FALSE, NULL); + return *mutex == NULL ? -1 : 0; +} + + +static int +pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + return CloseHandle(*mutex) == 0 ? -1 : 0; +} + + +static int +pthread_mutex_lock(pthread_mutex_t *mutex) +{ + return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1; +} + + +#ifdef ENABLE_UNUSED_PTHREAD_FUNCTIONS +static int +pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + switch (WaitForSingleObject(*mutex, 0)) { + case WAIT_OBJECT_0: + return 0; + case WAIT_TIMEOUT: + return -2; /* EBUSY */ + } + return -1; +} +#endif + + +static int +pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + return ReleaseMutex(*mutex) == 0 ? -1 : 0; +} + + +#ifndef WIN_PTHREADS_TIME_H +static int +clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + FILETIME ft; + ULARGE_INTEGER li; + BOOL ok = FALSE; + double d; + static double perfcnt_per_sec = 0.0; + + if (tp) { + memset(tp, 0, sizeof(*tp)); + if (clk_id == CLOCK_REALTIME) { + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */ + tp->tv_sec = (time_t)(li.QuadPart / 10000000); + tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; + ok = TRUE; + } else if (clk_id == CLOCK_MONOTONIC) { + if (perfcnt_per_sec == 0.0) { + QueryPerformanceFrequency((LARGE_INTEGER *)&li); + perfcnt_per_sec = 1.0 / li.QuadPart; + } + if (perfcnt_per_sec != 0.0) { + QueryPerformanceCounter((LARGE_INTEGER *)&li); + d = li.QuadPart * perfcnt_per_sec; + tp->tv_sec = (time_t)d; + d -= tp->tv_sec; + tp->tv_nsec = (long)(d * 1.0E9); + ok = TRUE; + } + } + } + + return ok ? 0 : -1; +} +#endif + + +static int +pthread_cond_init(pthread_cond_t *cv, const void *unused) +{ + (void)unused; + InitializeCriticalSection(&cv->threadIdSec); + cv->waitingthreadcount = 0; + cv->waitingthreadhdls = + (pthread_t *)mg_calloc(MAX_WORKER_THREADS, sizeof(pthread_t)); + return (cv->waitingthreadhdls != NULL) ? 0 : -1; +} + + +static int +pthread_cond_timedwait(pthread_cond_t *cv, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + struct mg_workerTLS *tls = + (struct mg_workerTLS *)pthread_getspecific(sTlsKey); + int ok; + struct timespec tsnow; + int64_t nsnow, nswaitabs, nswaitrel; + DWORD mswaitrel; + + EnterCriticalSection(&cv->threadIdSec); + assert(cv->waitingthreadcount < MAX_WORKER_THREADS); + cv->waitingthreadhdls[cv->waitingthreadcount] = + tls->pthread_cond_helper_mutex; + cv->waitingthreadcount++; + LeaveCriticalSection(&cv->threadIdSec); + + if (abstime) { + clock_gettime(CLOCK_REALTIME, &tsnow); + nsnow = (((int64_t)tsnow.tv_sec) * 1000000000) + tsnow.tv_nsec; + nswaitabs = + (((int64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec; + nswaitrel = nswaitabs - nsnow; + if (nswaitrel < 0) { + nswaitrel = 0; + } + mswaitrel = (DWORD)(nswaitrel / 1000000); + } else { + mswaitrel = INFINITE; + } + + pthread_mutex_unlock(mutex); + ok = (WAIT_OBJECT_0 + == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel)); + pthread_mutex_lock(mutex); + + return ok ? 0 : -1; +} + + +static int +pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) +{ + return pthread_cond_timedwait(cv, mutex, NULL); +} + + +static int +pthread_cond_signal(pthread_cond_t *cv) +{ + int i; + HANDLE wkup = NULL; + BOOL ok = FALSE; + + EnterCriticalSection(&cv->threadIdSec); + if (cv->waitingthreadcount) { + wkup = cv->waitingthreadhdls[0]; + ok = SetEvent(wkup); + + for (i = 1; i < cv->waitingthreadcount; i++) { + cv->waitingthreadhdls[i - 1] = cv->waitingthreadhdls[i]; + } + cv->waitingthreadcount--; + + assert(ok); + } + LeaveCriticalSection(&cv->threadIdSec); + + return ok ? 0 : 1; +} + + +static int +pthread_cond_broadcast(pthread_cond_t *cv) +{ + EnterCriticalSection(&cv->threadIdSec); + while (cv->waitingthreadcount) { + pthread_cond_signal(cv); + } + LeaveCriticalSection(&cv->threadIdSec); + + return 0; +} + + +static int +pthread_cond_destroy(pthread_cond_t *cv) +{ + EnterCriticalSection(&cv->threadIdSec); + assert(cv->waitingthreadcount == 0); + mg_free(cv->waitingthreadhdls); + cv->waitingthreadhdls = 0; + LeaveCriticalSection(&cv->threadIdSec); + DeleteCriticalSection(&cv->threadIdSec); + + return 0; +} + + +#if defined(__MINGW32__) +/* Enable unused function warning again */ +#pragma GCC diagnostic pop +#endif + + +/* For Windows, change all slashes to backslashes in path names. */ +static void +change_slashes_to_backslashes(char *path) +{ + int i; + + for (i = 0; path[i] != '\0'; i++) { + if (path[i] == '/') { + path[i] = '\\'; + } + + /* remove double backslash (check i > 0 to preserve UNC paths, + * like \\server\file.txt) */ + if ((path[i] == '\\') && (i > 0)) { + while (path[i + 1] == '\\' || path[i + 1] == '/') { + (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1)); + } + } + } +} + + +static int +mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2) +{ + int diff; + + do { + diff = tolower(*s1) - tolower(*s2); + s1++; + s2++; + } while (diff == 0 && s1[-1] != '\0'); + + return diff; +} + + +/* Encode 'path' which is assumed UTF-8 string, into UNICODE string. + * wbuf and wbuf_len is a target buffer and its length. */ +static void +path_to_unicode(const struct mg_connection *conn, + const char *path, + wchar_t *wbuf, + size_t wbuf_len) +{ + char buf[PATH_MAX], buf2[PATH_MAX]; + wchar_t wbuf2[MAX_PATH + 1]; + DWORD long_len, err; + int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp; + + mg_strlcpy(buf, path, sizeof(buf)); + change_slashes_to_backslashes(buf); + + /* Convert to Unicode and back. If doubly-converted string does not + * match the original, something is fishy, reject. */ + memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len); + WideCharToMultiByte( + CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL); + if (strcmp(buf, buf2) != 0) { + wbuf[0] = L'\0'; + } + + /* TODO: Add a configuration to switch between case sensitive and + * case insensitive URIs for Windows server. */ + /* + if (conn) { + if (conn->ctx->config[WINDOWS_CASE_SENSITIVE]) { + fcompare = wcscmp; + } + } + */ + (void)conn; /* conn is currently unused */ + + /* Only accept a full file path, not a Windows short (8.3) path. */ + memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t)); + long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1); + if (long_len == 0) { + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) { + /* File does not exist. This is not always a problem here. */ + return; + } + } + if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) { + /* Short name is used. */ + wbuf[0] = L'\0'; + } +} + + +#if defined(_WIN32_WCE) +/* Create substitutes for POSIX functions in Win32. */ + +#if defined(__MINGW32__) +/* Show no warning in case system functions are not used. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + + +static time_t +time(time_t *ptime) +{ + time_t t; + SYSTEMTIME st; + FILETIME ft; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime); + + if (ptime != NULL) { + *ptime = t; + } + + return t; +} + + +static struct tm * +localtime(const time_t *ptime, struct tm *ptm) +{ + int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF; + FILETIME ft, lft; + SYSTEMTIME st; + TIME_ZONE_INFORMATION tzinfo; + + if (ptm == NULL) { + return NULL; + } + + *(int64_t *)&ft = t; + FileTimeToLocalFileTime(&ft, &lft); + FileTimeToSystemTime(&lft, &st); + ptm->tm_year = st.wYear - 1900; + ptm->tm_mon = st.wMonth - 1; + ptm->tm_wday = st.wDayOfWeek; + ptm->tm_mday = st.wDay; + ptm->tm_hour = st.wHour; + ptm->tm_min = st.wMinute; + ptm->tm_sec = st.wSecond; + ptm->tm_yday = 0; /* hope nobody uses this */ + ptm->tm_isdst = + GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0; + + return ptm; +} + + +static struct tm * +gmtime(const time_t *ptime, struct tm *ptm) +{ + /* FIXME(lsm): fix this. */ + return localtime(ptime, ptm); +} + + +static size_t +strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm) +{ + (void)mg_snprintf(NULL, dst, dst_size, "implement strftime() for WinCE"); + return 0; +} + + +#if defined(__MINGW32__) +/* Enable unused function warning again */ +#pragma GCC diagnostic pop +#endif + +#endif + + +/* Windows happily opens files with some garbage at the end of file name. + * For example, fopen("a.cgi ", "r") on Windows successfully opens + * "a.cgi", despite one would expect an error back. + * This function returns non-0 if path ends with some garbage. */ +static int +path_cannot_disclose_cgi(const char *path) +{ + static const char *allowed_last_characters = "_-"; + int last = path[strlen(path) - 1]; + return isalnum(last) || strchr(allowed_last_characters, last) != NULL; +} + + +static int +mg_stat(struct mg_connection *conn, const char *path, struct file *filep) +{ + wchar_t wbuf[PATH_MAX]; + WIN32_FILE_ATTRIBUTE_DATA info; + time_t creation_time; + + if (!filep) { + return 0; + } + memset(filep, 0, sizeof(*filep)); + + if (conn && is_file_in_memory(conn, path, filep)) { + /* filep->is_directory = 0; filep->gzipped = 0; .. already done by + * memset */ + filep->last_modified = time(NULL); + /* last_modified = now ... assumes the file may change during runtime, + * so every mg_fopen call may return different data */ + /* last_modified = conn->ctx.start_time; + * May be used it the data does not change during runtime. This allows + * browser caching. Since we do not know, we have to assume the file + * in memory may change. */ + return 1; + } + + path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); + if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { + filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); + filep->last_modified = + SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime, + info.ftLastWriteTime.dwHighDateTime); + + /* On Windows, the file creation time can be higher than the + * modification time, e.g. when a file is copied. + * Since the Last-Modified timestamp is used for caching + * it should be based on the most recent timestamp. */ + creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime, + info.ftCreationTime.dwHighDateTime); + if (creation_time > filep->last_modified) { + filep->last_modified = creation_time; + } + + filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + /* If file name is fishy, reset the file structure and return + * error. + * Note it is important to reset, not just return the error, cause + * functions like is_file_opened() check the struct. */ + if (!filep->is_directory && !path_cannot_disclose_cgi(path)) { + memset(filep, 0, sizeof(*filep)); + return 0; + } + + return 1; + } + + return 0; +} + + +static int +mg_remove(const struct mg_connection *conn, const char *path) +{ + wchar_t wbuf[PATH_MAX]; + path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); + return DeleteFileW(wbuf) ? 0 : -1; +} + + +static int +mg_mkdir(const struct mg_connection *conn, const char *path, int mode) +{ + wchar_t wbuf[PATH_MAX]; + (void)mode; + path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); + return CreateDirectoryW(wbuf, NULL) ? 0 : -1; +} + + +/* Create substitutes for POSIX functions in Win32. */ + +#if defined(__MINGW32__) +/* Show no warning in case system functions are not used. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + + +/* Implementation of POSIX opendir/closedir/readdir for Windows. */ +static DIR * +mg_opendir(const struct mg_connection *conn, const char *name) +{ + DIR *dir = NULL; + wchar_t wpath[PATH_MAX]; + DWORD attrs; + + if (name == NULL) { + SetLastError(ERROR_BAD_ARGUMENTS); + } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + } else { + path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath)); + attrs = GetFileAttributesW(wpath); + if (attrs != 0xFFFFFFFF && ((attrs & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY)) { + (void)wcscat(wpath, L"\\*"); + dir->handle = FindFirstFileW(wpath, &dir->info); + dir->result.d_name[0] = '\0'; + } else { + mg_free(dir); + dir = NULL; + } + } + + return dir; +} + + +static int +mg_closedir(DIR *dir) +{ + int result = 0; + + if (dir != NULL) { + if (dir->handle != INVALID_HANDLE_VALUE) + result = FindClose(dir->handle) ? 0 : -1; + + mg_free(dir); + } else { + result = -1; + SetLastError(ERROR_BAD_ARGUMENTS); + } + + return result; +} + + +static struct dirent * +mg_readdir(DIR *dir) +{ + struct dirent *result = 0; + + if (dir) { + if (dir->handle != INVALID_HANDLE_VALUE) { + result = &dir->result; + (void)WideCharToMultiByte(CP_UTF8, + 0, + dir->info.cFileName, + -1, + result->d_name, + sizeof(result->d_name), + NULL, + NULL); + + if (!FindNextFileW(dir->handle, &dir->info)) { + (void)FindClose(dir->handle); + dir->handle = INVALID_HANDLE_VALUE; + } + + } else { + SetLastError(ERROR_FILE_NOT_FOUND); + } + } else { + SetLastError(ERROR_BAD_ARGUMENTS); + } + + return result; +} + + +#ifndef HAVE_POLL +static int +poll(struct pollfd *pfd, unsigned int n, int milliseconds) +{ + struct timeval tv; + fd_set set; + unsigned int i; + int result; + SOCKET maxfd = 0; + + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = (milliseconds % 1000) * 1000; + FD_ZERO(&set); + + for (i = 0; i < n; i++) { + FD_SET((SOCKET)pfd[i].fd, &set); + pfd[i].revents = 0; + + if (pfd[i].fd > maxfd) { + maxfd = pfd[i].fd; + } + } + + if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) { + for (i = 0; i < n; i++) { + if (FD_ISSET(pfd[i].fd, &set)) { + pfd[i].revents = POLLIN; + } + } + } + + return result; +} +#endif /* HAVE_POLL */ + +#if defined(__MINGW32__) +/* Enable unused function warning again */ +#pragma GCC diagnostic pop +#endif + + +static void +set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be null */) +{ + (void)conn; /* Unused. */ + (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0); +} + + +int +mg_start_thread(mg_thread_func_t f, void *p) +{ +#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) + /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 + */ + return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p) + == ((uintptr_t)(-1L))) + ? -1 + : 0); +#else + return ( + (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L))) + ? -1 + : 0); +#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ +} + + +/* Start a thread storing the thread context. */ +static int +mg_start_thread_with_id(unsigned(__stdcall *f)(void *), + void *p, + pthread_t *threadidptr) +{ + uintptr_t uip; + HANDLE threadhandle; + int result = -1; + + uip = _beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))f, p, 0, NULL); + threadhandle = (HANDLE)uip; + if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) { + *threadidptr = threadhandle; + result = 0; + } + + return result; +} + + +/* Wait for a thread to finish. */ +static int +mg_join_thread(pthread_t threadid) +{ + int result; + DWORD dwevent; + + result = -1; + dwevent = WaitForSingleObject(threadid, INFINITE); + if (dwevent == WAIT_FAILED) { + DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO); + } else { + if (dwevent == WAIT_OBJECT_0) { + CloseHandle(threadid); + result = 0; + } + } + + return result; +} + +#if !defined(NO_SSL_DL) +/* Create substitutes for POSIX functions in Win32. */ + +#if defined(__MINGW32__) +/* Show no warning in case system functions are not used. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + + +static HANDLE +dlopen(const char *dll_name, int flags) +{ + wchar_t wbuf[PATH_MAX]; + (void)flags; + path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf)); + return LoadLibraryW(wbuf); +} + + +static int +dlclose(void *handle) +{ + int result; + + if (FreeLibrary((HMODULE)handle) != 0) { + result = 0; + } else { + result = -1; + } + + return result; +} + + +#if defined(__MINGW32__) +/* Enable unused function warning again */ +#pragma GCC diagnostic pop +#endif + +#endif + + +#if !defined(NO_CGI) +#define SIGKILL (0) + +static int +kill(pid_t pid, int sig_num) +{ + (void)TerminateProcess((HANDLE)pid, (UINT)sig_num); + (void)CloseHandle((HANDLE)pid); + return 0; +} + + +static void +trim_trailing_whitespaces(char *s) +{ + char *e = s + strlen(s) - 1; + while (e > s && isspace(*(unsigned char *)e)) { + *e-- = '\0'; + } +} + + +static pid_t +spawn_process(struct mg_connection *conn, + const char *prog, + char *envblk, + char *envp[], + int fdin[2], + int fdout[2], + int fderr[2], + const char *dir) +{ + HANDLE me; + char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX], + cmdline[PATH_MAX], buf[PATH_MAX]; + int truncated; + struct file file = STRUCT_FILE_INITIALIZER; + STARTUPINFOA si; + PROCESS_INFORMATION pi = {0}; + + (void)envp; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + me = GetCurrentProcess(); + DuplicateHandle(me, + (HANDLE)_get_osfhandle(fdin[0]), + me, + &si.hStdInput, + 0, + TRUE, + DUPLICATE_SAME_ACCESS); + DuplicateHandle(me, + (HANDLE)_get_osfhandle(fdout[1]), + me, + &si.hStdOutput, + 0, + TRUE, + DUPLICATE_SAME_ACCESS); + DuplicateHandle(me, + (HANDLE)_get_osfhandle(fderr[1]), + me, + &si.hStdError, + 0, + TRUE, + DUPLICATE_SAME_ACCESS); + + /* Mark handles that should not be inherited. See + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx + */ + SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]), + HANDLE_FLAG_INHERIT, + 0); + SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]), + HANDLE_FLAG_INHERIT, + 0); + SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]), + HANDLE_FLAG_INHERIT, + 0); + + /* If CGI file is a script, try to read the interpreter line */ + interp = conn->ctx->config[CGI_INTERPRETER]; + if (interp == NULL) { + buf[0] = buf[1] = '\0'; + + /* Read the first line of the script into the buffer */ + mg_snprintf( + conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog); + + if (truncated) { + pi.hProcess = (pid_t)-1; + goto spawn_cleanup; + } + + if (mg_fopen(conn, cmdline, "r", &file)) { + p = (char *)file.membuf; + mg_fgets(buf, sizeof(buf), &file, &p); + mg_fclose(&file); + buf[sizeof(buf) - 1] = '\0'; + } + + if (buf[0] == '#' && buf[1] == '!') { + trim_trailing_whitespaces(buf + 2); + } else { + buf[2] = '\0'; + } + interp = buf + 2; + } + + if (interp[0] != '\0') { + GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL); + interp = full_interp; + } + GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL); + + if (interp[0] != '\0') { + mg_snprintf(conn, + &truncated, + cmdline, + sizeof(cmdline), + "\"%s\" \"%s\\%s\"", + interp, + full_dir, + prog); + } else { + mg_snprintf(conn, + &truncated, + cmdline, + sizeof(cmdline), + "\"%s\\%s\"", + full_dir, + prog); + } + + if (truncated) { + pi.hProcess = (pid_t)-1; + goto spawn_cleanup; + } + + DEBUG_TRACE("Running [%s]", cmdline); + if (CreateProcessA(NULL, + cmdline, + NULL, + NULL, + TRUE, + CREATE_NEW_PROCESS_GROUP, + envblk, + NULL, + &si, + &pi) == 0) { + mg_cry( + conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO); + pi.hProcess = (pid_t)-1; + /* goto spawn_cleanup; */ + } + +spawn_cleanup: + (void)CloseHandle(si.hStdOutput); + (void)CloseHandle(si.hStdError); + (void)CloseHandle(si.hStdInput); + if (pi.hThread != NULL) { + (void)CloseHandle(pi.hThread); + } + + return (pid_t)pi.hProcess; +} +#endif /* !NO_CGI */ + + +static int +set_non_blocking_mode(SOCKET sock) +{ + unsigned long on = 1; + return ioctlsocket(sock, (long)FIONBIO, &on); +} + +#else + +static int +mg_stat(struct mg_connection *conn, const char *path, struct file *filep) +{ + struct stat st; + if (!filep) { + return 0; + } + memset(filep, 0, sizeof(*filep)); + + if (conn && is_file_in_memory(conn, path, filep)) { + return 1; + } + + if (0 == stat(path, &st)) { + filep->size = (uint64_t)(st.st_size); + filep->last_modified = st.st_mtime; + filep->is_directory = S_ISDIR(st.st_mode); + return 1; + } + + return 0; +} + + +static void +set_close_on_exec(SOCKET fd, struct mg_connection *conn /* may be null */) +{ + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { + if (conn) { + mg_cry(conn, + "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", + __func__, + strerror(ERRNO)); + } + } +} + + +int +mg_start_thread(mg_thread_func_t func, void *param) +{ + pthread_t thread_id; + pthread_attr_t attr; + int result; + + (void)pthread_attr_init(&attr); + (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) + /* Compile-time option to control stack size, + * e.g. -DUSE_STACK_SIZE=16384 */ + (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); +#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ + + result = pthread_create(&thread_id, &attr, func, param); + pthread_attr_destroy(&attr); + + return result; +} + + +/* Start a thread storing the thread context. */ +static int +mg_start_thread_with_id(mg_thread_func_t func, + void *param, + pthread_t *threadidptr) +{ + pthread_t thread_id; + pthread_attr_t attr; + int result; + + (void)pthread_attr_init(&attr); + +#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) + /* Compile-time option to control stack size, + * e.g. -DUSE_STACK_SIZE=16384 */ + (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); +#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */ + + result = pthread_create(&thread_id, &attr, func, param); + pthread_attr_destroy(&attr); + if ((result == 0) && (threadidptr != NULL)) { + *threadidptr = thread_id; + } + return result; +} + + +/* Wait for a thread to finish. */ +static int +mg_join_thread(pthread_t threadid) +{ + int result; + + result = pthread_join(threadid, NULL); + return result; +} + + +#ifndef NO_CGI +static pid_t +spawn_process(struct mg_connection *conn, + const char *prog, + char *envblk, + char *envp[], + int fdin[2], + int fdout[2], + int fderr[2], + const char *dir) +{ + pid_t pid; + const char *interp; + + (void)envblk; + + if (conn == NULL) { + return 0; + } + + if ((pid = fork()) == -1) { + /* Parent */ + send_http_error(conn, + 500, + "Error: Creating CGI process\nfork(): %s", + strerror(ERRNO)); + } else if (pid == 0) { + /* Child */ + if (chdir(dir) != 0) { + mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); + } else if (dup2(fdin[0], 0) == -1) { + mg_cry(conn, + "%s: dup2(%d, 0): %s", + __func__, + fdin[0], + strerror(ERRNO)); + } else if (dup2(fdout[1], 1) == -1) { + mg_cry(conn, + "%s: dup2(%d, 1): %s", + __func__, + fdout[1], + strerror(ERRNO)); + } else if (dup2(fderr[1], 2) == -1) { + mg_cry(conn, + "%s: dup2(%d, 2): %s", + __func__, + fderr[1], + strerror(ERRNO)); + } else { + /* Keep stderr and stdout in two different pipes. + * Stdout will be sent back to the client, + * stderr should go into a server error log. */ + (void)close(fdin[0]); + (void)close(fdout[1]); + (void)close(fderr[1]); + + /* Close write end fdin and read end fdout and fderr */ + (void)close(fdin[1]); + (void)close(fdout[0]); + (void)close(fderr[0]); + + /* After exec, all signal handlers are restored to their default + * values, with one exception of SIGCHLD. According to + * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler will + * leave unchanged after exec if it was set to be ignored. Restore + * it to default action. */ + signal(SIGCHLD, SIG_DFL); + + interp = conn->ctx->config[CGI_INTERPRETER]; + if (interp == NULL) { + (void)execle(prog, prog, NULL, envp); + mg_cry(conn, + "%s: execle(%s): %s", + __func__, + prog, + strerror(ERRNO)); + } else { + (void)execle(interp, interp, prog, NULL, envp); + mg_cry(conn, + "%s: execle(%s %s): %s", + __func__, + interp, + prog, + strerror(ERRNO)); + } + } + exit(EXIT_FAILURE); + } + + return pid; +} +#endif /* !NO_CGI */ + + +static int +set_non_blocking_mode(SOCKET sock) +{ + int flags; + + flags = fcntl(sock, F_GETFL, 0); + (void)fcntl(sock, F_SETFL, flags | O_NONBLOCK); + + return 0; +} +#endif /* _WIN32 */ +/* End of initial operating system specific define block. */ + + +/* Get a random number (independent of C rand function) */ +static uint64_t +get_random(void) +{ + static uint64_t lfsr = 0; /* Linear feedback shift register */ + static uint64_t lcg = 0; /* Linear congruential generator */ + struct timespec now; + + memset(&now, 0, sizeof(now)); + clock_gettime(CLOCK_MONOTONIC, &now); + + if (lfsr == 0) { + /* lfsr will be only 0 if has not been initialized, + * so this code is called only once. */ + lfsr = (((uint64_t)now.tv_sec) << 21) ^ ((uint64_t)now.tv_nsec) + ^ ((uint64_t)(ptrdiff_t)&now) ^ (((uint64_t)time(NULL)) << 33); + lcg = (((uint64_t)now.tv_sec) << 25) + (uint64_t)now.tv_nsec + + (uint64_t)(ptrdiff_t)&now; + } else { + /* Get the next step of both random number generators. */ + lfsr = (lfsr >> 1) + | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1) + << 63); + lcg = lcg * 6364136223846793005 + 1442695040888963407; + } + + /* Combining two pseudo-random number generators and a high resolution part + * of the current server time will make it hard (impossible?) to guess the + * next number. */ + return (lfsr ^ lcg ^ (uint64_t)now.tv_nsec); +} + + +/* Write data to the IO channel - opened file descriptor, socket or SSL + * descriptor. Return number of bytes written. */ +static int +push(struct mg_context *ctx, + FILE *fp, + SOCKET sock, + SSL *ssl, + const char *buf, + int len, + double timeout) +{ + struct timespec start, now; + int n, err; + +#ifdef _WIN32 + typedef int len_t; +#else + typedef size_t len_t; +#endif + + if (timeout > 0) { + memset(&start, 0, sizeof(start)); + memset(&now, 0, sizeof(now)); + clock_gettime(CLOCK_MONOTONIC, &start); + } + + if (ctx == NULL) { + return -1; + } + +#ifdef NO_SSL + if (ssl) { + return -1; + } +#endif + + do { + +#ifndef NO_SSL + if (ssl != NULL) { + n = SSL_write(ssl, buf, len); + if (n <= 0) { + err = SSL_get_error(ssl, n); + if ((err == 5 /* SSL_ERROR_SYSCALL */) && (n == -1)) { + err = ERRNO; + } else { + DEBUG_TRACE("SSL_write() failed, error %d", err); + return -1; + } + } else { + err = 0; + } + } else +#endif + if (fp != NULL) { + n = (int)fwrite(buf, 1, (size_t)len, fp); + if (ferror(fp)) { + n = -1; + err = ERRNO; + } else { + err = 0; + } + } else { + n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL); + err = (n < 0) ? ERRNO : 0; + } + + if (ctx->stop_flag) { + return -1; + } + + if ((n > 0) || (n == 0 && len == 0)) { + /* some data has been read, or no data was requested */ + return n; + } + if (n == 0) { + /* shutdown of the socket at client side */ + return -1; + } + if (n < 0) { + /* socket error - check errno */ + DEBUG_TRACE("send() failed, error %d", err); + + /* TODO: error handling depending on the error code. + * These codes are different between Windows and Linux. + */ + return -1; + } + + /* This code is not reached in the moment. + * ==> Fix the TODOs above first. */ + + if (timeout > 0) { + clock_gettime(CLOCK_MONOTONIC, &now); + } + + } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout)); + + (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not + used */ + + return -1; +} + + +static int64_t +push_all(struct mg_context *ctx, + FILE *fp, + SOCKET sock, + SSL *ssl, + const char *buf, + int64_t len) +{ + double timeout = -1.0; + int64_t n, nwritten = 0; + + if (ctx == NULL) { + return -1; + } + + if (ctx->config[REQUEST_TIMEOUT]) { + timeout = atoi(ctx->config[REQUEST_TIMEOUT]) / 1000.0; + } + + while (len > 0 && ctx->stop_flag == 0) { + n = push(ctx, fp, sock, ssl, buf + nwritten, (int)len, timeout); + if (n < 0) { + if (nwritten == 0) { + nwritten = n; /* Propagate the error */ + } + break; + } else if (n == 0) { + break; /* No more data to write */ + } else { + nwritten += n; + len -= n; + } + } + + return nwritten; +} + + +/* Read from IO channel - opened file descriptor, socket, or SSL descriptor. + * Return negative value on error, or number of bytes read on success. */ +static int +pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout) +{ + int nread, err; + struct timespec start, now; + +#ifdef _WIN32 + typedef int len_t; +#else + typedef size_t len_t; +#endif + + if (timeout > 0) { + memset(&start, 0, sizeof(start)); + memset(&now, 0, sizeof(now)); + clock_gettime(CLOCK_MONOTONIC, &start); + } + + do { + if (fp != NULL) { + /* Use read() instead of fread(), because if we're reading from the + * CGI pipe, fread() may block until IO buffer is filled up. We + * cannot afford to block and must pass all read bytes immediately + * to the client. */ + nread = (int)read(fileno(fp), buf, (size_t)len); + err = (nread < 0) ? ERRNO : 0; + +#ifndef NO_SSL + } else if (conn->ssl != NULL) { + nread = SSL_read(conn->ssl, buf, len); + if (nread <= 0) { + err = SSL_get_error(conn->ssl, nread); + if ((err == 5 /* SSL_ERROR_SYSCALL */) && (nread == -1)) { + err = ERRNO; + } else { + DEBUG_TRACE("SSL_read() failed, error %d", err); + return -1; + } + } else { + err = 0; + } +#endif + + } else { + nread = (int)recv(conn->client.sock, buf, (len_t)len, 0); + err = (nread < 0) ? ERRNO : 0; + } + + if (conn->ctx->stop_flag) { + return -1; + } + + if ((nread > 0) || (nread == 0 && len == 0)) { + /* some data has been read, or no data was requested */ + return nread; + } + if (nread == 0) { + /* shutdown of the socket at client side */ + return -1; + } + if (nread < 0) { +/* socket error - check errno */ +#ifdef _WIN32 + if (err == WSAEWOULDBLOCK) { + /* standard case if called from close_socket_gracefully */ + return -1; + } else if (err == WSAETIMEDOUT) { + /* timeout is handled by the while loop */ + } else { + DEBUG_TRACE("recv() failed, error %d", err); + return -1; + } +#else + /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases, + * if the timeout is reached and if the socket was set to non- + * blocking in close_socket_gracefully, so we can not distinguish + * here. We have to wait for the timeout in both cases for now. + */ + if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) { + /* EAGAIN/EWOULDBLOCK: + * standard case if called from close_socket_gracefully + * => should return -1 */ + /* or timeout occured + * => the code must stay in the while loop */ + + /* EINTR can be generated on a socket with a timeout set even + * when SA_RESTART is effective for all relevant signals + * (see signal(7)). + * => stay in the while loop */ + } else { + DEBUG_TRACE("recv() failed, error %d", err); + return -1; + } +#endif + } + if (timeout > 0) { + clock_gettime(CLOCK_MONOTONIC, &now); + } + } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout)); + + /* Timeout occured, but no data available. */ + return -1; +} + + +static int +pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) +{ + int n, nread = 0; + double timeout = -1.0; + + if (conn->ctx->config[REQUEST_TIMEOUT]) { + timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; + } + + while (len > 0 && conn->ctx->stop_flag == 0) { + n = pull(fp, conn, buf + nread, len, timeout); + if (n < 0) { + if (nread == 0) { + nread = n; /* Propagate the error */ + } + break; + } else if (n == 0) { + break; /* No more data to read */ + } else { + conn->consumed_content += n; + nread += n; + len -= n; + } + } + + return nread; +} + + +static void +discard_unread_request_data(struct mg_connection *conn) +{ + char buf[MG_BUF_LEN]; + size_t to_read; + int nread; + + if (conn == NULL) { + return; + } + + to_read = sizeof(buf); + + if (conn->is_chunked) { + /* Chunked encoding: 1=chunk not read completely, 2=chunk read + * completely */ + while (conn->is_chunked == 1) { + nread = mg_read(conn, buf, to_read); + if (nread <= 0) { + break; + } + } + + } else { + /* Not chunked: content length is known */ + while (conn->consumed_content < conn->content_len) { + if (to_read + > (size_t)(conn->content_len - conn->consumed_content)) { + to_read = (size_t)(conn->content_len - conn->consumed_content); + } + + nread = mg_read(conn, buf, to_read); + if (nread <= 0) { + break; + } + } + } +} + + +static int +mg_read_inner(struct mg_connection *conn, void *buf, size_t len) +{ + int64_t n, buffered_len, nread; + int64_t len64 = + (int64_t)(len > INT_MAX ? INT_MAX : len); /* since the return value is + * int, we may not read more + * bytes */ + const char *body; + + if (conn == NULL) { + return 0; + } + + /* If Content-Length is not set for a PUT or POST request, read until + * socket is closed */ + if (conn->consumed_content == 0 && conn->content_len == -1) { + conn->content_len = INT64_MAX; + conn->must_close = 1; + } + + nread = 0; + if (conn->consumed_content < conn->content_len) { + /* Adjust number of bytes to read. */ + int64_t left_to_read = conn->content_len - conn->consumed_content; + if (left_to_read < len64) { + /* Do not read more than the total content length of the request. + */ + len64 = left_to_read; + } + + /* Return buffered data */ + buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len + - conn->consumed_content; + if (buffered_len > 0) { + if (len64 < buffered_len) { + buffered_len = len64; + } + body = conn->buf + conn->request_len + conn->consumed_content; + memcpy(buf, body, (size_t)buffered_len); + len64 -= buffered_len; + conn->consumed_content += buffered_len; + nread += buffered_len; + buf = (char *)buf + buffered_len; + } + + /* We have returned all buffered data. Read new data from the remote + * socket. + */ + if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) { + nread += n; + } else { + nread = (nread > 0 ? nread : n); + } + } + return (int)nread; +} + + +static char +mg_getc(struct mg_connection *conn) +{ + char c; + if (conn == NULL) { + return 0; + } + conn->content_len++; + if (mg_read_inner(conn, &c, 1) <= 0) { + return (char)0; + } + return c; +} + + +int +mg_read(struct mg_connection *conn, void *buf, size_t len) +{ + if (len > INT_MAX) { + len = INT_MAX; + } + + if (conn == NULL) { + return 0; + } + + if (conn->is_chunked) { + size_t all_read = 0; + + while (len > 0) { + + if (conn->is_chunked == 2) { + /* No more data left to read */ + return 0; + } + + if (conn->chunk_remainder) { + /* copy from the remainder of the last received chunk */ + long read_ret; + size_t read_now = + ((conn->chunk_remainder > len) ? (len) + : (conn->chunk_remainder)); + + conn->content_len += (int)read_now; + read_ret = + mg_read_inner(conn, (char *)buf + all_read, read_now); + all_read += (size_t)read_ret; + + conn->chunk_remainder -= read_now; + len -= read_now; + + if (conn->chunk_remainder == 0) { + /* the rest of the data in the current chunk has been read + */ + if ((mg_getc(conn) != '\r') || (mg_getc(conn) != '\n')) { + /* Protocol violation */ + return -1; + } + } + + } else { + /* fetch a new chunk */ + int i = 0; + char lenbuf[64]; + char *end = 0; + unsigned long chunkSize = 0; + + for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) { + lenbuf[i] = mg_getc(conn); + if (i > 0 && lenbuf[i] == '\r' && lenbuf[i - 1] != '\r') { + continue; + } + if (i > 1 && lenbuf[i] == '\n' && lenbuf[i - 1] == '\r') { + lenbuf[i + 1] = 0; + chunkSize = strtoul(lenbuf, &end, 16); + if (chunkSize == 0) { + /* regular end of content */ + conn->is_chunked = 2; + } + break; + } + if (!isalnum(lenbuf[i])) { + /* illegal character for chunk length */ + return -1; + } + } + if ((end == NULL) || (*end != '\r')) { + /* chunksize not set correctly */ + return -1; + } + if (chunkSize == 0) { + break; + } + + conn->chunk_remainder = chunkSize; + } + } + + return (int)all_read; + } + return mg_read_inner(conn, buf, len); +} + + +int +mg_write(struct mg_connection *conn, const void *buf, size_t len) +{ + time_t now; + int64_t n, total, allowed; + + if (conn == NULL) { + return 0; + } + + if (conn->throttle > 0) { + if ((now = time(NULL)) != conn->last_throttle_time) { + conn->last_throttle_time = now; + conn->last_throttle_bytes = 0; + } + allowed = conn->throttle - conn->last_throttle_bytes; + if (allowed > (int64_t)len) { + allowed = (int64_t)len; + } + if ((total = push_all(conn->ctx, + NULL, + conn->client.sock, + conn->ssl, + (const char *)buf, + (int64_t)allowed)) == allowed) { + buf = (const char *)buf + total; + conn->last_throttle_bytes += total; + while (total < (int64_t)len && conn->ctx->stop_flag == 0) { + allowed = conn->throttle > (int64_t)len - total + ? (int64_t)len - total + : conn->throttle; + if ((n = push_all(conn->ctx, + NULL, + conn->client.sock, + conn->ssl, + (const char *)buf, + (int64_t)allowed)) != allowed) { + break; + } + sleep(1); + conn->last_throttle_bytes = allowed; + conn->last_throttle_time = time(NULL); + buf = (const char *)buf + n; + total += n; + } + } + } else { + total = push_all(conn->ctx, + NULL, + conn->client.sock, + conn->ssl, + (const char *)buf, + (int64_t)len); + } + return (int)total; +} + + +/* Alternative alloc_vprintf() for non-compliant C runtimes */ +static int +alloc_vprintf2(char **buf, const char *fmt, va_list ap) +{ + va_list ap_copy; + size_t size = MG_BUF_LEN / 4; + int len = -1; + + *buf = NULL; + while (len < 0) { + if (*buf) { + mg_free(*buf); + } + + size *= 4; + *buf = (char *)mg_malloc(size); + if (!*buf) { + break; + } + + va_copy(ap_copy, ap); + len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy); + va_end(ap_copy); + (*buf)[size - 1] = 0; + } + + return len; +} + + +/* Print message to buffer. If buffer is large enough to hold the message, + * return buffer. If buffer is to small, allocate large enough buffer on heap, + * and return allocated buffer. */ +static int +alloc_vprintf(char **out_buf, + char *prealloc_buf, + size_t prealloc_size, + const char *fmt, + va_list ap) +{ + va_list ap_copy; + int len; + + /* Windows is not standard-compliant, and vsnprintf() returns -1 if + * buffer is too small. Also, older versions of msvcrt.dll do not have + * _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. + * Therefore, we make two passes: on first pass, get required message + * length. + * On second pass, actually print the message. */ + va_copy(ap_copy, ap); + len = vsnprintf_impl(NULL, 0, fmt, ap_copy); + va_end(ap_copy); + + if (len < 0) { + /* C runtime is not standard compliant, vsnprintf() returned -1. + * Switch to alternative code path that uses incremental allocations. + */ + va_copy(ap_copy, ap); + len = alloc_vprintf2(out_buf, fmt, ap); + va_end(ap_copy); + + } else if ((size_t)(len) >= prealloc_size) { + /* The pre-allocated buffer not large enough. */ + /* Allocate a new buffer. */ + *out_buf = (char *)mg_malloc((size_t)(len) + 1); + if (!*out_buf) { + /* Allocation failed. Return -1 as "out of memory" error. */ + return -1; + } + /* Buffer allocation successful. Store the string there. */ + va_copy(ap_copy, ap); + IGNORE_UNUSED_RESULT( + vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy)); + va_end(ap_copy); + + } else { + /* The pre-allocated buffer is large enough. + * Use it to store the string and return the address. */ + va_copy(ap_copy, ap); + IGNORE_UNUSED_RESULT( + vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy)); + va_end(ap_copy); + *out_buf = prealloc_buf; + } + + return len; +} + + +static int +mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) +{ + char mem[MG_BUF_LEN]; + char *buf = NULL; + int len; + + if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) { + len = mg_write(conn, buf, (size_t)len); + } + if (buf != mem && buf != NULL) { + mg_free(buf); + } + + return len; +} + + +int +mg_printf(struct mg_connection *conn, const char *fmt, ...) +{ + va_list ap; + int result; + + va_start(ap, fmt); + result = mg_vprintf(conn, fmt, ap); + va_end(ap); + + return result; +} + + +int +mg_url_decode(const char *src, + int src_len, + char *dst, + int dst_len, + int is_form_url_encoded) +{ + int i, j, a, b; +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (i < src_len - 2 && src[i] == '%' + && isxdigit(*(const unsigned char *)(src + i + 1)) + && isxdigit(*(const unsigned char *)(src + i + 2))) { + a = tolower(*(const unsigned char *)(src + i + 1)); + b = tolower(*(const unsigned char *)(src + i + 2)); + dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + + +int +mg_get_var(const char *data, + size_t data_len, + const char *name, + char *dst, + size_t dst_len) +{ + return mg_get_var2(data, data_len, name, dst, dst_len, 0); +} + + +int +mg_get_var2(const char *data, + size_t data_len, + const char *name, + char *dst, + size_t dst_len, + size_t occurrence) +{ + const char *p, *e, *s; + size_t name_len; + int len; + + if (dst == NULL || dst_len == 0) { + len = -2; + } else if (data == NULL || name == NULL || data_len == 0) { + len = -1; + dst[0] = '\0'; + } else { + name_len = strlen(name); + e = data + data_len; + len = -1; + dst[0] = '\0'; + + /* data is "var1=val1&var2=val2...". Find variable first */ + for (p = data; p + name_len < e; p++) { + if ((p == data || p[-1] == '&') && p[name_len] == '=' + && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) { + /* Point p to variable value */ + p += name_len + 1; + + /* Point s to the end of the value */ + s = (const char *)memchr(p, '&', (size_t)(e - p)); + if (s == NULL) { + s = e; + } + /* assert(s >= p); */ + if (s < p) { + return -3; + } + + /* Decode variable into destination buffer */ + len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1); + + /* Redirect error code from -1 to -2 (destination buffer too + * small). */ + if (len == -1) { + len = -2; + } + break; + } + } + } + + return len; +} + + +int +mg_get_cookie(const char *cookie_header, + const char *var_name, + char *dst, + size_t dst_size) +{ + const char *s, *p, *end; + int name_len, len = -1; + + if (dst == NULL || dst_size == 0) { + len = -2; + } else if (var_name == NULL || (s = cookie_header) == NULL) { + len = -1; + dst[0] = '\0'; + } else { + name_len = (int)strlen(var_name); + end = s + strlen(s); + dst[0] = '\0'; + + for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { + if (s[name_len] == '=') { + s += name_len + 1; + if ((p = strchr(s, ' ')) == NULL) { + p = end; + } + if (p[-1] == ';') { + p--; + } + if (*s == '"' && p[-1] == '"' && p > s + 1) { + s++; + p--; + } + if ((size_t)(p - s) < dst_size) { + len = (int)(p - s); + mg_strlcpy(dst, s, (size_t)len + 1); + } else { + len = -3; + } + break; + } + } + } + return len; +} + + +#if defined(USE_WEBSOCKET) || defined(USE_LUA) +static void +base64_encode(const unsigned char *src, int src_len, char *dst) +{ + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 63]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} +#endif + + +#if defined(USE_LUA) +static unsigned char +b64reverse(char letter) +{ + if (letter >= 'A' && letter <= 'Z') { + return letter - 'A'; + } + if (letter >= 'a' && letter <= 'z') { + return letter - 'a' + 26; + } + if (letter >= '0' && letter <= '9') { + return letter - '0' + 52; + } + if (letter == '+') { + return 62; + } + if (letter == '/') { + return 63; + } + if (letter == '=') { + return 255; /* normal end */ + } + return 254; /* error */ +} + + +static int +base64_decode(const unsigned char *src, int src_len, char *dst, size_t *dst_len) +{ + int i; + unsigned char a, b, c, d; + + *dst_len = 0; + + for (i = 0; i < src_len; i += 4) { + a = b64reverse(src[i]); + if (a >= 254) { + return i; + } + + b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]); + if (b >= 254) { + return i + 1; + } + + c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]); + if (c == 254) { + return i + 2; + } + + d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]); + if (d == 254) { + return i + 3; + } + + dst[(*dst_len)++] = (a << 2) + (b >> 4); + if (c != 255) { + dst[(*dst_len)++] = (b << 4) + (c >> 2); + if (d != 255) { + dst[(*dst_len)++] = (c << 6) + d; + } + } + } + return -1; +} +#endif + + +static int +is_put_or_delete_method(const struct mg_connection *conn) +{ + if (conn) { + const char *s = conn->request_info.request_method; + return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") + || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH")); + } + return 0; +} + + +static void +interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */ + char *filename, /* out: filename */ + size_t filename_buf_len, /* in: size of filename buffer */ + struct file *filep, /* out: file structure */ + int *is_found, /* out: file is found (directly) */ + int *is_script_resource, /* out: handled by a script? */ + int *is_websocket_request, /* out: websocket connetion? */ + int *is_put_or_delete_request /* out: put/delete a file? */ + ) +{ +/* TODO (high): Restructure this function */ + +#if !defined(NO_FILES) + const char *uri = conn->request_info.local_uri; + const char *root = conn->ctx->config[DOCUMENT_ROOT]; + const char *rewrite; + struct vec a, b; + int match_len; + char gz_path[PATH_MAX]; + char const *accept_encoding; + int truncated; +#if !defined(NO_CGI) || defined(USE_LUA) + char *p; +#endif +#else + (void)filename_buf_len; /* unused if NO_FILES is defined */ +#endif + + memset(filep, 0, sizeof(*filep)); + *filename = 0; + *is_found = 0; + *is_script_resource = 0; + *is_put_or_delete_request = is_put_or_delete_method(conn); + +#if defined(USE_WEBSOCKET) + *is_websocket_request = is_websocket_protocol(conn); +#if !defined(NO_FILES) + if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) { + root = conn->ctx->config[WEBSOCKET_ROOT]; + } +#endif /* !NO_FILES */ +#else /* USE_WEBSOCKET */ + *is_websocket_request = 0; +#endif /* USE_WEBSOCKET */ + +#if !defined(NO_FILES) + /* Note that root == NULL is a regular use case here. This occurs, + * if all requests are handled by callbacks, so the WEBSOCKET_ROOT + * config is not required. */ + if (root == NULL) { + /* all file related outputs have already been set to 0, just return + */ + return; + } + + /* Using buf_len - 1 because memmove() for PATH_INFO may shift part + * of the path one byte on the right. + * If document_root is NULL, leave the file empty. */ + mg_snprintf( + conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri); + + if (truncated) { + goto interpret_cleanup; + } + + rewrite = conn->ctx->config[REWRITE]; + while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { + if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { + mg_snprintf(conn, + &truncated, + filename, + filename_buf_len - 1, + "%.*s%s", + (int)b.len, + b.ptr, + uri + match_len); + break; + } + } + + if (truncated) { + goto interpret_cleanup; + } + + /* Local file path and name, corresponding to requested URI + * is now stored in "filename" variable. */ + if (mg_stat(conn, filename, filep)) { +#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) + /* File exists. Check if it is a script type. */ + if (0 +#if !defined(NO_CGI) + || match_prefix(conn->ctx->config[CGI_EXTENSIONS], + strlen(conn->ctx->config[CGI_EXTENSIONS]), + filename) > 0 +#endif +#if defined(USE_LUA) + || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], + strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), + filename) > 0 +#endif +#if defined(USE_DUKTAPE) + || match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], + strlen( + conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]), + filename) > 0 +#endif + ) { + /* The request addresses a CGI script or a Lua script. The URI + * corresponds to the script itself (like /path/script.cgi), + * and there is no additional resource path + * (like /path/script.cgi/something). + * Requests that modify (replace or delete) a resource, like + * PUT and DELETE requests, should replace/delete the script + * file. + * Requests that read or write from/to a resource, like GET and + * POST requests, should call the script and return the + * generated response. */ + *is_script_resource = !*is_put_or_delete_request; + } +#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */ + *is_found = 1; + return; + } + + /* If we can't find the actual file, look for the file + * with the same name but a .gz extension. If we find it, + * use that and set the gzipped flag in the file struct + * to indicate that the response need to have the content- + * encoding: gzip header. + * We can only do this if the browser declares support. */ + if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { + if (strstr(accept_encoding, "gzip") != NULL) { + mg_snprintf( + conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename); + + if (truncated) { + goto interpret_cleanup; + } + + if (mg_stat(conn, gz_path, filep)) { + if (filep) { + filep->gzipped = 1; + *is_found = 1; + } + /* Currently gz files can not be scripts. */ + return; + } + } + } + +#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) + /* Support PATH_INFO for CGI scripts. */ + for (p = filename + strlen(filename); p > filename + 1; p--) { + if (*p == '/') { + *p = '\0'; + if ((0 +#if !defined(NO_CGI) + || match_prefix(conn->ctx->config[CGI_EXTENSIONS], + strlen(conn->ctx->config[CGI_EXTENSIONS]), + filename) > 0 +#endif +#if defined(USE_LUA) + || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], + strlen( + conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), + filename) > 0 +#endif +#if defined(USE_DUKTAPE) + || match_prefix( + conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], + strlen(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]), + filename) > 0 +#endif + ) && mg_stat(conn, filename, filep)) { + /* Shift PATH_INFO block one character right, e.g. + * "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" + * conn->path_info is pointing to the local variable "path" + * declared in handle_request(), so PATH_INFO is not valid + * after handle_request returns. */ + conn->path_info = p + 1; + memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for + * trailing \0 */ + p[1] = '/'; + *is_script_resource = 1; + break; + } else { + *p = '/'; + } + } + } +#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */ +#endif /* !defined(NO_FILES) */ + return; + +#if !defined(NO_FILES) +/* Reset all outputs */ +interpret_cleanup: + memset(filep, 0, sizeof(*filep)); + *filename = 0; + *is_found = 0; + *is_script_resource = 0; + *is_websocket_request = 0; + *is_put_or_delete_request = 0; +#endif /* !defined(NO_FILES) */ +} + + +/* Check whether full request is buffered. Return: + * -1 if request is malformed + * 0 if request is not yet fully buffered + * >0 actual request length, including last \r\n\r\n */ +static int +get_request_len(const char *buf, int buflen) +{ + const char *s, *e; + int len = 0; + + for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) + /* Control characters are not allowed but >=128 is. */ + if (!isprint(*(const unsigned char *)s) && *s != '\r' && *s != '\n' + && *(const unsigned char *)s < 128) { + len = -1; + break; /* [i_a] abort scan as soon as one malformed character is + * found; */ + /* don't let subsequent \r\n\r\n win us over anyhow */ + } else if (s[0] == '\n' && s[1] == '\n') { + len = (int)(s - buf) + 2; + } else if (s[0] == '\n' && &s[1] < e && s[1] == '\r' && s[2] == '\n') { + len = (int)(s - buf) + 3; + } + + return len; +} + + +#if !defined(NO_CACHING) +/* Convert month to the month number. Return -1 on error, or month number */ +static int +get_month_index(const char *s) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(month_names); i++) { + if (!strcmp(s, month_names[i])) { + return (int)i; + } + } + + return -1; +} + + +/* Parse UTC date-time string, and return the corresponding time_t value. */ +static time_t +parse_date_string(const char *datetime) +{ + char month_str[32] = {0}; + int second, minute, hour, day, month, year; + time_t result = (time_t)0; + struct tm tm; + + if ((sscanf(datetime, + "%d/%3s/%d %d:%d:%d", + &day, + month_str, + &year, + &hour, + &minute, + &second) == 6) || (sscanf(datetime, + "%d %3s %d %d:%d:%d", + &day, + month_str, + &year, + &hour, + &minute, + &second) == 6) + || (sscanf(datetime, + "%*3s, %d %3s %d %d:%d:%d", + &day, + month_str, + &year, + &hour, + &minute, + &second) == 6) || (sscanf(datetime, + "%d-%3s-%d %d:%d:%d", + &day, + month_str, + &year, + &hour, + &minute, + &second) == 6)) { + month = get_month_index(month_str); + if ((month >= 0) && (year >= 1970)) { + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = minute; + tm.tm_sec = second; + result = timegm(&tm); + } + } + + return result; +} +#endif /* !NO_CACHING */ + + +/* Protect against directory disclosure attack by removing '..', + * excessive '/' and '\' characters */ +static void +remove_double_dots_and_double_slashes(char *s) +{ + char *p = s; + + while (*s != '\0') { + *p++ = *s++; + if (s[-1] == '/' || s[-1] == '\\') { + /* Skip all following slashes, backslashes and double-dots */ + while (s[0] != '\0') { + if (s[0] == '/' || s[0] == '\\') { + s++; + } else if (s[0] == '.' && s[1] == '.') { + s += 2; + } else { + break; + } + } + } + } + *p = '\0'; +} + + +static const struct { + const char *extension; + size_t ext_len; + const char *mime_type; +} builtin_mime_types[] = { + /* IANA registered MIME types (http://www.iana.org/assignments/media-types) + * application types */ + {".doc", 4, "application/msword"}, + {".eps", 4, "application/postscript"}, + {".exe", 4, "application/octet-stream"}, + {".js", 3, "application/javascript"}, + {".json", 5, "application/json"}, + {".pdf", 4, "application/pdf"}, + {".ps", 3, "application/postscript"}, + {".rtf", 4, "application/rtf"}, + {".xhtml", 6, "application/xhtml+xml"}, + {".xsl", 4, "application/xml"}, + {".xslt", 5, "application/xml"}, + + /* fonts */ + {".ttf", 4, "application/font-sfnt"}, + {".cff", 4, "application/font-sfnt"}, + {".otf", 4, "application/font-sfnt"}, + {".aat", 4, "application/font-sfnt"}, + {".sil", 4, "application/font-sfnt"}, + {".pfr", 4, "application/font-tdpfr"}, + {".woff", 5, "application/font-woff"}, + + /* audio */ + {".mp3", 4, "audio/mpeg"}, + {".oga", 4, "audio/ogg"}, + {".ogg", 4, "audio/ogg"}, + + /* image */ + {".gif", 4, "image/gif"}, + {".ief", 4, "image/ief"}, + {".jpeg", 5, "image/jpeg"}, + {".jpg", 4, "image/jpeg"}, + {".jpm", 4, "image/jpm"}, + {".jpx", 4, "image/jpx"}, + {".png", 4, "image/png"}, + {".svg", 4, "image/svg+xml"}, + {".tif", 4, "image/tiff"}, + {".tiff", 5, "image/tiff"}, + + /* model */ + {".wrl", 4, "model/vrml"}, + + /* text */ + {".css", 4, "text/css"}, + {".csv", 4, "text/csv"}, + {".htm", 4, "text/html"}, + {".html", 5, "text/html"}, + {".sgm", 4, "text/sgml"}, + {".shtm", 5, "text/html"}, + {".shtml", 6, "text/html"}, + {".txt", 4, "text/plain"}, + {".xml", 4, "text/xml"}, + + /* video */ + {".mov", 4, "video/quicktime"}, + {".mp4", 4, "video/mp4"}, + {".mpeg", 5, "video/mpeg"}, + {".mpg", 4, "video/mpeg"}, + {".ogv", 4, "video/ogg"}, + {".qt", 3, "video/quicktime"}, + + /* not registered types + * (http://reference.sitepoint.com/html/mime-types-full, + * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */ + {".arj", 4, "application/x-arj-compressed"}, + {".gz", 3, "application/x-gunzip"}, + {".rar", 4, "application/x-arj-compressed"}, + {".swf", 4, "application/x-shockwave-flash"}, + {".tar", 4, "application/x-tar"}, + {".tgz", 4, "application/x-tar-gz"}, + {".torrent", 8, "application/x-bittorrent"}, + {".ppt", 4, "application/x-mspowerpoint"}, + {".xls", 4, "application/x-msexcel"}, + {".zip", 4, "application/x-zip-compressed"}, + {".aac", + 4, + "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */ + {".aif", 4, "audio/x-aif"}, + {".m3u", 4, "audio/x-mpegurl"}, + {".mid", 4, "audio/x-midi"}, + {".ra", 3, "audio/x-pn-realaudio"}, + {".ram", 4, "audio/x-pn-realaudio"}, + {".wav", 4, "audio/x-wav"}, + {".bmp", 4, "image/bmp"}, + {".ico", 4, "image/x-icon"}, + {".pct", 4, "image/x-pct"}, + {".pict", 5, "image/pict"}, + {".rgb", 4, "image/x-rgb"}, + {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */ + {".asf", 4, "video/x-ms-asf"}, + {".avi", 4, "video/x-msvideo"}, + {".m4v", 4, "video/x-m4v"}, + {NULL, 0, NULL}}; + + +const char * +mg_get_builtin_mime_type(const char *path) +{ + const char *ext; + size_t i, path_len; + + path_len = strlen(path); + + for (i = 0; builtin_mime_types[i].extension != NULL; i++) { + ext = path + (path_len - builtin_mime_types[i].ext_len); + if (path_len > builtin_mime_types[i].ext_len + && mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) { + return builtin_mime_types[i].mime_type; + } + } + + return "text/plain"; +} + + +/* Look at the "path" extension and figure what mime type it has. + * Store mime type in the vector. */ +static void +get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec) +{ + struct vec ext_vec, mime_vec; + const char *list, *ext; + size_t path_len; + + path_len = strlen(path); + + if (ctx == NULL || vec == NULL) { + return; + } + + /* Scan user-defined mime types first, in case user wants to + * override default mime types. */ + list = ctx->config[EXTRA_MIME_TYPES]; + while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { + /* ext now points to the path suffix */ + ext = path + path_len - ext_vec.len; + if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { + *vec = mime_vec; + return; + } + } + + vec->ptr = mg_get_builtin_mime_type(path); + vec->len = strlen(vec->ptr); +} + + +/* Stringify binary data. Output buffer must be twice as big as input, + * because each byte takes 2 bytes in string representation */ +static void +bin2str(char *to, const unsigned char *p, size_t len) +{ + static const char *hex = "0123456789abcdef"; + + for (; len--; p++) { + *to++ = hex[p[0] >> 4]; + *to++ = hex[p[0] & 0x0f]; + } + *to = '\0'; +} + + +/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */ +char * +mg_md5(char buf[33], ...) +{ + md5_byte_t hash[16]; + const char *p; + va_list ap; + md5_state_t ctx; + + md5_init(&ctx); + + va_start(ap, buf); + while ((p = va_arg(ap, const char *)) != NULL) { + md5_append(&ctx, (const md5_byte_t *)p, strlen(p)); + } + va_end(ap); + + md5_finish(&ctx, hash); + bin2str(buf, hash, sizeof(hash)); + return buf; +} + + +/* Check the user's password, return 1 if OK */ +static int +check_password(const char *method, + const char *ha1, + const char *uri, + const char *nonce, + const char *nc, + const char *cnonce, + const char *qop, + const char *response) +{ + char ha2[32 + 1], expected_response[32 + 1]; + + /* Some of the parameters may be NULL */ + if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL + || qop == NULL + || response == NULL) { + return 0; + } + + /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */ + if (strlen(response) != 32) { + return 0; + } + + mg_md5(ha2, method, ":", uri, NULL); + mg_md5(expected_response, + ha1, + ":", + nonce, + ":", + nc, + ":", + cnonce, + ":", + qop, + ":", + ha2, + NULL); + + return mg_strcasecmp(response, expected_response) == 0; +} + + +/* Use the global passwords file, if specified by auth_gpass option, + * or search for .htpasswd in the requested directory. */ +static void +open_auth_file(struct mg_connection *conn, const char *path, struct file *filep) +{ + if (conn != NULL && conn->ctx != NULL) { + char name[PATH_MAX]; + const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE]; + struct file file = STRUCT_FILE_INITIALIZER; + int truncated; + + if (gpass != NULL) { + /* Use global passwords file */ + if (!mg_fopen(conn, gpass, "r", filep)) { +#ifdef DEBUG + mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO)); +#endif + } + /* Important: using local struct file to test path for is_directory + * flag. If filep is used, mg_stat() makes it appear as if auth file + * was opened. */ + } else if (mg_stat(conn, path, &file) && file.is_directory) { + mg_snprintf(conn, + &truncated, + name, + sizeof(name), + "%s/%s", + path, + PASSWORDS_FILE_NAME); + + if (truncated || !mg_fopen(conn, name, "r", filep)) { +#ifdef DEBUG + mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); +#endif + } + } else { + /* Try to find .htpasswd in requested directory. */ + for (p = path, e = p + strlen(p) - 1; e > p; e--) { + if (e[0] == '/') { + break; + } + } + mg_snprintf(conn, + &truncated, + name, + sizeof(name), + "%.*s/%s", + (int)(e - p), + p, + PASSWORDS_FILE_NAME); + + if (truncated || !mg_fopen(conn, name, "r", filep)) { +#ifdef DEBUG + mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); +#endif + } + } + } +} + + +/* Parsed Authorization header */ +struct ah { + char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; +}; + + +/* Return 1 on success. Always initializes the ah structure. */ +static int +parse_auth_header(struct mg_connection *conn, + char *buf, + size_t buf_size, + struct ah *ah) +{ + char *name, *value, *s; + const char *auth_header; + uint64_t nonce; + + if (!ah || !conn) { + return 0; + } + + (void)memset(ah, 0, sizeof(*ah)); + if ((auth_header = mg_get_header(conn, "Authorization")) == NULL + || mg_strncasecmp(auth_header, "Digest ", 7) != 0) { + return 0; + } + + /* Make modifiable copy of the auth header */ + (void)mg_strlcpy(buf, auth_header + 7, buf_size); + s = buf; + + /* Parse authorization header */ + for (;;) { + /* Gobble initial spaces */ + while (isspace(*(unsigned char *)s)) { + s++; + } + name = skip_quoted(&s, "=", " ", 0); + /* Value is either quote-delimited, or ends at first comma or space. */ + if (s[0] == '\"') { + s++; + value = skip_quoted(&s, "\"", " ", '\\'); + if (s[0] == ',') { + s++; + } + } else { + value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses + * spaces */ + } + if (*name == '\0') { + break; + } + + if (!strcmp(name, "username")) { + ah->user = value; + } else if (!strcmp(name, "cnonce")) { + ah->cnonce = value; + } else if (!strcmp(name, "response")) { + ah->response = value; + } else if (!strcmp(name, "uri")) { + ah->uri = value; + } else if (!strcmp(name, "qop")) { + ah->qop = value; + } else if (!strcmp(name, "nc")) { + ah->nc = value; + } else if (!strcmp(name, "nonce")) { + ah->nonce = value; + } + } + +#ifndef NO_NONCE_CHECK + /* Read the nonce from the response. */ + if (ah->nonce == NULL) { + return 0; + } + s = NULL; + nonce = strtoull(ah->nonce, &s, 10); + if ((s == NULL) || (*s != 0)) { + return 0; + } + + /* Convert the nonce from the client to a number. */ + nonce ^= conn->ctx->auth_nonce_mask; + + /* The converted number corresponds to the time the nounce has been + * created. This should not be earlier than the server start. */ + /* Server side nonce check is valuable in all situations but one: + * if the server restarts frequently, but the client should not see + * that, so the server should accept nonces from previous starts. */ + /* However, the reasonable default is to not accept a nonce from a + * previous start, so if anyone changed the access rights between + * two restarts, a new login is required. */ + if (nonce < (uint64_t)conn->ctx->start_time) { + /* nonce is from a previous start of the server and no longer valid + * (replay attack?) */ + return 0; + } + /* Check if the nonce is too high, so it has not (yet) been used by the + * server. */ + if (nonce >= ((uint64_t)conn->ctx->start_time + conn->ctx->nonce_count)) { + return 0; + } +#endif + + /* CGI needs it as REMOTE_USER */ + if (ah->user != NULL) { + conn->request_info.remote_user = mg_strdup(ah->user); + } else { + return 0; + } + + return 1; +} + + +static const char * +mg_fgets(char *buf, size_t size, struct file *filep, char **p) +{ + const char *eof; + size_t len; + const char *memend; + + if (!filep) { + return NULL; + } + + if (filep->membuf != NULL && *p != NULL) { + memend = (const char *)&filep->membuf[filep->size]; + /* Search for \n from p till the end of stream */ + eof = (char *)memchr(*p, '\n', (size_t)(memend - *p)); + if (eof != NULL) { + eof += 1; /* Include \n */ + } else { + eof = memend; /* Copy remaining data */ + } + len = (size_t)(eof - *p) > size - 1 ? size - 1 : (size_t)(eof - *p); + memcpy(buf, *p, len); + buf[len] = '\0'; + *p += len; + return len ? eof : NULL; + } else if (filep->fp != NULL) { + return fgets(buf, (int)size, filep->fp); + } else { + return NULL; + } +} + +struct read_auth_file_struct { + struct mg_connection *conn; + struct ah ah; + char *domain; + char buf[256 + 256 + 40]; + char *f_user; + char *f_domain; + char *f_ha1; +}; + + +static int +read_auth_file(struct file *filep, struct read_auth_file_struct *workdata) +{ + char *p; + int is_authorized = 0; + struct file fp; + size_t l; + + if (!filep || !workdata) { + return 0; + } + + /* Loop over passwords file */ + p = (char *)filep->membuf; + while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) { + l = strlen(workdata->buf); + while (l > 0) { + if (isspace(workdata->buf[l - 1]) + || iscntrl(workdata->buf[l - 1])) { + l--; + workdata->buf[l] = 0; + } else + break; + } + if (l < 1) { + continue; + } + + workdata->f_user = workdata->buf; + + if (workdata->f_user[0] == ':') { + /* user names may not contain a ':' and may not be empty, + * so lines starting with ':' may be used for a special purpose */ + if (workdata->f_user[1] == '#') { + /* :# is a comment */ + continue; + } else if (!strncmp(workdata->f_user + 1, "include=", 8)) { + if (mg_fopen(workdata->conn, workdata->f_user + 9, "r", &fp)) { + is_authorized = read_auth_file(&fp, workdata); + mg_fclose(&fp); + } else { + mg_cry(workdata->conn, + "%s: cannot open authorization file: %s", + __func__, + workdata->buf); + } + continue; + } + /* everything is invalid for the moment (might change in the + * future) */ + mg_cry(workdata->conn, + "%s: syntax error in authorization file: %s", + __func__, + workdata->buf); + continue; + } + + workdata->f_domain = strchr(workdata->f_user, ':'); + if (workdata->f_domain == NULL) { + mg_cry(workdata->conn, + "%s: syntax error in authorization file: %s", + __func__, + workdata->buf); + continue; + } + *(workdata->f_domain) = 0; + (workdata->f_domain)++; + + workdata->f_ha1 = strchr(workdata->f_domain, ':'); + if (workdata->f_ha1 == NULL) { + mg_cry(workdata->conn, + "%s: syntax error in authorization file: %s", + __func__, + workdata->buf); + continue; + } + *(workdata->f_ha1) = 0; + (workdata->f_ha1)++; + + if (!strcmp(workdata->ah.user, workdata->f_user) + && !strcmp(workdata->domain, workdata->f_domain)) { + return check_password(workdata->conn->request_info.request_method, + workdata->f_ha1, + workdata->ah.uri, + workdata->ah.nonce, + workdata->ah.nc, + workdata->ah.cnonce, + workdata->ah.qop, + workdata->ah.response); + } + } + + return is_authorized; +} + + +/* Authorize against the opened passwords file. Return 1 if authorized. */ +static int +authorize(struct mg_connection *conn, struct file *filep) +{ + struct read_auth_file_struct workdata; + char buf[MG_BUF_LEN]; + + if (!conn || !conn->ctx) { + return 0; + } + + memset(&workdata, 0, sizeof(workdata)); + workdata.conn = conn; + + if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) { + return 0; + } + workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; + + return read_auth_file(filep, &workdata); +} + + +/* Return 1 if request is authorised, 0 otherwise. */ +static int +check_authorization(struct mg_connection *conn, const char *path) +{ + char fname[PATH_MAX]; + struct vec uri_vec, filename_vec; + const char *list; + struct file file = STRUCT_FILE_INITIALIZER; + int authorized = 1, truncated; + + if (!conn || !conn->ctx) { + return 0; + } + + list = conn->ctx->config[PROTECT_URI]; + while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { + if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) { + mg_snprintf(conn, + &truncated, + fname, + sizeof(fname), + "%.*s", + (int)filename_vec.len, + filename_vec.ptr); + + if (truncated || !mg_fopen(conn, fname, "r", &file)) { + mg_cry(conn, + "%s: cannot open %s: %s", + __func__, + fname, + strerror(errno)); + } + break; + } + } + + if (!is_file_opened(&file)) { + open_auth_file(conn, path, &file); + } + + if (is_file_opened(&file)) { + authorized = authorize(conn, &file); + mg_fclose(&file); + } + + return authorized; +} + + +static void +send_authorization_request(struct mg_connection *conn) +{ + char date[64]; + time_t curtime = time(NULL); + + if (conn && conn->ctx) { + uint64_t nonce = (uint64_t)(conn->ctx->start_time); + + (void)pthread_mutex_lock(&conn->ctx->nonce_mutex); + nonce += conn->ctx->nonce_count; + ++conn->ctx->nonce_count; + (void)pthread_mutex_unlock(&conn->ctx->nonce_mutex); + + nonce ^= conn->ctx->auth_nonce_mask; + conn->status_code = 401; + conn->must_close = 1; + + gmt_time_string(date, sizeof(date), &curtime); + + mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n"); + send_no_cache_header(conn); + mg_printf(conn, + "Date: %s\r\n" + "Connection: %s\r\n" + "Content-Length: 0\r\n" + "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", " + "nonce=\"%" UINT64_FMT "\"\r\n\r\n", + date, + suggest_connection_header(conn), + conn->ctx->config[AUTHENTICATION_DOMAIN], + nonce); + } +} + + +#if !defined(NO_FILES) +static int +is_authorized_for_put(struct mg_connection *conn) +{ + if (conn) { + struct file file = STRUCT_FILE_INITIALIZER; + const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; + int ret = 0; + + if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) { + ret = authorize(conn, &file); + mg_fclose(&file); + } + + return ret; + } + return 0; +} +#endif + + +int +mg_modify_passwords_file(const char *fname, + const char *domain, + const char *user, + const char *pass) +{ + int found, i; + char line[512], u[512] = "", d[512] = "", ha1[33], tmp[PATH_MAX + 8]; + FILE *fp, *fp2; + + found = 0; + fp = fp2 = NULL; + + /* Regard empty password as no password - remove user record. */ + if (pass != NULL && pass[0] == '\0') { + pass = NULL; + } + + /* Other arguments must not be empty */ + if (fname == NULL || domain == NULL || user == NULL) { + return 0; + } + + /* Using the given file format, user name and domain must not contain ':' + */ + if (strchr(user, ':') != NULL) { + return 0; + } + if (strchr(domain, ':') != NULL) { + return 0; + } + + /* Do not allow control characters like newline in user name and domain. + * Do not allow excessively long names either. */ + for (i = 0; i < 255 && user[i] != 0; i++) { + if (iscntrl(user[i])) { + return 0; + } + } + if (user[i]) { + return 0; + } + for (i = 0; i < 255 && domain[i] != 0; i++) { + if (iscntrl(domain[i])) { + return 0; + } + } + if (domain[i]) { + return 0; + } + + /* The maximum length of the path to the password file is limited */ + if ((strlen(fname) + 4) >= PATH_MAX) { + return 0; + } + + /* Create a temporary file name. Length has been checked before. */ + strcpy(tmp, fname); + strcat(tmp, ".tmp"); + + /* Create the file if does not exist */ + /* Use of fopen here is OK, since fname is only ASCII */ + if ((fp = fopen(fname, "a+")) != NULL) { + (void)fclose(fp); + } + + /* Open the given file and temporary file */ + if ((fp = fopen(fname, "r")) == NULL) { + return 0; + } else if ((fp2 = fopen(tmp, "w+")) == NULL) { + fclose(fp); + return 0; + } + + /* Copy the stuff to temporary file */ + while (fgets(line, sizeof(line), fp) != NULL) { + if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) { + continue; + } + u[255] = 0; + d[255] = 0; + + if (!strcmp(u, user) && !strcmp(d, domain)) { + found++; + if (pass != NULL) { + mg_md5(ha1, user, ":", domain, ":", pass, NULL); + fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); + } + } else { + fprintf(fp2, "%s", line); + } + } + + /* If new user, just add it */ + if (!found && pass != NULL) { + mg_md5(ha1, user, ":", domain, ":", pass, NULL); + fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); + } + + /* Close files */ + fclose(fp); + fclose(fp2); + + /* Put the temp file in place of real file */ + IGNORE_UNUSED_RESULT(remove(fname)); + IGNORE_UNUSED_RESULT(rename(tmp, fname)); + + return 1; +} + + +static int +is_valid_port(unsigned long port) +{ + return port < 0xffff; +} + + +static int +mg_inet_pton(int af, const char *src, void *dst, size_t dstlen) +{ + struct addrinfo hints, *res, *ressave; + int func_ret = 0; + int gai_ret; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = af; + + gai_ret = getaddrinfo(src, NULL, &hints, &res); + if (gai_ret != 0) { + /* gai_strerror could be used to convert gai_ret to a string */ + /* POSIX return values: see + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html + */ + /* Windows return values: see + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx + */ + return 0; + } + + ressave = res; + + while (res) { + if (dstlen >= res->ai_addrlen) { + memcpy(dst, res->ai_addr, res->ai_addrlen); + func_ret = 1; + } + res = res->ai_next; + } + + freeaddrinfo(ressave); + return func_ret; +} + + +static int +connect_socket(struct mg_context *ctx /* may be NULL */, + const char *host, + int port, + int use_ssl, + char *ebuf, + size_t ebuf_len, + SOCKET *sock /* output: socket, must not be NULL */, + union usa *sa /* output: socket address, must not be NULL */ + ) +{ + int ip_ver = 0; + *sock = INVALID_SOCKET; + memset(sa, 0, sizeof(*sa)); + + if (ebuf_len > 0) { + *ebuf = 0; + } + + if (host == NULL) { + mg_snprintf(NULL, + NULL, /* No truncation check for ebuf */ + ebuf, + ebuf_len, + "%s", + "NULL host"); + return 0; + } + + if (port < 0 || !is_valid_port((unsigned)port)) { + mg_snprintf(NULL, + NULL, /* No truncation check for ebuf */ + ebuf, + ebuf_len, + "%s", + "invalid port"); + return 0; + } + + if (use_ssl && (SSLv23_client_method == NULL)) { + mg_snprintf(NULL, + NULL, /* No truncation check for ebuf */ + ebuf, + ebuf_len, + "%s", + "SSL is not initialized"); + return 0; + } + + if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin))) { + sa->sin.sin_port = htons((uint16_t)port); + ip_ver = 4; +#ifdef USE_IPV6 + } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6))) { + sa->sin6.sin6_port = htons((uint16_t)port); + ip_ver = 6; + } else if (host[0] == '[') { + /* While getaddrinfo on Windows will work with [::1], + * getaddrinfo on Linux only works with ::1 (without []). */ + size_t l = strlen(host + 1); + char *h = l > 1 ? mg_strdup(host + 1) : NULL; + if (h) { + h[l - 1] = 0; + if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6))) { + sa->sin6.sin6_port = htons((uint16_t)port); + ip_ver = 6; + } + mg_free(h); + } +#endif + } + + if (ip_ver == 0) { + mg_snprintf(NULL, + NULL, /* No truncation check for ebuf */ + ebuf, + ebuf_len, + "%s", + "host not found"); + return 0; + } + + if (ip_ver == 4) { + *sock = socket(PF_INET, SOCK_STREAM, 0); + } +#ifdef USE_IPV6 + else if (ip_ver == 6) { + *sock = socket(PF_INET6, SOCK_STREAM, 0); + } +#endif + + if (*sock == INVALID_SOCKET) { + mg_snprintf(NULL, + NULL, /* No truncation check for ebuf */ + ebuf, + ebuf_len, + "socket(): %s", + strerror(ERRNO)); + return 0; + } + + set_close_on_exec(*sock, fc(ctx)); + + if ((ip_ver == 4) + && (connect(*sock, (struct sockaddr *)&sa->sin, sizeof(sa->sin)) + == 0)) { + /* connected with IPv4 */ + return 1; + } + +#ifdef USE_IPV6 + if ((ip_ver == 6) + && (connect(*sock, (struct sockaddr *)&sa->sin6, sizeof(sa->sin6)) + == 0)) { + /* connected with IPv6 */ + return 1; + } +#endif + + /* Not connected */ + mg_snprintf(NULL, + NULL, /* No truncation check for ebuf */ + ebuf, + ebuf_len, + "connect(%s:%d): %s", + host, + port, + strerror(ERRNO)); + closesocket(*sock); + *sock = INVALID_SOCKET; + return 0; +} + + +int +mg_url_encode(const char *src, char *dst, size_t dst_len) +{ + static const char *dont_escape = "._-$,;~()"; + static const char *hex = "0123456789abcdef"; + char *pos = dst; + const char *end = dst + dst_len - 1; + + for (; *src != '\0' && pos < end; src++, pos++) { + if (isalnum(*(const unsigned char *)src) + || strchr(dont_escape, *(const unsigned char *)src) != NULL) { + *pos = *src; + } else if (pos + 2 < end) { + pos[0] = '%'; + pos[1] = hex[(*(const unsigned char *)src) >> 4]; + pos[2] = hex[(*(const unsigned char *)src) & 0xf]; + pos += 2; + } else { + break; + } + } + + *pos = '\0'; + return (*src == '\0') ? (int)(pos - dst) : -1; +} + + +static void +print_dir_entry(struct de *de) +{ + char size[64], mod[64], href[PATH_MAX]; + struct tm *tm; + + if (de->file.is_directory) { + mg_snprintf(de->conn, + NULL, /* Buffer is big enough */ + size, + sizeof(size), + "%s", + "[DIRECTORY]"); + } else { + /* We use (signed) cast below because MSVC 6 compiler cannot + * convert unsigned __int64 to double. Sigh. */ + if (de->file.size < 1024) { + mg_snprintf(de->conn, + NULL, /* Buffer is big enough */ + size, + sizeof(size), + "%d", + (int)de->file.size); + } else if (de->file.size < 0x100000) { + mg_snprintf(de->conn, + NULL, /* Buffer is big enough */ + size, + sizeof(size), + "%.1fk", + (double)de->file.size / 1024.0); + } else if (de->file.size < 0x40000000) { + mg_snprintf(de->conn, + NULL, /* Buffer is big enough */ + size, + sizeof(size), + "%.1fM", + (double)de->file.size / 1048576); + } else { + mg_snprintf(de->conn, + NULL, /* Buffer is big enough */ + size, + sizeof(size), + "%.1fG", + (double)de->file.size / 1073741824); + } + } + + /* Note: mg_snprintf will not cause a buffer overflow above. + * So, string truncation checks are not required here. */ + + tm = localtime(&de->file.last_modified); + if (tm != NULL) { + strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm); + } else { + mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod)); + mod[sizeof(mod) - 1] = '\0'; + } + mg_url_encode(de->file_name, href, sizeof(href)); + de->conn->num_bytes_sent += + mg_printf(de->conn, + "%s%s" + " %s  %s\n", + de->conn->request_info.local_uri, + href, + de->file.is_directory ? "/" : "", + de->file_name, + de->file.is_directory ? "/" : "", + mod, + size); +} + + +/* This function is called from send_directory() and used for + * sorting directory entries by size, or name, or modification time. + * On windows, __cdecl specification is needed in case if project is built + * with __stdcall convention. qsort always requires __cdels callback. */ +static int WINCDECL +compare_dir_entries(const void *p1, const void *p2) +{ + if (p1 && p2) { + const struct de *a = (const struct de *)p1, *b = (const struct de *)p2; + const char *query_string = a->conn->request_info.query_string; + int cmp_result = 0; + + if (query_string == NULL) { + query_string = "na"; + } + + if (a->file.is_directory && !b->file.is_directory) { + return -1; /* Always put directories on top */ + } else if (!a->file.is_directory && b->file.is_directory) { + return 1; /* Always put directories on top */ + } else if (*query_string == 'n') { + cmp_result = strcmp(a->file_name, b->file_name); + } else if (*query_string == 's') { + cmp_result = a->file.size == b->file.size + ? 0 + : a->file.size > b->file.size ? 1 : -1; + } else if (*query_string == 'd') { + cmp_result = + (a->file.last_modified == b->file.last_modified) + ? 0 + : ((a->file.last_modified > b->file.last_modified) ? 1 + : -1); + } + + return query_string[1] == 'd' ? -cmp_result : cmp_result; + } + return 0; +} + + +static int +must_hide_file(struct mg_connection *conn, const char *path) +{ + if (conn && conn->ctx) { + const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; + const char *pattern = conn->ctx->config[HIDE_FILES]; + return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 + || (pattern != NULL + && match_prefix(pattern, strlen(pattern), path) > 0); + } + return 0; +} + + +static int +scan_directory(struct mg_connection *conn, + const char *dir, + void *data, + void (*cb)(struct de *, void *)) +{ + char path[PATH_MAX]; + struct dirent *dp; + DIR *dirp; + struct de de; + int truncated; + + if ((dirp = mg_opendir(conn, dir)) == NULL) { + return 0; + } else { + de.conn = conn; + + while ((dp = mg_readdir(dirp)) != NULL) { + /* Do not show current dir and hidden files */ + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") + || must_hide_file(conn, dp->d_name)) { + continue; + } + + mg_snprintf( + conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name); + + /* If we don't memset stat structure to zero, mtime will have + * garbage and strftime() will segfault later on in + * print_dir_entry(). memset is required only if mg_stat() + * fails. For more details, see + * http://code.google.com/p/mongoose/issues/detail?id=79 */ + memset(&de.file, 0, sizeof(de.file)); + + if (truncated) { + /* If the path is not complete, skip processing. */ + continue; + } + + if (!mg_stat(conn, path, &de.file)) { + mg_cry(conn, + "%s: mg_stat(%s) failed: %s", + __func__, + path, + strerror(ERRNO)); + } + de.file_name = dp->d_name; + cb(&de, data); + } + (void)mg_closedir(dirp); + } + return 1; +} + + +#if !defined(NO_FILES) +static int +remove_directory(struct mg_connection *conn, const char *dir) +{ + char path[PATH_MAX]; + struct dirent *dp; + DIR *dirp; + struct de de; + int truncated; + int ok = 1; + + if ((dirp = mg_opendir(conn, dir)) == NULL) { + return 0; + } else { + de.conn = conn; + + while ((dp = mg_readdir(dirp)) != NULL) { + /* Do not show current dir (but show hidden files as they will + * also be removed) */ + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { + continue; + } + + mg_snprintf( + conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name); + + /* If we don't memset stat structure to zero, mtime will have + * garbage and strftime() will segfault later on in + * print_dir_entry(). memset is required only if mg_stat() + * fails. For more details, see + * http://code.google.com/p/mongoose/issues/detail?id=79 */ + memset(&de.file, 0, sizeof(de.file)); + + if (truncated) { + /* Do not delete anything shorter */ + ok = 0; + continue; + } + + if (!mg_stat(conn, path, &de.file)) { + mg_cry(conn, + "%s: mg_stat(%s) failed: %s", + __func__, + path, + strerror(ERRNO)); + ok = 0; + } + if (de.file.membuf == NULL) { + /* file is not in memory */ + if (de.file.is_directory) { + if (remove_directory(conn, path) == 0) { + ok = 0; + } + } else { + if (mg_remove(conn, path) == 0) { + ok = 0; + } + } + } else { + /* file is in memory. It can not be deleted. */ + ok = 0; + } + } + (void)mg_closedir(dirp); + + IGNORE_UNUSED_RESULT(rmdir(dir)); + } + + return ok; +} +#endif + + +struct dir_scan_data { + struct de *entries; + unsigned int num_entries; + unsigned int arr_size; +}; + + +/* Behaves like realloc(), but frees original pointer on failure */ +static void * +realloc2(void *ptr, size_t size) +{ + void *new_ptr = mg_realloc(ptr, size); + if (new_ptr == NULL) { + mg_free(ptr); + } + return new_ptr; +} + + +static void +dir_scan_callback(struct de *de, void *data) +{ + struct dir_scan_data *dsd = (struct dir_scan_data *)data; + + if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) { + dsd->arr_size *= 2; + dsd->entries = + (struct de *)realloc2(dsd->entries, + dsd->arr_size * sizeof(dsd->entries[0])); + } + if (dsd->entries == NULL) { + /* TODO(lsm, low): propagate an error to the caller */ + dsd->num_entries = 0; + } else { + dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name); + dsd->entries[dsd->num_entries].file = de->file; + dsd->entries[dsd->num_entries].conn = de->conn; + dsd->num_entries++; + } +} + + +static void +handle_directory_request(struct mg_connection *conn, const char *dir) +{ + unsigned int i; + int sort_direction; + struct dir_scan_data data = {NULL, 0, 128}; + char date[64]; + time_t curtime = time(NULL); + + if (!scan_directory(conn, dir, &data, dir_scan_callback)) { + send_http_error(conn, + 500, + "Error: Cannot open directory\nopendir(%s): %s", + dir, + strerror(ERRNO)); + return; + } + + gmt_time_string(date, sizeof(date), &curtime); + + if (!conn) { + return; + } + + sort_direction = conn->request_info.query_string != NULL + && conn->request_info.query_string[1] == 'd' + ? 'a' + : 'd'; + + conn->must_close = 1; + mg_printf(conn, "HTTP/1.1 200 OK\r\n"); + send_static_cache_header(conn); + mg_printf(conn, + "Date: %s\r\n" + "Connection: close\r\n" + "Content-Type: text/html; charset=utf-8\r\n\r\n", + date); + + conn->num_bytes_sent += + mg_printf(conn, + "Index of %s" + "" + "

Index of %s

"
+	              ""
+	              ""
+	              ""
+	              "",
+	              conn->request_info.local_uri,
+	              conn->request_info.local_uri,
+	              sort_direction,
+	              sort_direction,
+	              sort_direction);
+
+	/* Print first entry - link to a parent directory */
+	conn->num_bytes_sent +=
+	    mg_printf(conn,
+	              ""
+	              "\n",
+	              conn->request_info.local_uri,
+	              "..",
+	              "Parent directory",
+	              "-",
+	              "-");
+
+	/* Sort and print directory entries */
+	if (data.entries != NULL) {
+		qsort(data.entries,
+		      (size_t)data.num_entries,
+		      sizeof(data.entries[0]),
+		      compare_dir_entries);
+		for (i = 0; i < data.num_entries; i++) {
+			print_dir_entry(&data.entries[i]);
+			mg_free(data.entries[i].file_name);
+		}
+		mg_free(data.entries);
+	}
+
+	conn->num_bytes_sent += mg_printf(conn, "%s", "
NameModifiedSize

%s %s  %s
"); + conn->status_code = 200; +} + + +/* Send len bytes from the opened file to the client. */ +static void +send_file_data(struct mg_connection *conn, + struct file *filep, + int64_t offset, + int64_t len) +{ + char buf[MG_BUF_LEN]; + int to_read, num_read, num_written; + int64_t size; + + if (!filep || !conn) { + return; + } + + /* Sanity check the offset */ + size = filep->size > INT64_MAX ? INT64_MAX : (int64_t)(filep->size); + offset = offset < 0 ? 0 : offset > size ? size : offset; + + if (len > 0 && filep->membuf != NULL && size > 0) { + /* file stored in memory */ + if (len > size - offset) { + len = size - offset; + } + mg_write(conn, filep->membuf + offset, (size_t)len); + } else if (len > 0 && filep->fp != NULL) { +/* file stored on disk */ +#if defined(__linux__) + /* sendfile is only available for Linux */ + if (conn->throttle == 0 && conn->ssl == 0) { + off_t sf_offs = (off_t)offset; + ssize_t sf_sent; + int sf_file = fileno(filep->fp); + int loop_cnt = 0; + + do { + /* 2147479552 (0x7FFFF000) is a limit found by experiment on + * 64 bit Linux (2^31 minus one memory page of 4k?). */ + size_t sf_tosend = + (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000); + sf_sent = + sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend); + if (sf_sent > 0) { + conn->num_bytes_sent += sf_sent; + len -= sf_sent; + offset += sf_sent; + } else if (loop_cnt == 0) { + /* This file can not be sent using sendfile. + * This might be the case for pseudo-files in the + * /sys/ and /proc/ file system. + * Use the regular user mode copy code instead. */ + break; + } else if (sf_sent == 0) { + /* No error, but 0 bytes sent. May be EOF? */ + return; + } + loop_cnt++; + + } while ((len > 0) && (sf_sent >= 0)); + + if (sf_sent > 0) { + return; /* OK */ + } + + /* sf_sent<0 means error, thus fall back to the classic way */ + /* This is always the case, if sf_file is not a "normal" file, + * e.g., for sending data from the output of a CGI process. */ + offset = (int64_t)sf_offs; + } +#endif + if ((offset > 0) && (fseeko(filep->fp, offset, SEEK_SET) != 0)) { + mg_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO)); + send_http_error( + conn, + 500, + "%s", + "Error: Unable to access file at requested position."); + } else { + while (len > 0) { + /* Calculate how much to read from the file in the buffer */ + to_read = sizeof(buf); + if ((int64_t)to_read > len) { + to_read = (int)len; + } + + /* Read from file, exit the loop on error */ + if ((num_read = (int)fread(buf, 1, (size_t)to_read, filep->fp)) + <= 0) { + break; + } + + /* Send read bytes to the client, exit the loop on error */ + if ((num_written = mg_write(conn, buf, (size_t)num_read)) + != num_read) { + break; + } + + /* Both read and were successful, adjust counters */ + conn->num_bytes_sent += num_written; + len -= num_written; + } + } + } +} + + +static int +parse_range_header(const char *header, int64_t *a, int64_t *b) +{ + return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); +} + + +static void +construct_etag(char *buf, size_t buf_len, const struct file *filep) +{ + if (filep != NULL && buf != NULL) { + mg_snprintf(NULL, + NULL, /* All calls to construct_etag use 64 byte buffer */ + buf, + buf_len, + "\"%lx.%" INT64_FMT "\"", + (unsigned long)filep->last_modified, + filep->size); + } +} + + +static void +fclose_on_exec(struct file *filep, struct mg_connection *conn) +{ + if (filep != NULL && filep->fp != NULL) { +#ifdef _WIN32 + (void)conn; /* Unused. */ +#else + if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) { + mg_cry(conn, + "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", + __func__, + strerror(ERRNO)); + } +#endif + } +} + + +static void +handle_static_file_request(struct mg_connection *conn, + const char *path, + struct file *filep, + const char *mime_type) +{ + char date[64], lm[64], etag[64]; + char range[128]; /* large enough, so there will be no overflow */ + const char *msg = "OK", *hdr; + time_t curtime = time(NULL); + int64_t cl, r1, r2; + struct vec mime_vec; + int n, truncated; + char gz_path[PATH_MAX]; + const char *encoding = ""; + const char *cors1, *cors2, *cors3; + + if (conn == NULL || conn->ctx == NULL || filep == NULL) { + return; + } + + if (mime_type == NULL) { + get_mime_type(conn->ctx, path, &mime_vec); + } else { + mime_vec.ptr = mime_type; + mime_vec.len = strlen(mime_type); + } + if (filep->size > INT64_MAX) { + send_http_error(conn, + 500, + "Error: File size is too large to send\n%" INT64_FMT, + filep->size); + } + cl = (int64_t)filep->size; + conn->status_code = 200; + range[0] = '\0'; + + /* if this file is in fact a pre-gzipped file, rewrite its filename + * it's important to rewrite the filename after resolving + * the mime type from it, to preserve the actual file's type */ + if (filep->gzipped) { + mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path); + + if (truncated) { + send_http_error(conn, + 500, + "Error: Path of zipped file too long (%s)", + path); + return; + } + + path = gz_path; + encoding = "Content-Encoding: gzip\r\n"; + } + + if (!mg_fopen(conn, path, "rb", filep)) { + send_http_error(conn, + 500, + "Error: Cannot open file\nfopen(%s): %s", + path, + strerror(ERRNO)); + return; + } + + fclose_on_exec(filep, conn); + + /* If Range: header specified, act accordingly */ + r1 = r2 = 0; + hdr = mg_get_header(conn, "Range"); + if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && r1 >= 0 + && r2 >= 0) { + /* actually, range requests don't play well with a pre-gzipped + * file (since the range is specified in the uncompressed space) */ + if (filep->gzipped) { + send_http_error( + conn, + 501, + "%s", + "Error: Range requests in gzipped files are not supported"); + mg_fclose(filep); + return; + } + conn->status_code = 206; + cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1 : cl - r1; + mg_snprintf(conn, + NULL, /* range buffer is big enough */ + range, + sizeof(range), + "Content-Range: bytes " + "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", + r1, + r1 + cl - 1, + filep->size); + msg = "Partial Content"; + } + + hdr = mg_get_header(conn, "Origin"); + if (hdr) { + /* Cross-origin resource sharing (CORS), see + * http://www.html5rocks.com/en/tutorials/cors/, + * http://www.html5rocks.com/static/images/cors_server_flowchart.png - + * preflight is not supported for files. */ + cors1 = "Access-Control-Allow-Origin: "; + cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; + cors3 = "\r\n"; + } else { + cors1 = cors2 = cors3 = ""; + } + + /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */ + gmt_time_string(date, sizeof(date), &curtime); + gmt_time_string(lm, sizeof(lm), &filep->last_modified); + construct_etag(etag, sizeof(etag), filep); + + (void)mg_printf(conn, + "HTTP/1.1 %d %s\r\n" + "%s%s%s" + "Date: %s\r\n", + conn->status_code, + msg, + cors1, + cors2, + cors3, + date); + send_static_cache_header(conn); + (void)mg_printf(conn, + "Last-Modified: %s\r\n" + "Etag: %s\r\n" + "Content-Type: %.*s\r\n" + "Content-Length: %" INT64_FMT "\r\n" + "Connection: %s\r\n" + "Accept-Ranges: bytes\r\n" + "%s%s\r\n", + lm, + etag, + (int)mime_vec.len, + mime_vec.ptr, + cl, + suggest_connection_header(conn), + range, + encoding); + + if (strcmp(conn->request_info.request_method, "HEAD") != 0) { + send_file_data(conn, filep, r1, cl); + } + mg_fclose(filep); +} + + +void +mg_send_file(struct mg_connection *conn, const char *path) +{ + mg_send_mime_file(conn, path, NULL); +} + + +void +mg_send_mime_file(struct mg_connection *conn, + const char *path, + const char *mime_type) +{ + struct file file = STRUCT_FILE_INITIALIZER; + if (mg_stat(conn, path, &file)) { + if (file.is_directory) { + if (!conn) { + return; + } + if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], + "yes")) { + handle_directory_request(conn, path); + } else { + send_http_error(conn, + 403, + "%s", + "Error: Directory listing denied"); + } + } else { + handle_static_file_request(conn, path, &file, mime_type); + } + } else { + send_http_error(conn, 404, "%s", "Error: File not found"); + } +} + + +/* For a given PUT path, create all intermediate subdirectories. + * Return 0 if the path itself is a directory. + * Return 1 if the path leads to a file. + * Return -1 for if the path is too long. + * Return -2 if path can not be created. +*/ +static int +put_dir(struct mg_connection *conn, const char *path) +{ + char buf[PATH_MAX]; + const char *s, *p; + struct file file = STRUCT_FILE_INITIALIZER; + size_t len; + int res = 1; + + for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { + len = (size_t)(p - path); + if (len >= sizeof(buf)) { + /* path too long */ + res = -1; + break; + } + memcpy(buf, path, len); + buf[len] = '\0'; + + /* Try to create intermediate directory */ + DEBUG_TRACE("mkdir(%s)", buf); + if (!mg_stat(conn, buf, &file) && mg_mkdir(conn, buf, 0755) != 0) { + /* path does not exixt and can not be created */ + res = -2; + break; + } + + /* Is path itself a directory? */ + if (p[1] == '\0') { + res = 0; + } + } + + return res; +} + + +static void +remove_bad_file(const struct mg_connection *conn, const char *path) +{ + int r = mg_remove(conn, path); + if (r != 0) { + mg_cry(conn, "%s: Cannot remove invalid file %s", __func__, path); + } +} + + +long long +mg_store_body(struct mg_connection *conn, const char *path) +{ + char buf[MG_BUF_LEN]; + long long len = 0; + int ret, n; + struct file fi; + + if (conn->consumed_content != 0) { + mg_cry(conn, "%s: Contents already consumed", __func__); + return -11; + } + + ret = put_dir(conn, path); + if (ret < 0) { + /* -1 for path too long, + * -2 for path can not be created. */ + return ret; + } + if (ret != 1) { + /* Return 0 means, path itself is a directory. */ + return 0; + } + + if (mg_fopen(conn, path, "w", &fi) == 0) { + return -12; + } + + ret = mg_read(conn, buf, sizeof(buf)); + while (ret > 0) { + n = (int)fwrite(buf, 1, (size_t)ret, fi.fp); + if (n != ret) { + mg_fclose(&fi); + remove_bad_file(conn, path); + return -13; + } + ret = mg_read(conn, buf, sizeof(buf)); + } + + /* TODO: mg_fclose should return an error, + * and every caller should check and handle it. */ + if (fclose(fi.fp) != 0) { + remove_bad_file(conn, path); + return -14; + } + + return len; +} + + +/* Parse HTTP headers from the given buffer, advance buffer to the point + * where parsing stopped. */ +static void +parse_http_headers(char **buf, struct mg_request_info *ri) +{ + int i; + + if (!ri) { + return; + } + + ri->num_headers = 0; + + for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) { + char *dp = *buf; + while ((*dp != ':') && (*dp != '\r') && (*dp != 0)) { + dp++; + } + if (!*dp) { + /* neither : nor \r\n. This is not a valid field. */ + break; + } + if (*dp == '\r') { + if (dp[1] == '\n') { + /* \r\n */ + ri->http_headers[i].name = *buf; + ri->http_headers[i].value = 0; + *buf = dp; + } else { + /* stray \r. This is not valid. */ + break; + } + } else { + /* (*dp == ':') */ + *dp = 0; + ri->http_headers[i].name = *buf; + do { + dp++; + } while (*dp == ' '); + + ri->http_headers[i].value = dp; + *buf = strstr(dp, "\r\n"); + } + + ri->num_headers = i + 1; + if (*buf) { + (*buf)[0] = 0; + (*buf)[1] = 0; + *buf += 2; + } else { + *buf = dp; + break; + } + + if (*buf[0] == '\r') { + /* This is the end of the header */ + break; + } + } +} + + +static int +is_valid_http_method(const char *method) +{ + return !strcmp(method, "GET") /* HTTP (RFC 2616) */ + || !strcmp(method, "POST") /* HTTP (RFC 2616) */ + || !strcmp(method, "HEAD") /* HTTP (RFC 2616) */ + || !strcmp(method, "PUT") /* HTTP (RFC 2616) */ + || !strcmp(method, "DELETE") /* HTTP (RFC 2616) */ + || !strcmp(method, "OPTIONS") /* HTTP (RFC 2616) */ + /* TRACE method (RFC 2616) is not supported for security reasons */ + || !strcmp(method, "CONNECT") /* HTTP (RFC 2616) */ + + || !strcmp(method, "PROPFIND") /* WEBDAV (RFC 2518) */ + || !strcmp(method, "MKCOL") /* WEBDAV (RFC 2518) */ + + /* Unsupported WEBDAV Methods: */ + /* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */ + /* + 11 methods from RFC 3253 */ + /* ORDERPATCH (RFC 3648) */ + /* ACL (RFC 3744) */ + /* SEARCH (RFC 5323) */ + /* + MicroSoft extensions + * https://msdn.microsoft.com/en-us/library/aa142917.aspx */ + + /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */ + || !strcmp(method, "PATCH"); /* PATCH method (RFC 5789) */ +} + + +/* Parse HTTP request, fill in mg_request_info structure. + * This function modifies the buffer by NUL-terminating + * HTTP request components, header names and header values. */ +static int +parse_http_message(char *buf, int len, struct mg_request_info *ri) +{ + int is_request, request_length; + + if (!ri) { + return 0; + } + + request_length = get_request_len(buf, len); + + if (request_length > 0) { + /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr, + * remote_port */ + ri->remote_user = ri->request_method = ri->request_uri = + ri->http_version = NULL; + ri->num_headers = 0; + + buf[request_length - 1] = '\0'; + + /* RFC says that all initial whitespaces should be ingored */ + while (*buf != '\0' && isspace(*(unsigned char *)buf)) { + buf++; + } + ri->request_method = skip(&buf, " "); + ri->request_uri = skip(&buf, " "); + ri->http_version = skip(&buf, "\r\n"); + + /* HTTP message could be either HTTP request or HTTP response, e.g. + * "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." */ + is_request = is_valid_http_method(ri->request_method); + if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) + || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { + request_length = -1; + } else { + if (is_request) { + ri->http_version += 5; + } + parse_http_headers(&buf, ri); + } + } + return request_length; +} + + +/* Keep reading the input (either opened file descriptor fd, or socket sock, + * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the + * buffer (which marks the end of HTTP request). Buffer buf may already + * have some data. The length of the data is stored in nread. + * Upon every read operation, increase nread by the number of bytes read. */ +static int +read_request(FILE *fp, + struct mg_connection *conn, + char *buf, + int bufsiz, + int *nread) +{ + int request_len, n = 0; + struct timespec last_action_time; + double request_timeout; + + if (!conn) { + return 0; + } + + memset(&last_action_time, 0, sizeof(last_action_time)); + + if (conn->ctx->config[REQUEST_TIMEOUT]) { + /* value of request_timeout is in seconds, config in milliseconds */ + request_timeout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; + } else { + request_timeout = -1.0; + } + + request_len = get_request_len(buf, *nread); + + /* first time reading from this connection */ + clock_gettime(CLOCK_MONOTONIC, &last_action_time); + + while ( + (conn->ctx->stop_flag == 0) && (*nread < bufsiz) && (request_len == 0) + && ((mg_difftimespec(&last_action_time, &(conn->req_time)) + <= request_timeout) || (request_timeout < 0)) + && ((n = pull(fp, conn, buf + *nread, bufsiz - *nread, request_timeout)) + > 0)) { + *nread += n; + /* assert(*nread <= bufsiz); */ + if (*nread > bufsiz) { + return -2; + } + request_len = get_request_len(buf, *nread); + if (request_timeout > 0.0) { + clock_gettime(CLOCK_MONOTONIC, &last_action_time); + } + } + + return (request_len <= 0 && n <= 0) ? -1 : request_len; +} + +#if !defined(NO_FILES) +/* For given directory path, substitute it to valid index file. + * Return 1 if index file has been found, 0 if not found. + * If the file is found, it's stats is returned in stp. */ +static int +substitute_index_file(struct mg_connection *conn, + char *path, + size_t path_len, + struct file *filep) +{ + if (conn && conn->ctx) { + const char *list = conn->ctx->config[INDEX_FILES]; + struct file file = STRUCT_FILE_INITIALIZER; + struct vec filename_vec; + size_t n = strlen(path); + int found = 0; + + /* The 'path' given to us points to the directory. Remove all trailing + * directory separator characters from the end of the path, and + * then append single directory separator character. */ + while (n > 0 && path[n - 1] == '/') { + n--; + } + path[n] = '/'; + + /* Traverse index files list. For each entry, append it to the given + * path and see if the file exists. If it exists, break the loop */ + while ((list = next_option(list, &filename_vec, NULL)) != NULL) { + /* Ignore too long entries that may overflow path buffer */ + if (filename_vec.len > path_len - (n + 2)) { + continue; + } + + /* Prepare full path to the index file */ + mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); + + /* Does it exist? */ + if (mg_stat(conn, path, &file)) { + /* Yes it does, break the loop */ + *filep = file; + found = 1; + break; + } + } + + /* If no index file exists, restore directory path */ + if (!found) { + path[n] = '\0'; + } + + return found; + } + return 0; +} +#endif + + +#if !defined(NO_CACHING) +/* Return True if we should reply 304 Not Modified. */ +static int +is_not_modified(const struct mg_connection *conn, const struct file *filep) +{ + char etag[64]; + const char *ims = mg_get_header(conn, "If-Modified-Since"); + const char *inm = mg_get_header(conn, "If-None-Match"); + construct_etag(etag, sizeof(etag), filep); + if (!filep) { + return 0; + } + return (inm != NULL && !mg_strcasecmp(etag, inm)) + || (ims != NULL && (filep->last_modified <= parse_date_string(ims))); +} +#endif /* !NO_CACHING */ + + +#if !defined(NO_CGI) || !defined(NO_FILES) +static int +forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl) +{ + const char *expect, *body; + char buf[MG_BUF_LEN]; + int to_read, nread, success = 0; + int64_t buffered_len; + double timeout = -1.0; + + if (!conn) { + return 0; + } + if (conn->ctx->config[REQUEST_TIMEOUT]) { + timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; + } + + expect = mg_get_header(conn, "Expect"); + /* assert(fp != NULL); */ + if (!fp) { + send_http_error(conn, 500, "%s", "Error: NULL File"); + return 0; + } + + if (conn->content_len == -1 && !conn->is_chunked) { + /* Content length is not specified by the client. */ + send_http_error(conn, + 411, + "%s", + "Error: Client did not specify content length"); + } else if ((expect != NULL) + && (mg_strcasecmp(expect, "100-continue") != 0)) { + /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */ + send_http_error(conn, + 417, + "Error: Can not fulfill expectation %s", + expect); + } else { + if (expect != NULL) { + (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); + conn->status_code = 100; + } else { + conn->status_code = 200; + } + + buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len + - conn->consumed_content; + + /* assert(buffered_len >= 0); */ + /* assert(conn->consumed_content == 0); */ + + if ((buffered_len < 0) || (conn->consumed_content != 0)) { + send_http_error(conn, 500, "%s", "Error: Size mismatch"); + return 0; + } + + if (buffered_len > 0) { + if ((int64_t)buffered_len > conn->content_len) { + buffered_len = (int)conn->content_len; + } + body = conn->buf + conn->request_len + conn->consumed_content; + push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len); + conn->consumed_content += buffered_len; + } + + nread = 0; + while (conn->consumed_content < conn->content_len) { + to_read = sizeof(buf); + if ((int64_t)to_read > conn->content_len - conn->consumed_content) { + to_read = (int)(conn->content_len - conn->consumed_content); + } + nread = pull(NULL, conn, buf, to_read, timeout); + if (nread <= 0 + || push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) { + break; + } + conn->consumed_content += nread; + } + + if (conn->consumed_content == conn->content_len) { + success = (nread >= 0); + } + + /* Each error code path in this function must send an error */ + if (!success) { + /* NOTE: Maybe some data has already been sent. */ + /* TODO (low): If some data has been sent, a correct error + * reply can no longer be sent, so just close the connection */ + send_http_error(conn, 500, "%s", ""); + } + } + + return success; +} +#endif + +#if !defined(NO_CGI) +/* This structure helps to create an environment for the spawned CGI program. + * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, + * last element must be NULL. + * However, on Windows there is a requirement that all these VARIABLE=VALUE\0 + * strings must reside in a contiguous buffer. The end of the buffer is + * marked by two '\0' characters. + * We satisfy both worlds: we create an envp array (which is vars), all + * entries are actually pointers inside buf. */ +struct cgi_environment { + struct mg_connection *conn; + /* Data block */ + char *buf; /* Environment buffer */ + size_t buflen; /* Space available in buf */ + size_t bufused; /* Space taken in buf */ + /* Index block */ + char **var; /* char **envp */ + size_t varlen; /* Number of variables available in var */ + size_t varused; /* Number of variables stored in var */ +}; + + +static void addenv(struct cgi_environment *env, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(2, 3); + +/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective + * pointer into the vars array. Assumes env != NULL and fmt != NULL. */ +static void +addenv(struct cgi_environment *env, const char *fmt, ...) +{ + size_t n, space; + int truncated; + char *added; + va_list ap; + + /* Calculate how much space is left in the buffer */ + space = (env->buflen - env->bufused); + + /* Calculate an estimate for the required space */ + n = strlen(fmt) + 2 + 128; + + do { + if (space <= n) { + /* Allocate new buffer */ + n = env->buflen + CGI_ENVIRONMENT_SIZE; + added = (char *)mg_realloc(env->buf, n); + if (!added) { + /* Out of memory */ + mg_cry(env->conn, + "%s: Cannot allocate memory for CGI variable [%s]", + __func__, + fmt); + return; + } + env->buf = added; + env->buflen = n; + space = (env->buflen - env->bufused); + } + + /* Make a pointer to the free space int the buffer */ + added = env->buf + env->bufused; + + /* Copy VARIABLE=VALUE\0 string into the free space */ + va_start(ap, fmt); + mg_vsnprintf(env->conn, &truncated, added, (size_t)space, fmt, ap); + va_end(ap); + + /* Do not add truncated strings to the environment */ + if (truncated) { + /* Reallocate the buffer */ + space = 0; + n = 1; + } + } while (truncated); + + /* Calculate number of bytes added to the environment */ + n = strlen(added) + 1; + env->bufused += n; + + /* Now update the variable index */ + space = (env->varlen - env->varused); + if (space < 2) { + mg_cry(env->conn, + "%s: Cannot register CGI variable [%s]", + __func__, + fmt); + return; + } + + /* Append a pointer to the added string into the envp array */ + env->var[env->varused] = added; + env->varused++; +} + + +static void +prepare_cgi_environment(struct mg_connection *conn, + const char *prog, + struct cgi_environment *env) +{ + const char *s; + struct vec var_vec; + char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128]; + int i, truncated; + + if (conn == NULL || prog == NULL || env == NULL) { + return; + } + + env->conn = conn; + env->buflen = CGI_ENVIRONMENT_SIZE; + env->bufused = 0; + env->buf = (char *)mg_malloc(env->buflen); + env->varlen = MAX_CGI_ENVIR_VARS; + env->varused = 0; + env->var = (char **)mg_malloc(env->buflen * sizeof(char *)); + + addenv(env, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]); + addenv(env, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); + addenv(env, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); + addenv(env, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version()); + + /* Prepare the environment block */ + addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1"); + addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1"); + addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */ + +#if defined(USE_IPV6) + if (conn->client.lsa.sa.sa_family == AF_INET6) { + addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port)); + } else +#endif + { + addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port)); + } + + sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); + addenv(env, "REMOTE_ADDR=%s", src_addr); + + addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method); + addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port); + + addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri); + addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri); + + /* SCRIPT_NAME */ + addenv(env, + "SCRIPT_NAME=%.*s", + (int)strlen(conn->request_info.local_uri) + - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)), + conn->request_info.local_uri); + + addenv(env, "SCRIPT_FILENAME=%s", prog); + if (conn->path_info == NULL) { + addenv(env, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT]); + } else { + addenv(env, + "PATH_TRANSLATED=%s%s", + conn->ctx->config[DOCUMENT_ROOT], + conn->path_info); + } + + addenv(env, "HTTPS=%s", conn->ssl == NULL ? "off" : "on"); + + if ((s = mg_get_header(conn, "Content-Type")) != NULL) { + addenv(env, "CONTENT_TYPE=%s", s); + } + if (conn->request_info.query_string != NULL) { + addenv(env, "QUERY_STRING=%s", conn->request_info.query_string); + } + if ((s = mg_get_header(conn, "Content-Length")) != NULL) { + addenv(env, "CONTENT_LENGTH=%s", s); + } + if ((s = getenv("PATH")) != NULL) { + addenv(env, "PATH=%s", s); + } + if (conn->path_info != NULL) { + addenv(env, "PATH_INFO=%s", conn->path_info); + } + + if (conn->status_code > 0) { + /* CGI error handler should show the status code */ + addenv(env, "STATUS=%d", conn->status_code); + } + +#if defined(_WIN32) + if ((s = getenv("COMSPEC")) != NULL) { + addenv(env, "COMSPEC=%s", s); + } + if ((s = getenv("SYSTEMROOT")) != NULL) { + addenv(env, "SYSTEMROOT=%s", s); + } + if ((s = getenv("SystemDrive")) != NULL) { + addenv(env, "SystemDrive=%s", s); + } + if ((s = getenv("ProgramFiles")) != NULL) { + addenv(env, "ProgramFiles=%s", s); + } + if ((s = getenv("ProgramFiles(x86)")) != NULL) { + addenv(env, "ProgramFiles(x86)=%s", s); + } +#else + if ((s = getenv("LD_LIBRARY_PATH")) != NULL) { + addenv(env, "LD_LIBRARY_PATH=%s", s); + } +#endif /* _WIN32 */ + + if ((s = getenv("PERLLIB")) != NULL) { + addenv(env, "PERLLIB=%s", s); + } + + if (conn->request_info.remote_user != NULL) { + addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user); + addenv(env, "%s", "AUTH_TYPE=Digest"); + } + + /* Add all headers as HTTP_* variables */ + for (i = 0; i < conn->request_info.num_headers; i++) { + + (void)mg_snprintf(conn, + &truncated, + http_var_name, + sizeof(http_var_name), + "HTTP_%s", + conn->request_info.http_headers[i].name); + + if (truncated) { + mg_cry(conn, + "%s: HTTP header variable too long [%s]", + __func__, + conn->request_info.http_headers[i].name); + continue; + } + + /* Convert variable name into uppercase, and change - to _ */ + for (p = http_var_name; *p != '\0'; p++) { + if (*p == '-') { + *p = '_'; + } + *p = (char)toupper(*(unsigned char *)p); + } + + addenv(env, + "%s=%s", + http_var_name, + conn->request_info.http_headers[i].value); + } + + /* Add user-specified variables */ + s = conn->ctx->config[CGI_ENVIRONMENT]; + while ((s = next_option(s, &var_vec, NULL)) != NULL) { + addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr); + } + + env->var[env->varused] = NULL; + env->buf[env->bufused] = '\0'; +} + + +static void +handle_cgi_request(struct mg_connection *conn, const char *prog) +{ + char *buf; + size_t buflen; + int headers_len, data_len, i, truncated; + int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1}; + const char *status, *status_text, *connection_state; + char *pbuf, dir[PATH_MAX], *p; + struct mg_request_info ri; + struct cgi_environment blk; + FILE *in = NULL, *out = NULL, *err = NULL; + struct file fout = STRUCT_FILE_INITIALIZER; + pid_t pid = (pid_t)-1; + + if (conn == NULL) { + return; + } + + buf = NULL; + buflen = 16384; + prepare_cgi_environment(conn, prog, &blk); + + /* CGI must be executed in its own directory. 'dir' must point to the + * directory containing executable program, 'p' must point to the + * executable program name relative to 'dir'. */ + (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog); + + if (truncated) { + mg_cry(conn, "Error: CGI program \"%s\": Path too long", prog); + send_http_error(conn, 500, "Error: %s", "CGI path too long"); + goto done; + } + + if ((p = strrchr(dir, '/')) != NULL) { + *p++ = '\0'; + } else { + dir[0] = '.', dir[1] = '\0'; + p = (char *)prog; + } + + if (pipe(fdin) != 0 || pipe(fdout) != 0 || pipe(fderr) != 0) { + status = strerror(ERRNO); + mg_cry(conn, + "Error: CGI program \"%s\": Can not create CGI pipes: %s", + prog, + status); + send_http_error(conn, 500, "Error: Cannot create CGI pipe: %s", status); + goto done; + } + + pid = spawn_process(conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir); + + if (pid == (pid_t)-1) { + status = strerror(ERRNO); + mg_cry(conn, + "Error: CGI program \"%s\": Can not spawn CGI process: %s", + prog, + status); + send_http_error(conn, + 500, + "Error: Cannot spawn CGI process [%s]: %s", + prog, + status); + goto done; + } + + /* Make sure child closes all pipe descriptors. It must dup them to 0,1 */ + set_close_on_exec((SOCKET)fdin[0], conn); /* stdin read */ + set_close_on_exec((SOCKET)fdout[1], conn); /* stdout write */ + set_close_on_exec((SOCKET)fderr[1], conn); /* stderr write */ + set_close_on_exec((SOCKET)fdin[1], conn); /* stdin write */ + set_close_on_exec((SOCKET)fdout[0], conn); /* stdout read */ + set_close_on_exec((SOCKET)fderr[0], conn); /* stderr read */ + + /* Parent closes only one side of the pipes. + * If we don't mark them as closed, close() attempt before + * return from this function throws an exception on Windows. + * Windows does not like when closed descriptor is closed again. */ + (void)close(fdin[0]); + (void)close(fdout[1]); + (void)close(fderr[1]); + fdin[0] = fdout[1] = fderr[1] = -1; + + if ((in = fdopen(fdin[1], "wb")) == NULL) { + status = strerror(ERRNO); + mg_cry(conn, + "Error: CGI program \"%s\": Can not open stdin: %s", + prog, + status); + send_http_error(conn, + 500, + "Error: CGI can not open fdin\nfopen: %s", + status); + goto done; + } + + if ((out = fdopen(fdout[0], "rb")) == NULL) { + status = strerror(ERRNO); + mg_cry(conn, + "Error: CGI program \"%s\": Can not open stdout: %s", + prog, + status); + send_http_error(conn, + 500, + "Error: CGI can not open fdout\nfopen: %s", + status); + goto done; + } + + if ((err = fdopen(fderr[0], "rb")) == NULL) { + status = strerror(ERRNO); + mg_cry(conn, + "Error: CGI program \"%s\": Can not open stderr: %s", + prog, + status); + send_http_error(conn, + 500, + "Error: CGI can not open fdout\nfopen: %s", + status); + goto done; + } + + setbuf(in, NULL); + setbuf(out, NULL); + setbuf(err, NULL); + fout.fp = out; + + if ((conn->request_info.content_length > 0) || conn->is_chunked) { + /* This is a POST/PUT request, or another request with body data. */ + if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) { + /* Error sending the body data */ + mg_cry(conn, + "Error: CGI program \"%s\": Forward body data failed", + prog); + goto done; + } + } + + /* Close so child gets an EOF. */ + fclose(in); + in = NULL; + fdin[1] = -1; + + /* Now read CGI reply into a buffer. We need to set correct + * status code, thus we need to see all HTTP headers first. + * Do not send anything back to client, until we buffer in all + * HTTP headers. */ + data_len = 0; + buf = (char *)mg_malloc(buflen); + if (buf == NULL) { + send_http_error(conn, + 500, + "Error: Not enough memory for CGI buffer (%u bytes)", + (unsigned int)buflen); + mg_cry(conn, + "Error: CGI program \"%s\": Not enough memory for buffer (%u " + "bytes)", + prog, + (unsigned int)buflen); + goto done; + } + headers_len = read_request(out, conn, buf, (int)buflen, &data_len); + if (headers_len <= 0) { + + /* Could not parse the CGI response. Check if some error message on + * stderr. */ + i = pull_all(err, conn, buf, (int)buflen); + if (i > 0) { + mg_cry(conn, + "Error: CGI program \"%s\" sent error " + "message: [%.*s]", + prog, + i, + buf); + send_http_error(conn, + 500, + "Error: CGI program \"%s\" sent error " + "message: [%.*s]", + prog, + i, + buf); + } else { + mg_cry(conn, + "Error: CGI program sent malformed or too big " + "(>%u bytes) HTTP headers: [%.*s]", + (unsigned)buflen, + data_len, + buf); + + send_http_error(conn, + 500, + "Error: CGI program sent malformed or too big " + "(>%u bytes) HTTP headers: [%.*s]", + (unsigned)buflen, + data_len, + buf); + } + + goto done; + } + pbuf = buf; + buf[headers_len - 1] = '\0'; + parse_http_headers(&pbuf, &ri); + + /* Make up and send the status line */ + status_text = "OK"; + if ((status = get_header(&ri, "Status")) != NULL) { + conn->status_code = atoi(status); + status_text = status; + while (isdigit(*(const unsigned char *)status_text) + || *status_text == ' ') { + status_text++; + } + } else if (get_header(&ri, "Location") != NULL) { + conn->status_code = 302; + } else { + conn->status_code = 200; + } + connection_state = get_header(&ri, "Connection"); + if (!header_has_option(connection_state, "keep-alive")) { + conn->must_close = 1; + } + (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text); + + /* Send headers */ + for (i = 0; i < ri.num_headers; i++) { + mg_printf(conn, + "%s: %s\r\n", + ri.http_headers[i].name, + ri.http_headers[i].value); + } + mg_write(conn, "\r\n", 2); + + /* Send chunk of data that may have been read after the headers */ + conn->num_bytes_sent += + mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len)); + + /* Read the rest of CGI output and send to the client */ + send_file_data(conn, &fout, 0, INT64_MAX); + +done: + mg_free(blk.var); + mg_free(blk.buf); + + if (pid != (pid_t)-1) { + kill(pid, SIGKILL); +#if !defined(_WIN32) + { + int st; + while (waitpid(pid, &st, 0) != -1) + ; /* clean zombies */ + } +#endif + } + if (fdin[0] != -1) { + close(fdin[0]); + } + if (fdout[1] != -1) { + close(fdout[1]); + } + + if (in != NULL) { + fclose(in); + } else if (fdin[1] != -1) { + close(fdin[1]); + } + + if (out != NULL) { + fclose(out); + } else if (fdout[0] != -1) { + close(fdout[0]); + } + + if (err != NULL) { + fclose(err); + } else if (fderr[0] != -1) { + close(fderr[0]); + } + + if (buf != NULL) { + mg_free(buf); + } +} +#endif /* !NO_CGI */ + + +#if !defined(NO_FILES) +static void +mkcol(struct mg_connection *conn, const char *path) +{ + int rc, body_len; + struct de de; + char date[64]; + time_t curtime = time(NULL); + + if (conn == NULL) { + return; + } + + /* TODO (mid): Check the send_http_error situations in this function */ + + memset(&de.file, 0, sizeof(de.file)); + if (!mg_stat(conn, path, &de.file)) { + mg_cry(conn, + "%s: mg_stat(%s) failed: %s", + __func__, + path, + strerror(ERRNO)); + } + + if (de.file.last_modified) { + /* TODO (high): This check does not seem to make any sense ! */ + send_http_error( + conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO)); + return; + } + + body_len = conn->data_len - conn->request_len; + if (body_len > 0) { + send_http_error( + conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO)); + return; + } + + rc = mg_mkdir(conn, path, 0755); + + if (rc == 0) { + conn->status_code = 201; + gmt_time_string(date, sizeof(date), &curtime); + mg_printf(conn, + "HTTP/1.1 %d Created\r\n" + "Date: %s\r\n", + conn->status_code, + date); + send_static_cache_header(conn); + mg_printf(conn, + "Content-Length: 0\r\n" + "Connection: %s\r\n\r\n", + suggest_connection_header(conn)); + } else if (rc == -1) { + if (errno == EEXIST) { + send_http_error( + conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO)); + } else if (errno == EACCES) { + send_http_error( + conn, 403, "Error: mkcol(%s): %s", path, strerror(ERRNO)); + } else if (errno == ENOENT) { + send_http_error( + conn, 409, "Error: mkcol(%s): %s", path, strerror(ERRNO)); + } else { + send_http_error(conn, 500, "fopen(%s): %s", path, strerror(ERRNO)); + } + } +} + + +static void +put_file(struct mg_connection *conn, const char *path) +{ + struct file file = STRUCT_FILE_INITIALIZER; + const char *range; + int64_t r1, r2; + int rc; + char date[64]; + time_t curtime = time(NULL); + + if (conn == NULL) { + return; + } + + if (mg_stat(conn, path, &file)) { + /* File already exists */ + conn->status_code = 200; + + if (file.is_directory) { + /* This is an already existing directory, + * so there is nothing to do for the server. */ + rc = 0; + + } else { + /* File exists and is not a directory. */ + /* Can it be replaced? */ + + if (file.membuf != NULL) { + /* This is an "in-memory" file, that can not be replaced */ + send_http_error( + conn, + 405, + "Error: Put not possible\nReplacing %s is not supported", + path); + return; + } + + /* Check if the server may write this file */ + if (access(path, W_OK) == 0) { + /* Access granted */ + conn->status_code = 200; + rc = 1; + } else { + send_http_error( + conn, + 403, + "Error: Put not possible\nReplacing %s is not allowed", + path); + return; + } + } + } else { + /* File should be created */ + conn->status_code = 201; + rc = put_dir(conn, path); + } + + if (rc == 0) { + /* put_dir returns 0 if path is a directory */ + gmt_time_string(date, sizeof(date), &curtime); + mg_printf(conn, + "HTTP/1.1 %d %s\r\n", + conn->status_code, + mg_get_response_code_text(NULL, conn->status_code)); + send_no_cache_header(conn); + mg_printf(conn, + "Date: %s\r\n" + "Content-Length: 0\r\n" + "Connection: %s\r\n\r\n", + date, + suggest_connection_header(conn)); + + /* Request to create a directory has been fulfilled successfully. + * No need to put a file. */ + return; + } + + if (rc == -1) { + /* put_dir returns -1 if the path is too long */ + send_http_error(conn, + 414, + "Error: Path too long\nput_dir(%s): %s", + path, + strerror(ERRNO)); + return; + } + + if (rc == -2) { + /* put_dir returns -2 if the directory can not be created */ + send_http_error(conn, + 500, + "Error: Can not create directory\nput_dir(%s): %s", + path, + strerror(ERRNO)); + return; + } + + /* A file should be created or overwritten. */ + if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) { + mg_fclose(&file); + send_http_error(conn, + 500, + "Error: Can not create file\nfopen(%s): %s", + path, + strerror(ERRNO)); + return; + } + + fclose_on_exec(&file, conn); + range = mg_get_header(conn, "Content-Range"); + r1 = r2 = 0; + if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { + conn->status_code = 206; /* Partial content */ + fseeko(file.fp, r1, SEEK_SET); + } + + if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { + /* forward_body_data failed. + * The error code has already been sent to the client, + * and conn->status_code is already set. */ + mg_fclose(&file); + return; + } + + gmt_time_string(date, sizeof(date), &curtime); + mg_printf(conn, + "HTTP/1.1 %d %s\r\n", + conn->status_code, + mg_get_response_code_text(NULL, conn->status_code)); + send_no_cache_header(conn); + mg_printf(conn, + "Date: %s\r\n" + "Content-Length: 0\r\n" + "Connection: %s\r\n\r\n", + date, + suggest_connection_header(conn)); + + mg_fclose(&file); +} + + +static void +delete_file(struct mg_connection *conn, const char *path) +{ + struct de de; + memset(&de.file, 0, sizeof(de.file)); + if (!mg_stat(conn, path, &de.file)) { + /* mg_stat returns 0 if the file does not exist */ + send_http_error(conn, + 404, + "Error: Cannot delete file\nFile %s not found", + path); + return; + } + + if (de.file.membuf != NULL) { + /* the file is cached in memory */ + send_http_error( + conn, + 405, + "Error: Delete not possible\nDeleting %s is not supported", + path); + return; + } + + if (de.file.is_directory) { + if (remove_directory(conn, path)) { + /* Delete is successful: Return 204 without content. */ + send_http_error(conn, 204, "%s", ""); + } else { + /* Delete is not successful: Return 500 (Server error). */ + send_http_error(conn, 500, "Error: Could not delete %s", path); + } + return; + } + + /* This is an existing file (not a directory). + * Check if write permission is granted. */ + if (access(path, W_OK) != 0) { + /* File is read only */ + send_http_error( + conn, + 403, + "Error: Delete not possible\nDeleting %s is not allowed", + path); + return; + } + + /* Try to delete it. */ + if (mg_remove(conn, path) == 0) { + /* Delete was successful: Return 204 without content. */ + send_http_error(conn, 204, "%s", ""); + } else { + /* Delete not successful (file locked). */ + send_http_error(conn, + 423, + "Error: Cannot delete file\nremove(%s): %s", + path, + strerror(ERRNO)); + } +} +#endif /* !NO_FILES */ + + +static void +send_ssi_file(struct mg_connection *, const char *, struct file *, int); + + +static void +do_ssi_include(struct mg_connection *conn, + const char *ssi, + char *tag, + int include_level) +{ + char file_name[MG_BUF_LEN], path[512], *p; + struct file file = STRUCT_FILE_INITIALIZER; + size_t len; + int truncated = 0; + + if (conn == NULL) { + return; + } + + /* sscanf() is safe here, since send_ssi_file() also uses buffer + * of size MG_BUF_LEN to get the tag. So strlen(tag) is + * always < MG_BUF_LEN. */ + if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) { + /* File name is relative to the webserver root */ + file_name[511] = 0; + (void)mg_snprintf(conn, + &truncated, + path, + sizeof(path), + "%s/%s", + conn->ctx->config[DOCUMENT_ROOT], + file_name); + + } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) { + /* File name is relative to the webserver working directory + * or it is absolute system path */ + file_name[511] = 0; + (void) + mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name); + + } else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1 + || sscanf(tag, " \"%511[^\"]\"", file_name) == 1) { + /* File name is relative to the currect document */ + file_name[511] = 0; + (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi); + + if (!truncated) { + if ((p = strrchr(path, '/')) != NULL) { + p[1] = '\0'; + } + len = strlen(path); + (void)mg_snprintf(conn, + &truncated, + path + len, + sizeof(path) - len, + "%s", + file_name); + } + + } else { + mg_cry(conn, "Bad SSI #include: [%s]", tag); + return; + } + + if (truncated) { + mg_cry(conn, "SSI #include path length overflow: [%s]", tag); + return; + } + + if (!mg_fopen(conn, path, "rb", &file)) { + mg_cry(conn, + "Cannot open SSI #include: [%s]: fopen(%s): %s", + tag, + path, + strerror(ERRNO)); + } else { + fclose_on_exec(&file, conn); + if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], + strlen(conn->ctx->config[SSI_EXTENSIONS]), + path) > 0) { + send_ssi_file(conn, path, &file, include_level + 1); + } else { + send_file_data(conn, &file, 0, INT64_MAX); + } + mg_fclose(&file); + } +} + + +#if !defined(NO_POPEN) +static void +do_ssi_exec(struct mg_connection *conn, char *tag) +{ + char cmd[1024] = ""; + struct file file = STRUCT_FILE_INITIALIZER; + + if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) { + mg_cry(conn, "Bad SSI #exec: [%s]", tag); + } else { + cmd[1023] = 0; + if ((file.fp = popen(cmd, "r")) == NULL) { + mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); + } else { + send_file_data(conn, &file, 0, INT64_MAX); + pclose(file.fp); + } + } +} +#endif /* !NO_POPEN */ + + +static int +mg_fgetc(struct file *filep, int offset) +{ + if (filep == NULL) { + return EOF; + } + if (filep->membuf != NULL && offset >= 0 + && ((unsigned int)(offset)) < filep->size) { + return ((const unsigned char *)filep->membuf)[offset]; + } else if (filep->fp != NULL) { + return fgetc(filep->fp); + } else { + return EOF; + } +} + + +static void +send_ssi_file(struct mg_connection *conn, + const char *path, + struct file *filep, + int include_level) +{ + char buf[MG_BUF_LEN]; + int ch, offset, len, in_ssi_tag; + + if (include_level > 10) { + mg_cry(conn, "SSI #include level is too deep (%s)", path); + return; + } + + in_ssi_tag = len = offset = 0; + while ((ch = mg_fgetc(filep, offset)) != EOF) { + if (in_ssi_tag && ch == '>') { + in_ssi_tag = 0; + buf[len++] = (char)ch; + buf[len] = '\0'; + /* assert(len <= (int) sizeof(buf)); */ + if (len > (int)sizeof(buf)) { + break; + } + if (len < 6 || memcmp(buf, " + + + + Ventoy2Disk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+
+ + +
+
+
+ 设备 +
+ +
+ +
+ +
+ +
+
+ +
+
+
+

安装包内 Ventoy 版本

+
+
+ + + +
+
+ +
+
+

设备内部 Ventoy 版本

+
+
+ + + +
+
+
+ +
+ 状态 - 准备就绪 +
+
+
+
+
+ +
+ + +
+
+ + + + + + + +
+
+ +
+ + +
+ + + + + diff --git a/LinuxGUI/WebUI/static/AdminLTE/css/AdminLTE.min.css b/LinuxGUI/WebUI/static/AdminLTE/css/AdminLTE.min.css new file mode 100644 index 00000000..fd35437f --- /dev/null +++ b/LinuxGUI/WebUI/static/AdminLTE/css/AdminLTE.min.css @@ -0,0 +1,8 @@ +/**@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic); **/ +/*! + * AdminLTE v2.3.0 + * Author: Almsaeed Studio + * Website: Almsaeed Studio + * License: Open source - MIT + * Please visit http://opensource.org/licenses/MIT for more information +!*/html,body{/*min-height:100%*/}.layout-boxed html,.layout-boxed body{height:100%}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:'Arial','Microsoft YaHei','','',sans-serif;font-weight:400;overflow-x:hidden;overflow-y:auto}.wrapper{min-height:100%;position:static;overflow:hidden}.wrapper:before,.wrapper:after{content:" ";display:table}.wrapper:after{clear:both}.layout-boxed .wrapper{max-width:1250px;margin:0 auto;min-height:100%;box-shadow:0 0 8px rgba(0,0,0,0.5);position:relative}.layout-boxed{background:url('../img/boxed-bg.jpg') repeat fixed}.content-wrapper,.right-side,.main-footer{-webkit-transition:-webkit-transform .3s ease-in-out,margin .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,margin .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,margin .3s ease-in-out;transition:transform .3s ease-in-out,margin .3s ease-in-out;margin-left:230px;z-index:820}.layout-top-nav .content-wrapper,.layout-top-nav .right-side,.layout-top-nav .main-footer{margin-left:0}@media (max-width:767px){.content-wrapper,.right-side,.main-footer{margin-left:0}}@media (min-width:768px){.sidebar-collapse .content-wrapper,.sidebar-collapse .right-side,.sidebar-collapse .main-footer{margin-left:0}}@media (max-width:767px){.sidebar-open .content-wrapper,.sidebar-open .right-side,.sidebar-open .main-footer{-webkit-transform:translate(230px, 0);-ms-transform:translate(230px, 0);-o-transform:translate(230px, 0);transform:translate(230px, 0)}}.content-wrapper,.right-side{min-height:100%;background-color:#ecf0f5;z-index:800}.main-footer{background:#fff;padding:15px;color:#444;border-top:1px solid #d2d6de}.fixed .main-header,.fixed .main-sidebar,.fixed .left-side{position:fixed}.fixed .main-header{top:0;right:0;left:0}.fixed .content-wrapper,.fixed .right-side{padding-top:50px}@media (max-width:767px){.fixed .content-wrapper,.fixed .right-side{padding-top:100px}}.fixed.layout-boxed .wrapper{max-width:100%}body.hold-transition .content-wrapper,body.hold-transition .right-side,body.hold-transition .main-footer,body.hold-transition .main-sidebar,body.hold-transition .left-side,body.hold-transition .main-header>.navbar,body.hold-transition .main-header .logo{-webkit-transition:none;-o-transition:none;transition:none}.content{min-height:250px;padding:15px;margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:'Arial','Microsoft YaHei','','',sans-serif}a{color:#3c8dbc}a:hover,a:active,a:focus{outline:none;text-decoration:none;color:#72afd2}.page-header{margin:10px 0 20px 0;font-size:22px}.page-header>small{color:#666;display:block;margin-top:5px}.main-header{position:relative;max-height:100px;z-index:1030}.main-header>.navbar{-webkit-transition:margin-left .3s ease-in-out;-o-transition:margin-left .3s ease-in-out;transition:margin-left .3s ease-in-out;margin-bottom:0;margin-left:230px;border:none;min-height:50px;border-radius:0}.layout-top-nav .main-header>.navbar{margin-left:0}.main-header #navbar-search-input.form-control{background:rgba(255,255,255,0.2);border-color:transparent}.main-header #navbar-search-input.form-control:focus,.main-header #navbar-search-input.form-control:active{border-color:rgba(0,0,0,0.1);background:rgba(255,255,255,0.9)}.main-header #navbar-search-input.form-control::-moz-placeholder{color:#ccc;opacity:1}.main-header #navbar-search-input.form-control:-ms-input-placeholder{color:#ccc}.main-header #navbar-search-input.form-control::-webkit-input-placeholder{color:#ccc}.main-header .navbar-custom-menu,.main-header .navbar-right{float:right}@media (max-width:991px){.main-header .navbar-custom-menu a,.main-header .navbar-right a{color:inherit;background:transparent}}@media (max-width:767px){.main-header .navbar-right{float:none}.navbar-collapse .main-header .navbar-right{margin:7.5px -15px}.main-header .navbar-right>li{color:inherit;border:0}}.main-header .sidebar-toggle{float:left;background-color:transparent;background-image:none;padding:15px 15px;font-family:fontAwesome}.main-header .sidebar-toggle:before{content:"\f0c9"}.main-header .sidebar-toggle:hover{color:#fff}.main-header .sidebar-toggle:focus,.main-header .sidebar-toggle:active{background:transparent}.main-header .sidebar-toggle .icon-bar{display:none}.main-header .navbar .nav>li.user>a>.fa,.main-header .navbar .nav>li.user>a>.glyphicon,.main-header .navbar .nav>li.user>a>.ion{margin-right:5px}.main-header .navbar .nav>li>a>.label{position:absolute;top:9px;right:7px;text-align:center;font-size:9px;padding:2px 3px;line-height:.9}.main-header .logo{-webkit-transition:width .3s ease-in-out;-o-transition:width .3s ease-in-out;transition:width .3s ease-in-out;display:block;float:left;height:50px;font-size:20px;line-height:50px;text-align:center;width:230px;font-family:'Arial','Microsoft YaHei','','',sans-serif;padding:0 15px;font-weight:300;overflow:hidden}.main-header .logo .logo-lg{display:block}.main-header .logo .logo-mini{display:none}.main-header .navbar-brand{color:#fff}.content-header{position:relative;padding:15px 15px 0 15px}.content-header>h1{margin:0;font-size:24px}.content-header>h1>small{font-size:15px;display:inline-block;padding-left:4px;font-weight:300}.content-header>.breadcrumb{float:right;background:transparent;margin-top:0;margin-bottom:0;font-size:12px;padding:7px 5px;position:absolute;top:15px;right:10px;border-radius:2px}.content-header>.breadcrumb>li>a{color:#444;text-decoration:none;display:inline-block}.content-header>.breadcrumb>li>a>.fa,.content-header>.breadcrumb>li>a>.glyphicon,.content-header>.breadcrumb>li>a>.ion{margin-right:5px}.content-header>.breadcrumb>li+li:before{content:'>\00a0'}@media (max-width:991px){.content-header>.breadcrumb{position:relative;margin-top:5px;top:0;right:0;float:none;background:#d2d6de;padding-left:10px}.content-header>.breadcrumb li:before{color:#97a0b3}}.navbar-toggle{color:#fff;border:0;margin:0;padding:15px 15px}@media (max-width:991px){.navbar-custom-menu .navbar-nav>li{float:left}.navbar-custom-menu .navbar-nav{margin:0;float:left}.navbar-custom-menu .navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px}}@media (max-width:767px){.main-header{position:relative}.main-header .logo,.main-header .navbar{width:100%;float:none}.main-header .navbar{margin:0}.main-header .navbar-custom-menu{float:right}}@media (max-width:991px){.navbar-collapse.pull-left{float:none!important}.navbar-collapse.pull-left+.navbar-custom-menu{display:block;position:absolute;top:0;right:40px}}.main-sidebar,.left-side{position:absolute;top:0;left:0;padding-top:50px;min-height:100%;width:230px;z-index:810;-webkit-transition:-webkit-transform .3s ease-in-out,width .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,width .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,width .3s ease-in-out;transition:transform .3s ease-in-out,width .3s ease-in-out}@media (max-width:767px){.main-sidebar,.left-side{padding-top:100px}}@media (max-width:767px){.main-sidebar,.left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (min-width:768px){.sidebar-collapse .main-sidebar,.sidebar-collapse .left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (max-width:767px){.sidebar-open .main-sidebar,.sidebar-open .left-side{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}}.sidebar{padding-bottom:10px}.sidebar-form input:focus{border-color:transparent}.user-panel{position:relative;width:100%;padding:10px;overflow:hidden}.user-panel:before,.user-panel:after{content:" ";display:table}.user-panel:after{clear:both}.user-panel>.image>img{width:100%;max-width:45px;height:auto}.user-panel>.info{padding:5px 5px 5px 15px;line-height:1;position:absolute;left:55px}.user-panel>.info>p{font-weight:600;margin-bottom:9px}.user-panel>.info>a{text-decoration:none;padding-right:5px;margin-top:3px;font-size:11px}.user-panel>.info>a>.fa,.user-panel>.info>a>.ion,.user-panel>.info>a>.glyphicon{margin-right:3px}.sidebar-menu{list-style:none;margin:0;padding:0}.sidebar-menu>li{position:relative;margin:0;padding:0}.sidebar-menu>li>a{padding:12px 5px 12px 15px;display:block}.sidebar-menu>li>a>.fa,.sidebar-menu>li>a>.glyphicon,.sidebar-menu>li>a>.ion{width:20px}.sidebar-menu>li .label,.sidebar-menu>li .badge{margin-top:3px;margin-right:5px}.sidebar-menu li.header{padding:10px 25px 10px 15px;font-size:12px}.sidebar-menu li>a>.fa-angle-left{width:auto;height:auto;padding:0;margin-right:10px;margin-top:3px}.sidebar-menu li.active>a>.fa-angle-left{-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.sidebar-menu li.active>.treeview-menu{display:block}.sidebar-menu .treeview-menu{display:none;list-style:none;padding:0;margin:0;padding-left:5px}.sidebar-menu .treeview-menu .treeview-menu{padding-left:20px}.sidebar-menu .treeview-menu>li{margin:0}.sidebar-menu .treeview-menu>li>a{padding:5px 5px 5px 15px;display:block;font-size:14px}.sidebar-menu .treeview-menu>li>a>.fa,.sidebar-menu .treeview-menu>li>a>.glyphicon,.sidebar-menu .treeview-menu>li>a>.ion{width:20px}.sidebar-menu .treeview-menu>li>a>.fa-angle-left,.sidebar-menu .treeview-menu>li>a>.fa-angle-down{width:auto}@media (min-width:768px){.sidebar-mini.sidebar-collapse .content-wrapper,.sidebar-mini.sidebar-collapse .right-side,.sidebar-mini.sidebar-collapse .main-footer{margin-left:50px!important;z-index:840}.sidebar-mini.sidebar-collapse .main-sidebar{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);width:50px!important;z-index:850}.sidebar-mini.sidebar-collapse .sidebar-menu>li{position:relative}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a{margin-right:0}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span{border-top-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:not(.treeview)>a>span{border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{padding-top:5px;padding-bottom:5px;border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span:not(.pull-right),.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{display:block!important;position:absolute;width:180px;left:50px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span{top:0;margin-left:-3px;padding:12px 5px 12px 20px;background-color:inherit}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{top:44px;margin-left:0}.sidebar-mini.sidebar-collapse .main-sidebar .user-panel>.info,.sidebar-mini.sidebar-collapse .sidebar-form,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span,.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>.pull-right,.sidebar-mini.sidebar-collapse .sidebar-menu li.header{display:none!important;-webkit-transform:translateZ(0)}.sidebar-mini.sidebar-collapse .main-header .logo{width:50px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-mini{display:block;margin-left:-15px;margin-right:-15px;font-size:18px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-lg{display:none}.sidebar-mini.sidebar-collapse .main-header .navbar{margin-left:50px}}.sidebar-menu,.main-sidebar .user-panel,.sidebar-menu>li.header{white-space:nowrap;overflow:hidden}.sidebar-menu:hover{overflow:visible}.sidebar-form,.sidebar-menu>li.header{overflow:hidden;text-overflow:clip}.sidebar-menu li>a{position:relative}.sidebar-menu li>a>.pull-right{position:absolute;top:50%;right:10px;margin-top:-7px}.control-sidebar-bg{position:fixed;z-index:1000;bottom:0}.control-sidebar-bg,.control-sidebar{top:0;right:-230px;width:230px;-webkit-transition:right .3s ease-in-out;-o-transition:right .3s ease-in-out;transition:right .3s ease-in-out}.control-sidebar{position:absolute;padding-top:50px;z-index:1010}@media (max-width:768px){.control-sidebar{padding-top:100px}}.control-sidebar>.tab-content{padding:10px 15px}.control-sidebar.control-sidebar-open,.control-sidebar.control-sidebar-open+.control-sidebar-bg{right:0}.control-sidebar-open .control-sidebar-bg,.control-sidebar-open .control-sidebar{right:0}@media (min-width:768px){.control-sidebar-open .content-wrapper,.control-sidebar-open .right-side,.control-sidebar-open .main-footer{margin-right:230px}}.nav-tabs.control-sidebar-tabs>li:first-of-type>a,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:hover,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:focus{border-left-width:0}.nav-tabs.control-sidebar-tabs>li>a{border-radius:0}.nav-tabs.control-sidebar-tabs>li>a,.nav-tabs.control-sidebar-tabs>li>a:hover{border-top:none;border-right:none;border-left:1px solid transparent;border-bottom:1px solid transparent}.nav-tabs.control-sidebar-tabs>li>a .icon{font-size:16px}.nav-tabs.control-sidebar-tabs>li.active>a,.nav-tabs.control-sidebar-tabs>li.active>a:hover,.nav-tabs.control-sidebar-tabs>li.active>a:focus,.nav-tabs.control-sidebar-tabs>li.active>a:active{border-top:none;border-right:none;border-bottom:none}@media (max-width:768px){.nav-tabs.control-sidebar-tabs{display:table}.nav-tabs.control-sidebar-tabs>li{display:table-cell}}.control-sidebar-heading{font-weight:400;font-size:16px;padding:10px 0;margin-bottom:10px}.control-sidebar-subheading{display:block;font-weight:400;font-size:14px}.control-sidebar-menu{list-style:none;padding:0;margin:0 -15px}.control-sidebar-menu>li>a{display:block;padding:10px 15px}.control-sidebar-menu>li>a:before,.control-sidebar-menu>li>a:after{content:" ";display:table}.control-sidebar-menu>li>a:after{clear:both}.control-sidebar-menu>li>a>.control-sidebar-subheading{margin-top:0}.control-sidebar-menu .menu-icon{float:left;width:35px;height:35px;border-radius:50%;text-align:center;line-height:35px}.control-sidebar-menu .menu-info{margin-left:45px;margin-top:3px}.control-sidebar-menu .menu-info>.control-sidebar-subheading{margin:0}.control-sidebar-menu .menu-info>p{margin:0;font-size:11px}.control-sidebar-menu .progress{margin:0}.control-sidebar-dark{color:#b8c7ce}.control-sidebar-dark,.control-sidebar-dark+.control-sidebar-bg{background:#222d32}.control-sidebar-dark .nav-tabs.control-sidebar-tabs{border-bottom:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a{background:#181f23;color:#b8c7ce}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#141a1d;border-bottom-color:#141a1d}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:active{background:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover{color:#fff}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#222d32;color:#fff}.control-sidebar-dark .control-sidebar-heading,.control-sidebar-dark .control-sidebar-subheading{color:#fff}.control-sidebar-dark .control-sidebar-menu>li>a:hover{background:#1e282c}.control-sidebar-dark .control-sidebar-menu>li>a .menu-info>p{color:#b8c7ce}.control-sidebar-light{color:#5e5e5e}.control-sidebar-light,.control-sidebar-light+.control-sidebar-bg{background:#f9fafc;border-left:1px solid #d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs{border-bottom:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a{background:#e8ecf4;color:#444}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#d2d6de;border-bottom-color:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:active{background:#eff1f7}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#f9fafc;color:#111}.control-sidebar-light .control-sidebar-heading,.control-sidebar-light .control-sidebar-subheading{color:#111}.control-sidebar-light .control-sidebar-menu{margin-left:-14px}.control-sidebar-light .control-sidebar-menu>li>a:hover{background:#f4f4f5}.control-sidebar-light .control-sidebar-menu>li>a .menu-info>p{color:#5e5e5e}.dropdown-menu{box-shadow:none;border-color:#eee}.dropdown-menu>li>a{color:#777}.dropdown-menu>li>a>.glyphicon,.dropdown-menu>li>a>.fa,.dropdown-menu>li>a>.ion{margin-right:10px}.dropdown-menu>li>a:hover{background-color:#e1e3e9;color:#333}.dropdown-menu>.divider{background-color:#eee}.navbar-nav>.notifications-menu>.dropdown-menu,.navbar-nav>.messages-menu>.dropdown-menu,.navbar-nav>.tasks-menu>.dropdown-menu{width:280px;padding:0 0 0 0;margin:0;top:100%}.navbar-nav>.notifications-menu>.dropdown-menu>li,.navbar-nav>.messages-menu>.dropdown-menu>li,.navbar-nav>.tasks-menu>.dropdown-menu>li{position:relative}.navbar-nav>.notifications-menu>.dropdown-menu>li.header,.navbar-nav>.messages-menu>.dropdown-menu>li.header,.navbar-nav>.tasks-menu>.dropdown-menu>li.header{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;background-color:#ffffff;padding:7px 10px;border-bottom:1px solid #f4f4f4;color:#444444;font-size:14px}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;font-size:12px;background-color:#fff;padding:7px 10px;border-bottom:1px solid #eeeeee;color:#444!important;text-align:center}@media (max-width:991px){.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{background:#fff!important;color:#444!important}}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a:hover{text-decoration:none;font-weight:normal}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu,.navbar-nav>.messages-menu>.dropdown-menu>li .menu,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu{max-height:200px;margin:0;padding:0;list-style:none;overflow-x:hidden}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{display:block;white-space:nowrap;border-bottom:1px solid #f4f4f4}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a:hover{background:#f4f4f4;text-decoration:none}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a{color:#444444;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:10px}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.glyphicon,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.fa,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.ion{width:20px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a{margin:0;padding:10px 10px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>div>img{margin:auto 10px auto auto;width:40px;height:40px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4{padding:0;margin:0 0 0 45px;color:#444444;font-size:15px;position:relative}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4>small{color:#999999;font-size:10px;position:absolute;top:0;right:0}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>p{margin:0 0 0 45px;font-size:12px;color:#888888}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:before,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{content:" ";display:table}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{clear:both}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{padding:10px}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>h3{font-size:14px;padding:0;margin:0 0 10px 0;color:#666666}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>.progress{padding:0;margin:0}.navbar-nav>.user-menu>.dropdown-menu{border-top-right-radius:0;border-top-left-radius:0;padding:1px 0 0 0;border-top-width:0;width:280px}.navbar-nav>.user-menu>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header{height:175px;padding:10px;text-align:center}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>img{z-index:5;height:90px;width:90px;border:3px solid;border-color:transparent;border-color:rgba(255,255,255,0.2)}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p{z-index:5;color:#fff;color:rgba(255,255,255,0.8);font-size:17px;margin-top:10px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p>small{display:block;font-size:12px}.navbar-nav>.user-menu>.dropdown-menu>.user-body{padding:15px;border-bottom:1px solid #f4f4f4;border-top:1px solid #dddddd}.navbar-nav>.user-menu>.dropdown-menu>.user-body:before,.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-body a{color:#444 !important}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-body a{background:#fff !important;color:#444 !important}}.navbar-nav>.user-menu>.dropdown-menu>.user-footer{background-color:#f9f9f9;padding:10px}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:before,.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default{color:#666666}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default:hover{background-color:#f9f9f9}}.navbar-nav>.user-menu .user-image{float:left;width:25px;height:25px;border-radius:50%;margin-right:10px;margin-top:-2px}@media (max-width:767px){.navbar-nav>.user-menu .user-image{float:none;margin-right:0;margin-top:-8px;line-height:10px}}.open:not(.dropup)>.animated-dropdown-menu{backface-visibility:visible !important;-webkit-animation:flipInX .7s both;-o-animation:flipInX .7s both;animation:flipInX .7s both}@keyframes flipInX{0%{transform:perspective(400px) rotate3d(1, 0, 0, 90deg);transition-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotate3d(1, 0, 0, -20deg);transition-timing-function:ease-in}60%{transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{transform:perspective(400px)}}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 90deg);-webkit-transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -20deg);-webkit-transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{-webkit-transform:perspective(400px)}}.navbar-custom-menu>.navbar-nav>li{position:relative}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:0;left:auto}@media (max-width:991px){.navbar-custom-menu>.navbar-nav{float:right}.navbar-custom-menu>.navbar-nav>li{position:static}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:5%;left:auto;border:1px solid #ddd;background:#fff}}.form-control{border-radius:0;box-shadow:none;border-color:#d2d6de}.form-control:focus{border-color:#3c8dbc;box-shadow:none}.form-control::-moz-placeholder,.form-control:-ms-input-placeholder,.form-control::-webkit-input-placeholder{color:#bbb;opacity:1}.form-control:not(select){-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-group.has-success label{color:#00a65a}.form-group.has-success .form-control{border-color:#00a65a;box-shadow:none}.form-group.has-warning label{color:#f39c12}.form-group.has-warning .form-control{border-color:#f39c12;box-shadow:none}.form-group.has-error label{color:#dd4b39}.form-group.has-error .form-control{border-color:#dd4b39;box-shadow:none}.input-group .input-group-addon{border-radius:0;border-color:#d2d6de;background-color:#fff}.btn-group-vertical .btn.btn-flat:first-of-type,.btn-group-vertical .btn.btn-flat:last-of-type{border-radius:0}.icheck>label{padding-left:0}.form-control-feedback.fa{line-height:34px}.input-lg+.form-control-feedback.fa,.input-group-lg+.form-control-feedback.fa,.form-group-lg .form-control+.form-control-feedback.fa{line-height:46px}.input-sm+.form-control-feedback.fa,.input-group-sm+.form-control-feedback.fa,.form-group-sm .form-control+.form-control-feedback.fa{line-height:30px}.progress,.progress>.progress-bar{-webkit-box-shadow:none;box-shadow:none}.progress,.progress>.progress-bar,.progress .progress-bar,.progress>.progress-bar .progress-bar{border-radius:1px}.progress.sm,.progress-sm{height:10px}.progress.sm,.progress-sm,.progress.sm .progress-bar,.progress-sm .progress-bar{border-radius:1px}.progress.xs,.progress-xs{height:7px}.progress.xs,.progress-xs,.progress.xs .progress-bar,.progress-xs .progress-bar{border-radius:1px}.progress.xxs,.progress-xxs{height:3px}.progress.xxs,.progress-xxs,.progress.xxs .progress-bar,.progress-xxs .progress-bar{border-radius:1px}.progress.vertical{position:relative;width:30px;height:200px;display:inline-block;margin-right:10px}.progress.vertical>.progress-bar{width:100%;position:absolute;bottom:0}.progress.vertical.sm,.progress.vertical.progress-sm{width:20px}.progress.vertical.xs,.progress.vertical.progress-xs{width:10px}.progress.vertical.xxs,.progress.vertical.progress-xxs{width:3px}.progress-group .progress-text{font-weight:600}.progress-group .progress-number{float:right}.table tr>td .progress{margin:0}.progress-bar-light-blue,.progress-bar-primary{background-color:#3c8dbc}.progress-striped .progress-bar-light-blue,.progress-striped .progress-bar-primary{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-green,.progress-bar-success{background-color:#00a65a}.progress-striped .progress-bar-green,.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-aqua,.progress-bar-info{background-color:#00c0ef}.progress-striped .progress-bar-aqua,.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-yellow,.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-yellow,.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-red,.progress-bar-danger{background-color:#dd4b39}.progress-striped .progress-bar-red,.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.small-box{border-radius:2px;position:relative;display:block;margin-bottom:20px;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.small-box>.inner{padding:10px}.small-box>.small-box-footer{position:relative;text-align:center;padding:3px 0;color:#fff;color:rgba(255,255,255,0.8);display:block;z-index:10;background:rgba(0,0,0,0.1);text-decoration:none}.small-box>.small-box-footer:hover{color:#fff;background:rgba(0,0,0,0.15)}.small-box h3{font-size:38px;font-weight:bold;margin:0 0 10px 0;white-space:nowrap;padding:0}.small-box p{font-size:15px}.small-box p>small{display:block;color:#f9f9f9;font-size:13px;margin-top:5px}.small-box h3,.small-box p{z-index:5px}.small-box .icon{-webkit-transition:all .3s linear;-o-transition:all .3s linear;transition:all .3s linear;position:absolute;top:-10px;right:10px;z-index:0;font-size:90px;color:rgba(0,0,0,0.15)}.small-box:hover{text-decoration:none;color:#f9f9f9}.small-box:hover .icon{font-size:95px}@media (max-width:767px){.small-box{text-align:center}.small-box .icon{display:none}.small-box p{font-size:12px}}.box{position:relative;border-radius:3px;background:#ffffff;border-top:3px solid #d2d6de;margin-bottom:20px;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.box.box-primary{border-top-color:#3c8dbc}.box.box-info{border-top-color:#00c0ef}.box.box-danger{border-top-color:#dd4b39}.box.box-warning{border-top-color:#f39c12}.box.box-success{border-top-color:#00a65a}.box.box-default{border-top-color:#d2d6de}.box.collapsed-box .box-body,.box.collapsed-box .box-footer{display:none}.box .nav-stacked>li{border-bottom:1px solid #f4f4f4;margin:0}.box .nav-stacked>li:last-of-type{border-bottom:none}.box.height-control .box-body{max-height:300px;overflow:auto}.box .border-right{border-right:1px solid #f4f4f4}.box .border-left{border-left:1px solid #f4f4f4}.box.box-solid{border-top:0}.box.box-solid>.box-header .btn.btn-default{background:transparent}.box.box-solid>.box-header .btn:hover,.box.box-solid>.box-header a:hover{background:rgba(0,0,0,0.1)}.box.box-solid.box-default{border:1px solid #d2d6de}.box.box-solid.box-default>.box-header{color:#444;background:#d2d6de;background-color:#d2d6de}.box.box-solid.box-default>.box-header a,.box.box-solid.box-default>.box-header .btn{color:#444}.box.box-solid.box-primary{border:1px solid #3c8dbc}.box.box-solid.box-primary>.box-header{color:#fff;background:#3c8dbc;background-color:#3c8dbc}.box.box-solid.box-primary>.box-header a,.box.box-solid.box-primary>.box-header .btn{color:#fff}.box.box-solid.box-info{border:1px solid #00c0ef}.box.box-solid.box-info>.box-header{color:#fff;background:#00c0ef;background-color:#00c0ef}.box.box-solid.box-info>.box-header a,.box.box-solid.box-info>.box-header .btn{color:#fff}.box.box-solid.box-danger{border:1px solid #dd4b39}.box.box-solid.box-danger>.box-header{color:#fff;background:#dd4b39;background-color:#dd4b39}.box.box-solid.box-danger>.box-header a,.box.box-solid.box-danger>.box-header .btn{color:#fff}.box.box-solid.box-warning{border:1px solid #f39c12}.box.box-solid.box-warning>.box-header{color:#fff;background:#f39c12;background-color:#f39c12}.box.box-solid.box-warning>.box-header a,.box.box-solid.box-warning>.box-header .btn{color:#fff}.box.box-solid.box-success{border:1px solid #00a65a}.box.box-solid.box-success>.box-header{color:#fff;background:#00a65a;background-color:#00a65a}.box.box-solid.box-success>.box-header a,.box.box-solid.box-success>.box-header .btn{color:#fff}.box.box-solid>.box-header>.box-tools .btn{border:0;box-shadow:none}.box.box-solid[class*='bg']>.box-header{color:#fff}.box .box-group>.box{margin-bottom:5px}.box .knob-label{text-align:center;color:#333;font-weight:100;font-size:12px;margin-bottom:0.3em}.box>.overlay,.overlay-wrapper>.overlay,.box>.loading-img,.overlay-wrapper>.loading-img{position:absolute;top:0;left:0;width:100%;height:100%}.box .overlay,.overlay-wrapper .overlay{z-index:50;background:rgba(255,255,255,0.7);border-radius:3px}.box .overlay>.fa,.overlay-wrapper .overlay>.fa{position:absolute;top:50%;left:50%;margin-left:-15px;margin-top:-15px;color:#000;font-size:30px}.box .overlay.dark,.overlay-wrapper .overlay.dark{background:rgba(0,0,0,0.5)}.box-header:before,.box-body:before,.box-footer:before,.box-header:after,.box-body:after,.box-footer:after{content:" ";display:table}.box-header:after,.box-body:after,.box-footer:after{clear:both}.box-header{color:#444;display:block;padding:10px;position:relative}.box-header.with-border{border-bottom:1px solid #f4f4f4}.collapsed-box .box-header.with-border{border-bottom:none}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion,.box-header .box-title{display:inline-block;font-size:18px;margin:0;line-height:1}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion{margin-right:5px}.box-header>.box-tools{position:absolute;right:10px;top:5px}.box-header>.box-tools [data-toggle="tooltip"]{position:relative}.box-header>.box-tools.pull-right .dropdown-menu{right:0;left:auto}.btn-box-tool{padding:5px;font-size:12px;background:transparent;color:#97a0b3}.open .btn-box-tool,.btn-box-tool:hover{color:#606c84}.btn-box-tool.btn:active{box-shadow:none}.box-body{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;padding:10px}.no-header .box-body{border-top-right-radius:3px;border-top-left-radius:3px}.box-body>.table{margin-bottom:0}.box-body .fc{margin-top:5px}.box-body .full-width-chart{margin:-19px}.box-body.no-padding .full-width-chart{margin:-9px}.box-body .box-pane{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:3px}.box-body .box-pane-right{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:0}.box-footer{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;border-top:1px solid #f4f4f4;padding:10px;background-color:#fff}.chart-legend{margin:10px 0}@media (max-width:991px){.chart-legend>li{float:left;margin-right:10px}}.box-comments{background:#f7f7f7}.box-comments .box-comment{padding:8px 0;border-bottom:1px solid #eee}.box-comments .box-comment:before,.box-comments .box-comment:after{content:" ";display:table}.box-comments .box-comment:after{clear:both}.box-comments .box-comment:last-of-type{border-bottom:0}.box-comments .box-comment:first-of-type{padding-top:0}.box-comments .box-comment img{float:left}.box-comments .comment-text{margin-left:40px;color:#555}.box-comments .username{color:#444;display:block;font-weight:600}.box-comments .text-muted{font-weight:400;font-size:12px}.todo-list{margin:0;padding:0;list-style:none;overflow:auto}.todo-list>li{border-radius:2px;padding:10px;background:#f4f4f4;margin-bottom:2px;border-left:2px solid #e6e7e8;color:#444}.todo-list>li:last-of-type{margin-bottom:0}.todo-list>li>input[type='checkbox']{margin:0 10px 0 5px}.todo-list>li .text{display:inline-block;margin-left:5px;font-weight:600}.todo-list>li .label{margin-left:10px;font-size:9px}.todo-list>li .tools{display:none;float:right;color:#dd4b39}.todo-list>li .tools>.fa,.todo-list>li .tools>.glyphicon,.todo-list>li .tools>.ion{margin-right:5px;cursor:pointer}.todo-list>li:hover .tools{display:inline-block}.todo-list>li.done{color:#999}.todo-list>li.done .text{text-decoration:line-through;font-weight:500}.todo-list>li.done .label{background:#d2d6de !important}.todo-list .danger{border-left-color:#dd4b39}.todo-list .warning{border-left-color:#f39c12}.todo-list .info{border-left-color:#00c0ef}.todo-list .success{border-left-color:#00a65a}.todo-list .primary{border-left-color:#3c8dbc}.todo-list .handle{display:inline-block;cursor:move;margin:0 5px}.chat{padding:5px 20px 5px 10px}.chat .item{margin-bottom:10px}.chat .item:before,.chat .item:after{content:" ";display:table}.chat .item:after{clear:both}.chat .item>img{width:40px;height:40px;border:2px solid transparent;border-radius:50%}.chat .item>.online{border:2px solid #00a65a}.chat .item>.offline{border:2px solid #dd4b39}.chat .item>.message{margin-left:55px;margin-top:-40px}.chat .item>.message>.name{display:block;font-weight:600}.chat .item>.attachment{border-radius:3px;background:#f4f4f4;margin-left:65px;margin-right:15px;padding:10px}.chat .item>.attachment>h4{margin:0 0 5px 0;font-weight:600;font-size:14px}.chat .item>.attachment>p,.chat .item>.attachment>.filename{font-weight:600;font-size:13px;font-style:italic;margin:0}.chat .item>.attachment:before,.chat .item>.attachment:after{content:" ";display:table}.chat .item>.attachment:after{clear:both}.box-input{max-width:200px}.modal .panel-body{color:#444}.info-box{display:block;min-height:90px;background:#fff;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:2px;margin-bottom:15px}.info-box small{font-size:14px}.info-box .progress{background:rgba(0,0,0,0.2);margin:5px -10px 5px -10px;height:2px}.info-box .progress,.info-box .progress .progress-bar{border-radius:0}.info-box .progress .progress-bar{background:#fff}.info-box-icon{border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px;display:block;float:left;height:90px;width:90px;text-align:center;font-size:45px;line-height:90px;background:rgba(0,0,0,0.2)}.info-box-icon>img{max-width:100%}.info-box-content{padding:5px 10px;margin-left:90px}.info-box-number{display:block;font-weight:bold;font-size:18px}.progress-description,.info-box-text{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.info-box-text{text-transform:uppercase}.info-box-more{display:block}.progress-description{margin:0}.timeline{position:relative;margin:0 0 30px 0;padding:0;list-style:none}.timeline:before{content:'';position:absolute;top:0;bottom:0;width:4px;background:#ddd;left:31px;margin:0;border-radius:2px}.timeline>li{position:relative;margin-right:10px;margin-bottom:15px}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-item{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;margin-top:0;background:#fff;color:#444;margin-left:60px;margin-right:15px;padding:0;position:relative}.timeline>li>.timeline-item>.time{color:#999;float:right;padding:10px;font-size:12px}.timeline>li>.timeline-item>.timeline-header{margin:0;color:#555;border-bottom:1px solid #f4f4f4;padding:10px;font-size:16px;line-height:1.1}.timeline>li>.timeline-item>.timeline-header>a{font-weight:600}.timeline>li>.timeline-item>.timeline-body,.timeline>li>.timeline-item>.timeline-footer{padding:10px}.timeline>li>.fa,.timeline>li>.glyphicon,.timeline>li>.ion{width:30px;height:30px;font-size:15px;line-height:30px;position:absolute;color:#666;background:#d2d6de;border-radius:50%;text-align:center;left:18px;top:0}.timeline>.time-label>span{font-weight:600;padding:5px;display:inline-block;background-color:#fff;border-radius:4px}.timeline-inverse>li>.timeline-item{background:#f0f0f0;border:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none}.timeline-inverse>li>.timeline-item>.timeline-header{border-bottom-color:#ddd}.btn{border-radius:3px;-webkit-box-shadow:none;box-shadow:none;border:1px solid transparent}.btn.uppercase{text-transform:uppercase}.btn.btn-flat{border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-width:1px}.btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:focus{outline:none}.btn.btn-file{position:relative;overflow:hidden}.btn.btn-file>input[type='file']{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;opacity:0;filter:alpha(opacity=0);outline:none;background:white;cursor:inherit;display:block}.btn-default{background-color:#f4f4f4;color:#444;border-color:#ddd}.btn-default:hover,.btn-default:active,.btn-default.hover{background-color:#e7e7e7}.btn-primary{background-color:#3c8dbc;border-color:#367fa9}.btn-primary:hover,.btn-primary:active,.btn-primary.hover{background-color:#367fa9}.btn-success{background-color:#00a65a;border-color:#008d4c}.btn-success:hover,.btn-success:active,.btn-success.hover{background-color:#008d4c}.btn-info{background-color:#00c0ef;border-color:#00acd6}.btn-info:hover,.btn-info:active,.btn-info.hover{background-color:#00acd6}.btn-danger{background-color:#dd4b39;border-color:#d73925}.btn-danger:hover,.btn-danger:active,.btn-danger.hover{background-color:#d73925}.btn-warning{background-color:#f39c12;border-color:#e08e0b}.btn-warning:hover,.btn-warning:active,.btn-warning.hover{background-color:#e08e0b}.btn-outline{border:1px solid #fff;background:transparent;color:#fff}.btn-outline:hover,.btn-outline:focus,.btn-outline:active{color:rgba(255,255,255,0.7);border-color:rgba(255,255,255,0.7)}.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn[class*='bg-']:hover{-webkit-box-shadow:inset 0 0 100px rgba(0,0,0,0.2);box-shadow:inset 0 0 100px rgba(0,0,0,0.2)}.btn-app{border-radius:3px;position:relative;padding:15px 5px;margin:0 0 10px 10px;min-width:80px;height:60px;text-align:center;color:#666;border:1px solid #ddd;background-color:#f4f4f4;font-size:12px}.btn-app>.fa,.btn-app>.glyphicon,.btn-app>.ion{font-size:20px;display:block}.btn-app:hover{background:#f4f4f4;color:#444;border-color:#aaa}.btn-app:active,.btn-app:focus{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-app>.badge{position:absolute;top:-3px;right:-10px;font-size:10px;font-weight:400}.callout{border-radius:3px;margin:0 0 20px 0;padding:15px 30px 15px 15px;border-left:5px solid #eee}.callout a{color:#fff;text-decoration:underline}.callout a:hover{color:#eee}.callout h4{margin-top:0;font-weight:600}.callout p:last-child{margin-bottom:0}.callout code,.callout .highlight{background-color:#fff}.callout.callout-danger{border-color:#c23321}.callout.callout-warning{border-color:#c87f0a}.callout.callout-info{border-color:#0097bc}.callout.callout-success{border-color:#00733e}.alert{border-radius:3px}.alert h4{font-weight:600}.alert .icon{margin-right:10px}.alert .close{color:#000;opacity:.2;filter:alpha(opacity=20)}.alert .close:hover{opacity:.5;filter:alpha(opacity=50)}.alert a{color:#fff;text-decoration:underline}.alert-success{border-color:#008d4c}.alert-danger,.alert-error{border-color:#d73925}.alert-warning{border-color:#e08e0b}.alert-info{border-color:#00acd6}.nav>li>a:hover,.nav>li>a:active,.nav>li>a:focus{color:#444;background:#f7f7f7}.nav-pills>li>a{border-radius:0;border-top:3px solid transparent;color:#444}.nav-pills>li>a>.fa,.nav-pills>li>a>.glyphicon,.nav-pills>li>a>.ion{margin-right:5px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{border-top-color:#3c8dbc}.nav-pills>li.active>a{font-weight:600}.nav-stacked>li>a{border-radius:0;border-top:0;border-left:3px solid transparent;color:#444}.nav-stacked>li.active>a,.nav-stacked>li.active>a:hover{background:transparent;color:#444;border-top:0;border-left-color:#3c8dbc}.nav-stacked>li.header{border-bottom:1px solid #ddd;color:#777;margin-bottom:10px;padding:5px 10px;text-transform:uppercase}.nav-tabs-custom{margin-bottom:20px;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px}.nav-tabs-custom>.nav-tabs{margin:0;/* border-bottom-color: #3C8DBC /*#f4f4f4*/; */border-top-right-radius:3px;border-top-left-radius:3px}.nav-tabs-custom>.nav-tabs>li{border-top:3px solid transparent;/*margin-bottom:-2px;*/margin-right:5px}.nav-tabs-custom>.nav-tabs>li>a{color:#444;border-radius:0}.nav-tabs-custom>.nav-tabs>li>a.text-muted{color:#999}.nav-tabs-custom>.nav-tabs>li>a,.nav-tabs-custom>.nav-tabs>li>a:hover{background:transparent;margin:0}.nav-tabs-custom>.nav-tabs>li>a:hover{color:#999}.nav-tabs-custom>.nav-tabs>li:not(.active)>a:hover,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:focus,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:active{border-color:transparent}.nav-tabs-custom>.nav-tabs>li.active{border-top-color:#3c8dbc}.nav-tabs-custom>.nav-tabs>li.active>a,.nav-tabs-custom>.nav-tabs>li.active:hover>a{background-color:#fff;color:#444}.nav-tabs-custom>.nav-tabs>li.active>a{border-top:1px solid #3C8DBC;border-left:1px solid #3C8DBC;border-right:1px solid #3C8DBC;}.nav-tabs-custom>.nav-tabs>li:first-of-type{margin-left:0}.nav-tabs-custom>.nav-tabs>li:first-of-type.active>a{border-left-color:transparent}.nav-tabs-custom>.nav-tabs.pull-right{float:none!important}.nav-tabs-custom>.nav-tabs.pull-right>li{float:right}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type{margin-right:0}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type>a{border-left-width:1px}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type.active>a{border-left-color:#f4f4f4;border-right-color:transparent}.nav-tabs-custom>.nav-tabs>li.header{line-height:35px;padding:0 10px;font-size:20px;color:#444}.nav-tabs-custom>.nav-tabs>li.header>.fa,.nav-tabs-custom>.nav-tabs>li.header>.glyphicon,.nav-tabs-custom>.nav-tabs>li.header>.ion{margin-right:5px}.nav-tabs-custom>.tab-content{background:#fff;padding:10px;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.nav-tabs-custom .dropdown.open>a:active,.nav-tabs-custom .dropdown.open>a:focus{background:transparent;color:#999}.pagination>li>a{background:#fafafa;color:#666}.pagination.pagination-flat>li>a{border-radius:0 !important}.products-list{list-style:none;margin:0;padding:0}.products-list>.item{border-radius:3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);padding:10px 0;background:#fff}.products-list>.item:before,.products-list>.item:after{content:" ";display:table}.products-list>.item:after{clear:both}.products-list .product-img{float:left}.products-list .product-img img{width:50px;height:50px}.products-list .product-info{margin-left:60px}.products-list .product-title{font-weight:600}.products-list .product-description{display:block;color:#999;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.product-list-in-box>.item{-webkit-box-shadow:none;box-shadow:none;border-radius:0;border-bottom:1px solid #f4f4f4}.product-list-in-box>.item:last-of-type{border-bottom-width:0}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{border-top:1px solid #f4f4f4}.table>thead>tr>th{border-bottom:2px solid #f4f4f4}.table tr td .progress{margin-top:5px}.table-bordered{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table.no-border,.table.no-border td,.table.no-border th{border:0}table.text-center,table.text-center td,table.text-center th{text-align:center}.table.align th{text-align:left}.table.align td{text-align:right}.label-default{background-color:#d2d6de;color:#444}.direct-chat .box-body{border-bottom-right-radius:0;border-bottom-left-radius:0;position:relative;overflow-x:hidden;padding:0}.direct-chat.chat-pane-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-messages{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);padding:10px;height:250px;overflow:auto}.direct-chat-msg,.direct-chat-text{display:block}.direct-chat-msg{margin-bottom:10px}.direct-chat-msg:before,.direct-chat-msg:after{content:" ";display:table}.direct-chat-msg:after{clear:both}.direct-chat-messages,.direct-chat-contacts{-webkit-transition:-webkit-transform .5s ease-in-out;-moz-transition:-moz-transform .5s ease-in-out;-o-transition:-o-transform .5s ease-in-out;transition:transform .5s ease-in-out}.direct-chat-text{border-radius:5px;position:relative;padding:5px 10px;background:#d2d6de;border:1px solid #d2d6de;margin:5px 0 0 50px;color:#444}.direct-chat-text:after,.direct-chat-text:before{position:absolute;right:100%;top:15px;border:solid transparent;border-right-color:#d2d6de;content:' ';height:0;width:0;pointer-events:none}.direct-chat-text:after{border-width:5px;margin-top:-5px}.direct-chat-text:before{border-width:6px;margin-top:-6px}.right .direct-chat-text{margin-right:50px;margin-left:0}.right .direct-chat-text:after,.right .direct-chat-text:before{right:auto;left:100%;border-right-color:transparent;border-left-color:#d2d6de}.direct-chat-img{border-radius:50%;float:left;width:40px;height:40px}.right .direct-chat-img{float:right}.direct-chat-info{display:block;margin-bottom:2px;font-size:12px}.direct-chat-name{font-weight:600}.direct-chat-timestamp{color:#999}.direct-chat-contacts-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-contacts{-webkit-transform:translate(101%, 0);-ms-transform:translate(101%, 0);-o-transform:translate(101%, 0);transform:translate(101%, 0);position:absolute;top:0;bottom:0;height:250px;width:100%;background:#222d32;color:#fff;overflow:auto}.contacts-list>li{border-bottom:1px solid rgba(0,0,0,0.2);padding:10px;margin:0}.contacts-list>li:before,.contacts-list>li:after{content:" ";display:table}.contacts-list>li:after{clear:both}.contacts-list>li:last-of-type{border-bottom:none}.contacts-list-img{border-radius:50%;width:40px;float:left}.contacts-list-info{margin-left:45px;color:#fff}.contacts-list-name,.contacts-list-status{display:block}.contacts-list-name{font-weight:600}.contacts-list-status{font-size:12px}.contacts-list-date{color:#aaa;font-weight:normal}.contacts-list-msg{color:#999}.direct-chat-danger .right>.direct-chat-text{background:#dd4b39;border-color:#dd4b39;color:#fff}.direct-chat-danger .right>.direct-chat-text:after,.direct-chat-danger .right>.direct-chat-text:before{border-left-color:#dd4b39}.direct-chat-primary .right>.direct-chat-text{background:#3c8dbc;border-color:#3c8dbc;color:#fff}.direct-chat-primary .right>.direct-chat-text:after,.direct-chat-primary .right>.direct-chat-text:before{border-left-color:#3c8dbc}.direct-chat-warning .right>.direct-chat-text{background:#f39c12;border-color:#f39c12;color:#fff}.direct-chat-warning .right>.direct-chat-text:after,.direct-chat-warning .right>.direct-chat-text:before{border-left-color:#f39c12}.direct-chat-info .right>.direct-chat-text{background:#00c0ef;border-color:#00c0ef;color:#fff}.direct-chat-info .right>.direct-chat-text:after,.direct-chat-info .right>.direct-chat-text:before{border-left-color:#00c0ef}.direct-chat-success .right>.direct-chat-text{background:#00a65a;border-color:#00a65a;color:#fff}.direct-chat-success .right>.direct-chat-text:after,.direct-chat-success .right>.direct-chat-text:before{border-left-color:#00a65a}.users-list>li{width:25%;float:left;padding:10px;text-align:center}.users-list>li img{border-radius:50%;max-width:100%;height:auto}.users-list>li>a:hover,.users-list>li>a:hover .users-list-name{color:#999}.users-list-name,.users-list-date{display:block}.users-list-name{font-weight:600;color:#444;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.users-list-date{color:#999;font-size:12px}.carousel-control.left,.carousel-control.right{background-image:none}.carousel-control>.fa{font-size:40px;position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-20px}.modal{background:rgba(0,0,0,0.3)}.modal-content{border-radius:0;-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125);border:0}@media (min-width:768px){.modal-content{-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125)}}.modal-header{border-bottom-color:#f4f4f4}.modal-footer{border-top-color:#f4f4f4}.modal-primary .modal-header,.modal-primary .modal-footer{border-color:#307095}.modal-warning .modal-header,.modal-warning .modal-footer{border-color:#c87f0a}.modal-info .modal-header,.modal-info .modal-footer{border-color:#0097bc}.modal-success .modal-header,.modal-success .modal-footer{border-color:#00733e}.modal-danger .modal-header,.modal-danger .modal-footer{border-color:#c23321}.box-widget{border:none;position:relative}.widget-user .widget-user-header{padding:20px;height:120px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user .widget-user-username{margin-top:0;margin-bottom:5px;font-size:25px;font-weight:300;text-shadow:0 1px 1px rgba(0,0,0,0.2)}.widget-user .widget-user-desc{margin-top:0}.widget-user .widget-user-image{position:absolute;top:65px;left:50%;margin-left:-45px}.widget-user .widget-user-image>img{width:90px;height:auto;border:3px solid #fff}.widget-user .box-footer{padding-top:30px}.widget-user-2 .widget-user-header{padding:20px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user-2 .widget-user-username{margin-top:5px;margin-bottom:5px;font-size:25px;font-weight:300}.widget-user-2 .widget-user-desc{margin-top:0}.widget-user-2 .widget-user-username,.widget-user-2 .widget-user-desc{margin-left:75px}.widget-user-2 .widget-user-image>img{width:65px;height:auto;float:left}.mailbox-messages>.table{margin:0}.mailbox-controls{padding:5px}.mailbox-controls.with-border{border-bottom:1px solid #f4f4f4}.mailbox-read-info{border-bottom:1px solid #f4f4f4;padding:10px}.mailbox-read-info h3{font-size:20px;margin:0}.mailbox-read-info h5{margin:0;padding:5px 0 0 0}.mailbox-read-time{color:#999;font-size:13px}.mailbox-read-message{padding:10px}.mailbox-attachments li{float:left;width:200px;border:1px solid #eee;margin-bottom:10px;margin-right:10px}.mailbox-attachment-name{font-weight:bold;color:#666}.mailbox-attachment-icon,.mailbox-attachment-info,.mailbox-attachment-size{display:block}.mailbox-attachment-info{padding:10px;background:#f4f4f4}.mailbox-attachment-size{color:#999;font-size:12px}.mailbox-attachment-icon{text-align:center;font-size:65px;color:#666;padding:20px 10px}.mailbox-attachment-icon.has-img{padding:0}.mailbox-attachment-icon.has-img>img{max-width:100%;height:auto}.lockscreen{background:#d2d6de}.lockscreen-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.lockscreen-logo a{color:#444}.lockscreen-wrapper{max-width:400px;margin:0 auto;margin-top:10%}.lockscreen .lockscreen-name{text-align:center;font-weight:600}.lockscreen-item{border-radius:4px;padding:0;background:#fff;position:relative;margin:10px auto 30px auto;width:290px}.lockscreen-image{border-radius:50%;position:absolute;left:-10px;top:-25px;background:#fff;padding:5px;z-index:10}.lockscreen-image>img{border-radius:50%;width:70px;height:70px}.lockscreen-credentials{margin-left:70px}.lockscreen-credentials .form-control{border:0}.lockscreen-credentials .btn{background-color:#fff;border:0;padding:0 10px}.lockscreen-footer{margin-top:10px}.login-logo,.register-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.login-logo a,.register-logo a{color:#444}.login-page,.register-page{background:#d2d6de}.login-box,.register-box{width:360px;margin:7% auto}@media (max-width:768px){.login-box,.register-box{width:90%;margin-top:20px}}.login-box-body,.register-box-body{background:#fff;padding:20px;border-top:0;color:#666}.login-box-body .form-control-feedback,.register-box-body .form-control-feedback{color:#777}.login-box-msg,.register-box-msg{margin:0;text-align:center;padding:0 20px 20px 20px}.social-auth-links{margin:10px 0}.error-page{width:600px;margin:20px auto 0 auto}@media (max-width:991px){.error-page{width:100%}}.error-page>.headline{float:left;font-size:100px;font-weight:300}@media (max-width:991px){.error-page>.headline{float:none;text-align:center}}.error-page>.error-content{margin-left:190px;display:block}@media (max-width:991px){.error-page>.error-content{margin-left:0}}.error-page>.error-content>h3{font-weight:300;font-size:25px}@media (max-width:991px){.error-page>.error-content>h3{text-align:center}}.invoice{position:relative;background:#fff;border:1px solid #f4f4f4;padding:20px;margin:10px 25px}.invoice-title{margin-top:0}.profile-user-img{margin:0 auto;width:100px;padding:3px;border:3px solid #d2d6de}.profile-username{font-size:21px;margin-top:5px}.post{border-bottom:1px solid #d2d6de;margin-bottom:15px;padding-bottom:15px;color:#666}.post:last-of-type{border-bottom:0;margin-bottom:0;padding-bottom:0}.post .user-block{margin-bottom:15px}.btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon>:first-child{border:none;text-align:center;width:100%}.btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0}.btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0}.btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0}.btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:hover,.btn-adn:focus,.btn-adn.focus,.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none}.btn-adn .badge{color:#d87a68;background-color:#fff}.btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:hover,.btn-bitbucket:focus,.btn-bitbucket.focus,.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none}.btn-bitbucket .badge{color:#205081;background-color:#fff}.btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:hover,.btn-dropbox:focus,.btn-dropbox.focus,.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none}.btn-dropbox .badge{color:#1087dd;background-color:#fff}.btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:hover,.btn-facebook:focus,.btn-facebook.focus,.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none}.btn-facebook .badge{color:#3b5998;background-color:#fff}.btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:hover,.btn-flickr:focus,.btn-flickr.focus,.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none}.btn-flickr .badge{color:#ff0084;background-color:#fff}.btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:hover,.btn-foursquare:focus,.btn-foursquare.focus,.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none}.btn-foursquare .badge{color:#f94877;background-color:#fff}.btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:hover,.btn-github:focus,.btn-github.focus,.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none}.btn-github .badge{color:#444;background-color:#fff}.btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:hover,.btn-google:focus,.btn-google.focus,.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none}.btn-google .badge{color:#dd4b39;background-color:#fff}.btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:hover,.btn-instagram:focus,.btn-instagram.focus,.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none}.btn-instagram .badge{color:#3f729b;background-color:#fff}.btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:hover,.btn-linkedin:focus,.btn-linkedin.focus,.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none}.btn-linkedin .badge{color:#007bb6;background-color:#fff}.btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:hover,.btn-microsoft:focus,.btn-microsoft.focus,.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none}.btn-microsoft .badge{color:#2672ec;background-color:#fff}.btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:hover,.btn-openid:focus,.btn-openid.focus,.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none}.btn-openid .badge{color:#f7931e;background-color:#fff}.btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:hover,.btn-pinterest:focus,.btn-pinterest.focus,.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none}.btn-pinterest .badge{color:#cb2027;background-color:#fff}.btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:hover,.btn-reddit:focus,.btn-reddit.focus,.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none}.btn-reddit .badge{color:#eff7ff;background-color:#000}.btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:hover,.btn-soundcloud:focus,.btn-soundcloud.focus,.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none}.btn-soundcloud .badge{color:#f50;background-color:#fff}.btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:hover,.btn-tumblr:focus,.btn-tumblr.focus,.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none}.btn-tumblr .badge{color:#2c4762;background-color:#fff}.btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:hover,.btn-twitter:focus,.btn-twitter.focus,.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none}.btn-twitter .badge{color:#55acee;background-color:#fff}.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:hover,.btn-vimeo:focus,.btn-vimeo.focus,.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none}.btn-vimeo .badge{color:#1ab7ea;background-color:#fff}.btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:hover,.btn-vk:focus,.btn-vk.focus,.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none}.btn-vk .badge{color:#587ea3;background-color:#fff}.btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:hover,.btn-yahoo:focus,.btn-yahoo.focus,.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none}.btn-yahoo .badge{color:#720e9e;background-color:#fff}.fc-button{background:#f4f4f4;background-image:none;color:#444;border-color:#ddd;border-bottom-color:#ddd}.fc-button:hover,.fc-button:active,.fc-button.hover{background-color:#e9e9e9}.fc-header-title h2{font-size:15px;line-height:1.6em;color:#666;margin-left:10px}.fc-header-right{padding-right:10px}.fc-header-left{padding-left:10px}.fc-widget-header{background:#fafafa}.fc-grid{width:100%;border:0}.fc-widget-header:first-of-type,.fc-widget-content:first-of-type{border-left:0;border-right:0}.fc-widget-header:last-of-type,.fc-widget-content:last-of-type{border-right:0}.fc-toolbar{padding:10px;margin:0}.fc-day-number{font-size:20px;font-weight:300;padding-right:10px}.fc-color-picker{list-style:none;margin:0;padding:0}.fc-color-picker>li{float:left;font-size:30px;margin-right:5px;line-height:30px}.fc-color-picker>li .fa{-webkit-transition:-webkit-transform linear .3s;-moz-transition:-moz-transform linear .3s;-o-transition:-o-transform linear .3s;transition:transform linear .3s}.fc-color-picker>li .fa:hover{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}#add-new-event{-webkit-transition:all linear .3s;-o-transition:all linear .3s;transition:all linear .3s}.external-event{padding:5px 10px;font-weight:bold;margin-bottom:4px;box-shadow:0 1px 1px rgba(0,0,0,0.1);text-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;cursor:move}.external-event:hover{box-shadow:inset 0 0 90px rgba(0,0,0,0.2)}.select2-container--default.select2-container--focus,.select2-selection.select2-container--focus,.select2-container--default:focus,.select2-selection:focus,.select2-container--default:active,.select2-selection:active{outline:none}.select2-container--default .select2-selection--single,.select2-selection .select2-selection--single{border:1px solid #d2d6de;border-radius:0;padding:6px 12px;height:34px}.select2-container--default.select2-container--open{border-color:#3c8dbc}.select2-dropdown{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#3c8dbc;color:white}.select2-results__option{padding:6px 12px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{padding-left:0;padding-right:0;height:auto;margin-top:-4px}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:6px;padding-left:20px}.select2-container--default .select2-selection--single .select2-selection__arrow{height:28px;right:3px}.select2-container--default .select2-selection--single .select2-selection__arrow b{margin-top:0}.select2-dropdown .select2-search__field,.select2-search--inline .select2-search__field{border:1px solid #d2d6de}.select2-dropdown .select2-search__field:focus,.select2-search--inline .select2-search__field:focus{outline:none;border:1px solid #3c8dbc}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option[aria-selected=true],.select2-container--default .select2-results__option[aria-selected=true]:hover{color:#444}.select2-container--default .select2-selection--multiple{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-selection--multiple:focus{border-color:#3c8dbc}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:#d2d6de}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc;border-color:#367fa9;padding:1px 10px;color:#fff}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-right:5px;color:rgba(255,255,255,0.7)}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#fff}.select2-container .select2-selection--single .select2-selection__rendered{padding-right:10px}.pad{padding:10px}.margin{margin:10px}.margin-bottom{margin-bottom:20px}.margin-bottom-none{margin-bottom:0}.margin-r-5{margin-right:5px}.inline{display:inline}.description-block{display:block;margin:10px 0;text-align:center}.description-block.margin-bottom{margin-bottom:25px}.description-block>.description-header{margin:0;padding:0;font-weight:600;font-size:16px}.description-block>.description-text{text-transform:uppercase}.bg-red,.bg-yellow,.bg-aqua,.bg-blue,.bg-light-blue,.bg-green,.bg-navy,.bg-teal,.bg-olive,.bg-lime,.bg-orange,.bg-fuchsia,.bg-purple,.bg-maroon,.bg-black,.bg-red-active,.bg-yellow-active,.bg-aqua-active,.bg-blue-active,.bg-light-blue-active,.bg-green-active,.bg-navy-active,.bg-teal-active,.bg-olive-active,.bg-lime-active,.bg-orange-active,.bg-fuchsia-active,.bg-purple-active,.bg-maroon-active,.bg-black-active,.callout.callout-danger,.callout.callout-warning,.callout.callout-info,.callout.callout-success,.alert-success,.alert-danger,.alert-error,.alert-warning,.alert-info,.label-danger,.label-info,.label-warning,.label-primary,.label-success,.modal-primary .modal-body,.modal-primary .modal-header,.modal-primary .modal-footer,.modal-warning .modal-body,.modal-warning .modal-header,.modal-warning .modal-footer,.modal-info .modal-body,.modal-info .modal-header,.modal-info .modal-footer,.modal-success .modal-body,.modal-success .modal-header,.modal-success .modal-footer,.modal-danger .modal-body,.modal-danger .modal-header,.modal-danger .modal-footer{color:#fff !important}.bg-gray{color:#000;background-color:#d2d6de !important}.bg-gray-light{background-color:#f7f7f7}.bg-black{background-color:#111 !important}.bg-red,.callout.callout-danger,.alert-danger,.alert-error,.label-danger,.modal-danger .modal-body{background-color:#dd4b39 !important}.bg-yellow,.callout.callout-warning,.alert-warning,.label-warning,.modal-warning .modal-body{background-color:#f39c12 !important}.bg-aqua,.callout.callout-info,.alert-info,.label-info,.modal-info .modal-body{background-color:#00c0ef !important}.bg-blue{background-color:#0073b7 !important}.bg-light-blue,.label-primary,.modal-primary .modal-body{background-color:#3c8dbc !important}.bg-green,.callout.callout-success,.alert-success,.label-success,.modal-success .modal-body{background-color:#00a65a !important}.bg-navy{background-color:#001f3f !important}.bg-teal{background-color:#39cccc !important}.bg-olive{background-color:#3d9970 !important}.bg-lime{background-color:#01ff70 !important}.bg-orange{background-color:#ff851b !important}.bg-fuchsia{background-color:#f012be !important}.bg-purple{background-color:#605ca8 !important}.bg-maroon{background-color:#d81b60 !important}.bg-gray-active{color:#000;background-color:#b5bbc8 !important}.bg-black-active{background-color:#000 !important}.bg-red-active,.modal-danger .modal-header,.modal-danger .modal-footer{background-color:#d33724 !important}.bg-yellow-active,.modal-warning .modal-header,.modal-warning .modal-footer{background-color:#db8b0b !important}.bg-aqua-active,.modal-info .modal-header,.modal-info .modal-footer{background-color:#00a7d0 !important}.bg-blue-active{background-color:#005384 !important}.bg-light-blue-active,.modal-primary .modal-header,.modal-primary .modal-footer{background-color:#357ca5 !important}.bg-green-active,.modal-success .modal-header,.modal-success .modal-footer{background-color:#008d4c !important}.bg-navy-active{background-color:#001a35 !important}.bg-teal-active{background-color:#30bbbb !important}.bg-olive-active{background-color:#368763 !important}.bg-lime-active{background-color:#00e765 !important}.bg-orange-active{background-color:#ff7701 !important}.bg-fuchsia-active{background-color:#db0ead !important}.bg-purple-active{background-color:#555299 !important}.bg-maroon-active{background-color:#ca195a !important}[class^="bg-"].disabled{opacity:.65;filter:alpha(opacity=65)}.text-red{color:#dd4b39 !important}.text-yellow{color:#f39c12 !important}.text-aqua{color:#00c0ef !important}.text-blue{color:#0073b7 !important}.text-black{color:#111 !important}.text-light-blue{color:#3c8dbc !important}.text-green{color:#00a65a !important}.text-gray{color:#d2d6de !important}.text-navy{color:#001f3f !important}.text-teal{color:#39cccc !important}.text-olive{color:#3d9970 !important}.text-lime{color:#01ff70 !important}.text-orange{color:#ff851b !important}.text-fuchsia{color:#f012be !important}.text-purple{color:#605ca8 !important}.text-maroon{color:#d81b60 !important}.link-muted{color:#7a869d}.link-muted:hover,.link-muted:focus{color:#606c84}.link-black{color:#666}.link-black:hover,.link-black:focus{color:#999}.hide{display:none !important}.no-border{border:0 !important}.no-padding{padding:0 !important}.no-margin{margin:0 !important}.no-shadow{box-shadow:none!important}.list-unstyled,.chart-legend,.contacts-list,.users-list,.mailbox-attachments{list-style:none;margin:0;padding:0}.list-group-unbordered>.list-group-item{border-left:0;border-right:0;border-radius:0;padding-left:0;padding-right:0}.flat{border-radius:0 !important}.text-bold,.text-bold.table td,.text-bold.table th{font-weight:700}.text-sm{font-size:12px}.jqstooltip{padding:5px!important;width:auto!important;height:auto!important}.bg-teal-gradient{background:#39cccc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #39cccc), color-stop(1, #7adddd)) !important;background:-ms-linear-gradient(bottom, #39cccc, #7adddd) !important;background:-moz-linear-gradient(center bottom, #39cccc 0, #7adddd 100%) !important;background:-o-linear-gradient(#7adddd, #39cccc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7adddd', endColorstr='#39cccc', GradientType=0) !important;color:#fff}.bg-light-blue-gradient{background:#3c8dbc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #3c8dbc), color-stop(1, #67a8ce)) !important;background:-ms-linear-gradient(bottom, #3c8dbc, #67a8ce) !important;background:-moz-linear-gradient(center bottom, #3c8dbc 0, #67a8ce 100%) !important;background:-o-linear-gradient(#67a8ce, #3c8dbc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#67a8ce', endColorstr='#3c8dbc', GradientType=0) !important;color:#fff}.bg-blue-gradient{background:#0073b7 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #0073b7), color-stop(1, #0089db)) !important;background:-ms-linear-gradient(bottom, #0073b7, #0089db) !important;background:-moz-linear-gradient(center bottom, #0073b7 0, #0089db 100%) !important;background:-o-linear-gradient(#0089db, #0073b7) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0089db', endColorstr='#0073b7', GradientType=0) !important;color:#fff}.bg-aqua-gradient{background:#00c0ef !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00c0ef), color-stop(1, #14d1ff)) !important;background:-ms-linear-gradient(bottom, #00c0ef, #14d1ff) !important;background:-moz-linear-gradient(center bottom, #00c0ef 0, #14d1ff 100%) !important;background:-o-linear-gradient(#14d1ff, #00c0ef) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#14d1ff', endColorstr='#00c0ef', GradientType=0) !important;color:#fff}.bg-yellow-gradient{background:#f39c12 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #f39c12), color-stop(1, #f7bc60)) !important;background:-ms-linear-gradient(bottom, #f39c12, #f7bc60) !important;background:-moz-linear-gradient(center bottom, #f39c12 0, #f7bc60 100%) !important;background:-o-linear-gradient(#f7bc60, #f39c12) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7bc60', endColorstr='#f39c12', GradientType=0) !important;color:#fff}.bg-purple-gradient{background:#605ca8 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #605ca8), color-stop(1, #9491c4)) !important;background:-ms-linear-gradient(bottom, #605ca8, #9491c4) !important;background:-moz-linear-gradient(center bottom, #605ca8 0, #9491c4 100%) !important;background:-o-linear-gradient(#9491c4, #605ca8) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9491c4', endColorstr='#605ca8', GradientType=0) !important;color:#fff}.bg-green-gradient{background:#00a65a !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00a65a), color-stop(1, #00ca6d)) !important;background:-ms-linear-gradient(bottom, #00a65a, #00ca6d) !important;background:-moz-linear-gradient(center bottom, #00a65a 0, #00ca6d 100%) !important;background:-o-linear-gradient(#00ca6d, #00a65a) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ca6d', endColorstr='#00a65a', GradientType=0) !important;color:#fff}.bg-red-gradient{background:#dd4b39 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #dd4b39), color-stop(1, #e47365)) !important;background:-ms-linear-gradient(bottom, #dd4b39, #e47365) !important;background:-moz-linear-gradient(center bottom, #dd4b39 0, #e47365 100%) !important;background:-o-linear-gradient(#e47365, #dd4b39) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e47365', endColorstr='#dd4b39', GradientType=0) !important;color:#fff}.bg-black-gradient{background:#111 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #111), color-stop(1, #2b2b2b)) !important;background:-ms-linear-gradient(bottom, #111, #2b2b2b) !important;background:-moz-linear-gradient(center bottom, #111 0, #2b2b2b 100%) !important;background:-o-linear-gradient(#2b2b2b, #111) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2b2b2b', endColorstr='#111111', GradientType=0) !important;color:#fff}.bg-maroon-gradient{background:#d81b60 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #d81b60), color-stop(1, #e73f7c)) !important;background:-ms-linear-gradient(bottom, #d81b60, #e73f7c) !important;background:-moz-linear-gradient(center bottom, #d81b60 0, #e73f7c 100%) !important;background:-o-linear-gradient(#e73f7c, #d81b60) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e73f7c', endColorstr='#d81b60', GradientType=0) !important;color:#fff}.description-block .description-icon{font-size:16px}.no-pad-top{padding-top:0}.position-static{position:static!important}.list-header{font-size:15px;padding:10px 4px;font-weight:bold;color:#666}.list-seperator{height:1px;background:#f4f4f4;margin:15px 0 9px 0}.list-link>a{padding:4px;color:#777}.list-link>a:hover{color:#222}.font-light{font-weight:300}.user-block:before,.user-block:after{content:" ";display:table}.user-block:after{clear:both}.user-block img{width:40px;height:40px;float:left}.user-block .username,.user-block .description,.user-block .comment{display:block;margin-left:50px}.user-block .username{font-size:16px;font-weight:600}.user-block .description{color:#999;font-size:13px}.user-block.user-block-sm .username,.user-block.user-block-sm .description,.user-block.user-block-sm .comment{margin-left:40px}.user-block.user-block-sm .username{font-size:14px}.img-sm,.img-md,.img-lg,.box-comments .box-comment img,.user-block.user-block-sm img{float:left}.img-sm,.box-comments .box-comment img,.user-block.user-block-sm img{width:30px!important;height:30px!important}.img-sm+.img-push{margin-left:40px}.img-md{width:60px;height:60px}.img-md+.img-push{margin-left:70px}.img-lg{width:100px;height:100px}.img-lg+.img-push{margin-left:110px}.img-bordered{border:3px solid #d2d6de;padding:3px}.img-bordered-sm{border:2px solid #d2d6de;padding:2px}.attachment-block{border:1px solid #f4f4f4;padding:5px;margin-bottom:10px;background:#f7f7f7}.attachment-block .attachment-img{max-width:100px;max-height:100px;height:auto;float:left}.attachment-block .attachment-pushed{margin-left:110px}.attachment-block .attachment-heading{margin:0}.attachment-block .attachment-text{color:#555}.connectedSortable{min-height:100px}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sort-highlight{background:#f4f4f4;border:1px dashed #ddd;margin-bottom:10px}.full-opacity-hover{opacity:.65;filter:alpha(opacity=65)}.full-opacity-hover:hover{opacity:1;filter:alpha(opacity=100)}.chart{position:relative;overflow:hidden;width:100%}.chart svg,.chart canvas{width:100%!important}@media print{.no-print,.main-sidebar,.left-side,.main-header,.content-header{display:none!important}.content-wrapper,.right-side,.main-footer{margin-left:0!important;min-height:0!important;-webkit-transform:translate(0, 0) !important;-ms-transform:translate(0, 0) !important;-o-transform:translate(0, 0) !important;transform:translate(0, 0) !important}.fixed .content-wrapper,.fixed .right-side{padding-top:0!important}.invoice{width:100%;border:0;margin:0;padding:0}.invoice-col{float:left;width:33.3333333%}.table-responsive{overflow:auto}.table-responsive>.table tr th,.table-responsive>.table tr td{white-space:normal!important}} \ No newline at end of file diff --git a/LinuxGUI/WebUI/static/AdminLTE/css/skins/skin-blue.min.css b/LinuxGUI/WebUI/static/AdminLTE/css/skins/skin-blue.min.css new file mode 100644 index 00000000..123c04f4 --- /dev/null +++ b/LinuxGUI/WebUI/static/AdminLTE/css/skins/skin-blue.min.css @@ -0,0 +1 @@ +.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header .logo{background-color:#367fa9;color:#fff;border-bottom:0 solid transparent}.skin-blue .main-header .logo:hover{background-color:#357ca5}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .wrapper,.skin-blue .main-sidebar,.skin-blue .left-side{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li:hover>a,.skin-blue .sidebar-menu>li.active>a{color:#fff;background:#1e282c;border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .treeview-menu>li>a{color:#8aa4af}.skin-blue .treeview-menu>li.active>a,.skin-blue .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-blue .sidebar-form input[type="text"],.skin-blue .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type="text"]:focus,.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8} \ No newline at end of file diff --git a/LinuxGUI/WebUI/static/AdminLTE/js/app.min.js b/LinuxGUI/WebUI/static/AdminLTE/js/app.min.js new file mode 100644 index 00000000..679f18f1 --- /dev/null +++ b/LinuxGUI/WebUI/static/AdminLTE/js/app.min.js @@ -0,0 +1,13 @@ +/*! AdminLTE app.js + * ================ + * Main JS application file for AdminLTE v2. This file + * should be included in all pages. It controls some layout + * options and implements exclusive AdminLTE plugins. + * + * @Author Almsaeed Studio + * @Support + * @Email + * @version 2.3.0 + * @license MIT + */ +function _init(){"use strict";$.AdminLTE.layout={activate:function(){var a=this;a.fix(),a.fixSidebar(),$(window,".wrapper").resize(function(){a.fix(),a.fixSidebar()})},fix:function(){var a=$(".main-header").outerHeight()+$(".main-footer").outerHeight(),b=$(window).height(),c=$(".sidebar").height();if($("body").hasClass("fixed"))$(".content-wrapper, .right-side").css("min-height",b-$(".main-footer").outerHeight());else{var d;b>=c?($(".content-wrapper, .right-side").css("min-height",b-a),d=b-a):($(".content-wrapper, .right-side").css("min-height",c),d=c);var e=$($.AdminLTE.options.controlSidebarOptions.selector);"undefined"!=typeof e&&e.height()>d&&$(".content-wrapper, .right-side").css("min-height",e.height())}},fixSidebar:function(){return $("body").hasClass("fixed")?("undefined"==typeof $.fn.slimScroll&&window.console&&window.console.error("Error: the fixed layout requires the slimscroll plugin!"),void($.AdminLTE.options.sidebarSlimScroll&&"undefined"!=typeof $.fn.slimScroll&&($(".sidebar").slimScroll({destroy:!0}).height("auto"),$(".sidebar").slimscroll({height:$(window).height()-$(".main-header").height()+"px",color:"rgba(0,0,0,0.2)",size:"3px"})))):void("undefined"!=typeof $.fn.slimScroll&&$(".sidebar").slimScroll({destroy:!0}).height("auto"))}},$.AdminLTE.pushMenu={activate:function(a){var b=$.AdminLTE.options.screenSizes;$(a).on("click",function(a){a.preventDefault(),$(window).width()>b.sm-1?$("body").hasClass("sidebar-collapse")?$("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu"):$("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").hasClass("sidebar-open")?$("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").addClass("sidebar-open").trigger("expanded.pushMenu")}),$(".content-wrapper").click(function(){$(window).width()<=b.sm-1&&$("body").hasClass("sidebar-open")&&$("body").removeClass("sidebar-open")}),($.AdminLTE.options.sidebarExpandOnHover||$("body").hasClass("fixed")&&$("body").hasClass("sidebar-mini"))&&this.expandOnHover()},expandOnHover:function(){var a=this,b=$.AdminLTE.options.screenSizes.sm-1;$(".main-sidebar").hover(function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-collapse")&&$(window).width()>b&&a.expand()},function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-expanded-on-hover")&&$(window).width()>b&&a.collapse()})},expand:function(){$("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")},collapse:function(){$("body").hasClass("sidebar-expanded-on-hover")&&$("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")}},$.AdminLTE.tree=function(a){var b=this,c=$.AdminLTE.options.animationSpeed;$(document).on("click",a+" li a",function(a){var d=$(this),e=d.next();if(e.is(".treeview-menu")&&e.is(":visible"))e.slideUp(c,function(){e.removeClass("menu-open")}),e.parent("li").removeClass("active");else if(e.is(".treeview-menu")&&!e.is(":visible")){var f=d.parents("ul").first(),g=f.find("ul:visible").slideUp(c);g.removeClass("menu-open");var h=d.parent("li");e.slideDown(c,function(){e.addClass("menu-open"),f.find("li.active").removeClass("active"),h.addClass("active"),b.layout.fix()})}e.is(".treeview-menu")&&a.preventDefault()})},$.AdminLTE.controlSidebar={activate:function(){var a=this,b=$.AdminLTE.options.controlSidebarOptions,c=$(b.selector),d=$(b.toggleBtnSelector);d.on("click",function(d){d.preventDefault(),c.hasClass("control-sidebar-open")||$("body").hasClass("control-sidebar-open")?a.close(c,b.slide):a.open(c,b.slide)});var e=$(".control-sidebar-bg");a._fix(e),$("body").hasClass("fixed")?a._fixForFixed(c):$(".content-wrapper, .right-side").height() .box-body, > .box-footer, > form >.box-body, > form > .box-footer");c.hasClass("collapsed-box")?(a.children(":first").removeClass(b.icons.open).addClass(b.icons.collapse),d.slideDown(b.animationSpeed,function(){c.removeClass("collapsed-box")})):(a.children(":first").removeClass(b.icons.collapse).addClass(b.icons.open),d.slideUp(b.animationSpeed,function(){c.addClass("collapsed-box")}))},remove:function(a){var b=a.parents(".box").first();b.slideUp(this.animationSpeed)}}}if("undefined"==typeof jQuery)throw new Error("AdminLTE requires jQuery");$.AdminLTE={},$.AdminLTE.options={navbarMenuSlimscroll:!0,navbarMenuSlimscrollWidth:"3px",navbarMenuHeight:"200px",animationSpeed:500,sidebarToggleSelector:"[data-toggle='offcanvas']",sidebarPushMenu:!0,sidebarSlimScroll:!0,sidebarExpandOnHover:!1,enableBoxRefresh:!0,enableBSToppltip:!0,BSTooltipSelector:"[data-toggle='tooltip']",enableFastclick:!0,enableControlSidebar:!0,controlSidebarOptions:{toggleBtnSelector:"[data-toggle='control-sidebar']",selector:".control-sidebar",slide:!0},enableBoxWidget:!0,boxWidgetOptions:{boxWidgetIcons:{collapse:"fa-minus",open:"fa-plus",remove:"fa-times"},boxWidgetSelectors:{remove:'[data-widget="remove"]',collapse:'[data-widget="collapse"]'}},directChat:{enable:!0,contactToggleSelector:'[data-widget="chat-pane-toggle"]'},colors:{lightBlue:"#3c8dbc",red:"#f56954",green:"#00a65a",aqua:"#00c0ef",yellow:"#f39c12",blue:"#0073b7",navy:"#001F3F",teal:"#39CCCC",olive:"#3D9970",lime:"#01FF70",orange:"#FF851B",fuchsia:"#F012BE",purple:"#8E24AA",maroon:"#D81B60",black:"#222222",gray:"#d2d6de"},screenSizes:{xs:480,sm:768,md:992,lg:1200}},$(function(){"use strict";$("body").removeClass("hold-transition"),"undefined"!=typeof AdminLTEOptions&&$.extend(!0,$.AdminLTE.options,AdminLTEOptions);var a=$.AdminLTE.options;_init(),$.AdminLTE.layout.activate(),$.AdminLTE.tree(".sidebar"),a.enableControlSidebar&&$.AdminLTE.controlSidebar.activate(),a.navbarMenuSlimscroll&&"undefined"!=typeof $.fn.slimscroll&&$(".navbar .menu").slimscroll({height:a.navbarMenuHeight,alwaysVisible:!1,size:a.navbarMenuSlimscrollWidth}).css("width","100%"),a.sidebarPushMenu&&$.AdminLTE.pushMenu.activate(a.sidebarToggleSelector),a.enableBSToppltip&&$("body").tooltip({selector:a.BSTooltipSelector}),a.enableBoxWidget&&$.AdminLTE.boxWidget.activate(),a.enableFastclick&&"undefined"!=typeof FastClick&&FastClick.attach(document.body),a.directChat.enable&&$(document).on("click",a.directChat.contactToggleSelector,function(){var a=$(this).parents(".direct-chat").first();a.toggleClass("direct-chat-contacts-open")}),$('.btn-group[data-toggle="btn-toggle"]').each(function(){var a=$(this);$(this).find(".btn").on("click",function(b){a.find(".btn.active").removeClass("active"),$(this).addClass("active"),b.preventDefault()})})}),function(a){"use strict";a.fn.boxRefresh=function(b){function c(a){a.append(f),e.onLoadStart.call(a)}function d(a){a.find(f).remove(),e.onLoadDone.call(a)}var e=a.extend({trigger:".refresh-btn",source:"",onLoadStart:function(a){return a},onLoadDone:function(a){return a}},b),f=a('
');return this.each(function(){if(""===e.source)return void(window.console&&window.console.log("Please specify a source first - boxRefresh()"));var b=a(this),f=b.find(e.trigger).first();f.on("click",function(a){a.preventDefault(),c(b),b.find(".box-body").load(e.source,function(){d(b)})})})}}(jQuery),function(a){"use strict";a.fn.activateBox=function(){a.AdminLTE.boxWidget.activate(this)}}(jQuery),function(a){"use strict";a.fn.todolist=function(b){var c=a.extend({onCheck:function(a){return a},onUncheck:function(a){return a}},b);return this.each(function(){"undefined"!=typeof a.fn.iCheck?(a("input",this).on("ifChecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onCheck.call(b)}),a("input",this).on("ifUnchecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onUncheck.call(b)})):a("input",this).on("change",function(){var b=a(this).parents("li").first();b.toggleClass("done"),a("input",b).is(":checked")?c.onCheck.call(b):c.onUncheck.call(b)})})}}(jQuery); \ No newline at end of file diff --git a/LinuxGUI/WebUI/static/bootstrap/css/bootstrap-theme.min.css b/LinuxGUI/WebUI/static/bootstrap/css/bootstrap-theme.min.css new file mode 100644 index 00000000..61358b13 --- /dev/null +++ b/LinuxGUI/WebUI/static/bootstrap/css/bootstrap-theme.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} \ No newline at end of file diff --git a/LinuxGUI/WebUI/static/bootstrap/css/bootstrap.min.css b/LinuxGUI/WebUI/static/bootstrap/css/bootstrap.min.css new file mode 100644 index 00000000..be11b9ff --- /dev/null +++ b/LinuxGUI/WebUI/static/bootstrap/css/bootstrap.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px;font-size:14px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.eot b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..b93a4953 Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.eot differ diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.svg b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..94fb5490 --- /dev/null +++ b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.ttf b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..1413fc60 Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.ttf differ diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..9e612858 Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff differ diff --git a/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 00000000..64539b54 Binary files /dev/null and b/LinuxGUI/WebUI/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/LinuxGUI/WebUI/static/bootstrap/js/bootstrap.min.js b/LinuxGUI/WebUI/static/bootstrap/js/bootstrap.min.js new file mode 100644 index 00000000..133aeecb --- /dev/null +++ b/LinuxGUI/WebUI/static/bootstrap/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/LinuxGUI/WebUI/static/css/font-awesome.min.css b/LinuxGUI/WebUI/static/css/font-awesome.min.css new file mode 100644 index 00000000..aae88c41 --- /dev/null +++ b/LinuxGUI/WebUI/static/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"} diff --git a/LinuxGUI/WebUI/static/css/ionicons.min.css b/LinuxGUI/WebUI/static/css/ionicons.min.css new file mode 100644 index 00000000..e4bd1ef2 --- /dev/null +++ b/LinuxGUI/WebUI/static/css/ionicons.min.css @@ -0,0 +1,11 @@ +@charset "UTF-8";/*! + Ionicons, v2.0.1 + Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ + https://twitter.com/benjsperry https://twitter.com/ionicframework + MIT License: https://github.com/driftyco/ionicons + + Android-style icons originally built by Google’s + Material Design Icons: https://github.com/google/material-design-icons + used under CC BY http://creativecommons.org/licenses/by/4.0/ + Modified icons to fit ionicon’s grid from original. +*/@font-face{font-family:"Ionicons";src:url("../fonts/ionicons.eot?v=2.0.1");src:url("../fonts/ionicons.eot?v=2.0.1#iefix") format("embedded-opentype"),url("../fonts/ionicons.ttf?v=2.0.1") format("truetype"),url("../fonts/ionicons.woff?v=2.0.1") format("woff"),url("../fonts/ionicons.svg?v=2.0.1#Ionicons") format("svg");font-weight:normal;font-style:normal}.ion,.ionicons,.ion-alert:before,.ion-alert-circled:before,.ion-android-add:before,.ion-android-add-circle:before,.ion-android-alarm-clock:before,.ion-android-alert:before,.ion-android-apps:before,.ion-android-archive:before,.ion-android-arrow-back:before,.ion-android-arrow-down:before,.ion-android-arrow-dropdown:before,.ion-android-arrow-dropdown-circle:before,.ion-android-arrow-dropleft:before,.ion-android-arrow-dropleft-circle:before,.ion-android-arrow-dropright:before,.ion-android-arrow-dropright-circle:before,.ion-android-arrow-dropup:before,.ion-android-arrow-dropup-circle:before,.ion-android-arrow-forward:before,.ion-android-arrow-up:before,.ion-android-attach:before,.ion-android-bar:before,.ion-android-bicycle:before,.ion-android-boat:before,.ion-android-bookmark:before,.ion-android-bulb:before,.ion-android-bus:before,.ion-android-calendar:before,.ion-android-call:before,.ion-android-camera:before,.ion-android-cancel:before,.ion-android-car:before,.ion-android-cart:before,.ion-android-chat:before,.ion-android-checkbox:before,.ion-android-checkbox-blank:before,.ion-android-checkbox-outline:before,.ion-android-checkbox-outline-blank:before,.ion-android-checkmark-circle:before,.ion-android-clipboard:before,.ion-android-close:before,.ion-android-cloud:before,.ion-android-cloud-circle:before,.ion-android-cloud-done:before,.ion-android-cloud-outline:before,.ion-android-color-palette:before,.ion-android-compass:before,.ion-android-contact:before,.ion-android-contacts:before,.ion-android-contract:before,.ion-android-create:before,.ion-android-delete:before,.ion-android-desktop:before,.ion-android-document:before,.ion-android-done:before,.ion-android-done-all:before,.ion-android-download:before,.ion-android-drafts:before,.ion-android-exit:before,.ion-android-expand:before,.ion-android-favorite:before,.ion-android-favorite-outline:before,.ion-android-film:before,.ion-android-folder:before,.ion-android-folder-open:before,.ion-android-funnel:before,.ion-android-globe:before,.ion-android-hand:before,.ion-android-hangout:before,.ion-android-happy:before,.ion-android-home:before,.ion-android-image:before,.ion-android-laptop:before,.ion-android-list:before,.ion-android-locate:before,.ion-android-lock:before,.ion-android-mail:before,.ion-android-map:before,.ion-android-menu:before,.ion-android-microphone:before,.ion-android-microphone-off:before,.ion-android-more-horizontal:before,.ion-android-more-vertical:before,.ion-android-navigate:before,.ion-android-notifications:before,.ion-android-notifications-none:before,.ion-android-notifications-off:before,.ion-android-open:before,.ion-android-options:before,.ion-android-people:before,.ion-android-person:before,.ion-android-person-add:before,.ion-android-phone-landscape:before,.ion-android-phone-portrait:before,.ion-android-pin:before,.ion-android-plane:before,.ion-android-playstore:before,.ion-android-print:before,.ion-android-radio-button-off:before,.ion-android-radio-button-on:before,.ion-android-refresh:before,.ion-android-remove:before,.ion-android-remove-circle:before,.ion-android-restaurant:before,.ion-android-sad:before,.ion-android-search:before,.ion-android-send:before,.ion-android-settings:before,.ion-android-share:before,.ion-android-share-alt:before,.ion-android-star:before,.ion-android-star-half:before,.ion-android-star-outline:before,.ion-android-stopwatch:before,.ion-android-subway:before,.ion-android-sunny:before,.ion-android-sync:before,.ion-android-textsms:before,.ion-android-time:before,.ion-android-train:before,.ion-android-unlock:before,.ion-android-upload:before,.ion-android-volume-down:before,.ion-android-volume-mute:before,.ion-android-volume-off:before,.ion-android-volume-up:before,.ion-android-walk:before,.ion-android-warning:before,.ion-android-watch:before,.ion-android-wifi:before,.ion-aperture:before,.ion-archive:before,.ion-arrow-down-a:before,.ion-arrow-down-b:before,.ion-arrow-down-c:before,.ion-arrow-expand:before,.ion-arrow-graph-down-left:before,.ion-arrow-graph-down-right:before,.ion-arrow-graph-up-left:before,.ion-arrow-graph-up-right:before,.ion-arrow-left-a:before,.ion-arrow-left-b:before,.ion-arrow-left-c:before,.ion-arrow-move:before,.ion-arrow-resize:before,.ion-arrow-return-left:before,.ion-arrow-return-right:before,.ion-arrow-right-a:before,.ion-arrow-right-b:before,.ion-arrow-right-c:before,.ion-arrow-shrink:before,.ion-arrow-swap:before,.ion-arrow-up-a:before,.ion-arrow-up-b:before,.ion-arrow-up-c:before,.ion-asterisk:before,.ion-at:before,.ion-backspace:before,.ion-backspace-outline:before,.ion-bag:before,.ion-battery-charging:before,.ion-battery-empty:before,.ion-battery-full:before,.ion-battery-half:before,.ion-battery-low:before,.ion-beaker:before,.ion-beer:before,.ion-bluetooth:before,.ion-bonfire:before,.ion-bookmark:before,.ion-bowtie:before,.ion-briefcase:before,.ion-bug:before,.ion-calculator:before,.ion-calendar:before,.ion-camera:before,.ion-card:before,.ion-cash:before,.ion-chatbox:before,.ion-chatbox-working:before,.ion-chatboxes:before,.ion-chatbubble:before,.ion-chatbubble-working:before,.ion-chatbubbles:before,.ion-checkmark:before,.ion-checkmark-circled:before,.ion-checkmark-round:before,.ion-chevron-down:before,.ion-chevron-left:before,.ion-chevron-right:before,.ion-chevron-up:before,.ion-clipboard:before,.ion-clock:before,.ion-close:before,.ion-close-circled:before,.ion-close-round:before,.ion-closed-captioning:before,.ion-cloud:before,.ion-code:before,.ion-code-download:before,.ion-code-working:before,.ion-coffee:before,.ion-compass:before,.ion-compose:before,.ion-connection-bars:before,.ion-contrast:before,.ion-crop:before,.ion-cube:before,.ion-disc:before,.ion-document:before,.ion-document-text:before,.ion-drag:before,.ion-earth:before,.ion-easel:before,.ion-edit:before,.ion-egg:before,.ion-eject:before,.ion-email:before,.ion-email-unread:before,.ion-erlenmeyer-flask:before,.ion-erlenmeyer-flask-bubbles:before,.ion-eye:before,.ion-eye-disabled:before,.ion-female:before,.ion-filing:before,.ion-film-marker:before,.ion-fireball:before,.ion-flag:before,.ion-flame:before,.ion-flash:before,.ion-flash-off:before,.ion-folder:before,.ion-fork:before,.ion-fork-repo:before,.ion-forward:before,.ion-funnel:before,.ion-gear-a:before,.ion-gear-b:before,.ion-grid:before,.ion-hammer:before,.ion-happy:before,.ion-happy-outline:before,.ion-headphone:before,.ion-heart:before,.ion-heart-broken:before,.ion-help:before,.ion-help-buoy:before,.ion-help-circled:before,.ion-home:before,.ion-icecream:before,.ion-image:before,.ion-images:before,.ion-information:before,.ion-information-circled:before,.ion-ionic:before,.ion-ios-alarm:before,.ion-ios-alarm-outline:before,.ion-ios-albums:before,.ion-ios-albums-outline:before,.ion-ios-americanfootball:before,.ion-ios-americanfootball-outline:before,.ion-ios-analytics:before,.ion-ios-analytics-outline:before,.ion-ios-arrow-back:before,.ion-ios-arrow-down:before,.ion-ios-arrow-forward:before,.ion-ios-arrow-left:before,.ion-ios-arrow-right:before,.ion-ios-arrow-thin-down:before,.ion-ios-arrow-thin-left:before,.ion-ios-arrow-thin-right:before,.ion-ios-arrow-thin-up:before,.ion-ios-arrow-up:before,.ion-ios-at:before,.ion-ios-at-outline:before,.ion-ios-barcode:before,.ion-ios-barcode-outline:before,.ion-ios-baseball:before,.ion-ios-baseball-outline:before,.ion-ios-basketball:before,.ion-ios-basketball-outline:before,.ion-ios-bell:before,.ion-ios-bell-outline:before,.ion-ios-body:before,.ion-ios-body-outline:before,.ion-ios-bolt:before,.ion-ios-bolt-outline:before,.ion-ios-book:before,.ion-ios-book-outline:before,.ion-ios-bookmarks:before,.ion-ios-bookmarks-outline:before,.ion-ios-box:before,.ion-ios-box-outline:before,.ion-ios-briefcase:before,.ion-ios-briefcase-outline:before,.ion-ios-browsers:before,.ion-ios-browsers-outline:before,.ion-ios-calculator:before,.ion-ios-calculator-outline:before,.ion-ios-calendar:before,.ion-ios-calendar-outline:before,.ion-ios-camera:before,.ion-ios-camera-outline:before,.ion-ios-cart:before,.ion-ios-cart-outline:before,.ion-ios-chatboxes:before,.ion-ios-chatboxes-outline:before,.ion-ios-chatbubble:before,.ion-ios-chatbubble-outline:before,.ion-ios-checkmark:before,.ion-ios-checkmark-empty:before,.ion-ios-checkmark-outline:before,.ion-ios-circle-filled:before,.ion-ios-circle-outline:before,.ion-ios-clock:before,.ion-ios-clock-outline:before,.ion-ios-close:before,.ion-ios-close-empty:before,.ion-ios-close-outline:before,.ion-ios-cloud:before,.ion-ios-cloud-download:before,.ion-ios-cloud-download-outline:before,.ion-ios-cloud-outline:before,.ion-ios-cloud-upload:before,.ion-ios-cloud-upload-outline:before,.ion-ios-cloudy:before,.ion-ios-cloudy-night:before,.ion-ios-cloudy-night-outline:before,.ion-ios-cloudy-outline:before,.ion-ios-cog:before,.ion-ios-cog-outline:before,.ion-ios-color-filter:before,.ion-ios-color-filter-outline:before,.ion-ios-color-wand:before,.ion-ios-color-wand-outline:before,.ion-ios-compose:before,.ion-ios-compose-outline:before,.ion-ios-contact:before,.ion-ios-contact-outline:before,.ion-ios-copy:before,.ion-ios-copy-outline:before,.ion-ios-crop:before,.ion-ios-crop-strong:before,.ion-ios-download:before,.ion-ios-download-outline:before,.ion-ios-drag:before,.ion-ios-email:before,.ion-ios-email-outline:before,.ion-ios-eye:before,.ion-ios-eye-outline:before,.ion-ios-fastforward:before,.ion-ios-fastforward-outline:before,.ion-ios-filing:before,.ion-ios-filing-outline:before,.ion-ios-film:before,.ion-ios-film-outline:before,.ion-ios-flag:before,.ion-ios-flag-outline:before,.ion-ios-flame:before,.ion-ios-flame-outline:before,.ion-ios-flask:before,.ion-ios-flask-outline:before,.ion-ios-flower:before,.ion-ios-flower-outline:before,.ion-ios-folder:before,.ion-ios-folder-outline:before,.ion-ios-football:before,.ion-ios-football-outline:before,.ion-ios-game-controller-a:before,.ion-ios-game-controller-a-outline:before,.ion-ios-game-controller-b:before,.ion-ios-game-controller-b-outline:before,.ion-ios-gear:before,.ion-ios-gear-outline:before,.ion-ios-glasses:before,.ion-ios-glasses-outline:before,.ion-ios-grid-view:before,.ion-ios-grid-view-outline:before,.ion-ios-heart:before,.ion-ios-heart-outline:before,.ion-ios-help:before,.ion-ios-help-empty:before,.ion-ios-help-outline:before,.ion-ios-home:before,.ion-ios-home-outline:before,.ion-ios-infinite:before,.ion-ios-infinite-outline:before,.ion-ios-information:before,.ion-ios-information-empty:before,.ion-ios-information-outline:before,.ion-ios-ionic-outline:before,.ion-ios-keypad:before,.ion-ios-keypad-outline:before,.ion-ios-lightbulb:before,.ion-ios-lightbulb-outline:before,.ion-ios-list:before,.ion-ios-list-outline:before,.ion-ios-location:before,.ion-ios-location-outline:before,.ion-ios-locked:before,.ion-ios-locked-outline:before,.ion-ios-loop:before,.ion-ios-loop-strong:before,.ion-ios-medical:before,.ion-ios-medical-outline:before,.ion-ios-medkit:before,.ion-ios-medkit-outline:before,.ion-ios-mic:before,.ion-ios-mic-off:before,.ion-ios-mic-outline:before,.ion-ios-minus:before,.ion-ios-minus-empty:before,.ion-ios-minus-outline:before,.ion-ios-monitor:before,.ion-ios-monitor-outline:before,.ion-ios-moon:before,.ion-ios-moon-outline:before,.ion-ios-more:before,.ion-ios-more-outline:before,.ion-ios-musical-note:before,.ion-ios-musical-notes:before,.ion-ios-navigate:before,.ion-ios-navigate-outline:before,.ion-ios-nutrition:before,.ion-ios-nutrition-outline:before,.ion-ios-paper:before,.ion-ios-paper-outline:before,.ion-ios-paperplane:before,.ion-ios-paperplane-outline:before,.ion-ios-partlysunny:before,.ion-ios-partlysunny-outline:before,.ion-ios-pause:before,.ion-ios-pause-outline:before,.ion-ios-paw:before,.ion-ios-paw-outline:before,.ion-ios-people:before,.ion-ios-people-outline:before,.ion-ios-person:before,.ion-ios-person-outline:before,.ion-ios-personadd:before,.ion-ios-personadd-outline:before,.ion-ios-photos:before,.ion-ios-photos-outline:before,.ion-ios-pie:before,.ion-ios-pie-outline:before,.ion-ios-pint:before,.ion-ios-pint-outline:before,.ion-ios-play:before,.ion-ios-play-outline:before,.ion-ios-plus:before,.ion-ios-plus-empty:before,.ion-ios-plus-outline:before,.ion-ios-pricetag:before,.ion-ios-pricetag-outline:before,.ion-ios-pricetags:before,.ion-ios-pricetags-outline:before,.ion-ios-printer:before,.ion-ios-printer-outline:before,.ion-ios-pulse:before,.ion-ios-pulse-strong:before,.ion-ios-rainy:before,.ion-ios-rainy-outline:before,.ion-ios-recording:before,.ion-ios-recording-outline:before,.ion-ios-redo:before,.ion-ios-redo-outline:before,.ion-ios-refresh:before,.ion-ios-refresh-empty:before,.ion-ios-refresh-outline:before,.ion-ios-reload:before,.ion-ios-reverse-camera:before,.ion-ios-reverse-camera-outline:before,.ion-ios-rewind:before,.ion-ios-rewind-outline:before,.ion-ios-rose:before,.ion-ios-rose-outline:before,.ion-ios-search:before,.ion-ios-search-strong:before,.ion-ios-settings:before,.ion-ios-settings-strong:before,.ion-ios-shuffle:before,.ion-ios-shuffle-strong:before,.ion-ios-skipbackward:before,.ion-ios-skipbackward-outline:before,.ion-ios-skipforward:before,.ion-ios-skipforward-outline:before,.ion-ios-snowy:before,.ion-ios-speedometer:before,.ion-ios-speedometer-outline:before,.ion-ios-star:before,.ion-ios-star-half:before,.ion-ios-star-outline:before,.ion-ios-stopwatch:before,.ion-ios-stopwatch-outline:before,.ion-ios-sunny:before,.ion-ios-sunny-outline:before,.ion-ios-telephone:before,.ion-ios-telephone-outline:before,.ion-ios-tennisball:before,.ion-ios-tennisball-outline:before,.ion-ios-thunderstorm:before,.ion-ios-thunderstorm-outline:before,.ion-ios-time:before,.ion-ios-time-outline:before,.ion-ios-timer:before,.ion-ios-timer-outline:before,.ion-ios-toggle:before,.ion-ios-toggle-outline:before,.ion-ios-trash:before,.ion-ios-trash-outline:before,.ion-ios-undo:before,.ion-ios-undo-outline:before,.ion-ios-unlocked:before,.ion-ios-unlocked-outline:before,.ion-ios-upload:before,.ion-ios-upload-outline:before,.ion-ios-videocam:before,.ion-ios-videocam-outline:before,.ion-ios-volume-high:before,.ion-ios-volume-low:before,.ion-ios-wineglass:before,.ion-ios-wineglass-outline:before,.ion-ios-world:before,.ion-ios-world-outline:before,.ion-ipad:before,.ion-iphone:before,.ion-ipod:before,.ion-jet:before,.ion-key:before,.ion-knife:before,.ion-laptop:before,.ion-leaf:before,.ion-levels:before,.ion-lightbulb:before,.ion-link:before,.ion-load-a:before,.ion-load-b:before,.ion-load-c:before,.ion-load-d:before,.ion-location:before,.ion-lock-combination:before,.ion-locked:before,.ion-log-in:before,.ion-log-out:before,.ion-loop:before,.ion-magnet:before,.ion-male:before,.ion-man:before,.ion-map:before,.ion-medkit:before,.ion-merge:before,.ion-mic-a:before,.ion-mic-b:before,.ion-mic-c:before,.ion-minus:before,.ion-minus-circled:before,.ion-minus-round:before,.ion-model-s:before,.ion-monitor:before,.ion-more:before,.ion-mouse:before,.ion-music-note:before,.ion-navicon:before,.ion-navicon-round:before,.ion-navigate:before,.ion-network:before,.ion-no-smoking:before,.ion-nuclear:before,.ion-outlet:before,.ion-paintbrush:before,.ion-paintbucket:before,.ion-paper-airplane:before,.ion-paperclip:before,.ion-pause:before,.ion-person:before,.ion-person-add:before,.ion-person-stalker:before,.ion-pie-graph:before,.ion-pin:before,.ion-pinpoint:before,.ion-pizza:before,.ion-plane:before,.ion-planet:before,.ion-play:before,.ion-playstation:before,.ion-plus:before,.ion-plus-circled:before,.ion-plus-round:before,.ion-podium:before,.ion-pound:before,.ion-power:before,.ion-pricetag:before,.ion-pricetags:before,.ion-printer:before,.ion-pull-request:before,.ion-qr-scanner:before,.ion-quote:before,.ion-radio-waves:before,.ion-record:before,.ion-refresh:before,.ion-reply:before,.ion-reply-all:before,.ion-ribbon-a:before,.ion-ribbon-b:before,.ion-sad:before,.ion-sad-outline:before,.ion-scissors:before,.ion-search:before,.ion-settings:before,.ion-share:before,.ion-shuffle:before,.ion-skip-backward:before,.ion-skip-forward:before,.ion-social-android:before,.ion-social-android-outline:before,.ion-social-angular:before,.ion-social-angular-outline:before,.ion-social-apple:before,.ion-social-apple-outline:before,.ion-social-bitcoin:before,.ion-social-bitcoin-outline:before,.ion-social-buffer:before,.ion-social-buffer-outline:before,.ion-social-chrome:before,.ion-social-chrome-outline:before,.ion-social-codepen:before,.ion-social-codepen-outline:before,.ion-social-css3:before,.ion-social-css3-outline:before,.ion-social-designernews:before,.ion-social-designernews-outline:before,.ion-social-dribbble:before,.ion-social-dribbble-outline:before,.ion-social-dropbox:before,.ion-social-dropbox-outline:before,.ion-social-euro:before,.ion-social-euro-outline:before,.ion-social-facebook:before,.ion-social-facebook-outline:before,.ion-social-foursquare:before,.ion-social-foursquare-outline:before,.ion-social-freebsd-devil:before,.ion-social-github:before,.ion-social-github-outline:before,.ion-social-google:before,.ion-social-google-outline:before,.ion-social-googleplus:before,.ion-social-googleplus-outline:before,.ion-social-hackernews:before,.ion-social-hackernews-outline:before,.ion-social-html5:before,.ion-social-html5-outline:before,.ion-social-instagram:before,.ion-social-instagram-outline:before,.ion-social-javascript:before,.ion-social-javascript-outline:before,.ion-social-linkedin:before,.ion-social-linkedin-outline:before,.ion-social-markdown:before,.ion-social-nodejs:before,.ion-social-octocat:before,.ion-social-pinterest:before,.ion-social-pinterest-outline:before,.ion-social-python:before,.ion-social-reddit:before,.ion-social-reddit-outline:before,.ion-social-rss:before,.ion-social-rss-outline:before,.ion-social-sass:before,.ion-social-skype:before,.ion-social-skype-outline:before,.ion-social-snapchat:before,.ion-social-snapchat-outline:before,.ion-social-tumblr:before,.ion-social-tumblr-outline:before,.ion-social-tux:before,.ion-social-twitch:before,.ion-social-twitch-outline:before,.ion-social-twitter:before,.ion-social-twitter-outline:before,.ion-social-usd:before,.ion-social-usd-outline:before,.ion-social-vimeo:before,.ion-social-vimeo-outline:before,.ion-social-whatsapp:before,.ion-social-whatsapp-outline:before,.ion-social-windows:before,.ion-social-windows-outline:before,.ion-social-wordpress:before,.ion-social-wordpress-outline:before,.ion-social-yahoo:before,.ion-social-yahoo-outline:before,.ion-social-yen:before,.ion-social-yen-outline:before,.ion-social-youtube:before,.ion-social-youtube-outline:before,.ion-soup-can:before,.ion-soup-can-outline:before,.ion-speakerphone:before,.ion-speedometer:before,.ion-spoon:before,.ion-star:before,.ion-stats-bars:before,.ion-steam:before,.ion-stop:before,.ion-thermometer:before,.ion-thumbsdown:before,.ion-thumbsup:before,.ion-toggle:before,.ion-toggle-filled:before,.ion-transgender:before,.ion-trash-a:before,.ion-trash-b:before,.ion-trophy:before,.ion-tshirt:before,.ion-tshirt-outline:before,.ion-umbrella:before,.ion-university:before,.ion-unlocked:before,.ion-upload:before,.ion-usb:before,.ion-videocamera:before,.ion-volume-high:before,.ion-volume-low:before,.ion-volume-medium:before,.ion-volume-mute:before,.ion-wand:before,.ion-waterdrop:before,.ion-wifi:before,.ion-wineglass:before,.ion-woman:before,.ion-wrench:before,.ion-xbox:before{display:inline-block;font-family:"Ionicons";speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;text-rendering:auto;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ion-alert:before{content:"\f101"}.ion-alert-circled:before{content:"\f100"}.ion-android-add:before{content:"\f2c7"}.ion-android-add-circle:before{content:"\f359"}.ion-android-alarm-clock:before{content:"\f35a"}.ion-android-alert:before{content:"\f35b"}.ion-android-apps:before{content:"\f35c"}.ion-android-archive:before{content:"\f2c9"}.ion-android-arrow-back:before{content:"\f2ca"}.ion-android-arrow-down:before{content:"\f35d"}.ion-android-arrow-dropdown:before{content:"\f35f"}.ion-android-arrow-dropdown-circle:before{content:"\f35e"}.ion-android-arrow-dropleft:before{content:"\f361"}.ion-android-arrow-dropleft-circle:before{content:"\f360"}.ion-android-arrow-dropright:before{content:"\f363"}.ion-android-arrow-dropright-circle:before{content:"\f362"}.ion-android-arrow-dropup:before{content:"\f365"}.ion-android-arrow-dropup-circle:before{content:"\f364"}.ion-android-arrow-forward:before{content:"\f30f"}.ion-android-arrow-up:before{content:"\f366"}.ion-android-attach:before{content:"\f367"}.ion-android-bar:before{content:"\f368"}.ion-android-bicycle:before{content:"\f369"}.ion-android-boat:before{content:"\f36a"}.ion-android-bookmark:before{content:"\f36b"}.ion-android-bulb:before{content:"\f36c"}.ion-android-bus:before{content:"\f36d"}.ion-android-calendar:before{content:"\f2d1"}.ion-android-call:before{content:"\f2d2"}.ion-android-camera:before{content:"\f2d3"}.ion-android-cancel:before{content:"\f36e"}.ion-android-car:before{content:"\f36f"}.ion-android-cart:before{content:"\f370"}.ion-android-chat:before{content:"\f2d4"}.ion-android-checkbox:before{content:"\f374"}.ion-android-checkbox-blank:before{content:"\f371"}.ion-android-checkbox-outline:before{content:"\f373"}.ion-android-checkbox-outline-blank:before{content:"\f372"}.ion-android-checkmark-circle:before{content:"\f375"}.ion-android-clipboard:before{content:"\f376"}.ion-android-close:before{content:"\f2d7"}.ion-android-cloud:before{content:"\f37a"}.ion-android-cloud-circle:before{content:"\f377"}.ion-android-cloud-done:before{content:"\f378"}.ion-android-cloud-outline:before{content:"\f379"}.ion-android-color-palette:before{content:"\f37b"}.ion-android-compass:before{content:"\f37c"}.ion-android-contact:before{content:"\f2d8"}.ion-android-contacts:before{content:"\f2d9"}.ion-android-contract:before{content:"\f37d"}.ion-android-create:before{content:"\f37e"}.ion-android-delete:before{content:"\f37f"}.ion-android-desktop:before{content:"\f380"}.ion-android-document:before{content:"\f381"}.ion-android-done:before{content:"\f383"}.ion-android-done-all:before{content:"\f382"}.ion-android-download:before{content:"\f2dd"}.ion-android-drafts:before{content:"\f384"}.ion-android-exit:before{content:"\f385"}.ion-android-expand:before{content:"\f386"}.ion-android-favorite:before{content:"\f388"}.ion-android-favorite-outline:before{content:"\f387"}.ion-android-film:before{content:"\f389"}.ion-android-folder:before{content:"\f2e0"}.ion-android-folder-open:before{content:"\f38a"}.ion-android-funnel:before{content:"\f38b"}.ion-android-globe:before{content:"\f38c"}.ion-android-hand:before{content:"\f2e3"}.ion-android-hangout:before{content:"\f38d"}.ion-android-happy:before{content:"\f38e"}.ion-android-home:before{content:"\f38f"}.ion-android-image:before{content:"\f2e4"}.ion-android-laptop:before{content:"\f390"}.ion-android-list:before{content:"\f391"}.ion-android-locate:before{content:"\f2e9"}.ion-android-lock:before{content:"\f392"}.ion-android-mail:before{content:"\f2eb"}.ion-android-map:before{content:"\f393"}.ion-android-menu:before{content:"\f394"}.ion-android-microphone:before{content:"\f2ec"}.ion-android-microphone-off:before{content:"\f395"}.ion-android-more-horizontal:before{content:"\f396"}.ion-android-more-vertical:before{content:"\f397"}.ion-android-navigate:before{content:"\f398"}.ion-android-notifications:before{content:"\f39b"}.ion-android-notifications-none:before{content:"\f399"}.ion-android-notifications-off:before{content:"\f39a"}.ion-android-open:before{content:"\f39c"}.ion-android-options:before{content:"\f39d"}.ion-android-people:before{content:"\f39e"}.ion-android-person:before{content:"\f3a0"}.ion-android-person-add:before{content:"\f39f"}.ion-android-phone-landscape:before{content:"\f3a1"}.ion-android-phone-portrait:before{content:"\f3a2"}.ion-android-pin:before{content:"\f3a3"}.ion-android-plane:before{content:"\f3a4"}.ion-android-playstore:before{content:"\f2f0"}.ion-android-print:before{content:"\f3a5"}.ion-android-radio-button-off:before{content:"\f3a6"}.ion-android-radio-button-on:before{content:"\f3a7"}.ion-android-refresh:before{content:"\f3a8"}.ion-android-remove:before{content:"\f2f4"}.ion-android-remove-circle:before{content:"\f3a9"}.ion-android-restaurant:before{content:"\f3aa"}.ion-android-sad:before{content:"\f3ab"}.ion-android-search:before{content:"\f2f5"}.ion-android-send:before{content:"\f2f6"}.ion-android-settings:before{content:"\f2f7"}.ion-android-share:before{content:"\f2f8"}.ion-android-share-alt:before{content:"\f3ac"}.ion-android-star:before{content:"\f2fc"}.ion-android-star-half:before{content:"\f3ad"}.ion-android-star-outline:before{content:"\f3ae"}.ion-android-stopwatch:before{content:"\f2fd"}.ion-android-subway:before{content:"\f3af"}.ion-android-sunny:before{content:"\f3b0"}.ion-android-sync:before{content:"\f3b1"}.ion-android-textsms:before{content:"\f3b2"}.ion-android-time:before{content:"\f3b3"}.ion-android-train:before{content:"\f3b4"}.ion-android-unlock:before{content:"\f3b5"}.ion-android-upload:before{content:"\f3b6"}.ion-android-volume-down:before{content:"\f3b7"}.ion-android-volume-mute:before{content:"\f3b8"}.ion-android-volume-off:before{content:"\f3b9"}.ion-android-volume-up:before{content:"\f3ba"}.ion-android-walk:before{content:"\f3bb"}.ion-android-warning:before{content:"\f3bc"}.ion-android-watch:before{content:"\f3bd"}.ion-android-wifi:before{content:"\f305"}.ion-aperture:before{content:"\f313"}.ion-archive:before{content:"\f102"}.ion-arrow-down-a:before{content:"\f103"}.ion-arrow-down-b:before{content:"\f104"}.ion-arrow-down-c:before{content:"\f105"}.ion-arrow-expand:before{content:"\f25e"}.ion-arrow-graph-down-left:before{content:"\f25f"}.ion-arrow-graph-down-right:before{content:"\f260"}.ion-arrow-graph-up-left:before{content:"\f261"}.ion-arrow-graph-up-right:before{content:"\f262"}.ion-arrow-left-a:before{content:"\f106"}.ion-arrow-left-b:before{content:"\f107"}.ion-arrow-left-c:before{content:"\f108"}.ion-arrow-move:before{content:"\f263"}.ion-arrow-resize:before{content:"\f264"}.ion-arrow-return-left:before{content:"\f265"}.ion-arrow-return-right:before{content:"\f266"}.ion-arrow-right-a:before{content:"\f109"}.ion-arrow-right-b:before{content:"\f10a"}.ion-arrow-right-c:before{content:"\f10b"}.ion-arrow-shrink:before{content:"\f267"}.ion-arrow-swap:before{content:"\f268"}.ion-arrow-up-a:before{content:"\f10c"}.ion-arrow-up-b:before{content:"\f10d"}.ion-arrow-up-c:before{content:"\f10e"}.ion-asterisk:before{content:"\f314"}.ion-at:before{content:"\f10f"}.ion-backspace:before{content:"\f3bf"}.ion-backspace-outline:before{content:"\f3be"}.ion-bag:before{content:"\f110"}.ion-battery-charging:before{content:"\f111"}.ion-battery-empty:before{content:"\f112"}.ion-battery-full:before{content:"\f113"}.ion-battery-half:before{content:"\f114"}.ion-battery-low:before{content:"\f115"}.ion-beaker:before{content:"\f269"}.ion-beer:before{content:"\f26a"}.ion-bluetooth:before{content:"\f116"}.ion-bonfire:before{content:"\f315"}.ion-bookmark:before{content:"\f26b"}.ion-bowtie:before{content:"\f3c0"}.ion-briefcase:before{content:"\f26c"}.ion-bug:before{content:"\f2be"}.ion-calculator:before{content:"\f26d"}.ion-calendar:before{content:"\f117"}.ion-camera:before{content:"\f118"}.ion-card:before{content:"\f119"}.ion-cash:before{content:"\f316"}.ion-chatbox:before{content:"\f11b"}.ion-chatbox-working:before{content:"\f11a"}.ion-chatboxes:before{content:"\f11c"}.ion-chatbubble:before{content:"\f11e"}.ion-chatbubble-working:before{content:"\f11d"}.ion-chatbubbles:before{content:"\f11f"}.ion-checkmark:before{content:"\f122"}.ion-checkmark-circled:before{content:"\f120"}.ion-checkmark-round:before{content:"\f121"}.ion-chevron-down:before{content:"\f123"}.ion-chevron-left:before{content:"\f124"}.ion-chevron-right:before{content:"\f125"}.ion-chevron-up:before{content:"\f126"}.ion-clipboard:before{content:"\f127"}.ion-clock:before{content:"\f26e"}.ion-close:before{content:"\f12a"}.ion-close-circled:before{content:"\f128"}.ion-close-round:before{content:"\f129"}.ion-closed-captioning:before{content:"\f317"}.ion-cloud:before{content:"\f12b"}.ion-code:before{content:"\f271"}.ion-code-download:before{content:"\f26f"}.ion-code-working:before{content:"\f270"}.ion-coffee:before{content:"\f272"}.ion-compass:before{content:"\f273"}.ion-compose:before{content:"\f12c"}.ion-connection-bars:before{content:"\f274"}.ion-contrast:before{content:"\f275"}.ion-crop:before{content:"\f3c1"}.ion-cube:before{content:"\f318"}.ion-disc:before{content:"\f12d"}.ion-document:before{content:"\f12f"}.ion-document-text:before{content:"\f12e"}.ion-drag:before{content:"\f130"}.ion-earth:before{content:"\f276"}.ion-easel:before{content:"\f3c2"}.ion-edit:before{content:"\f2bf"}.ion-egg:before{content:"\f277"}.ion-eject:before{content:"\f131"}.ion-email:before{content:"\f132"}.ion-email-unread:before{content:"\f3c3"}.ion-erlenmeyer-flask:before{content:"\f3c5"}.ion-erlenmeyer-flask-bubbles:before{content:"\f3c4"}.ion-eye:before{content:"\f133"}.ion-eye-disabled:before{content:"\f306"}.ion-female:before{content:"\f278"}.ion-filing:before{content:"\f134"}.ion-film-marker:before{content:"\f135"}.ion-fireball:before{content:"\f319"}.ion-flag:before{content:"\f279"}.ion-flame:before{content:"\f31a"}.ion-flash:before{content:"\f137"}.ion-flash-off:before{content:"\f136"}.ion-folder:before{content:"\f139"}.ion-fork:before{content:"\f27a"}.ion-fork-repo:before{content:"\f2c0"}.ion-forward:before{content:"\f13a"}.ion-funnel:before{content:"\f31b"}.ion-gear-a:before{content:"\f13d"}.ion-gear-b:before{content:"\f13e"}.ion-grid:before{content:"\f13f"}.ion-hammer:before{content:"\f27b"}.ion-happy:before{content:"\f31c"}.ion-happy-outline:before{content:"\f3c6"}.ion-headphone:before{content:"\f140"}.ion-heart:before{content:"\f141"}.ion-heart-broken:before{content:"\f31d"}.ion-help:before{content:"\f143"}.ion-help-buoy:before{content:"\f27c"}.ion-help-circled:before{content:"\f142"}.ion-home:before{content:"\f144"}.ion-icecream:before{content:"\f27d"}.ion-image:before{content:"\f147"}.ion-images:before{content:"\f148"}.ion-information:before{content:"\f14a"}.ion-information-circled:before{content:"\f149"}.ion-ionic:before{content:"\f14b"}.ion-ios-alarm:before{content:"\f3c8"}.ion-ios-alarm-outline:before{content:"\f3c7"}.ion-ios-albums:before{content:"\f3ca"}.ion-ios-albums-outline:before{content:"\f3c9"}.ion-ios-americanfootball:before{content:"\f3cc"}.ion-ios-americanfootball-outline:before{content:"\f3cb"}.ion-ios-analytics:before{content:"\f3ce"}.ion-ios-analytics-outline:before{content:"\f3cd"}.ion-ios-arrow-back:before{content:"\f3cf"}.ion-ios-arrow-down:before{content:"\f3d0"}.ion-ios-arrow-forward:before{content:"\f3d1"}.ion-ios-arrow-left:before{content:"\f3d2"}.ion-ios-arrow-right:before{content:"\f3d3"}.ion-ios-arrow-thin-down:before{content:"\f3d4"}.ion-ios-arrow-thin-left:before{content:"\f3d5"}.ion-ios-arrow-thin-right:before{content:"\f3d6"}.ion-ios-arrow-thin-up:before{content:"\f3d7"}.ion-ios-arrow-up:before{content:"\f3d8"}.ion-ios-at:before{content:"\f3da"}.ion-ios-at-outline:before{content:"\f3d9"}.ion-ios-barcode:before{content:"\f3dc"}.ion-ios-barcode-outline:before{content:"\f3db"}.ion-ios-baseball:before{content:"\f3de"}.ion-ios-baseball-outline:before{content:"\f3dd"}.ion-ios-basketball:before{content:"\f3e0"}.ion-ios-basketball-outline:before{content:"\f3df"}.ion-ios-bell:before{content:"\f3e2"}.ion-ios-bell-outline:before{content:"\f3e1"}.ion-ios-body:before{content:"\f3e4"}.ion-ios-body-outline:before{content:"\f3e3"}.ion-ios-bolt:before{content:"\f3e6"}.ion-ios-bolt-outline:before{content:"\f3e5"}.ion-ios-book:before{content:"\f3e8"}.ion-ios-book-outline:before{content:"\f3e7"}.ion-ios-bookmarks:before{content:"\f3ea"}.ion-ios-bookmarks-outline:before{content:"\f3e9"}.ion-ios-box:before{content:"\f3ec"}.ion-ios-box-outline:before{content:"\f3eb"}.ion-ios-briefcase:before{content:"\f3ee"}.ion-ios-briefcase-outline:before{content:"\f3ed"}.ion-ios-browsers:before{content:"\f3f0"}.ion-ios-browsers-outline:before{content:"\f3ef"}.ion-ios-calculator:before{content:"\f3f2"}.ion-ios-calculator-outline:before{content:"\f3f1"}.ion-ios-calendar:before{content:"\f3f4"}.ion-ios-calendar-outline:before{content:"\f3f3"}.ion-ios-camera:before{content:"\f3f6"}.ion-ios-camera-outline:before{content:"\f3f5"}.ion-ios-cart:before{content:"\f3f8"}.ion-ios-cart-outline:before{content:"\f3f7"}.ion-ios-chatboxes:before{content:"\f3fa"}.ion-ios-chatboxes-outline:before{content:"\f3f9"}.ion-ios-chatbubble:before{content:"\f3fc"}.ion-ios-chatbubble-outline:before{content:"\f3fb"}.ion-ios-checkmark:before{content:"\f3ff"}.ion-ios-checkmark-empty:before{content:"\f3fd"}.ion-ios-checkmark-outline:before{content:"\f3fe"}.ion-ios-circle-filled:before{content:"\f400"}.ion-ios-circle-outline:before{content:"\f401"}.ion-ios-clock:before{content:"\f403"}.ion-ios-clock-outline:before{content:"\f402"}.ion-ios-close:before{content:"\f406"}.ion-ios-close-empty:before{content:"\f404"}.ion-ios-close-outline:before{content:"\f405"}.ion-ios-cloud:before{content:"\f40c"}.ion-ios-cloud-download:before{content:"\f408"}.ion-ios-cloud-download-outline:before{content:"\f407"}.ion-ios-cloud-outline:before{content:"\f409"}.ion-ios-cloud-upload:before{content:"\f40b"}.ion-ios-cloud-upload-outline:before{content:"\f40a"}.ion-ios-cloudy:before{content:"\f410"}.ion-ios-cloudy-night:before{content:"\f40e"}.ion-ios-cloudy-night-outline:before{content:"\f40d"}.ion-ios-cloudy-outline:before{content:"\f40f"}.ion-ios-cog:before{content:"\f412"}.ion-ios-cog-outline:before{content:"\f411"}.ion-ios-color-filter:before{content:"\f414"}.ion-ios-color-filter-outline:before{content:"\f413"}.ion-ios-color-wand:before{content:"\f416"}.ion-ios-color-wand-outline:before{content:"\f415"}.ion-ios-compose:before{content:"\f418"}.ion-ios-compose-outline:before{content:"\f417"}.ion-ios-contact:before{content:"\f41a"}.ion-ios-contact-outline:before{content:"\f419"}.ion-ios-copy:before{content:"\f41c"}.ion-ios-copy-outline:before{content:"\f41b"}.ion-ios-crop:before{content:"\f41e"}.ion-ios-crop-strong:before{content:"\f41d"}.ion-ios-download:before{content:"\f420"}.ion-ios-download-outline:before{content:"\f41f"}.ion-ios-drag:before{content:"\f421"}.ion-ios-email:before{content:"\f423"}.ion-ios-email-outline:before{content:"\f422"}.ion-ios-eye:before{content:"\f425"}.ion-ios-eye-outline:before{content:"\f424"}.ion-ios-fastforward:before{content:"\f427"}.ion-ios-fastforward-outline:before{content:"\f426"}.ion-ios-filing:before{content:"\f429"}.ion-ios-filing-outline:before{content:"\f428"}.ion-ios-film:before{content:"\f42b"}.ion-ios-film-outline:before{content:"\f42a"}.ion-ios-flag:before{content:"\f42d"}.ion-ios-flag-outline:before{content:"\f42c"}.ion-ios-flame:before{content:"\f42f"}.ion-ios-flame-outline:before{content:"\f42e"}.ion-ios-flask:before{content:"\f431"}.ion-ios-flask-outline:before{content:"\f430"}.ion-ios-flower:before{content:"\f433"}.ion-ios-flower-outline:before{content:"\f432"}.ion-ios-folder:before{content:"\f435"}.ion-ios-folder-outline:before{content:"\f434"}.ion-ios-football:before{content:"\f437"}.ion-ios-football-outline:before{content:"\f436"}.ion-ios-game-controller-a:before{content:"\f439"}.ion-ios-game-controller-a-outline:before{content:"\f438"}.ion-ios-game-controller-b:before{content:"\f43b"}.ion-ios-game-controller-b-outline:before{content:"\f43a"}.ion-ios-gear:before{content:"\f43d"}.ion-ios-gear-outline:before{content:"\f43c"}.ion-ios-glasses:before{content:"\f43f"}.ion-ios-glasses-outline:before{content:"\f43e"}.ion-ios-grid-view:before{content:"\f441"}.ion-ios-grid-view-outline:before{content:"\f440"}.ion-ios-heart:before{content:"\f443"}.ion-ios-heart-outline:before{content:"\f442"}.ion-ios-help:before{content:"\f446"}.ion-ios-help-empty:before{content:"\f444"}.ion-ios-help-outline:before{content:"\f445"}.ion-ios-home:before{content:"\f448"}.ion-ios-home-outline:before{content:"\f447"}.ion-ios-infinite:before{content:"\f44a"}.ion-ios-infinite-outline:before{content:"\f449"}.ion-ios-information:before{content:"\f44d"}.ion-ios-information-empty:before{content:"\f44b"}.ion-ios-information-outline:before{content:"\f44c"}.ion-ios-ionic-outline:before{content:"\f44e"}.ion-ios-keypad:before{content:"\f450"}.ion-ios-keypad-outline:before{content:"\f44f"}.ion-ios-lightbulb:before{content:"\f452"}.ion-ios-lightbulb-outline:before{content:"\f451"}.ion-ios-list:before{content:"\f454"}.ion-ios-list-outline:before{content:"\f453"}.ion-ios-location:before{content:"\f456"}.ion-ios-location-outline:before{content:"\f455"}.ion-ios-locked:before{content:"\f458"}.ion-ios-locked-outline:before{content:"\f457"}.ion-ios-loop:before{content:"\f45a"}.ion-ios-loop-strong:before{content:"\f459"}.ion-ios-medical:before{content:"\f45c"}.ion-ios-medical-outline:before{content:"\f45b"}.ion-ios-medkit:before{content:"\f45e"}.ion-ios-medkit-outline:before{content:"\f45d"}.ion-ios-mic:before{content:"\f461"}.ion-ios-mic-off:before{content:"\f45f"}.ion-ios-mic-outline:before{content:"\f460"}.ion-ios-minus:before{content:"\f464"}.ion-ios-minus-empty:before{content:"\f462"}.ion-ios-minus-outline:before{content:"\f463"}.ion-ios-monitor:before{content:"\f466"}.ion-ios-monitor-outline:before{content:"\f465"}.ion-ios-moon:before{content:"\f468"}.ion-ios-moon-outline:before{content:"\f467"}.ion-ios-more:before{content:"\f46a"}.ion-ios-more-outline:before{content:"\f469"}.ion-ios-musical-note:before{content:"\f46b"}.ion-ios-musical-notes:before{content:"\f46c"}.ion-ios-navigate:before{content:"\f46e"}.ion-ios-navigate-outline:before{content:"\f46d"}.ion-ios-nutrition:before{content:"\f470"}.ion-ios-nutrition-outline:before{content:"\f46f"}.ion-ios-paper:before{content:"\f472"}.ion-ios-paper-outline:before{content:"\f471"}.ion-ios-paperplane:before{content:"\f474"}.ion-ios-paperplane-outline:before{content:"\f473"}.ion-ios-partlysunny:before{content:"\f476"}.ion-ios-partlysunny-outline:before{content:"\f475"}.ion-ios-pause:before{content:"\f478"}.ion-ios-pause-outline:before{content:"\f477"}.ion-ios-paw:before{content:"\f47a"}.ion-ios-paw-outline:before{content:"\f479"}.ion-ios-people:before{content:"\f47c"}.ion-ios-people-outline:before{content:"\f47b"}.ion-ios-person:before{content:"\f47e"}.ion-ios-person-outline:before{content:"\f47d"}.ion-ios-personadd:before{content:"\f480"}.ion-ios-personadd-outline:before{content:"\f47f"}.ion-ios-photos:before{content:"\f482"}.ion-ios-photos-outline:before{content:"\f481"}.ion-ios-pie:before{content:"\f484"}.ion-ios-pie-outline:before{content:"\f483"}.ion-ios-pint:before{content:"\f486"}.ion-ios-pint-outline:before{content:"\f485"}.ion-ios-play:before{content:"\f488"}.ion-ios-play-outline:before{content:"\f487"}.ion-ios-plus:before{content:"\f48b"}.ion-ios-plus-empty:before{content:"\f489"}.ion-ios-plus-outline:before{content:"\f48a"}.ion-ios-pricetag:before{content:"\f48d"}.ion-ios-pricetag-outline:before{content:"\f48c"}.ion-ios-pricetags:before{content:"\f48f"}.ion-ios-pricetags-outline:before{content:"\f48e"}.ion-ios-printer:before{content:"\f491"}.ion-ios-printer-outline:before{content:"\f490"}.ion-ios-pulse:before{content:"\f493"}.ion-ios-pulse-strong:before{content:"\f492"}.ion-ios-rainy:before{content:"\f495"}.ion-ios-rainy-outline:before{content:"\f494"}.ion-ios-recording:before{content:"\f497"}.ion-ios-recording-outline:before{content:"\f496"}.ion-ios-redo:before{content:"\f499"}.ion-ios-redo-outline:before{content:"\f498"}.ion-ios-refresh:before{content:"\f49c"}.ion-ios-refresh-empty:before{content:"\f49a"}.ion-ios-refresh-outline:before{content:"\f49b"}.ion-ios-reload:before{content:"\f49d"}.ion-ios-reverse-camera:before{content:"\f49f"}.ion-ios-reverse-camera-outline:before{content:"\f49e"}.ion-ios-rewind:before{content:"\f4a1"}.ion-ios-rewind-outline:before{content:"\f4a0"}.ion-ios-rose:before{content:"\f4a3"}.ion-ios-rose-outline:before{content:"\f4a2"}.ion-ios-search:before{content:"\f4a5"}.ion-ios-search-strong:before{content:"\f4a4"}.ion-ios-settings:before{content:"\f4a7"}.ion-ios-settings-strong:before{content:"\f4a6"}.ion-ios-shuffle:before{content:"\f4a9"}.ion-ios-shuffle-strong:before{content:"\f4a8"}.ion-ios-skipbackward:before{content:"\f4ab"}.ion-ios-skipbackward-outline:before{content:"\f4aa"}.ion-ios-skipforward:before{content:"\f4ad"}.ion-ios-skipforward-outline:before{content:"\f4ac"}.ion-ios-snowy:before{content:"\f4ae"}.ion-ios-speedometer:before{content:"\f4b0"}.ion-ios-speedometer-outline:before{content:"\f4af"}.ion-ios-star:before{content:"\f4b3"}.ion-ios-star-half:before{content:"\f4b1"}.ion-ios-star-outline:before{content:"\f4b2"}.ion-ios-stopwatch:before{content:"\f4b5"}.ion-ios-stopwatch-outline:before{content:"\f4b4"}.ion-ios-sunny:before{content:"\f4b7"}.ion-ios-sunny-outline:before{content:"\f4b6"}.ion-ios-telephone:before{content:"\f4b9"}.ion-ios-telephone-outline:before{content:"\f4b8"}.ion-ios-tennisball:before{content:"\f4bb"}.ion-ios-tennisball-outline:before{content:"\f4ba"}.ion-ios-thunderstorm:before{content:"\f4bd"}.ion-ios-thunderstorm-outline:before{content:"\f4bc"}.ion-ios-time:before{content:"\f4bf"}.ion-ios-time-outline:before{content:"\f4be"}.ion-ios-timer:before{content:"\f4c1"}.ion-ios-timer-outline:before{content:"\f4c0"}.ion-ios-toggle:before{content:"\f4c3"}.ion-ios-toggle-outline:before{content:"\f4c2"}.ion-ios-trash:before{content:"\f4c5"}.ion-ios-trash-outline:before{content:"\f4c4"}.ion-ios-undo:before{content:"\f4c7"}.ion-ios-undo-outline:before{content:"\f4c6"}.ion-ios-unlocked:before{content:"\f4c9"}.ion-ios-unlocked-outline:before{content:"\f4c8"}.ion-ios-upload:before{content:"\f4cb"}.ion-ios-upload-outline:before{content:"\f4ca"}.ion-ios-videocam:before{content:"\f4cd"}.ion-ios-videocam-outline:before{content:"\f4cc"}.ion-ios-volume-high:before{content:"\f4ce"}.ion-ios-volume-low:before{content:"\f4cf"}.ion-ios-wineglass:before{content:"\f4d1"}.ion-ios-wineglass-outline:before{content:"\f4d0"}.ion-ios-world:before{content:"\f4d3"}.ion-ios-world-outline:before{content:"\f4d2"}.ion-ipad:before{content:"\f1f9"}.ion-iphone:before{content:"\f1fa"}.ion-ipod:before{content:"\f1fb"}.ion-jet:before{content:"\f295"}.ion-key:before{content:"\f296"}.ion-knife:before{content:"\f297"}.ion-laptop:before{content:"\f1fc"}.ion-leaf:before{content:"\f1fd"}.ion-levels:before{content:"\f298"}.ion-lightbulb:before{content:"\f299"}.ion-link:before{content:"\f1fe"}.ion-load-a:before{content:"\f29a"}.ion-load-b:before{content:"\f29b"}.ion-load-c:before{content:"\f29c"}.ion-load-d:before{content:"\f29d"}.ion-location:before{content:"\f1ff"}.ion-lock-combination:before{content:"\f4d4"}.ion-locked:before{content:"\f200"}.ion-log-in:before{content:"\f29e"}.ion-log-out:before{content:"\f29f"}.ion-loop:before{content:"\f201"}.ion-magnet:before{content:"\f2a0"}.ion-male:before{content:"\f2a1"}.ion-man:before{content:"\f202"}.ion-map:before{content:"\f203"}.ion-medkit:before{content:"\f2a2"}.ion-merge:before{content:"\f33f"}.ion-mic-a:before{content:"\f204"}.ion-mic-b:before{content:"\f205"}.ion-mic-c:before{content:"\f206"}.ion-minus:before{content:"\f209"}.ion-minus-circled:before{content:"\f207"}.ion-minus-round:before{content:"\f208"}.ion-model-s:before{content:"\f2c1"}.ion-monitor:before{content:"\f20a"}.ion-more:before{content:"\f20b"}.ion-mouse:before{content:"\f340"}.ion-music-note:before{content:"\f20c"}.ion-navicon:before{content:"\f20e"}.ion-navicon-round:before{content:"\f20d"}.ion-navigate:before{content:"\f2a3"}.ion-network:before{content:"\f341"}.ion-no-smoking:before{content:"\f2c2"}.ion-nuclear:before{content:"\f2a4"}.ion-outlet:before{content:"\f342"}.ion-paintbrush:before{content:"\f4d5"}.ion-paintbucket:before{content:"\f4d6"}.ion-paper-airplane:before{content:"\f2c3"}.ion-paperclip:before{content:"\f20f"}.ion-pause:before{content:"\f210"}.ion-person:before{content:"\f213"}.ion-person-add:before{content:"\f211"}.ion-person-stalker:before{content:"\f212"}.ion-pie-graph:before{content:"\f2a5"}.ion-pin:before{content:"\f2a6"}.ion-pinpoint:before{content:"\f2a7"}.ion-pizza:before{content:"\f2a8"}.ion-plane:before{content:"\f214"}.ion-planet:before{content:"\f343"}.ion-play:before{content:"\f215"}.ion-playstation:before{content:"\f30a"}.ion-plus:before{content:"\f218"}.ion-plus-circled:before{content:"\f216"}.ion-plus-round:before{content:"\f217"}.ion-podium:before{content:"\f344"}.ion-pound:before{content:"\f219"}.ion-power:before{content:"\f2a9"}.ion-pricetag:before{content:"\f2aa"}.ion-pricetags:before{content:"\f2ab"}.ion-printer:before{content:"\f21a"}.ion-pull-request:before{content:"\f345"}.ion-qr-scanner:before{content:"\f346"}.ion-quote:before{content:"\f347"}.ion-radio-waves:before{content:"\f2ac"}.ion-record:before{content:"\f21b"}.ion-refresh:before{content:"\f21c"}.ion-reply:before{content:"\f21e"}.ion-reply-all:before{content:"\f21d"}.ion-ribbon-a:before{content:"\f348"}.ion-ribbon-b:before{content:"\f349"}.ion-sad:before{content:"\f34a"}.ion-sad-outline:before{content:"\f4d7"}.ion-scissors:before{content:"\f34b"}.ion-search:before{content:"\f21f"}.ion-settings:before{content:"\f2ad"}.ion-share:before{content:"\f220"}.ion-shuffle:before{content:"\f221"}.ion-skip-backward:before{content:"\f222"}.ion-skip-forward:before{content:"\f223"}.ion-social-android:before{content:"\f225"}.ion-social-android-outline:before{content:"\f224"}.ion-social-angular:before{content:"\f4d9"}.ion-social-angular-outline:before{content:"\f4d8"}.ion-social-apple:before{content:"\f227"}.ion-social-apple-outline:before{content:"\f226"}.ion-social-bitcoin:before{content:"\f2af"}.ion-social-bitcoin-outline:before{content:"\f2ae"}.ion-social-buffer:before{content:"\f229"}.ion-social-buffer-outline:before{content:"\f228"}.ion-social-chrome:before{content:"\f4db"}.ion-social-chrome-outline:before{content:"\f4da"}.ion-social-codepen:before{content:"\f4dd"}.ion-social-codepen-outline:before{content:"\f4dc"}.ion-social-css3:before{content:"\f4df"}.ion-social-css3-outline:before{content:"\f4de"}.ion-social-designernews:before{content:"\f22b"}.ion-social-designernews-outline:before{content:"\f22a"}.ion-social-dribbble:before{content:"\f22d"}.ion-social-dribbble-outline:before{content:"\f22c"}.ion-social-dropbox:before{content:"\f22f"}.ion-social-dropbox-outline:before{content:"\f22e"}.ion-social-euro:before{content:"\f4e1"}.ion-social-euro-outline:before{content:"\f4e0"}.ion-social-facebook:before{content:"\f231"}.ion-social-facebook-outline:before{content:"\f230"}.ion-social-foursquare:before{content:"\f34d"}.ion-social-foursquare-outline:before{content:"\f34c"}.ion-social-freebsd-devil:before{content:"\f2c4"}.ion-social-github:before{content:"\f233"}.ion-social-github-outline:before{content:"\f232"}.ion-social-google:before{content:"\f34f"}.ion-social-google-outline:before{content:"\f34e"}.ion-social-googleplus:before{content:"\f235"}.ion-social-googleplus-outline:before{content:"\f234"}.ion-social-hackernews:before{content:"\f237"}.ion-social-hackernews-outline:before{content:"\f236"}.ion-social-html5:before{content:"\f4e3"}.ion-social-html5-outline:before{content:"\f4e2"}.ion-social-instagram:before{content:"\f351"}.ion-social-instagram-outline:before{content:"\f350"}.ion-social-javascript:before{content:"\f4e5"}.ion-social-javascript-outline:before{content:"\f4e4"}.ion-social-linkedin:before{content:"\f239"}.ion-social-linkedin-outline:before{content:"\f238"}.ion-social-markdown:before{content:"\f4e6"}.ion-social-nodejs:before{content:"\f4e7"}.ion-social-octocat:before{content:"\f4e8"}.ion-social-pinterest:before{content:"\f2b1"}.ion-social-pinterest-outline:before{content:"\f2b0"}.ion-social-python:before{content:"\f4e9"}.ion-social-reddit:before{content:"\f23b"}.ion-social-reddit-outline:before{content:"\f23a"}.ion-social-rss:before{content:"\f23d"}.ion-social-rss-outline:before{content:"\f23c"}.ion-social-sass:before{content:"\f4ea"}.ion-social-skype:before{content:"\f23f"}.ion-social-skype-outline:before{content:"\f23e"}.ion-social-snapchat:before{content:"\f4ec"}.ion-social-snapchat-outline:before{content:"\f4eb"}.ion-social-tumblr:before{content:"\f241"}.ion-social-tumblr-outline:before{content:"\f240"}.ion-social-tux:before{content:"\f2c5"}.ion-social-twitch:before{content:"\f4ee"}.ion-social-twitch-outline:before{content:"\f4ed"}.ion-social-twitter:before{content:"\f243"}.ion-social-twitter-outline:before{content:"\f242"}.ion-social-usd:before{content:"\f353"}.ion-social-usd-outline:before{content:"\f352"}.ion-social-vimeo:before{content:"\f245"}.ion-social-vimeo-outline:before{content:"\f244"}.ion-social-whatsapp:before{content:"\f4f0"}.ion-social-whatsapp-outline:before{content:"\f4ef"}.ion-social-windows:before{content:"\f247"}.ion-social-windows-outline:before{content:"\f246"}.ion-social-wordpress:before{content:"\f249"}.ion-social-wordpress-outline:before{content:"\f248"}.ion-social-yahoo:before{content:"\f24b"}.ion-social-yahoo-outline:before{content:"\f24a"}.ion-social-yen:before{content:"\f4f2"}.ion-social-yen-outline:before{content:"\f4f1"}.ion-social-youtube:before{content:"\f24d"}.ion-social-youtube-outline:before{content:"\f24c"}.ion-soup-can:before{content:"\f4f4"}.ion-soup-can-outline:before{content:"\f4f3"}.ion-speakerphone:before{content:"\f2b2"}.ion-speedometer:before{content:"\f2b3"}.ion-spoon:before{content:"\f2b4"}.ion-star:before{content:"\f24e"}.ion-stats-bars:before{content:"\f2b5"}.ion-steam:before{content:"\f30b"}.ion-stop:before{content:"\f24f"}.ion-thermometer:before{content:"\f2b6"}.ion-thumbsdown:before{content:"\f250"}.ion-thumbsup:before{content:"\f251"}.ion-toggle:before{content:"\f355"}.ion-toggle-filled:before{content:"\f354"}.ion-transgender:before{content:"\f4f5"}.ion-trash-a:before{content:"\f252"}.ion-trash-b:before{content:"\f253"}.ion-trophy:before{content:"\f356"}.ion-tshirt:before{content:"\f4f7"}.ion-tshirt-outline:before{content:"\f4f6"}.ion-umbrella:before{content:"\f2b7"}.ion-university:before{content:"\f357"}.ion-unlocked:before{content:"\f254"}.ion-upload:before{content:"\f255"}.ion-usb:before{content:"\f2b8"}.ion-videocamera:before{content:"\f256"}.ion-volume-high:before{content:"\f257"}.ion-volume-low:before{content:"\f258"}.ion-volume-medium:before{content:"\f259"}.ion-volume-mute:before{content:"\f25a"}.ion-wand:before{content:"\f358"}.ion-waterdrop:before{content:"\f25b"}.ion-wifi:before{content:"\f25c"}.ion-wineglass:before{content:"\f2b9"}.ion-woman:before{content:"\f25d"}.ion-wrench:before{content:"\f2ba"}.ion-xbox:before{content:"\f30c"} diff --git a/LinuxGUI/WebUI/static/css/vtoy.css b/LinuxGUI/WebUI/static/css/vtoy.css new file mode 100644 index 00000000..f9fcf4e1 --- /dev/null +++ b/LinuxGUI/WebUI/static/css/vtoy.css @@ -0,0 +1,357 @@ +/*loading */ +.loading { + width: 100%; + height: 100%; + position: fixed; + top:0; + left:0; + z-index: 999999; + background-color: rgba(0, 0, 0, 0); +} +.loading .title { + width: 300px; + text-align: center; + margin: 250px auto 0px; + color: #2F7095; + font-weight: 900; + font-size: 24px; +} +.loading .rectbox { + width: 150px; + height: 40px; + margin: 10px auto; +} +.loading .rectbox .title { + font-size: 14px; + font-family: 'Microsoft YaHei'; + font-weight: 900; + text-align: center; + color: rgba(68, 149, 57, 1); +} +.loading .rectbox .rect { + width: 25px; + height: 25px; + background-color: rgba(68, 149, 57, 1); + margin: 0 2px auto 3px; + float: left; + -webkit-animation: loading 0.48s infinite ease-in-out; + -o-animation: loading 0.48s infinite ease-in-out; + animation: loading 0.48s infinite ease-in-out; +} +.loading .rectbox .rect1 { + -webkit-animation-delay: 0s; + -moz-animation-delay: 0s; + -o-animation-delay: 0s; + animation-delay: 0s; +} +.loading .rectbox .rect2 { + -webkit-animation-delay: 0.12s; + -moz-animation-delay: 0.12s; + -o-animation-delay: 0.12s; + animation-delay: 0.12s; + background-color: rgba(68, 149, 57, 0.2); +} +.loading .rectbox .rect3 { + -webkit-animation-delay: 0.24s; + -moz-animation-delay: 0.24s; + -o-animation-delay: 0.24s; + animation-delay: 0.24s; + background-color: rgba(68, 149, 57, 0.4); +} +.loading .rectbox .rect4 { + -webkit-animation-delay: 0.36s; + -moz-animation-delay: 0.36s; + -o-animation-delay: 0.36s; + animation-delay: 0.36s; + background-color: rgba(68, 149, 57, 0.6); +} +.loading .rectbox .rect5 { + -webkit-animation-delay: 0.48s; + -moz-animation-delay: 0.48s; + -o-animation-delay: 0.48s; + animation-delay: 0.48s; + background-color: rgba(68, 149, 57, 0.8); +} +@keyframes loading { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} +@-moz-keyframes loading { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} +@-ms-keyframes loading { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} +@-webkit-keyframes loading { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} + +/*vtoy server is running*/ +#vtoy-main .loading { + width: 100%; + position: relative; + top:0; + left:0; + background-color: rgba(0, 0, 0, 0); +} +#vtoy-main .loading .title { + width: 300px; + text-align: center; + margin: 250px auto 0px; + color: #449539; + font-weight: 900; + font-size: 24px; +} +#vtoy-main .loading .rectbox { + width: 120px; + height: 40px; + margin: 10px auto; +} +#vtoy-main .loading .rectbox .title { + font-size: 14px; + font-family: 'Microsoft YaHei'; + font-weight: 900; + text-align: center; + color: rgba(68, 149, 57, 1); +} +#vtoy-main .loading .rectbox .rect { + width: 25px; + height: 25px; + background-color: rgba(68, 149, 57, 1); + margin: 0 2px auto 3px; + float: left; + -webkit-animation: loading 1.44s infinite ease-in-out; + -o-animation: loading 0.48s infinite ease-in-out; + animation: loading 0.48s infinite ease-in-out; +} +#vtoy-main .loading .rectbox .rect1 { + -webkit-animation-delay: 0s; + -moz-animation-delay: 0s; + -o-animation-delay: 0s; + animation-delay: 0s; +} +#vtoy-main .loading .rectbox .rect2 { + -webkit-animation-delay: 0.36s; + -moz-animation-delay: 0.12s; + -o-animation-delay: 0.12s; + animation-delay: 0.12s; + background-color: rgba(68, 149, 57, 0.2); +} +#vtoy-main .loading .rectbox .rect3 { + -webkit-animation-delay: 0.72s; + -moz-animation-delay: 0.24s; + -o-animation-delay: 0.24s; + animation-delay: 0.24s; + background-color: rgba(68, 149, 57, 0.4); +} +#vtoy-main .loading .rectbox .rect4 { + -webkit-animation-delay: 1.08s; + -moz-animation-delay: 0.36s; + -o-animation-delay: 0.36s; + animation-delay: 0.36s; + background-color: rgba(68, 149, 57, 0.6); +} +#vtoy-main .loading .rectbox .rect5 { + -webkit-animation-delay: 1.44s; + -moz-animation-delay: 0.48s; + -o-animation-delay: 0.48s; + animation-delay: 0.48s; + background-color: rgba(68, 149, 57, 0.8); +} +@keyframes running { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} +@-moz-keyframes running { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} +@-ms-keyframes running { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} +@-webkit-keyframes running { + 0% { + background-color: rgba(68, 149, 57, 1); + } + 25% { + background-color: rgba(68, 149, 57, 0.8); + } + 50% { + background-color: rgba(68, 149, 57, 0.6); + } + 75% { + background-color: rgba(68, 149, 57, 0.4); + } + 100% { + background-color: rgba(68, 149, 57, 0.2); + } +} + + + +.loadEffect{ + width: 110px; + height: 110px; + position: relative; + margin: 0 auto; + margin-top:0 auto; +} +.loadEffect span{ + display: inline-block; + width: 20px; + height: 20px; + border-radius: 50%; + background: lightgreen; + position: absolute; + -webkit-animation: load 1.04s ease infinite; +} +@-webkit-keyframes load{ + 0%{ + opacity: 1; + } + 100%{ + opacity: 0.2; + } +} + + + + +.loadEffect span:nth-child(1){ + left: 40%; + top: -130%; + -webkit-animation-delay:0.13s; +} +.loadEffect span:nth-child(2){ + left: 90%; + top: 8%; + margin-top:-120%; + -webkit-animation-delay:0.26s; +} +.loadEffect span:nth-child(3){ + left: 110%; + top: -80%; + margin-left: %-100; + -webkit-animation-delay:0.39s; +} +.loadEffect span:nth-child(4){ + top: -40%; + left:110%; + -webkit-animation-delay:0.52s; +} +.loadEffect span:nth-child(5){ + left: 40%; + top: 0; + margin-top:10%; + -webkit-animation-delay:0.65s; +} +.loadEffect span:nth-child(6){ + left: -20%; + bottom:120%; + -webkit-animation-delay:0.78s; +} +.loadEffect span:nth-child(7){ + bottom: 160%; + left: -20%; + -webkit-animation-delay:0.91s; +} +.loadEffect span:nth-child(8){ + bottom: 200%; + left: -10%; + -webkit-animation-delay:1.04s; +} diff --git a/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.ttf b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 00000000..d7994e13 Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.ttf differ diff --git a/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff new file mode 100644 index 00000000..6fd4ede0 Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff differ diff --git a/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff2 b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff2 new file mode 100644 index 00000000..5560193c Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/fontawesome-webfont.woff2 differ diff --git a/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.ttf b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..1413fc60 Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.ttf differ diff --git a/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..9e612858 Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff differ diff --git a/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff2 b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 00000000..64539b54 Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/LinuxGUI/WebUI/static/fonts/ionicons.eot b/LinuxGUI/WebUI/static/fonts/ionicons.eot new file mode 100644 index 00000000..9caa3489 Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/ionicons.eot differ diff --git a/LinuxGUI/WebUI/static/fonts/ionicons.ttf b/LinuxGUI/WebUI/static/fonts/ionicons.ttf new file mode 100644 index 00000000..180ce515 Binary files /dev/null and b/LinuxGUI/WebUI/static/fonts/ionicons.ttf differ diff --git a/LinuxGUI/WebUI/static/img/dropdown.png b/LinuxGUI/WebUI/static/img/dropdown.png new file mode 100644 index 00000000..201a4ca9 Binary files /dev/null and b/LinuxGUI/WebUI/static/img/dropdown.png differ diff --git a/LinuxGUI/WebUI/static/img/refresh.ico b/LinuxGUI/WebUI/static/img/refresh.ico new file mode 100644 index 00000000..c2bc3388 Binary files /dev/null and b/LinuxGUI/WebUI/static/img/refresh.ico differ diff --git a/LinuxGUI/WebUI/static/js/jQuery-2.1.4.min.js b/LinuxGUI/WebUI/static/js/jQuery-2.1.4.min.js new file mode 100644 index 00000000..49990d6e --- /dev/null +++ b/LinuxGUI/WebUI/static/js/jQuery-2.1.4.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ +return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("