From b14a9ed1a3f743c7bf278b7a1136e183e8efdc9f Mon Sep 17 00:00:00 2001 From: xlfeng Date: Fri, 22 Apr 2022 20:17:58 +0800 Subject: [PATCH] =?UTF-8?q?fixed=20452da39=20from=20https://gitee.com/xlfe?= =?UTF-8?q?ng/third=5Fparty=5Fexfat-utils/pulls/14=20=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E7=94=A8exfatprogs=E4=BB=A3=E7=A0=81=E6=9B=BF=E6=8D=A2exfat-ut?= =?UTF-8?q?ils=E4=BB=A3=E7=A0=81=EF=BC=8C=E7=AD=89exfatprogs=E4=BB=93?= =?UTF-8?q?=E5=AD=B5=E5=8C=96=E5=90=8E=E6=9C=AC=E4=BB=93=E4=B8=8B=E7=BA=BF?= =?UTF-8?q?=E3=80=82=E6=9B=BF=E6=8D=A2=E5=8E=9F=E5=9B=A0=EF=BC=9A=E6=9C=AC?= =?UTF-8?q?=E4=BB=93=E5=B7=A5=E5=85=B7=E4=B8=8D=E6=94=AF=E6=8C=81=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Dexfat=E4=B8=AD=E7=9A=84=E9=94=99=E8=AF=AF=EF=BC=8C?= =?UTF-8?q?=E8=80=8Cexfatprogs=E6=98=AFLinux=E5=86=85=E6=A0=B8exfat?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8=E7=9A=84maintainer=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E7=9A=84=E5=AE=98=E6=96=B9=E9=85=8D=E5=A5=97=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: xlfeng --- .travis.yml | 47 + .travis_get_mainline_kernel | 39 + BUILD.gn | 78 +- ChangeLog | 195 -- Makefile.am | 46 +- NEWS | 117 ++ OAT.xml | 15 +- README.OpenSource | 10 +- README.md | 170 +- autogen.sh | 3 + configure.ac | 82 +- dump/Makefile.am | 31 +- dump/dump.c | 255 +++ dump/dumpexfat.8 | 55 - dump/main.c | 246 --- fsck/Makefile.am | 35 +- fsck/de_iter.c | 313 ++++ fsck/exfatfsck.8 | 55 - fsck/fsck.c | 1648 +++++++++++++++++ fsck/fsck.h | 99 + fsck/main.c | 199 -- fsck/repair.c | 119 ++ fsck/repair.h | 24 + fuse/Makefile.am | 34 - fuse/main.c | 644 ------- fuse/mount.exfat-fuse.8 | 108 -- include/exfat_ondisk.h | 222 +++ include/libexfat.h | 144 ++ include/list.h | 333 ++++ include/version.h | 10 + label/Makefile.am | 29 +- label/exfatlabel.8 | 48 - label/label.c | 114 ++ label/main.c | 62 - lib/Makefile.am | 4 + lib/libexfat.c | 658 +++++++ libexfat/Makefile.am | 39 - libexfat/byteorder.h | 68 - libexfat/cluster.c | 491 ----- libexfat/compiler.h | 69 - libexfat/config.h | 66 - libexfat/exfat.h | 247 --- libexfat/exfatfs.h | 181 -- libexfat/io.c | 497 ----- libexfat/log.c | 136 -- libexfat/lookup.c | 222 --- libexfat/mount.c | 373 ---- libexfat/node.c | 1243 ------------- libexfat/platform.h | 73 - libexfat/repair.c | 102 - libexfat/time.c | 173 -- libexfat/utf.c | 254 --- libexfat/utils.c | 192 -- manpages/dump.exfat.8 | 17 + manpages/exfatlabel.8 | 36 + manpages/fsck.exfat.8 | 51 + manpages/mkfs.exfat.8 | 111 ++ manpages/tune.exfat.8 | 46 + mkfs/Makefile.am | 50 +- mkfs/cbm.c | 79 - mkfs/cbm.h | 30 - mkfs/fat.c | 88 - mkfs/fat.h | 30 - mkfs/main.c | 255 --- mkfs/mkexfat.c | 162 -- mkfs/mkexfat.h | 49 - mkfs/mkexfatfs.8 | 71 - mkfs/mkfs.c | 712 +++++++ mkfs/mkfs.h | 33 + mkfs/rootdir.c | 102 - mkfs/rootdir.h | 30 - mkfs/uct.c | 52 - mkfs/uct.h | 30 - mkfs/uctc.c | 757 -------- mkfs/uctc.h | 30 - mkfs/upcase.c | 514 +++++ mkfs/vbr.c | 148 -- mkfs/vbr.h | 30 - tests/2tb_disk/exfat.img.tar.xz | Bin 0 -> 13368 bytes tests/bs_bad_csum/exfat.img.tar.xz | Bin 0 -> 3036 bytes tests/de_bad_csum/exfat.img.tar.xz | Bin 0 -> 3196 bytes tests/file_invalid_clus/exfat.img.expected.xz | Bin 0 -> 4048 bytes tests/file_invalid_clus/exfat.img.tar.xz | Bin 0 -> 3392 bytes .../exfat.img.expected.xz | Bin 0 -> 48520 bytes .../large_file_invalid_clus/exfat.img.tar.xz | Bin 0 -> 17280 bytes tests/test_fsck.sh | 73 + tune/Makefile.am | 6 + tune/tune.c | 124 ++ 88 files changed, 6107 insertions(+), 8326 deletions(-) create mode 100644 .travis.yml create mode 100755 .travis_get_mainline_kernel delete mode 100644 ChangeLog create mode 100644 NEWS create mode 100755 autogen.sh create mode 100644 dump/dump.c delete mode 100644 dump/dumpexfat.8 delete mode 100644 dump/main.c create mode 100644 fsck/de_iter.c delete mode 100644 fsck/exfatfsck.8 create mode 100644 fsck/fsck.c create mode 100644 fsck/fsck.h delete mode 100644 fsck/main.c create mode 100644 fsck/repair.c create mode 100644 fsck/repair.h delete mode 100644 fuse/Makefile.am delete mode 100644 fuse/main.c delete mode 100644 fuse/mount.exfat-fuse.8 create mode 100644 include/exfat_ondisk.h create mode 100644 include/libexfat.h create mode 100644 include/list.h create mode 100644 include/version.h delete mode 100644 label/exfatlabel.8 create mode 100644 label/label.c delete mode 100644 label/main.c create mode 100644 lib/Makefile.am create mode 100644 lib/libexfat.c delete mode 100644 libexfat/Makefile.am delete mode 100644 libexfat/byteorder.h delete mode 100644 libexfat/cluster.c delete mode 100644 libexfat/compiler.h delete mode 100644 libexfat/config.h delete mode 100644 libexfat/exfat.h delete mode 100644 libexfat/exfatfs.h delete mode 100644 libexfat/io.c delete mode 100644 libexfat/log.c delete mode 100644 libexfat/lookup.c delete mode 100644 libexfat/mount.c delete mode 100644 libexfat/node.c delete mode 100644 libexfat/platform.h delete mode 100644 libexfat/repair.c delete mode 100644 libexfat/time.c delete mode 100644 libexfat/utf.c delete mode 100644 libexfat/utils.c create mode 100644 manpages/dump.exfat.8 create mode 100644 manpages/exfatlabel.8 create mode 100644 manpages/fsck.exfat.8 create mode 100644 manpages/mkfs.exfat.8 create mode 100644 manpages/tune.exfat.8 delete mode 100644 mkfs/cbm.c delete mode 100644 mkfs/cbm.h delete mode 100644 mkfs/fat.c delete mode 100644 mkfs/fat.h delete mode 100644 mkfs/main.c delete mode 100644 mkfs/mkexfat.c delete mode 100644 mkfs/mkexfat.h delete mode 100644 mkfs/mkexfatfs.8 create mode 100644 mkfs/mkfs.c create mode 100644 mkfs/mkfs.h delete mode 100644 mkfs/rootdir.c delete mode 100644 mkfs/rootdir.h delete mode 100644 mkfs/uct.c delete mode 100644 mkfs/uct.h delete mode 100644 mkfs/uctc.c delete mode 100644 mkfs/uctc.h create mode 100644 mkfs/upcase.c delete mode 100644 mkfs/vbr.c delete mode 100644 mkfs/vbr.h create mode 100644 tests/2tb_disk/exfat.img.tar.xz create mode 100644 tests/bs_bad_csum/exfat.img.tar.xz create mode 100644 tests/de_bad_csum/exfat.img.tar.xz create mode 100644 tests/file_invalid_clus/exfat.img.expected.xz create mode 100644 tests/file_invalid_clus/exfat.img.tar.xz create mode 100644 tests/large_file_invalid_clus/exfat.img.expected.xz create mode 100644 tests/large_file_invalid_clus/exfat.img.tar.xz create mode 100755 tests/test_fsck.sh create mode 100644 tune/Makefile.am create mode 100644 tune/tune.c diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..90aff89 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,47 @@ +dist: bionic + +language: c + +notifications: + - email: true + +before_script: + - sudo apt-get install linux-headers-$(uname -r) + - git clone --branch=exfat-next https://github.com/namjaejeon/exfat_oot + - ./.travis_get_mainline_kernel + - export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + - export PATH=/usr/local/lib:$PATH + +script: + # run checkpatch.pl + - git format-patch -20 + - ./linux/scripts/checkpatch.pl *.patch || true + # build & install exfatprogs + - ./autogen.sh > /dev/null + - ./configure > /dev/null + - make -j$((`nproc`+1)) > /dev/null + - sudo make install > /dev/null + - cd exfat_oot + - make > /dev/null + - sudo make install > /dev/null + - sudo modprobe exfat + - sudo mkdir -p /mnt/test + # create file/director test + - truncate -s 10G test.img + - sudo losetup /dev/loop22 test.img + - sudo mkfs.exfat /dev/loop22 + - sudo mount -t exfat /dev/loop22 /mnt/test/ + - cd /mnt/test/ + - i=1;while [ $i -le 10000 ];do sudo touch file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done + - sync + - sudo rm -rf * + - i=1;while [ $i -le 10000 ];do sudo mkdir dir$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done + - sync + - sudo rm -rf * + - i=1;while [ $i -le 10000 ];do sudo touch file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done + - i=1;while [ $i -le 10000 ];do sudo mkdir dir$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done + - sync + - sudo fsck.exfat /dev/loop22 + - sudo find . -delete + - sudo fsck.exfat /dev/loop22 + - cd - diff --git a/.travis_get_mainline_kernel b/.travis_get_mainline_kernel new file mode 100755 index 0000000..76b3a3a --- /dev/null +++ b/.travis_get_mainline_kernel @@ -0,0 +1,39 @@ +#!/bin/sh + +# +# A simple script we are using to get the latest mainline kernel +# tar ball +# + +wget https://www.kernel.org/releases.json +if [ $? -ne 0 ]; then + echo "Could not download kernel.org/releases.json" + exit 1 +fi + +VER=$(cat releases.json | python2.7 -c "import sys, json; print json.load(sys.stdin)['latest_stable']['version']") +if [ $? -ne 0 ]; then + echo "Could not parse release.json" + exit 1 +fi + +if [ "z$VER" = "z" ]; then + echo "Could not determine latest release version" + exit 1 +fi + +MVER=$(echo $VER | cut -d. -f1) + +wget https://cdn.kernel.org/pub/linux/kernel/v"$MVER".x/linux-"$VER".tar.gz +if [ $? -ne 0 ]; then + echo "Could not download $VER kernel version" + exit 1 +fi + +tar xf linux-"$VER".tar.gz +if [ $? -ne 0 ]; then + echo "Could not untar kernel tar ball" + exit 1 +fi + +mv linux-"$VER" linux diff --git a/BUILD.gn b/BUILD.gn index 73789f4..e6b64f5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Huawei Device Co., Ltd. +# Copyright (c) 2022 Huawei Device Co., Ltd. # # 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 @@ -28,26 +28,22 @@ config("exfat-defaults") { "-std=gnu99", "-Wno-error", "-D_FILE_OFFSET_BITS=64", - "-DPACKAGE=\"exfat\"", - "-DVERSION=\"1.3.0\"", + "-DPACKAGE=\"exfatprogs\"", + "-DVERSION=\"1.1.3\"", + ] + include_dirs = [ + "dump", + "fsck", + "include", + "label", + "mkfs", + "tune", ] - include_dirs = [ "//developtools/liblog" ] } ohos_shared_library("libexfat") { configs = [ ":exfat-defaults" ] - sources = [ - "libexfat/cluster.c", - "libexfat/io.c", - "libexfat/log.c", - "libexfat/lookup.c", - "libexfat/mount.c", - "libexfat/node.c", - "libexfat/repair.c", - "libexfat/time.c", - "libexfat/utf.c", - "libexfat/utils.c", - ] + sources = [ "lib/libexfat.c" ] include_dirs = [ "./libexfat" ] @@ -63,18 +59,12 @@ ohos_shared_library("libexfat") { ohos_executable("mkfs.exfat") { configs = [ ":exfat-defaults" ] sources = [ - "mkfs/cbm.c", - "mkfs/fat.c", - "mkfs/main.c", - "mkfs/mkexfat.c", - "mkfs/rootdir.c", - "mkfs/uct.c", - "mkfs/uctc.c", - "mkfs/vbr.c", + "mkfs/mkfs.c", + "mkfs/upcase.c", ] include_dirs = [ - "./libexfat", + "./lib", "./mkfs", ] @@ -89,10 +79,14 @@ ohos_executable("mkfs.exfat") { ##Build fsck.exfat ohos_executable("fsck.exfat") { configs = [ ":exfat-defaults" ] - sources = [ "fsck/main.c" ] + sources = [ + "fsck/de_iter.c", + "fsck/fsck.c", + "fsck/repair.c", + ] include_dirs = [ - "./libexfat", + "./lib", "./mkfs", "./fsck", ] @@ -105,13 +99,13 @@ ohos_executable("fsck.exfat") { } ################################################### -##Build dumpexfat -ohos_executable("dumpexfat") { +##Build dump.exfat +ohos_executable("dump.exfat") { configs = [ ":exfat-defaults" ] - sources = [ "dump/main.c" ] + sources = [ "dump/dump.c" ] include_dirs = [ - "./libexfat", + "./lib", "./mkfs", "./fsck", "./dump", @@ -126,10 +120,28 @@ ohos_executable("dumpexfat") { ##Build exfatlable ohos_executable("exfatlable") { configs = [ ":exfat-defaults" ] - sources = [ "label/main.c" ] + sources = [ "label/label.c" ] include_dirs = [ - "./libexfat", + "./lib", + "./mkfs", + "./fsck", + "./label", + ] + + deps = [ ":libexfat" ] + subsystem_name = "filemanagement" + part_name = "storage_service" +} + +################################################### +##Build tune.exfat +ohos_executable("tune.exfat") { + configs = [ ":exfat-defaults" ] + sources = [ "tune/tune.c" ] + + include_dirs = [ + "./lib", "./mkfs", "./fsck", "./label", diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 9e20bf5..0000000 --- a/ChangeLog +++ /dev/null @@ -1,195 +0,0 @@ -1.3.0 (2018-09-15) - -* exfatfsck can now repair some errors. -* Added experimental Android support for exfat-utils [liminghao, LongPingWEI, -Pablo Mendez Hernandez, Pierre-Hugues Husson]. -* Cleaned up FUSE code preparing for FUSE 3 support. -* Removed OpenBSD support as it does not handle -o option in fuse_main(). -* Re-introduced FreeBSD support [Oleksii Samorukov]. -* Fixed DragonFly BSD support [Tomohiro Kusumi]. -* dirent->d_type in now filled on readdir() [Mark Browning]. - -1.2.8 (2018-02-03) - -* Fixed new files or directories creation in the root directory: ensure there -are no 0x00 entries before valid ones; otherwise Windows can corrupt them. -* Fixed compilation on GNU/HURD platform. - -1.2.7 (2017-06-05) - -* Fixed handling of two last clusters: operations with files that occupy these -clusters could fail. -* Fixed crash when started with stdin, stdout or stderr closed. - -1.2.6 (2017-01-28) - -* Operations with directories (except initial listing) now make less -read/write system calls. -* Fixed handling of files with optional tail entries (0xe0-0xff): videoclip -files created by Sony cameras were missing. -* Write operations now correctly return ENOSPC (instead of EIO) when there is -no free disk space left. -* Fixed max file name length: it's 255 16-bit code units (not 256). - -1.2.5 (2016-12-05) - -* Added an option for dumpexfat to show file fragments [Daniel Drake]. -* Fixed crash when directory starts with an invalid cluster. -* Daylight saving time in now properly reflected in file timestamps. - -1.2.4 (2016-06-03) - -* Fixed wrong files names hashes when upper case table is compressed. -* Man pages are now installed by default. -* Commas and backslashes in device names are now escaped. - -1.2.3 (2015-12-19) - -* Fixed clusters loss when file renaming replaces target. - -1.2.2 (2015-11-09) - -* Improved reliability in case of a sudden unplug: FS will be in a clean state -after closing all files and performing sync(1). -* Fixed compilation on Debian GNU/kFreeBSD and GNU/Hurd platforms. -* Updated mount.exfat-fuse man page. - -1.2.1 (2015-09-24) - -* Fixed compatibility with Zalman VE-200: now newly created directories do not -have archive bit set. -* Fixed heap corruption: malformed FS can use invalid sector or cluster size. -* Fixed hang on mount: malformed FS can have cyclic references in the clusters -map. - -1.2.0 (2015-08-26) - -* Switched from SCons to autotools. -* Added musl libc support [Brendan Heading]. -* Worked around "FS is larger than device" error for memory cards formatted by -Panasonic Lumix cameras. -* Worked around "unknown entry type 0xe1" error for memory cards formatted by -Sony cameras. - -1.1.1 (2014-11-15) - -* Fixed mkfs crash on some sectors-per-cluster (-s option) values. - -1.1.0 (2014-07-08) - -* Relicensed the project from GPLv3+ to GPLv2+. -* OpenBSD support [Helg Bredow]. -* Improved I/O errors handling. -* Implemented fsync() and fsyncdir(). -* Fixed crash on Mac OS X 10.5 caused by non-standard use of realpath(). Also -fixed TrueCrypt disks unmounting. -* Avoid extra erase on writes to the end of a file. This should improve linear -write speed. -* Allow arbitrary changing of lower 9 bits of mode. Allow owner/group changing -to the same owner/group. This fixes rsync. -* Fixed buffers overflows when handling lengthy file names. -* Fixed "real size does not equal to size" error on volumes with pagefile.sys. -* Fixed negative IUsed in "df -i" output. - -1.0.1 (2013-02-02) - -* Fixed unexpected removal of a directory if it is moved into itself. -* Fixed "Operation not permitted" error on reading an empty file. - -1.0.0 (2013-01-19) - -* Fixed crash when renaming a file within a single directory and a new name -differs only in case. -* Fixed clusters allocation: a cluster beyond valid clusters range could be -allocated. -* Fixed crash when a volume is unmounted while some files are open. -* SConscript now respects AR and RANLIB environment variables. -* Improved error handling. - -Linux: - -* Enabled big_writes. This improves write speed (larger block size means less -switches between kernel- and user-space). -* Do BLKROGET ioctl to make sure the device is not read-only: after -"blockdev --setro" kernel still allows to open the device in read-write mode -but fails writes. - -OS X: - -* Fixed OS X 10.8 support. -* Switched to 64-bit inode numbers (now Mac OS X 10.5 or later is required). -* Switched from unmaintained MacFUSE to OSXFUSE (http://osxfuse.github.com). -* Fixed device size detection. Now mkfs works. -* Workarounded some utilities failures due to missing chmod() support. -* Disabled (senseless) permission checks made by FUSE. - -0.9.8 (2012-08-09) - -* The mkfs utility can now create huge file systems (up to several exabytes). -* Fixed handling of characters beyond Basic Multilingual Plane. -* Echo messages to syslog only if stderr is not connected to a terminal. - -0.9.7 (2012-03-08) - -* Out-of-the-box FreeBSD support (via ublio library). -* Fixed "missing EOD entry" error (could happen while reading directory that -consists of several clusters). -* Fixed interpretation of minutes field in files timestamps (minutes could be -displayed incorrectly). -* Fixed mtime seconds field initialization for newly created file (mtime could -be 1 sec less than creation time). -* SConscript now respects CC, CCFLAGS and LDFLAGS environment variables. - -0.9.6 (2012-01-14) - -* Fixed write performance regression introduced in 0.9.4. -* Mount in read-only mode if the device is write-protected. -* Set ctime to mtime to ensure we don't break programs that rely on ctime -(e.g. rsync considered that all files are outdated) [Eldad Zack]. -* Indicate that FS in not clean when it was not cleanly unmounted. -* Utilities are now compatible with GNU/Hurd. -* Fixed several memory leaks that could occur on error handling paths. -* Improved handling of corrupted file systems. - -0.9.5 (2011-05-15) - -* Fixed erasing of the root directory cluster when creating a new FS with -mkexfatfs. This bug could cause mkexfatfs to produce invalid FS. -* Utilities are not linked with libfuse anymore. -* Ensure that the path being opened is either a device or a regular file. - -0.9.4 (2011-03-05) - -* Introduced exfat-utils: dumpexfat, exfatfsck, mkexfatfs, exfatlabel. -* Fixed "Invalid argument" error while mounting a volume from a disk with sector size greater than 512 bytes. -* Wait for all data to be flushed to disk on unmount. -* Kernel cache is no longer flushed on open. This can slightly improve read performance by avoiding extra read requests from kernel to user-space. -* Allow to unmount volumes as user (fusermount -u) if they were mounted from the very same user [Tino Lange]. -* Errors and warnings are now duplicated to syslog. - -0.9.3 (2010-09-25) - -* Directories now can shrink. -* Improved timestamps resolution from 2 sec to 1 sec. -* Fixed timestamps displaying under Mac OS X when compiled for i386 or ppc. -* Fixed FS size displaying for non-GNU systems. - -0.9.2 (2010-07-24) - -* Fixed a bug which could cause the whole directory to become unreadable after renaming a file in it. -* Support for Solaris and various *BSD [Albert Lee]. -* Improved error handling on corrupted volumes. -* Improved allowed file name characters filter. -* Added man page. - -0.9.1 (2010-06-12) - -* Implemented automounting (util-linux-ng 2.18 or later is required). -* Fixed mounting when cluster bitmap is larger than expected. -* Fixed crash on statfs() when root directory contains error. -* Fixed bugs specific to big-endian machines. -* Other bugfixes. - -0.9.0 (2010-03-21) - -* Initial release. diff --git a/Makefile.am b/Makefile.am index 067f481..44f8635 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,23 +1,25 @@ -# -# Makefile.am (30.03.15) -# Automake source. -# -# 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. -# +## Makefile.am -SUBDIRS = libexfat dump fsck fuse label mkfs +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = lib mkfs fsck tune label dump + +# manpages +dist_man8_MANS = \ + manpages/fsck.exfat.8 \ + manpages/tune.exfat.8 \ + manpages/mkfs.exfat.8 \ + manpages/exfatlabel.8 \ + manpages/dump.exfat.8 + +# other stuff +EXTRA_DIST = \ + include \ + Android.bp \ + lib/Android.bp \ + mkfs/Android.bp \ + tune/Android.bp \ + fsck/Android.bp \ + label/Android.bp \ + dump/Android.bp \ + README.md diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..4f5ad6c --- /dev/null +++ b/NEWS @@ -0,0 +1,117 @@ +exfatprogs 1.1.3 - released 2021-11-11 +====================================== + +CHANGES : + * mkfs.exfat: ensure that the cluster size is greater than or + equal than the sector size. + * mkfs.exfat: replace lseek() + write() with pwrite(). + +BUG FIXES : + * mkfs.exfat: prevent an integer overflow when computing the FAT + length. + * fsck.exfat: fix a double free memory error. + +exfatprogs 1.1.2 - released 2021-05-20 +====================================== + +CHANGES : + * mkfs.exfat: set 0x80 to DriveSelect of the boot sector + +BUG FIXES : + * Fix issues on 4KB logical sector devices + * Fix issues when the sector size of of a file system is different from + that of a block device. + +exfatprogs 1.1.1 - released 2021-04-21 +====================================== + +CHANGES : + * mkfs.exfat: adjust the boundary alignment calculations to compensate + for the volume offset. + +NEW FEATURES : + * mkfs.exfat: add the "--pack-bitmap" option to relocate the allocation + bitmap to allow the FAT and the bitmap to share the same allocation + unit on flash media. + +BUG FIXES : + * Fix wrong bit operations on 64-bit big. + * Fix memory leaks in error paths. + +exfatprogs 1.1.0 - released 2021-02-09 +====================================== + +CHANGES : + * fsck.exfat: recover corrupted boot region. + +NEW FEATURES : + * exfatlabel: Print or Set volume label and serial. + * dump.exfat: Show the on-disk metadata information and the statistics. + +BUG FIXES : + * set _FILE_OFFSET_BITS=64 for Android build. + +exfatprogs 1.0.4 - released 2020-07-31 +====================================== + +CHANGES : + * fsck.exfat: display sector, cluster, and volume sizes in the human + readable format. + * fsck.exfat: reduce the elapsed time using read-ahead. + +NEW FEATURES : + * mkfs.exfat: generate pseudo unique serials while creating filesystems. + * mkfs.exfat: add the "-b" option to align the start offset of FAT and + data clusters. + * fsck.exfat: repair zero-byte files which have the NoFatChain attribute. + +BUG FIXES : + * Fix memory leaks on error handling paths. + * fsck.exfat: fix the bug that cannot access space beyond 2TB. + +exfatprogs 1.0.3 - released 2020-05-12 +====================================== + +CHANGES : + * Rename label.exfat to tune.exfat. + * tune.exfat: change argument style(-l option for print level, + -L option for setting label) + * mkfs.exfat: harmonize set volume label option with tune.exfat. + +NEW FEATURES : + * Add man page. + +BUG FIXES : + * Fix the reported build warnings/errors. + * Add memset to clean garbage in allocation. + * Fix wrong volume label array size. + * Open a device using O_EXCL to avoid formatting it while it is mounted. + * Fix incomplete "make dist" generated tarball. + + +exfatprogs 1.0.2 - released 2020-04-23 +====================================== + +CHANGES : + * Rename project name to exfatprogs. + * Replace iconv library by standard C functions mbstowcs() and wcrtomb(). + +NEW FEATURES : + * Add support for Android build system. + * label.exfat: Add support for label.exfat to set/get exfat volume label. + +BUG FIXES : + * Fix the build warnings/errors and add warning options. + * Fix several bugs(memory leak, wrong endian conversion, zero out beyond + end of file) and cleanup codes + * Fix issues on big endian system and on 32bit system. + + +exfatprogs 1.0.1 - released 2020-04-09 +====================================== + +NEW FEATURES : + * mkfs.exfat: quick/full format support + * mkfs.exfat: specify cluster size + * mkfs.exfat: set volume label + * fsck.exfat: consistency check support diff --git a/OAT.xml b/OAT.xml index 2637c23..a242354 100644 --- a/OAT.xml +++ b/OAT.xml @@ -50,19 +50,23 @@ COPYING - - - + + + + + - + + + @@ -71,7 +75,8 @@ + - \ No newline at end of file + diff --git a/README.OpenSource b/README.OpenSource index b2fac80..454f904 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -1,11 +1,11 @@ [ { - "Name" : "exfat-utils", + "Name" : "exfatprogs", "License" : "GPL-2.0-or-later", "License File" : "COPYING", - "Version Number" : "1.3.0", - "Owner" : "relan@users.noreply.github.com", - "Upstream URL" : "https://github.com/relan/exfat", - "Description" : "Free exFAT file system implementation." + "Version Number" : "1.1.3", + "Owner" : "Namjae Jeon", + "Upstream URL" : "https://github.com/exfatprogs/exfatprogs/releases/download/1.1.3/exfatprogs-1.1.3.tat.gz", + "Description" : "exFAT filesystem userspace utilities." } ] diff --git a/README.md b/README.md index b012eca..b3fbdd0 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,119 @@ -About ------ -This project aims to provide a full-featured [exFAT][1] file system implementation for Unix-like systems. It consists of a [FUSE][2] module (fuse-exfat) and a set of utilities (exfat-utils). +## exfatprogs +As new exfat filesystem is merged into linux-5.7 kernel, exfatprogs is +created as an official userspace utilities that contain all of the standard +utilities for creating and fixing and debugging exfat filesystem in linux +system. The goal of exfatprogs is to provide high performance and quality +at the level of exfat utilities in windows. And this software is licensed +under the GNU General Public License Version 2. -Supported operating systems: +## Building exfatprogs +Install prerequisite packages: +``` +For Ubuntu: + sudo apt-get install autoconf libtool pkg-config -* GNU/Linux -* Mac OS X 10.5 or later -* FreeBSD +For Fedora, RHEL: + sudo yum install autoconf automake libtool +``` -Most GNU/Linux distributions already have fuse-exfat and exfat-utils in their repositories, so you can just install and use them. The next chapter describes how to compile them from source. - -Compiling ---------- - -To build this project on GNU/Linux you need to install the following packages: - -* [git][4] -* [autoconf][5] -* [automake][6] -* [pkg-config][7] -* fuse-devel (or libfuse-dev) -* [gcc][8] -* [make][9] - -On Mac OS X: - -* autoconf -* automake -* pkg-config -* [OSXFUSE][10] -* [Xcode][11] (legacy versions include autotools but their versions are too old) - -On OpenBSD: - -* git -* autoconf (set AUTOCONF_VERSION environment variable) -* automake (set AUTOMAKE_VERSION environment variable) - -Get the source code, change directory and compile: - - git clone https://github.com/relan/exfat.git - cd exfat - autoreconf --install +Build steps: +``` + cd into the exfatprogs directory: + ./autogen.sh ./configure make - -Then install driver and utilities (from root): - make install +``` -You can remove them using this command (from root): +## Using exfatprogs +``` +- mkfs.exfat: + Build a exfat filesystem on a device or partition(e.g. /dev/hda1, dev/sda1). - make uninstall +Usage example: + 1. No option(default) : cluster size adjustment as per device size, quick format. + mkfs.exfat /dev/sda1 + 2. To change cluster size(KB or MB or Byte) user want + mkfs.exfat -c 1048576 /dev/sda1 + mkfs.exfat -c 1024K /dev/sda1 + mkfs.exfat -c 1M /dev/sda1 + 3. For full format(zero out) + mkfs.exfat -f /dev/sda1 + 4. For set volume label, use -l option with string user want. + mkfs.exfat -L "my usb" /dev/sda1 + 5. To change boundary alignment(KB or MB or Byte) user want + mkfs.exfat -b 16777216 /dev/sda1 + mkfs.exfat -b 16384K /dev/sda1 + mkfs.exfat -b 16M /dev/sda1 -Mounting --------- +- fsck.exfat: + Check the consistency of your exfat filesystem and optionally repair a corrupted device formatted by exfat. -Modern GNU/Linux distributions (with [util-linux][12] 2.18 or later) will mount exFAT volumes automatically. Anyway, you can mount manually (from root): +Usage example: + 1. check the consistency. + fsck.exfat /dev/sda1 + 2. repair and fix.(preparing) - mount.exfat-fuse /dev/spec /mnt/exfat +- tune.exfat: + Adjust tunable filesystem parameters on an exFAT filesystem -where /dev/spec is the [device file][13], /mnt/exfat is a mountpoint. +Usage example: + 1. print current volume label. + tune.exfat -l /dev/sda1 + 2. set new volume label. + tune.exfat -L "new label" /dev/sda1 + 3. print current volume serial. + tune.exfat -i /dev/sda1 + 4. set new volume serial. + tune.exfat -I 0x12345678 /dev/sda1 -Feedback --------- +- exfatlabel: + Get or Set volume label or serial -If you have any questions, issues, suggestions, bug reports, etc. please create an [issue][3]. Pull requests are also welcome! +Usage example: + 1. get current volume label. + exfatlabel /dev/sda1 + 2. set new volume label. + exfatlabel /dev/sda1 "new label" + 3. get current volume serial. + exfatlabel -i /dev/sda1 + 4. set new volume serial. + exfatlabel -i /dev/sda1 0x12345678 -[1]: https://en.wikipedia.org/wiki/ExFAT -[2]: https://en.wikipedia.org/wiki/Filesystem_in_Userspace -[3]: https://github.com/relan/exfat/issues -[4]: https://www.git-scm.com/ -[5]: https://www.gnu.org/software/autoconf/ -[6]: https://www.gnu.org/software/automake/ -[7]: http://www.freedesktop.org/wiki/Software/pkg-config/ -[8]: https://gcc.gnu.org/ -[9]: https://www.gnu.org/software/make/ -[10]: https://osxfuse.github.io/ -[11]: https://en.wikipedia.org/wiki/Xcode -[12]: https://www.kernel.org/pub/linux/utils/util-linux/ -[13]: https://en.wikipedia.org/wiki/Device_file +- dump.exfat: + Show on-disk information + +Usage example: + dump.exfat /dev/sda1 + +``` + +## Benchmarks + +Some fsck implementations were tested and compared for Samsung 64GB Pro +microSDXC UHS-I Class 10 which was filled up to 35GB with 9948 directories +and 16506 files by fsstress. + +The difference in the execution time for each testing is very small. + + +| Implementation | version | execution time (seconds) | +|----------------------|-----------------|--------------------------| +| **exfatprogs fsck** | 1.0.4 | 11.561 | +| Windows fsck | Windows 10 1809 | 11.449 | +| [exfat-fuse fsck] | 1.3.0 | 68.977 | + +[exfat-fuse fsck]: https://github.com/relan/exfat + +## Sending feedback +If you have any issues, please create [issues][1] or contact to [Namjae Jeon](mailto:linkinjeon@kernel.org) and +[Hyunchul Lee](mailto:hyc.lee@gmail.com). +[Contributions][2] are also welcome. + +[1]: https://github.com/exfatprogs/exfatprogs/issues +[2]: https://github.com/exfatprogs/exfatprogs/pulls + +## Contributor information +* Please base your pull requests on the `exfat-next` branch. +* Make sure you add 'Signed-Off' information to your commits (e.g. `git commit --signoff`). diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..552165f --- /dev/null +++ b/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +autoreconf --install --verbose diff --git a/configure.ac b/configure.ac index b675be5..0544309 100644 --- a/configure.ac +++ b/configure.ac @@ -1,57 +1,37 @@ -# -# configure.ac (30.03.15) -# Autoconf source. -# -# 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. -# +AC_PREREQ([2.68]) -AC_INIT([Free exFAT implementation], - [1.3.0], - [relan@users.noreply.github.com], - [exfat], - [https://github.com/relan/exfat]) -AM_INIT_AUTOMAKE([1.11.2 -Wall -Werror foreign subdir-objects]) +m4_define([exfat_progs_version], m4_esyscmd_s( + grep "define EXFAT_PROGS_VERSION " include/version.h | \ + awk '{print $3}' | sed 's/\"//g')) + +AC_INIT([exfatprogs], + exfat_progs_version, + [linkinjeon@kernel.org], + [exfatprogs], + [https://github.com/exfatprogs/exfatprogs]) + +AC_CONFIG_SRCDIR([config.h.in]) +AC_CONFIG_HEADER([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([foreign tar-pax dist-xz subdir-objects]) + +AC_LANG([C]) AC_PROG_CC -AC_PROG_CC_C99 -AC_PROG_RANLIB -AM_PROG_AR +AC_PROG_CC_STDC +AM_SILENT_RULES([yes]) +AC_PROG_LIBTOOL AC_SYS_LARGEFILE -AC_CANONICAL_HOST -PKG_CHECK_MODULES([UBLIO], [libublio], [ - CFLAGS="$CFLAGS $UBLIO_CFLAGS" - LIBS="$LIBS $UBLIO_LIBS" - AC_DEFINE([USE_UBLIO], [1], - [Define if block devices are not supported.]) -], [:]) -PKG_CHECK_MODULES([FUSE], [fuse]) -case "$host_os" in - *-gnu) - AC_DEFINE([_XOPEN_SOURCE], [500], [Enable pread() and pwrite().]) - AC_DEFINE([_DEFAULT_SOURCE], [], [Enable vsyslog().]) - ;; -esac -AC_CONFIG_HEADERS([libexfat/config.h]) +AC_C_BIGENDIAN + AC_CONFIG_FILES([ - libexfat/Makefile - dump/Makefile - fsck/Makefile - fuse/Makefile - label/Makefile + Makefile + lib/Makefile mkfs/Makefile - Makefile]) + fsck/Makefile + tune/Makefile + label/Makefile + dump/Makefile +]) + AC_OUTPUT diff --git a/dump/Makefile.am b/dump/Makefile.am index d081f7b..29c966e 100644 --- a/dump/Makefile.am +++ b/dump/Makefile.am @@ -1,27 +1,6 @@ -# -# Makefile.am (30.03.15) -# Automake source. -# -# 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. -# +AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +dump_exfat_LDADD = $(top_builddir)/lib/libexfat.a -sbin_PROGRAMS = dumpexfat -dist_man8_MANS = dumpexfat.8 -dumpexfat_SOURCES = main.c -dumpexfat_CPPFLAGS = -I$(top_srcdir)/libexfat -dumpexfat_LDADD = ../libexfat/libexfat.a +sbin_PROGRAMS = dump.exfat + +dump_exfat_SOURCES = dump.c diff --git a/dump/dump.c b/dump/dump.c new file mode 100644 index 0000000..7ede550 --- /dev/null +++ b/dump/dump.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Namjae Jeon + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" + +#define EXFAT_MIN_SECT_SIZE_BITS 9 +#define EXFAT_MAX_SECT_SIZE_BITS 12 +#define BITS_PER_BYTE 8 +#define BITS_PER_BYTE_MASK 0x7 + +static const unsigned char used_bit[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/ + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/ + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/ + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/ + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/ + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/ + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/ + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/ + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/ + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/ +}; + +static void usage(void) +{ + fprintf(stderr, "Usage: dump.exfat\n"); + fprintf(stderr, "\t-V | --version Show version\n"); + fprintf(stderr, "\t-h | --help Show help\n"); + + exit(EXIT_FAILURE); +} + +static struct option opts[] = { + {"version", no_argument, NULL, 'V' }, + {"help", no_argument, NULL, 'h' }, + {"?", no_argument, NULL, '?' }, + {NULL, 0, NULL, 0 } +}; + +static unsigned int exfat_count_used_clusters(unsigned char *bitmap, + unsigned long long bitmap_len) +{ + unsigned int count = 0; + unsigned long long i; + + for (i = 0; i < bitmap_len; i++) + count += used_bit[bitmap[i]]; + + return count; +} + +static int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) +{ + struct pbr *ppbr; + struct bsx64 *pbsx; + struct exfat_dentry *ed; + unsigned int root_clu_off, bitmap_clu_off, bitmap_clu; + unsigned int total_clus, used_clus, clu_offset, root_clu; + unsigned long long bitmap_len; + int ret; + unsigned char *bitmap; + char *volume_label; + + ppbr = malloc(EXFAT_MAX_SECTOR_SIZE); + if (!ppbr) { + exfat_err("Cannot allocate pbr: out of memory\n"); + return -ENOMEM; + } + + /* read main boot sector */ + if (exfat_read(bd->dev_fd, (char *)ppbr, EXFAT_MAX_SECTOR_SIZE, + 0) != (ssize_t)EXFAT_MAX_SECTOR_SIZE) { + exfat_err("main boot sector read failed\n"); + ret = -EIO; + goto free_ppbr; + } + + pbsx = &ppbr->bsx; + + if (pbsx->sect_size_bits < EXFAT_MIN_SECT_SIZE_BITS || + pbsx->sect_size_bits > EXFAT_MAX_SECT_SIZE_BITS) { + exfat_err("bogus sector size bits : %u\n", + pbsx->sect_size_bits); + ret = -EINVAL; + goto free_ppbr; + } + + if (pbsx->sect_per_clus_bits > 25 - pbsx->sect_size_bits) { + exfat_err("bogus sectors bits per cluster : %u\n", + pbsx->sect_per_clus_bits); + ret = -EINVAL; + goto free_ppbr; + } + + bd->sector_size_bits = pbsx->sect_size_bits; + bd->sector_size = 1 << pbsx->sect_size_bits; + + clu_offset = le32_to_cpu(pbsx->clu_offset); + total_clus = le32_to_cpu(pbsx->clu_count); + root_clu = le32_to_cpu(pbsx->root_cluster); + + exfat_info("-------------- Dump Boot sector region --------------\n"); + exfat_info("Volume Length(sectors): \t\t%" PRIu64 "\n", + le64_to_cpu(pbsx->vol_length)); + exfat_info("FAT Offset(sector offset): \t\t%u\n", + le32_to_cpu(pbsx->fat_offset)); + exfat_info("FAT Length(sectors): \t\t\t%u\n", + le32_to_cpu(pbsx->fat_length)); + exfat_info("Cluster Heap Offset (sector offset): \t%u\n", clu_offset); + exfat_info("Cluster Count: \t\t\t\t%u\n", total_clus); + exfat_info("Root Cluster (cluster offset): \t\t%u\n", root_clu); + exfat_info("Volume Serial: \t\t\t\t0x%x\n", le32_to_cpu(pbsx->vol_serial)); + exfat_info("Sector Size Bits: \t\t\t%u\n", pbsx->sect_size_bits); + exfat_info("Sector per Cluster bits: \t\t%u\n\n", pbsx->sect_per_clus_bits); + + bd->cluster_size = + 1 << (pbsx->sect_per_clus_bits + pbsx->sect_size_bits); + root_clu_off = exfat_clus_to_blk_dev_off(bd, clu_offset, root_clu); + + ed = malloc(sizeof(struct exfat_dentry)*3); + if (!ed) { + exfat_err("failed to allocate memory\n"); + ret = -ENOMEM; + goto free_ppbr; + } + + ret = exfat_read(bd->dev_fd, ed, sizeof(struct exfat_dentry)*3, + root_clu_off); + if (ret < 0) { + exfat_err("bitmap entry read failed: %d\n", errno); + ret = -EIO; + goto free_entry; + } + + volume_label = exfat_conv_volume_label(&ed[0]); + if (!volume_label) { + ret = -EINVAL; + goto free_entry; + } + + bitmap_clu = le32_to_cpu(ed[1].bitmap_start_clu); + bitmap_clu_off = exfat_clus_to_blk_dev_off(bd, clu_offset, + bitmap_clu); + bitmap_len = le64_to_cpu(ed[1].bitmap_size); + + exfat_info("----------------- Dump Root entries -----------------\n"); + exfat_info("Volume entry type: \t\t\t0x%x\n", ed[0].type); + exfat_info("Volume label: \t\t\t\t%s\n", volume_label); + exfat_info("Volume label character count: \t\t%u\n", ed[0].vol_char_cnt); + + exfat_info("Bitmap entry type: \t\t\t0x%x\n", ed[1].type); + exfat_info("Bitmap start cluster: \t\t\t%x\n", bitmap_clu); + exfat_info("Bitmap size: \t\t\t\t%llu\n", bitmap_len); + + exfat_info("Upcase table entry type: \t\t0x%x\n", ed[2].type); + exfat_info("Upcase table start cluster: \t\t%x\n", + le32_to_cpu(ed[2].upcase_start_clu)); + exfat_info("Upcase table size: \t\t\t%" PRIu64 "\n\n", + le64_to_cpu(ed[2].upcase_size)); + + bitmap = malloc(bitmap_len); + if (!bitmap) { + exfat_err("bitmap allocation failed\n"); + ret = -ENOMEM; + goto free_volume_label; + } + + ret = exfat_read(bd->dev_fd, bitmap, bitmap_len, bitmap_clu_off); + if (ret < 0) { + exfat_err("bitmap entry read failed: %d\n", errno); + ret = -EIO; + free(bitmap); + goto free_volume_label; + } + + total_clus = le32_to_cpu(pbsx->clu_count); + used_clus = exfat_count_used_clusters(bitmap, bitmap_len); + + exfat_info("---------------- Show the statistics ----------------\n"); + exfat_info("Cluster size: \t\t\t\t%u\n", bd->cluster_size); + exfat_info("Total Clusters: \t\t\t%u\n", total_clus); + exfat_info("Free Clusters: \t\t\t\t%u\n", total_clus-used_clus); + ret = 0; + + free(bitmap); + +free_volume_label: + free(volume_label); +free_entry: + free(ed); +free_ppbr: + free(ppbr); + return ret; +} + +int main(int argc, char *argv[]) +{ + int c; + int ret = EXIT_FAILURE; + struct exfat_blk_dev bd; + struct exfat_user_input ui; + bool version_only = false; + + init_user_input(&ui); + + if (!setlocale(LC_CTYPE, "")) + exfat_err("failed to init locale/codeset\n"); + + opterr = 0; + while ((c = getopt_long(argc, argv, "iVh", opts, NULL)) != EOF) + switch (c) { + case 'V': + version_only = true; + break; + case '?': + case 'h': + default: + usage(); + } + + show_version(); + if (version_only) + exit(EXIT_FAILURE); + + if (argc < 2) + usage(); + + memset(ui.dev_name, 0, sizeof(ui.dev_name)); + snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[1]); + + ret = exfat_get_blk_dev_info(&ui, &bd); + if (ret < 0) + goto out; + + ret = exfat_show_ondisk_all_info(&bd); + close(bd.dev_fd); + +out: + return ret; +} diff --git a/dump/dumpexfat.8 b/dump/dumpexfat.8 deleted file mode 100644 index 06022a5..0000000 --- a/dump/dumpexfat.8 +++ /dev/null @@ -1,55 +0,0 @@ -.\" Copyright (C) 2011-2018 Andrew Nayenko -.\" -.TH DUMPEXFAT 8 "July 2016" -.SH NAME -.B dumpexfat -\- dump exFAT file system -.SH SYNOPSIS -.B dumpexfat -[ -.B \-s -] -[ -.B \-u -] -[ -.B \-f -.I file -] -[ -.B \-V -] -.I device - -.SH DESCRIPTION -.B dumpexfat -dumps details about exFAT file system including low-level info. All sizes are -in bytes. - -.SH OPTIONS -Command line options available: -.TP -.B \-s -Dump only info from super block. May be useful for heavily corrupted file -systems. -.TP -.B \-u -Dump ranges of used sectors starting from 0 and separated with spaces. May be -useful for backup tools. -.TP -.B \-f file -Print out a list of fragments that compose the given file. Each fragment is -printed on its own line, as the start offset (in bytes) into the file system, -and the length (in bytes). -.TP -.BI \-V -Print version and copyright. - -.SH EXIT CODES -Zero is returned on success. Any other code means an error. - -.SH AUTHOR -Andrew Nayenko - -.SH SEE ALSO -.BR mkexfatfs (8) diff --git a/dump/main.c b/dump/main.c deleted file mode 100644 index 4f562f3..0000000 --- a/dump/main.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - main.c (08.11.10) - Prints detailed information about exFAT volume. - - 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 -#include -#include -#include -#include -#include - -static void print_generic_info(const struct exfat_super_block* sb) -{ - printf("Volume serial number 0x%08x\n", - le32_to_cpu(sb->volume_serial)); - printf("FS version %hhu.%hhu\n", - sb->version.major, sb->version.minor); - printf("Sector size %10u\n", - SECTOR_SIZE(*sb)); - printf("Cluster size %10u\n", - CLUSTER_SIZE(*sb)); -} - -static void print_sector_info(const struct exfat_super_block* sb) -{ - printf("Sectors count %10"PRIu64"\n", - le64_to_cpu(sb->sector_count)); -} - -static void print_cluster_info(const struct exfat_super_block* sb) -{ - printf("Clusters count %10u\n", - le32_to_cpu(sb->cluster_count)); -} - -static void print_other_info(const struct exfat_super_block* sb) -{ - printf("First sector %10"PRIu64"\n", - le64_to_cpu(sb->sector_start)); - printf("FAT first sector %10u\n", - le32_to_cpu(sb->fat_sector_start)); - printf("FAT sectors count %10u\n", - le32_to_cpu(sb->fat_sector_count)); - printf("First cluster sector %10u\n", - le32_to_cpu(sb->cluster_sector_start)); - printf("Root directory cluster %10u\n", - le32_to_cpu(sb->rootdir_cluster)); - printf("Volume state 0x%04hx\n", - le16_to_cpu(sb->volume_state)); - printf("FATs count %10hhu\n", - sb->fat_count); - printf("Drive number 0x%02hhx\n", - sb->drive_no); - printf("Allocated space %9hhu%%\n", - sb->allocated_percent); -} - -static int dump_sb(const char* spec) -{ - struct exfat_dev* dev; - struct exfat_super_block sb; - - dev = exfat_open(spec, EXFAT_MODE_RO); - if (dev == NULL) - return 1; - - if (exfat_read(dev, &sb, sizeof(struct exfat_super_block)) < 0) - { - exfat_close(dev); - exfat_error("failed to read from '%s'", spec); - return 1; - } - if (memcmp(sb.oem_name, "EXFAT ", sizeof(sb.oem_name)) != 0) - { - exfat_close(dev); - exfat_error("exFAT file system is not found on '%s'", spec); - return 1; - } - - print_generic_info(&sb); - print_sector_info(&sb); - print_cluster_info(&sb); - print_other_info(&sb); - - exfat_close(dev); - return 0; -} - -static void dump_sectors(struct exfat* ef) -{ - off_t a = 0, b = 0; - - printf("Used sectors "); - while (exfat_find_used_sectors(ef, &a, &b) == 0) - printf(" %"PRIu64"-%"PRIu64, a, b); - puts(""); -} - -static int dump_full(const char* spec, bool used_sectors) -{ - struct exfat ef; - uint32_t free_clusters; - uint64_t free_sectors; - - if (exfat_mount(&ef, spec, "ro") != 0) - return 1; - - free_clusters = exfat_count_free_clusters(&ef); - free_sectors = (uint64_t) free_clusters << ef.sb->spc_bits; - - printf("Volume label %15s\n", exfat_get_label(&ef)); - print_generic_info(ef.sb); - print_sector_info(ef.sb); - printf("Free sectors %10"PRIu64"\n", free_sectors); - print_cluster_info(ef.sb); - printf("Free clusters %10u\n", free_clusters); - print_other_info(ef.sb); - if (used_sectors) - dump_sectors(&ef); - - exfat_unmount(&ef); - return 0; -} - -static int dump_file_fragments(const char* spec, const char* path) -{ - struct exfat ef; - struct exfat_node* node; - cluster_t cluster; - cluster_t next_cluster; - cluster_t fragment_start_cluster; - off_t remainder; - off_t fragment_size = 0; - int rc = 0; - - if (exfat_mount(&ef, spec, "ro") != 0) - return 1; - - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - { - exfat_unmount(&ef); - exfat_error("'%s': %s", path, strerror(-rc)); - return 1; - } - - cluster = fragment_start_cluster = node->start_cluster; - remainder = node->size; - while (remainder > 0) - { - off_t lsize; - - if (CLUSTER_INVALID(*ef.sb, cluster)) - { - exfat_error("'%s' has invalid cluster %#x", path, cluster); - rc = 1; - break; - } - - lsize = MIN(CLUSTER_SIZE(*ef.sb), remainder); - fragment_size += lsize; - remainder -= lsize; - - next_cluster = exfat_next_cluster(&ef, node, cluster); - if (next_cluster != cluster + 1 || remainder == 0) - { - /* next cluster is not contiguous or this is EOF */ - printf("%"PRIu64" %"PRIu64"\n", - exfat_c2o(&ef, fragment_start_cluster), fragment_size); - /* start a new fragment */ - fragment_start_cluster = next_cluster; - fragment_size = 0; - } - cluster = next_cluster; - } - - exfat_put_node(&ef, node); - exfat_unmount(&ef); - return rc; -} - -static void usage(const char* prog) -{ - fprintf(stderr, "Usage: %s [-s] [-u] [-f file] [-V] \n", prog); - exit(1); -} - -int main(int argc, char* argv[]) -{ - int opt; - const char* spec = NULL; - bool sb_only = false; - bool used_sectors = false; - const char* file_path = NULL; - - while ((opt = getopt(argc, argv, "suf:V")) != -1) - { - switch (opt) - { - case 's': - sb_only = true; - break; - case 'u': - used_sectors = true; - break; - case 'f': - file_path = optarg; - break; - case 'V': - printf("dumpexfat %s\n", VERSION); - puts("Copyright (C) 2011-2018 Andrew Nayenko"); - return 0; - default: - usage(argv[0]); - } - } - if (argc - optind != 1) - usage(argv[0]); - spec = argv[optind]; - - if (file_path) - return dump_file_fragments(spec, file_path); - - if (sb_only) - return dump_sb(spec); - - return dump_full(spec, used_sectors); -} diff --git a/fsck/Makefile.am b/fsck/Makefile.am index 34d0f6c..57a0ede 100644 --- a/fsck/Makefile.am +++ b/fsck/Makefile.am @@ -1,33 +1,6 @@ -# -# Makefile.am (30.03.15) -# Automake source. -# -# 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. -# +AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +fsck_exfat_LDADD = $(top_builddir)/lib/libexfat.a -sbin_PROGRAMS = exfatfsck -dist_man8_MANS = exfatfsck.8 -exfatfsck_SOURCES = main.c -exfatfsck_CPPFLAGS = -I$(top_srcdir)/libexfat -exfatfsck_LDADD = ../libexfat/libexfat.a +sbin_PROGRAMS = fsck.exfat -install-exec-hook: - ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/fsck.exfat - -uninstall-hook: - rm -f $(DESTDIR)$(sbindir)/fsck.exfat +fsck_exfat_SOURCES = fsck.c repair.c fsck.h de_iter.c repair.h diff --git a/fsck/de_iter.c b/fsck/de_iter.c new file mode 100644 index 0000000..bc95c49 --- /dev/null +++ b/fsck/de_iter.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Hyunchul Lee + */ +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" +#include "fsck.h" + +static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block) +{ + off_t device_offset; + struct exfat *exfat = iter->exfat; + struct buffer_desc *desc; + unsigned int i; + + desc = &iter->buffer_desc[block & 0x01]; + device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset; + + for (i = 0; i < iter->read_size / iter->write_size; i++) { + if (desc->dirty[i]) { + if (exfat_write(exfat->blk_dev->dev_fd, + desc->buffer + i * iter->write_size, + iter->write_size, + device_offset + i * iter->write_size) + != (ssize_t)iter->write_size) + return -EIO; + desc->dirty[i] = 0; + } + } + return 0; +} + +static int read_ahead_first_blocks(struct exfat_de_iter *iter) +{ +#ifdef POSIX_FADV_WILLNEED + struct exfat *exfat = iter->exfat; + clus_t clus_count; + unsigned int size; + + clus_count = iter->parent->size / exfat->clus_size; + + if (clus_count > 1) { + iter->ra_begin_offset = 0; + iter->ra_next_clus = 1; + size = exfat->clus_size; + } else { + iter->ra_begin_offset = 0; + iter->ra_next_clus = 0; + size = iter->ra_partial_size; + } + return posix_fadvise(exfat->blk_dev->dev_fd, + exfat_c2o(exfat, iter->parent->first_clus), size, + POSIX_FADV_WILLNEED); +#else + return -ENOTSUP; +#endif +} + +/** + * read the next fragment in advance, and assume the fragment + * which covers @clus is already read. + */ +static int read_ahead_next_blocks(struct exfat_de_iter *iter, + clus_t clus, unsigned int offset, clus_t p_clus) +{ +#ifdef POSIX_FADV_WILLNEED + struct exfat *exfat = iter->exfat; + off_t device_offset; + clus_t clus_count, ra_clus, ra_p_clus; + unsigned int size; + int ret = 0; + + clus_count = iter->parent->size / exfat->clus_size; + if (clus + 1 < clus_count) { + ra_clus = clus + 1; + if (ra_clus == iter->ra_next_clus && + offset >= iter->ra_begin_offset) { + ret = get_next_clus(exfat, iter->parent, + p_clus, &ra_p_clus); + if (ra_p_clus == EXFAT_EOF_CLUSTER) + return -EIO; + + device_offset = exfat_c2o(exfat, ra_p_clus); + size = ra_clus + 1 < clus_count ? + exfat->clus_size : iter->ra_partial_size; + ret = posix_fadvise(exfat->blk_dev->dev_fd, + device_offset, size, + POSIX_FADV_WILLNEED); + iter->ra_next_clus = ra_clus + 1; + iter->ra_begin_offset = 0; + } + } else { + if (offset >= iter->ra_begin_offset && + offset + iter->ra_partial_size <= + exfat->clus_size) { + device_offset = exfat_c2o(exfat, p_clus) + + offset + iter->ra_partial_size; + ret = posix_fadvise(exfat->blk_dev->dev_fd, + device_offset, iter->ra_partial_size, + POSIX_FADV_WILLNEED); + iter->ra_begin_offset = + offset + iter->ra_partial_size; + } + } + + return ret; +#else + return -ENOTSUP; +#endif +} + +static int read_ahead_next_dir_blocks(struct exfat_de_iter *iter) +{ +#ifdef POSIX_FADV_WILLNEED + struct exfat *exfat = iter->exfat; + struct list_head *current; + struct exfat_inode *next_inode; + off_t offset; + + if (list_empty(&exfat->dir_list)) + return -EINVAL; + + current = exfat->dir_list.next; + if (iter->parent == list_entry(current, struct exfat_inode, list) && + current->next != &exfat->dir_list) { + next_inode = list_entry(current->next, struct exfat_inode, + list); + offset = exfat_c2o(exfat, next_inode->first_clus); + return posix_fadvise(exfat->blk_dev->dev_fd, offset, + iter->ra_partial_size, + POSIX_FADV_WILLNEED); + } + + return 0; +#else + return -ENOTSUP; +#endif +} + +static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block) +{ + struct exfat *exfat = iter->exfat; + struct buffer_desc *desc, *prev_desc; + off_t device_offset; + ssize_t ret; + + desc = &iter->buffer_desc[block & 0x01]; + if (block == 0) { + desc->p_clus = iter->parent->first_clus; + desc->offset = 0; + } + + /* if the buffer already contains dirty dentries, write it */ + if (write_block(iter, block)) + return -EIO; + + if (block > 0) { + if (block > iter->parent->size / iter->read_size) + return EOF; + + prev_desc = &iter->buffer_desc[(block-1) & 0x01]; + if (prev_desc->offset + 2 * iter->read_size <= + exfat->clus_size) { + desc->p_clus = prev_desc->p_clus; + desc->offset = prev_desc->offset + iter->read_size; + } else { + ret = get_next_clus(exfat, iter->parent, + prev_desc->p_clus, &desc->p_clus); + desc->offset = 0; + if (!ret && desc->p_clus == EXFAT_EOF_CLUSTER) + return EOF; + else if (ret) + return ret; + } + } + + device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset; + ret = exfat_read(exfat->blk_dev->dev_fd, desc->buffer, + iter->read_size, device_offset); + if (ret <= 0) + return ret; + + /* + * if a buffer is filled with dentries, read blocks ahead of time, + * otherwise read blocks of the next directory in advance. + */ + if (desc->buffer[iter->read_size - 32] != EXFAT_LAST) + read_ahead_next_blocks(iter, + (block * iter->read_size) / exfat->clus_size, + (block * iter->read_size) % exfat->clus_size, + desc->p_clus); + else + read_ahead_next_dir_blocks(iter); + return ret; +} + +int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, + struct exfat_inode *dir) +{ + iter->exfat = exfat; + iter->parent = dir; + iter->write_size = exfat->sect_size; + iter->read_size = exfat->clus_size <= 4*KB ? exfat->clus_size : 4*KB; + if (exfat->clus_size <= 32 * KB) + iter->ra_partial_size = MAX(4 * KB, exfat->clus_size / 2); + else + iter->ra_partial_size = exfat->clus_size / 4; + iter->ra_partial_size = MIN(iter->ra_partial_size, 8 * KB); + + if (!iter->buffer_desc) + iter->buffer_desc = exfat->buffer_desc; + + if (iter->parent->size == 0) + return EOF; + + read_ahead_first_blocks(iter); + if (read_block(iter, 0) != (ssize_t)iter->read_size) { + exfat_err("failed to read directory entries.\n"); + return -EIO; + } + + iter->de_file_offset = 0; + iter->next_read_offset = iter->read_size; + iter->max_skip_dentries = 0; + return 0; +} + +int exfat_de_iter_get(struct exfat_de_iter *iter, + int ith, struct exfat_dentry **dentry) +{ + off_t next_de_file_offset; + ssize_t ret; + unsigned int block; + + next_de_file_offset = iter->de_file_offset + + ith * sizeof(struct exfat_dentry); + block = (unsigned int)(next_de_file_offset / iter->read_size); + + if (next_de_file_offset + sizeof(struct exfat_dentry) > + iter->parent->size) + return EOF; + /* the dentry must be in current, or next block which will be read */ + if (block > iter->de_file_offset / iter->read_size + 1) + return -ERANGE; + + /* read next cluster if needed */ + if (next_de_file_offset >= iter->next_read_offset) { + ret = read_block(iter, block); + if (ret != (ssize_t)iter->read_size) + return ret; + iter->next_read_offset += iter->read_size; + } + + if (ith + 1 > iter->max_skip_dentries) + iter->max_skip_dentries = ith + 1; + + *dentry = (struct exfat_dentry *) + (iter->buffer_desc[block & 0x01].buffer + + next_de_file_offset % iter->read_size); + return 0; +} + +int exfat_de_iter_get_dirty(struct exfat_de_iter *iter, + int ith, struct exfat_dentry **dentry) +{ + off_t next_file_offset; + unsigned int block; + int ret, sect_idx; + + ret = exfat_de_iter_get(iter, ith, dentry); + if (!ret) { + next_file_offset = iter->de_file_offset + + ith * sizeof(struct exfat_dentry); + block = (unsigned int)(next_file_offset / iter->read_size); + sect_idx = (int)((next_file_offset % iter->read_size) / + iter->write_size); + iter->buffer_desc[block & 0x01].dirty[sect_idx] = 1; + } + + return ret; +} + +int exfat_de_iter_flush(struct exfat_de_iter *iter) +{ + if (write_block(iter, 0) || write_block(iter, 1)) + return -EIO; + return 0; +} + +/* + * @skip_dentries must be the largest @ith + 1 of exfat_de_iter_get + * since the last call of exfat_de_iter_advance + */ +int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries) +{ + if (skip_dentries != iter->max_skip_dentries) + return -EINVAL; + + iter->max_skip_dentries = 0; + iter->de_file_offset = iter->de_file_offset + + skip_dentries * sizeof(struct exfat_dentry); + return 0; +} + +off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter) +{ + return iter->de_file_offset; +} diff --git a/fsck/exfatfsck.8 b/fsck/exfatfsck.8 deleted file mode 100644 index a641b6c..0000000 --- a/fsck/exfatfsck.8 +++ /dev/null @@ -1,55 +0,0 @@ -.\" Copyright (C) 2011-2018 Andrew Nayenko -.\" -.TH EXFATFSCK 8 "September 2018" -.SH NAME -.B exfatfsck -\- check an exFAT file system -.SH SYNOPSIS -.B exfatfsck -[ -.B \-a -| -.B \-n -| -.B \-p -| -.B \-y -] -.I device -.br -.B exfatfsck -[ -.B \-V -] - -.SH DESCRIPTION -.B exfatfsck -checks an exFAT file system for errors. It can repair some of them. - -.SH COMMAND LINE OPTIONS -Command line options available: -.TP -.BI \-a -Automatically repair the file system. No user intervention required. -.TP -.BI \-n -No-operation mode: non-interactively check for errors, but don't write -anything to the file system. -.TP -.BI \-p -Same as \fB\-a\fR for compatibility with other *fsck. -.TP -.BI \-V -Print version and copyright. -.TP -.BI \-y -Same as \fB\-a\fR for compatibility with other *fsck. - -.SH EXIT CODES -Zero is returned if errors were not found. Any other code means an error. - -.SH AUTHOR -Andrew Nayenko - -.SH SEE ALSO -.BR fsck (8) diff --git a/fsck/fsck.c b/fsck/fsck.c new file mode 100644 index 0000000..6131d13 --- /dev/null +++ b/fsck/fsck.c @@ -0,0 +1,1648 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Namjae Jeon + * Copyright (C) 2020 Hyunchul Lee + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" +#include "fsck.h" +#include "repair.h" + +struct fsck_user_input { + struct exfat_user_input ei; + enum fsck_ui_options options; +}; + +#define EXFAT_MAX_UPCASE_CHARS 0x10000 + +#ifdef WORDS_BIGENDIAN +typedef __u8 bitmap_t; +#else +typedef __u32 bitmap_t; +#endif + +#define BITS_PER (sizeof(bitmap_t) * 8) +#define BIT_MASK(__c) (1 << ((__c) % BITS_PER)) +#define BIT_ENTRY(__c) ((__c) / BITS_PER) + +#define EXFAT_BITMAP_SIZE(__c_count) \ + (DIV_ROUND_UP(__c_count, BITS_PER) * sizeof(bitmap_t)) +#define EXFAT_BITMAP_GET(__bmap, __c) \ + (((bitmap_t *)(__bmap))[BIT_ENTRY(__c)] & BIT_MASK(__c)) +#define EXFAT_BITMAP_SET(__bmap, __c) \ + (((bitmap_t *)(__bmap))[BIT_ENTRY(__c)] |= \ + BIT_MASK(__c)) + +#define FSCK_EXIT_NO_ERRORS 0x00 +#define FSCK_EXIT_CORRECTED 0x01 +#define FSCK_EXIT_NEED_REBOOT 0x02 +#define FSCK_EXIT_ERRORS_LEFT 0x04 +#define FSCK_EXIT_OPERATION_ERROR 0x08 +#define FSCK_EXIT_SYNTAX_ERROR 0x10 +#define FSCK_EXIT_USER_CANCEL 0x20 +#define FSCK_EXIT_LIBRARY_ERROR 0x80 + +struct exfat_stat { + long dir_count; + long file_count; + long error_count; + long fixed_count; +}; + +struct path_resolve_ctx { + struct exfat_inode *ancestors[255]; + __le16 utf16_path[PATH_MAX + 2]; + char local_path[PATH_MAX * MB_LEN_MAX + 1]; +}; + +struct exfat_stat exfat_stat; +struct path_resolve_ctx path_resolve_ctx; + +static struct option opts[] = { + {"repair", no_argument, NULL, 'r' }, + {"repair-yes", no_argument, NULL, 'y' }, + {"repair-no", no_argument, NULL, 'n' }, + {"repair-auto", no_argument, NULL, 'p' }, + {"version", no_argument, NULL, 'V' }, + {"verbose", no_argument, NULL, 'v' }, + {"help", no_argument, NULL, 'h' }, + {"?", no_argument, NULL, '?' }, + {NULL, 0, NULL, 0 } +}; + +static void usage(char *name) +{ + fprintf(stderr, "Usage: %s\n", name); + fprintf(stderr, "\t-r | --repair Repair interactively\n"); + fprintf(stderr, "\t-y | --repair-yes Repair without ask\n"); + fprintf(stderr, "\t-n | --repair-no No repair\n"); + fprintf(stderr, "\t-p | --repair-auto Repair automatically\n"); + fprintf(stderr, "\t-a Repair automatically\n"); + fprintf(stderr, "\t-V | --version Show version\n"); + fprintf(stderr, "\t-v | --verbose Print debug\n"); + fprintf(stderr, "\t-h | --help Show help\n"); + + exit(FSCK_EXIT_SYNTAX_ERROR); +} + +#define fsck_err(parent, inode, fmt, ...) \ +({ \ + resolve_path_parent(&path_resolve_ctx, \ + parent, inode); \ + exfat_err("ERROR: %s: " fmt, \ + path_resolve_ctx.local_path, \ + ##__VA_ARGS__); \ +}) + +static struct exfat_inode *alloc_exfat_inode(__u16 attr) +{ + struct exfat_inode *node; + int size; + + size = offsetof(struct exfat_inode, name) + NAME_BUFFER_SIZE; + node = (struct exfat_inode *)calloc(1, size); + if (!node) { + exfat_err("failed to allocate exfat_node\n"); + return NULL; + } + + node->parent = NULL; + INIT_LIST_HEAD(&node->children); + INIT_LIST_HEAD(&node->sibling); + INIT_LIST_HEAD(&node->list); + + node->last_pclus = EXFAT_EOF_CLUSTER; + node->attr = attr; + if (attr & ATTR_SUBDIR) + exfat_stat.dir_count++; + else + exfat_stat.file_count++; + return node; +} + +static void free_exfat_inode(struct exfat_inode *node) +{ + free(node); +} + +static void inode_free_children(struct exfat_inode *dir, bool file_only) +{ + struct exfat_inode *node, *i; + + list_for_each_entry_safe(node, i, &dir->children, sibling) { + if (file_only) { + if (!(node->attr & ATTR_SUBDIR)) { + list_del(&node->sibling); + free_exfat_inode(node); + } + } else { + list_del(&node->sibling); + list_del(&node->list); + free_exfat_inode(node); + } + } +} + +static void inode_free_file_children(struct exfat_inode *dir) +{ + inode_free_children(dir, true); +} + +/* delete @child and all ancestors that does not have + * children + */ +static void inode_free_ancestors(struct exfat_inode *child) +{ + struct exfat_inode *parent; + + if (!list_empty(&child->children)) + return; + + do { + if (!(child->attr & ATTR_SUBDIR)) { + exfat_err("not directory.\n"); + return; + } + + parent = child->parent; + list_del(&child->sibling); + free_exfat_inode(child); + + child = parent; + } while (child && list_empty(&child->children)); + + return; +} + +static void free_exfat(struct exfat *exfat) +{ + int i; + + if (exfat) { + if (exfat->bs) + free(exfat->bs); + if (exfat->alloc_bitmap) + free(exfat->alloc_bitmap); + if (exfat->disk_bitmap) + free(exfat->disk_bitmap); + for (i = 0; i < 2; i++) { + if (exfat->buffer_desc[i].buffer) + free(exfat->buffer_desc[i].buffer); + if (exfat->buffer_desc[i].dirty) + free(exfat->buffer_desc[i].dirty); + } + free(exfat); + } +} + +static int init_exfat(struct exfat *exfat, struct pbr *bs) +{ + int i; + + INIT_LIST_HEAD(&exfat->dir_list); + exfat->bs = bs; + exfat->clus_count = le32_to_cpu(bs->bsx.clu_count); + exfat->clus_size = EXFAT_CLUSTER_SIZE(bs); + exfat->sect_size = EXFAT_SECTOR_SIZE(bs); + + /* TODO: bitmap could be very large. */ + exfat->alloc_bitmap = (char *)calloc(1, + EXFAT_BITMAP_SIZE(exfat->clus_count)); + if (!exfat->alloc_bitmap) { + exfat_err("failed to allocate bitmap\n"); + goto err; + } + + exfat->disk_bitmap = (char *)malloc( + EXFAT_BITMAP_SIZE(exfat->clus_count)); + if (!exfat->disk_bitmap) { + exfat_err("failed to allocate bitmap\n"); + goto err; + } + + /* allocate cluster buffers */ + for (i = 0; i < 2; i++) { + exfat->buffer_desc[i].buffer = + (char *)malloc(exfat->clus_size); + if (!exfat->buffer_desc[i].buffer) + goto err; + exfat->buffer_desc[i].dirty = + (char *)calloc( + (exfat->clus_size / exfat->sect_size), 1); + if (!exfat->buffer_desc[i].dirty) + goto err; + } + return 0; +err: + free_exfat(exfat); + return -ENOMEM; +} + +static void exfat_free_dir_list(struct exfat *exfat) +{ + struct exfat_inode *dir, *i; + + list_for_each_entry_safe(dir, i, &exfat->dir_list, list) { + inode_free_file_children(dir); + list_del(&dir->list); + free_exfat_inode(dir); + } +} + +/* + * get references of ancestors that include @child until the count of + * ancestors is not larger than @count and the count of characters of + * their names is not larger than @max_char_len. + * return true if root is reached. + */ +bool get_ancestors(struct exfat_inode *child, + struct exfat_inode **ancestors, int count, + int max_char_len, + int *ancestor_count) +{ + struct exfat_inode *dir; + int name_len, char_len; + int root_depth, depth, i; + + root_depth = 0; + char_len = 0; + max_char_len += 1; + + dir = child; + while (dir) { + name_len = exfat_utf16_len(dir->name, NAME_BUFFER_SIZE); + if (char_len + name_len > max_char_len) + break; + + /* include '/' */ + char_len += name_len + 1; + root_depth++; + + dir = dir->parent; + } + + depth = MIN(root_depth, count); + + for (dir = child, i = depth - 1; i >= 0; dir = dir->parent, i--) + ancestors[i] = dir; + + *ancestor_count = depth; + return dir == NULL; +} + +static int resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child) +{ + int depth, i; + int name_len; + __le16 *utf16_path; + static const __le16 utf16_slash = cpu_to_le16(0x002F); + static const __le16 utf16_null = cpu_to_le16(0x0000); + size_t in_size; + + ctx->local_path[0] = '\0'; + + get_ancestors(child, + ctx->ancestors, + sizeof(ctx->ancestors) / sizeof(ctx->ancestors[0]), + PATH_MAX, + &depth); + + utf16_path = ctx->utf16_path; + for (i = 0; i < depth; i++) { + name_len = exfat_utf16_len(ctx->ancestors[i]->name, + NAME_BUFFER_SIZE); + memcpy((char *)utf16_path, (char *)ctx->ancestors[i]->name, + name_len * 2); + utf16_path += name_len; + memcpy((char *)utf16_path, &utf16_slash, sizeof(utf16_slash)); + utf16_path++; + } + + if (depth > 0) + utf16_path--; + memcpy((char *)utf16_path, &utf16_null, sizeof(utf16_null)); + utf16_path++; + + in_size = (utf16_path - ctx->utf16_path) * sizeof(__le16); + return exfat_utf16_dec(ctx->utf16_path, in_size, + ctx->local_path, sizeof(ctx->local_path)); +} + +static int resolve_path_parent(struct path_resolve_ctx *ctx, + struct exfat_inode *parent, struct exfat_inode *child) +{ + int ret; + struct exfat_inode *old; + + old = child->parent; + child->parent = parent; + + ret = resolve_path(ctx, child); + child->parent = old; + return ret; +} + +#define repair_file_ask(iter, inode, code, fmt, ...) \ +({ \ + resolve_path_parent(&path_resolve_ctx, \ + (iter)->parent, inode); \ + exfat_repair_ask((iter)->exfat, code, \ + "ERROR: %s: " fmt, \ + path_resolve_ctx.local_path, \ + ##__VA_ARGS__); \ +}) + +static inline bool heap_clus(struct exfat *exfat, clus_t clus) +{ + return clus >= EXFAT_FIRST_CLUSTER && + (clus - EXFAT_FIRST_CLUSTER) < exfat->clus_count; +} + +int get_next_clus(struct exfat *exfat, struct exfat_inode *node, + clus_t clus, clus_t *next) +{ + off_t offset; + + *next = EXFAT_EOF_CLUSTER; + + if (!heap_clus(exfat, clus)) + return -EINVAL; + + if (node->is_contiguous) { + *next = clus + 1; + return 0; + } + + offset = (off_t)le32_to_cpu(exfat->bs->bsx.fat_offset) << + exfat->bs->bsx.sect_size_bits; + offset += sizeof(clus_t) * clus; + + if (exfat_read(exfat->blk_dev->dev_fd, next, sizeof(*next), offset) + != sizeof(*next)) + return -EIO; + *next = le32_to_cpu(*next); + return 0; +} + +static int set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus) +{ + off_t offset; + + offset = le32_to_cpu(exfat->bs->bsx.fat_offset) << + exfat->bs->bsx.sect_size_bits; + offset += sizeof(clus_t) * clus; + + if (exfat_write(exfat->blk_dev->dev_fd, &next_clus, sizeof(next_clus), + offset) != sizeof(next_clus)) + return -EIO; + return 0; +} + +static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) +{ + struct exfat_dentry *stream_de; + clus_t clus, prev, next; + uint64_t count, max_count; + + clus = node->first_clus; + prev = EXFAT_EOF_CLUSTER; + count = 0; + max_count = DIV_ROUND_UP(node->size, exfat->clus_size); + + if (node->size == 0 && node->first_clus == EXFAT_FREE_CLUSTER) + return 0; + + /* the first cluster is wrong */ + if ((node->size == 0 && node->first_clus != EXFAT_FREE_CLUSTER) || + (node->size > 0 && !heap_clus(exfat, node->first_clus))) { + if (repair_file_ask(&exfat->de_iter, node, + ER_FILE_FIRST_CLUS, "first cluster is wrong")) + goto truncate_file; + else + return -EINVAL; + } + + while (clus != EXFAT_EOF_CLUSTER) { + if (count >= max_count) { + if (node->is_contiguous) + break; + if (repair_file_ask(&exfat->de_iter, node, + ER_FILE_SMALLER_SIZE, + "more clusters are allocated. " + "truncate to %" PRIu64 " bytes", + count * exfat->clus_size)) + goto truncate_file; + else + return -EINVAL; + } + + /* + * This cluster is already allocated. it may be shared with + * the other file, or there is a loop in cluster chain. + */ + if (EXFAT_BITMAP_GET(exfat->alloc_bitmap, + clus - EXFAT_FIRST_CLUSTER)) { + if (repair_file_ask(&exfat->de_iter, node, + ER_FILE_DUPLICATED_CLUS, + "cluster is already allocated for " + "the other file. truncated to %" + PRIu64 " bytes", + count * exfat->clus_size)) + goto truncate_file; + else + return -EINVAL; + } + + if (!EXFAT_BITMAP_GET(exfat->disk_bitmap, + clus - EXFAT_FIRST_CLUSTER)) { + if (repair_file_ask(&exfat->de_iter, node, + ER_FILE_INVALID_CLUS, + "cluster is marked as free. truncate to %" PRIu64 " bytes", + count * exfat->clus_size)) + goto truncate_file; + + else + return -EINVAL; + } + + /* This cluster is allocated or not */ + if (get_next_clus(exfat, node, clus, &next)) + goto truncate_file; + if (!node->is_contiguous) { + if (!heap_clus(exfat, next) && + next != EXFAT_EOF_CLUSTER) { + if (repair_file_ask(&exfat->de_iter, node, + ER_FILE_INVALID_CLUS, + "broken cluster chain. " + "truncate to %" + PRIu64 " bytes", + count * exfat->clus_size)) + goto truncate_file; + + else + return -EINVAL; + } + } + + count++; + EXFAT_BITMAP_SET(exfat->alloc_bitmap, + clus - EXFAT_FIRST_CLUSTER); + prev = clus; + clus = next; + } + + if (count < max_count) { + if (repair_file_ask(&exfat->de_iter, node, + ER_FILE_LARGER_SIZE, "less clusters are allocated. " + "truncates to %" PRIu64 " bytes", + count * exfat->clus_size)) + goto truncate_file; + else + return -EINVAL; + } + + return 0; +truncate_file: + node->size = count * exfat->clus_size; + if (!heap_clus(exfat, prev)) + node->first_clus = EXFAT_FREE_CLUSTER; + + exfat_de_iter_get_dirty(&exfat->de_iter, 1, &stream_de); + if (count * exfat->clus_size < + le64_to_cpu(stream_de->stream_valid_size)) + stream_de->stream_valid_size = cpu_to_le64( + count * exfat->clus_size); + if (!heap_clus(exfat, prev)) + stream_de->stream_start_clu = EXFAT_FREE_CLUSTER; + stream_de->stream_size = cpu_to_le64( + count * exfat->clus_size); + + /* remaining clusters will be freed while FAT is compared with + * alloc_bitmap. + */ + if (!node->is_contiguous && heap_clus(exfat, prev)) + return set_fat(exfat, prev, EXFAT_EOF_CLUSTER); + return 1; +} + +static bool root_get_clus_count(struct exfat *exfat, struct exfat_inode *node, + clus_t *clus_count) +{ + clus_t clus; + + clus = node->first_clus; + *clus_count = 0; + + do { + if (!heap_clus(exfat, clus)) { + exfat_err("/: bad cluster. 0x%x\n", clus); + return false; + } + + if (EXFAT_BITMAP_GET(exfat->alloc_bitmap, + clus - EXFAT_FIRST_CLUSTER)) { + exfat_err("/: cluster is already allocated, or " + "there is a loop in cluster chain\n"); + return false; + } + + EXFAT_BITMAP_SET(exfat->alloc_bitmap, + clus - EXFAT_FIRST_CLUSTER); + + if (get_next_clus(exfat, node, clus, &clus) != 0) { + exfat_err("/: broken cluster chain\n"); + return false; + } + + (*clus_count)++; + } while (clus != EXFAT_EOF_CLUSTER); + return true; +} + +static off_t exfat_s2o(struct exfat *exfat, off_t sect) +{ + return sect << exfat->bs->bsx.sect_size_bits; +} + +off_t exfat_c2o(struct exfat *exfat, unsigned int clus) +{ + if (clus < EXFAT_FIRST_CLUSTER) + return ~0L; + + return exfat_s2o(exfat, le32_to_cpu(exfat->bs->bsx.clu_offset) + + ((off_t)(clus - EXFAT_FIRST_CLUSTER) << + exfat->bs->bsx.sect_per_clus_bits)); +} + +static int boot_region_checksum(int dev_fd, + int bs_offset, unsigned int sect_size) +{ + void *sect; + unsigned int i; + uint32_t checksum; + int ret = 0; + + sect = malloc(sect_size); + if (!sect) + return -ENOMEM; + + checksum = 0; + for (i = 0; i < 11; i++) { + if (exfat_read(dev_fd, sect, sect_size, + bs_offset * sect_size + i * sect_size) != + (ssize_t)sect_size) { + exfat_err("failed to read boot region\n"); + ret = -EIO; + goto out; + } + boot_calc_checksum(sect, sect_size, i == 0, &checksum); + } + + if (exfat_read(dev_fd, sect, sect_size, + bs_offset * sect_size + 11 * sect_size) != + (ssize_t)sect_size) { + exfat_err("failed to read a boot checksum sector\n"); + ret = -EIO; + goto out; + } + + for (i = 0; i < sect_size/sizeof(checksum); i++) { + if (le32_to_cpu(((__le32 *)sect)[i]) != checksum) { + exfat_err("checksum of boot region is not correct. %#x, but expected %#x\n", + le32_to_cpu(((__le32 *)sect)[i]), checksum); + ret = -EINVAL; + goto out; + } + } +out: + free(sect); + return ret; +} + +static int exfat_mark_volume_dirty(struct exfat *exfat, bool dirty) +{ + uint16_t flags; + + if (!(exfat->options & FSCK_OPTS_REPAIR_WRITE)) + return 0; + + flags = le16_to_cpu(exfat->bs->bsx.vol_flags); + if (dirty) + flags |= 0x02; + else + flags &= ~0x02; + + exfat->bs->bsx.vol_flags = cpu_to_le16(flags); + if (exfat_write(exfat->blk_dev->dev_fd, exfat->bs, + sizeof(struct pbr), 0) != (ssize_t)sizeof(struct pbr)) { + exfat_err("failed to set VolumeDirty\n"); + return -EIO; + } + + if (fsync(exfat->blk_dev->dev_fd) != 0) { + exfat_err("failed to set VolumeDirty\n"); + return -EIO; + } + return 0; +} + +static int read_boot_region(struct exfat_blk_dev *bd, struct pbr **pbr, + int bs_offset, unsigned int sect_size, + bool verbose) +{ + struct pbr *bs; + int ret = -EINVAL; + + *pbr = NULL; + bs = (struct pbr *)malloc(sizeof(struct pbr)); + if (!bs) { + exfat_err("failed to allocate memory\n"); + return -ENOMEM; + } + + if (exfat_read(bd->dev_fd, bs, sizeof(*bs), + bs_offset * sect_size) != (ssize_t)sizeof(*bs)) { + exfat_err("failed to read a boot sector\n"); + ret = -EIO; + goto err; + } + + if (memcmp(bs->bpb.oem_name, "EXFAT ", 8) != 0) { + if (verbose) + exfat_err("failed to find exfat file system\n"); + goto err; + } + + ret = boot_region_checksum(bd->dev_fd, bs_offset, sect_size); + if (ret < 0) + goto err; + + ret = -EINVAL; + if (EXFAT_SECTOR_SIZE(bs) < 512 || EXFAT_SECTOR_SIZE(bs) > 4 * KB) { + if (verbose) + exfat_err("too small or big sector size: %d\n", + EXFAT_SECTOR_SIZE(bs)); + goto err; + } + + if (EXFAT_CLUSTER_SIZE(bs) > 32 * MB) { + if (verbose) + exfat_err("too big cluster size: %d\n", + EXFAT_CLUSTER_SIZE(bs)); + goto err; + } + + if (bs->bsx.fs_version[1] != 1 || bs->bsx.fs_version[0] != 0) { + if (verbose) + exfat_err("unsupported exfat version: %d.%d\n", + bs->bsx.fs_version[1], bs->bsx.fs_version[0]); + goto err; + } + + if (bs->bsx.num_fats != 1) { + if (verbose) + exfat_err("unsupported FAT count: %d\n", + bs->bsx.num_fats); + goto err; + } + + if (le64_to_cpu(bs->bsx.vol_length) * EXFAT_SECTOR_SIZE(bs) > + bd->size) { + if (verbose) + exfat_err("too large sector count: %" PRIu64 ", expected: %llu\n", + le64_to_cpu(bs->bsx.vol_length), + bd->num_sectors); + goto err; + } + + if (le32_to_cpu(bs->bsx.clu_count) * EXFAT_CLUSTER_SIZE(bs) > + bd->size) { + if (verbose) + exfat_err("too large cluster count: %u, expected: %u\n", + le32_to_cpu(bs->bsx.clu_count), + bd->num_clusters); + goto err; + } + + *pbr = bs; + return 0; +err: + free(bs); + return ret; +} + +static int restore_boot_region(struct exfat_blk_dev *bd, unsigned int sect_size) +{ + int i; + char *sector; + int ret; + + sector = malloc(sect_size); + if (!sector) + return -ENOMEM; + + for (i = 0; i < 12; i++) { + if (exfat_read(bd->dev_fd, sector, sect_size, + BACKUP_BOOT_SEC_IDX * sect_size + + i * sect_size) != + (ssize_t)sect_size) { + ret = -EIO; + goto free_sector; + } + if (i == 0) + ((struct pbr *)sector)->bsx.perc_in_use = 0xff; + + if (exfat_write(bd->dev_fd, sector, sect_size, + BOOT_SEC_IDX * sect_size + + i * sect_size) != + (ssize_t)sect_size) { + ret = -EIO; + goto free_sector; + } + } + + if (fsync(bd->dev_fd)) { + ret = -EIO; + goto free_sector; + } + ret = 0; + +free_sector: + free(sector); + return ret; +} + +static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) +{ + struct pbr *boot_sect; + unsigned int sect_size; + int ret; + + /* First, find out the exfat sector size */ + boot_sect = malloc(sizeof(*boot_sect)); + if (boot_sect == NULL) + return -ENOMEM; + + if (exfat_read(exfat->blk_dev->dev_fd, boot_sect, + sizeof(*boot_sect), 0) != (ssize_t)sizeof(*boot_sect)) { + exfat_err("failed to read Main boot sector\n"); + return -EIO; + } + + sect_size = 1 << boot_sect->bsx.sect_size_bits; + free(boot_sect); + + /* check boot regions */ + ret = read_boot_region(exfat->blk_dev, bs, + BOOT_SEC_IDX, sect_size, true); + if (ret == -EINVAL && exfat_repair_ask(exfat, ER_BS_BOOT_REGION, + "boot region is corrupted. try to restore the region from backup" + )) { + const unsigned int sector_sizes[] = {512, 4096, 1024, 2048}; + unsigned int i; + + if (sect_size >= 512 && sect_size <= EXFAT_MAX_SECTOR_SIZE) { + ret = read_boot_region(exfat->blk_dev, bs, + BACKUP_BOOT_SEC_IDX, sect_size, + false); + if (!ret) + goto restore; + } + + for (i = 0; i < sizeof(sector_sizes)/sizeof(sector_sizes[0]); i++) { + if (sector_sizes[i] == sect_size) + continue; + + ret = read_boot_region(exfat->blk_dev, bs, + BACKUP_BOOT_SEC_IDX, + sector_sizes[i], false); + if (!ret) { + sect_size = sector_sizes[i]; + goto restore; + } + } + exfat_err("backup boot region is also corrupted\n"); + } + + return ret; +restore: + ret = restore_boot_region(exfat->blk_dev, sect_size); + if (ret) { + exfat_err("failed to restore boot region from backup\n"); + free(*bs); + *bs = NULL; + } + return ret; +} + +static void dentry_calc_checksum(struct exfat_dentry *dentry, + __le16 *checksum, bool primary) +{ + unsigned int i; + uint8_t *bytes; + + bytes = (uint8_t *)dentry; + + *checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[0]; + *checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[1]; + + i = primary ? 4 : 2; + for (; i < sizeof(*dentry); i++) { + *checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[i]; + } +} + +static __le16 file_calc_checksum(struct exfat_de_iter *iter) +{ + __le16 checksum; + struct exfat_dentry *file_de, *de; + int i; + + checksum = 0; + exfat_de_iter_get(iter, 0, &file_de); + + dentry_calc_checksum(file_de, &checksum, true); + for (i = 1; i <= file_de->file_num_ext; i++) { + exfat_de_iter_get(iter, i, &de); + dentry_calc_checksum(de, &checksum, false); + } + + return checksum; +} + +/* + * return 0 if there are no errors, or 1 if errors are fixed, or + * an error code + */ +static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node) +{ + struct exfat *exfat = iter->exfat; + struct exfat_dentry *dentry; + int ret = 0; + uint16_t checksum; + bool valid = true; + + ret = check_clus_chain(exfat, node); + if (ret < 0) + return ret; + + if (node->size > le32_to_cpu(exfat->bs->bsx.clu_count) * + (uint64_t)exfat->clus_size) { + fsck_err(iter->parent, node, + "size %" PRIu64 " is greater than cluster heap\n", + node->size); + valid = false; + } + + if (node->size == 0 && node->is_contiguous) { + if (repair_file_ask(iter, node, ER_FILE_ZERO_NOFAT, + "empty, but has no Fat chain\n")) { + exfat_de_iter_get_dirty(iter, 1, &dentry); + dentry->stream_flags &= ~EXFAT_SF_CONTIGUOUS; + ret = 1; + } else + valid = false; + } + + if ((node->attr & ATTR_SUBDIR) && + node->size % exfat->clus_size != 0) { + fsck_err(iter->parent, node, + "directory size %" PRIu64 " is not divisible by %d\n", + node->size, exfat->clus_size); + valid = false; + } + + checksum = file_calc_checksum(iter); + exfat_de_iter_get(iter, 0, &dentry); + if (checksum != le16_to_cpu(dentry->file_checksum)) { + if (repair_file_ask(iter, node, ER_DE_CHECKSUM, + "the checksum of a file is wrong")) { + exfat_de_iter_get_dirty(iter, 0, &dentry); + dentry->file_checksum = cpu_to_le16(checksum); + ret = 1; + } else + valid = false; + } + + return valid ? ret : -EINVAL; +} + +static int read_file_dentries(struct exfat_de_iter *iter, + struct exfat_inode **new_node, int *skip_dentries) +{ + struct exfat_dentry *file_de, *stream_de, *name_de; + struct exfat_inode *node; + int i, ret; + + /* TODO: mtime, atime, ... */ + + ret = exfat_de_iter_get(iter, 0, &file_de); + if (ret || file_de->type != EXFAT_FILE) { + exfat_err("failed to get file dentry. %d\n", ret); + return -EINVAL; + } + ret = exfat_de_iter_get(iter, 1, &stream_de); + if (ret || stream_de->type != EXFAT_STREAM) { + exfat_err("failed to get stream dentry. %d\n", ret); + return -EINVAL; + } + + *new_node = NULL; + node = alloc_exfat_inode(le16_to_cpu(file_de->file_attr)); + if (!node) + return -ENOMEM; + + if (file_de->file_num_ext < 2) { + exfat_err("too few secondary count. %d\n", + file_de->file_num_ext); + free_exfat_inode(node); + return -EINVAL; + } + + for (i = 2; i <= file_de->file_num_ext; i++) { + ret = exfat_de_iter_get(iter, i, &name_de); + if (ret || name_de->type != EXFAT_NAME) { + exfat_err("failed to get name dentry. %d\n", ret); + ret = -EINVAL; + goto err; + } + + memcpy(node->name + + (i-2) * ENTRY_NAME_MAX, name_de->name_unicode, + sizeof(name_de->name_unicode)); + } + + node->first_clus = le32_to_cpu(stream_de->stream_start_clu); + node->is_contiguous = + ((stream_de->stream_flags & EXFAT_SF_CONTIGUOUS) != 0); + node->size = le64_to_cpu(stream_de->stream_size); + + if (node->size < le64_to_cpu(stream_de->stream_valid_size)) { + if (repair_file_ask(iter, node, ER_FILE_VALID_SIZE, + "valid size %" PRIu64 " greater than size %" PRIu64, + le64_to_cpu(stream_de->stream_valid_size), + node->size)) { + exfat_de_iter_get_dirty(iter, 1, &stream_de); + stream_de->stream_valid_size = + stream_de->stream_size; + } else { + ret = -EINVAL; + goto err; + } + } + + *skip_dentries = (file_de->file_num_ext + 1); + *new_node = node; + return 0; +err: + *skip_dentries = 0; + *new_node = NULL; + free_exfat_inode(node); + return ret; +} + +static int read_file(struct exfat_de_iter *de_iter, + struct exfat_inode **new_node, int *dentry_count) +{ + struct exfat_inode *node; + int ret; + + *new_node = NULL; + + ret = read_file_dentries(de_iter, &node, dentry_count); + if (ret) + return ret; + + ret = check_inode(de_iter, node); + if (ret < 0) { + free_exfat_inode(node); + return -EINVAL; + } + + *new_node = node; + return ret; +} + +static bool read_volume_label(struct exfat_de_iter *iter) +{ + struct exfat *exfat; + struct exfat_dentry *dentry; + __le16 disk_label[VOLUME_LABEL_MAX_LEN]; + + exfat = iter->exfat; + if (exfat_de_iter_get(iter, 0, &dentry)) + return false; + + if (dentry->vol_char_cnt == 0) + return true; + + if (dentry->vol_char_cnt > VOLUME_LABEL_MAX_LEN) { + exfat_err("too long label. %d\n", dentry->vol_char_cnt); + return false; + } + + memcpy(disk_label, dentry->vol_label, sizeof(disk_label)); + if (exfat_utf16_dec(disk_label, dentry->vol_char_cnt*2, + exfat->volume_label, sizeof(exfat->volume_label)) < 0) { + exfat_err("failed to decode volume label\n"); + return false; + } + + exfat_info("volume label [%s]\n", exfat->volume_label); + return true; +} + +static void exfat_bitmap_set_range(struct exfat *exfat, + clus_t start_clus, clus_t count) +{ + clus_t clus; + + if (!heap_clus(exfat, start_clus) || + !heap_clus(exfat, start_clus + count)) + return; + + clus = start_clus; + while (clus < start_clus + count) { + EXFAT_BITMAP_SET(exfat->alloc_bitmap, + clus - EXFAT_FIRST_CLUSTER); + clus++; + } +} + +static bool read_bitmap(struct exfat_de_iter *iter) +{ + struct exfat_dentry *dentry; + struct exfat *exfat; + + exfat = iter->exfat; + if (exfat_de_iter_get(iter, 0, &dentry)) + return false; + + exfat_debug("start cluster %#x, size %#" PRIx64 "\n", + le32_to_cpu(dentry->bitmap_start_clu), + le64_to_cpu(dentry->bitmap_size)); + + if (le64_to_cpu(dentry->bitmap_size) < + DIV_ROUND_UP(exfat->clus_count, 8)) { + exfat_err("invalid size of allocation bitmap. 0x%" PRIx64 "\n", + le64_to_cpu(dentry->bitmap_size)); + return false; + } + if (!heap_clus(exfat, le32_to_cpu(dentry->bitmap_start_clu))) { + exfat_err("invalid start cluster of allocate bitmap. 0x%x\n", + le32_to_cpu(dentry->bitmap_start_clu)); + return false; + } + + exfat->disk_bitmap_clus = le32_to_cpu(dentry->bitmap_start_clu); + exfat->disk_bitmap_size = DIV_ROUND_UP(exfat->clus_count, 8); + + exfat_bitmap_set_range(exfat, le64_to_cpu(dentry->bitmap_start_clu), + DIV_ROUND_UP(exfat->disk_bitmap_size, + exfat->clus_size)); + + if (exfat_read(exfat->blk_dev->dev_fd, exfat->disk_bitmap, + exfat->disk_bitmap_size, + exfat_c2o(exfat, exfat->disk_bitmap_clus)) != + (ssize_t)exfat->disk_bitmap_size) + return false; + + return true; +} + +static bool read_upcase_table(struct exfat_de_iter *iter) +{ + struct exfat_dentry *dentry; + struct exfat *exfat; + ssize_t size; + __le16 *upcase; + __le32 checksum; + + exfat = iter->exfat; + + if (exfat_de_iter_get(iter, 0, &dentry)) + return false; + + if (!heap_clus(exfat, le32_to_cpu(dentry->upcase_start_clu))) { + exfat_err("invalid start cluster of upcase table. 0x%x\n", + le32_to_cpu(dentry->upcase_start_clu)); + return false; + } + + size = (ssize_t)le64_to_cpu(dentry->upcase_size); + if (size > (ssize_t)(EXFAT_MAX_UPCASE_CHARS * sizeof(__le16)) || + size == 0 || size % sizeof(__le16)) { + exfat_err("invalid size of upcase table. 0x%" PRIx64 "\n", + le64_to_cpu(dentry->upcase_size)); + return false; + } + + upcase = (__le16 *)malloc(size); + if (!upcase) { + exfat_err("failed to allocate upcase table\n"); + return false; + } + + if (exfat_read(exfat->blk_dev->dev_fd, upcase, size, + exfat_c2o(exfat, + le32_to_cpu(dentry->upcase_start_clu))) != size) { + exfat_err("failed to read upcase table\n"); + free(upcase); + return false; + } + + checksum = 0; + boot_calc_checksum((unsigned char *)upcase, size, false, &checksum); + if (le32_to_cpu(dentry->upcase_checksum) != checksum) { + exfat_err("corrupted upcase table %#x (expected: %#x)\n", + checksum, le32_to_cpu(dentry->upcase_checksum)); + free(upcase); + return false; + } + + exfat_bitmap_set_range(exfat, le32_to_cpu(dentry->upcase_start_clu), + DIV_ROUND_UP(le64_to_cpu(dentry->upcase_size), + exfat->clus_size)); + + free(upcase); + return true; +} + +static int read_children(struct exfat *exfat, struct exfat_inode *dir) +{ + int ret; + struct exfat_inode *node = NULL; + struct exfat_dentry *dentry; + int dentry_count; + struct exfat_de_iter *de_iter; + + de_iter = &exfat->de_iter; + ret = exfat_de_iter_init(de_iter, exfat, dir); + if (ret == EOF) + return 0; + else if (ret) + return ret; + + while (1) { + ret = exfat_de_iter_get(de_iter, 0, &dentry); + if (ret == EOF) { + break; + } else if (ret) { + fsck_err(dir->parent, dir, + "failed to get a dentry. %d\n", ret); + goto err; + } + + dentry_count = 1; + + switch (dentry->type) { + case EXFAT_FILE: + ret = read_file(de_iter, &node, &dentry_count); + if (ret < 0) { + exfat_stat.error_count++; + goto err; + } else if (ret) { + exfat_stat.error_count++; + exfat_stat.fixed_count++; + } + + if ((node->attr & ATTR_SUBDIR) && node->size) { + node->parent = dir; + list_add_tail(&node->sibling, &dir->children); + list_add_tail(&node->list, &exfat->dir_list); + } else + free_exfat_inode(node); + break; + case EXFAT_VOLUME: + if (!read_volume_label(de_iter)) { + exfat_err("failed to verify volume label\n"); + ret = -EINVAL; + goto err; + } + break; + case EXFAT_BITMAP: + if (!read_bitmap(de_iter)) { + exfat_err( + "failed to verify allocation bitmap\n"); + ret = -EINVAL; + goto err; + } + break; + case EXFAT_UPCASE: + if (!read_upcase_table(de_iter)) { + exfat_err( + "failed to verify upcase table\n"); + ret = -EINVAL; + goto err; + } + break; + case EXFAT_LAST: + goto out; + default: + if (IS_EXFAT_DELETED(dentry->type)) + break; + exfat_err("unknown entry type. 0x%x\n", dentry->type); + ret = -EINVAL; + goto err; + } + + exfat_de_iter_advance(de_iter, dentry_count); + } +out: + exfat_de_iter_flush(de_iter); + return 0; +err: + inode_free_children(dir, false); + INIT_LIST_HEAD(&dir->children); + exfat_de_iter_flush(de_iter); + return ret; +} + +static int write_dirty_fat(struct exfat *exfat) +{ + struct buffer_desc *bd; + off_t offset; + ssize_t len; + size_t read_size, write_size; + clus_t clus, last_clus, clus_count, i; + unsigned int idx; + + clus = 0; + last_clus = le32_to_cpu(exfat->bs->bsx.clu_count) + 2; + bd = exfat->buffer_desc; + idx = 0; + offset = le32_to_cpu(exfat->bs->bsx.fat_offset) * + exfat->sect_size; + read_size = exfat->clus_size; + write_size = exfat->sect_size; + + while (clus < last_clus) { + clus_count = MIN(read_size / sizeof(clus_t), last_clus - clus); + len = exfat_read(exfat->blk_dev->dev_fd, bd[idx].buffer, + clus_count * sizeof(clus_t), offset); + if (len != (ssize_t)(sizeof(clus_t) * clus_count)) { + exfat_err("failed to read fat entries, %zd\n", len); + return -EIO; + } + + /* TODO: read ahead */ + + for (i = clus ? clus : EXFAT_FIRST_CLUSTER; + i < clus + clus_count; i++) { + if (!EXFAT_BITMAP_GET(exfat->alloc_bitmap, + i - EXFAT_FIRST_CLUSTER) && + ((clus_t *)bd[idx].buffer)[i - clus] != + EXFAT_FREE_CLUSTER) { + ((clus_t *)bd[idx].buffer)[i - clus] = + EXFAT_FREE_CLUSTER; + bd[idx].dirty[(i - clus) / + (write_size / sizeof(clus_t))] = true; + } + } + + for (i = 0; i < read_size; i += write_size) { + if (bd[idx].dirty[i / write_size]) { + if (exfat_write(exfat->blk_dev->dev_fd, + &bd[idx].buffer[i], write_size, + offset + i) != + (ssize_t)write_size) { + exfat_err("failed to write " + "fat entries\n"); + return -EIO; + + } + bd[idx].dirty[i / write_size] = false; + } + } + + idx ^= 0x01; + clus = clus + clus_count; + offset += len; + } + return 0; +} + +static int write_dirty_bitmap(struct exfat *exfat) +{ + struct buffer_desc *bd; + off_t offset, last_offset, bitmap_offset; + ssize_t len; + ssize_t read_size, write_size, i, size; + int idx; + + offset = exfat_c2o(exfat, exfat->disk_bitmap_clus); + last_offset = offset + exfat->disk_bitmap_size; + bitmap_offset = 0; + read_size = exfat->clus_size; + write_size = exfat->sect_size; + + bd = exfat->buffer_desc; + idx = 0; + + while (offset < last_offset) { + len = MIN(read_size, last_offset - offset); + if (exfat_read(exfat->blk_dev->dev_fd, bd[idx].buffer, + len, offset) != (ssize_t)len) + return -EIO; + + /* TODO: read-ahead */ + + for (i = 0; i < len; i += write_size) { + size = MIN(write_size, len - i); + if (memcmp(&bd[idx].buffer[i], + exfat->alloc_bitmap + bitmap_offset + i, + size)) { + if (exfat_write(exfat->blk_dev->dev_fd, + exfat->alloc_bitmap + bitmap_offset + i, + size, offset + i) != size) + return -EIO; + } + } + + idx ^= 0x01; + offset += len; + bitmap_offset += len; + } + return 0; +} + +static int reclaim_free_clusters(struct exfat *exfat) +{ + if (write_dirty_fat(exfat)) { + exfat_err("failed to write fat entries\n"); + return -EIO; + } + if (write_dirty_bitmap(exfat)) { + exfat_err("failed to write bitmap\n"); + return -EIO; + } + return 0; +} + +/* + * for each directory in @dir_list. + * 1. read all dentries and allocate exfat_nodes for files and directories. + * and append directory exfat_nodes to the head of @dir_list + * 2. free all of file exfat_nodes. + * 3. if the directory does not have children, free its exfat_node. + */ +static int exfat_filesystem_check(struct exfat *exfat) +{ + struct exfat_inode *dir; + int ret = 0, dir_errors; + + if (!exfat->root) { + exfat_err("root is NULL\n"); + return -ENOENT; + } + + list_add(&exfat->root->list, &exfat->dir_list); + + while (!list_empty(&exfat->dir_list)) { + dir = list_entry(exfat->dir_list.next, struct exfat_inode, list); + + if (!(dir->attr & ATTR_SUBDIR)) { + fsck_err(dir->parent, dir, + "failed to travel directories. " + "the node is not directory\n"); + ret = -EINVAL; + goto out; + } + + dir_errors = read_children(exfat, dir); + if (dir_errors) { + resolve_path(&path_resolve_ctx, dir); + exfat_debug("failed to check dentries: %s\n", + path_resolve_ctx.local_path); + ret = dir_errors; + } + + list_del(&dir->list); + inode_free_file_children(dir); + inode_free_ancestors(dir); + } +out: + exfat_free_dir_list(exfat); + exfat->root = NULL; + if (exfat->dirty_fat && reclaim_free_clusters(exfat)) + return -EIO; + return ret; +} + +static int exfat_root_dir_check(struct exfat *exfat) +{ + struct exfat_inode *root; + clus_t clus_count; + + root = alloc_exfat_inode(ATTR_SUBDIR); + if (!root) { + exfat_err("failed to allocate memory\n"); + return -ENOMEM; + } + + root->first_clus = le32_to_cpu(exfat->bs->bsx.root_cluster); + if (!root_get_clus_count(exfat, root, &clus_count)) { + exfat_err("failed to follow the cluster chain of root\n"); + free_exfat_inode(root); + return -EINVAL; + } + root->size = clus_count * exfat->clus_size; + + exfat->root = root; + exfat_debug("root directory: start cluster[0x%x] size[0x%" PRIx64 "]\n", + root->first_clus, root->size); + return 0; +} + +static char *bytes_to_human_readable(size_t bytes) +{ + static const char * const units[] = {"B", "KB", "MB", "GB", "TB", "PB"}; + static char buf[15*4]; + unsigned int i, shift, quoti, remain; + + shift = 0; + for (i = 0; i < sizeof(units)/sizeof(units[0]); i++) { + if (bytes / (1ULL << (shift + 10)) == 0) + break; + shift += 10; + } + + if (i >= sizeof(units)/sizeof(units[0])) { + i = i - 1; + shift = shift - 10; + } + + quoti = (unsigned int)(bytes / (1ULL << shift)); + remain = 0; + if (shift > 0) { + remain = (unsigned int) + ((bytes & ((1ULL << shift) - 1)) >> (shift - 10)); + remain = (remain * 100) / 1024; + } + + snprintf(buf, sizeof(buf), "%u.%02u %s", quoti, remain, units[i]); + return buf; +} + +static void exfat_show_info(struct exfat *exfat, const char *dev_name, + int errors) +{ + exfat_info("sector size: %s\n", + bytes_to_human_readable(1 << exfat->bs->bsx.sect_size_bits)); + exfat_info("cluster size: %s\n", + bytes_to_human_readable(exfat->clus_size)); + exfat_info("volume size: %s\n", + bytes_to_human_readable(exfat->blk_dev->size)); + + printf("%s: %s. directories %ld, files %ld\n", dev_name, + errors ? "checking stopped" : "clean", + exfat_stat.dir_count, exfat_stat.file_count); + if (errors || exfat->dirty) + printf("%s: files corrupted %ld, files fixed %ld\n", dev_name, + exfat_stat.error_count, exfat_stat.fixed_count); +} + +int main(int argc, char * const argv[]) +{ + struct fsck_user_input ui; + struct exfat_blk_dev bd; + struct exfat *exfat = NULL; + struct pbr *bs = NULL; + int c, ret, exit_code; + bool version_only = false; + + memset(&ui, 0, sizeof(ui)); + memset(&bd, 0, sizeof(bd)); + + print_level = EXFAT_ERROR; + + if (!setlocale(LC_CTYPE, "")) + exfat_err("failed to init locale/codeset\n"); + + opterr = 0; + while ((c = getopt_long(argc, argv, "arynpVvh", opts, NULL)) != EOF) { + switch (c) { + case 'n': + if (ui.options & FSCK_OPTS_REPAIR_ALL) + usage(argv[0]); + ui.options |= FSCK_OPTS_REPAIR_NO; + break; + case 'r': + if (ui.options & FSCK_OPTS_REPAIR_ALL) + usage(argv[0]); + ui.options |= FSCK_OPTS_REPAIR_ASK; + break; + case 'y': + if (ui.options & FSCK_OPTS_REPAIR_ALL) + usage(argv[0]); + ui.options |= FSCK_OPTS_REPAIR_YES; + break; + case 'a': + case 'p': + if (ui.options & FSCK_OPTS_REPAIR_ALL) + usage(argv[0]); + ui.options |= FSCK_OPTS_REPAIR_AUTO; + break; + case 'V': + version_only = true; + break; + case 'v': + if (print_level < EXFAT_DEBUG) + print_level++; + break; + case '?': + case 'h': + default: + usage(argv[0]); + } + } + + show_version(); + if (optind != argc - 1) + usage(argv[0]); + + if (version_only) + exit(FSCK_EXIT_SYNTAX_ERROR); + if (ui.options & FSCK_OPTS_REPAIR_WRITE) + ui.ei.writeable = true; + else { + ui.options |= FSCK_OPTS_REPAIR_NO; + ui.ei.writeable = false; + } + + snprintf(ui.ei.dev_name, sizeof(ui.ei.dev_name), "%s", argv[optind]); + ret = exfat_get_blk_dev_info(&ui.ei, &bd); + if (ret < 0) { + exfat_err("failed to open %s. %d\n", ui.ei.dev_name, ret); + return FSCK_EXIT_OPERATION_ERROR; + } + + exfat = (struct exfat *)calloc(1, sizeof(*exfat)); + if (!exfat) { + exfat_err("failed to allocate exfat\n"); + ret = -ENOMEM; + goto err; + } + exfat->blk_dev = &bd; + exfat->options = ui.options; + + ret = exfat_boot_region_check(exfat, &bs); + if (ret) + goto err; + + ret = init_exfat(exfat, bs); + if (ret) { + exfat = NULL; + goto err; + } + + if (exfat_mark_volume_dirty(exfat, true)) { + ret = -EIO; + goto err; + } + + exfat_debug("verifying root directory...\n"); + ret = exfat_root_dir_check(exfat); + if (ret) { + exfat_err("failed to verify root directory.\n"); + goto out; + } + + exfat_debug("verifying directory entries...\n"); + ret = exfat_filesystem_check(exfat); + if (ret) + goto out; + + if (ui.ei.writeable && fsync(bd.dev_fd)) { + exfat_err("failed to sync\n"); + ret = -EIO; + goto out; + } + exfat_mark_volume_dirty(exfat, false); + +out: + exfat_show_info(exfat, ui.ei.dev_name, ret); +err: + if (ret == -EINVAL) + exit_code = FSCK_EXIT_ERRORS_LEFT; + else if (ret) + exit_code = FSCK_EXIT_OPERATION_ERROR; + else if (exfat->dirty) + exit_code = FSCK_EXIT_CORRECTED; + else + exit_code = FSCK_EXIT_NO_ERRORS; + + free_exfat(exfat); + close(bd.dev_fd); + return exit_code; +} diff --git a/fsck/fsck.h b/fsck/fsck.h new file mode 100644 index 0000000..6c91fac --- /dev/null +++ b/fsck/fsck.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Hyunchul Lee + */ +#ifndef _FSCK_H +#define _FSCK_H + +#include "list.h" + +typedef __u32 clus_t; + +struct exfat_inode { + struct exfat_inode *parent; + struct list_head children; + struct list_head sibling; + struct list_head list; + clus_t first_clus; + clus_t last_lclus; + clus_t last_pclus; + __u16 attr; + uint64_t size; + bool is_contiguous; + __le16 name[0]; /* only for directory */ +}; + +#define EXFAT_NAME_MAX 255 +#define NAME_BUFFER_SIZE ((EXFAT_NAME_MAX+1)*2) + +struct buffer_desc { + clus_t p_clus; + unsigned int offset; + char *buffer; + char *dirty; +}; + +struct exfat_de_iter { + struct exfat *exfat; + struct exfat_inode *parent; + struct buffer_desc *buffer_desc; /* cluster * 2 */ + clus_t ra_next_clus; + unsigned int ra_begin_offset; + unsigned int ra_partial_size; + unsigned int read_size; /* cluster size */ + unsigned int write_size; /* sector size */ + off_t de_file_offset; + off_t next_read_offset; + int max_skip_dentries; +}; + +enum fsck_ui_options { + FSCK_OPTS_REPAIR_ASK = 0x01, + FSCK_OPTS_REPAIR_YES = 0x02, + FSCK_OPTS_REPAIR_NO = 0x04, + FSCK_OPTS_REPAIR_AUTO = 0x08, + FSCK_OPTS_REPAIR_WRITE = 0x0b, + FSCK_OPTS_REPAIR_ALL = 0x0f, +}; + +struct exfat { + enum fsck_ui_options options; + bool dirty:1; + bool dirty_fat:1; + struct exfat_blk_dev *blk_dev; + struct pbr *bs; + char volume_label[VOLUME_LABEL_BUFFER_SIZE]; + struct exfat_inode *root; + struct list_head dir_list; + clus_t clus_count; + unsigned int clus_size; + unsigned int sect_size; + struct exfat_de_iter de_iter; + struct buffer_desc buffer_desc[2]; /* cluster * 2 */ + char *alloc_bitmap; + char *disk_bitmap; + clus_t disk_bitmap_clus; + unsigned int disk_bitmap_size; +}; + +#define EXFAT_CLUSTER_SIZE(pbr) (1 << ((pbr)->bsx.sect_size_bits + \ + (pbr)->bsx.sect_per_clus_bits)) +#define EXFAT_SECTOR_SIZE(pbr) (1 << (pbr)->bsx.sect_size_bits) + +/* fsck.c */ +off_t exfat_c2o(struct exfat *exfat, unsigned int clus); +int get_next_clus(struct exfat *exfat, struct exfat_inode *node, + clus_t clus, clus_t *next); + +/* de_iter.c */ +int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, + struct exfat_inode *dir); +int exfat_de_iter_get(struct exfat_de_iter *iter, + int ith, struct exfat_dentry **dentry); +int exfat_de_iter_get_dirty(struct exfat_de_iter *iter, + int ith, struct exfat_dentry **dentry); +int exfat_de_iter_flush(struct exfat_de_iter *iter); +int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries); +off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter); + +#endif diff --git a/fsck/main.c b/fsck/main.c deleted file mode 100644 index 6a42092..0000000 --- a/fsck/main.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - main.c (02.09.09) - exFAT file system checker. - - 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 -#include -#include -#include -#include - -#define exfat_debug(format, ...) do {} while (0) - -uint64_t files_count, directories_count; - -static int nodeck(struct exfat* ef, struct exfat_node* node) -{ - const cluster_t cluster_size = CLUSTER_SIZE(*ef->sb); - cluster_t clusters = DIV_ROUND_UP(node->size, cluster_size); - cluster_t c = node->start_cluster; - int rc = 0; - - while (clusters--) - { - if (CLUSTER_INVALID(*ef->sb, c)) - { - char name[EXFAT_UTF8_NAME_BUFFER_MAX]; - - exfat_get_name(node, name); - exfat_error("file '%s' has invalid cluster 0x%x", name, c); - rc = 1; - break; - } - if (BMAP_GET(ef->cmap.chunk, c - EXFAT_FIRST_DATA_CLUSTER) == 0) - { - char name[EXFAT_UTF8_NAME_BUFFER_MAX]; - - exfat_get_name(node, name); - exfat_error("cluster 0x%x of file '%s' is not allocated", c, name); - rc = 1; - } - c = exfat_next_cluster(ef, node, c); - } - return rc; -} - -static void dirck(struct exfat* ef, const char* path) -{ - struct exfat_node* parent; - struct exfat_node* node; - struct exfat_iterator it; - int rc; - size_t path_length; - char* entry_path; - - if (exfat_lookup(ef, &parent, path) != 0) - exfat_bug("directory '%s' is not found", path); - if (!(parent->attrib & EXFAT_ATTRIB_DIR)) - exfat_bug("'%s' is not a directory (%#hx)", path, parent->attrib); - if (nodeck(ef, parent) != 0) - { - exfat_put_node(ef, parent); - return; - } - - path_length = strlen(path); - entry_path = malloc(path_length + 1 + EXFAT_UTF8_NAME_BUFFER_MAX); - if (entry_path == NULL) - { - exfat_put_node(ef, parent); - exfat_error("out of memory"); - return; - } - strcpy(entry_path, path); - strcat(entry_path, "/"); - - rc = exfat_opendir(ef, parent, &it); - if (rc != 0) - { - free(entry_path); - exfat_put_node(ef, parent); - return; - } - while ((node = exfat_readdir(&it))) - { - exfat_get_name(node, entry_path + path_length + 1); - exfat_debug("%s: %s, %"PRIu64" bytes, cluster %u", entry_path, - node->is_contiguous ? "contiguous" : "fragmented", - node->size, node->start_cluster); - if (node->attrib & EXFAT_ATTRIB_DIR) - { - directories_count++; - dirck(ef, entry_path); - } - else - { - files_count++; - nodeck(ef, node); - } - exfat_flush_node(ef, node); - exfat_put_node(ef, node); - } - exfat_closedir(ef, &it); - exfat_flush_node(ef, parent); - exfat_put_node(ef, parent); - free(entry_path); -} - -static void fsck(struct exfat* ef, const char* spec, const char* options) -{ - if (exfat_mount(ef, spec, options) != 0) - { - fputs("File system checking stopped. ", stdout); - return; - } - - exfat_print_info(ef->sb, exfat_count_free_clusters(ef)); - exfat_soil_super_block(ef); - dirck(ef, ""); - exfat_unmount(ef); - - printf("Totally %"PRIu64" directories and %"PRIu64" files.\n", - directories_count, files_count); - fputs("File system checking finished. ", stdout); -} - -static void usage(const char* prog) -{ - fprintf(stderr, "Usage: %s [-a | -n | -p | -y] \n", prog); - fprintf(stderr, " %s -V\n", prog); - exit(1); -} - -int main(int argc, char* argv[]) -{ - int opt; - const char* options; - const char* spec = NULL; - struct exfat ef; - - printf("exfatfsck %s\n", VERSION); - - if (isatty(STDIN_FILENO)) - options = "repair=1"; - else - options = "repair=0"; - - while ((opt = getopt(argc, argv, "anpVy")) != -1) - { - switch (opt) - { - case 'a': - case 'p': - case 'y': - options = "repair=2"; - break; - case 'n': - options = "repair=0,ro"; - break; - case 'V': - puts("Copyright (C) 2011-2018 Andrew Nayenko"); - return 0; - default: - usage(argv[0]); - break; - } - } - if (argc - optind != 1) - usage(argv[0]); - spec = argv[optind]; - - printf("Checking file system on %s.\n", spec); - fsck(&ef, spec, options); - if (exfat_errors != 0) - { - printf("ERRORS FOUND: %d, FIXED: %d.\n", - exfat_errors, exfat_errors_fixed); - return 1; - } - puts("No errors found."); - return 0; -} diff --git a/fsck/repair.c b/fsck/repair.c new file mode 100644 index 0000000..c79d379 --- /dev/null +++ b/fsck/repair.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Hyunchul Lee + */ +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" +#include "fsck.h" +#include "repair.h" + +struct exfat_repair_problem { + er_problem_code_t prcode; + unsigned int flags; + unsigned int prompt_type; +}; + +/* Problem flags */ +#define ERF_PREEN_YES 0x00000001 +#define ERF_DEFAULT_YES 0x00000002 +#define ERF_DEFAULT_NO 0x00000004 + +/* Prompt types */ +#define ERP_FIX 0x00000001 +#define ERP_TRUNCATE 0x00000002 + +static const char *prompts[] = { + "Repair", + "Fix", + "Truncate", +}; + +static struct exfat_repair_problem problems[] = { + {ER_BS_CHECKSUM, ERF_PREEN_YES, ERP_FIX}, + {ER_BS_BOOT_REGION, 0, ERP_FIX}, + {ER_DE_CHECKSUM, ERF_PREEN_YES, ERP_FIX}, + {ER_FILE_VALID_SIZE, ERF_PREEN_YES, ERP_FIX}, + {ER_FILE_INVALID_CLUS, ERF_DEFAULT_NO, ERP_TRUNCATE}, + {ER_FILE_FIRST_CLUS, ERF_DEFAULT_NO, ERP_TRUNCATE}, + {ER_FILE_SMALLER_SIZE, ERF_DEFAULT_NO, ERP_TRUNCATE}, + {ER_FILE_LARGER_SIZE, ERF_DEFAULT_NO, ERP_TRUNCATE}, + {ER_FILE_DUPLICATED_CLUS, ERF_DEFAULT_NO, ERP_TRUNCATE}, + {ER_FILE_ZERO_NOFAT, ERF_PREEN_YES, ERP_FIX}, +}; + +static struct exfat_repair_problem *find_problem(er_problem_code_t prcode) +{ + unsigned int i; + + for (i = 0; i < sizeof(problems)/sizeof(problems[0]); i++) { + if (problems[i].prcode == prcode) { + return &problems[i]; + } + } + return NULL; +} + +static bool ask_repair(struct exfat *exfat, struct exfat_repair_problem *pr) +{ + bool repair = false; + char answer[8]; + + if (exfat->options & FSCK_OPTS_REPAIR_NO || + pr->flags & ERF_DEFAULT_NO) + repair = false; + else if (exfat->options & FSCK_OPTS_REPAIR_YES || + pr->flags & ERF_DEFAULT_YES) + repair = true; + else { + if (exfat->options & FSCK_OPTS_REPAIR_ASK) { + do { + printf(". %s (y/N)? ", + prompts[pr->prompt_type]); + fflush(stdout); + + if (fgets(answer, sizeof(answer), stdin)) { + if (strcasecmp(answer, "Y\n") == 0) + return true; + else if (strcasecmp(answer, "\n") == 0 + || strcasecmp(answer, "N\n") == 0) + return false; + } + } while (1); + } else if (exfat->options & FSCK_OPTS_REPAIR_AUTO && + pr->flags & ERF_PREEN_YES) + repair = true; + } + + printf(". %s (y/N)? %c\n", prompts[pr->prompt_type], + repair ? 'y' : 'n'); + return repair; +} + +bool exfat_repair_ask(struct exfat *exfat, er_problem_code_t prcode, + const char *desc, ...) +{ + struct exfat_repair_problem *pr = NULL; + va_list ap; + + pr = find_problem(prcode); + if (!pr) { + exfat_err("unknown problem code. %#x\n", prcode); + return false; + } + + va_start(ap, desc); + vprintf(desc, ap); + va_end(ap); + + if (ask_repair(exfat, pr)) { + if (pr->prompt_type & ERP_TRUNCATE) + exfat->dirty_fat = true; + exfat->dirty = true; + return true; + } else + return false; +} diff --git a/fsck/repair.h b/fsck/repair.h new file mode 100644 index 0000000..f7286b9 --- /dev/null +++ b/fsck/repair.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Hyunchul Lee + */ +#ifndef _REPAIR_H +#define _REPAIR_H + +#define ER_BS_CHECKSUM 0x00000001 +#define ER_BS_BOOT_REGION 0x00000002 +#define ER_DE_CHECKSUM 0x00001001 +#define ER_FILE_VALID_SIZE 0x00002001 +#define ER_FILE_INVALID_CLUS 0x00002002 +#define ER_FILE_FIRST_CLUS 0x00002003 +#define ER_FILE_SMALLER_SIZE 0x00002004 +#define ER_FILE_LARGER_SIZE 0x00002005 +#define ER_FILE_DUPLICATED_CLUS 0x00002006 +#define ER_FILE_ZERO_NOFAT 0x00002007 + +typedef unsigned int er_problem_code_t; + +bool exfat_repair_ask(struct exfat *exfat, er_problem_code_t prcode, + const char *fmt, ...); + +#endif diff --git a/fuse/Makefile.am b/fuse/Makefile.am deleted file mode 100644 index be27dc9..0000000 --- a/fuse/Makefile.am +++ /dev/null @@ -1,34 +0,0 @@ -# -# Makefile.am (30.03.15) -# Automake source. -# -# 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. -# - -sbin_PROGRAMS = mount.exfat-fuse -dist_man8_MANS = mount.exfat-fuse.8 -mount_exfat_fuse_SOURCES = main.c -mount_exfat_fuse_CPPFLAGS = -I$(top_srcdir)/libexfat -mount_exfat_fuse_CFLAGS = $(FUSE_CFLAGS) -mount_exfat_fuse_LDADD = ../libexfat/libexfat.a $(FUSE_LIBS) - -install-exec-hook: - ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/mount.exfat - -uninstall-hook: - rm -f $(DESTDIR)$(sbindir)/mount.exfat diff --git a/fuse/main.c b/fuse/main.c deleted file mode 100644 index 3143693..0000000 --- a/fuse/main.c +++ /dev/null @@ -1,644 +0,0 @@ -/* - main.c (01.09.09) - FUSE-based exFAT implementation. Requires FUSE 2.6 or later. - - 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 -#define FUSE_USE_VERSION 26 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef DEBUG - #define exfat_debug(format, ...) do {} while (0) -#endif - -#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26) - #error FUSE 2.6 or later is required -#endif - -struct exfat ef; - -static struct exfat_node* get_node(const struct fuse_file_info* fi) -{ - return (struct exfat_node*) (size_t) fi->fh; -} - -static void set_node(struct fuse_file_info* fi, struct exfat_node* node) -{ - fi->fh = (uint64_t) (size_t) node; - fi->keep_cache = 1; -} - -static int fuse_exfat_getattr(const char* path, struct stat* stbuf) -{ - struct exfat_node* node; - int rc; - - exfat_debug("[%s] %s", __func__, path); - - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - return rc; - - exfat_stat(&ef, node, stbuf); - exfat_put_node(&ef, node); - return 0; -} - -static int fuse_exfat_truncate(const char* path, off_t size) -{ - struct exfat_node* node; - int rc; - - exfat_debug("[%s] %s, %"PRId64, __func__, path, size); - - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - return rc; - - rc = exfat_truncate(&ef, node, size, true); - if (rc != 0) - { - exfat_flush_node(&ef, node); /* ignore return code */ - exfat_put_node(&ef, node); - return rc; - } - rc = exfat_flush_node(&ef, node); - exfat_put_node(&ef, node); - return rc; -} - -static int fuse_exfat_readdir(const char* path, void* buffer, - fuse_fill_dir_t filler, UNUSED off_t offset, - UNUSED struct fuse_file_info* fi) -{ - struct exfat_node* parent; - struct exfat_node* node; - struct exfat_iterator it; - int rc; - char name[EXFAT_UTF8_NAME_BUFFER_MAX]; - struct stat stbuf; - - exfat_debug("[%s] %s", __func__, path); - - rc = exfat_lookup(&ef, &parent, path); - if (rc != 0) - return rc; - if (!(parent->attrib & EXFAT_ATTRIB_DIR)) - { - exfat_put_node(&ef, parent); - exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib); - return -ENOTDIR; - } - - filler(buffer, ".", NULL, 0); - filler(buffer, "..", NULL, 0); - - rc = exfat_opendir(&ef, parent, &it); - if (rc != 0) - { - exfat_put_node(&ef, parent); - exfat_error("failed to open directory '%s'", path); - return rc; - } - while ((node = exfat_readdir(&it))) - { - exfat_get_name(node, name); - exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__, - name, node->is_contiguous ? "contiguous" : "fragmented", - node->size, node->start_cluster); - exfat_stat(&ef, node, &stbuf); - filler(buffer, name, &stbuf, 0); - exfat_put_node(&ef, node); - } - exfat_closedir(&ef, &it); - exfat_put_node(&ef, parent); - return 0; -} - -static int fuse_exfat_open(const char* path, struct fuse_file_info* fi) -{ - struct exfat_node* node; - int rc; - - exfat_debug("[%s] %s", __func__, path); - - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - return rc; - set_node(fi, node); - return 0; -} - -static int fuse_exfat_create(const char* path, UNUSED mode_t mode, - struct fuse_file_info* fi) -{ - struct exfat_node* node; - int rc; - - exfat_debug("[%s] %s 0%ho", __func__, path, mode); - - rc = exfat_mknod(&ef, path); - if (rc != 0) - return rc; - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - return rc; - set_node(fi, node); - return 0; -} - -static int fuse_exfat_release(UNUSED const char* path, - struct fuse_file_info* fi) -{ - /* - This handler is called by FUSE on close() syscall. If the FUSE - implementation does not call flush handler, we will flush node here. - But in this case we will not be able to return an error to the caller. - See fuse_exfat_flush() below. - */ - exfat_debug("[%s] %s", __func__, path); - exfat_flush_node(&ef, get_node(fi)); - exfat_put_node(&ef, get_node(fi)); - return 0; /* FUSE ignores this return value */ -} - -static int fuse_exfat_flush(UNUSED const char* path, struct fuse_file_info* fi) -{ - /* - This handler may be called by FUSE on close() syscall. FUSE also deals - with removals of open files, so we don't free clusters on close but - only on rmdir and unlink. If the FUSE implementation does not call this - handler we will flush node on release. See fuse_exfat_relase() above. - */ - exfat_debug("[%s] %s", __func__, path); - return exfat_flush_node(&ef, get_node(fi)); -} - -static int fuse_exfat_fsync(UNUSED const char* path, UNUSED int datasync, - UNUSED struct fuse_file_info *fi) -{ - int rc; - - exfat_debug("[%s] %s", __func__, path); - rc = exfat_flush_nodes(&ef); - if (rc != 0) - return rc; - rc = exfat_flush(&ef); - if (rc != 0) - return rc; - return exfat_fsync(ef.dev); -} - -static int fuse_exfat_read(UNUSED const char* path, char* buffer, - size_t size, off_t offset, struct fuse_file_info* fi) -{ - exfat_debug("[%s] %s (%zu bytes)", __func__, path, size); - return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset); -} - -static int fuse_exfat_write(UNUSED const char* path, const char* buffer, - size_t size, off_t offset, struct fuse_file_info* fi) -{ - exfat_debug("[%s] %s (%zu bytes)", __func__, path, size); - return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset); -} - -static int fuse_exfat_unlink(const char* path) -{ - struct exfat_node* node; - int rc; - - exfat_debug("[%s] %s", __func__, path); - - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - return rc; - - rc = exfat_unlink(&ef, node); - exfat_put_node(&ef, node); - if (rc != 0) - return rc; - return exfat_cleanup_node(&ef, node); -} - -static int fuse_exfat_rmdir(const char* path) -{ - struct exfat_node* node; - int rc; - - exfat_debug("[%s] %s", __func__, path); - - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - return rc; - - rc = exfat_rmdir(&ef, node); - exfat_put_node(&ef, node); - if (rc != 0) - return rc; - return exfat_cleanup_node(&ef, node); -} - -static int fuse_exfat_mknod(const char* path, UNUSED mode_t mode, - UNUSED dev_t dev) -{ - exfat_debug("[%s] %s 0%ho", __func__, path, mode); - return exfat_mknod(&ef, path); -} - -static int fuse_exfat_mkdir(const char* path, UNUSED mode_t mode) -{ - exfat_debug("[%s] %s 0%ho", __func__, path, mode); - return exfat_mkdir(&ef, path); -} - -static int fuse_exfat_rename(const char* old_path, const char* new_path) -{ - exfat_debug("[%s] %s => %s", __func__, old_path, new_path); - return exfat_rename(&ef, old_path, new_path); -} - -static int fuse_exfat_utimens(const char* path, const struct timespec tv[2]) -{ - struct exfat_node* node; - int rc; - - exfat_debug("[%s] %s", __func__, path); - - rc = exfat_lookup(&ef, &node, path); - if (rc != 0) - return rc; - - exfat_utimes(node, tv); - rc = exfat_flush_node(&ef, node); - exfat_put_node(&ef, node); - return rc; -} - -static int fuse_exfat_chmod(UNUSED const char* path, mode_t mode) -{ - const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR | - S_IRWXU | S_IRWXG | S_IRWXO; - - exfat_debug("[%s] %s 0%ho", __func__, path, mode); - if (mode & ~VALID_MODE_MASK) - return -EPERM; - return 0; -} - -static int fuse_exfat_chown(UNUSED const char* path, uid_t uid, gid_t gid) -{ - exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid); - if (uid != ef.uid || gid != ef.gid) - return -EPERM; - return 0; -} - -static int fuse_exfat_statfs(UNUSED const char* path, struct statvfs* sfs) -{ - exfat_debug("[%s]", __func__); - - sfs->f_bsize = CLUSTER_SIZE(*ef.sb); - sfs->f_frsize = CLUSTER_SIZE(*ef.sb); - sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits; - sfs->f_bavail = exfat_count_free_clusters(&ef); - sfs->f_bfree = sfs->f_bavail; - sfs->f_namemax = EXFAT_NAME_MAX; - - /* - Below are fake values because in exFAT there is - a) no simple way to count files; - b) no such thing as inode; - So here we assume that inode = cluster. - */ - sfs->f_files = le32_to_cpu(ef.sb->cluster_count); - sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits; - sfs->f_ffree = sfs->f_bavail; - - return 0; -} - -static void* fuse_exfat_init(struct fuse_conn_info* fci) -{ - exfat_debug("[%s]", __func__); -#ifdef FUSE_CAP_BIG_WRITES - fci->want |= FUSE_CAP_BIG_WRITES; -#endif - - /* mark super block as dirty; failure isn't a big deal */ - exfat_soil_super_block(&ef); - - return NULL; -} - -static void fuse_exfat_destroy(UNUSED void* unused) -{ - exfat_debug("[%s]", __func__); - exfat_unmount(&ef); -} - -static void usage(const char* prog) -{ - fprintf(stderr, "Usage: %s [-d] [-o options] [-V] \n", prog); - exit(1); -} - -static struct fuse_operations fuse_exfat_ops = -{ - .getattr = fuse_exfat_getattr, - .truncate = fuse_exfat_truncate, - .readdir = fuse_exfat_readdir, - .open = fuse_exfat_open, - .create = fuse_exfat_create, - .release = fuse_exfat_release, - .flush = fuse_exfat_flush, - .fsync = fuse_exfat_fsync, - .fsyncdir = fuse_exfat_fsync, - .read = fuse_exfat_read, - .write = fuse_exfat_write, - .unlink = fuse_exfat_unlink, - .rmdir = fuse_exfat_rmdir, - .mknod = fuse_exfat_mknod, - .mkdir = fuse_exfat_mkdir, - .rename = fuse_exfat_rename, - .utimens = fuse_exfat_utimens, - .chmod = fuse_exfat_chmod, - .chown = fuse_exfat_chown, - .statfs = fuse_exfat_statfs, - .init = fuse_exfat_init, - .destroy = fuse_exfat_destroy, -}; - -static char* add_option(char* options, const char* name, const char* value) -{ - size_t size; - char* optionsf = options; - - if (value) - size = strlen(options) + strlen(name) + strlen(value) + 3; - else - size = strlen(options) + strlen(name) + 2; - - options = realloc(options, size); - if (options == NULL) - { - free(optionsf); - exfat_error("failed to reallocate options string"); - return NULL; - } - strcat(options, ","); - strcat(options, name); - if (value) - { - strcat(options, "="); - strcat(options, value); - } - return options; -} - -static void escape(char* escaped, const char* orig) -{ - do - { - if (*orig == ',' || *orig == '\\') - *escaped++ = '\\'; - } - while ((*escaped++ = *orig++)); -} - -static char* add_fsname_option(char* options, const char* spec) -{ - /* escaped string cannot be more than twice as big as the original one */ - char* escaped = malloc(strlen(spec) * 2 + 1); - - if (escaped == NULL) - { - free(options); - exfat_error("failed to allocate escaped string for %s", spec); - return NULL; - } - - /* on some platforms (e.g. Android, Solaris) device names can contain - commas */ - escape(escaped, spec); - options = add_option(options, "fsname", escaped); - free(escaped); - return options; -} - -static char* add_ro_option(char* options, bool ro) -{ - return ro ? add_option(options, "ro", NULL) : options; -} - -#if defined(__linux__) -static char* add_user_option(char* options) -{ - struct passwd* pw; - - if (getuid() == 0) - return options; - - pw = getpwuid(getuid()); - if (pw == NULL || pw->pw_name == NULL) - { - free(options); - exfat_error("failed to determine username"); - return NULL; - } - return add_option(options, "user", pw->pw_name); -} -#endif - -#if defined(__linux__) -static char* add_blksize_option(char* options, long cluster_size) -{ - long page_size = sysconf(_SC_PAGESIZE); - char blksize[20]; - - if (page_size < 1) - page_size = 0x1000; - - snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size)); - return add_option(options, "blksize", blksize); -} -#endif - -static char* add_fuse_options(char* options, const char* spec, bool ro) -{ - options = add_fsname_option(options, spec); - if (options == NULL) - return NULL; - options = add_ro_option(options, ro); - if (options == NULL) - return NULL; -#if defined(__linux__) - options = add_user_option(options); - if (options == NULL) - return NULL; - options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb)); - if (options == NULL) - return NULL; -#endif - return options; -} - -static char* add_passthrough_fuse_options(char* fuse_options, - const char* options) -{ - const char* passthrough_list[] = - { -#if defined(__FreeBSD__) - "automounted", -#endif - "nonempty", - NULL - }; - int i; - - for (i = 0; passthrough_list[i] != NULL; i++) - if (exfat_match_option(options, passthrough_list[i])) - { - fuse_options = add_option(fuse_options, passthrough_list[i], NULL); - if (fuse_options == NULL) - return NULL; - } - - return fuse_options; -} - -static int fuse_exfat_main(char* mount_options, char* mount_point) -{ - char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL}; - return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv, - &fuse_exfat_ops, NULL); -} - -int main(int argc, char* argv[]) -{ - const char* spec = NULL; - char* mount_point = NULL; - char* fuse_options; - char* exfat_options; - int opt; - int rc; - - printf("FUSE exfat %s\n", VERSION); - - fuse_options = strdup("allow_other," -#if defined(__linux__) || defined(__FreeBSD__) - "big_writes," -#endif -#if defined(__linux__) - "blkdev," -#endif - "default_permissions"); - exfat_options = strdup("ro_fallback"); - if (fuse_options == NULL || exfat_options == NULL) - { - free(fuse_options); - free(exfat_options); - exfat_error("failed to allocate options string"); - return 1; - } - - while ((opt = getopt(argc, argv, "dno:Vv")) != -1) - { - switch (opt) - { - case 'd': - fuse_options = add_option(fuse_options, "debug", NULL); - if (fuse_options == NULL) - { - free(exfat_options); - return 1; - } - break; - case 'n': - break; - case 'o': - exfat_options = add_option(exfat_options, optarg, NULL); - if (exfat_options == NULL) - { - free(fuse_options); - return 1; - } - fuse_options = add_passthrough_fuse_options(fuse_options, optarg); - if (fuse_options == NULL) - { - free(exfat_options); - return 1; - } - break; - case 'V': - free(exfat_options); - free(fuse_options); - puts("Copyright (C) 2010-2018 Andrew Nayenko"); - return 0; - case 'v': - break; - default: - free(exfat_options); - free(fuse_options); - usage(argv[0]); - break; - } - } - if (argc - optind != 2) - { - free(exfat_options); - free(fuse_options); - usage(argv[0]); - } - spec = argv[optind]; - mount_point = argv[optind + 1]; - - if (exfat_mount(&ef, spec, exfat_options) != 0) - { - free(exfat_options); - free(fuse_options); - return 1; - } - - free(exfat_options); - - fuse_options = add_fuse_options(fuse_options, spec, ef.ro != 0); - if (fuse_options == NULL) - { - exfat_unmount(&ef); - return 1; - } - - /* let FUSE do all its wizardry */ - rc = fuse_exfat_main(fuse_options, mount_point); - - free(fuse_options); - return rc; -} diff --git a/fuse/mount.exfat-fuse.8 b/fuse/mount.exfat-fuse.8 deleted file mode 100644 index e0925b2..0000000 --- a/fuse/mount.exfat-fuse.8 +++ /dev/null @@ -1,108 +0,0 @@ -.\" Copyright (C) 2010-2016 Andrew Nayenko -.\" -.TH EXFAT-FUSE 8 "November 2015" -.SH NAME -mount.exfat-fuse \- mount an exFAT file system -.SH SYNOPSIS -.B mount.exfat-fuse -[ -.B \-d -] -[ -.B \-n -] -[ -.B \-o -.I options -] -[ -.B \-V -] -[ -.B \-v -] -.I device dir - -.SH DESCRIPTION -.B mount.exfat-fuse -is a free exFAT file system implementation with write support. exFAT is a -simple file system created by Microsoft. It is intended to replace FAT32 -removing some of its limitations. exFAT is a standard FS for SDXC memory -cards. - -.SH COMMAND LINE OPTIONS -Command line options available: -.TP -.BI \-d -Enable debug logging and do not detach from shell. -.TP -.BI \-n -Ignored. -.TP -.BI \-o " options" -File system specific options. For more details see -.B FILE SYSTEM OPTIONS -section below. -.TP -.BI \-V -Print version and copyright. -.TP -.BI \-v -Ignored. - -.SH FILE SYSTEM OPTIONS -.TP -.BI umask= value -Set the umask (the bitmask of the permissions that are -.B not -present, in octal). -The default is 0. -.TP -.BI dmask= value -Set the umask for directories only. -.TP -.BI fmask= value -Set the umask for files only. -.TP -.BI uid= n -Set the owner for all files and directories. -The default is the owner of the current process. -.TP -.BI gid= n -Set the group for all files and directories. -The default is the group of the current process. -.TP -.BI ro -Mount the file system in read only mode. -.TP -.BI noatime -Do not update access time when file is read. - -.SH EXIT CODES -Zero is returned on successful mount. Any other code means an error. - -.SH BUGS -exFAT is a case-insensitive file system. Some things can behave unexpectedly, -e.g. directory renaming that changes only case of some characters: - -.B \t$ mv FOO Foo -.br -.B \tmv: cannot move \(cqFOO\(cq to a subdirectory of itself, \(cqFoo/FOO\(cq - -This happens because -.B mv -finds that destination exists (for case-insensitive file -systems -.B FOO -and -.B Foo -are the same thing) and adds source basename to the destination. The file -system gets -.B rename(\(dqFOO\(dq,\ \(dqFoo/FOO\(dq) -syscall and returns an error. - -.SH AUTHOR -Andrew Nayenko - -.SH SEE ALSO -.BR mount (8) diff --git a/include/exfat_ondisk.h b/include/exfat_ondisk.h new file mode 100644 index 0000000..b3fc1fe --- /dev/null +++ b/include/exfat_ondisk.h @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Namjae Jeon + */ + +#ifndef _EXFAT_H +#define _EXFAT_H + +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef WORDS_BIGENDIAN +#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)) +#define cpu_to_le32(x) \ + ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ + (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) +#define cpu_to_le64(x) (cpu_to_le32((uint64_t)(x)) << 32 | \ + cpu_to_le32((uint64_t)(x) >> 32)) +#else +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) +#endif + +#define le64_to_cpu(x) ((uint64_t)cpu_to_le64(x)) +#define le32_to_cpu(x) ((uint32_t)cpu_to_le32(x)) +#define le16_to_cpu(x) ((uint16_t)cpu_to_le16(x)) + +#define PBR_SIGNATURE 0xAA55 + +#define VOL_CLEAN 0x0000 +#define VOL_DIRTY 0x0002 + +#define DENTRY_SIZE 32 /* directory entry size */ +#define DENTRY_SIZE_BITS 5 +/* exFAT allows 8388608(256MB) directory entries */ +#define MAX_EXFAT_DENTRIES 8388608 + +/* dentry types */ +#define MSDOS_DELETED 0xE5 /* deleted mark */ +#define MSDOS_UNUSED 0x00 /* end of directory */ + +#define EXFAT_LAST 0x00 /* end of directory */ +#define EXFAT_DELETE ~(0x80) +#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */ +#define EXFAT_INVAL 0x80 /* invalid value */ +#define EXFAT_BITMAP 0x81 /* allocation bitmap */ +#define EXFAT_UPCASE 0x82 /* upcase table */ +#define EXFAT_VOLUME 0x83 /* volume label */ +#define EXFAT_FILE 0x85 /* file or dir */ +#define EXFAT_GUID 0xA0 +#define EXFAT_PADDING 0xA1 +#define EXFAT_ACLTAB 0xA2 +#define EXFAT_STREAM 0xC0 /* stream entry */ +#define EXFAT_NAME 0xC1 /* file name entry */ +#define EXFAT_ACL 0xC2 /* stream entry */ + +/* checksum types */ +#define CS_DIR_ENTRY 0 +#define CS_PBR_SECTOR 1 +#define CS_DEFAULT 2 + +/* file attributes */ +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_SUBDIR 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_EXTEND (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | \ + ATTR_VOLUME) /* 0x000F */ + +#define ATTR_EXTEND_MASK (ATTR_EXTEND | ATTR_SUBDIR | ATTR_ARCHIVE) +#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \ + ATTR_SUBDIR | ATTR_ARCHIVE) + +#define ATTR_READONLY_LE cpu_to_le16(0x0001) +#define ATTR_HIDDEN_LE cpu_to_le16(0x0002) +#define ATTR_SYSTEM_LE cpu_to_le16(0x0004) +#define ATTR_VOLUME_LE cpu_to_le16(0x0008) +#define ATTR_SUBDIR_LE cpu_to_le16(0x0010) +#define ATTR_ARCHIVE_LE cpu_to_le16(0x0020) + +/* stream flags */ +#define EXFAT_SF_CONTIGUOUS 0x02 + +#define CLUSTER_32(x) ((unsigned int)((x) & 0xFFFFFFFFU)) +#define EXFAT_EOF_CLUSTER CLUSTER_32(~0) +#define EXFAT_BAD_CLUSTER (0xFFFFFFF7U) +#define EXFAT_FREE_CLUSTER (0) +#define EXFAT_FIRST_CLUSTER (2) +#define EXFAT_RESERVED_CLUSTERS (2) + + +/* EXFAT BIOS parameter block (64 bytes) */ +struct bpb64 { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + __u8 res_zero[53]; +}; + +/* EXFAT EXTEND BIOS parameter block (56 bytes) */ +struct bsx64 { + __le64 vol_offset; + __le64 vol_length; + __le32 fat_offset; + __le32 fat_length; + __le32 clu_offset; + __le32 clu_count; + __le32 root_cluster; + __le32 vol_serial; + __u8 fs_version[2]; + __le16 vol_flags; + __u8 sect_size_bits; + __u8 sect_per_clus_bits; + __u8 num_fats; + __u8 phy_drv_no; + __u8 perc_in_use; + __u8 reserved2[7]; +}; + +/* Common PBR[Partition Boot Record] (512 bytes) */ +struct pbr { + struct bpb64 bpb; + struct bsx64 bsx; + __u8 boot_code[390]; + __le16 signature; +}; + +#define VOLUME_LABEL_MAX_LEN 11 +#define ENTRY_NAME_MAX 15 + +struct exfat_dentry { + __u8 type; + union { + struct { + __u8 character_count; + __le16 volume_label[VOLUME_LABEL_MAX_LEN]; + __u8 reserved[8]; + } __attribute__((packed)) vol; /* file directory entry */ + + struct { + __u8 num_ext; + __le16 checksum; + __le16 attr; + __le16 reserved1; + __le16 create_time; + __le16 create_date; + __le16 modify_time; + __le16 modify_date; + __le16 access_time; + __le16 access_date; + __u8 create_time_ms; + __u8 modify_time_ms; + __u8 access_time_ms; + __u8 reserved2[9]; + } __attribute__((packed)) file; /* file directory entry */ + struct { + __u8 flags; + __u8 reserved1; + __u8 name_len; + __le16 name_hash; + __le16 reserved2; + __le64 valid_size; + __le32 reserved3; + __le32 start_clu; + __le64 size; + } __attribute__((packed)) stream; /* stream extension directory entry */ + struct { + __u8 flags; + __le16 unicode_0_14[15]; + } __attribute__((packed)) name; /* file name directory entry */ + struct { + __u8 flags; + __u8 reserved[18]; + __le32 start_clu; + __le64 size; + } __attribute__((packed)) bitmap; /* allocation bitmap directory entry */ + struct { + __u8 reserved1[3]; + __le32 checksum; + __u8 reserved2[12]; + __le32 start_clu; + __le64 size; + } __attribute__((packed)) upcase; /* up-case table directory entry */ + } __attribute__((packed)) dentry; +} __attribute__((packed)); + +#define vol_char_cnt dentry.vol.character_count +#define vol_label dentry.vol.volume_label +#define file_num_ext dentry.file.num_ext +#define file_checksum dentry.file.checksum +#define file_attr dentry.file.attr +#define file_create_time dentry.file.create_time +#define file_create_date dentry.file.create_date +#define file_modify_time dentry.file.modify_time +#define file_modify_date dentry.file.modify_date +#define file_access_time dentry.file.access_time +#define file_access_date dentry.file.access_date +#define file_create_time_ms dentry.file.create_time_ms +#define file_modify_time_ms dentry.file.modify_time_ms +#define file_access_time_ms dentry.file.access_time_ms +#define stream_flags dentry.stream.flags +#define stream_name_len dentry.stream.name_len +#define stream_name_hash dentry.stream.name_hash +#define stream_start_clu dentry.stream.start_clu +#define stream_valid_size dentry.stream.valid_size +#define stream_size dentry.stream.size +#define name_flags dentry.name.flags +#define name_unicode dentry.name.unicode_0_14 +#define bitmap_flags dentry.bitmap.flags +#define bitmap_start_clu dentry.bitmap.start_clu +#define bitmap_size dentry.bitmap.size +#define upcase_start_clu dentry.upcase.start_clu +#define upcase_size dentry.upcase.size +#define upcase_checksum dentry.upcase.checksum + +#endif /* !_EXFAT_H */ diff --git a/include/libexfat.h b/include/libexfat.h new file mode 100644 index 0000000..53a82a1 --- /dev/null +++ b/include/libexfat.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Namjae Jeon + */ + +#ifndef _LIBEXFAT_H + +#include +#include +#include +#include + +#define KB (1024) +#define MB (1024*1024) +#define GB (1024UL*1024UL*1024UL) + +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define DIV_ROUND_UP(__i, __d) (((__i) + (__d) - 1) / (__d)) + +#define EXFAT_MIN_NUM_SEC_VOL (2048) +#define EXFAT_MAX_NUM_SEC_VOL ((2 << 64) - 1) + +#define EXFAT_MAX_NUM_CLUSTER (0xFFFFFFF5) + +#define DEFAULT_BOUNDARY_ALIGNMENT (1024*1024) + +#define DEFAULT_SECTOR_SIZE (512) + +#define VOLUME_LABEL_BUFFER_SIZE (VOLUME_LABEL_MAX_LEN*MB_LEN_MAX+1) + +/* Upcase table macro */ +#define EXFAT_UPCASE_TABLE_SIZE (5836) + +/* Flags for tune.exfat and exfatlabel */ +#define EXFAT_GET_VOLUME_LABEL 0x01 +#define EXFAT_SET_VOLUME_LABEL 0x02 +#define EXFAT_GET_VOLUME_SERIAL 0x03 +#define EXFAT_SET_VOLUME_SERIAL 0x04 + +#define EXFAT_MAX_SECTOR_SIZE 4096 + +enum { + BOOT_SEC_IDX = 0, + EXBOOT_SEC_IDX, + EXBOOT_SEC_NUM = 8, + OEM_SEC_IDX, + RESERVED_SEC_IDX, + CHECKSUM_SEC_IDX, + BACKUP_BOOT_SEC_IDX, +}; + +struct exfat_blk_dev { + int dev_fd; + unsigned long long offset; + unsigned long long size; + unsigned int sector_size; + unsigned int sector_size_bits; + unsigned long long num_sectors; + unsigned int num_clusters; + unsigned int cluster_size; +}; + +struct exfat_user_input { + char dev_name[255]; + bool writeable; + unsigned int cluster_size; + unsigned int sec_per_clu; + unsigned int boundary_align; + bool pack_bitmap; + bool quick; + __u16 volume_label[VOLUME_LABEL_MAX_LEN]; + int volume_label_len; + unsigned int volume_serial; +}; + +void show_version(void); + +void exfat_set_bit(struct exfat_blk_dev *bd, char *bitmap, + unsigned int clu); +void exfat_clear_bit(struct exfat_blk_dev *bd, char *bitmap, + unsigned int clu); +wchar_t exfat_bad_char(wchar_t w); +void boot_calc_checksum(unsigned char *sector, unsigned short size, + bool is_boot_sec, __le32 *checksum); +void init_user_input(struct exfat_user_input *ui); +int exfat_get_blk_dev_info(struct exfat_user_input *ui, + struct exfat_blk_dev *bd); +ssize_t exfat_read(int fd, void *buf, size_t size, off_t offset); +ssize_t exfat_write(int fd, void *buf, size_t size, off_t offset); + +size_t exfat_utf16_len(const __le16 *str, size_t max_size); +ssize_t exfat_utf16_enc(const char *in_str, __u16 *out_str, size_t out_size); +ssize_t exfat_utf16_dec(const __u16 *in_str, size_t in_len, + char *out_str, size_t out_size); +off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd); +int exfat_show_volume_label(struct exfat_blk_dev *bd, off_t root_clu_off); +int exfat_set_volume_label(struct exfat_blk_dev *bd, + char *label_input, off_t root_clu_off); +int exfat_read_sector(struct exfat_blk_dev *bd, void *buf, + unsigned int sec_off); +int exfat_write_sector(struct exfat_blk_dev *bd, void *buf, + unsigned int sec_off); +int exfat_write_checksum_sector(struct exfat_blk_dev *bd, + unsigned int checksum, bool is_backup); +char *exfat_conv_volume_label(struct exfat_dentry *vol_entry); +int exfat_show_volume_serial(int fd); +int exfat_set_volume_serial(struct exfat_blk_dev *bd, + struct exfat_user_input *ui); +unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd, + unsigned int clu_off, unsigned int clu); + + +/* + * Exfat Print + */ + +extern unsigned int print_level; + +#define EXFAT_ERROR (1) +#define EXFAT_INFO (2) +#define EXFAT_DEBUG (3) + +#define exfat_msg(level, dir, fmt, ...) \ + do { \ + if (print_level >= level) { \ + fprintf(dir, fmt, ##__VA_ARGS__); \ + } \ + } while (0) \ + +#define exfat_err(fmt, ...) exfat_msg(EXFAT_ERROR, stderr, \ + fmt, ##__VA_ARGS__) +#define exfat_info(fmt, ...) exfat_msg(EXFAT_INFO, stdout, \ + fmt, ##__VA_ARGS__) +#define exfat_debug(fmt, ...) exfat_msg(EXFAT_DEBUG, stdout, \ + "[%s:%4d] " fmt, __func__, \ + __LINE__, ##__VA_ARGS__) + +#endif /* !_LIBEXFAT_H */ diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..cc93668 --- /dev/null +++ b/include/list.h @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) * __mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/** + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use as a start point in + * list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - iterate over list of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against + * removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue - iterate over list of given type + * continuing after existing point safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list of given + * type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +#endif diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..da9be8d --- /dev/null +++ b/include/version.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Namjae Jeon + */ + +#ifndef _VERSION_H + +#define EXFAT_PROGS_VERSION "1.1.3" + +#endif /* !_VERSION_H */ diff --git a/label/Makefile.am b/label/Makefile.am index 5e94c22..3f980bb 100644 --- a/label/Makefile.am +++ b/label/Makefile.am @@ -1,27 +1,6 @@ -# -# Makefile.am (30.03.15) -# Automake source. -# -# 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. -# +AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +exfatlabel_LDADD = $(top_builddir)/lib/libexfat.a sbin_PROGRAMS = exfatlabel -dist_man8_MANS = exfatlabel.8 -exfatlabel_SOURCES = main.c -exfatlabel_CPPFLAGS = -I$(top_srcdir)/libexfat -exfatlabel_LDADD = ../libexfat/libexfat.a + +exfatlabel_SOURCES = label.c diff --git a/label/exfatlabel.8 b/label/exfatlabel.8 deleted file mode 100644 index 4f55510..0000000 --- a/label/exfatlabel.8 +++ /dev/null @@ -1,48 +0,0 @@ -.\" Copyright (C) 2011-2018 Andrew Nayenko -.\" -.TH EXFATLABEL 8 "September 2017" -.SH NAME -.B exfatlabel -\- get or set an exFAT file system label -.SH SYNOPSIS -.B exfatlabel -[ -.B \-V -] -.I device -[ -.I label -] - -.SH DESCRIPTION -.B exfatlabel -reads or changes an exFAT file system label (volume name). - -If -.I label -argument is present, -.B exfatlabel -sets the new volume name. Empty label ('') removes volume name. Label can be -up to 15 characters. This limit is shorter if characters beyond Unicode BMP -are used because internally label is stored in UTF-16. - -If -.I label -argument is omitted, -.B exfatlabel -just prints current volume name. - -.SH COMMAND LINE OPTIONS -Command line options available: -.TP -.BI \-V -Print version and copyright. - -.SH EXIT CODES -Zero is returned on success. Any other code means an error. - -.SH AUTHOR -Andrew Nayenko - -.SH SEE ALSO -.BR mkexfatfs (8) diff --git a/label/label.c b/label/label.c new file mode 100644 index 0000000..b41e827 --- /dev/null +++ b/label/label.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Namjae Jeon + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" + +static void usage(void) +{ + fprintf(stderr, "Usage: exfatlabel\n"); + fprintf(stderr, "\t-i | --volume-serial Switch to volume serial mode\n"); + fprintf(stderr, "\t-V | --version Show version\n"); + fprintf(stderr, "\t-h | --help Show help\n"); + + exit(EXIT_FAILURE); +} + +static struct option opts[] = { + {"volume-serial", no_argument, NULL, 'i' }, + {"version", no_argument, NULL, 'V' }, + {"help", no_argument, NULL, 'h' }, + {"?", no_argument, NULL, '?' }, + {NULL, 0, NULL, 0 } +}; + +int main(int argc, char *argv[]) +{ + int c; + int ret = EXIT_FAILURE; + struct exfat_blk_dev bd; + struct exfat_user_input ui; + bool version_only = false; + off_t root_clu_off; + int serial_mode = 0; + int flags = 0; + + init_user_input(&ui); + + if (!setlocale(LC_CTYPE, "")) + exfat_err("failed to init locale/codeset\n"); + + if (argc == 2) + flags = EXFAT_GET_VOLUME_LABEL; + else if (argc == 3) + flags = EXFAT_SET_VOLUME_LABEL; + + opterr = 0; + while ((c = getopt_long(argc, argv, "iVh", opts, NULL)) != EOF) + switch (c) { + case 'i': + serial_mode = true; + if (argc == 3) + flags = EXFAT_GET_VOLUME_SERIAL; + else if (argc == 4) + flags = EXFAT_SET_VOLUME_SERIAL; + + break; + case 'V': + version_only = true; + break; + case '?': + case 'h': + default: + usage(); + } + + show_version(); + if (version_only) + exit(EXIT_FAILURE); + + if (argc < 2) + usage(); + + memset(ui.dev_name, 0, sizeof(ui.dev_name)); + snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[serial_mode + 1]); + + ret = exfat_get_blk_dev_info(&ui, &bd); + if (ret < 0) + goto out; + + if (serial_mode) { + /* Mode to change or display volume serial */ + if (flags == EXFAT_GET_VOLUME_SERIAL) { + ret = exfat_show_volume_serial(bd.dev_fd); + } else if (flags == EXFAT_SET_VOLUME_SERIAL) { + ui.volume_serial = strtoul(argv[3], NULL, 0); + ret = exfat_set_volume_serial(&bd, &ui); + } + } else { + /* Mode to change or display volume label */ + root_clu_off = exfat_get_root_entry_offset(&bd); + if (root_clu_off < 0) + goto close_fd_out; + + if (flags == EXFAT_GET_VOLUME_LABEL) + ret = exfat_show_volume_label(&bd, root_clu_off); + else if (flags == EXFAT_SET_VOLUME_LABEL) + ret = exfat_set_volume_label(&bd, argv[2], root_clu_off); + } + +close_fd_out: + close(bd.dev_fd); +out: + return ret; +} diff --git a/label/main.c b/label/main.c deleted file mode 100644 index 398498a..0000000 --- a/label/main.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - main.c (20.01.11) - Prints or changes exFAT volume label. - - 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 -#include -#include - -int main(int argc, char* argv[]) -{ - char** pp; - struct exfat ef; - int rc = 0; - - for (pp = argv + 1; *pp; pp++) - if (strcmp(*pp, "-V") == 0) - { - printf("exfatlabel %s\n", VERSION); - puts("Copyright (C) 2011-2018 Andrew Nayenko"); - return 0; - } - - if (argc != 2 && argc != 3) - { - fprintf(stderr, "Usage: %s [-V] [label]\n", argv[0]); - return 1; - } - - if (argv[2]) - { - if (exfat_mount(&ef, argv[1], "") != 0) - return 1; - rc = (exfat_set_label(&ef, argv[2]) != 0); - } - else - { - if (exfat_mount(&ef, argv[1], "ro") != 0) - return 1; - puts(exfat_get_label(&ef)); - } - - exfat_unmount(&ef); - return rc; -} diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..5ea12db --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,4 @@ +AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +noinst_LIBRARIES = libexfat.a + +libexfat_a_SOURCES = libexfat.c diff --git a/lib/libexfat.c b/lib/libexfat.c new file mode 100644 index 0000000..c1c9b03 --- /dev/null +++ b/lib/libexfat.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Namjae Jeon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" +#include "version.h" + +#define BITS_PER_LONG (sizeof(long) * CHAR_BIT) + +#ifdef WORDS_BIGENDIAN +#define BITOP_LE_SWIZZLE ((BITS_PER_LONG - 1) & ~0x7) +#else +#define BITOP_LE_SWIZZLE 0 +#endif + +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) + +unsigned int print_level = EXFAT_INFO; + +static inline void set_bit(int nr, void *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p |= mask; +} + +static inline void clear_bit(int nr, void *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p &= ~mask; +} + +static inline void set_bit_le(int nr, void *addr) +{ + set_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +static inline void clear_bit_le(int nr, void *addr) +{ + clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); +} + +void exfat_set_bit(struct exfat_blk_dev *bd, char *bitmap, + unsigned int clu) +{ + int b; + + b = clu & ((bd->sector_size << 3) - 1); + + set_bit_le(b, bitmap); +} + +void exfat_clear_bit(struct exfat_blk_dev *bd, char *bitmap, + unsigned int clu) +{ + int b; + + b = clu & ((bd->sector_size << 3) - 1); + + clear_bit_le(b, bitmap); +} + +wchar_t exfat_bad_char(wchar_t w) +{ + return (w < 0x0020) + || (w == '*') || (w == '?') || (w == '<') || (w == '>') + || (w == '|') || (w == '"') || (w == ':') || (w == '/') + || (w == '\\'); +} + +void boot_calc_checksum(unsigned char *sector, unsigned short size, + bool is_boot_sec, __le32 *checksum) +{ + unsigned int index; + + if (is_boot_sec) { + for (index = 0; index < size; index++) { + if ((index == 106) || (index == 107) || (index == 112)) + continue; + *checksum = ((*checksum & 1) ? 0x80000000 : 0) + + (*checksum >> 1) + sector[index]; + } + } else { + for (index = 0; index < size; index++) { + *checksum = ((*checksum & 1) ? 0x80000000 : 0) + + (*checksum >> 1) + sector[index]; + } + } +} + +void show_version(void) +{ + printf("exfatprogs version : %s\n", EXFAT_PROGS_VERSION); +} + +static inline unsigned int sector_size_bits(unsigned int size) +{ + unsigned int bits = 8; + + do { + bits++; + size >>= 1; + } while (size > 256); + + return bits; +} + +static void exfat_set_default_cluster_size(struct exfat_blk_dev *bd, + struct exfat_user_input *ui) +{ + if (256 * MB >= bd->size) + ui->cluster_size = 4 * KB; + else if (32 * GB >= bd->size) + ui->cluster_size = 32 * KB; + else + ui->cluster_size = 128 * KB; +} + +void init_user_input(struct exfat_user_input *ui) +{ + memset(ui, 0, sizeof(struct exfat_user_input)); + ui->writeable = true; + ui->quick = true; +} + +int exfat_get_blk_dev_info(struct exfat_user_input *ui, + struct exfat_blk_dev *bd) +{ + int fd, ret = -1; + off_t blk_dev_size; + struct stat st; + unsigned long long blk_dev_offset = 0; + + fd = open(ui->dev_name, ui->writeable ? O_RDWR|O_EXCL : O_RDONLY); + if (fd < 0) { + exfat_err("open failed : %s, %s\n", ui->dev_name, + strerror(errno)); + return -1; + } + blk_dev_size = lseek(fd, 0, SEEK_END); + if (blk_dev_size <= 0) { + exfat_err("invalid block device size(%s)\n", + ui->dev_name); + ret = blk_dev_size; + close(fd); + goto out; + } + + if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) { + char pathname[sizeof("/sys/dev/block/4294967295:4294967295/start")]; + FILE *fp; + + snprintf(pathname, sizeof(pathname), "/sys/dev/block/%u:%u/start", + major(st.st_rdev), minor(st.st_rdev)); + fp = fopen(pathname, "r"); + if (fp != NULL) { + if (fscanf(fp, "%llu", &blk_dev_offset) == 1) { + /* + * Linux kernel always reports partition offset + * in 512-byte units, regardless of sector size + */ + blk_dev_offset <<= 9; + } + fclose(fp); + } + } + + bd->dev_fd = fd; + bd->offset = blk_dev_offset; + bd->size = blk_dev_size; + if (!ui->cluster_size) + exfat_set_default_cluster_size(bd, ui); + + if (!ui->boundary_align) + ui->boundary_align = DEFAULT_BOUNDARY_ALIGNMENT; + + if (ioctl(fd, BLKSSZGET, &bd->sector_size) < 0) + bd->sector_size = DEFAULT_SECTOR_SIZE; + bd->sector_size_bits = sector_size_bits(bd->sector_size); + bd->num_sectors = blk_dev_size / bd->sector_size; + bd->num_clusters = blk_dev_size / ui->cluster_size; + + exfat_debug("Block device name : %s\n", ui->dev_name); + exfat_debug("Block device offset : %llu\n", bd->offset); + exfat_debug("Block device size : %llu\n", bd->size); + exfat_debug("Block sector size : %u\n", bd->sector_size); + exfat_debug("Number of the sectors : %llu\n", + bd->num_sectors); + exfat_debug("Number of the clusters : %u\n", + bd->num_clusters); + + ret = 0; + bd->dev_fd = fd; +out: + return ret; +} + +ssize_t exfat_read(int fd, void *buf, size_t size, off_t offset) +{ + return pread(fd, buf, size, offset); +} + +ssize_t exfat_write(int fd, void *buf, size_t size, off_t offset) +{ + return pwrite(fd, buf, size, offset); +} + +size_t exfat_utf16_len(const __le16 *str, size_t max_size) +{ + size_t i = 0; + + while (le16_to_cpu(str[i]) && i < max_size) + i++; + return i; +} + +ssize_t exfat_utf16_enc(const char *in_str, __u16 *out_str, size_t out_size) +{ + size_t mbs_len, out_len, i; + wchar_t *wcs; + + mbs_len = mbstowcs(NULL, in_str, 0); + if (mbs_len == (size_t)-1) { + if (errno == EINVAL || errno == EILSEQ) + exfat_err("invalid character sequence in current locale\n"); + return -errno; + } + + wcs = calloc(mbs_len+1, sizeof(wchar_t)); + if (!wcs) + return -ENOMEM; + + /* First convert multibyte char* string to wchar_t* string */ + if (mbstowcs(wcs, in_str, mbs_len+1) == (size_t)-1) { + if (errno == EINVAL || errno == EILSEQ) + exfat_err("invalid character sequence in current locale\n"); + free(wcs); + return -errno; + } + + /* Convert wchar_t* string (sequence of code points) to UTF-16 string */ + for (i = 0, out_len = 0; i < mbs_len; i++) { + if (2*(out_len+1) > out_size || + (wcs[i] >= 0x10000 && 2*(out_len+2) > out_size)) { + exfat_err("input string is too long\n"); + free(wcs); + return -E2BIG; + } + + /* Encode code point above Plane0 as UTF-16 surrogate pair */ + if (wcs[i] >= 0x10000) { + out_str[out_len++] = + cpu_to_le16(((wcs[i] - 0x10000) >> 10) + 0xD800); + wcs[i] = ((wcs[i] - 0x10000) & 0x3FF) + 0xDC00; + } + + out_str[out_len++] = cpu_to_le16(wcs[i]); + } + + free(wcs); + return 2*out_len; +} + +ssize_t exfat_utf16_dec(const __u16 *in_str, size_t in_len, + char *out_str, size_t out_size) +{ + size_t wcs_len, out_len, c_len, i; + char c_str[MB_LEN_MAX]; + wchar_t *wcs; + mbstate_t ps; + wchar_t w; + + wcs = calloc(in_len/2+1, sizeof(wchar_t)); + if (!wcs) + return -ENOMEM; + + /* First convert UTF-16 string to wchar_t* string */ + for (i = 0, wcs_len = 0; i < in_len/2; i++, wcs_len++) { + wcs[wcs_len] = le16_to_cpu(in_str[i]); + /* + * If wchar_t can store code point above Plane0 + * then unpack UTF-16 surrogate pair to code point + */ +#if WCHAR_MAX >= 0x10FFFF + if (wcs[wcs_len] >= 0xD800 && wcs[wcs_len] <= 0xDBFF && + i+1 < in_len/2) { + w = le16_to_cpu(in_str[i+1]); + if (w >= 0xDC00 && w <= 0xDFFF) { + wcs[wcs_len] = 0x10000 + + ((wcs[wcs_len] - 0xD800) << 10) + + (w - 0xDC00); + i++; + } + } +#endif + } + + memset(&ps, 0, sizeof(ps)); + + /* And then convert wchar_t* string to multibyte char* string */ + for (i = 0, out_len = 0, c_len = 0; i <= wcs_len; i++) { + c_len = wcrtomb(c_str, wcs[i], &ps); + /* + * If character is non-representable in current locale then + * try to store it as Unicode replacement code point U+FFFD + */ + if (c_len == (size_t)-1 && errno == EILSEQ) + c_len = wcrtomb(c_str, 0xFFFD, &ps); + /* If U+FFFD is also non-representable, try question mark */ + if (c_len == (size_t)-1 && errno == EILSEQ) + c_len = wcrtomb(c_str, L'?', &ps); + /* If also (7bit) question mark fails then we cannot do more */ + if (c_len == (size_t)-1) { + exfat_err("invalid UTF-16 sequence\n"); + free(wcs); + return -errno; + } + if (out_len+c_len > out_size) { + exfat_err("input string is too long\n"); + free(wcs); + return -E2BIG; + } + memcpy(out_str+out_len, c_str, c_len); + out_len += c_len; + } + + free(wcs); + + /* Last iteration of above loop should have produced null byte */ + if (c_len == 0 || out_str[out_len-1] != 0) { + exfat_err("invalid UTF-16 sequence\n"); + return -errno; + } + + return out_len-1; +} + +off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd) +{ + struct pbr *bs; + int nbytes; + unsigned int cluster_size, sector_size; + off_t root_clu_off; + + bs = (struct pbr *)malloc(EXFAT_MAX_SECTOR_SIZE); + if (!bs) { + exfat_err("failed to allocate memory\n"); + return -ENOMEM; + } + + nbytes = exfat_read(bd->dev_fd, bs, EXFAT_MAX_SECTOR_SIZE, 0); + if (nbytes != EXFAT_MAX_SECTOR_SIZE) { + exfat_err("boot sector read failed: %d\n", errno); + free(bs); + return -1; + } + + sector_size = 1 << bs->bsx.sect_size_bits; + cluster_size = (1 << bs->bsx.sect_per_clus_bits) * sector_size; + root_clu_off = le32_to_cpu(bs->bsx.clu_offset) * sector_size + + (le32_to_cpu(bs->bsx.root_cluster) - EXFAT_RESERVED_CLUSTERS) * + cluster_size; + free(bs); + + return root_clu_off; +} + +char *exfat_conv_volume_label(struct exfat_dentry *vol_entry) +{ + char *volume_label; + __le16 disk_label[VOLUME_LABEL_MAX_LEN]; + + volume_label = malloc(VOLUME_LABEL_BUFFER_SIZE); + if (!volume_label) + return NULL; + + memcpy(disk_label, vol_entry->vol_label, sizeof(disk_label)); + memset(volume_label, 0, VOLUME_LABEL_BUFFER_SIZE); + if (exfat_utf16_dec(disk_label, vol_entry->vol_char_cnt*2, + volume_label, VOLUME_LABEL_BUFFER_SIZE) < 0) { + exfat_err("failed to decode volume label\n"); + free(volume_label); + return NULL; + } + + return volume_label; +} + +int exfat_show_volume_label(struct exfat_blk_dev *bd, off_t root_clu_off) +{ + struct exfat_dentry *vol_entry; + char *volume_label; + int nbytes; + + vol_entry = malloc(sizeof(struct exfat_dentry)); + if (!vol_entry) { + exfat_err("failed to allocate memory\n"); + return -ENOMEM; + } + + nbytes = exfat_read(bd->dev_fd, vol_entry, + sizeof(struct exfat_dentry), root_clu_off); + if (nbytes != sizeof(struct exfat_dentry)) { + exfat_err("volume entry read failed: %d\n", errno); + free(vol_entry); + return -1; + } + + volume_label = exfat_conv_volume_label(vol_entry); + if (!volume_label) { + free(vol_entry); + return -EINVAL; + } + + exfat_info("label: %s\n", volume_label); + + free(volume_label); + free(vol_entry); + return 0; +} + +int exfat_set_volume_label(struct exfat_blk_dev *bd, + char *label_input, off_t root_clu_off) +{ + struct exfat_dentry vol; + int nbytes; + __u16 volume_label[VOLUME_LABEL_MAX_LEN]; + int volume_label_len; + + volume_label_len = exfat_utf16_enc(label_input, + volume_label, sizeof(volume_label)); + if (volume_label_len < 0) { + exfat_err("failed to encode volume label\n"); + return -1; + } + + vol.type = EXFAT_VOLUME; + memset(vol.vol_label, 0, sizeof(vol.vol_label)); + memcpy(vol.vol_label, volume_label, volume_label_len); + vol.vol_char_cnt = volume_label_len/2; + + nbytes = exfat_write(bd->dev_fd, &vol, sizeof(struct exfat_dentry), + root_clu_off); + if (nbytes != sizeof(struct exfat_dentry)) { + exfat_err("volume entry write failed: %d\n", errno); + return -1; + } + fsync(bd->dev_fd); + + exfat_info("new label: %s\n", label_input); + return 0; +} + +int exfat_read_sector(struct exfat_blk_dev *bd, void *buf, unsigned int sec_off) +{ + int ret; + unsigned long long offset = sec_off * bd->sector_size; + + ret = pread(bd->dev_fd, buf, bd->sector_size, offset); + if (ret < 0) { + exfat_err("read failed, sec_off : %u\n", sec_off); + return -1; + } + return 0; +} + +int exfat_write_sector(struct exfat_blk_dev *bd, void *buf, + unsigned int sec_off) +{ + int bytes; + unsigned long long offset = sec_off * bd->sector_size; + + bytes = pwrite(bd->dev_fd, buf, bd->sector_size, offset); + if (bytes != (int)bd->sector_size) { + exfat_err("write failed, sec_off : %u, bytes : %d\n", sec_off, + bytes); + return -1; + } + return 0; +} + +int exfat_write_checksum_sector(struct exfat_blk_dev *bd, + unsigned int checksum, bool is_backup) +{ + __le32 *checksum_buf; + int ret = 0; + unsigned int i; + unsigned int sec_idx = CHECKSUM_SEC_IDX; + + checksum_buf = malloc(bd->sector_size); + if (!checksum_buf) + return -1; + + if (is_backup) + sec_idx += BACKUP_BOOT_SEC_IDX; + + for (i = 0; i < bd->sector_size / sizeof(int); i++) + checksum_buf[i] = cpu_to_le32(checksum); + + ret = exfat_write_sector(bd, checksum_buf, sec_idx); + if (ret) { + exfat_err("checksum sector write failed\n"); + goto free; + } + +free: + free(checksum_buf); + return ret; +} + +int exfat_show_volume_serial(int fd) +{ + struct pbr *ppbr; + int ret; + + ppbr = malloc(EXFAT_MAX_SECTOR_SIZE); + if (!ppbr) { + exfat_err("Cannot allocate pbr: out of memory\n"); + return -1; + } + + /* read main boot sector */ + ret = exfat_read(fd, (char *)ppbr, EXFAT_MAX_SECTOR_SIZE, 0); + if (ret < 0) { + exfat_err("main boot sector read failed\n"); + ret = -1; + goto free_ppbr; + } + + exfat_info("volume serial : 0x%x\n", ppbr->bsx.vol_serial); + +free_ppbr: + free(ppbr); + return ret; +} + +static int exfat_update_boot_checksum(struct exfat_blk_dev *bd, bool is_backup) +{ + unsigned int checksum = 0; + int ret, sec_idx, backup_sec_idx = 0; + unsigned char *buf; + + buf = malloc(bd->sector_size); + if (!buf) { + exfat_err("Cannot allocate pbr: out of memory\n"); + return -1; + } + + if (is_backup) + backup_sec_idx = BACKUP_BOOT_SEC_IDX; + + for (sec_idx = BOOT_SEC_IDX; sec_idx < CHECKSUM_SEC_IDX; sec_idx++) { + bool is_boot_sec = false; + + ret = exfat_read_sector(bd, buf, sec_idx + backup_sec_idx); + if (ret < 0) { + exfat_err("sector(%d) read failed\n", sec_idx); + ret = -1; + goto free_buf; + } + + if (sec_idx == BOOT_SEC_IDX) + is_boot_sec = true; + + boot_calc_checksum(buf, bd->sector_size, is_boot_sec, + &checksum); + } + + ret = exfat_write_checksum_sector(bd, checksum, is_backup); + +free_buf: + free(buf); + + return ret; +} + +int exfat_set_volume_serial(struct exfat_blk_dev *bd, + struct exfat_user_input *ui) +{ + int ret; + struct pbr *ppbr; + + ppbr = malloc(EXFAT_MAX_SECTOR_SIZE); + if (!ppbr) { + exfat_err("Cannot allocate pbr: out of memory\n"); + return -1; + } + + /* read main boot sector */ + ret = exfat_read(bd->dev_fd, (char *)ppbr, EXFAT_MAX_SECTOR_SIZE, + BOOT_SEC_IDX); + if (ret < 0) { + exfat_err("main boot sector read failed\n"); + ret = -1; + goto free_ppbr; + } + + bd->sector_size = 1 << ppbr->bsx.sect_size_bits; + ppbr->bsx.vol_serial = ui->volume_serial; + + /* update main boot sector */ + ret = exfat_write_sector(bd, (char *)ppbr, BOOT_SEC_IDX); + if (ret < 0) { + exfat_err("main boot sector write failed\n"); + ret = -1; + goto free_ppbr; + } + + /* update backup boot sector */ + ret = exfat_write_sector(bd, (char *)ppbr, BACKUP_BOOT_SEC_IDX); + if (ret < 0) { + exfat_err("backup boot sector write failed\n"); + ret = -1; + goto free_ppbr; + } + + ret = exfat_update_boot_checksum(bd, 0); + if (ret < 0) { + exfat_err("main checksum update failed\n"); + goto free_ppbr; + } + + ret = exfat_update_boot_checksum(bd, 1); + if (ret < 0) + exfat_err("backup checksum update failed\n"); +free_ppbr: + free(ppbr); + + exfat_info("New volume serial : 0x%x\n", ui->volume_serial); + + return ret; +} + +unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd, + unsigned int clu_off_sectnr, unsigned int clu) +{ + return clu_off_sectnr * bd->sector_size + + (clu - EXFAT_RESERVED_CLUSTERS) * bd->cluster_size; +} diff --git a/libexfat/Makefile.am b/libexfat/Makefile.am deleted file mode 100644 index d639e13..0000000 --- a/libexfat/Makefile.am +++ /dev/null @@ -1,39 +0,0 @@ -# -# Makefile.am (30.03.15) -# Automake source. -# -# 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. -# - -noinst_LIBRARIES = libexfat.a -libexfat_a_SOURCES = \ - byteorder.h \ - cluster.c \ - compiler.h \ - exfat.h \ - exfatfs.h \ - io.c \ - log.c \ - lookup.c \ - mount.c \ - node.c \ - platform.h \ - repair.c \ - time.c \ - utf.c \ - utils.c diff --git a/libexfat/byteorder.h b/libexfat/byteorder.h deleted file mode 100644 index ba54410..0000000 --- a/libexfat/byteorder.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - 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/libexfat/cluster.c b/libexfat/cluster.c deleted file mode 100644 index 0f2e91b..0000000 --- a/libexfat/cluster.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - 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) ~((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/libexfat/compiler.h b/libexfat/compiler.h deleted file mode 100644 index 88d3d7c..0000000 --- a/libexfat/compiler.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - 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)) -#define UNUSED __attribute__((unused)) -#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)) -#define UNUSED __attribute__((unused)) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#define USE_C11_STATIC_ASSERT -#endif - -#else - -#define PRINTF -#define NORETURN -#define PACKED -#define UNUSED - -#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/libexfat/config.h b/libexfat/config.h deleted file mode 100644 index 55477be..0000000 --- a/libexfat/config.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 Huawei Device Co., Ltd. - * - * 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. - */ -/* libexfat/config.h.in. Generated from configure.ac by autoheader. */ - -#ifndef CONFIG_H -#define CONFIG_H - -/* 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 - -/* Enable vsyslog(). */ -#define _DEFAULT_SOURCE /**/ - -/* 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 */ - -/* Enable pread() and pwrite(). */ -#define _XOPEN_SOURCE 500 -#endif diff --git a/libexfat/exfat.h b/libexfat/exfat.h deleted file mode 100644 index 939fec0..0000000 --- a/libexfat/exfat.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - 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; - -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; - -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); -bool exfat_match_option(const char* options, const char* option_name); - -int exfat_utf16_to_utf8(char* output, const le16_t* input, size_t outsize, - size_t insize); -int exfat_utf8_to_utf16(le16_t* output, const char* input, size_t outsize, - size_t insize); -size_t exfat_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_soil_super_block(const struct exfat* ef); -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, - uint8_t tzoffset); -void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, - uint8_t* centisec, uint8_t* tzoffset); -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/libexfat/exfatfs.h b/libexfat/exfatfs.h deleted file mode 100644 index b9ea268..0000000 --- a/libexfat/exfatfs.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - 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 crtime_tzo, mtime_tzo, atime_tzo; /* timezone offset encoded */ - uint8_t __unknown2[7]; -} -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/libexfat/io.c b/libexfat/io.c deleted file mode 100644 index 0fc35d2..0000000 --- a/libexfat/io.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - 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 defined(__NetBSD__) -#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 -}; - -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 = open_ro(spec); - 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 = open_rw(spec); - 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 = open_rw(spec); - if (dev->fd != -1) - { - dev->mode = EXFAT_MODE_RW; - break; - } - dev->fd = open_ro(spec); - 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 -#elif defined(__NetBSD__) - if (!S_ISREG(stbuf.st_mode)) - { - off_t size; - - if (ioctl(dev->fd, DIOCGMEDIASIZE, &size) == -1) - { - close(dev->fd); - free(dev); - exfat_error("failed to get media size"); - return NULL; - } - dev->size = size; - } - 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 (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 - 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, (void*) 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, (void*) 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) -{ - uint64_t newsize = offset; - cluster_t cluster; - char* bufp = buffer; - off_t lsize, loffset, remainder; - - if (offset < 0) - return -EINVAL; - if (newsize >= node->size) - return 0; - if (size == 0) - return 0; - - cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb)); - if (CLUSTER_INVALID(*ef->sb, cluster)) - { - exfat_error("invalid cluster 0x%x while reading", cluster); - return -EIO; - } - - loffset = newsize % CLUSTER_SIZE(*ef->sb); - remainder = MIN(size, node->size - newsize); - 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 - newsize) - remainder; -} - -ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, - const void* buffer, size_t size, off_t offset) -{ - uint64_t newsize = offset; - int rc; - cluster_t cluster; - const char* bufp = buffer; - off_t lsize, loffset, remainder; - - if (offset < 0) - return -EINVAL; - if (newsize > node->size) - { - rc = exfat_truncate(ef, node, newsize, true); - if (rc != 0) - return rc; - } - if (newsize + size > node->size) - { - rc = exfat_truncate(ef, node, newsize + size, false); - if (rc != 0) - return rc; - } - if (size == 0) - return 0; - - cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb)); - if (CLUSTER_INVALID(*ef->sb, cluster)) - { - exfat_error("invalid cluster 0x%x while writing", cluster); - return -EIO; - } - - loffset = newsize % 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/libexfat/log.c b/libexfat/log.c deleted file mode 100644 index 22e5ccd..0000000 --- a/libexfat/log.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - log.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 -#ifdef __ANDROID__ -#include -#else -#include -#endif -#include - -int exfat_errors; - -/* - * This message means an internal bug in exFAT implementation. - */ -void exfat_bug(const char* format, ...) -{ - va_list ap, aq; - - va_start(ap, format); - va_copy(aq, ap); - - fflush(stdout); - fputs("BUG: ", stderr); - vfprintf(stderr, format, ap); - va_end(ap); - fputs(".\n", stderr); - -#ifdef __ANDROID__ - __android_log_vprint(ANDROID_LOG_FATAL, PACKAGE, format, aq); -#else - if (!isatty(STDERR_FILENO)) - vsyslog(LOG_CRIT, format, aq); -#endif - va_end(aq); - - abort(); -} - -/* - * This message means an error in exFAT file system. - */ -void exfat_error(const char* format, ...) -{ - va_list ap, aq; - - exfat_errors++; - va_start(ap, format); - va_copy(aq, ap); - - fflush(stdout); - fputs("ERROR: ", stderr); - vfprintf(stderr, format, ap); - va_end(ap); - fputs(".\n", stderr); - -#ifdef __ANDROID__ - __android_log_vprint(ANDROID_LOG_ERROR, PACKAGE, format, aq); -#else - if (!isatty(STDERR_FILENO)) - vsyslog(LOG_ERR, format, aq); -#endif - va_end(aq); -} - -/* - * This message means that there is something unexpected in exFAT file system - * that can be a potential problem. - */ -void exfat_warn(const char* format, ...) -{ - va_list ap, aq; - - va_start(ap, format); - va_copy(aq, ap); - - fflush(stdout); - fputs("WARN: ", stderr); - vfprintf(stderr, format, ap); - va_end(ap); - fputs(".\n", stderr); - -#ifdef __ANDROID__ - __android_log_vprint(ANDROID_LOG_WARN, PACKAGE, format, aq); -#else - if (!isatty(STDERR_FILENO)) - vsyslog(LOG_WARNING, format, aq); -#endif - va_end(aq); -} - -/* - * Just debug message. Disabled by default. - */ -void exfat_debug(const char* format, ...) -{ - va_list ap, aq; - - va_start(ap, format); - va_copy(aq, ap); - - fflush(stdout); - fputs("DEBUG: ", stderr); - vfprintf(stderr, format, ap); - va_end(ap); - fputs(".\n", stderr); - -#ifdef __ANDROID__ - __android_log_vprint(ANDROID_LOG_DEBUG, PACKAGE, format, aq); -#else - if (!isatty(STDERR_FILENO)) - vsyslog(LOG_DEBUG, format, aq); -#endif - va_end(aq); -} diff --git a/libexfat/lookup.c b/libexfat/lookup.c deleted file mode 100644 index 50ad31f..0000000 --- a/libexfat/lookup.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - 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 = exfat_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 = exfat_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"); -} diff --git a/libexfat/mount.c b/libexfat/mount.c deleted file mode 100644 index f0ca0e3..0000000 --- a/libexfat/mount.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - 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 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 = exfat_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; - size_t 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); -} - -int exfat_soil_super_block(const struct exfat* ef) -{ - 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 (exfat_match_option(options, "ro")) - mode = EXFAT_MODE_RO; - else if (exfat_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) > - (uint64_t) 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; - } - if (le16_to_cpu(ef->sb->volume_state) & EXFAT_STATE_MOUNTED) - exfat_warn("volume was not unmounted cleanly"); - - 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; - } - - 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/libexfat/node.c b/libexfat/node.c deleted file mode 100644 index 9cffbcb..0000000 --- a/libexfat/node.c +++ /dev/null @@ -1,1243 +0,0 @@ -/* - 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 == (ssize_t) 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 == (ssize_t) 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, meta1->mtime_tzo); - /* there is no centiseconds field for atime */ - node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, - 0, meta1->atime_tzo); -} - -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; - le16_t label_name[EXFAT_ENAME_MAX]; - - 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; - } - /* copy to a temporary buffer to avoid unaligned access to a - packed member */ - memcpy(label_name, label->name, sizeof(label_name)); - if (exfat_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; - le16_t edate, etime; - - 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, &edate, &etime, - &meta1->mtime_cs, &meta1->mtime_tzo); - meta1->mdate = edate; - meta1->mtime = etime; - exfat_unix2exfat(node->atime, &edate, &etime, - NULL, &meta1->atime_tzo); - meta1->adate = edate; - meta1->atime = etime; - 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(exfat_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; - int 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 < 1u + 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) - { - int rc; - - /* suitable slot is found, check that it's not occupied */ - rc = check_slot(ef, dir, *offset, n); - if (rc == -EINVAL) - { - /* slot at (i-n) is occupied, go back and check (i-n+1) */ - i -= contiguous - 1; - contiguous = 0; - } - else - { - /* slot is free or an error occurred */ - free(dmap); - return rc; - } - } - } - 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 = exfat_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; - le16_t edate, etime; - - 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), &edate, &etime, - &meta1->crtime_cs, &meta1->crtime_tzo); - meta1->adate = meta1->mdate = meta1->crdate = edate; - meta1->atime = meta1->mtime = meta1->crtime = etime; - meta1->mtime_cs = meta1->crtime_cs; /* there is no atime_cs */ - meta1->atime_tzo = meta1->mtime_tzo = meta1->crtime_tzo; - - 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(exfat_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 = exfat_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(exfat_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 = exfat_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 = exfat_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/libexfat/platform.h b/libexfat/platform.h deleted file mode 100644 index d42e1ee..0000000 --- a/libexfat/platform.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - 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 - -#elif defined(__sun) - -#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 - -#else -#error Unknown platform -#endif - -#endif /* ifndef PLATFORM_H_INCLUDED */ diff --git a/libexfat/repair.c b/libexfat/repair.c deleted file mode 100644 index 8e8e962..0000000 --- a/libexfat/repair.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - 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); -} - -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(UNUSED 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/libexfat/time.c b/libexfat/time.c deleted file mode 100644 index e2a3b23..0000000 --- a/libexfat/time.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - 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, - uint8_t tzoffset) -{ - 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 */ - if (tzoffset & 0x80) - /* lower 7 bits are signed timezone offset in 15 minute increments */ - unix_time -= (int8_t)(tzoffset << 1) * 15 * 60 / 2; - else - /* timezone offset not present, assume our local timezone */ - unix_time += exfat_timezone; - - return unix_time; -} - -void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, - uint8_t* centisec, uint8_t* tzoffset) -{ - 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; - - /* record our local timezone offset in exFAT (15 minute increment) format */ - *tzoffset = (uint8_t)(-exfat_timezone / 60 / 15) | 0x80; -} - -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/libexfat/utf.c b/libexfat/utf.c deleted file mode 100644 index 96f4608..0000000 --- a/libexfat/utf.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - 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 exfat_utf16_to_utf8(char* output, const le16_t* input, size_t outsize, - size_t insize) -{ - const le16_t* iptr = input; - const le16_t* iend = input + insize; - char* optr = output; - const char* oend = output + outsize; - wchar_t wc; - - while (iptr < iend) - { - iptr = utf16_to_wchar(iptr, &wc, iend - iptr); - if (iptr == NULL) - { - exfat_error("illegal UTF-16 sequence"); - return -EILSEQ; - } - optr = wchar_to_utf8(optr, wc, oend - optr); - if (optr == NULL) - { - exfat_error("name is too long"); - return -ENAMETOOLONG; - } - if (wc == 0) - return 0; - } - if (optr >= oend) - { - exfat_error("name is too long"); - return -ENAMETOOLONG; - } - *optr = '\0'; - return 0; -} - -static const char* utf8_to_wchar(const char* input, wchar_t* wc, - size_t insize) -{ - size_t size; - size_t i; - - if (insize == 0) - exfat_bug("no input for utf8_to_wchar"); - - if ((input[0] & 0x80) == 0) - { - *wc = (wchar_t) input[0]; - return input + 1; - } - else if ((input[0] & 0xe0) == 0xc0) - { - *wc = ((wchar_t) input[0] & 0x1f) << 6; - size = 2; - } - else if ((input[0] & 0xf0) == 0xe0) - { - *wc = ((wchar_t) input[0] & 0x0f) << 12; - size = 3; - } - else if ((input[0] & 0xf8) == 0xf0) - { - *wc = ((wchar_t) input[0] & 0x07) << 18; - size = 4; - } - else if ((input[0] & 0xfc) == 0xf8) - { - *wc = ((wchar_t) input[0] & 0x03) << 24; - size = 5; - } - else if ((input[0] & 0xfe) == 0xfc) - { - *wc = ((wchar_t) input[0] & 0x01) << 30; - size = 6; - } - else - return NULL; - - if (insize < size) - return NULL; - - /* the first byte is handled above */ - for (i = 1; i < size; i++) - { - if ((input[i] & 0xc0) != 0x80) - return NULL; - *wc |= (input[i] & 0x3f) << ((size - i - 1) * 6); - } - - return input + size; -} - -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 exfat_utf8_to_utf16(le16_t* output, const char* input, size_t outsize, - size_t insize) -{ - const char* iptr = input; - const char* iend = input + insize; - le16_t* optr = output; - const le16_t* oend = output + outsize; - wchar_t wc; - - while (iptr < iend) - { - iptr = utf8_to_wchar(iptr, &wc, iend - iptr); - if (iptr == NULL) - { - exfat_error("illegal UTF-8 sequence"); - return -EILSEQ; - } - optr = wchar_to_utf16(optr, wc, oend - optr); - if (optr == NULL) - { - exfat_error("name is too long"); - return -ENAMETOOLONG; - } - if (wc == 0) - break; - } - if (optr >= oend) - { - exfat_error("name is too long"); - return -ENAMETOOLONG; - } - *optr = cpu_to_le16(0); - return 0; -} - -size_t exfat_utf16_length(const le16_t* str) -{ - size_t i = 0; - - while (le16_to_cpu(str[i])) - i++; - return i; -} diff --git a/libexfat/utils.c b/libexfat/utils.c deleted file mode 100644 index 3a1154e..0000000 --- a/libexfat/utils.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - 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 (exfat_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) -{ - size_t 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; - size_t 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); -} - -bool exfat_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; -} diff --git a/manpages/dump.exfat.8 b/manpages/dump.exfat.8 new file mode 100644 index 0000000..4c6f589 --- /dev/null +++ b/manpages/dump.exfat.8 @@ -0,0 +1,17 @@ +.TH dump.exfat 8 +.SH NAME +dump.exfat \- Show on-disk information of exfat filesystem +.SH SYNOPSIS +.B dump.exfat +.I device +.br +.B dump.exfat \-V +.SH DESCRIPTION +.B dump.exfat +Print on-disk information from given device that formatted by exFAT filesystem. + +.PP +.SH OPTIONS +.TP +.B \-V +Prints the version number and exits. diff --git a/manpages/exfatlabel.8 b/manpages/exfatlabel.8 new file mode 100644 index 0000000..f3274bb --- /dev/null +++ b/manpages/exfatlabel.8 @@ -0,0 +1,36 @@ +.TH exfatlabel 8 +.SH NAME +exfatlabel \- Get or Set volume label or volume serial of an exFAT filesystem +.SH SYNOPSIS +.B exfatlabel +[ +.B \-i +.I volume-label +] [ +.B \-v +] +.I device +[ +.I label_string or serial_value +] +.br +.B exfatlabel \-V +.SH DESCRIPTION +.B exfatlabel +Print or set volume label of an existing exFAT filesystem. + +If there is a +.I label_string +in argument of exfatlabel, It will be written to volume label +field on given device. If not, exfatlabel will just print out +after reading volume label field from given device. If -i or +--volume-serial is given, It can be switched to volume serial +mode. +.PP +.SH OPTIONS +.TP +.BI \-i +Switch to volume serial mode. +.TP +.B \-V +Prints the version number and exits. diff --git a/manpages/fsck.exfat.8 b/manpages/fsck.exfat.8 new file mode 100644 index 0000000..83f7815 --- /dev/null +++ b/manpages/fsck.exfat.8 @@ -0,0 +1,51 @@ +.TH fsck.exfat 8 +.SH NAME +fsck.exfat \- check an exFAT filesystem +.SH SYNOPSIS +.B fsck.exfat +[ +.B \-a +] [ +.B \-n +] [ +.B \-r +] [ +.B \-v +] [ +.B \-y +] [ +.B \-v +] +.I device +.br +.B fsck.exfat \-V +.SH DESCRIPTION +.B fsck.exfat +checks an exFAT filesystem and repairs the filesystem +depending on the options passed. +.PP +.SH OPTIONS +.TP +.BI \-a +This option does the same thing as the -p option. It is provided for backwards compatibility only; it is suggested that people use -p option whenever possible. +.TP +.BI \-n +Check the filesystem but do not attempt to repair the filesystem. +.TP +.BI \-p +Repair the filesystem without user interaction if it can be done safely. +.TP +.BI \-r +Repair the filesystem interactively. +.TP +.BI \-v +Prints verbose debugging information while checking the exFAT filesystem. +.TP +.BI \-V +Prints the version number and exits. +.TP +.B \-y +Repair the filesystem answering yes to all questions. +.SH SEE ALSO +.BR fsck (8), +.BR fstab (5), diff --git a/manpages/mkfs.exfat.8 b/manpages/mkfs.exfat.8 new file mode 100644 index 0000000..9f867d3 --- /dev/null +++ b/manpages/mkfs.exfat.8 @@ -0,0 +1,111 @@ +.TH mkfs.exfat 8 +.SH NAME +mkfs.exfat \- create an exFAT filesystem +.SH SYNOPSIS +.B mkfs.exfat +[ +.B \-b +.I boundary_alignment +] [ +.B \-c +.I cluster_size +] [ +.B \-f +] [ +.B \-h +] [ +.B \-L +.I volume_label +] [ +.B \-\-pack\-bitmap +] [ +.B \-v +] +.I device +.br +.B mkfs.exfat \-V +.SH DESCRIPTION +.B mkfs.exfat +creates an exFAT filesystem by writing on a special +file using the values found in the arguments of the command line. +It is invoked automatically by +.BR mkfs (8) +when it is given the +.B \-t exfat +option. +.PP +As an example, to make a filesystem on the first partition on the first +SCSI disk, use: +.IP +.B mkfs.exfat /dev/sda1 +.PP +.SH OPTIONS +.TP +.BR \-b ", " \-\-boundary\-align =\fIalignment\fR +Specifies the alignment for the FAT and the start of the cluster heap. +The \fIalignment\fR argument is specified in bytes or may be specified with +\fBm\fR/\fBM\fR suffix for mebibytes or \fBk\fR/\fBK\fR suffix for kibibytes +and should be a power of two. +Some media like SD cards need this for optimal performance and endurance, +in which case \fIalignment\fR should be set to half of the card's native +boundary unit size. +If the card's native boundary unit size is not known, refer to the following +table of boundary unit sizes recommended by the SD Card Association. +.\" source: SD Specifications Part 2: File System Specification Version 3.00 +.TS +center; +cb1s6cbcb,nnnn. +Card Capacity Range Cluster Size Boundary Unit +_ + \[<=]8 MiB 8 KiB 8 KiB +>8 MiB \[<=]64 MiB 16 KiB 16 KiB +>64 MiB \[<=]256 MiB 16 KiB 32 KiB +>256 MiB \[<=]1 GiB 16 KiB 64 KiB +>1 GiB \[<=]2 GiB 32 KiB 64 KiB +>2 GiB \[<=]32 GiB 32 KiB 4 MiB +>32 GiB \[<=]128 GiB 128 KiB 16 MiB +>128 GiB \[<=]512 GiB 256 KiB 32 MiB +>512 GiB \[<=]2 TiB 512 KiB 64 MiB +.TE +.TP +.BR \-c ", " \-\-cluster\-size =\fIsize\fR +Specifies the cluster size of the exFAT file system. +The \fIsize\fR argument is specified in bytes or may be specified with +\fBm\fR/\fBM\fR suffix for mebibytes or \fBk\fR/\fBK\fR suffix for kibibytes +and must be a power of two. +.TP +.BR \-f ", " \-\-full\-format +Performs a full format. +This zeros the entire disk device while creating the exFAT filesystem. +.TP +.BR \-h ", " \-\-help +Prints the help and exit. +.TP +.BR \-L ", " \-\-volume\-label =\fIlabel\fR +Specifies the volume label to be associated with the exFAT filesystem. +.TP +.B \-\-pack\-bitmap +Attempts to relocate the exFAT allocation bitmap so that it ends at the +alignment boundary immediately following the FAT rather than beginning at that +boundary. +This strictly violates the SD card specification but may improve performance +and endurance on SD cards and other flash media not designed for use with exFAT +by allowing file-system metadata updates to touch fewer flash allocation units. +Furthermore, many SD cards and other flash devices specially optimize the +allocation unit where the FAT resides so as to support tiny writes with reduced +write amplification but expect only larger writes in subsequent allocation +units \[em] where the exFAT bitmap would be placed by default. +Specifying \fB\-\-pack\-bitmap\fR attempts to avoid the potential problems +associated with issuing many small writes to the bitmap by making it share an +allocation unit with the FAT. +If there is insufficient space for the bitmap there, then this option will have +no effect, and the bitmap will be aligned at the boundary as by default. +.TP +.BR \-v ", " \-\-verbose +Prints verbose debugging information while creating the exFAT filesystem. +.TP +.BR \-V ", " \-\-version +Prints the version number and exits. +.SH SEE ALSO +.BR mkfs (8), +.BR mount (8), diff --git a/manpages/tune.exfat.8 b/manpages/tune.exfat.8 new file mode 100644 index 0000000..865dc07 --- /dev/null +++ b/manpages/tune.exfat.8 @@ -0,0 +1,46 @@ +.TH tune.exfat 8 +.SH NAME +tune.exfat \- adjust tunable filesystem parameters on an exFAT filesystem +.SH SYNOPSIS +.B tune.exfat +[ +.B \-l +.I print-label +] [ +.B \-L +.I set-label +] [ +.B \-i +.I print-serial +] [ +.B \-I +.I set-serial +] [ +.B \-v +] +.I device +.br +.B tune.exfat \-V +.SH DESCRIPTION +.B tune.exfat +adjust tunable ondisk parameters of an existing exFAT filesystem. +.PP +.SH OPTIONS +.TP +.BI \-l " print-label" +Print the volume label of the exFAT filesystem. +.TP +.BI \-L " set-label" +Set the volume label of the filesystem to the provided argument. +.TP +.BI \-i " print-serial" +Print the volume serial of the exFAT filesystem. +.TP +.BI \-I " set-serial" +Set the volume serial of the filesystem to the provided argument. +.TP +.BI \-v +Prints verbose debugging information while extracting or tuning parameters of the exFAT filesystem. +.TP +.B \-V +Prints the version number and exits. diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am index 4d3c9d6..7b3e35b 100644 --- a/mkfs/Makefile.am +++ b/mkfs/Makefile.am @@ -1,48 +1,6 @@ -# -# Makefile.am (30.03.15) -# Automake source. -# -# 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. -# +AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +mkfs_exfat_LDADD = $(top_builddir)/lib/libexfat.a -sbin_PROGRAMS = mkexfatfs -dist_man8_MANS = mkexfatfs.8 -mkexfatfs_SOURCES = \ - cbm.c \ - cbm.h \ - fat.c \ - fat.h \ - main.c \ - mkexfat.c \ - mkexfat.h \ - rootdir.c \ - rootdir.h \ - uct.c \ - uct.h \ - uctc.c \ - uctc.h \ - vbr.c \ - vbr.h -mkexfatfs_CPPFLAGS = -I$(top_srcdir)/libexfat -mkexfatfs_LDADD = ../libexfat/libexfat.a +sbin_PROGRAMS = mkfs.exfat -install-exec-hook: - ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/mkfs.exfat - -uninstall-hook: - rm -f $(DESTDIR)$(sbindir)/mkfs.exfat +mkfs_exfat_SOURCES = mkfs.c upcase.c mkfs.h diff --git a/mkfs/cbm.c b/mkfs/cbm.c deleted file mode 100644 index ad5accc..0000000 --- a/mkfs/cbm.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - 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/mkfs/cbm.h b/mkfs/cbm.h deleted file mode 100644 index 3803b79..0000000 --- a/mkfs/cbm.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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/mkfs/fat.c b/mkfs/fat.c deleted file mode 100644 index c5174a7..0000000 --- a/mkfs/fat.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - 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/mkfs/fat.h b/mkfs/fat.h deleted file mode 100644 index fbecaa2..0000000 --- a/mkfs/fat.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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/mkfs/main.c b/mkfs/main.c deleted file mode 100644 index 0762024..0000000 --- a/mkfs/main.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - 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 -#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; - -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 < 256LL * 1024 * 1024) - return MAX(0, 12 - sector_bits); /* 4 KB */ - if (volume_size < 32LL * 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 exfat_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) -{ - size_t 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 main(int argc, char* argv[]) -{ - const char* spec = NULL; - int opt; - int spc_bits = -1; - const char* volume_label = NULL; - uint32_t volume_serial = 0; - uint64_t first_sector = 0; - struct exfat_dev* dev; - - printf("mkexfatfs %s\n", VERSION); - - 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; - } - } - if (argc - optind != 1) - usage(argv[0]); - spec = argv[optind]; - - dev = exfat_open(spec, EXFAT_MODE_RW); - if (dev == NULL) - return 1; - if (setup(dev, 9, spc_bits, volume_label, volume_serial, - first_sector) != 0) - { - exfat_close(dev); - return 1; - } - if (exfat_close(dev) != 0) - return 1; - printf("File system created successfully.\n"); - return 0; -} diff --git a/mkfs/mkexfat.c b/mkfs/mkexfat.c deleted file mode 100644 index eadbe96..0000000 --- a/mkfs/mkexfat.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - 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, - off_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 off_t block_size = 1024 * 1024; - void* block = malloc(block_size); - - if (block == NULL) - { - exfat_error("failed to allocate erase block"); - 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; - - fputs("Creating... ", stdout); - fflush(stdout); - if (erase(dev) != 0) - return 1; - if (create(dev) != 0) - return 1; - puts("done."); - - fputs("Flushing... ", stdout); - fflush(stdout); - 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"); -} diff --git a/mkfs/mkexfat.h b/mkfs/mkexfat.h deleted file mode 100644 index 963b3e5..0000000 --- a/mkfs/mkexfat.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - 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 - -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/mkfs/mkexfatfs.8 b/mkfs/mkexfatfs.8 deleted file mode 100644 index 6d3c653..0000000 --- a/mkfs/mkexfatfs.8 +++ /dev/null @@ -1,71 +0,0 @@ -.\" Copyright (C) 2011-2018 Andrew Nayenko -.\" -.TH MKEXFATFS 8 "January 2014" -.SH NAME -.B mkexfatfs -\- create an exFAT file system -.SH SYNOPSIS -.B mkexfatfs -[ -.B \-i -.I volume-id -] -[ -.B \-n -.I volume-name -] -[ -.B \-p -.I partition-first-sector -] -[ -.B \-s -.I sectors-per-cluster -] -[ -.B \-V -] -.I device - -.SH DESCRIPTION -.B mkexfatfs -creates an exFAT file system on a block device. -.I device -is a special file corresponding to the partition on the device. Note that if -this is an MBR partition then the file system type should be set to 0x07 -(NTFS/exFAT) otherwise other operating systems may refuse to mount the -file system. - -.SH OPTIONS -Command line options available: -.TP -.BI \-i " volume-id" -A 32-bit hexadecimal number. By default a value based on current time is set. -.TP -.BI \-n " volume-name" -Volume name (label), up to 15 characters. By default no label is set. -.TP -.BI \-p " partition-first-sector" -First sector of the partition starting from the beginning of the whole disk. -exFAT super block has a field for this value but in fact it's optional and -does not affect anything. Default is 0. -.TP -.BI \-s " sectors-per-cluster" -Number of physical sectors per cluster (cluster is an allocation unit in -exFAT). Must be a power of 2, i.e. 1, 2, 4, 8, etc. Cluster size can not -exceed 32 MB. Default cluster sizes are: -4 KB if volume size is less than 256 MB, -32 KB if volume size is from 256 MB to 32 GB, -128 KB if volume size is 32 GB or larger. -.TP -.BI \-V -Print version and copyright. - -.SH EXIT CODES -Zero is returned on successful creation. Any other code means an error. - -.SH AUTHOR -Andrew Nayenko - -.SH SEE ALSO -.BR mkfs (8), fdisk (8) diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c new file mode 100644 index 0000000..b5b4957 --- /dev/null +++ b/mkfs/mkfs.c @@ -0,0 +1,712 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Namjae Jeon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" +#include "mkfs.h" + +struct exfat_mkfs_info finfo; + +/* random serial generator based on current time */ +static unsigned int get_new_serial(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts)) { + /* set 0000-0000 on error */ + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + + return (unsigned int)(ts.tv_nsec << 12 | ts.tv_sec); +} + +static void exfat_setup_boot_sector(struct pbr *ppbr, + struct exfat_blk_dev *bd, struct exfat_user_input *ui) +{ + struct bpb64 *pbpb = &ppbr->bpb; + struct bsx64 *pbsx = &ppbr->bsx; + unsigned int i; + + /* Fill exfat BIOS parameter block */ + pbpb->jmp_boot[0] = 0xeb; + pbpb->jmp_boot[1] = 0x76; + pbpb->jmp_boot[2] = 0x90; + memcpy(pbpb->oem_name, "EXFAT ", 8); + memset(pbpb->res_zero, 0, 53); + + /* Fill exfat extend BIOS parameter block */ + pbsx->vol_offset = cpu_to_le64(bd->offset / bd->sector_size); + pbsx->vol_length = cpu_to_le64(bd->size / bd->sector_size); + pbsx->fat_offset = cpu_to_le32(finfo.fat_byte_off / bd->sector_size); + pbsx->fat_length = cpu_to_le32(finfo.fat_byte_len / bd->sector_size); + pbsx->clu_offset = cpu_to_le32(finfo.clu_byte_off / bd->sector_size); + pbsx->clu_count = cpu_to_le32(finfo.total_clu_cnt); + pbsx->root_cluster = cpu_to_le32(finfo.root_start_clu); + pbsx->vol_serial = cpu_to_le32(finfo.volume_serial); + pbsx->vol_flags = 0; + pbsx->sect_size_bits = bd->sector_size_bits; + pbsx->sect_per_clus_bits = 0; + /* Compute base 2 logarithm of ui->cluster_size / bd->sector_size */ + for (i = ui->cluster_size / bd->sector_size; i > 1; i /= 2) + pbsx->sect_per_clus_bits++; + pbsx->num_fats = 1; + /* fs_version[0] : minor and fs_version[1] : major */ + pbsx->fs_version[0] = 0; + pbsx->fs_version[1] = 1; + pbsx->phy_drv_no = 0x80; + memset(pbsx->reserved2, 0, 7); + + memset(ppbr->boot_code, 0, 390); + ppbr->signature = cpu_to_le16(PBR_SIGNATURE); + + exfat_debug("Volume Offset(sectors) : %" PRIu64 "\n", + le64_to_cpu(pbsx->vol_offset)); + exfat_debug("Volume Length(sectors) : %" PRIu64 "\n", + le64_to_cpu(pbsx->vol_length)); + exfat_debug("FAT Offset(sector offset) : %u\n", + le32_to_cpu(pbsx->fat_offset)); + exfat_debug("FAT Length(sectors) : %u\n", + le32_to_cpu(pbsx->fat_length)); + exfat_debug("Cluster Heap Offset (sector offset) : %u\n", + le32_to_cpu(pbsx->clu_offset)); + exfat_debug("Cluster Count : %u\n", + le32_to_cpu(pbsx->clu_count)); + exfat_debug("Root Cluster (cluster offset) : %u\n", + le32_to_cpu(pbsx->root_cluster)); + exfat_debug("Volume Serial : 0x%x\n", le32_to_cpu(pbsx->vol_serial)); + exfat_debug("Sector Size Bits : %u\n", + pbsx->sect_size_bits); + exfat_debug("Sector per Cluster bits : %u\n", + pbsx->sect_per_clus_bits); +} + +static int exfat_write_boot_sector(struct exfat_blk_dev *bd, + struct exfat_user_input *ui, unsigned int *checksum, + bool is_backup) +{ + struct pbr *ppbr; + unsigned int sec_idx = BOOT_SEC_IDX; + int ret = 0; + + if (is_backup) + sec_idx += BACKUP_BOOT_SEC_IDX; + + ppbr = malloc(bd->sector_size); + if (!ppbr) { + exfat_err("Cannot allocate pbr: out of memory\n"); + return -1; + } + memset(ppbr, 0, bd->sector_size); + + exfat_setup_boot_sector(ppbr, bd, ui); + + /* write main boot sector */ + ret = exfat_write_sector(bd, ppbr, sec_idx); + if (ret < 0) { + exfat_err("main boot sector write failed\n"); + ret = -1; + goto free_ppbr; + } + + boot_calc_checksum((unsigned char *)ppbr, bd->sector_size, + true, checksum); + +free_ppbr: + free(ppbr); + return ret; +} + +static int exfat_write_extended_boot_sectors(struct exfat_blk_dev *bd, + unsigned int *checksum, bool is_backup) +{ + char *peb; + __le16 *peb_signature; + int ret = 0; + int i; + unsigned int sec_idx = EXBOOT_SEC_IDX; + + peb = malloc(bd->sector_size); + if (!peb) + return -1; + + if (is_backup) + sec_idx += BACKUP_BOOT_SEC_IDX; + + memset(peb, 0, bd->sector_size); + peb_signature = (__le16*) (peb + bd->sector_size - 2); + *peb_signature = cpu_to_le16(PBR_SIGNATURE); + for (i = 0; i < EXBOOT_SEC_NUM; i++) { + if (exfat_write_sector(bd, peb, sec_idx++)) { + exfat_err("extended boot sector write failed\n"); + ret = -1; + goto free_peb; + } + + boot_calc_checksum((unsigned char *) peb, bd->sector_size, + false, checksum); + } + +free_peb: + free(peb); + return ret; +} + +static int exfat_write_oem_sector(struct exfat_blk_dev *bd, + unsigned int *checksum, bool is_backup) +{ + char *oem; + int ret = 0; + unsigned int sec_idx = OEM_SEC_IDX; + + oem = malloc(bd->sector_size); + if (!oem) + return -1; + + if (is_backup) + sec_idx += BACKUP_BOOT_SEC_IDX; + + memset(oem, 0xFF, bd->sector_size); + ret = exfat_write_sector(bd, oem, sec_idx); + if (ret) { + exfat_err("oem sector write failed\n"); + ret = -1; + goto free_oem; + } + + boot_calc_checksum((unsigned char *)oem, bd->sector_size, false, + checksum); + + /* Zero out reserved sector */ + memset(oem, 0, bd->sector_size); + ret = exfat_write_sector(bd, oem, sec_idx + 1); + if (ret) { + exfat_err("reserved sector write failed\n"); + ret = -1; + goto free_oem; + } + + boot_calc_checksum((unsigned char *)oem, bd->sector_size, false, + checksum); + +free_oem: + free(oem); + return ret; +} + +static int exfat_create_volume_boot_record(struct exfat_blk_dev *bd, + struct exfat_user_input *ui, bool is_backup) +{ + unsigned int checksum = 0; + int ret; + + ret = exfat_write_boot_sector(bd, ui, &checksum, is_backup); + if (ret) + return ret; + ret = exfat_write_extended_boot_sectors(bd, &checksum, is_backup); + if (ret) + return ret; + ret = exfat_write_oem_sector(bd, &checksum, is_backup); + if (ret) + return ret; + + return exfat_write_checksum_sector(bd, checksum, is_backup); +} + +static int write_fat_entry(int fd, __le32 clu, + unsigned long long offset) +{ + int nbyte; + off_t fat_entry_offset = finfo.fat_byte_off + (offset * sizeof(__le32)); + + nbyte = pwrite(fd, (__u8 *) &clu, sizeof(__le32), fat_entry_offset); + if (nbyte != sizeof(int)) { + exfat_err("write failed, offset : %llu, clu : %x\n", + offset, clu); + return -1; + } + + return 0; +} + +static int write_fat_entries(struct exfat_user_input *ui, int fd, + unsigned int clu, unsigned int length) +{ + int ret; + unsigned int count; + + count = clu + round_up(length, ui->cluster_size) / ui->cluster_size; + + for (; clu < count - 1; clu++) { + ret = write_fat_entry(fd, cpu_to_le32(clu + 1), clu); + if (ret) + return ret; + } + + ret = write_fat_entry(fd, cpu_to_le32(EXFAT_EOF_CLUSTER), clu); + if (ret) + return ret; + + return clu; +} + +static int exfat_create_fat_table(struct exfat_blk_dev *bd, + struct exfat_user_input *ui) +{ + int ret, clu; + + /* fat entry 0 should be media type field(0xF8) */ + ret = write_fat_entry(bd->dev_fd, cpu_to_le32(0xfffffff8), 0); + if (ret) { + exfat_err("fat 0 entry write failed\n"); + return ret; + } + + /* fat entry 1 is historical precedence(0xFFFFFFFF) */ + ret = write_fat_entry(bd->dev_fd, cpu_to_le32(0xffffffff), 1); + if (ret) { + exfat_err("fat 1 entry write failed\n"); + return ret; + } + + /* write bitmap entries */ + clu = write_fat_entries(ui, bd->dev_fd, EXFAT_FIRST_CLUSTER, + finfo.bitmap_byte_len); + if (clu < 0) + return ret; + + /* write upcase table entries */ + clu = write_fat_entries(ui, bd->dev_fd, clu + 1, finfo.ut_byte_len); + if (clu < 0) + return ret; + + /* write root directory entries */ + clu = write_fat_entries(ui, bd->dev_fd, clu + 1, finfo.root_byte_len); + if (clu < 0) + return ret; + + finfo.used_clu_cnt = clu + 1; + exfat_debug("Total used cluster count : %d\n", finfo.used_clu_cnt); + + return ret; +} + +static int exfat_create_bitmap(struct exfat_blk_dev *bd) +{ + char *bitmap; + unsigned int i, nbytes; + + bitmap = calloc(finfo.bitmap_byte_len, sizeof(*bitmap)); + if (!bitmap) + return -1; + + for (i = 0; i < finfo.used_clu_cnt - EXFAT_FIRST_CLUSTER; i++) + exfat_set_bit(bd, bitmap, i); + + nbytes = pwrite(bd->dev_fd, bitmap, finfo.bitmap_byte_len, finfo.bitmap_byte_off); + if (nbytes != finfo.bitmap_byte_len) { + exfat_err("write failed, nbytes : %d, bitmap_len : %d\n", + nbytes, finfo.bitmap_byte_len); + free(bitmap); + return -1; + } + + free(bitmap); + return 0; +} + +static int exfat_create_root_dir(struct exfat_blk_dev *bd, + struct exfat_user_input *ui) +{ + struct exfat_dentry ed[3]; + int dentries_len = sizeof(struct exfat_dentry) * 3; + int nbytes; + + /* Set volume label entry */ + ed[0].type = EXFAT_VOLUME; + memset(ed[0].vol_label, 0, 22); + memcpy(ed[0].vol_label, ui->volume_label, ui->volume_label_len); + ed[0].vol_char_cnt = ui->volume_label_len/2; + + /* Set bitmap entry */ + ed[1].type = EXFAT_BITMAP; + ed[1].bitmap_flags = 0; + ed[1].bitmap_start_clu = cpu_to_le32(EXFAT_FIRST_CLUSTER); + ed[1].bitmap_size = cpu_to_le64(finfo.bitmap_byte_len); + + /* Set upcase table entry */ + ed[2].type = EXFAT_UPCASE; + ed[2].upcase_checksum = cpu_to_le32(0xe619d30d); + ed[2].upcase_start_clu = cpu_to_le32(finfo.ut_start_clu); + ed[2].upcase_size = cpu_to_le64(EXFAT_UPCASE_TABLE_SIZE); + + nbytes = pwrite(bd->dev_fd, ed, dentries_len, finfo.root_byte_off); + if (nbytes != dentries_len) { + exfat_err("write failed, nbytes : %d, dentries_len : %d\n", + nbytes, dentries_len); + return -1; + } + + return 0; +} + +static void usage(void) +{ + fputs("Usage: mkfs.exfat\n" + "\t-L | --volume-label=label Set volume label\n" + "\t-c | --cluster-size=size(or suffixed by 'K' or 'M') Specify cluster size\n" + "\t-b | --boundary-align=size(or suffixed by 'K' or 'M') Specify boundary alignment\n" + "\t --pack-bitmap Move bitmap into FAT segment\n" + "\t-f | --full-format Full format\n" + "\t-V | --version Show version\n" + "\t-v | --verbose Print debug\n" + "\t-h | --help Show help\n", + stderr); + + exit(EXIT_FAILURE); +} + +#define PACK_BITMAP (CHAR_MAX + 1) + +static const struct option opts[] = { + {"volume-label", required_argument, NULL, 'L' }, + {"cluster-size", required_argument, NULL, 'c' }, + {"boundary-align", required_argument, NULL, 'b' }, + {"pack-bitmap", no_argument, NULL, PACK_BITMAP }, + {"full-format", no_argument, NULL, 'f' }, + {"version", no_argument, NULL, 'V' }, + {"verbose", no_argument, NULL, 'v' }, + {"help", no_argument, NULL, 'h' }, + {"?", no_argument, NULL, '?' }, + {NULL, 0, NULL, 0 } +}; + +/* + * Moves the bitmap to just before the alignment boundary if there is space + * between the boundary and the end of the FAT. This may allow the FAT and the + * bitmap to share the same allocation unit on flash media, thereby improving + * performance and endurance. + */ +static int exfat_pack_bitmap(const struct exfat_user_input *ui) +{ + unsigned int fat_byte_end = finfo.fat_byte_off + finfo.fat_byte_len, + bitmap_byte_len = finfo.bitmap_byte_len, + bitmap_clu_len = round_up(bitmap_byte_len, ui->cluster_size), + bitmap_clu_cnt, total_clu_cnt, new_bitmap_clu_len; + + for (;;) { + bitmap_clu_cnt = bitmap_clu_len / ui->cluster_size; + if (finfo.clu_byte_off - bitmap_clu_len < fat_byte_end || + finfo.total_clu_cnt > EXFAT_MAX_NUM_CLUSTER - + bitmap_clu_cnt) + return -1; + total_clu_cnt = finfo.total_clu_cnt + bitmap_clu_cnt; + bitmap_byte_len = round_up(total_clu_cnt, 8) / 8; + new_bitmap_clu_len = round_up(bitmap_byte_len, ui->cluster_size); + if (new_bitmap_clu_len == bitmap_clu_len) { + finfo.clu_byte_off -= bitmap_clu_len; + finfo.total_clu_cnt = total_clu_cnt; + finfo.bitmap_byte_off -= bitmap_clu_len; + finfo.bitmap_byte_len = bitmap_byte_len; + return 0; + } + bitmap_clu_len = new_bitmap_clu_len; + } +} + +static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, + struct exfat_user_input *ui) +{ + unsigned long long total_clu_cnt; + int clu_len; + + if (ui->cluster_size < bd->sector_size) { + exfat_err("cluster size (%u bytes) is smaller than sector size (%u bytes)\n", + ui->cluster_size, bd->sector_size); + return -1; + } + if (ui->boundary_align < bd->sector_size) { + exfat_err("boundary alignment is too small (min %d)\n", + bd->sector_size); + return -1; + } + finfo.fat_byte_off = round_up(bd->offset + 24 * bd->sector_size, + ui->boundary_align) - bd->offset; + /* Prevent integer overflow when computing the FAT length */ + if (bd->num_clusters > UINT32_MAX / 4) { + exfat_err("cluster size (%u bytes) is too small\n", ui->cluster_size); + return -1; + } + finfo.fat_byte_len = round_up((bd->num_clusters * 4), ui->cluster_size); + finfo.clu_byte_off = round_up(bd->offset + finfo.fat_byte_off + + finfo.fat_byte_len, ui->boundary_align) - bd->offset; + if (bd->size <= finfo.clu_byte_off) { + exfat_err("boundary alignment is too big\n"); + return -1; + } + total_clu_cnt = (bd->size - finfo.clu_byte_off) / ui->cluster_size; + if (total_clu_cnt > EXFAT_MAX_NUM_CLUSTER) { + exfat_err("cluster size is too small\n"); + return -1; + } + finfo.total_clu_cnt = (unsigned int) total_clu_cnt; + + finfo.bitmap_byte_off = finfo.clu_byte_off; + finfo.bitmap_byte_len = round_up(finfo.total_clu_cnt, 8) / 8; + if (ui->pack_bitmap) + exfat_pack_bitmap(ui); + clu_len = round_up(finfo.bitmap_byte_len, ui->cluster_size); + + finfo.ut_start_clu = EXFAT_FIRST_CLUSTER + clu_len / ui->cluster_size; + finfo.ut_byte_off = finfo.bitmap_byte_off + clu_len; + finfo.ut_byte_len = EXFAT_UPCASE_TABLE_SIZE; + clu_len = round_up(finfo.ut_byte_len, ui->cluster_size); + + finfo.root_start_clu = finfo.ut_start_clu + clu_len / ui->cluster_size; + finfo.root_byte_off = finfo.ut_byte_off + clu_len; + finfo.root_byte_len = sizeof(struct exfat_dentry) * 3; + finfo.volume_serial = get_new_serial(); + + return 0; +} + +static int exfat_zero_out_disk(struct exfat_blk_dev *bd, + struct exfat_user_input *ui) +{ + int nbytes; + unsigned long long total_written = 0; + char *buf; + unsigned int chunk_size = ui->cluster_size; + unsigned long long size; + + if (ui->quick) + size = finfo.root_byte_off + chunk_size; + else + size = bd->size; + + buf = malloc(chunk_size); + if (!buf) + return -1; + + memset(buf, 0, chunk_size); + lseek(bd->dev_fd, 0, SEEK_SET); + do { + + nbytes = write(bd->dev_fd, buf, chunk_size); + if (nbytes <= 0) { + if (nbytes < 0) + exfat_err("write failed(errno : %d)\n", errno); + break; + } + total_written += nbytes; + } while (total_written < size); + + free(buf); + exfat_debug("zero out written size : %llu, disk size : %llu\n", + total_written, bd->size); + return 0; +} + +static int make_exfat(struct exfat_blk_dev *bd, struct exfat_user_input *ui) +{ + int ret; + + exfat_info("Creating exFAT filesystem(%s, cluster size=%u)\n\n", + ui->dev_name, ui->cluster_size); + + exfat_info("Writing volume boot record: "); + ret = exfat_create_volume_boot_record(bd, ui, 0); + exfat_info("%s\n", ret ? "failed" : "done"); + if (ret) + return ret; + + exfat_info("Writing backup volume boot record: "); + /* backup sector */ + ret = exfat_create_volume_boot_record(bd, ui, 1); + exfat_info("%s\n", ret ? "failed" : "done"); + if (ret) + return ret; + + exfat_info("Fat table creation: "); + ret = exfat_create_fat_table(bd, ui); + exfat_info("%s\n", ret ? "failed" : "done"); + if (ret) + return ret; + + exfat_info("Allocation bitmap creation: "); + ret = exfat_create_bitmap(bd); + exfat_info("%s\n", ret ? "failed" : "done"); + if (ret) + return ret; + + exfat_info("Upcase table creation: "); + ret = exfat_create_upcase_table(bd); + exfat_info("%s\n", ret ? "failed" : "done"); + if (ret) + return ret; + + exfat_info("Writing root directory entry: "); + ret = exfat_create_root_dir(bd, ui); + exfat_info("%s\n", ret ? "failed" : "done"); + if (ret) + return ret; + + return 0; +} + +static long long parse_size(const char *size) +{ + char *data_unit; + unsigned long long byte_size = strtoull(size, &data_unit, 0); + + switch (*data_unit) { + case 'M': + case 'm': + byte_size <<= 20; + break; + case 'K': + case 'k': + byte_size <<= 10; + break; + case '\0': + break; + default: + exfat_err("Wrong unit input('%c') for size\n", + *data_unit); + return -EINVAL; + } + + return byte_size; +} + +int main(int argc, char *argv[]) +{ + int c; + int ret = EXIT_FAILURE; + struct exfat_blk_dev bd; + struct exfat_user_input ui; + bool version_only = false; + + init_user_input(&ui); + + if (!setlocale(LC_CTYPE, "")) + exfat_err("failed to init locale/codeset\n"); + + opterr = 0; + while ((c = getopt_long(argc, argv, "n:L:c:b:fVvh", opts, NULL)) != EOF) + switch (c) { + /* + * Make 'n' option fallthrough to 'L' option for for backward + * compatibility with old utils. + */ + case 'n': + case 'L': + { + ret = exfat_utf16_enc(optarg, + ui.volume_label, sizeof(ui.volume_label)); + if (ret < 0) + goto out; + + ui.volume_label_len = ret; + break; + } + case 'c': + ret = parse_size(optarg); + if (ret < 0) + goto out; + else if (ret & (ret - 1)) { + exfat_err("cluster size(%d) is not a power of 2)\n", + ret); + goto out; + } else if (ret > EXFAT_MAX_CLUSTER_SIZE) { + exfat_err("cluster size(%d) exceeds max cluster size(%d)\n", + ui.cluster_size, EXFAT_MAX_CLUSTER_SIZE); + goto out; + } + ui.cluster_size = ret; + break; + case 'b': + ret = parse_size(optarg); + if (ret < 0) + goto out; + else if (ret & (ret - 1)) { + exfat_err("boundary align(%d) is not a power of 2)\n", + ret); + goto out; + } + ui.boundary_align = ret; + break; + case PACK_BITMAP: + ui.pack_bitmap = true; + break; + case 'f': + ui.quick = false; + break; + case 'V': + version_only = true; + break; + case 'v': + print_level = EXFAT_DEBUG; + break; + case '?': + case 'h': + default: + usage(); + } + + show_version(); + if (version_only) + exit(EXIT_FAILURE); + + if (argc - optind != 1) { + usage(); + } + + memset(ui.dev_name, 0, sizeof(ui.dev_name)); + snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[optind]); + + ret = exfat_get_blk_dev_info(&ui, &bd); + if (ret < 0) + goto out; + + ret = exfat_build_mkfs_info(&bd, &ui); + if (ret) + goto close; + + ret = exfat_zero_out_disk(&bd, &ui); + if (ret) + goto close; + + ret = make_exfat(&bd, &ui); + if (ret) + goto close; + + exfat_info("Synchronizing...\n"); + ret = fsync(bd.dev_fd); +close: + close(bd.dev_fd); +out: + if (!ret) + exfat_info("\nexFAT format complete!\n"); + else + exfat_info("\nexFAT format fail!\n"); + return ret; +} diff --git a/mkfs/mkfs.h b/mkfs/mkfs.h new file mode 100644 index 0000000..ffd56e3 --- /dev/null +++ b/mkfs/mkfs.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Namjae Jeon + */ + +#ifndef _MKFS_H + +#define MIN_NUM_SECTOR (2048) +#define EXFAT_MAX_CLUSTER_SIZE (32*1024*1024) + +struct exfat_mkfs_info { + unsigned int total_clu_cnt; + unsigned int used_clu_cnt; + unsigned int fat_byte_off; + unsigned int fat_byte_len; + unsigned int clu_byte_off; + unsigned int bitmap_byte_off; + unsigned int bitmap_byte_len; + unsigned int ut_byte_off; + unsigned int ut_start_clu; + unsigned int ut_clus_off; + unsigned int ut_byte_len; + unsigned int root_byte_off; + unsigned int root_byte_len; + unsigned int root_start_clu; + unsigned int volume_serial; +}; + +extern struct exfat_mkfs_info finfo; + +int exfat_create_upcase_table(struct exfat_blk_dev *bd); + +#endif /* !_MKFS_H */ diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c deleted file mode 100644 index 4f9b0e8..0000000 --- a/mkfs/rootdir.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - 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 (exfat_utf16_length(get_volume_label()) == 0) - return; - - memcpy(label_entry->name, get_volume_label(), - EXFAT_ENAME_MAX * sizeof(le16_t)); - label_entry->length = exfat_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/mkfs/rootdir.h b/mkfs/rootdir.h deleted file mode 100644 index 87f437f..0000000 --- a/mkfs/rootdir.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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/mkfs/uct.c b/mkfs/uct.c deleted file mode 100644 index 98cdce8..0000000 --- a/mkfs/uct.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - 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/mkfs/uct.h b/mkfs/uct.h deleted file mode 100644 index ce619ec..0000000 --- a/mkfs/uct.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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/mkfs/uctc.c b/mkfs/uctc.c deleted file mode 100644 index 518219c..0000000 --- a/mkfs/uctc.c +++ /dev/null @@ -1,757 +0,0 @@ -/* - 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/mkfs/uctc.h b/mkfs/uctc.h deleted file mode 100644 index bd5ebb2..0000000 --- a/mkfs/uctc.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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/mkfs/upcase.c b/mkfs/upcase.c new file mode 100644 index 0000000..f86fa23 --- /dev/null +++ b/mkfs/upcase.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Namjae Jeon + */ + +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" +#include "mkfs.h" + +static const unsigned char upcase_table[EXFAT_UPCASE_TABLE_SIZE] = { + 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 +}; + +int exfat_create_upcase_table(struct exfat_blk_dev *bd) +{ + int nbytes; + + nbytes = pwrite(bd->dev_fd, upcase_table, EXFAT_UPCASE_TABLE_SIZE, finfo.ut_byte_off); + if (nbytes != EXFAT_UPCASE_TABLE_SIZE) + return -1; + + return 0; +} diff --git a/mkfs/vbr.c b/mkfs/vbr.c deleted file mode 100644 index e40081f..0000000 --- a/mkfs/vbr.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - 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/mkfs/vbr.h b/mkfs/vbr.h deleted file mode 100644 index c1f59a6..0000000 --- a/mkfs/vbr.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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/tests/2tb_disk/exfat.img.tar.xz b/tests/2tb_disk/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..f979bdebd0996a3558aa0fe609b6e667211f236f GIT binary patch literal 13368 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 literal 0 HcmV?d00001 diff --git a/tests/de_bad_csum/exfat.img.tar.xz b/tests/de_bad_csum/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..f753e82ba92b5fcdc694023b9027fc517e328bef GIT binary patch literal 3196 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 literal 0 HcmV?d00001 diff --git a/tests/file_invalid_clus/exfat.img.expected.xz b/tests/file_invalid_clus/exfat.img.expected.xz new file mode 100644 index 0000000000000000000000000000000000000000..08e992efdb1b8243f58c32e2e969afbd142502cb GIT binary patch literal 4048 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 literal 0 HcmV?d00001 diff --git a/tests/file_invalid_clus/exfat.img.tar.xz b/tests/file_invalid_clus/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..830edf2be206f0adf3d72d70ba8261d7ff8c43f7 GIT binary patch literal 3392 zcmV-G4ZrgJH+ooF000E$*0e?f03iVu0001VFXf})@Ba+_T>vtk2=9huXrK&lK<@Vg zSOg-0M*kK-_aA@!}~a|mqutDfAsX^V|K@-)O3oGgL4cl-hv zpbF~NC%>DX@B}CIrMpth-C&^2?qu2j1>3fUexZA%0yZ_N+No^%CT@rSCUBeBqz%Eu zPkbRK2f?>fxz?5}0ir$+l@oOAM3}Gh`Y!NUKEzcp{Z*?PL1z~HCaP-cuJoPLVGkkK z#hfYvLg2H}!l7}G-C;S$^E&a>3^WmBa0cUWzW7E%+4%8=)6oiTXRM}0m>G;sPG3rI1WgL&0JCP5XTItyWLE^ zGS3fW=~+wS>;rIUbY4FY(zGszJ4tKh%HV~YhwJ5Cnh-XcOALL4a!S3b!gL?v?VKC8 zrBzXFIVnYWqnWBG3T1mNoy|+)T^7mgkB|)+{RWIVXtgUGkkxLb zl~$qUdEQEx)z2Tuq=Kp&qBz(0M)+O_h%DL_F4a*1qC1zg_9OtOY$NFx_Ua1q81}j= zSM!n!8VmcZ-lw*(A_IzE3|+WmOkHDl})z$00RTGz~XR?YybkEMkS%KXn(l8ONP#X zoTD5pJ=rgpxj{L*IbY7wBdHi}M0a|Ye9@bH5=A4y72>Z#cC01ge2 zrr=o)wvfv}{l_cZD41f0Iy6nlx}nXfy3}Y6cGpOoOLJ_{Td3WFQ z3}ui_H;^7NE+MP6gud*f23eh6-+tRzW7tvD77TSsP&O=;g?8{5XV*t1(cBDP!h`&O{>+wZv+U_@BG4R|^V3pmGccBqwQhdAwBt@3InT1&y|g zeEt``)@DljgM=fUHiUQ&XI+%iXDYe+VnhVtK`s<*I4i7r#o|=ZNyjvExkbyC526- z@!}AN@qu%4ulc3N5vbS)+5`(vNM z66Wj4L72nw>RI!q)0m5Ef^}z%UA^GeyaMNz`YX3frx##4=(aY);+tK4KS4z9ar7bG z14rywze_e>?D!5EM;wf@;!Dj6LKXqi7gS<+J+NPcZX}75GqKi<~De-5b0RJ z4v;;hBH7cP>)8#^qfv@~dI(%UqNbC2XC)U5MQcon45wJHrdSpYpo3=hp72zBxz!Zn z)NDU5fN#=+gm(op_pNs-j3;qx&!o6=M+E*D;C*gOEB%67bo*aU7Oe~Vn_7B^h# zDco4S-FEy34=2u*s7u}Sc>2Elr9^1fR;4I8uqbKqHhX9woj>y{{)RsP^93x8Umr&# zVLX-*x^jaI{)H-+XjI>2W{*_!g3U2CVNV7X`o$4~_V$x!2#cP0R2EK~mvwR+twaSs z0P^?`y>YlISD@jXvh^2?5+*a9TKz;>Fy1vql^b0NIYwg`Q{e_B0_5g|FL7&1b0e@= zxx2ko_O8_FY*+H4cw52T`^n<(w21@#h5wF{lxZ`EUwFDtW{zL3D21{p#32NW6^U|tDCWY9ZMP>@t z4SBaOGjIRcIUE7BG0&hUe$;EBVCmp;*xudTrIp||C>m83IQLWf2i);&`C1@Us*dKY zn(4%V`a>5k3jW9XV}L*O1mjF*?amaJ+-6=y+QyKj8#9W; z&`9Kf!I8Y3RP`^gvPVFY7eV8$rZ_T31{o8X4We>s+305-Ck&w)N{plipWG06> z%w?I}`GmbU28qxL=%QiC=w6ci(NdHk$)}Gd5=&uL-Vt+9*r|bP?^1>UYZfsc#mvmqJuwd*F*AR6-|7GM z#6+BWu7`?M>+JRAKDjgBxV*J=KtLe&7wROyKuAHcKtMoTnfCHOJ_PYv^g-ORVjxr& z>8i%Rn$X0WvW@R&I$Msp*0MGPthaYhl>BnDV4{P8cpzKL*^9+7Plo5zo!uxFwQ(S(CQoGZ~!ksXx z@zc|}juYvUh&-*S3Ov%7!tw=<(%Np+^Ii6}Ga8m6f0i{Di9<*FKuLUb^3;BWAOA?A zmOw6f^<95SXJ*vsiVeF73Fn z{K#BItK-PMTzhv<7ZT=aa4>Ga?Q8#Ihl%yUxW87k+eHm?yg{>c&7 z9u5$S#z#B(IcSqyzr+<#^>8l3&VxKWKRr{fs4#y7&JY^qH- z86@NSvZB#9&EL$=bVLh35O-0f&9NONy(1!xAN4L^MO1FhNDe*5mC2v!BBWAy*6wxE zktv!}fEM*--35 z$0dPf6GWn}AyYou#4-01jDksAq=qpS*DvmG=a%9}ULaEpJOUxmv9U7P3ejjYO) zf*R|3)TRb?tP$0@T*PTXo^US%QFkAKa?{`jJZLq}<$>fE6c8T5S(+r>QsBCF;jDa| zKRJ{R;W_O~E>)(y-mm1M&fsSsFUhP&ctjfxhbZtcpPWaTd>g&{(eZz#7WWHe88T|% z&FkY%eaDj7p_ausN}r@;dI*{+JU=jI0R2=Vp=0=yXx$@rgmQk+wGi3)E!v{{zPA?f zx;RQ*5hs=Sl)q{f5ns0?5d`_PG?+~eeb9^U;yBcbF?fRP&l=dGH*)+^Sh6(b*2vS{ z?Laf5%Gj$c<)P9H|7yu znngyu{o4kRws*={-aKQXO=W}?I*V^%s4vStX2?t``7lHYY1F#JE?xAdne3%EQ%E{i zIR(1rY>pIQ%ZOU<(S+U~)eU%p69|Op*aw!>BO}Z)^ptIi?pSA@HuYbfH%Ko88%WsM z-#aNKlTA+#Zd^4}sJw#9c`e3W`J7)3?&%p`WgPCKmTwL}6_Kz_Om4#O`E~IHSZH1_ zG!}TiK7^oL*lGZlPEF-pu9vGwTPN zpkIRGwsW_5hl#e zmX}8C{BmvkgWCT`rjr(rIf!Qy z->E~+USnN{HVTaJe`uh(K2uAm)k+tFOu~u<%Hc?5l4bD}*LwG)s^ox%qQhR@=mjo) z4cuyAQmkI&VkNt1t2qmz%6HvPqnY6kiWMxXcyRNEmAFD7m_1(9twXHY<$j?ecoBVs zEDK5S-*+L@uVbH>G??p0F%jpoqSjFqON98N&s41)TE{4^pu*dVjnZz7P`$nyA4*ZA z=R3EQX=e!HX!7DqkWWwNjHSup`{3n#{N(n~CE)whqKl;c4P@Es$5QBV7GoI0PY|iN zfY6l{B<=jMT|wo%qUCg=OQOjvIfTeM zc1Z+9OL77!B)hT-JlUH{Jh1!kXW8V0O;5ewc0bwK=c|g)6Oj5-_fGeKnL!goB=7jL zF%9+ccx@>`CLT_Y7oSUWpr8*T+sbI5tLaDDJ1S&p*T?2oFba0LqPh?i4O4o}$ziK| zmmlm&+bn>hnRXF_K3yaRVc;*Sxlh_ILDw{A;Tr`T%OMaz3m-3!b47Mok0RVC_500J zM37;q&kfF{PV~9#t>r_SD+fM1(~oYb$i6WLiMoy5hkJmFFkRS&2=jPF^1vcU;^9!i zAXu4&s4vi@@J(4oqp*I+67{8$WE;D#WZHmse-9pHUoPH4?8DYL_xoPCJDS{B* zqz25f?GdY>ZLC?-Z6y$3n?JF~fAXYP5&2yx7J0UsoB#T~kP;pWPK^ppljrvk&$$ph zD8`C?Yq4k@MpR@*V6HkeVHipm$!I^1U-m!MqO^MW=A1t{PpZ7X3>>BZkUo(2lnMl0QeFt~r@?|Ip+6(;Z zb{;$&T##lcgMib#VlO;{UpLlrh1z*{y9w7Ua2W+_nwnm8$XyexSms|O7RA<>#C%>y zrXDcTs0345VWiA`F&pMgua5_n4-om@(sDaYwWK0&DF|OnyD6QtP>?FGAP8uMn%G zkUc6&aW3lMhwC`UHA#9>+K8cT-s7k8cENx=6;E*4L_g^@m4aNmc7gi}JCe_Kb`zJUPtr>k+;k53HXtPA(HzvliLKDKnV2S@U_$ILvhpBAm-6La`cA3 z@VVl4hjLb4@86cIRg1gjzj2mtle0>;E#ncLB}Ie9$d~1Gy_yra)k91K)!To~h`@#5 zF_82^uG+edoTV>!eDSB`=Qi~td$=>0bq1yR`jZQH3F@Z|9G+R+U605iOnq{m01lOx zu*H}>g4(6e&EO)g%W`VL<(~RVjC%_{dtox3)mFgrDg9O8GtBgD9C8n;B4gr1n1cX% zm4I2W<>ClDwIQ4Kwlw%*0X1S~JRFQX<((Ck?4-M1FVY{7c@NXn8p8BWWPjoeDfSuk znA!pe<(&D*p8PM&K?KVXgmfIXg`t)tK@Uo6lC2$Ba7vTus~SpQVFwJBQ^L0EnPO@y-fJj2nxn zw0Jp}L?>0OIG=$&)n@DtJ}Oc>Mh-t1zt9i=T)%IilW4sko>N&h%h%yhWh|A|i z#uM|bZ-}PjvmxAAVqMIbGV`57m&`s!z($mGXJrWO+ImE6_var)^3qXp_$@V+PC4t5 zJ{YY&hp{`CzE<$+w)Ita>Gy_(G+*!>N2`O#E=iF;2^gV*ZQ>ac^~f@HN=HZ;3q*pH#%h&A5wGyk^|GIkZt@#IZ`0*#>^J0o5b*9 z(UN+NZt*r?Z-4Skv{O2*lEZ+`1rd7QO0|@u_c0KEW2x+iMUsoIw{lXA4$gE_?kqtkSv^OGnYgvN^S_aDVo$-hv5bY^RHa$6<>rQJYM6EfH^DB+yQ2 zp_Cm?Mz2%*j!|Cpt|88VEfYSUGeV~?5~hPIzpHZQUJSiauDEYM%zKR#GDWoLl@uO+gadSy`nEqaEH;?|hc1k_hm~Z*f2X!Wsp#<-! z{wE(7M8biLV$|DZM`k3xMh#u(_oAYtE^7La2;%8iq#+!!p!)#MEWIE6e_rkHvtbsr zOY|npi;1N1&v5wM8|P7KEr?mFJ>dol#2oA%pz%hd79>1<8yYxO^(s zri#`sS!aJ>%9HYN8}H!AmY3uP+VlI&ERTgqmXHbvH1?clij z&8S}BD@$qT0&&bzxj=7ZPDzdoKEu&1c}h+AAJLfS>~7Glx!$F=D7?cjtnKoe(;dkP zq&*5aHry7V(l$xK9V~%El^C6$mA&R3n{HYIewV1QwzKabkNA;VTX65_6_U*naOe@N z#u3S5ndnOo!g^!OE<4O`B*)zh?2)S=e*Y2VIXSYIV8!oWlSJm2K<|)2pp=c{biV%y zd*Ao&r77rbz2@;g7;P+vRYWv9vZ&*`iN3mGO%X=C%U3;%(a7^|r#jr`9({PBK$B%sYH)(Cl*R4w zsu_Uu%&=>^tWMDcTQWALR-CkYlZhD7w)E@HXhpS!?F8Q&CW+2tX6vo`SBk~{(VNZM zb&;1x?vdw`^&)OeQF^i_yi&jCIr;%dF^KJSEXI#*A3SbnmeR&d+c6l%v{TlZO{N&w)J2JT4ILl!#pVN- z^ZjS+;E4Nk*_aDi%^Y{KkZP$O=HSeB;L*Gxu#v)cH0nxF*wB%nUaw?$lAA5aU7tp^ z%krZGb$LJ!eKgrv7&j4I-U{`>8NjVvi$8ulMWYFeCW%D1!r8${F=M|4dS`RVO_0N& z_X_YRCFE)k5&!I)YS${6R z|Ky(e@nHl(KoW&U;p~OC8iE+{%U93P2y0ZmLU-IkVIGNpS@o0t$muixBSA%#9gPT~ z&W!<>g8W{u=iTXpSSIcPpMf_#=*M-?HG=cbtcmIAuq0H`oGUzg?D(YobT2fj(_;ad z+sGdTu=5js^-sKQ!_*Zq(YysTtwEW~95jbL+@o?UZ1tGMH+?zeql8uiQ4RUIwmZ?^ z5+;`%98^q-Tgc|lzz5pnh;%q~EgxqG>*C_0oV=WquiP-_6pB#DzDj#BPU-kfbjE@p%A|xsQ5<2K{%)cKL!o)ko&fQ{ zH5s@YdT_VEeN^6tSqg`YX7fW-Q2NEExoWUI785Y`#P^W3RO5|&L$UP}-k2w;x%BD>(j#iT+WY>?DcG)O-}f)A-%?q_n*VVYzL3v^E+ zP^idRRf!p}je3Oa&*T3-$zRd*x0FMyjodL272mK_i z(5;T%{W?rp%qi*QNhUhiFRbp)w`k5L!nB=UmV`3vS}LubW9YaSJeS@g_M3~JV^;6F zM`^(GJL7`z86g?l9tm4w8j$rTQXQC9*L}Iwp?w^AKeNwrW|{lO>!~lc2xUo+v8559MJuH@n#y8h@&L(4e2YR$au zD<%b#H}ywgcz|T^;S`$1iQVu3pSk#}9jiJM-EK$zq^;Wk+sbNt=uoC_*C6u8;r&%! zqec0(pR)}10GX4{J2aXzecKjjuRnRWqMH?M-ECttyWF`d*1(O8L@(8+umui}|Qaj?a3UgCvW zeWV%c71o^l*kU&Y7lGk+2JS;&r*r-*iMHB9sHm$OrPavHa|!&pzoFM!`dah$n!j@wU~k1@`x# zhiP*vK`^`YTZ{SNdAwMe4kR$HdvI^8!6>OUKOR>Kb*4*|5T9)Rdqf{VJJFq9%S2?%T8c!cy-H zXR}AbXpbLR`u-wK&bpW5tbx0nOpin=ZetZX7rQ^Mzfb(BjmrJmj53)zmo@*zP(s(! zV|b{uqcS=*Xp`mU7&JR_fOb*W;IigJUy-^W`>Y6JSW zIsNe_DxG;_a+1lQo^K`nb-ct1^g`&KD}!efn?5OhxXaw$Ywip&@hKQXidQy;5~sLt z7uhZ-@11qJylWZ!WR9csrn1dC=d|Dl$CH#FI!T%SMV3ec4b|hBq9v>Yb_>#_ARgVmE@h7fkw=E|3h(NNMks?wj$FT3J(N1ZP!@ z!Acd_rLfR5lZB-u6Rmj~M>fCQ zj^0;GM}GME#4%lIPmop*$bb`)cm&0{v>o~#$*VeMFs1%94@l_HCCGWjDU;oIQV1Ay z9AsLd?WU3^HX$eIXtW#AL=DLU5s9xI%Y9QpLF7`!XeM8>3@RK3zN+EVRpB1ftcw@C zQ?Y~RuK4H2z3AaKVqVbX+IF;eQbi&EQ8N565?nb-Xm@`LJ}eGyNq~8(6GsHSk^FJDnT??SQd~<2q|``%+W2cKw+GxVs8spXx23B2IOMidR1Slo^$mwg@aM1V7t=Bsr*K^c~A4DnOvQ{L^wJgWJ+iQYW_eR zL?1K)zx3NklDNQv1W&{F^XJv|7|=iA9~yE4`Vmkn$>8p*X4hs=Q|S@Lrgd<8eYRPT zdz@%!sHd^5!D=AHTNrY>Ll6|8s{1UTyU!)$+g7%Bz41^RYOTQkaDQj*jlGdIVZ8|Y zM(;p;3;SjCS?HP>KGoXDZ!w(kr{K5s4Rk$k3mbSE50Ld#(^)~Zu-zh}S1Zx`?1F&z zCf?Y0P#RtOCORL);;=~b3P)A)fpc~F3}r)iMarE(eByE#mpe@2*AXftjZ<<+>#pG! zY%DuNXHtXWMsji6<}+)p$^DZffziP8>C;&QOMv}w(Ag^6u6`YCD`os~B~K9Hwf`vVXnwKA*JnOiKaG5v*c7&TCz9i1{MjfOA` z6U}VVSp998p7>I-iHBjC5zZy2$=+)5;%@y~_n=$S0-3{~36PJUUm37xjS)q0Q&{zH z5LM1@6T&sjdkN66O|UmOKRe9rZhteZN{>@rF7IHB;`l(f3n#EUXXMN3cTL-xF~2q& zm=Ar6Q>3QptcEee1=qv0wbL1C^KYa%&-?1K;W}!^36E;@^}?Ec_O(0jLkxqlB| zY253z(_@z~{F*}-{ija%Nj9mx>LtDj4hB-7tdp>lRI|wltV}yPS@)`_*-zp)I-gGS zmZxjn0v2wk1NP_Ndy%8Gol;*lJ`4oGo4)VdSe;T&_FH?&E+#_hA-A%Zv61W%2TkCO z4&(i3gv^p!I=HKSgORkTxD}W#w>>XndMl^c&%WK-VKB=~L53%)jJP$5`j$Z<*sH_W ze+nA3xMzTM&`gHeTNEL8IkTMtd!aJPNP??+ixP#ufw3o?sxe69q{0!D$rm+CJ!3i( zX-PHCFP6?nYiBIvpvNn^kbxAJ9!z$i{vO&++)sFljKr3^{Fppg=|n8Y&X3M2G9Oh%`O|gJe8$Nu4?Fc&t5f zp=J!Uo#k0BV?ZL^2qAy>S1SY~_e1h5&oZJZ80?+vE9peng5fcaX*YvwVQ4AK*4pp4 zW#K7&^}_f;7(W>v$aIp@mcAYOsN4F587aKSq*C~rrLdOhh64+S@4;RXA|1@He%z~fL33w@&ev$rmp#$)%LG+jV^;^?SCR-! z2KEp}b6>{d)q`v4no}R^v>tNWd~i2WJ4&2Ym6vcR*uZcLszkekpk@97Jo(!Emv{uU zi|vh&F>;cx7_!$mOxYn@l1NWs#4&eK=ThCX>&_B`sE{m3$i;g&N{d8(#Jf%xABHaQ zDoD-*pHW3V*tBboxb4it<92e=${Uq?!x9%=h$4a#Z;w0rvJ>2fwb3;e&?>ngBRW!C z(qk^gL;Z(u2A@91C{<1lk}<8Py6$|hZ#Hlnrhz5RA3Y6GwajXh52-@%2u-l_4l(yh zl-qVVL&6buD7UO8J>(e09|j@zI>2du#ChG88QG68`(U&EIiAgjZ_8q;!Nwqq+T4dw zerp8U*pF6P6j#rId$f0c61JisW=Cg@6>O2sMorm_4Z8ZTkpG;aFtjtSRG zZF&1)N$3F~bd*#XlY3-*$p1W+qSG#KXCe8zfyPUL3ravDKAb^5@VAP-aQe4XltiQAD>eeLsdp8@WhPV0| zmA3parFIBdA8k7ZR{g?DZ<8Szexojd}H;E1}Z}R&ev!ELS|L znw0VUevRm;mV?qraN(MN;Do<;>vVf08h(uewX%#3_%r(iwonR`;1;!e;bXd(D52XY zJTo2z)OWaT*(Z3kzA;RR)_4osG%-W-oKkW3BohlQLY+qqhlvgNSJYs<5vI8@9RXI_ zpL<`x!)|ievwt|qfRO3Tze8RZfYS3Mn2d~2VB}*V-wuEIHd7~^yC4SM@`&_1FO?F% z1VIY#LFeAgeFaws4?@$1C1jiXeoM*O_n3rl*4w80(OBTlx=R<^iZMt^;o_KT?_q$L z-BX;6q%B4e3R)H8=2n`76jbOK8RloI`4qn|#!jid5!JNx{xKRwzPXW-t#5Q%O z;1@#oO%|7!YZNL@uE&HPCC?d(8Z>H%|K%Z79v9!uIhi zv!h-&&k!^O;jL95sl&%FN7>;S8FjtDaxzSlvXyytkkH$b; zg^r4j3c7&e z(3(!=A1onyGhvSTg<+YK$h$1V(GX5W8D@j!!67EMOV@I#CweOc-=R1Us4F;M)7pHB zY%*IsVmaqz-8}rkO1&LwxagPe+s_+X8$r2mL(d!CBUs`-bH15-WbEfpCKIlwXuDJ= z_^sJ0Y!?{txl5vG(P3+N5@)visu%Ld?jQoq6n!}uizP)Zd0ncFs@lo0>P$pcpNmex zk-fLXC3E>Oe%;)Z5GFhH=sSV#cD0Kxv=$_qFQa2VjQfA;G!U@iF{ zO?Uk>VF;*UKn(+G7*NCh=a?zbt@w-801yli?C&j~fWrWX0S@~wpNR0!WM<%z1RhD? zkpv#e|L#*=fEosLEB<2T1q1^G`+K7ba2Vh)z+pfG5ojR(UmffRtR??q`2++51p9mQ z25=bQFu-9zdk1Lm{AZ3D{%1C&fVJdbth|6=fM9=bQ~?eH90oWHXzu{+9iYAQk8Ydz zXCff5`2LG|0|*8P_V)%f;4r{pfWv@(7|;&``eA?n>6pOyA29y+_nyBBI1F$Y;IRMG zEt3H01Edd-K0x{a=>wz>kp4fqiy25sASHp61X2=6NgySGlmt@p-){E#FLjmxtN^eA zzzP5>0IUG80>BCYD*&tj?gT;pENd z0IUG8;{VOtRDi<(4g)w0;4py001g8<4B#+;!vGHRWx>m7{0{7K{YTXRm<0KY%nS$y z2=@0bG607G4g(wp3}^lSKAaWkegpywa_XX5JaNlMx(M>G9~pIxD(0NKE)dYQXRyG3 Y{eXgiWOOH_V_@?!ga3~&Kxpgy3jnhjbN~PV literal 0 HcmV?d00001 diff --git a/tests/large_file_invalid_clus/exfat.img.tar.xz b/tests/large_file_invalid_clus/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..5e3dbef8c143caf2dc028f2c9b570923fe99bfee GIT binary patch literal 17280 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 "${EXPECTED_FILE}" + diff <(xxd "${IMAGE_FILE}") <(xxd "${EXPECTED_FILE}") + if [ $? -ne 0 ]; then + echo "" + echo "Failed ${TESTCASE_DIR}" + losetup -d "${DEV_FILE}" + cleanup + fi + fi + + echo "" + echo "Passed ${TESTCASE_DIR}" + PASS_COUNT=$((PASS_COUNT + 1)) + + losetup -d "${DEV_FILE}" +done +cleanup diff --git a/tune/Makefile.am b/tune/Makefile.am new file mode 100644 index 0000000..1b3740b --- /dev/null +++ b/tune/Makefile.am @@ -0,0 +1,6 @@ +AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +tune_exfat_LDADD = $(top_builddir)/lib/libexfat.a + +sbin_PROGRAMS = tune.exfat + +tune_exfat_SOURCES = tune.c diff --git a/tune/tune.c b/tune/tune.c new file mode 100644 index 0000000..a53be59 --- /dev/null +++ b/tune/tune.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Namjae Jeon + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" + +static void usage(void) +{ + fprintf(stderr, "Usage: tune.exfat\n"); + fprintf(stderr, "\t-l | --print-label Print volume label\n"); + fprintf(stderr, "\t-L | --set-label=label Set volume label\n"); + fprintf(stderr, "\t-i | --print-serial Print volume serial\n"); + fprintf(stderr, "\t-I | --set-serial=value Set volume serial\n"); + fprintf(stderr, "\t-V | --version Show version\n"); + fprintf(stderr, "\t-v | --verbose Print debug\n"); + fprintf(stderr, "\t-h | --help Show help\n"); + + exit(EXIT_FAILURE); +} + +static struct option opts[] = { + {"print-label", no_argument, NULL, 'l' }, + {"set-label", required_argument, NULL, 'L' }, + {"print-serial", no_argument, NULL, 'i' }, + {"set-serial", required_argument, NULL, 'I' }, + {"version", no_argument, NULL, 'V' }, + {"verbose", no_argument, NULL, 'v' }, + {"help", no_argument, NULL, 'h' }, + {"?", no_argument, NULL, '?' }, + {NULL, 0, NULL, 0 } +}; + +int main(int argc, char *argv[]) +{ + int c; + int ret = EXIT_FAILURE; + struct exfat_blk_dev bd; + struct exfat_user_input ui; + bool version_only = false; + int flags = 0; + char label_input[VOLUME_LABEL_BUFFER_SIZE]; + off_t root_clu_off; + + init_user_input(&ui); + + if (!setlocale(LC_CTYPE, "")) + exfat_err("failed to init locale/codeset\n"); + + opterr = 0; + while ((c = getopt_long(argc, argv, "I:iL:lVvh", opts, NULL)) != EOF) + switch (c) { + case 'l': + flags = EXFAT_GET_VOLUME_LABEL; + break; + case 'L': + snprintf(label_input, sizeof(label_input), "%s", + optarg); + flags = EXFAT_SET_VOLUME_LABEL; + break; + case 'i': + flags = EXFAT_GET_VOLUME_SERIAL; + break; + case 'I': + ui.volume_serial = strtoul(optarg, NULL, 0); + flags = EXFAT_SET_VOLUME_SERIAL; + break; + case 'V': + version_only = true; + break; + case 'v': + print_level = EXFAT_DEBUG; + break; + case '?': + case 'h': + default: + usage(); + } + + show_version(); + if (version_only) + exit(EXIT_FAILURE); + + if (argc < 3) + usage(); + + memset(ui.dev_name, 0, sizeof(ui.dev_name)); + snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[argc - 1]); + + ret = exfat_get_blk_dev_info(&ui, &bd); + if (ret < 0) + goto out; + + /* Mode to change or display volume serial */ + if (flags == EXFAT_GET_VOLUME_SERIAL) { + ret = exfat_show_volume_serial(bd.dev_fd); + goto close_fd_out; + } else if (flags == EXFAT_SET_VOLUME_SERIAL) { + ret = exfat_set_volume_serial(&bd, &ui); + goto close_fd_out; + } + + root_clu_off = exfat_get_root_entry_offset(&bd); + if (root_clu_off < 0) + goto close_fd_out; + + if (flags == EXFAT_GET_VOLUME_LABEL) + ret = exfat_show_volume_label(&bd, root_clu_off); + else if (flags == EXFAT_SET_VOLUME_LABEL) + ret = exfat_set_volume_label(&bd, label_input, root_clu_off); +close_fd_out: + close(bd.dev_fd); +out: + return ret; +}