From 7e30487e6d92f98e1ab91b037f5285453359ec9a Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sat, 14 Aug 2021 07:43:39 +0000 Subject: [PATCH 01/57] exfatprogs: use correct license file Use the license file from https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt instead of the current one, which differs in some whitespace and hence has a different hash sum. Signed-off-by: a1346054 <36859588+a1346054@users.noreply.github.com> --- COPYING | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/COPYING b/COPYING index 941c87de..d159169d 100644 --- a/COPYING +++ b/COPYING @@ -56,7 +56,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS - How to Apply These Terms to Your New Programs + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it From 24c651701a5d09056a233acda7f59740753fbd34 Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sat, 14 Aug 2021 08:44:13 +0000 Subject: [PATCH 02/57] exfatprogs: fix test script issues identified through shellcheck * use correct bash from $PATH instead of hardcoded path * use correct function notation in bash * avoid deprecated `` notation for subshells and use $() instead * quote variables to avoid splitting * whitespace and style consolidation Signed-off-by: a1346054 <36859588+a1346054@users.noreply.github.com> --- tests/test_fsck.sh | 47 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/tests/test_fsck.sh b/tests/test_fsck.sh index 3a59d7f9..f9548149 100755 --- a/tests/test_fsck.sh +++ b/tests/test_fsck.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash TESTCASE_DIR=$1 IMAGE_FILE=exfat.img @@ -6,15 +6,15 @@ FSCK_PROG=../build/sbin/fsck.exfat FSCK_OPTS=-y PASS_COUNT=0 -function cleanup { +cleanup() { echo "" echo "Passed ${PASS_COUNT} of ${TEST_COUNT}" exit } if [ $# -eq 0 ]; then - TESTCASE_DIRS=`find -mindepth 1 -maxdepth 1 -type d` - TEST_COUNT=`find -mindepth 1 -maxdepth 1 -type d | wc -l` + TESTCASE_DIRS=$(find . -mindepth 1 -maxdepth 1 -type d) + TEST_COUNT=$(find . -mindepth 1 -maxdepth 1 -type d | wc -l) else TESTCASE_DIRS=$@ TEST_COUNT=$# @@ -22,54 +22,53 @@ fi for TESTCASE_DIR in $TESTCASE_DIRS do - if [ ! -e ${TESTCASE_DIR}/${IMAGE_FILE}.tar.xz ]; then + if [ ! -e "${TESTCASE_DIR}/${IMAGE_FILE}.tar.xz" ]; then TEST_COUNT=$((TEST_COUNT - 1)) continue fi - echo "Running $TESTCASE_DIR" + echo "Running ${TESTCASE_DIR}" echo "-----------------------------------" # Set up image file as loop device - tar -C . -xf $TESTCASE_DIR/$IMAGE_FILE.tar.xz - losetup -f $IMAGE_FILE - DEV_FILE=`losetup -j $IMAGE_FILE | awk '{print $1}' | sed 's/://g'` + tar -C . -xf "${TESTCASE_DIR}/${IMAGE_FILE}.tar.xz" + DEV_FILE=$(losetup -f "${IMAGE_FILE}" --show) # Run fsck for repair $FSCK_PROG $FSCK_OPTS $DEV_FILE - if [ "$?" -ne "1" ]; then + if [ "$?" -ne 1 ]; then echo "" - echo "Failed to repair $TESTCASE_DIR" - losetup -d $DEV_FILE + echo "Failed to repair ${TESTCASE_DIR}" + losetup -d "${DEV_FILE}" cleanup fi echo "" # Run fsck again $FSCK_PROG -n $DEV_FILE - if [ "$?" -ne "0" ]; then + if [ "$?" -ne 0 ]; then echo "" - echo "Failed, corrupted $TESTCASE_DIR" - losetup -d $DEV_FILE + echo "Failed, corrupted ${TESTCASE_DIR}" + losetup -d "${DEV_FILE}" cleanup fi - if [ -e "$TESTCASE_DIR/exfat.img.expected.xz" ]; then - EXPECTED_FILE=$IMAGE_FILE.expected - unxz -cfk "$TESTCASE_DIR/$EXPECTED_FILE.xz" > $EXPECTED_FILE - diff <(xxd $IMAGE_FILE) <(xxd $EXPECTED_FILE) - if [ "$?" -ne "0" ]; then + if [ -e "${TESTCASE_DIR}/exfat.img.expected.xz" ]; then + EXPECTED_FILE=${IMAGE_FILE}.expected + unxz -cfk "${TESTCASE_DIR}/${EXPECTED_FILE}.xz" > "${EXPECTED_FILE}" + diff <(xxd "${IMAGE_FILE}") <(xxd "${EXPECTED_FILE}") + if [ "$?" -ne 0 ]; then echo "" - echo "Failed $TESTCASE_DIR" - losetup -d $DEV_FILE + echo "Failed ${TESTCASE_DIR}" + losetup -d "${DEV_FILE}" cleanup fi fi echo "" - echo "Passed $TESTCASE_DIR" + echo "Passed ${TESTCASE_DIR}" PASS_COUNT=$((PASS_COUNT + 1)) - losetup -d $DEV_FILE + losetup -d "${DEV_FILE}" done cleanup From 707d6a44fa0bafca79878476fc21ba6762ee30c2 Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sat, 14 Aug 2021 08:46:34 +0000 Subject: [PATCH 03/57] exfatprogs: fix spelling in a few files Signed-off-by: a1346054 <36859588+a1346054@users.noreply.github.com> --- README.md | 2 +- fsck/fsck.c | 2 +- include/libexfat.h | 2 +- include/list.h | 3 --- mkfs/mkfs.c | 4 ++-- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4db98fda..b3fbdd0d 100644 --- a/README.md +++ b/README.md @@ -116,4 +116,4 @@ If you have any issues, please create [issues][1] or contact to [Namjae Jeon](ma ## 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`). +* Make sure you add 'Signed-Off' information to your commits (e.g. `git commit --signoff`). diff --git a/fsck/fsck.c b/fsck/fsck.c index 00d1ca7f..5f07a1d0 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -261,7 +261,7 @@ static void exfat_free_dir_list(struct exfat *exfat) /* * get references of ancestors that include @child until the count of - * ancesters is not larger than @count and the count of characters 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. */ diff --git a/include/libexfat.h b/include/libexfat.h index fecd7f23..53a82a1e 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -34,7 +34,7 @@ #define VOLUME_LABEL_BUFFER_SIZE (VOLUME_LABEL_MAX_LEN*MB_LEN_MAX+1) -/* Upcase tabel macro */ +/* Upcase table macro */ #define EXFAT_UPCASE_TABLE_SIZE (5836) /* Flags for tune.exfat and exfatlabel */ diff --git a/include/list.h b/include/list.h index 30a32de9..cc93668a 100644 --- a/include/list.h +++ b/include/list.h @@ -84,7 +84,6 @@ 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. @@ -111,8 +110,6 @@ static inline void list_del(struct list_head *entry) entry->prev = LIST_POISON2; } - - /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 8c812213..ded1e20c 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -46,14 +46,14 @@ static void exfat_setup_boot_sector(struct pbr *ppbr, struct bsx64 *pbsx = &ppbr->bsx; unsigned int i; - /* Fill exfat BIOS paramemter block */ + /* 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 paramemter block */ + /* 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); From 12e6f3190c4918b7b15347ac50afba19981c7182 Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Tue, 17 Aug 2021 21:24:35 +0000 Subject: [PATCH 04/57] exfatprogs: fix additional issues in scripts * some variables such as $? don't need to be quoted ever, and in fact shouldn't --- tests/test_fsck.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/test_fsck.sh b/tests/test_fsck.sh index f9548149..35f81e10 100755 --- a/tests/test_fsck.sh +++ b/tests/test_fsck.sh @@ -20,8 +20,7 @@ else TEST_COUNT=$# fi -for TESTCASE_DIR in $TESTCASE_DIRS -do +for TESTCASE_DIR in $TESTCASE_DIRS; do if [ ! -e "${TESTCASE_DIR}/${IMAGE_FILE}.tar.xz" ]; then TEST_COUNT=$((TEST_COUNT - 1)) continue @@ -35,8 +34,8 @@ do DEV_FILE=$(losetup -f "${IMAGE_FILE}" --show) # Run fsck for repair - $FSCK_PROG $FSCK_OPTS $DEV_FILE - if [ "$?" -ne 1 ]; then + $FSCK_PROG $FSCK_OPTS "$DEV_FILE" + if [ $? -ne 1 ]; then echo "" echo "Failed to repair ${TESTCASE_DIR}" losetup -d "${DEV_FILE}" @@ -45,8 +44,8 @@ do echo "" # Run fsck again - $FSCK_PROG -n $DEV_FILE - if [ "$?" -ne 0 ]; then + $FSCK_PROG -n "$DEV_FILE" + if [ $? -ne 0 ]; then echo "" echo "Failed, corrupted ${TESTCASE_DIR}" losetup -d "${DEV_FILE}" @@ -57,7 +56,7 @@ do EXPECTED_FILE=${IMAGE_FILE}.expected unxz -cfk "${TESTCASE_DIR}/${EXPECTED_FILE}.xz" > "${EXPECTED_FILE}" diff <(xxd "${IMAGE_FILE}") <(xxd "${EXPECTED_FILE}") - if [ "$?" -ne 0 ]; then + if [ $? -ne 0 ]; then echo "" echo "Failed ${TESTCASE_DIR}" losetup -d "${DEV_FILE}" From 5c22bb8620b86e37cded363719b1cf80afd9c880 Mon Sep 17 00:00:00 2001 From: Christophe Vu-Brugier Date: Tue, 12 Oct 2021 15:49:08 +0200 Subject: [PATCH 05/57] mkfs: replace lseek() + write() with pwrite() This reduces the number of system calls issued and may improve robustness because the return value of lseek() was never checked. Signed-off-by: Christophe Vu-Brugier --- lib/libexfat.c | 6 ++---- mkfs/mkfs.c | 10 ++++------ mkfs/upcase.c | 3 +-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/libexfat.c b/lib/libexfat.c index c54a7c8d..c1c9b037 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -475,8 +475,7 @@ 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; - lseek(bd->dev_fd, offset, SEEK_SET); - ret = read(bd->dev_fd, buf, 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; @@ -490,8 +489,7 @@ int exfat_write_sector(struct exfat_blk_dev *bd, void *buf, int bytes; unsigned long long offset = sec_off * bd->sector_size; - lseek(bd->dev_fd, offset, SEEK_SET); - bytes = write(bd->dev_fd, buf, 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); diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index ded1e20c..b663cb82 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -235,9 +235,9 @@ 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)); - lseek(fd, finfo.fat_byte_off + (offset * sizeof(__le32)), SEEK_SET); - nbyte = write(fd, (__u8 *) &clu, 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); @@ -321,8 +321,7 @@ static int exfat_create_bitmap(struct exfat_blk_dev *bd) for (i = 0; i < finfo.used_clu_cnt - EXFAT_FIRST_CLUSTER; i++) exfat_set_bit(bd, bitmap, i); - lseek(bd->dev_fd, finfo.bitmap_byte_off, SEEK_SET); - nbytes = write(bd->dev_fd, bitmap, finfo.bitmap_byte_len); + 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); @@ -359,8 +358,7 @@ static int exfat_create_root_dir(struct exfat_blk_dev *bd, ed[2].upcase_start_clu = cpu_to_le32(finfo.ut_start_clu); ed[2].upcase_size = cpu_to_le64(EXFAT_UPCASE_TABLE_SIZE); - lseek(bd->dev_fd, finfo.root_byte_off, SEEK_SET); - nbytes = write(bd->dev_fd, ed, dentries_len); + 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); diff --git a/mkfs/upcase.c b/mkfs/upcase.c index 8d5ef1a7..f86fa237 100644 --- a/mkfs/upcase.c +++ b/mkfs/upcase.c @@ -506,8 +506,7 @@ int exfat_create_upcase_table(struct exfat_blk_dev *bd) { int nbytes; - lseek(bd->dev_fd, finfo.ut_byte_off, SEEK_SET); - nbytes = write(bd->dev_fd, upcase_table, EXFAT_UPCASE_TABLE_SIZE); + nbytes = pwrite(bd->dev_fd, upcase_table, EXFAT_UPCASE_TABLE_SIZE, finfo.ut_byte_off); if (nbytes != EXFAT_UPCASE_TABLE_SIZE) return -1; From c86ccdda79a672a779f4590b73ad49424e4f4c0c Mon Sep 17 00:00:00 2001 From: Christophe Vu-Brugier Date: Mon, 18 Oct 2021 16:33:59 +0200 Subject: [PATCH 06/57] mkfs: ensure that the cluster size is greater or equal than the sector size Signed-off-by: Christophe Vu-Brugier --- mkfs/mkfs.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index ded1e20c..030197bc 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -440,6 +440,11 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, 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); From 2dc478ff6092e44f7c6c3fde7812efa9811da38b Mon Sep 17 00:00:00 2001 From: Christophe Vu-Brugier Date: Tue, 19 Oct 2021 14:07:50 +0200 Subject: [PATCH 07/57] mkfs: prevent an integer overflow when computing the FAT length The FAT length field on disk is 4 bytes. The computation of the FAT length, roughly "num_cluster * 4" can easily overflow those 4 bytes if the number of cluster is high. This patch adds a sanity check before computing the FAT length. This bug was observed when formatting a large HDD (16 TB) with a small cluster size (8 KiB). `mkfs.exfat` succeeded but Linux refused to mount the file system and reported an error with the FAT length ("bogus fat length"). Signed-off-by: Christophe Vu-Brugier --- mkfs/mkfs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 030197bc..cb931d5a 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -452,8 +452,12 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, } finfo.fat_byte_off = round_up(bd->offset + 24 * bd->sector_size, ui->boundary_align) - bd->offset; - finfo.fat_byte_len = round_up((bd->num_clusters * sizeof(int)), - ui->cluster_size); + /* 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) { From 831e2e8a6bff1ca100e2faab21c8f51cd767307c Mon Sep 17 00:00:00 2001 From: yijiangqiu1 Date: Mon, 8 Nov 2021 15:46:27 +0800 Subject: [PATCH 08/57] fsck: fix double free of exfat pointer in function init_exfat(exfat,bs), if it fails to calloc memory, it will call function free_exfat and return -ENOMEM. Thus it will goto err in main function and call free_exfat(exfat) again. As follows: main ->init_exfat(exfat,bs) ->free_exfat(exfat) return -ENOMEM ->free_exfat(exfat). Let exfat = NULL if we failed to init exfat Signed-off-by: yijiangqiu1 --- fsck/fsck.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 5f07a1d0..2cfacf3b 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -1596,8 +1596,10 @@ int main(int argc, char * const argv[]) goto err; ret = init_exfat(exfat, bs); - if (ret) + if (ret) { + exfat = NULL; goto err; + } if (exfat_mark_volume_dirty(exfat, true)) { ret = -EIO; From 6ebabb7f07a25b00908a8bc914d871cf4d753959 Mon Sep 17 00:00:00 2001 From: yijiangqiu1 Date: Mon, 8 Nov 2021 15:49:29 +0800 Subject: [PATCH 09/57] fsck: fix Out-of-Bounds Accesses in function bytes_to_human_readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In function bytes_to_human_readable, if bytes > 1024PB, the variable named i will be 6 and in “snprintf(buf, sizeof(buf), "%u.%02u %s", quoti, remain, units[i]);”, function will access units[6] . This will cause Out-of-Bounds Accesses. Signed-off-by: yijiangqiu1 --- fsck/fsck.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fsck/fsck.c b/fsck/fsck.c index 2cfacf3b..6131d13d 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -1477,6 +1477,11 @@ static char *bytes_to_human_readable(size_t bytes) 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) { From 9c164ba60ebc49381016fc4c73862e602aa0643f Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 11 Nov 2021 15:17:50 +0900 Subject: [PATCH 10/57] exfatprogs: release 1.1.3 version 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. Signed-off-by: Hyunchul Lee --- NEWS | 13 +++++++++++++ include/version.h | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index e2d53dc5..4f5ad6c2 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,16 @@ +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 ====================================== diff --git a/include/version.h b/include/version.h index 0f8a4630..da9be8d8 100644 --- a/include/version.h +++ b/include/version.h @@ -5,6 +5,6 @@ #ifndef _VERSION_H -#define EXFAT_PROGS_VERSION "1.1.2" +#define EXFAT_PROGS_VERSION "1.1.3" #endif /* !_VERSION_H */ From 772ce2001e846f2d1a0ef8bed4870a9e7fd54bb6 Mon Sep 17 00:00:00 2001 From: Sven Hoexter Date: Wed, 8 Dec 2021 20:41:37 +0100 Subject: [PATCH 11/57] manpages: correct inaccuracies in the exfatlabel manpage Update the synopsis to match the actual behaviour, the serial_value must be provided after the device, like the volume label_string. Also there is no lower case -v option, change to upper case -V. Rewrote the description slightly, and corrected some lower/upper case mistakes along the way. Signed-off-by: Sven Hoexter --- manpages/exfatlabel.8 | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/manpages/exfatlabel.8 b/manpages/exfatlabel.8 index f3274bbd..40c8d56b 100644 --- a/manpages/exfatlabel.8 +++ b/manpages/exfatlabel.8 @@ -5,9 +5,8 @@ exfatlabel \- Get or Set volume label or volume serial of an exFAT filesystem .B exfatlabel [ .B \-i -.I volume-label ] [ -.B \-v +.B \-V ] .I device [ @@ -21,15 +20,14 @@ 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. +in the argument of exfatlabel, it will be written to the volume +label field on a given device. If not, exfatlabel will just print +it after reading the volume label field from the given device. If -i +or --volume-serial is given, it will switch to volume serial mode. .PP .SH OPTIONS .TP -.BI \-i +.BI \-i\ \-\-volume-serial Switch to volume serial mode. .TP .B \-V From d58057bd9b2f4a24b874a69975ea186466937360 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 9 Dec 2021 08:56:12 +0900 Subject: [PATCH 12/57] fsck: introduce the option "b" to repair the main boot sector If the option "b" is given, try to recover the main boot sector even if exfat is not found. otherwise warn it and just exit. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 17 ++++++++++++++++- fsck/fsck.h | 1 + manpages/fsck.exfat.8 | 5 +++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 6131d13d..c06197f4 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -77,6 +77,7 @@ static struct option opts[] = { {"verbose", no_argument, NULL, 'v' }, {"help", no_argument, NULL, 'h' }, {"?", no_argument, NULL, '?' }, + {"ignore-bad-fs", no_argument, NULL, 'b' }, {NULL, 0, NULL, 0 } }; @@ -88,6 +89,7 @@ static void usage(char *name) 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-b | --ignore-bad-fs Try to recover even if exfat is not found\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"); @@ -796,9 +798,17 @@ static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) 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"); + free(boot_sect); return -EIO; } + if (memcmp(boot_sect->bpb.oem_name, "EXFAT ", 8) != 0 && + !(exfat->options & FSCK_OPTS_IGNORE_BAD_FS_NAME)) { + exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n"); + free(boot_sect); + return -ENOTSUP; + } + sect_size = 1 << boot_sect->bsx.sect_size_bits; free(boot_sect); @@ -1530,7 +1540,7 @@ int main(int argc, char * const argv[]) exfat_err("failed to init locale/codeset\n"); opterr = 0; - while ((c = getopt_long(argc, argv, "arynpVvh", opts, NULL)) != EOF) { + while ((c = getopt_long(argc, argv, "arynpbVvh", opts, NULL)) != EOF) { switch (c) { case 'n': if (ui.options & FSCK_OPTS_REPAIR_ALL) @@ -1553,6 +1563,9 @@ int main(int argc, char * const argv[]) usage(argv[0]); ui.options |= FSCK_OPTS_REPAIR_AUTO; break; + case 'b': + ui.options |= FSCK_OPTS_IGNORE_BAD_FS_NAME; + break; case 'V': version_only = true; break; @@ -1576,6 +1589,8 @@ int main(int argc, char * const argv[]) if (ui.options & FSCK_OPTS_REPAIR_WRITE) ui.ei.writeable = true; else { + if (ui.options & FSCK_OPTS_IGNORE_BAD_FS_NAME) + usage(argv[0]); ui.options |= FSCK_OPTS_REPAIR_NO; ui.ei.writeable = false; } diff --git a/fsck/fsck.h b/fsck/fsck.h index 6c91face..56d3b3b8 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -54,6 +54,7 @@ enum fsck_ui_options { FSCK_OPTS_REPAIR_AUTO = 0x08, FSCK_OPTS_REPAIR_WRITE = 0x0b, FSCK_OPTS_REPAIR_ALL = 0x0f, + FSCK_OPTS_IGNORE_BAD_FS_NAME = 0x10, }; struct exfat { diff --git a/manpages/fsck.exfat.8 b/manpages/fsck.exfat.8 index 83f7815a..b93baaa9 100644 --- a/manpages/fsck.exfat.8 +++ b/manpages/fsck.exfat.8 @@ -14,6 +14,8 @@ fsck.exfat \- check an exFAT filesystem ] [ .B \-y ] [ +.B \-b +] [ .B \-v ] .I device @@ -46,6 +48,9 @@ Prints the version number and exits. .TP .B \-y Repair the filesystem answering yes to all questions. +.TP +.B \-b +Try to repair the filesystem even if the exFAT filesystem is not found. .SH SEE ALSO .BR fsck (8), .BR fstab (5), From c0c202560bec628692e100d12b0a49c4e808bd27 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 9 Dec 2021 10:17:09 +0900 Subject: [PATCH 13/57] exfatprogs: if exfat is not found, warn it and exit If exfat is not found, make dump, label, tune warn it and exit. Signed-off-by: Hyunchul Lee --- dump/dump.c | 6 ++++++ lib/libexfat.c | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/dump/dump.c b/dump/dump.c index 7ede5508..3d77bb9b 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -90,6 +90,12 @@ static int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) goto free_ppbr; } + if (memcmp(ppbr->bpb.oem_name, "EXFAT ", 8) != 0) { + exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n"); + ret = -EINVAL; + goto free_ppbr; + } + pbsx = &ppbr->bsx; if (pbsx->sect_size_bits < EXFAT_MIN_SECT_SIZE_BITS || diff --git a/lib/libexfat.c b/lib/libexfat.c index c1c9b037..42e3fdc1 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -374,6 +374,12 @@ off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd) return -1; } + if (memcmp(bs->bpb.oem_name, "EXFAT ", 8) != 0) { + exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n"); + 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 + @@ -546,6 +552,12 @@ int exfat_show_volume_serial(int fd) goto free_ppbr; } + if (memcmp(ppbr->bpb.oem_name, "EXFAT ", 8) != 0) { + exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n"); + ret = -1; + goto free_ppbr; + } + exfat_info("volume serial : 0x%x\n", ppbr->bsx.vol_serial); free_ppbr: @@ -614,6 +626,12 @@ int exfat_set_volume_serial(struct exfat_blk_dev *bd, goto free_ppbr; } + if (memcmp(ppbr->bpb.oem_name, "EXFAT ", 8) != 0) { + exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n"); + ret = -1; + goto free_ppbr; + } + bd->sector_size = 1 << ppbr->bsx.sect_size_bits; ppbr->bsx.vol_serial = ui->volume_serial; From 428b9e5b8bf55e28914af0425a047b215acac68f Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: Thu, 5 May 2022 19:49:00 +0200 Subject: [PATCH 14/57] fsck: Refactor bytes_to_human_readable() After recent fix there was an unnecessary extra if statement and also the for loop contained needless break statement. Signed-off-by: Pavel Reichl --- fsck/fsck.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index c06197f4..ba454e64 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -1479,19 +1479,12 @@ 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; + i = sizeof(units) / sizeof(units[0]) - 1; - 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; - } + while (i && (bytes >> i * 10) == 0) + i--; + shift = i * 10; quoti = (unsigned int)(bytes / (1ULL << shift)); remain = 0; if (shift > 0) { From 8069e05285423841d781a4f3e65067ac91ed3b83 Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: Fri, 6 May 2022 16:37:49 +0200 Subject: [PATCH 15/57] README: Add checkpatch note into contribution section Signed-off-by: Pavel Reichl --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b3fbdd0d..4a0a772f 100644 --- a/README.md +++ b/README.md @@ -117,3 +117,4 @@ If you have any issues, please create [issues][1] or contact to [Namjae Jeon](ma ## 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`). +* Please check your code contribution using kernel dev-tool script [checkpatch](https://docs.kernel.org/dev-tools/checkpatch.html). From 88a334ac2f98affcc526e861c2ed1b8bd2c34b3c Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: Wed, 11 May 2022 23:20:43 +0200 Subject: [PATCH 16/57] exfatprogs: fix some minor code issues * Add checking of function return value * Fix potentially overflowing expression Signed-off-by: Pavel Reichl --- fsck/de_iter.c | 9 ++++++--- lib/libexfat.c | 13 ++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/fsck/de_iter.c b/fsck/de_iter.c index bc95c496..587b0273 100644 --- a/fsck/de_iter.c +++ b/fsck/de_iter.c @@ -82,6 +82,9 @@ static int read_ahead_next_blocks(struct exfat_de_iter *iter, offset >= iter->ra_begin_offset) { ret = get_next_clus(exfat, iter->parent, p_clus, &ra_p_clus); + if (ret) + return ret; + if (ra_p_clus == EXFAT_EOF_CLUSTER) return -EIO; @@ -172,10 +175,10 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block) 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) + if (ret) return ret; + else if (desc->p_clus == EXFAT_EOF_CLUSTER) + return EOF; } } diff --git a/lib/libexfat.c b/lib/libexfat.c index 42e3fdc1..ee48d3af 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -470,7 +470,12 @@ int exfat_set_volume_label(struct exfat_blk_dev *bd, exfat_err("volume entry write failed: %d\n", errno); return -1; } - fsync(bd->dev_fd); + + if (fsync(bd->dev_fd) == -1) { + exfat_err("failed to sync volume entry: %d, %s\n", errno, + strerror(errno)); + return -1; + } exfat_info("new label: %s\n", label_input); return 0; @@ -479,7 +484,8 @@ int exfat_set_volume_label(struct exfat_blk_dev *bd, 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; + unsigned long long offset = + (unsigned long long)sec_off * bd->sector_size; ret = pread(bd->dev_fd, buf, bd->sector_size, offset); if (ret < 0) { @@ -493,7 +499,8 @@ 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; + unsigned long long offset = + (unsigned long long)sec_off * bd->sector_size; bytes = pwrite(bd->dev_fd, buf, bd->sector_size, offset); if (bytes != (int)bd->sector_size) { From 3c6edd718ac72ea9e3444e005ab7f45f60746022 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 26 Mar 2021 08:16:37 +0900 Subject: [PATCH 17/57] fsck: add -Wextra compile option Add -Wextra compile option Signed-off-by: Hyunchul Lee --- fsck/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsck/Makefile.am b/fsck/Makefile.am index 57a0ede0..604cac2a 100644 --- a/fsck/Makefile.am +++ b/fsck/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +AM_CFLAGS = -Wall -Wextra -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common fsck_exfat_LDADD = $(top_builddir)/lib/libexfat.a sbin_PROGRAMS = fsck.exfat From f4aa96dedd1b057adea8d25341287dcee9d8e533 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 16:30:39 +0900 Subject: [PATCH 18/57] fsck: tests: allow of running testcases with an image file Allow of running testcases with an image file, instead of a loopback device. Signed-off-by: Hyunchul Lee --- tests/test_fsck.sh | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/test_fsck.sh b/tests/test_fsck.sh index 35f81e10..936db54a 100755 --- a/tests/test_fsck.sh +++ b/tests/test_fsck.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash TESTCASE_DIR=$1 +NEED_LOOPDEV=$2 IMAGE_FILE=exfat.img -FSCK_PROG=../build/sbin/fsck.exfat +FSCK_PROG=fsck.exfat FSCK_OPTS=-y PASS_COUNT=0 @@ -31,14 +32,20 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do # Set up image file as loop device tar -C . -xf "${TESTCASE_DIR}/${IMAGE_FILE}.tar.xz" - DEV_FILE=$(losetup -f "${IMAGE_FILE}" --show) + if [ $NEED_LOOPDEV ]; then + DEV_FILE=$(losetup -f "${IMAGE_FILE}" --show) + else + DEV_FILE=$IMAGE_FILE + fi # Run fsck for repair $FSCK_PROG $FSCK_OPTS "$DEV_FILE" if [ $? -ne 1 ]; then echo "" echo "Failed to repair ${TESTCASE_DIR}" - losetup -d "${DEV_FILE}" + if [ $NEED_LOOPDEV ]; then + losetup -d "${DEV_FILE}" + fi cleanup fi @@ -48,7 +55,9 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do if [ $? -ne 0 ]; then echo "" echo "Failed, corrupted ${TESTCASE_DIR}" - losetup -d "${DEV_FILE}" + if [ $NEED_LOOPDEV ]; then + losetup -d "${DEV_FILE}" + fi cleanup fi @@ -59,7 +68,9 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do if [ $? -ne 0 ]; then echo "" echo "Failed ${TESTCASE_DIR}" - losetup -d "${DEV_FILE}" + if [ $NEED_LOOPDEV ]; then + losetup -d "${DEV_FILE}" + fi cleanup fi fi @@ -68,6 +79,8 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do echo "Passed ${TESTCASE_DIR}" PASS_COUNT=$((PASS_COUNT + 1)) - losetup -d "${DEV_FILE}" + if [ $NEED_LOOPDEV ]; then + losetup -d "${DEV_FILE}" + fi done cleanup From 6faa78e7e75b7503a83c3f3f1626be159da1ea07 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 9 Apr 2021 13:07:45 +0900 Subject: [PATCH 19/57] fsck: don't stop traveling directory entries even if there is an unknown entry or corrupted file entry set, keep traveling entries. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index ba454e64..0dced9d4 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -1220,7 +1220,7 @@ static int read_children(struct exfat *exfat, struct exfat_inode *dir) ret = read_file(de_iter, &node, &dentry_count); if (ret < 0) { exfat_stat.error_count++; - goto err; + break; } else if (ret) { exfat_stat.error_count++; exfat_stat.fixed_count++; @@ -1259,11 +1259,10 @@ static int read_children(struct exfat *exfat, struct exfat_inode *dir) 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; + if (!IS_EXFAT_DELETED(dentry->type)) + exfat_err("unknown entry type. 0x%x\n", + dentry->type); + break; } exfat_de_iter_advance(de_iter, dentry_count); From e818b535db63c1f9c163661e55bf4068d6c16aaa Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 16:32:08 +0900 Subject: [PATCH 20/57] fsck: subtract the count of fixed files from the count of corrupted files Subtract the count of fixed files from the count of corrupted files. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 0dced9d4..9d898133 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -1511,7 +1511,8 @@ static void exfat_show_info(struct exfat *exfat, const char *dev_name, 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); + exfat_stat.error_count - exfat_stat.fixed_count, + exfat_stat.fixed_count); } int main(int argc, char * const argv[]) From 4e29696e73c16a4572ca22a4a43185c5e838523a Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 6 May 2021 17:25:32 +0900 Subject: [PATCH 21/57] fsck: print "corrupted" if there are still corrupted files Print "corrupted" if there are still corrupted files. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 9d898133..e3b603b2 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -1496,9 +1496,10 @@ static char *bytes_to_human_readable(size_t bytes) return buf; } -static void exfat_show_info(struct exfat *exfat, const char *dev_name, - int errors) +static void exfat_show_info(struct exfat *exfat, const char *dev_name) { + bool clean; + exfat_info("sector size: %s\n", bytes_to_human_readable(1 << exfat->bs->bsx.sect_size_bits)); exfat_info("cluster size: %s\n", @@ -1506,10 +1507,12 @@ static void exfat_show_info(struct exfat *exfat, const char *dev_name, exfat_info("volume size: %s\n", bytes_to_human_readable(exfat->blk_dev->size)); + clean = exfat_stat.error_count == 0 || + exfat_stat.error_count == exfat_stat.fixed_count; printf("%s: %s. directories %ld, files %ld\n", dev_name, - errors ? "checking stopped" : "clean", + clean ? "clean" : "corrupted", exfat_stat.dir_count, exfat_stat.file_count); - if (errors || exfat->dirty) + if (exfat_stat.error_count) printf("%s: files corrupted %ld, files fixed %ld\n", dev_name, exfat_stat.error_count - exfat_stat.fixed_count, exfat_stat.fixed_count); @@ -1639,12 +1642,13 @@ int main(int argc, char * const argv[]) exfat_mark_volume_dirty(exfat, false); out: - exfat_show_info(exfat, ui.ei.dev_name, ret); + exfat_show_info(exfat, ui.ei.dev_name); err: - if (ret == -EINVAL) - exit_code = FSCK_EXIT_ERRORS_LEFT; - else if (ret) + if (ret && ret != -EINVAL) exit_code = FSCK_EXIT_OPERATION_ERROR; + else if (ret == -EINVAL || + exfat_stat.error_count != exfat_stat.fixed_count) + exit_code = FSCK_EXIT_ERRORS_LEFT; else if (exfat->dirty) exit_code = FSCK_EXIT_CORRECTED; else From 05bc35b1c7d9e12776211f03fecb4ffdd362d8c5 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 15:25:51 +0900 Subject: [PATCH 22/57] fsck: handle bad clusters properly in check_clus_chain() Truncate a NotFat file if a next cluster is BAD, And even if a next cluster is out of range, allocate the cluster to a file Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index e3b603b2..7c469af4 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -480,19 +480,31 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) /* 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 (next == EXFAT_BAD_CLUSTER) { + if (repair_file_ask(&exfat->de_iter, node, + ER_FILE_INVALID_CLUS, + "BAD cluster. truncate to %" + PRIu64 " bytes", + count * exfat->clus_size)) + goto truncate_file; + else + return -EINVAL; + } else if (!node->is_contiguous) { + if (next != EXFAT_EOF_CLUSTER && + !heap_clus(exfat, next)) { if (repair_file_ask(&exfat->de_iter, node, ER_FILE_INVALID_CLUS, - "broken cluster chain. " - "truncate to %" + "broken cluster chain. truncate to %" PRIu64 " bytes", - count * exfat->clus_size)) + (count + 1) * exfat->clus_size)) { + count++; + prev = clus; + EXFAT_BITMAP_SET(exfat->alloc_bitmap, + clus - EXFAT_FIRST_CLUSTER); goto truncate_file; - - else + } else { return -EINVAL; + } } } From d46254931505ea7aa17bf5519c7ddcc3c9c3bb87 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 24 Feb 2022 10:57:00 +0900 Subject: [PATCH 23/57] fsck: not cut cluster chain if one of them is marked as free Not cut a cluster chain if FAT entries are valid, Even if one of them is marked as free in the bitmap. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 7c469af4..b5ee1c35 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -467,13 +467,10 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) 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 + if (!repair_file_ask(&exfat->de_iter, node, + ER_FILE_INVALID_CLUS, + "cluster %#x is marked as free", + clus)) return -EINVAL; } @@ -544,8 +541,10 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) /* 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); + if (!node->is_contiguous && heap_clus(exfat, prev)) { + if (set_fat(exfat, prev, EXFAT_EOF_CLUSTER)) + return -EIO; + } return 1; } From f5f0c944aeea2024f24629d2bdd797bebe369296 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 15:28:00 +0900 Subject: [PATCH 24/57] fsck: fix infinite loop in read_children() if reading file dentry set is failed, we must skip the first directory entry. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index b5ee1c35..69be851b 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -958,8 +958,8 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node) return valid ? ret : -EINVAL; } -static int read_file_dentries(struct exfat_de_iter *iter, - struct exfat_inode **new_node, int *skip_dentries) +static int read_file_dentry_set(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; @@ -1026,7 +1026,7 @@ static int read_file_dentries(struct exfat_de_iter *iter, *new_node = node; return 0; err: - *skip_dentries = 0; + *skip_dentries = 1; *new_node = NULL; free_exfat_inode(node); return ret; @@ -1040,7 +1040,7 @@ static int read_file(struct exfat_de_iter *de_iter, *new_node = NULL; - ret = read_file_dentries(de_iter, &node, dentry_count); + ret = read_file_dentry_set(de_iter, &node, dentry_count); if (ret) return ret; From ac0cea6cab031dfc0eeb10acb65747cc84e56822 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 24 Aug 2022 12:59:12 +0900 Subject: [PATCH 25/57] fsck: split exfat into exfat and exfat_fsck To move shared code to lib diretory, split exfat structure into exfat and exfat_fsck structure. Signed-off-by: Hyunchul Lee --- fsck/de_iter.c | 4 +- fsck/fsck.c | 284 ++++++++++++++++++++++++++++--------------------- fsck/fsck.h | 16 +-- fsck/repair.c | 29 ++--- fsck/repair.h | 4 +- 5 files changed, 193 insertions(+), 144 deletions(-) diff --git a/fsck/de_iter.c b/fsck/de_iter.c index 587b0273..a9fcdd94 100644 --- a/fsck/de_iter.c +++ b/fsck/de_iter.c @@ -203,7 +203,7 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block) } int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, - struct exfat_inode *dir) + struct exfat_inode *dir, struct buffer_desc *bd) { iter->exfat = exfat; iter->parent = dir; @@ -216,7 +216,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, iter->ra_partial_size = MIN(iter->ra_partial_size, 8 * KB); if (!iter->buffer_desc) - iter->buffer_desc = exfat->buffer_desc; + iter->buffer_desc = bd; if (iter->parent->size == 0) return EOF; diff --git a/fsck/fsck.c b/fsck/fsck.c index 69be851b..4a4d69ab 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -65,6 +65,7 @@ struct path_resolve_ctx { char local_path[PATH_MAX * MB_LEN_MAX + 1]; }; +struct exfat_fsck exfat_fsck; struct exfat_stat exfat_stat; struct path_resolve_ctx path_resolve_ctx; @@ -125,10 +126,6 @@ static struct exfat_inode *alloc_exfat_inode(__u16 attr) node->last_pclus = EXFAT_EOF_CLUSTER; node->attr = attr; - if (attr & ATTR_SUBDIR) - exfat_stat.dir_count++; - else - exfat_stat.file_count++; return node; } @@ -186,10 +183,8 @@ static void inode_free_ancestors(struct exfat_inode *child) return; } -static void free_exfat(struct exfat *exfat) +void exfat_free_exfat(struct exfat *exfat) { - int i; - if (exfat) { if (exfat->bs) free(exfat->bs); @@ -197,21 +192,20 @@ static void free_exfat(struct exfat *exfat) 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) +struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs) { - int i; + struct exfat *exfat; + + exfat = (struct exfat *)calloc(1, sizeof(*exfat)); + if (!exfat) + return NULL; INIT_LIST_HEAD(&exfat->dir_list); + exfat->blk_dev = blk_dev; exfat->bs = bs; exfat->clus_count = le32_to_cpu(bs->bsx.clu_count); exfat->clus_size = EXFAT_CLUSTER_SIZE(bs); @@ -232,22 +226,47 @@ static int init_exfat(struct exfat *exfat, struct pbr *bs) 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) + return exfat; +err: + exfat_free_exfat(exfat); + return NULL; +} + +void exfat_free_buffer(struct buffer_desc *bd, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (bd[i].buffer) + free(bd[i].buffer); + if (bd[i].dirty) + free(bd[i].dirty); + } + free(bd); +} + +struct buffer_desc *exfat_alloc_buffer(int count, + unsigned int clu_size, unsigned int sect_size) +{ + struct buffer_desc *bd; + int i; + + bd = (struct buffer_desc *)calloc(sizeof(*bd), count); + if (!bd) + return NULL; + + for (i = 0; i < count; i++) { + bd[i].buffer = (char *)malloc(clu_size); + if (!bd[i].buffer) goto err; - exfat->buffer_desc[i].dirty = - (char *)calloc( - (exfat->clus_size / exfat->sect_size), 1); - if (!exfat->buffer_desc[i].dirty) + bd[i].dirty = (char *)calloc(clu_size / sect_size, 1); + if (!bd[i].dirty) goto err; } - return 0; + return bd; err: - free_exfat(exfat); - return -ENOMEM; + exfat_free_buffer(bd, count); + return NULL; } static void exfat_free_dir_list(struct exfat *exfat) @@ -358,7 +377,7 @@ static int resolve_path_parent(struct path_resolve_ctx *ctx, ({ \ resolve_path_parent(&path_resolve_ctx, \ (iter)->parent, inode); \ - exfat_repair_ask((iter)->exfat, code, \ + exfat_repair_ask(&exfat_fsck, code, \ "ERROR: %s: " fmt, \ path_resolve_ctx.local_path, \ ##__VA_ARGS__); \ @@ -410,8 +429,10 @@ static int set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus) return 0; } -static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) +static int check_clus_chain(struct exfat_de_iter *de_iter, + struct exfat_inode *node) { + struct exfat *exfat = de_iter->exfat; struct exfat_dentry *stream_de; clus_t clus, prev, next; uint64_t count, max_count; @@ -426,9 +447,9 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) /* 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")) + (node->size > 0 && !heap_clus(exfat, node->first_clus))) { + if (repair_file_ask(de_iter, node, + ER_FILE_FIRST_CLUS, "first cluster is wrong")) goto truncate_file; else return -EINVAL; @@ -438,11 +459,11 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) 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)) + if (repair_file_ask(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; @@ -453,13 +474,12 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) * 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)) + clus - EXFAT_FIRST_CLUSTER)) { + if (repair_file_ask(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; @@ -478,7 +498,7 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) if (get_next_clus(exfat, node, clus, &next)) goto truncate_file; if (next == EXFAT_BAD_CLUSTER) { - if (repair_file_ask(&exfat->de_iter, node, + if (repair_file_ask(de_iter, node, ER_FILE_INVALID_CLUS, "BAD cluster. truncate to %" PRIu64 " bytes", @@ -489,11 +509,11 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) } else if (!node->is_contiguous) { if (next != EXFAT_EOF_CLUSTER && !heap_clus(exfat, next)) { - if (repair_file_ask(&exfat->de_iter, node, - ER_FILE_INVALID_CLUS, - "broken cluster chain. truncate to %" - PRIu64 " bytes", - (count + 1) * exfat->clus_size)) { + if (repair_file_ask(de_iter, node, + ER_FILE_INVALID_CLUS, + "broken cluster chain. truncate to %" + PRIu64 " bytes", + (count + 1) * exfat->clus_size)) { count++; prev = clus; EXFAT_BITMAP_SET(exfat->alloc_bitmap, @@ -507,16 +527,16 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) count++; EXFAT_BITMAP_SET(exfat->alloc_bitmap, - clus - EXFAT_FIRST_CLUSTER); + 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)) + if (repair_file_ask(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; @@ -528,15 +548,15 @@ static int check_clus_chain(struct exfat *exfat, struct exfat_inode *node) if (!heap_clus(exfat, prev)) node->first_clus = EXFAT_FREE_CLUSTER; - exfat_de_iter_get_dirty(&exfat->de_iter, 1, &stream_de); + exfat_de_iter_get_dirty(de_iter, 1, &stream_de); if (count * exfat->clus_size < - le64_to_cpu(stream_de->stream_valid_size)) + le64_to_cpu(stream_de->stream_valid_size)) stream_de->stream_valid_size = cpu_to_le64( - count * exfat->clus_size); + 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); + count * exfat->clus_size); /* remaining clusters will be freed while FAT is compared with * alloc_bitmap. @@ -646,9 +666,6 @@ 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; @@ -795,7 +812,9 @@ static int restore_boot_region(struct exfat_blk_dev *bd, unsigned int sect_size) return ret; } -static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) +static int exfat_boot_region_check(struct exfat_blk_dev *blkdev, + struct pbr **bs, + bool ignore_bad_fs_name) { struct pbr *boot_sect; unsigned int sect_size; @@ -806,7 +825,7 @@ static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) if (boot_sect == NULL) return -ENOMEM; - if (exfat_read(exfat->blk_dev->dev_fd, boot_sect, + if (exfat_read(blkdev->dev_fd, boot_sect, sizeof(*boot_sect), 0) != (ssize_t)sizeof(*boot_sect)) { exfat_err("failed to read Main boot sector\n"); free(boot_sect); @@ -814,7 +833,7 @@ static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) } if (memcmp(boot_sect->bpb.oem_name, "EXFAT ", 8) != 0 && - !(exfat->options & FSCK_OPTS_IGNORE_BAD_FS_NAME)) { + !ignore_bad_fs_name) { exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n"); free(boot_sect); return -ENOTSUP; @@ -824,16 +843,17 @@ static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) free(boot_sect); /* check boot regions */ - ret = read_boot_region(exfat->blk_dev, bs, + ret = read_boot_region(blkdev, 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" + if (ret == -EINVAL && + exfat_repair_ask(&exfat_fsck, 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, + ret = read_boot_region(blkdev, bs, BACKUP_BOOT_SEC_IDX, sect_size, false); if (!ret) @@ -844,7 +864,7 @@ static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) if (sector_sizes[i] == sect_size) continue; - ret = read_boot_region(exfat->blk_dev, bs, + ret = read_boot_region(blkdev, bs, BACKUP_BOOT_SEC_IDX, sector_sizes[i], false); if (!ret) { @@ -857,7 +877,7 @@ static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) return ret; restore: - ret = restore_boot_region(exfat->blk_dev, sect_size); + ret = restore_boot_region(blkdev, sect_size); if (ret) { exfat_err("failed to restore boot region from backup\n"); free(*bs); @@ -913,7 +933,7 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node) uint16_t checksum; bool valid = true; - ret = check_clus_chain(exfat, node); + ret = check_clus_chain(iter, node); if (ret < 0) return ret; @@ -1050,6 +1070,10 @@ static int read_file(struct exfat_de_iter *de_iter, return -EINVAL; } + if (node->attr & ATTR_SUBDIR) + exfat_stat.dir_count++; + else + exfat_stat.file_count++; *new_node = node; return ret; } @@ -1083,19 +1107,19 @@ static bool read_volume_label(struct exfat_de_iter *iter) return true; } -static void exfat_bitmap_set_range(struct exfat *exfat, - clus_t start_clus, clus_t count) +static void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, + clus_t start_clus, clus_t count) { clus_t clus; if (!heap_clus(exfat, start_clus) || - !heap_clus(exfat, start_clus + count)) + !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); + EXFAT_BITMAP_SET(bitmap, + clus - EXFAT_FIRST_CLUSTER); clus++; } } @@ -1106,6 +1130,10 @@ static bool read_bitmap(struct exfat_de_iter *iter) struct exfat *exfat; exfat = iter->exfat; + + if (heap_clus(exfat, exfat->disk_bitmap_clus)) + return true; + if (exfat_de_iter_get(iter, 0, &dentry)) return false; @@ -1128,9 +1156,10 @@ static bool read_bitmap(struct exfat_de_iter *iter) 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)); + exfat_bitmap_set_range(exfat, exfat->alloc_bitmap, + 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, @@ -1191,24 +1220,26 @@ static bool read_upcase_table(struct exfat_de_iter *iter) 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)); + exfat_bitmap_set_range(exfat, exfat->alloc_bitmap, + 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) +static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) { - int ret; + struct exfat *exfat = fsck->exfat; struct exfat_inode *node = NULL; struct exfat_dentry *dentry; - int dentry_count; struct exfat_de_iter *de_iter; + int dentry_count; + int ret; - de_iter = &exfat->de_iter; - ret = exfat_de_iter_init(de_iter, exfat, dir); + de_iter = &fsck->de_iter; + ret = exfat_de_iter_init(de_iter, exfat, dir, fsck->buffer_desc); if (ret == EOF) return 0; else if (ret) @@ -1288,8 +1319,9 @@ static int read_children(struct exfat *exfat, struct exfat_inode *dir) return ret; } -static int write_dirty_fat(struct exfat *exfat) +static int write_dirty_fat(struct exfat_fsck *fsck) { + struct exfat *exfat = fsck->exfat; struct buffer_desc *bd; off_t offset; ssize_t len; @@ -1299,7 +1331,7 @@ static int write_dirty_fat(struct exfat *exfat) clus = 0; last_clus = le32_to_cpu(exfat->bs->bsx.clu_count) + 2; - bd = exfat->buffer_desc; + bd = fsck->buffer_desc; idx = 0; offset = le32_to_cpu(exfat->bs->bsx.fat_offset) * exfat->sect_size; @@ -1352,8 +1384,9 @@ static int write_dirty_fat(struct exfat *exfat) return 0; } -static int write_dirty_bitmap(struct exfat *exfat) +static int write_dirty_bitmap(struct exfat_fsck *fsck) { + struct exfat *exfat = fsck->exfat; struct buffer_desc *bd; off_t offset, last_offset, bitmap_offset; ssize_t len; @@ -1366,7 +1399,7 @@ static int write_dirty_bitmap(struct exfat *exfat) read_size = exfat->clus_size; write_size = exfat->sect_size; - bd = exfat->buffer_desc; + bd = fsck->buffer_desc; idx = 0; while (offset < last_offset) { @@ -1396,13 +1429,13 @@ static int write_dirty_bitmap(struct exfat *exfat) return 0; } -static int reclaim_free_clusters(struct exfat *exfat) +static int reclaim_free_clusters(struct exfat_fsck *fsck) { - if (write_dirty_fat(exfat)) { + if (write_dirty_fat(fsck)) { exfat_err("failed to write fat entries\n"); return -EIO; } - if (write_dirty_bitmap(exfat)) { + if (write_dirty_bitmap(fsck)) { exfat_err("failed to write bitmap\n"); return -EIO; } @@ -1416,8 +1449,9 @@ static int reclaim_free_clusters(struct exfat *exfat) * 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) +static int exfat_filesystem_check(struct exfat_fsck *fsck) { + struct exfat *exfat = fsck->exfat; struct exfat_inode *dir; int ret = 0, dir_errors; @@ -1429,7 +1463,8 @@ static int exfat_filesystem_check(struct exfat *exfat) 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); + dir = list_entry(exfat->dir_list.next, + struct exfat_inode, list); if (!(dir->attr & ATTR_SUBDIR)) { fsck_err(dir->parent, dir, @@ -1439,7 +1474,7 @@ static int exfat_filesystem_check(struct exfat *exfat) goto out; } - dir_errors = read_children(exfat, dir); + dir_errors = read_children(fsck, dir); if (dir_errors) { resolve_path(&path_resolve_ctx, dir); exfat_debug("failed to check dentries: %s\n", @@ -1454,7 +1489,7 @@ static int exfat_filesystem_check(struct exfat *exfat) out: exfat_free_dir_list(exfat); exfat->root = NULL; - if (exfat->dirty_fat && reclaim_free_clusters(exfat)) + if (fsck->dirty_fat && reclaim_free_clusters(fsck)) return -EIO; return ret; } @@ -1479,6 +1514,7 @@ static int exfat_root_dir_check(struct exfat *exfat) root->size = clus_count * exfat->clus_size; exfat->root = root; + exfat_stat.dir_count++; exfat_debug("root directory: start cluster[0x%x] size[0x%" PRIx64 "]\n", root->first_clus, root->size); return 0; @@ -1507,8 +1543,9 @@ static char *bytes_to_human_readable(size_t bytes) return buf; } -static void exfat_show_info(struct exfat *exfat, const char *dev_name) +static void exfat_show_info(struct exfat_fsck *fsck, const char *dev_name) { + struct exfat *exfat = fsck->exfat; bool clean; exfat_info("sector size: %s\n", @@ -1533,7 +1570,6 @@ 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; @@ -1602,6 +1638,8 @@ int main(int argc, char * const argv[]) ui.ei.writeable = false; } + exfat_fsck.options = ui.options; + 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) { @@ -1609,39 +1647,41 @@ int main(int argc, char * const argv[]) return FSCK_EXIT_OPERATION_ERROR; } - exfat = (struct exfat *)calloc(1, sizeof(*exfat)); - if (!exfat) { - exfat_err("failed to allocate exfat\n"); - ret = -ENOMEM; + ret = exfat_boot_region_check(&bd, &bs, + ui.options & FSCK_OPTS_IGNORE_BAD_FS_NAME ? + true : false); + if (ret) goto err; - } - exfat->blk_dev = &bd; - exfat->options = ui.options; - ret = exfat_boot_region_check(exfat, &bs); - if (ret) + exfat_fsck.exfat = exfat_alloc_exfat(&bd, bs); + if (!exfat_fsck.exfat) { + ret = -ENOMEM; goto err; + } - ret = init_exfat(exfat, bs); - if (ret) { - exfat = NULL; + exfat_fsck.buffer_desc = exfat_alloc_buffer(2, + exfat_fsck.exfat->clus_size, + exfat_fsck.exfat->sect_size); + if (!exfat_fsck.buffer_desc) { + ret = -ENOMEM; goto err; } - if (exfat_mark_volume_dirty(exfat, true)) { + if ((exfat_fsck.options & FSCK_OPTS_REPAIR_WRITE) && + exfat_mark_volume_dirty(exfat_fsck.exfat, true)) { ret = -EIO; goto err; } exfat_debug("verifying root directory...\n"); - ret = exfat_root_dir_check(exfat); + ret = exfat_root_dir_check(exfat_fsck.exfat); if (ret) { exfat_err("failed to verify root directory.\n"); goto out; } exfat_debug("verifying directory entries...\n"); - ret = exfat_filesystem_check(exfat); + ret = exfat_filesystem_check(&exfat_fsck); if (ret) goto out; @@ -1650,22 +1690,26 @@ int main(int argc, char * const argv[]) ret = -EIO; goto out; } - exfat_mark_volume_dirty(exfat, false); + if (exfat_fsck.options & FSCK_OPTS_REPAIR_WRITE) + exfat_mark_volume_dirty(exfat_fsck.exfat, false); out: - exfat_show_info(exfat, ui.ei.dev_name); + exfat_show_info(&exfat_fsck, ui.ei.dev_name); err: if (ret && ret != -EINVAL) exit_code = FSCK_EXIT_OPERATION_ERROR; else if (ret == -EINVAL || exfat_stat.error_count != exfat_stat.fixed_count) exit_code = FSCK_EXIT_ERRORS_LEFT; - else if (exfat->dirty) + else if (exfat_fsck.dirty) exit_code = FSCK_EXIT_CORRECTED; else exit_code = FSCK_EXIT_NO_ERRORS; - free_exfat(exfat); + if (exfat_fsck.buffer_desc) + exfat_free_buffer(exfat_fsck.buffer_desc, 2); + if (exfat_fsck.exfat) + exfat_free_exfat(exfat_fsck.exfat); close(bd.dev_fd); return exit_code; } diff --git a/fsck/fsck.h b/fsck/fsck.h index 56d3b3b8..c324593a 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -58,9 +58,6 @@ enum fsck_ui_options { }; 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]; @@ -69,14 +66,21 @@ struct exfat { 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; }; +struct exfat_fsck { + struct exfat *exfat; + struct exfat_de_iter de_iter; + struct buffer_desc *buffer_desc; /* cluster * 2 */ + enum fsck_ui_options options; + bool dirty:1; + bool dirty_fat:1; +}; + #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) @@ -88,7 +92,7 @@ int get_next_clus(struct exfat *exfat, struct exfat_inode *node, /* de_iter.c */ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, - struct exfat_inode *dir); + struct exfat_inode *dir, struct buffer_desc *bd); 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, diff --git a/fsck/repair.c b/fsck/repair.c index c79d3797..205a5f9f 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -57,19 +57,19 @@ static struct exfat_repair_problem *find_problem(er_problem_code_t prcode) return NULL; } -static bool ask_repair(struct exfat *exfat, struct exfat_repair_problem *pr) +static bool ask_repair(struct exfat_fsck *fsck, struct exfat_repair_problem *pr) { bool repair = false; char answer[8]; - if (exfat->options & FSCK_OPTS_REPAIR_NO || - pr->flags & ERF_DEFAULT_NO) + if (fsck->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) + else if (fsck->options & FSCK_OPTS_REPAIR_YES || + pr->flags & ERF_DEFAULT_YES) repair = true; else { - if (exfat->options & FSCK_OPTS_REPAIR_ASK) { + if (fsck->options & FSCK_OPTS_REPAIR_ASK) { do { printf(". %s (y/N)? ", prompts[pr->prompt_type]); @@ -83,8 +83,8 @@ static bool ask_repair(struct exfat *exfat, struct exfat_repair_problem *pr) return false; } } while (1); - } else if (exfat->options & FSCK_OPTS_REPAIR_AUTO && - pr->flags & ERF_PREEN_YES) + } else if (fsck->options & FSCK_OPTS_REPAIR_AUTO && + pr->flags & ERF_PREEN_YES) repair = true; } @@ -93,8 +93,8 @@ static bool ask_repair(struct exfat *exfat, struct exfat_repair_problem *pr) return repair; } -bool exfat_repair_ask(struct exfat *exfat, er_problem_code_t prcode, - const char *desc, ...) +bool exfat_repair_ask(struct exfat_fsck *fsck, er_problem_code_t prcode, + const char *desc, ...) { struct exfat_repair_problem *pr = NULL; va_list ap; @@ -109,11 +109,12 @@ bool exfat_repair_ask(struct exfat *exfat, er_problem_code_t prcode, vprintf(desc, ap); va_end(ap); - if (ask_repair(exfat, pr)) { + if (ask_repair(fsck, pr)) { if (pr->prompt_type & ERP_TRUNCATE) - exfat->dirty_fat = true; - exfat->dirty = true; + fsck->dirty_fat = true; + fsck->dirty = true; return true; - } else + } else { return false; + } } diff --git a/fsck/repair.h b/fsck/repair.h index f7286b92..e927e799 100644 --- a/fsck/repair.h +++ b/fsck/repair.h @@ -18,7 +18,7 @@ typedef unsigned int er_problem_code_t; -bool exfat_repair_ask(struct exfat *exfat, er_problem_code_t prcode, - const char *fmt, ...); +bool exfat_repair_ask(struct exfat_fsck *fsck, er_problem_code_t prcode, + const char *fmt, ...); #endif From d40e54a4d73b90b5f7ce43766525c2c3b8c55a20 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 24 Aug 2022 14:10:36 +0900 Subject: [PATCH 26/57] lib: move shared code to lib For adding dumping filesystem tools, Move shared code to lib directory. Signed-off-by: Hyunchul Lee --- fsck/Makefile.am | 2 +- fsck/fsck.c | 391 +++--------------------------- fsck/fsck.h | 74 +----- fsck/repair.c | 4 +- fsck/repair.h | 1 + include/exfat_dir.h | 36 +++ include/exfat_fs.h | 73 ++++++ include/libexfat.h | 16 +- lib/Makefile.am | 2 +- fsck/de_iter.c => lib/exfat_dir.c | 11 +- lib/exfat_fs.c | 296 ++++++++++++++++++++++ lib/libexfat.c | 62 +++++ 12 files changed, 526 insertions(+), 442 deletions(-) create mode 100644 include/exfat_dir.h create mode 100644 include/exfat_fs.h rename fsck/de_iter.c => lib/exfat_dir.c (97%) create mode 100644 lib/exfat_fs.c diff --git a/fsck/Makefile.am b/fsck/Makefile.am index 604cac2a..519b13a6 100644 --- a/fsck/Makefile.am +++ b/fsck/Makefile.am @@ -3,4 +3,4 @@ fsck_exfat_LDADD = $(top_builddir)/lib/libexfat.a sbin_PROGRAMS = fsck.exfat -fsck_exfat_SOURCES = fsck.c repair.c fsck.h de_iter.c repair.h +fsck_exfat_SOURCES = fsck.c repair.c fsck.h repair.h diff --git a/fsck/fsck.c b/fsck/fsck.c index 4a4d69ab..02e3f2b8 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -15,8 +15,10 @@ #include "exfat_ondisk.h" #include "libexfat.h" -#include "fsck.h" #include "repair.h" +#include "exfat_fs.h" +#include "exfat_dir.h" +#include "fsck.h" struct fsck_user_input { struct exfat_user_input ei; @@ -59,12 +61,6 @@ struct exfat_stat { 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_fsck exfat_fsck; struct exfat_stat exfat_stat; struct path_resolve_ctx path_resolve_ctx; @@ -100,282 +96,16 @@ static void usage(char *name) #define fsck_err(parent, inode, fmt, ...) \ ({ \ - resolve_path_parent(&path_resolve_ctx, \ + exfat_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; - 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; -} - -void exfat_free_exfat(struct exfat *exfat) -{ - if (exfat) { - if (exfat->bs) - free(exfat->bs); - if (exfat->alloc_bitmap) - free(exfat->alloc_bitmap); - if (exfat->disk_bitmap) - free(exfat->disk_bitmap); - free(exfat); - } -} - -struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs) -{ - struct exfat *exfat; - - exfat = (struct exfat *)calloc(1, sizeof(*exfat)); - if (!exfat) - return NULL; - - INIT_LIST_HEAD(&exfat->dir_list); - exfat->blk_dev = blk_dev; - 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; - } - - return exfat; -err: - exfat_free_exfat(exfat); - return NULL; -} - -void exfat_free_buffer(struct buffer_desc *bd, int count) -{ - int i; - - for (i = 0; i < count; i++) { - if (bd[i].buffer) - free(bd[i].buffer); - if (bd[i].dirty) - free(bd[i].dirty); - } - free(bd); -} - -struct buffer_desc *exfat_alloc_buffer(int count, - unsigned int clu_size, unsigned int sect_size) -{ - struct buffer_desc *bd; - int i; - - bd = (struct buffer_desc *)calloc(sizeof(*bd), count); - if (!bd) - return NULL; - - for (i = 0; i < count; i++) { - bd[i].buffer = (char *)malloc(clu_size); - if (!bd[i].buffer) - goto err; - bd[i].dirty = (char *)calloc(clu_size / sect_size, 1); - if (!bd[i].dirty) - goto err; - } - return bd; -err: - exfat_free_buffer(bd, count); - return NULL; -} - -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, \ + exfat_resolve_path_parent(&path_resolve_ctx, \ (iter)->parent, inode); \ exfat_repair_ask(&exfat_fsck, code, \ "ERROR: %s: " fmt, \ @@ -383,52 +113,6 @@ static int resolve_path_parent(struct path_resolve_ctx *ctx, ##__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_de_iter *de_iter, struct exfat_inode *node) { @@ -447,7 +131,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, /* the first cluster is wrong */ if ((node->size == 0 && node->first_clus != EXFAT_FREE_CLUSTER) || - (node->size > 0 && !heap_clus(exfat, node->first_clus))) { + (node->size > 0 && !exfat_heap_clus(exfat, node->first_clus))) { if (repair_file_ask(de_iter, node, ER_FILE_FIRST_CLUS, "first cluster is wrong")) goto truncate_file; @@ -495,7 +179,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, } /* This cluster is allocated or not */ - if (get_next_clus(exfat, node, clus, &next)) + if (exfat_get_next_clus(exfat, node, clus, &next)) goto truncate_file; if (next == EXFAT_BAD_CLUSTER) { if (repair_file_ask(de_iter, node, @@ -508,7 +192,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, return -EINVAL; } else if (!node->is_contiguous) { if (next != EXFAT_EOF_CLUSTER && - !heap_clus(exfat, next)) { + !exfat_heap_clus(exfat, next)) { if (repair_file_ask(de_iter, node, ER_FILE_INVALID_CLUS, "broken cluster chain. truncate to %" @@ -545,7 +229,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, return 0; truncate_file: node->size = count * exfat->clus_size; - if (!heap_clus(exfat, prev)) + if (!exfat_heap_clus(exfat, prev)) node->first_clus = EXFAT_FREE_CLUSTER; exfat_de_iter_get_dirty(de_iter, 1, &stream_de); @@ -553,7 +237,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, 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)) + if (!exfat_heap_clus(exfat, prev)) stream_de->stream_start_clu = EXFAT_FREE_CLUSTER; stream_de->stream_size = cpu_to_le64( count * exfat->clus_size); @@ -561,8 +245,8 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, /* remaining clusters will be freed while FAT is compared with * alloc_bitmap. */ - if (!node->is_contiguous && heap_clus(exfat, prev)) { - if (set_fat(exfat, prev, EXFAT_EOF_CLUSTER)) + if (!node->is_contiguous && exfat_heap_clus(exfat, prev)) { + if (exfat_set_fat(exfat, prev, EXFAT_EOF_CLUSTER)) return -EIO; } return 1; @@ -577,7 +261,7 @@ static bool root_get_clus_count(struct exfat *exfat, struct exfat_inode *node, *clus_count = 0; do { - if (!heap_clus(exfat, clus)) { + if (!exfat_heap_clus(exfat, clus)) { exfat_err("/: bad cluster. 0x%x\n", clus); return false; } @@ -592,7 +276,7 @@ static bool root_get_clus_count(struct exfat *exfat, struct exfat_inode *node, EXFAT_BITMAP_SET(exfat->alloc_bitmap, clus - EXFAT_FIRST_CLUSTER); - if (get_next_clus(exfat, node, clus, &clus) != 0) { + if (exfat_get_next_clus(exfat, node, clus, &clus) != 0) { exfat_err("/: broken cluster chain\n"); return false; } @@ -602,21 +286,6 @@ static bool root_get_clus_count(struct exfat *exfat, struct exfat_inode *node, 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) { @@ -999,14 +668,14 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, } *new_node = NULL; - node = alloc_exfat_inode(le16_to_cpu(file_de->file_attr)); + node = exfat_alloc_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); + exfat_free_inode(node); return -EINVAL; } @@ -1048,7 +717,7 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, err: *skip_dentries = 1; *new_node = NULL; - free_exfat_inode(node); + exfat_free_inode(node); return ret; } @@ -1066,7 +735,7 @@ static int read_file(struct exfat_de_iter *de_iter, ret = check_inode(de_iter, node); if (ret < 0) { - free_exfat_inode(node); + exfat_free_inode(node); return -EINVAL; } @@ -1112,8 +781,8 @@ static void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, { clus_t clus; - if (!heap_clus(exfat, start_clus) || - !heap_clus(exfat, start_clus + count)) + if (!exfat_heap_clus(exfat, start_clus) || + !exfat_heap_clus(exfat, start_clus + count)) return; clus = start_clus; @@ -1131,7 +800,7 @@ static bool read_bitmap(struct exfat_de_iter *iter) exfat = iter->exfat; - if (heap_clus(exfat, exfat->disk_bitmap_clus)) + if (exfat_heap_clus(exfat, exfat->disk_bitmap_clus)) return true; if (exfat_de_iter_get(iter, 0, &dentry)) @@ -1147,7 +816,7 @@ static bool read_bitmap(struct exfat_de_iter *iter) le64_to_cpu(dentry->bitmap_size)); return false; } - if (!heap_clus(exfat, le32_to_cpu(dentry->bitmap_start_clu))) { + if (!exfat_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; @@ -1183,7 +852,7 @@ static bool read_upcase_table(struct exfat_de_iter *iter) if (exfat_de_iter_get(iter, 0, &dentry)) return false; - if (!heap_clus(exfat, le32_to_cpu(dentry->upcase_start_clu))) { + if (!exfat_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; @@ -1273,7 +942,7 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) list_add_tail(&node->sibling, &dir->children); list_add_tail(&node->list, &exfat->dir_list); } else - free_exfat_inode(node); + exfat_free_inode(node); break; case EXFAT_VOLUME: if (!read_volume_label(de_iter)) { @@ -1313,7 +982,7 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) exfat_de_iter_flush(de_iter); return 0; err: - inode_free_children(dir, false); + exfat_free_children(dir, false); INIT_LIST_HEAD(&dir->children); exfat_de_iter_flush(de_iter); return ret; @@ -1476,15 +1145,15 @@ static int exfat_filesystem_check(struct exfat_fsck *fsck) dir_errors = read_children(fsck, dir); if (dir_errors) { - resolve_path(&path_resolve_ctx, dir); + exfat_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); + exfat_free_file_children(dir); + exfat_free_ancestors(dir); } out: exfat_free_dir_list(exfat); @@ -1499,7 +1168,7 @@ static int exfat_root_dir_check(struct exfat *exfat) struct exfat_inode *root; clus_t clus_count; - root = alloc_exfat_inode(ATTR_SUBDIR); + root = exfat_alloc_inode(ATTR_SUBDIR); if (!root) { exfat_err("failed to allocate memory\n"); return -ENOMEM; @@ -1508,7 +1177,7 @@ static int exfat_root_dir_check(struct exfat *exfat) 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); + exfat_free_inode(root); return -EINVAL; } root->size = clus_count * exfat->clus_size; diff --git a/fsck/fsck.h b/fsck/fsck.h index c324593a..34a695a5 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -7,46 +7,6 @@ #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, @@ -57,20 +17,8 @@ enum fsck_ui_options { FSCK_OPTS_IGNORE_BAD_FS_NAME = 0x10, }; -struct exfat { - 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; - char *alloc_bitmap; - char *disk_bitmap; - clus_t disk_bitmap_clus; - unsigned int disk_bitmap_size; -}; +struct exfat; +struct exfat_inode; struct exfat_fsck { struct exfat *exfat; @@ -81,24 +29,6 @@ struct exfat_fsck { bool dirty_fat:1; }; -#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, struct buffer_desc *bd); -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/repair.c b/fsck/repair.c index 205a5f9f..941c86be 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -8,8 +8,10 @@ #include "exfat_ondisk.h" #include "libexfat.h" -#include "fsck.h" #include "repair.h" +#include "exfat_fs.h" +#include "exfat_dir.h" +#include "fsck.h" struct exfat_repair_problem { er_problem_code_t prcode; diff --git a/fsck/repair.h b/fsck/repair.h index e927e799..fbdc607a 100644 --- a/fsck/repair.h +++ b/fsck/repair.h @@ -17,6 +17,7 @@ #define ER_FILE_ZERO_NOFAT 0x00002007 typedef unsigned int er_problem_code_t; +struct exfat_fsck; bool exfat_repair_ask(struct exfat_fsck *fsck, er_problem_code_t prcode, const char *fmt, ...); diff --git a/include/exfat_dir.h b/include/exfat_dir.h new file mode 100644 index 00000000..ce010d2e --- /dev/null +++ b/include/exfat_dir.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2022 Hyunchul Lee + */ + +#ifndef _DIR_H_ +#define _DIR_H_ + +struct exfat; +struct exfat_inode; +struct buffer_desc; + +struct exfat_de_iter { + struct exfat *exfat; + struct exfat_inode *parent; + struct buffer_desc *buffer_desc; /* cluster * 2 */ + __u32 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; +}; + +int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, + struct exfat_inode *dir, struct buffer_desc *bd); +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); + +#endif diff --git a/include/exfat_fs.h b/include/exfat_fs.h new file mode 100644 index 00000000..b31c2b6c --- /dev/null +++ b/include/exfat_fs.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Hyunchul Lee + */ +#ifndef _EXFAT_FS_H_ +#define _EXFAT_FS_H_ + +#include "list.h" + +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 exfat { + 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; + char *alloc_bitmap; + char *disk_bitmap; + clus_t disk_bitmap_clus; + unsigned int disk_bitmap_size; +}; + +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 buffer_desc { + __u32 p_clus; + unsigned int offset; + char *buffer; + char *dirty; +}; + +struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs); +void exfat_free_exfat(struct exfat *exfat); + +struct exfat_inode *exfat_alloc_inode(__u16 attr); +void exfat_free_inode(struct exfat_inode *node); + +void exfat_free_children(struct exfat_inode *dir, bool file_only); +void exfat_free_file_children(struct exfat_inode *dir); +void exfat_free_ancestors(struct exfat_inode *child); +void exfat_free_dir_list(struct exfat *exfat); + +int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child); +int exfat_resolve_path_parent(struct path_resolve_ctx *ctx, + struct exfat_inode *parent, struct exfat_inode *child); + +struct buffer_desc *exfat_alloc_buffer(int count, + unsigned int clu_size, unsigned int sect_size); +void exfat_free_buffer(struct buffer_desc *bd, int count); +#endif diff --git a/include/libexfat.h b/include/libexfat.h index 53a82a1e..fbedfecc 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -10,6 +10,8 @@ #include #include +typedef __u32 clus_t; + #define KB (1024) #define MB (1024*1024) #define GB (1024UL*1024UL*1024UL) @@ -45,6 +47,10 @@ #define EXFAT_MAX_SECTOR_SIZE 4096 +#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) + enum { BOOT_SEC_IDX = 0, EXBOOT_SEC_IDX, @@ -79,6 +85,9 @@ struct exfat_user_input { unsigned int volume_serial; }; +struct exfat; +struct exfat_inode; + void show_version(void); void exfat_set_bit(struct exfat_blk_dev *bd, char *bitmap, @@ -114,7 +123,12 @@ 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); - +int exfat_get_next_clus(struct exfat *exfat, struct exfat_inode *node, + clus_t clus, clus_t *next); +int exfat_set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus); +off_t exfat_s2o(struct exfat *exfat, off_t sect); +off_t exfat_c2o(struct exfat *exfat, unsigned int clus); +bool exfat_heap_clus(struct exfat *exfat, clus_t clus); /* * Exfat Print diff --git a/lib/Makefile.am b/lib/Makefile.am index 5ea12db4..d732cfd4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,4 +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 +libexfat_a_SOURCES = libexfat.c exfat_fs.c exfat_dir.c diff --git a/fsck/de_iter.c b/lib/exfat_dir.c similarity index 97% rename from fsck/de_iter.c rename to lib/exfat_dir.c index a9fcdd94..62578423 100644 --- a/fsck/de_iter.c +++ b/lib/exfat_dir.c @@ -9,7 +9,8 @@ #include "exfat_ondisk.h" #include "libexfat.h" -#include "fsck.h" +#include "exfat_fs.h" +#include "exfat_dir.h" static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block) { @@ -80,8 +81,8 @@ static int read_ahead_next_blocks(struct exfat_de_iter *iter, 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); + ret = exfat_get_next_clus(exfat, iter->parent, + p_clus, &ra_p_clus); if (ret) return ret; @@ -172,8 +173,8 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block) 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); + ret = exfat_get_next_clus(exfat, iter->parent, + prev_desc->p_clus, &desc->p_clus); desc->offset = 0; if (ret) return ret; diff --git a/lib/exfat_fs.c b/lib/exfat_fs.c new file mode 100644 index 00000000..84ff72d0 --- /dev/null +++ b/lib/exfat_fs.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2022 Hyunchul Lee + */ + +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" + +#include "exfat_fs.h" +#include "exfat_dir.h" + +#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)) + +struct exfat_inode *exfat_alloc_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; + return node; +} + +void exfat_free_inode(struct exfat_inode *node) +{ + free(node); +} + +void exfat_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); + exfat_free_inode(node); + } + } else { + list_del(&node->sibling); + list_del(&node->list); + exfat_free_inode(node); + } + } +} + +void exfat_free_file_children(struct exfat_inode *dir) +{ + exfat_free_children(dir, true); +} + +/* delete @child and all ancestors that does not have + * children + */ +void exfat_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); + exfat_free_inode(child); + + child = parent; + } while (child && list_empty(&child->children)); +} + +void exfat_free_dir_list(struct exfat *exfat) +{ + struct exfat_inode *dir, *i; + + list_for_each_entry_safe(dir, i, &exfat->dir_list, list) { + exfat_free_file_children(dir); + list_del(&dir->list); + exfat_free_inode(dir); + } +} + +void exfat_free_exfat(struct exfat *exfat) +{ + if (exfat) { + if (exfat->bs) + free(exfat->bs); + if (exfat->alloc_bitmap) + free(exfat->alloc_bitmap); + if (exfat->disk_bitmap) + free(exfat->disk_bitmap); + free(exfat); + } +} + +struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs) +{ + struct exfat *exfat; + + exfat = (struct exfat *)calloc(1, sizeof(*exfat)); + if (!exfat) + return NULL; + + INIT_LIST_HEAD(&exfat->dir_list); + exfat->blk_dev = blk_dev; + 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; + } + + return exfat; +err: + exfat_free_exfat(exfat); + return NULL; +} + +struct buffer_desc *exfat_alloc_buffer(int count, + unsigned int clu_size, unsigned int sect_size) +{ + struct buffer_desc *bd; + int i; + + bd = (struct buffer_desc *)calloc(sizeof(*bd), count); + if (!bd) + return NULL; + + for (i = 0; i < count; i++) { + bd[i].buffer = (char *)malloc(clu_size); + if (!bd[i].buffer) + goto err; + bd[i].dirty = (char *)calloc(clu_size / sect_size, 1); + if (!bd[i].dirty) + goto err; + } + return bd; +err: + exfat_free_buffer(bd, count); + return NULL; +} + +void exfat_free_buffer(struct buffer_desc *bd, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (bd[i].buffer) + free(bd[i].buffer); + if (bd[i].dirty) + free(bd[i].dirty); + } + free(bd); +} + +/* + * get references of ancestors that include @child until the count of + * ancesters 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. + */ +static 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; +} + +int exfat_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)); +} + +int exfat_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 = exfat_resolve_path(ctx, child); + child->parent = old; + return ret; +} diff --git a/lib/libexfat.c b/lib/libexfat.c index ee48d3af..b6084ee2 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -19,6 +19,7 @@ #include "exfat_ondisk.h" #include "libexfat.h" #include "version.h" +#include "exfat_fs.h" #define BITS_PER_LONG (sizeof(long) * CHAR_BIT) @@ -681,3 +682,64 @@ unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd, return clu_off_sectnr * bd->sector_size + (clu - EXFAT_RESERVED_CLUSTERS) * bd->cluster_size; } + +int exfat_get_next_clus(struct exfat *exfat, struct exfat_inode *node, + clus_t clus, clus_t *next) +{ + off_t offset; + + *next = EXFAT_EOF_CLUSTER; + + if (!exfat_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; +} + +int exfat_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; +} + +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)); +} + +bool exfat_heap_clus(struct exfat *exfat, clus_t clus) +{ + return clus >= EXFAT_FIRST_CLUSTER && + (clus - EXFAT_FIRST_CLUSTER) < exfat->clus_count; +} From cba94136deb98eae024587220de1fa7ff32ef311 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 24 Aug 2022 16:18:49 +0900 Subject: [PATCH 27/57] lib: move bitmap functions to lib Move fsck's bitmap functions to library to avoid duplication. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 35 ------------------------- include/libexfat.h | 24 ++++++++++++++--- lib/exfat_fs.c | 18 ------------- lib/libexfat.c | 64 +++++++++------------------------------------- mkfs/mkfs.c | 4 +-- 5 files changed, 34 insertions(+), 111 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 02e3f2b8..f9f0dca4 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -27,24 +27,6 @@ struct fsck_user_input { #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 @@ -776,23 +758,6 @@ static bool read_volume_label(struct exfat_de_iter *iter) return true; } -static void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, - clus_t start_clus, clus_t count) -{ - clus_t clus; - - if (!exfat_heap_clus(exfat, start_clus) || - !exfat_heap_clus(exfat, start_clus + count)) - return; - - clus = start_clus; - while (clus < start_clus + count) { - EXFAT_BITMAP_SET(bitmap, - clus - EXFAT_FIRST_CLUSTER); - clus++; - } -} - static bool read_bitmap(struct exfat_de_iter *iter) { struct exfat_dentry *dentry; diff --git a/include/libexfat.h b/include/libexfat.h index fbedfecc..a0c89f64 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -88,12 +88,28 @@ struct exfat_user_input { struct exfat; struct exfat_inode; +#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)) +void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, + clus_t start_clus, clus_t count); + 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); diff --git a/lib/exfat_fs.c b/lib/exfat_fs.c index 84ff72d0..93476146 100644 --- a/lib/exfat_fs.c +++ b/lib/exfat_fs.c @@ -14,24 +14,6 @@ #include "exfat_fs.h" #include "exfat_dir.h" -#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)) - struct exfat_inode *exfat_alloc_inode(__u16 attr) { struct exfat_inode *node; diff --git a/lib/libexfat.c b/lib/libexfat.c index b6084ee2..896cecf5 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -21,63 +21,23 @@ #include "version.h" #include "exfat_fs.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) +void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, + clus_t start_clus, clus_t count) { - 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; + clus_t clus; - b = clu & ((bd->sector_size << 3) - 1); + if (!exfat_heap_clus(exfat, start_clus) || + !exfat_heap_clus(exfat, start_clus + count)) + return; - 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); + clus = start_clus; + while (clus < start_clus + count) { + EXFAT_BITMAP_SET(bitmap, + clus - EXFAT_FIRST_CLUSTER); + clus++; + } } wchar_t exfat_bad_char(wchar_t w) diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index b5b4957c..cc1f62dc 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -314,12 +314,12 @@ static int exfat_create_bitmap(struct exfat_blk_dev *bd) char *bitmap; unsigned int i, nbytes; - bitmap = calloc(finfo.bitmap_byte_len, sizeof(*bitmap)); + bitmap = calloc(EXFAT_BITMAP_SIZE(finfo.total_clu_cnt), sizeof(*bitmap)); if (!bitmap) return -1; for (i = 0; i < finfo.used_clu_cnt - EXFAT_FIRST_CLUSTER; i++) - exfat_set_bit(bd, bitmap, i); + EXFAT_BITMAP_SET(bitmap, i); nbytes = pwrite(bd->dev_fd, bitmap, finfo.bitmap_byte_len, finfo.bitmap_byte_off); if (nbytes != finfo.bitmap_byte_len) { From 139e7a93543372e28712dbe8a315a5dd1c34c82b Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 29 Mar 2021 09:53:47 +0900 Subject: [PATCH 28/57] fsck: add dentry set lookup function The specified dentry set can be found using exfat_lookup_dentry_set() with parent. Signed-off-by: Hyunchul Lee --- include/exfat_dir.h | 24 +++++++++ lib/exfat_dir.c | 125 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 140 insertions(+), 9 deletions(-) diff --git a/include/exfat_dir.h b/include/exfat_dir.h index ce010d2e..746d818c 100644 --- a/include/exfat_dir.h +++ b/include/exfat_dir.h @@ -24,6 +24,26 @@ struct exfat_de_iter { int max_skip_dentries; }; +struct exfat_lookup_filter { + struct { + uint8_t type; + /* return 0 if matched, return 1 if not matched, + * otherwise return errno + */ + int (*filter)(struct exfat_de_iter *iter, + void *param, int *dentry_count); + void *param; + } in; + struct { + struct exfat_dentry *dentry_set; + int dentry_count; + /* device offset where the dentry_set locates, or + * the empty slot locates or EOF if not found. + */ + off_t dentry_d_offset; + } out; +}; + int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, struct exfat_inode *dir, struct buffer_desc *bd); int exfat_de_iter_get(struct exfat_de_iter *iter, @@ -32,5 +52,9 @@ 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_device_offset(struct exfat_de_iter *iter); + +int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, + struct exfat_lookup_filter *filter); #endif diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 62578423..5742e2f4 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -6,12 +6,24 @@ #include #include #include +#include #include "exfat_ondisk.h" #include "libexfat.h" #include "exfat_fs.h" #include "exfat_dir.h" +static struct path_resolve_ctx path_resolve_ctx; + +#define fsck_err(parent, inode, fmt, ...) \ +({ \ + exfat_resolve_path_parent(&path_resolve_ctx, \ + parent, inode); \ + exfat_err("ERROR: %s: " fmt, \ + path_resolve_ctx.local_path, \ + ##__VA_ARGS__); \ +}) + static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block) { off_t device_offset; @@ -216,8 +228,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, 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 = bd; + iter->buffer_desc = bd; if (iter->parent->size == 0) return EOF; @@ -296,13 +307,9 @@ int exfat_de_iter_flush(struct exfat_de_iter *iter) 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) + if (skip_dentries > iter->max_skip_dentries) return -EINVAL; iter->max_skip_dentries = 0; @@ -311,7 +318,107 @@ int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries) return 0; } -off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter) +off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter) { - return iter->de_file_offset; + struct buffer_desc *bd; + unsigned int block; + + if ((uint64_t)iter->de_file_offset >= iter->parent->size) + return EOF; + + block = iter->de_file_offset / iter->read_size; + bd = &iter->buffer_desc[block & 0x01]; + return exfat_c2o(iter->exfat, bd->p_clus) + bd->offset + + iter->de_file_offset % iter->read_size; +} + +/* + * try to find the dentry set matched with @filter. this function + * doesn't verify the dentry set. + * + * if found, return 0. if not found, return EOF. otherwise return errno. + */ +int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, + struct exfat_lookup_filter *filter) +{ + struct buffer_desc *bd = NULL; + struct exfat_dentry *dentry = NULL; + off_t free_offset = 0; + struct exfat_de_iter de_iter; + int dentry_count; + int retval; + bool last_is_free = false; + + bd = exfat_alloc_buffer(2, exfat->clus_size, exfat->sect_size); + if (!bd) + return -ENOMEM; + + retval = exfat_de_iter_init(&de_iter, exfat, parent, bd); + if (retval == EOF || retval) + goto out; + + filter->out.dentry_set = NULL; + while (1) { + retval = exfat_de_iter_get(&de_iter, 0, &dentry); + if (retval == EOF) { + break; + } else if (retval) { + fsck_err(parent->parent, parent, + "failed to get a dentry. %d\n", retval); + goto out; + } + + dentry_count = 1; + if (dentry->type == filter->in.type) { + retval = 0; + if (filter->in.filter) + retval = filter->in.filter(&de_iter, + filter->in.param, + &dentry_count); + + if (retval == 0) { + struct exfat_dentry *d; + int i; + + filter->out.dentry_set = calloc(dentry_count, + sizeof(struct exfat_dentry)); + if (!filter->out.dentry_set) { + retval = -ENOMEM; + goto out; + } + for (i = 0; i < dentry_count; i++) { + exfat_de_iter_get(&de_iter, i, &d); + memcpy(filter->out.dentry_set + i, d, + sizeof(struct exfat_dentry)); + } + filter->out.dentry_count = dentry_count; + goto out; + } else if (retval < 0) { + goto out; + } + last_is_free = false; + } else if ((dentry->type == EXFAT_LAST || + IS_EXFAT_DELETED(dentry->type))) { + if (!last_is_free) { + free_offset = exfat_de_iter_device_offset(&de_iter); + last_is_free = true; + } + } else { + last_is_free = false; + } + + exfat_de_iter_advance(&de_iter, dentry_count); + } + +out: + if (retval == 0) + filter->out.dentry_d_offset = + exfat_de_iter_device_offset(&de_iter); + else if (retval == EOF && last_is_free) + filter->out.dentry_d_offset = free_offset; + else + filter->out.dentry_d_offset = EOF; + if (bd) + exfat_free_buffer(bd, 2); + return retval; } From 303936af072f66b4f458d484c33abbeac1bf0c4d Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 30 Mar 2021 09:33:35 +0900 Subject: [PATCH 29/57] fsck: read bitmap using lookup function Read bitmap using lookup function. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index f9f0dca4..a71c7d99 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -758,19 +758,21 @@ static bool read_volume_label(struct exfat_de_iter *iter) return true; } -static bool read_bitmap(struct exfat_de_iter *iter) +static int read_bitmap(struct exfat *exfat) { + struct exfat_lookup_filter filter = { + .in.type = EXFAT_BITMAP, + .in.filter = NULL, + .in.param = NULL, + }; struct exfat_dentry *dentry; - struct exfat *exfat; - - exfat = iter->exfat; + int retval; - if (exfat_heap_clus(exfat, exfat->disk_bitmap_clus)) - return true; - - if (exfat_de_iter_get(iter, 0, &dentry)) - return false; + retval = exfat_lookup_dentry_set(exfat, exfat->root, &filter); + if (retval) + return retval; + dentry = filter.out.dentry_set; exfat_debug("start cluster %#x, size %#" PRIx64 "\n", le32_to_cpu(dentry->bitmap_start_clu), le64_to_cpu(dentry->bitmap_size)); @@ -779,12 +781,12 @@ static bool read_bitmap(struct exfat_de_iter *iter) 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; + return -EINVAL; } if (!exfat_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; + return -EINVAL; } exfat->disk_bitmap_clus = le32_to_cpu(dentry->bitmap_start_clu); @@ -794,14 +796,14 @@ static bool read_bitmap(struct exfat_de_iter *iter) le64_to_cpu(dentry->bitmap_start_clu), DIV_ROUND_UP(exfat->disk_bitmap_size, exfat->clus_size)); + free(filter.out.dentry_set); 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; + return -EIO; + return 0; } static bool read_upcase_table(struct exfat_de_iter *iter) @@ -916,14 +918,6 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) 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( @@ -935,10 +929,12 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) case EXFAT_LAST: goto out; default: - if (!IS_EXFAT_DELETED(dentry->type)) - exfat_err("unknown entry type. 0x%x\n", - dentry->type); - break; + if (IS_EXFAT_DELETED(dentry->type) || + dentry->type == EXFAT_BITMAP) + break; + exfat_err("unknown entry type. 0x%x\n", dentry->type); + ret = -EINVAL; + goto err; } exfat_de_iter_advance(de_iter, dentry_count); @@ -1151,6 +1147,11 @@ static int exfat_root_dir_check(struct exfat *exfat) exfat_stat.dir_count++; exfat_debug("root directory: start cluster[0x%x] size[0x%" PRIx64 "]\n", root->first_clus, root->size); + + if (read_bitmap(exfat)) { + exfat_err("failed to read bitmap\n"); + return -EINVAL; + } return 0; } From cf8644512f5ef6bf4345d245eb80dd3f91f530c9 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 1 Apr 2021 17:21:38 +0900 Subject: [PATCH 30/57] fsck: load upcase table using lookup function Load upcase table using lookup function and decompress the table for calculating the hash value of a file name. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 95 ++++++++++++++++++++++++++++++++++------------ include/exfat_fs.h | 1 + include/libexfat.h | 1 + 3 files changed, 73 insertions(+), 24 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index a71c7d99..18995402 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -806,23 +806,56 @@ static int read_bitmap(struct exfat *exfat) return 0; } -static bool read_upcase_table(struct exfat_de_iter *iter) +static int decompress_upcase_table(const __le16 *in_table, size_t in_len, + __u16 *out_table, size_t out_len) { - struct exfat_dentry *dentry; - struct exfat *exfat; + size_t i, k; + uint16_t ch; + + if (in_len > out_len) + return -E2BIG; + + for (k = 0; k < out_len; k++) + out_table[k] = k; + + for (i = 0, k = 0; i < in_len && k < out_len; i++) { + ch = le16_to_cpu(in_table[i]); + + if (ch == 0xFFFF && i + 1 < in_len) { + uint16_t len = le16_to_cpu(in_table[++i]); + + k += len; + } else { + out_table[k++] = ch; + } + } + return 0; +} + +static int read_upcase_table(struct exfat *exfat) +{ + struct exfat_lookup_filter filter = { + .in.type = EXFAT_UPCASE, + .in.filter = NULL, + .in.param = NULL, + }; + struct exfat_dentry *dentry = NULL; + __le16 *upcase = NULL; + int retval; ssize_t size; - __le16 *upcase; __le32 checksum; - exfat = iter->exfat; + retval = exfat_lookup_dentry_set(exfat, exfat->root, &filter); + if (retval) + return retval; - if (exfat_de_iter_get(iter, 0, &dentry)) - return false; + dentry = filter.out.dentry_set; if (!exfat_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; + retval = -EINVAL; + goto out; } size = (ssize_t)le64_to_cpu(dentry->upcase_size); @@ -830,21 +863,23 @@ static bool read_upcase_table(struct exfat_de_iter *iter) size == 0 || size % sizeof(__le16)) { exfat_err("invalid size of upcase table. 0x%" PRIx64 "\n", le64_to_cpu(dentry->upcase_size)); - return false; + retval = -EINVAL; + goto out; } upcase = (__le16 *)malloc(size); if (!upcase) { exfat_err("failed to allocate upcase table\n"); - return false; + retval = -ENOMEM; + goto out; } 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; + retval = -EIO; + goto out; } checksum = 0; @@ -852,8 +887,8 @@ static bool read_upcase_table(struct exfat_de_iter *iter) 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; + retval = -EINVAL; + goto out; } exfat_bitmap_set_range(exfat, exfat->alloc_bitmap, @@ -861,8 +896,21 @@ static bool read_upcase_table(struct exfat_de_iter *iter) DIV_ROUND_UP(le64_to_cpu(dentry->upcase_size), exfat->clus_size)); - free(upcase); - return true; + exfat->upcase_table = calloc(1, + sizeof(uint16_t) * EXFAT_UPCASE_TABLE_CHARS); + if (!exfat->upcase_table) { + retval = -EIO; + goto out; + } + + decompress_upcase_table(upcase, size / 2, + exfat->upcase_table, EXFAT_UPCASE_TABLE_CHARS); +out: + if (dentry) + free(dentry); + if (upcase) + free(upcase); + return retval; } static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) @@ -918,19 +966,13 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) goto err; } break; + case EXFAT_BITMAP: 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) || - dentry->type == EXFAT_BITMAP) + if (IS_EXFAT_DELETED(dentry->type)) break; exfat_err("unknown entry type. 0x%x\n", dentry->type); ret = -EINVAL; @@ -1152,6 +1194,11 @@ static int exfat_root_dir_check(struct exfat *exfat) exfat_err("failed to read bitmap\n"); return -EINVAL; } + + if (read_upcase_table(exfat)) { + exfat_err("failed to read upcase table\n"); + return -EINVAL; + } return 0; } diff --git a/include/exfat_fs.h b/include/exfat_fs.h index b31c2b6c..820dbe68 100644 --- a/include/exfat_fs.h +++ b/include/exfat_fs.h @@ -37,6 +37,7 @@ struct exfat { char *disk_bitmap; clus_t disk_bitmap_clus; unsigned int disk_bitmap_size; + __u16 *upcase_table; }; struct path_resolve_ctx { diff --git a/include/libexfat.h b/include/libexfat.h index a0c89f64..7ed1d9e7 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -37,6 +37,7 @@ typedef __u32 clus_t; #define VOLUME_LABEL_BUFFER_SIZE (VOLUME_LABEL_MAX_LEN*MB_LEN_MAX+1) /* Upcase table macro */ +#define EXFAT_UPCASE_TABLE_CHARS (0x10000) #define EXFAT_UPCASE_TABLE_SIZE (5836) /* Flags for tune.exfat and exfatlabel */ From 25a85939ddb8c62ccd4cd2b6cdb5f0703ae6473c Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 26 May 2021 17:19:57 +0900 Subject: [PATCH 31/57] fsck: read volume label in exfat_root_dir_check() Read the volume label in exfat_root_dir_check() using lookup function and remove this read from read_children(). Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 18995402..aab59a76 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -729,33 +729,43 @@ static int read_file(struct exfat_de_iter *de_iter, return ret; } -static bool read_volume_label(struct exfat_de_iter *iter) +static int read_volume_label(struct exfat *exfat) { - struct exfat *exfat; struct exfat_dentry *dentry; + int err; __le16 disk_label[VOLUME_LABEL_MAX_LEN]; + struct exfat_lookup_filter filter = { + .in.type = EXFAT_VOLUME, + .in.filter = NULL, + }; - exfat = iter->exfat; - if (exfat_de_iter_get(iter, 0, &dentry)) - return false; + err = exfat_lookup_dentry_set(exfat, exfat->root, &filter); + if (err) + return err; + + dentry = filter.out.dentry_set; if (dentry->vol_char_cnt == 0) - return true; + goto out; if (dentry->vol_char_cnt > VOLUME_LABEL_MAX_LEN) { exfat_err("too long label. %d\n", dentry->vol_char_cnt); - return false; + err = -EINVAL; + goto out; } 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; + err = -EINVAL; + goto out; } exfat_info("volume label [%s]\n", exfat->volume_label); - return true; +out: + free(filter.out.dentry_set); + return err; } static int read_bitmap(struct exfat *exfat) @@ -960,12 +970,6 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) exfat_free_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: case EXFAT_UPCASE: break; @@ -1190,6 +1194,9 @@ static int exfat_root_dir_check(struct exfat *exfat) exfat_debug("root directory: start cluster[0x%x] size[0x%" PRIx64 "]\n", root->first_clus, root->size); + if (read_volume_label(exfat)) + exfat_err("failed to read volume label\n"); + if (read_bitmap(exfat)) { exfat_err("failed to read bitmap\n"); return -EINVAL; From cab0c62ce4135f646b57606c1d73cb83b2925fb3 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 6 Apr 2021 10:37:26 +0900 Subject: [PATCH 32/57] fsck: add file dentry set lookup function Add a lookup function to find the specified file dentry set. Signed-off-by: Hyunchul Lee --- include/exfat_dir.h | 2 ++ lib/exfat_dir.c | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/include/exfat_dir.h b/include/exfat_dir.h index 746d818c..109262e6 100644 --- a/include/exfat_dir.h +++ b/include/exfat_dir.h @@ -56,5 +56,7 @@ off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter); int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, struct exfat_lookup_filter *filter); +int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent, + const char *name, struct exfat_lookup_filter *filter_out); #endif diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 5742e2f4..466c1191 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -422,3 +422,67 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, exfat_free_buffer(bd, 2); return retval; } + +static int filter_lookup_file(struct exfat_de_iter *de_iter, + void *param, int *dentry_count) +{ + struct exfat_dentry *file_de, *stream_de, *name_de; + __le16 *name; + int retval, name_len; + int i; + + retval = exfat_de_iter_get(de_iter, 0, &file_de); + if (retval || file_de->type != EXFAT_FILE) + return 1; + + retval = exfat_de_iter_get(de_iter, 1, &stream_de); + if (retval || stream_de->type != EXFAT_STREAM) + return 1; + + name = (__le16 *)param; + name_len = (int)exfat_utf16_len(name, PATH_MAX); + + if (file_de->dentry.file.num_ext < + 1 + (name_len + ENTRY_NAME_MAX - 1) / ENTRY_NAME_MAX) + return 1; + + for (i = 2; i <= file_de->dentry.file.num_ext && name_len > 0; i++) { + int len; + + retval = exfat_de_iter_get(de_iter, i, &name_de); + if (retval || name_de->type != EXFAT_NAME) + return 1; + + len = MIN(name_len, ENTRY_NAME_MAX); + if (memcmp(name_de->dentry.name.unicode_0_14, + name, len * 2) != 0) + return 1; + + name += len; + name_len -= len; + } + + *dentry_count = i; + return 0; +} + +int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent, + const char *name, struct exfat_lookup_filter *filter_out) +{ + int retval; + __le16 utf16_name[PATH_MAX + 2] = {0, }; + + retval = (int)exfat_utf16_enc(name, utf16_name, sizeof(utf16_name)); + if (retval < 0) + return retval; + + filter_out->in.type = EXFAT_FILE; + filter_out->in.filter = filter_lookup_file; + filter_out->in.param = utf16_name; + + retval = exfat_lookup_dentry_set(exfat, parent, filter_out); + if (retval < 0) + return retval; + + return 0; +} From 2ea5c975161f468b32eeff1585e32e6fe077415b Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 6 Apr 2021 16:08:01 +0900 Subject: [PATCH 33/57] fsck: add function to create a file Add exfat_create_file() to create a file. Signed-off-by: Hyunchul Lee --- include/exfat_dir.h | 2 + include/exfat_ondisk.h | 6 +- include/libexfat.h | 2 + lib/exfat_dir.c | 143 +++++++++++++++++++++++++++++++++++++++++ lib/libexfat.c | 17 +++++ 5 files changed, 168 insertions(+), 2 deletions(-) diff --git a/include/exfat_dir.h b/include/exfat_dir.h index 109262e6..6ca9c175 100644 --- a/include/exfat_dir.h +++ b/include/exfat_dir.h @@ -58,5 +58,7 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, struct exfat_lookup_filter *filter); int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent, const char *name, struct exfat_lookup_filter *filter_out); +int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent, + const char *name, unsigned short attr); #endif diff --git a/include/exfat_ondisk.h b/include/exfat_ondisk.h index b3fc1fe4..79f0883e 100644 --- a/include/exfat_ondisk.h +++ b/include/exfat_ondisk.h @@ -156,8 +156,10 @@ struct exfat_dentry { __le16 access_date; __u8 create_time_ms; __u8 modify_time_ms; - __u8 access_time_ms; - __u8 reserved2[9]; + __u8 create_tz; + __u8 modify_tz; + __u8 access_tz; + __u8 reserved2[7]; } __attribute__((packed)) file; /* file directory entry */ struct { __u8 flags; diff --git a/include/libexfat.h b/include/libexfat.h index 7ed1d9e7..714f605e 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -145,6 +145,8 @@ int exfat_get_next_clus(struct exfat *exfat, struct exfat_inode *node, int exfat_set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus); off_t exfat_s2o(struct exfat *exfat, off_t sect); off_t exfat_c2o(struct exfat *exfat, unsigned int clus); +int exfat_o2c(struct exfat *exfat, off_t device_offset, + unsigned int *clu, unsigned int *offset); bool exfat_heap_clus(struct exfat *exfat, clus_t clus); /* diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 466c1191..387a1e02 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "exfat_ondisk.h" #include "libexfat.h" @@ -486,3 +487,145 @@ int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent, return 0; } + +static void unix_time_to_exfat_time(time_t unix_time, __u8 *tz, __le16 *date, + __le16 *time, __u8 *time_ms) +{ + struct tm tm; + __u16 t, d; + + gmtime_r(&unix_time, &tm); + d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday; + t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1); + + *tz = 0x80; + *date = cpu_to_le16(d); + *time = cpu_to_le16(t); + if (time_ms) + *time_ms = (tm.tm_sec & 1) * 100; +} + +static __u16 exfat_calc_name_chksum(struct exfat *exfat, __le16 *name, int len) +{ + int i; + __le16 ch; + __u16 chksum = 0; + + for (i = 0; i < len; i++) { + ch = exfat->upcase_table[le16_to_cpu(name[i])]; + ch = cpu_to_le16(ch); + + chksum = ((chksum << 15) | (chksum >> 1)) + (ch & 0xFF); + chksum = ((chksum << 15) | (chksum >> 1)) + (ch >> 8); + } + return chksum; +} + +int exfat_build_file_dentry_set(struct exfat *exfat, const char *name, + unsigned short attr, struct exfat_dentry **dentry_set, + int *dentry_count) +{ + struct exfat_dentry *d_set; + __le16 utf16_name[PATH_MAX + 2]; + int retval; + int d_count, name_len, i; + __le16 e_date, e_time; + __u8 tz, e_time_ms; + + memset(utf16_name, 0, sizeof(utf16_name)); + retval = exfat_utf16_enc(name, utf16_name, sizeof(utf16_name)); + if (retval < 0) + return retval; + + name_len = retval / 2; + d_count = 2 + ((name_len + ENTRY_NAME_MAX - 1) / ENTRY_NAME_MAX); + d_set = calloc(d_count, sizeof(struct exfat_dentry)); + if (!d_set) + return -ENOMEM; + + d_set[0].type = EXFAT_FILE; + d_set[0].dentry.file.num_ext = d_count - 1; + d_set[0].dentry.file.attr = cpu_to_le16(attr); + + unix_time_to_exfat_time(time(NULL), &tz, + &e_date, &e_time, &e_time_ms); + + d_set[0].dentry.file.create_date = e_date; + d_set[0].dentry.file.create_time = e_time; + d_set[0].dentry.file.create_time_ms = e_time_ms; + d_set[0].dentry.file.create_tz = tz; + + d_set[0].dentry.file.modify_date = e_date; + d_set[0].dentry.file.modify_time = e_time; + d_set[0].dentry.file.modify_time_ms = e_time_ms; + d_set[0].dentry.file.modify_tz = tz; + + d_set[0].dentry.file.access_date = e_date; + d_set[0].dentry.file.access_time = e_time; + d_set[0].dentry.file.access_tz = tz; + + d_set[1].type = EXFAT_STREAM; + d_set[1].dentry.stream.flags = 0x01; + d_set[1].dentry.stream.name_len = (__u8)name_len; + d_set[1].dentry.stream.name_hash = + cpu_to_le16(exfat_calc_name_chksum(exfat, utf16_name, name_len)); + + for (i = 2; i < d_count; i++) { + d_set[i].type = EXFAT_NAME; + memcpy(d_set[i].dentry.name.unicode_0_14, + utf16_name + (i - 2) * ENTRY_NAME_MAX * 2, + ENTRY_NAME_MAX * 2); + } + + *dentry_set = d_set; + *dentry_count = d_count; + return 0; +} + +int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent, + const char *name, unsigned short attr) +{ + struct exfat_dentry *dentry_set; + int dentry_count; + int retval; + unsigned int clu, offset, set_len; + struct exfat_lookup_filter filter; + + retval = exfat_lookup_file(exfat, parent, name, + &filter); + if (retval == 0) { + struct exfat_dentry *dentry; + + dentry = filter.out.dentry_set; + if ((le16_to_cpu(dentry->dentry.file.attr) & attr) != attr) + retval = -EEXIST; + + free(filter.out.dentry_set); + return retval; + } + + retval = exfat_build_file_dentry_set(exfat, name, attr, + &dentry_set, &dentry_count); + if (retval < 0) + return retval; + + retval = exfat_o2c(exfat, filter.out.dentry_d_offset, &clu, &offset); + if (retval) + goto out; + + set_len = dentry_count * sizeof(struct exfat_dentry); + if (offset + set_len > exfat->clus_size) { + retval = -ENOSPC; + goto out; + } + + if (exfat_write(exfat->blk_dev->dev_fd, dentry_set, + set_len, filter.out.dentry_d_offset) != + (ssize_t)set_len) { + retval = -EIO; + goto out; + } +out: + free(dentry_set); + return 0; +} diff --git a/lib/libexfat.c b/lib/libexfat.c index 896cecf5..9b79a84d 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -698,6 +698,23 @@ off_t exfat_c2o(struct exfat *exfat, unsigned int clus) exfat->bs->bsx.sect_per_clus_bits)); } +int exfat_o2c(struct exfat *exfat, off_t device_offset, + unsigned int *clu, unsigned int *offset) +{ + off_t heap_offset; + + heap_offset = exfat_s2o(exfat, le32_to_cpu(exfat->bs->bsx.clu_offset)); + if (device_offset < heap_offset) + return -ERANGE; + + *clu = (unsigned int)((device_offset - heap_offset) / + exfat->clus_size) + EXFAT_FIRST_CLUSTER; + if (!exfat_heap_clus(exfat, *clu)) + return -ERANGE; + *offset = (device_offset - heap_offset) % exfat->clus_size; + return 0; +} + bool exfat_heap_clus(struct exfat *exfat, clus_t clus) { return clus >= EXFAT_FIRST_CLUSTER && From 216d0fc5a560a2ac952432288ad2a6f090035e5f Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 12 Apr 2021 15:29:45 +0900 Subject: [PATCH 34/57] fsck: change cluster argument of bitmap operations Change the cluster argument of bitmap operations to start with EXFAT_FIRST_CLUSTER instead of 0. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 28 +++++++++++----------------- include/libexfat.h | 20 +++++++++++++++----- lib/libexfat.c | 5 ++--- mkfs/mkfs.c | 7 ++++--- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index aab59a76..0b14b1a3 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -139,8 +139,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, * 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 (exfat_bitmap_get(exfat->alloc_bitmap, clus)) { if (repair_file_ask(de_iter, node, ER_FILE_DUPLICATED_CLUS, "cluster is already allocated for the other file. truncated to %" @@ -151,9 +150,8 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, return -EINVAL; } - if (!EXFAT_BITMAP_GET(exfat->disk_bitmap, - clus - EXFAT_FIRST_CLUSTER)) { - if (!repair_file_ask(&exfat->de_iter, node, + if (!exfat_bitmap_get(exfat->disk_bitmap, clus)) { + if (!repair_file_ask(de_iter, node, ER_FILE_INVALID_CLUS, "cluster %#x is marked as free", clus)) @@ -182,8 +180,8 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, (count + 1) * exfat->clus_size)) { count++; prev = clus; - EXFAT_BITMAP_SET(exfat->alloc_bitmap, - clus - EXFAT_FIRST_CLUSTER); + exfat_bitmap_set(exfat->alloc_bitmap, + clus); goto truncate_file; } else { return -EINVAL; @@ -192,8 +190,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, } count++; - EXFAT_BITMAP_SET(exfat->alloc_bitmap, - clus - EXFAT_FIRST_CLUSTER); + exfat_bitmap_set(exfat->alloc_bitmap, clus); prev = clus; clus = next; } @@ -248,15 +245,13 @@ static bool root_get_clus_count(struct exfat *exfat, struct exfat_inode *node, return false; } - if (EXFAT_BITMAP_GET(exfat->alloc_bitmap, - clus - EXFAT_FIRST_CLUSTER)) { + if (exfat_bitmap_get(exfat->alloc_bitmap, clus)) { 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); + exfat_bitmap_set(exfat->alloc_bitmap, clus); if (exfat_get_next_clus(exfat, node, clus, &clus) != 0) { exfat_err("/: broken cluster chain\n"); @@ -1027,10 +1022,9 @@ static int write_dirty_fat(struct exfat_fsck *fsck) 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) { + if (!exfat_bitmap_get(exfat->alloc_bitmap, i) && + ((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) / diff --git a/include/libexfat.h b/include/libexfat.h index 714f605e..cac88512 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -101,11 +101,21 @@ typedef __u32 bitmap_t; #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)) + +static inline bool exfat_bitmap_get(char *bmap, clus_t c) +{ + clus_t cc = c - EXFAT_FIRST_CLUSTER; + + return ((bitmap_t *)(bmap))[BIT_ENTRY(cc)] & BIT_MASK(cc); +} + +static inline void exfat_bitmap_set(char *bmap, clus_t c) +{ + clus_t cc = c - EXFAT_FIRST_CLUSTER; + + (((bitmap_t *)(bmap))[BIT_ENTRY(cc)] |= BIT_MASK(cc)); +} + void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, clus_t start_clus, clus_t count); diff --git a/lib/libexfat.c b/lib/libexfat.c index 9b79a84d..d805b50b 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -29,13 +29,12 @@ void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, clus_t clus; if (!exfat_heap_clus(exfat, start_clus) || - !exfat_heap_clus(exfat, start_clus + count)) + !exfat_heap_clus(exfat, start_clus + count - 1)) return; clus = start_clus; while (clus < start_clus + count) { - EXFAT_BITMAP_SET(bitmap, - clus - EXFAT_FIRST_CLUSTER); + exfat_bitmap_set(bitmap, clus); clus++; } } diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index cc1f62dc..511662b5 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -314,12 +314,13 @@ static int exfat_create_bitmap(struct exfat_blk_dev *bd) char *bitmap; unsigned int i, nbytes; - bitmap = calloc(EXFAT_BITMAP_SIZE(finfo.total_clu_cnt), sizeof(*bitmap)); + bitmap = calloc(round_up(finfo.bitmap_byte_len, sizeof(bitmap_t)), + sizeof(*bitmap)); if (!bitmap) return -1; - for (i = 0; i < finfo.used_clu_cnt - EXFAT_FIRST_CLUSTER; i++) - EXFAT_BITMAP_SET(bitmap, i); + for (i = EXFAT_FIRST_CLUSTER; i < finfo.used_clu_cnt; i++) + exfat_bitmap_set(bitmap, i); nbytes = pwrite(bd->dev_fd, bitmap, finfo.bitmap_byte_len, finfo.bitmap_byte_off); if (nbytes != finfo.bitmap_byte_len) { From 896f1bc9f6424a129be53bcac9c0e1d102f42b80 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 12 Apr 2021 15:50:43 +0900 Subject: [PATCH 35/57] fsck: add function to get an FAT entry Add get_next_clus to get an FAT entry. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 4 ++-- include/libexfat.h | 5 +++-- lib/exfat_dir.c | 8 ++++---- lib/libexfat.c | 23 ++++++++++++++++------- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 0b14b1a3..6c71a4a5 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -159,7 +159,7 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, } /* This cluster is allocated or not */ - if (exfat_get_next_clus(exfat, node, clus, &next)) + if (exfat_get_inode_next_clus(exfat, node, clus, &next)) goto truncate_file; if (next == EXFAT_BAD_CLUSTER) { if (repair_file_ask(de_iter, node, @@ -253,7 +253,7 @@ static bool root_get_clus_count(struct exfat *exfat, struct exfat_inode *node, exfat_bitmap_set(exfat->alloc_bitmap, clus); - if (exfat_get_next_clus(exfat, node, clus, &clus) != 0) { + if (exfat_get_inode_next_clus(exfat, node, clus, &clus) != 0) { exfat_err("/: broken cluster chain\n"); return false; } diff --git a/include/libexfat.h b/include/libexfat.h index cac88512..246d4725 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -150,8 +150,9 @@ 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); -int exfat_get_next_clus(struct exfat *exfat, struct exfat_inode *node, - clus_t clus, clus_t *next); +int exfat_get_next_clus(struct exfat *exfat, clus_t clus, clus_t *next); +int exfat_get_inode_next_clus(struct exfat *exfat, struct exfat_inode *node, + clus_t clus, clus_t *next); int exfat_set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus); off_t exfat_s2o(struct exfat *exfat, off_t sect); off_t exfat_c2o(struct exfat *exfat, unsigned int clus); diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 387a1e02..ade39567 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -94,8 +94,8 @@ static int read_ahead_next_blocks(struct exfat_de_iter *iter, ra_clus = clus + 1; if (ra_clus == iter->ra_next_clus && offset >= iter->ra_begin_offset) { - ret = exfat_get_next_clus(exfat, iter->parent, - p_clus, &ra_p_clus); + ret = exfat_get_inode_next_clus(exfat, iter->parent, + p_clus, &ra_p_clus); if (ret) return ret; @@ -186,8 +186,8 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block) desc->p_clus = prev_desc->p_clus; desc->offset = prev_desc->offset + iter->read_size; } else { - ret = exfat_get_next_clus(exfat, iter->parent, - prev_desc->p_clus, &desc->p_clus); + ret = exfat_get_inode_next_clus(exfat, iter->parent, + prev_desc->p_clus, &desc->p_clus); desc->offset = 0; if (ret) return ret; diff --git a/lib/libexfat.c b/lib/libexfat.c index d805b50b..2f8c78ea 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -642,8 +642,7 @@ unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd, (clu - EXFAT_RESERVED_CLUSTERS) * bd->cluster_size; } -int exfat_get_next_clus(struct exfat *exfat, struct exfat_inode *node, - clus_t clus, clus_t *next) +int exfat_get_next_clus(struct exfat *exfat, clus_t clus, clus_t *next) { off_t offset; @@ -652,11 +651,6 @@ int exfat_get_next_clus(struct exfat *exfat, struct exfat_inode *node, if (!exfat_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; @@ -668,6 +662,21 @@ int exfat_get_next_clus(struct exfat *exfat, struct exfat_inode *node, return 0; } +int exfat_get_inode_next_clus(struct exfat *exfat, struct exfat_inode *node, + clus_t clus, clus_t *next) +{ + *next = EXFAT_EOF_CLUSTER; + + if (node->is_contiguous) { + if (!exfat_heap_clus(exfat, clus)) + return -EINVAL; + *next = clus + 1; + return 0; + } + + return exfat_get_next_clus(exfat, clus, next); +} + int exfat_set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus) { off_t offset; From 073f2c33302bf9d9df6d96605f7def02c1ede0b1 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 14:56:53 +0900 Subject: [PATCH 36/57] fsck: add file offset to exfat_lookup_filter Make exfat_lookup_dentry_set store the file offset of a found directory entry to the exfat_lookup_filter strcuture. Signed-off-by: Hyunchul Lee --- include/exfat_dir.h | 4 +++- lib/exfat_dir.c | 40 +++++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/exfat_dir.h b/include/exfat_dir.h index 6ca9c175..930918e9 100644 --- a/include/exfat_dir.h +++ b/include/exfat_dir.h @@ -37,10 +37,11 @@ struct exfat_lookup_filter { struct { struct exfat_dentry *dentry_set; int dentry_count; + off_t file_offset; /* device offset where the dentry_set locates, or * the empty slot locates or EOF if not found. */ - off_t dentry_d_offset; + off_t dev_offset; } out; }; @@ -53,6 +54,7 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter, 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_device_offset(struct exfat_de_iter *iter); +off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter); int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, struct exfat_lookup_filter *filter); diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index ade39567..29bd133e 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -231,6 +231,10 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, iter->buffer_desc = bd; + iter->de_file_offset = 0; + iter->next_read_offset = iter->read_size; + iter->max_skip_dentries = 0; + if (iter->parent->size == 0) return EOF; @@ -240,9 +244,6 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, return -EIO; } - iter->de_file_offset = 0; - iter->next_read_offset = iter->read_size; - iter->max_skip_dentries = 0; return 0; } @@ -333,6 +334,11 @@ off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter) iter->de_file_offset % iter->read_size; } +off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter) +{ + return iter->de_file_offset; +} + /* * try to find the dentry set matched with @filter. this function * doesn't verify the dentry set. @@ -344,7 +350,7 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, { struct buffer_desc *bd = NULL; struct exfat_dentry *dentry = NULL; - off_t free_offset = 0; + off_t free_file_offset = 0, free_dev_offset = 0; struct exfat_de_iter de_iter; int dentry_count; int retval; @@ -401,7 +407,10 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, } else if ((dentry->type == EXFAT_LAST || IS_EXFAT_DELETED(dentry->type))) { if (!last_is_free) { - free_offset = exfat_de_iter_device_offset(&de_iter); + free_file_offset = + exfat_de_iter_file_offset(&de_iter); + free_dev_offset = + exfat_de_iter_device_offset(&de_iter); last_is_free = true; } } else { @@ -412,13 +421,18 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, } out: - if (retval == 0) - filter->out.dentry_d_offset = + if (retval == 0) { + filter->out.file_offset = + exfat_de_iter_file_offset(&de_iter); + filter->out.dev_offset = exfat_de_iter_device_offset(&de_iter); - else if (retval == EOF && last_is_free) - filter->out.dentry_d_offset = free_offset; - else - filter->out.dentry_d_offset = EOF; + } else if (retval == EOF && last_is_free) { + filter->out.file_offset = free_file_offset; + filter->out.dev_offset = free_dev_offset; + } else { + filter->out.file_offset = exfat_de_iter_file_offset(&de_iter); + filter->out.dev_offset = EOF; + } if (bd) exfat_free_buffer(bd, 2); return retval; @@ -609,7 +623,7 @@ int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent, if (retval < 0) return retval; - retval = exfat_o2c(exfat, filter.out.dentry_d_offset, &clu, &offset); + retval = exfat_o2c(exfat, filter.out.dev_offset, &clu, &offset); if (retval) goto out; @@ -620,7 +634,7 @@ int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent, } if (exfat_write(exfat->blk_dev->dev_fd, dentry_set, - set_len, filter.out.dentry_d_offset) != + set_len, filter.out.dev_offset) != (ssize_t)set_len) { retval = -EIO; goto out; From e966e5f3349a2289c08e1dee3489fc717beab583 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 15:22:44 +0900 Subject: [PATCH 37/57] fsck: add function to append file dentry set Add function to append file dentry sets. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 26 +-- include/exfat_dir.h | 16 ++ include/exfat_fs.h | 15 +- include/exfat_ondisk.h | 1 + include/libexfat.h | 8 + lib/exfat_dir.c | 444 +++++++++++++++++++++++++++++++++-------- lib/exfat_fs.c | 12 +- lib/libexfat.c | 17 ++ 8 files changed, 433 insertions(+), 106 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 6c71a4a5..537df667 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -532,38 +532,20 @@ static int exfat_boot_region_check(struct exfat_blk_dev *blkdev, return ret; } -static void dentry_calc_checksum(struct exfat_dentry *dentry, - __le16 *checksum, bool primary) +static uint16_t file_calc_checksum(struct exfat_de_iter *iter) { - 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; + uint16_t 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); + exfat_calc_dentry_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); + exfat_calc_dentry_checksum(de, &checksum, false); } - return checksum; } diff --git a/include/exfat_dir.h b/include/exfat_dir.h index 930918e9..8e55000b 100644 --- a/include/exfat_dir.h +++ b/include/exfat_dir.h @@ -8,6 +8,7 @@ struct exfat; struct exfat_inode; +struct exfat_dentry_loc; struct buffer_desc; struct exfat_de_iter { @@ -60,7 +61,22 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, struct exfat_lookup_filter *filter); int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent, const char *name, struct exfat_lookup_filter *filter_out); + int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent, const char *name, unsigned short attr); +int exfat_update_file_dentry_set(struct exfat *exfat, + struct exfat_dentry *dset, int dcount, + const char *name, + clus_t start_clu, clus_t ccount); +int exfat_build_file_dentry_set(struct exfat *exfat, const char *name, + unsigned short attr, struct exfat_dentry **dentry_set, + int *dentry_count); +int exfat_add_dentry_set(struct exfat *exfat, struct exfat_dentry_loc *loc, + struct exfat_dentry *dset, int dcount, + bool need_next_loc); +void exfat_calc_dentry_checksum(struct exfat_dentry *dentry, + uint16_t *checksum, bool primary); +uint16_t exfat_calc_name_hash(struct exfat *exfat, + __le16 *name, int len); #endif diff --git a/include/exfat_fs.h b/include/exfat_fs.h index 820dbe68..47b3253f 100644 --- a/include/exfat_fs.h +++ b/include/exfat_fs.h @@ -7,17 +7,20 @@ #include "list.h" +struct exfat_dentry; + 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; + struct exfat_dentry *dentry_set; + int dentry_count; + off_t dev_offset; __le16 name[0]; /* only for directory */ }; @@ -38,6 +41,14 @@ struct exfat { clus_t disk_bitmap_clus; unsigned int disk_bitmap_size; __u16 *upcase_table; + clus_t start_clu; + char *zero_cluster; +}; + +struct exfat_dentry_loc { + struct exfat_inode *parent; + off_t file_offset; + off_t dev_offset; }; struct path_resolve_ctx { diff --git a/include/exfat_ondisk.h b/include/exfat_ondisk.h index 79f0883e..d1786bf0 100644 --- a/include/exfat_ondisk.h +++ b/include/exfat_ondisk.h @@ -39,6 +39,7 @@ #define DENTRY_SIZE_BITS 5 /* exFAT allows 8388608(256MB) directory entries */ #define MAX_EXFAT_DENTRIES 8388608 +#define MIN_FILE_DENTRIES 3 /* dentry types */ #define MSDOS_DELETED 0xE5 /* deleted mark */ diff --git a/include/libexfat.h b/include/libexfat.h index 246d4725..ec57eb13 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -116,8 +116,16 @@ static inline void exfat_bitmap_set(char *bmap, clus_t c) (((bitmap_t *)(bmap))[BIT_ENTRY(cc)] |= BIT_MASK(cc)); } +static inline void exfat_bitmap_clear(char *bmap, clus_t c) +{ + clus_t cc = c - EXFAT_FIRST_CLUSTER; + (((bitmap_t *)(bmap))[BIT_ENTRY(cc)] &= ~BIT_MASK(cc)); +} + void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, clus_t start_clus, clus_t count); +int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap, + clus_t start_clu, clus_t *next); void show_version(void); diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 29bd133e..55f1eb6c 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -502,28 +502,43 @@ int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent, return 0; } -static void unix_time_to_exfat_time(time_t unix_time, __u8 *tz, __le16 *date, - __le16 *time, __u8 *time_ms) +void exfat_calc_dentry_checksum(struct exfat_dentry *dentry, + uint16_t *checksum, bool primary) { - struct tm tm; - __u16 t, d; + unsigned int i; + uint8_t *bytes; - gmtime_r(&unix_time, &tm); - d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday; - t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1); + bytes = (uint8_t *)dentry; - *tz = 0x80; - *date = cpu_to_le16(d); - *time = cpu_to_le16(t); - if (time_ms) - *time_ms = (tm.tm_sec & 1) * 100; + *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 uint16_t calc_dentry_set_checksum(struct exfat_dentry *dset, int dcount) +{ + uint16_t checksum; + int i; + + if (dcount < MIN_FILE_DENTRIES) + return 0; + + checksum = 0; + exfat_calc_dentry_checksum(&dset[0], &checksum, true); + for (i = 1; i < dcount; i++) + exfat_calc_dentry_checksum(&dset[i], &checksum, false); + return checksum; } -static __u16 exfat_calc_name_chksum(struct exfat *exfat, __le16 *name, int len) +uint16_t exfat_calc_name_hash(struct exfat *exfat, + __le16 *name, int len) { int i; __le16 ch; - __u16 chksum = 0; + uint16_t chksum = 0; for (i = 0; i < len; i++) { ch = exfat->upcase_table[le16_to_cpu(name[i])]; @@ -535,14 +550,31 @@ static __u16 exfat_calc_name_chksum(struct exfat *exfat, __le16 *name, int len) return chksum; } +static void unix_time_to_exfat_time(time_t unix_time, __u8 *tz, __le16 *date, + __le16 *time, __u8 *time_ms) +{ + struct tm tm; + __u16 t, d; + + gmtime_r(&unix_time, &tm); + d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday; + t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1); + + *tz = 0x80; + *date = cpu_to_le16(d); + *time = cpu_to_le16(t); + if (time_ms) + *time_ms = (tm.tm_sec & 1) * 100; +} + int exfat_build_file_dentry_set(struct exfat *exfat, const char *name, unsigned short attr, struct exfat_dentry **dentry_set, int *dentry_count) { - struct exfat_dentry *d_set; + struct exfat_dentry *dset; __le16 utf16_name[PATH_MAX + 2]; int retval; - int d_count, name_len, i; + int dcount, name_len, i; __le16 e_date, e_time; __u8 tz, e_time_ms; @@ -552,94 +584,344 @@ int exfat_build_file_dentry_set(struct exfat *exfat, const char *name, return retval; name_len = retval / 2; - d_count = 2 + ((name_len + ENTRY_NAME_MAX - 1) / ENTRY_NAME_MAX); - d_set = calloc(d_count, sizeof(struct exfat_dentry)); - if (!d_set) + dcount = 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX); + dset = calloc(1, dcount * DENTRY_SIZE); + if (!dset) return -ENOMEM; - d_set[0].type = EXFAT_FILE; - d_set[0].dentry.file.num_ext = d_count - 1; - d_set[0].dentry.file.attr = cpu_to_le16(attr); + dset[0].type = EXFAT_FILE; + dset[0].dentry.file.num_ext = dcount - 1; + dset[0].dentry.file.attr = cpu_to_le16(attr); unix_time_to_exfat_time(time(NULL), &tz, &e_date, &e_time, &e_time_ms); - d_set[0].dentry.file.create_date = e_date; - d_set[0].dentry.file.create_time = e_time; - d_set[0].dentry.file.create_time_ms = e_time_ms; - d_set[0].dentry.file.create_tz = tz; - - d_set[0].dentry.file.modify_date = e_date; - d_set[0].dentry.file.modify_time = e_time; - d_set[0].dentry.file.modify_time_ms = e_time_ms; - d_set[0].dentry.file.modify_tz = tz; - - d_set[0].dentry.file.access_date = e_date; - d_set[0].dentry.file.access_time = e_time; - d_set[0].dentry.file.access_tz = tz; - - d_set[1].type = EXFAT_STREAM; - d_set[1].dentry.stream.flags = 0x01; - d_set[1].dentry.stream.name_len = (__u8)name_len; - d_set[1].dentry.stream.name_hash = - cpu_to_le16(exfat_calc_name_chksum(exfat, utf16_name, name_len)); - - for (i = 2; i < d_count; i++) { - d_set[i].type = EXFAT_NAME; - memcpy(d_set[i].dentry.name.unicode_0_14, - utf16_name + (i - 2) * ENTRY_NAME_MAX * 2, + dset[0].dentry.file.create_date = e_date; + dset[0].dentry.file.create_time = e_time; + dset[0].dentry.file.create_time_ms = e_time_ms; + dset[0].dentry.file.create_tz = tz; + + dset[0].dentry.file.modify_date = e_date; + dset[0].dentry.file.modify_time = e_time; + dset[0].dentry.file.modify_time_ms = e_time_ms; + dset[0].dentry.file.modify_tz = tz; + + dset[0].dentry.file.access_date = e_date; + dset[0].dentry.file.access_time = e_time; + dset[0].dentry.file.access_tz = tz; + + dset[1].type = EXFAT_STREAM; + dset[1].dentry.stream.flags = 0x01; + dset[1].dentry.stream.name_len = (__u8)name_len; + dset[1].dentry.stream.name_hash = + cpu_to_le16(exfat_calc_name_hash(exfat, utf16_name, name_len)); + + for (i = 2; i < dcount; i++) { + dset[i].type = EXFAT_NAME; + memcpy(dset[i].dentry.name.unicode_0_14, + utf16_name + (i - 2) * ENTRY_NAME_MAX, ENTRY_NAME_MAX * 2); } - *dentry_set = d_set; - *dentry_count = d_count; + dset[0].dentry.file.checksum = + cpu_to_le16(calc_dentry_set_checksum(dset, dcount)); + + *dentry_set = dset; + *dentry_count = dcount; return 0; } -int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent, - const char *name, unsigned short attr) +int exfat_update_file_dentry_set(struct exfat *exfat, + struct exfat_dentry *dset, int dcount, + const char *name, + clus_t start_clu, clus_t ccount) { - struct exfat_dentry *dentry_set; - int dentry_count; - int retval; - unsigned int clu, offset, set_len; - struct exfat_lookup_filter filter; + int i, name_len; + __le16 utf16_name[PATH_MAX + 2]; - retval = exfat_lookup_file(exfat, parent, name, - &filter); - if (retval == 0) { - struct exfat_dentry *dentry; + if (dset[0].type != EXFAT_FILE || dcount < MIN_FILE_DENTRIES) + return -EINVAL; + + if (name) { + name_len = (int)exfat_utf16_enc(name, + utf16_name, sizeof(utf16_name)); + if (name_len < 0) + return name_len; + + name_len /= 2; + if (dcount != 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX)) + return -EINVAL; + + dset[1].dentry.stream.name_len = (__u8)name_len; + dset[1].dentry.stream.name_hash = + exfat_calc_name_hash(exfat, utf16_name, name_len); + + for (i = 2; i < dcount; i++) { + dset[i].type = EXFAT_NAME; + memcpy(dset[i].dentry.name.unicode_0_14, + utf16_name + (i - 2) * ENTRY_NAME_MAX, + ENTRY_NAME_MAX * 2); + } + } - dentry = filter.out.dentry_set; - if ((le16_to_cpu(dentry->dentry.file.attr) & attr) != attr) - retval = -EEXIST; + dset[1].dentry.stream.valid_size = cpu_to_le64(ccount * exfat->clus_size); + dset[1].dentry.stream.size = cpu_to_le64(ccount * exfat->clus_size); + if (start_clu) + dset[1].dentry.stream.start_clu = cpu_to_le32(start_clu); - free(filter.out.dentry_set); - return retval; + dset[0].dentry.file.checksum = + cpu_to_le16(calc_dentry_set_checksum(dset, dcount)); + return 0; +} + +static int find_free_cluster(struct exfat *exfat, + clus_t start, clus_t *new_clu) +{ + clus_t end = le32_to_cpu(exfat->bs->bsx.clu_count) + + EXFAT_FIRST_CLUSTER; + + if (!exfat_heap_clus(exfat, start)) + return -EINVAL; + + while (start < end) { + if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap, + start, new_clu)) + break; + if (!exfat_bitmap_get(exfat->disk_bitmap, *new_clu)) + return 0; + start = *new_clu + 1; } - retval = exfat_build_file_dentry_set(exfat, name, attr, - &dentry_set, &dentry_count); - if (retval < 0) - return retval; + end = start; + start = EXFAT_FIRST_CLUSTER; + while (start < end) { + if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap, + start, new_clu)) + goto out_nospc; + if (!exfat_bitmap_get(exfat->disk_bitmap, *new_clu)) + return 0; + start = *new_clu + 1; + } - retval = exfat_o2c(exfat, filter.out.dev_offset, &clu, &offset); - if (retval) - goto out; +out_nospc: + *new_clu = EXFAT_EOF_CLUSTER; + return -ENOSPC; +} - set_len = dentry_count * sizeof(struct exfat_dentry); - if (offset + set_len > exfat->clus_size) { - retval = -ENOSPC; - goto out; +static int exfat_map_cluster(struct exfat *exfat, struct exfat_inode *inode, + off_t file_off, clus_t *mapped_clu) +{ + clus_t clu, next, count, last_count; + + if (!exfat_heap_clus(exfat, inode->first_clus)) + return -EINVAL; + + clu = inode->first_clus; + next = EXFAT_EOF_CLUSTER; + count = 1; + if (file_off == EOF) + last_count = DIV_ROUND_UP(inode->size, exfat->clus_size); + else + last_count = file_off / exfat->clus_size + 1; + + while (true) { + if (count * exfat->clus_size > inode->size) + return -EINVAL; + + if (count == last_count) { + *mapped_clu = clu; + return 0; + } + + if (exfat_get_inode_next_clus(exfat, inode, clu, &next)) + return -EINVAL; + + if (!exfat_heap_clus(exfat, clu)) + return -EINVAL; + + clu = next; + count++; + } + return -EINVAL; +} + +static int exfat_write_dentry_set(struct exfat *exfat, + struct exfat_dentry *dset, int dcount, + off_t dev_off, off_t *next_dev_off) +{ + clus_t clus; + unsigned int clus_off, dent_len, first_half_len, sec_half_len; + off_t first_half_off, sec_half_off = 0; + + if (exfat_o2c(exfat, dev_off, &clus, &clus_off)) + return -ERANGE; + + dent_len = dcount * DENTRY_SIZE; + first_half_len = MIN(dent_len, exfat->clus_size - clus_off); + sec_half_len = dent_len - first_half_len; + + first_half_off = dev_off; + if (sec_half_len) { + clus_t next_clus; + + if (exfat_get_next_clus(exfat, clus, &next_clus)) + return -EIO; + if (!exfat_heap_clus(exfat, next_clus)) + return -EINVAL; + sec_half_off = exfat_c2o(exfat, next_clus); + } + + if (exfat_write(exfat->blk_dev->dev_fd, dset, first_half_len, + first_half_off) != (ssize_t)first_half_len) + return -EIO; + + if (sec_half_len) { + dset = (struct exfat_dentry *)((char *)dset + first_half_len); + if (exfat_write(exfat->blk_dev->dev_fd, dset, sec_half_len, + sec_half_off) != (ssize_t)sec_half_len) + return -EIO; + } + + if (next_dev_off) { + if (sec_half_len) + *next_dev_off = sec_half_off + sec_half_len; + else + *next_dev_off = first_half_off + first_half_len; } + return 0; +} + +static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode, + clus_t *new_clu) +{ + clus_t last_clu; + int err; + bool need_dset = inode != exfat->root; - if (exfat_write(exfat->blk_dev->dev_fd, dentry_set, - set_len, filter.out.dev_offset) != - (ssize_t)set_len) { - retval = -EIO; + if ((need_dset && !inode->dentry_set) || inode->is_contiguous) + return -EINVAL; + + err = find_free_cluster(exfat, exfat->start_clu, new_clu); + if (err) { + exfat->start_clu = EXFAT_FIRST_CLUSTER; + exfat_err("failed to find an free cluster\n"); + return -ENOSPC; + } + exfat->start_clu = *new_clu; + + if (exfat_set_fat(exfat, *new_clu, EXFAT_EOF_CLUSTER)) + return -EIO; + + /* zero out the new cluster */ + if (exfat_write(exfat->blk_dev->dev_fd, exfat->zero_cluster, + exfat->clus_size, exfat_c2o(exfat, *new_clu)) != + (ssize_t)exfat->clus_size) { + exfat_err("failed to fill new cluster with zeroes\n"); + return -EIO; + } + + if (inode->size) { + err = exfat_map_cluster(exfat, inode, EOF, &last_clu); + if (err) { + exfat_err("failed to get the last cluster\n"); + return err; + } + + if (exfat_set_fat(exfat, last_clu, *new_clu)) + return -EIO; + + if (need_dset) { + err = exfat_update_file_dentry_set(exfat, + inode->dentry_set, + inode->dentry_count, + NULL, 0, + DIV_ROUND_UP(inode->size, + exfat->clus_size) + 1); + if (err) + return -EINVAL; + } + } else { + if (need_dset) { + err = exfat_update_file_dentry_set(exfat, + inode->dentry_set, + inode->dentry_count, + NULL, *new_clu, 1); + if (err) + return -EINVAL; + } + } + + if (need_dset && exfat_write_dentry_set(exfat, inode->dentry_set, + inode->dentry_count, + inode->dev_offset, NULL)) + return -EIO; + + exfat_bitmap_set(exfat->alloc_bitmap, *new_clu); + if (inode->size == 0) + inode->first_clus = *new_clu; + inode->size += exfat->clus_size; + return 0; +} + +int exfat_add_dentry_set(struct exfat *exfat, struct exfat_dentry_loc *loc, + struct exfat_dentry *dset, int dcount, + bool need_next_loc) +{ + struct exfat_inode *parent = loc->parent; + off_t dev_off, next_dev_off; + + if (parent->is_contiguous || + (uint64_t)loc->file_offset > parent->size || + (unsigned int)dcount * DENTRY_SIZE > exfat->clus_size) + return -EINVAL; + + dev_off = loc->dev_offset; + if ((uint64_t)loc->file_offset + dcount * DENTRY_SIZE > parent->size) { + clus_t new_clus; + + if (exfat_alloc_cluster(exfat, parent, &new_clus)) + return -EIO; + if ((uint64_t)loc->file_offset == parent->size - exfat->clus_size) + dev_off = exfat_c2o(exfat, new_clus); + } + + if (exfat_write_dentry_set(exfat, dset, dcount, dev_off, &next_dev_off)) + return -EIO; + + if (need_next_loc) { + loc->file_offset += dcount * DENTRY_SIZE; + loc->dev_offset = next_dev_off; + } + return 0; +} + +int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent, + const char *name, unsigned short attr) +{ + struct exfat_dentry *dset; + int err, dcount; + struct exfat_lookup_filter filter; + struct exfat_dentry_loc loc; + + err = exfat_lookup_file(exfat, parent, name, &filter); + if (err == 0) { + dset = filter.out.dentry_set; + dcount = filter.out.dentry_count; + if ((le16_to_cpu(dset->dentry.file.attr) & attr) != attr) + err = -EEXIST; goto out; } + + err = exfat_build_file_dentry_set(exfat, name, attr, + &dset, &dcount); + if (err) + return err; + + loc.parent = parent; + loc.file_offset = filter.out.file_offset; + loc.dev_offset = filter.out.dev_offset; + err = exfat_add_dentry_set(exfat, &loc, dset, dcount, false); out: - free(dentry_set); - return 0; + free(dset); + return err; } diff --git a/lib/exfat_fs.c b/lib/exfat_fs.c index 93476146..c5ebe8bd 100644 --- a/lib/exfat_fs.c +++ b/lib/exfat_fs.c @@ -31,13 +31,14 @@ struct exfat_inode *exfat_alloc_inode(__u16 attr) INIT_LIST_HEAD(&node->sibling); INIT_LIST_HEAD(&node->list); - node->last_pclus = EXFAT_EOF_CLUSTER; node->attr = attr; return node; } void exfat_free_inode(struct exfat_inode *node) { + if (node->dentry_set) + free(node->dentry_set); free(node); } @@ -108,6 +109,8 @@ void exfat_free_exfat(struct exfat *exfat) free(exfat->alloc_bitmap); if (exfat->disk_bitmap) free(exfat->disk_bitmap); + if (exfat->zero_cluster) + free(exfat->zero_cluster); free(exfat); } } @@ -141,6 +144,13 @@ struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs) goto err; } + exfat->zero_cluster = calloc(1, exfat->clus_size); + if (!exfat->zero_cluster) { + exfat_err("failed to allocate a zero-filled cluster buffer\n"); + goto err; + } + + exfat->start_clu = EXFAT_FIRST_CLUSTER; return exfat; err: exfat_free_exfat(exfat); diff --git a/lib/libexfat.c b/lib/libexfat.c index 2f8c78ea..b093eb60 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -39,6 +39,23 @@ void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, } } +int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap, + clus_t start_clu, clus_t *next) +{ + clus_t last_clu; + + last_clu = le32_to_cpu(exfat->bs->bsx.clu_count) + + EXFAT_FIRST_CLUSTER; + while (start_clu < last_clu) { + if (!exfat_bitmap_get(bmap, start_clu)) { + *next = start_clu; + return 0; + } + start_clu++; + } + return 1; +} + wchar_t exfat_bad_char(wchar_t w) { return (w < 0x0020) From 30bde40a04c1f2507fd11e6461a88fedce0d8922 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 4 Jun 2021 10:13:44 +0900 Subject: [PATCH 38/57] fsck: change repair message format Add the device offset of a corrupted directory entry set to the end of the repair message. And fix the path of root directory, "/" isn't shown in error messages Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 13 ++++++++----- lib/exfat_fs.c | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 537df667..d40a4702 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -90,9 +90,10 @@ static void usage(char *name) exfat_resolve_path_parent(&path_resolve_ctx, \ (iter)->parent, inode); \ exfat_repair_ask(&exfat_fsck, code, \ - "ERROR: %s: " fmt, \ - path_resolve_ctx.local_path, \ - ##__VA_ARGS__); \ + "ERROR: %s: " fmt " at %#" PRIx64, \ + path_resolve_ctx.local_path, \ + ##__VA_ARGS__, \ + exfat_de_iter_device_offset(iter)); \ }) static int check_clus_chain(struct exfat_de_iter *de_iter, @@ -115,7 +116,9 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, if ((node->size == 0 && node->first_clus != EXFAT_FREE_CLUSTER) || (node->size > 0 && !exfat_heap_clus(exfat, node->first_clus))) { if (repair_file_ask(de_iter, node, - ER_FILE_FIRST_CLUS, "first cluster is wrong")) + ER_FILE_FIRST_CLUS, + "size %#" PRIx64 ", but the first cluster %#x", + node->size, node->first_clus)) goto truncate_file; else return -EINVAL; @@ -575,7 +578,7 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node) if (node->size == 0 && node->is_contiguous) { if (repair_file_ask(iter, node, ER_FILE_ZERO_NOFAT, - "empty, but has no Fat chain\n")) { + "empty, but has no Fat chain")) { exfat_de_iter_get_dirty(iter, 1, &dentry); dentry->stream_flags &= ~EXFAT_SF_CONTIGUOUS; ret = 1; diff --git a/lib/exfat_fs.c b/lib/exfat_fs.c index c5ebe8bd..e9615474 100644 --- a/lib/exfat_fs.c +++ b/lib/exfat_fs.c @@ -263,7 +263,7 @@ int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child) utf16_path++; } - if (depth > 0) + if (depth > 1) utf16_path--; memcpy((char *)utf16_path, &utf16_null, sizeof(utf16_null)); utf16_path++; From d6c8a3fa08f0b1d46bb1026aa2fd65a62cbabcd0 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 16:12:07 +0900 Subject: [PATCH 39/57] fsck: Add rescue option to assign orphaned cluster to files If the "s" option is given, fsck tries to create files in LOST+FOUND and assigns orphaned clusters which are marked as allocated but does not belong to any files to theses files. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 307 ++++++++++++++++++++++++++++----------------- fsck/fsck.h | 1 + include/exfat_fs.h | 3 +- include/libexfat.h | 2 + lib/exfat_fs.c | 30 +++-- lib/libexfat.c | 21 +++- 6 files changed, 234 insertions(+), 130 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index d40a4702..1e1d84d8 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -52,6 +52,7 @@ static struct option opts[] = { {"repair-yes", no_argument, NULL, 'y' }, {"repair-no", no_argument, NULL, 'n' }, {"repair-auto", no_argument, NULL, 'p' }, + {"rescue", no_argument, NULL, 's' }, {"version", no_argument, NULL, 'V' }, {"verbose", no_argument, NULL, 'v' }, {"help", no_argument, NULL, 'h' }, @@ -69,6 +70,7 @@ static void usage(char *name) fprintf(stderr, "\t-p | --repair-auto Repair automatically\n"); fprintf(stderr, "\t-a Repair automatically\n"); fprintf(stderr, "\t-b | --ignore-bad-fs Try to recover even if exfat is not found\n"); + fprintf(stderr, "\t-s | --rescue Assign orphaned clusters to files\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"); @@ -975,126 +977,45 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) return ret; } -static int write_dirty_fat(struct exfat_fsck *fsck) +/* write bitmap segments for clusters which are marked + * as free, but allocated to files. + */ +static int write_bitmap(struct exfat_fsck *fsck) { struct exfat *exfat = fsck->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 = fsck->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; - } + bitmap_t *disk_b, *alloc_b, *ohead_b; + off_t dev_offset; + unsigned int i, bitmap_bytes, byte_offset, write_bytes; - /* TODO: read ahead */ - - for (i = clus ? clus : EXFAT_FIRST_CLUSTER; - i < clus + clus_count; i++) { - if (!exfat_bitmap_get(exfat->alloc_bitmap, i) && - ((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; - } - } + dev_offset = exfat_c2o(exfat, exfat->disk_bitmap_clus); + bitmap_bytes = EXFAT_BITMAP_SIZE(le32_to_cpu(exfat->bs->bsx.clu_count)); - 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; + disk_b = (bitmap_t *)exfat->disk_bitmap; + alloc_b = (bitmap_t *)exfat->alloc_bitmap; + ohead_b = (bitmap_t *)exfat->ohead_bitmap; - } - bd[idx].dirty[i / write_size] = false; - } + for (i = 0; i < bitmap_bytes / sizeof(bitmap_t); i++) + ohead_b[i] = alloc_b[i] | disk_b[i]; + + i = 0; + while (i < bitmap_bytes / sizeof(bitmap_t)) { + if (ohead_b[i] == disk_b[i]) { + i++; + continue; } - idx ^= 0x01; - clus = clus + clus_count; - offset += len; - } - return 0; -} + byte_offset = ((i * sizeof(bitmap_t)) / 512) * 512; + write_bytes = MIN(512, bitmap_bytes - byte_offset); -static int write_dirty_bitmap(struct exfat_fsck *fsck) -{ - struct exfat *exfat = fsck->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 = fsck->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) + if (exfat_write(exfat->blk_dev->dev_fd, + (char *)ohead_b + byte_offset, write_bytes, + dev_offset + byte_offset) != (ssize_t)write_bytes) 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; + i = (byte_offset + write_bytes) / sizeof(bitmap_t); } return 0; -} -static int reclaim_free_clusters(struct exfat_fsck *fsck) -{ - if (write_dirty_fat(fsck)) { - exfat_err("failed to write fat entries\n"); - return -EIO; - } - if (write_dirty_bitmap(fsck)) { - exfat_err("failed to write bitmap\n"); - return -EIO; - } - return 0; } /* @@ -1143,9 +1064,6 @@ static int exfat_filesystem_check(struct exfat_fsck *fsck) } out: exfat_free_dir_list(exfat); - exfat->root = NULL; - if (fsck->dirty_fat && reclaim_free_clusters(fsck)) - return -EIO; return ret; } @@ -1153,12 +1071,11 @@ static int exfat_root_dir_check(struct exfat *exfat) { struct exfat_inode *root; clus_t clus_count; + int err; root = exfat_alloc_inode(ATTR_SUBDIR); - if (!root) { - exfat_err("failed to allocate memory\n"); + if (!root) return -ENOMEM; - } root->first_clus = le32_to_cpu(exfat->bs->bsx.root_cluster); if (!root_get_clus_count(exfat, root, &clus_count)) { @@ -1176,18 +1093,139 @@ static int exfat_root_dir_check(struct exfat *exfat) if (read_volume_label(exfat)) exfat_err("failed to read volume label\n"); - if (read_bitmap(exfat)) { + err = read_bitmap(exfat); + if (err) { exfat_err("failed to read bitmap\n"); return -EINVAL; } - if (read_upcase_table(exfat)) { + err = read_upcase_table(exfat); + if (err) { exfat_err("failed to read upcase table\n"); return -EINVAL; } + + root->dev_offset = 0; + err = exfat_build_file_dentry_set(exfat, " ", ATTR_SUBDIR, + &root->dentry_set, &root->dentry_count); + if (err) { + exfat_free_inode(root); + return -ENOMEM; + } + return 0; +} + +static int read_lostfound(struct exfat *exfat, struct exfat_inode **lostfound) +{ + struct exfat_lookup_filter filter; + struct exfat_inode *inode; + int err; + + err = exfat_lookup_file(exfat, exfat->root, "LOST+FOUND", &filter); + if (err) + return err; + + inode = exfat_alloc_inode(ATTR_SUBDIR); + if (!inode) { + free(filter.out.dentry_set); + return -ENOMEM; + } + + inode->dentry_set = filter.out.dentry_set; + inode->dentry_count = filter.out.dentry_count; + inode->dev_offset = filter.out.dev_offset; + + inode->first_clus = + le32_to_cpu(filter.out.dentry_set[1].dentry.stream.start_clu); + inode->size = + le64_to_cpu(filter.out.dentry_set[1].dentry.stream.size); + + *lostfound = inode; return 0; } +/* Create temporary files under LOST+FOUND and assign orphan + * chains of clusters to these files. + */ +static int rescue_orphan_clusters(struct exfat_fsck *fsck) +{ + struct exfat *exfat = fsck->exfat; + struct exfat_inode *lostfound; + bitmap_t *disk_b, *alloc_b, *ohead_b; + struct exfat_dentry *dset; + clus_t clu_count, clu, s_clu, e_clu; + int err, dcount; + unsigned int i; + char name[] = "FILE0000000.CHK"; + struct exfat_dentry_loc loc; + struct exfat_lookup_filter lf = { + .in.type = EXFAT_INVAL, + .in.filter = NULL, + }; + + err = read_lostfound(exfat, &lostfound); + if (err) { + exfat_err("failed to find LOST+FOUND\n"); + return err; + } + + /* get the last empty region of LOST+FOUND */ + err = exfat_lookup_dentry_set(exfat, lostfound, &lf); + if (err && err != EOF) { + exfat_err("failed to find the last empty slot in LOST+FOUND\n"); + goto out; + } + + loc.parent = lostfound; + loc.file_offset = lf.out.file_offset; + loc.dev_offset = lf.out.dev_offset; + + /* build a template dentry set */ + err = exfat_build_file_dentry_set(exfat, name, 0, &dset, &dcount); + if (err) { + exfat_err("failed to create a temporary file in LOST+FOUNDn"); + goto out; + } + dset[1].dentry.stream.flags |= EXFAT_SF_CONTIGUOUS; + + clu_count = le32_to_cpu(exfat->bs->bsx.clu_count); + + /* find clusters which are not marked as free, but not allocated to + * any files. + */ + disk_b = (bitmap_t *)exfat->disk_bitmap; + alloc_b = (bitmap_t *)exfat->alloc_bitmap; + ohead_b = (bitmap_t *)exfat->ohead_bitmap; + for (i = 0; i < EXFAT_BITMAP_SIZE(clu_count) / sizeof(bitmap_t); i++) + ohead_b[i] = disk_b[i] & ~alloc_b[i]; + + /* create temporary files and allocate contiguous orphan clusters + * to each file. + */ + for (clu = EXFAT_FIRST_CLUSTER; clu < clu_count + EXFAT_FIRST_CLUSTER && + exfat_bitmap_find_one(exfat, exfat->ohead_bitmap, clu, &s_clu) == 0;) { + if (exfat_bitmap_find_zero(exfat, exfat->ohead_bitmap, s_clu, &e_clu)) + e_clu = clu_count + EXFAT_FIRST_CLUSTER; + clu = e_clu; + + snprintf(name, sizeof(name), "FILE%07d.CHK", + (unsigned int)(loc.file_offset >> 5)); + err = exfat_update_file_dentry_set(exfat, dset, dcount, + name, s_clu, e_clu - s_clu); + if (err) + continue; + err = exfat_add_dentry_set(exfat, &loc, dset, dcount, true); + if (err) + continue; + } + + free(dset); + err = 0; +out: + exfat_free_inode(lostfound); + return err; +} + static char *bytes_to_human_readable(size_t bytes) { static const char * const units[] = {"B", "KB", "MB", "GB", "TB", "PB"}; @@ -1251,7 +1289,7 @@ int main(int argc, char * const argv[]) exfat_err("failed to init locale/codeset\n"); opterr = 0; - while ((c = getopt_long(argc, argv, "arynpbVvh", opts, NULL)) != EOF) { + while ((c = getopt_long(argc, argv, "arynpbsVvh", opts, NULL)) != EOF) { switch (c) { case 'n': if (ui.options & FSCK_OPTS_REPAIR_ALL) @@ -1277,6 +1315,9 @@ int main(int argc, char * const argv[]) case 'b': ui.options |= FSCK_OPTS_IGNORE_BAD_FS_NAME; break; + case 's': + ui.options |= FSCK_OPTS_RESCUE_CLUS; + break; case 'V': version_only = true; break; @@ -1300,7 +1341,8 @@ int main(int argc, char * const argv[]) if (ui.options & FSCK_OPTS_REPAIR_WRITE) ui.ei.writeable = true; else { - if (ui.options & FSCK_OPTS_IGNORE_BAD_FS_NAME) + if (ui.options & (FSCK_OPTS_IGNORE_BAD_FS_NAME | + FSCK_OPTS_RESCUE_CLUS)) usage(argv[0]); ui.options |= FSCK_OPTS_REPAIR_NO; ui.ei.writeable = false; @@ -1348,11 +1390,42 @@ int main(int argc, char * const argv[]) goto out; } + if (exfat_fsck.options & FSCK_OPTS_RESCUE_CLUS) { + ret = exfat_create_file(exfat_fsck.exfat, + exfat_fsck.exfat->root, + "LOST+FOUND", + ATTR_SUBDIR); + if (ret) { + exfat_err("failed to create lost+found directory\n"); + goto out; + } + + if (fsync(exfat_fsck.exfat->blk_dev->dev_fd) != 0) { + ret = -EIO; + exfat_err("failed to sync()\n"); + goto out; + } + } + exfat_debug("verifying directory entries...\n"); ret = exfat_filesystem_check(&exfat_fsck); if (ret) goto out; + if (exfat_fsck.options & FSCK_OPTS_RESCUE_CLUS) { + rescue_orphan_clusters(&exfat_fsck); + exfat_fsck.dirty = true; + exfat_fsck.dirty_fat = true; + } + + if (exfat_fsck.options & FSCK_OPTS_REPAIR_WRITE) { + ret = write_bitmap(&exfat_fsck); + if (ret) { + exfat_err("failed to write bitmap\n"); + goto out; + } + } + if (ui.ei.writeable && fsync(bd.dev_fd)) { exfat_err("failed to sync\n"); ret = -EIO; diff --git a/fsck/fsck.h b/fsck/fsck.h index 34a695a5..53003f60 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -15,6 +15,7 @@ enum fsck_ui_options { FSCK_OPTS_REPAIR_WRITE = 0x0b, FSCK_OPTS_REPAIR_ALL = 0x0f, FSCK_OPTS_IGNORE_BAD_FS_NAME = 0x10, + FSCK_OPTS_RESCUE_CLUS = 0x20, }; struct exfat; diff --git a/include/exfat_fs.h b/include/exfat_fs.h index 47b3253f..c1cfff07 100644 --- a/include/exfat_fs.h +++ b/include/exfat_fs.h @@ -36,8 +36,9 @@ struct exfat { clus_t clus_count; unsigned int clus_size; unsigned int sect_size; - char *alloc_bitmap; char *disk_bitmap; + char *alloc_bitmap; + char *ohead_bitmap; clus_t disk_bitmap_clus; unsigned int disk_bitmap_size; __u16 *upcase_table; diff --git a/include/libexfat.h b/include/libexfat.h index ec57eb13..b98f2b6b 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -126,6 +126,8 @@ void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, clus_t start_clus, clus_t count); int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap, clus_t start_clu, clus_t *next); +int exfat_bitmap_find_one(struct exfat *exfat, char *bmap, + clus_t start_clu, clus_t *next); void show_version(void); diff --git a/lib/exfat_fs.c b/lib/exfat_fs.c index e9615474..baf2d127 100644 --- a/lib/exfat_fs.c +++ b/lib/exfat_fs.c @@ -72,21 +72,17 @@ void exfat_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"); + while (child && list_empty(&child->children)) { + if (!child->parent || !(child->attr & ATTR_SUBDIR)) return; - } parent = child->parent; list_del(&child->sibling); exfat_free_inode(child); child = parent; - } while (child && list_empty(&child->children)); + } + return; } void exfat_free_dir_list(struct exfat *exfat) @@ -94,6 +90,8 @@ void exfat_free_dir_list(struct exfat *exfat) struct exfat_inode *dir, *i; list_for_each_entry_safe(dir, i, &exfat->dir_list, list) { + if (!dir->parent) + continue; exfat_free_file_children(dir); list_del(&dir->list); exfat_free_inode(dir); @@ -109,6 +107,12 @@ void exfat_free_exfat(struct exfat *exfat) free(exfat->alloc_bitmap); if (exfat->disk_bitmap) free(exfat->disk_bitmap); + if (exfat->ohead_bitmap) + free(exfat->ohead_bitmap); + if (exfat->upcase_table) + free(exfat->upcase_table); + if (exfat->root) + exfat_free_inode(exfat->root); if (exfat->zero_cluster) free(exfat->zero_cluster); free(exfat); @@ -138,7 +142,15 @@ struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs) goto err; } - exfat->disk_bitmap = (char *)malloc(EXFAT_BITMAP_SIZE(exfat->clus_count)); + exfat->ohead_bitmap = + calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count)); + if (!exfat->ohead_bitmap) { + exfat_err("failed to allocate bitmap\n"); + goto err; + } + + exfat->disk_bitmap = + calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count)); if (!exfat->disk_bitmap) { exfat_err("failed to allocate bitmap\n"); goto err; diff --git a/lib/libexfat.c b/lib/libexfat.c index b093eb60..92e5e916 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -39,15 +39,16 @@ void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap, } } -int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap, - clus_t start_clu, clus_t *next) +static int exfat_bitmap_find_bit(struct exfat *exfat, char *bmap, + clus_t start_clu, clus_t *next, + int bit) { clus_t last_clu; last_clu = le32_to_cpu(exfat->bs->bsx.clu_count) + EXFAT_FIRST_CLUSTER; while (start_clu < last_clu) { - if (!exfat_bitmap_get(bmap, start_clu)) { + if (!!exfat_bitmap_get(bmap, start_clu) == bit) { *next = start_clu; return 0; } @@ -56,6 +57,20 @@ int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap, return 1; } +int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap, + clus_t start_clu, clus_t *next) +{ + return exfat_bitmap_find_bit(exfat, bmap, + start_clu, next, 0); +} + +int exfat_bitmap_find_one(struct exfat *exfat, char *bmap, + clus_t start_clu, clus_t *next) +{ + return exfat_bitmap_find_bit(exfat, bmap, + start_clu, next, 1); +} + wchar_t exfat_bad_char(wchar_t w) { return (w < 0x0020) From 1c3f9737f6d9a886397421779fa2291da5d0b4b3 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 16:14:48 +0900 Subject: [PATCH 40/57] fsck: enable repairng the cluster chain which contains invalid clusters Enabling repairing the cluster chain which contains invalid clusters. Signed-off-by: Hyunchul Lee --- fsck/repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsck/repair.c b/fsck/repair.c index 941c86be..a9125f68 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -39,7 +39,7 @@ static struct exfat_repair_problem problems[] = { {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_INVALID_CLUS, ERF_PREEN_YES, 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}, From 316961824c2565d3277abc3a882b14e6fcaec98d Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 16:17:07 +0900 Subject: [PATCH 41/57] fsck: enable reparing the cluster chain which contains a loop Enable repairing the cluster chain which contains a loop. Signed-off-by: Hyunchul Lee --- fsck/repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsck/repair.c b/fsck/repair.c index a9125f68..0110c979 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -43,7 +43,7 @@ static struct exfat_repair_problem problems[] = { {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_DUPLICATED_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, {ER_FILE_ZERO_NOFAT, ERF_PREEN_YES, ERP_FIX}, }; From 8ca6723727181d411338ad0b4dd1e237b9500426 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 16:18:17 +0900 Subject: [PATCH 42/57] fsck: tests: add a testcase for cluster chains which contain invalid cluster numbers Add a testcase for cluster chains which contain invalid cluster numbers. Signed-off-by: Hyunchul Lee --- tests/bad_num_chain/config | 1 + tests/bad_num_chain/exfat.img.tar.xz | Bin 0 -> 4064 bytes 2 files changed, 1 insertion(+) create mode 100644 tests/bad_num_chain/config create mode 100644 tests/bad_num_chain/exfat.img.tar.xz diff --git a/tests/bad_num_chain/config b/tests/bad_num_chain/config new file mode 100644 index 00000000..f62cec1e --- /dev/null +++ b/tests/bad_num_chain/config @@ -0,0 +1 @@ +#OPTS: -s diff --git a/tests/bad_num_chain/exfat.img.tar.xz b/tests/bad_num_chain/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..68b8605644f3e37a9d5f7b0316073789f6397e78 GIT binary patch literal 4064 zcmd7VRag^_qsH-(lF~I=kRCW1X$PZQCLx`Ql=J{82O}L_N(c-bt+W$x4rzX%bR#VY z(xd<9+??m!oaZ?g|MNWO`n~u*@8#!O0d8dj0FZ7>HbIF2{6wq(03c9mz3leZj9A(Z zAYDYUAMff}LgTBkcSf?BLX`-C3$D++yj}wr5E(g<9DkGLbJ#%{@ZG3h3w<3kZ>vu} zEu(XwpQG1_jw=v~UIl=;S7G{I?FlOv)w@JD*5qDECs}R8X8`L|3r~|NHis}t$;Or9 zAUAWl=WWjtx8|voYeru*--M&=Yd7HjxQPtFlkSToElj0?g4!|H$VkV)WixPK7Gm=7 zEtD7UHCWyak}zHp9GqJYI5LEmVZ-zs>pT$*xlKLq{``<>7U3<*W?KqOqC~4PRf$MN zM&ZPU?(ezEZgdUASvP#zNFcCGBtMjv1o7|n4}F>0N~+BT-Ae8haCeL}3bQ}8j^B1S zx9Z#uuCZQQwSrBsd{6nnAMto#@MMbZI7r)J>( z2*q^wmOhiMfzo}rideiXcpwSueuFrQi|Ka5-?t!~q|1ZJDjHU2sMFTe zg0Zr(ozLq=@1wE7fsi$@9RxgyGGoWe`*vP&RC6sY-ycGRf_SI-vZVGf3%*< zcTOoJDlhHoP}Ek&M4jblz7 zny4_p`+BaDt>mt^1lavaacgedr;sJ@wP+77;E}dd6|2=@L!av5{lfxcHcQ%7{rC?@ z)4nWxp6p*DhGD6>wl7s}T;b$p?Yj5!Mw`PHk+Dm!o(E&&qxTeTGB!DB0RFf$Q6`Aq z(~WhTD%Z0%_5!?VZ<^I>Wt4Hjf`!uZLdBBYo|>}p28;emy;qrb#}yfuRd$8Vt+x16 zag+XXZ2@9$#E^2OFy_4js#s20-}ZU^5WYpeG!qmugRs@P?h?fo%*rN+$acsdt`AQf zjH*2Vnktk1;ikGH{M#Sg(X;V&lQhPG#!yFX9XcF<`iv&qH6pG5-~SrDoJDGk7JWKbsb--KzaV!cy?s>++j&U8JJ3`g=xDP^me z-d0C36LorS=B)bl^}xLLEy*~Xm~^S8t=Bz1x6T_Q=Mfa867sCwy+0L3;|u6wTP)MF z$512$8N6?dBRAGcK*-bVH*tfBLEbaY9)qMTkUcS)mg@Qy#v3iSS>PnkFl}GPbDq%x zwd52aBTVW=mjb`WHY+}4qq;ZXsKK&25KXVaXbvreQ`|Xs+z^ACuf(w`V_0w9smaeB zg2MNlSs!tZrs5hSDx~vS8C?@>aC=lNpTV}@Tuyj;!RZVzo4kL%*@9ZVN*LJkNNZSAGLJrJd26&27eq>rX-WL`4vpMt~4X`y~Y81M~J$hKa> zVPNZIwr+j)#i{|PTuu7yV3>)CXtExgD#DP-#0Zn36Jq9^DC+WF%G#z~nPTuL#)&KHALG~jWQE`S;Rpmw0AELY$U@~93K zFL@#)geM@ztA&t3nf3czd(z1HGfbSCfq~1#>l5kavz8GXy{<|nMY4B6{@;MfVT1Iv z)bXbJb_qhO7QH@x;+$H-6M4c{N!o7~&D2>L`o~QyO+1Ur;Q5upW<`t+fs3^0dX7fp zn=u6D8{+q^e&`ofU=T5AK_>->xfUE_c@RbuFxkgeH3xYS9q;ApYs&eQ@xu3IA+bL_ zZEQ@qDNOLYV{;N;gR8=-D;un&_0D3HXUT&slU*{t$bT+H*18cB14oaxeV9{kl0YZp zZ>K{j48{E7u{KC#B4d<>D2 z+@{`cU^$rfd*sHQe$DbS5#RU*7UUpt#y*;=TD98DY#}!VIW~U<4r;}ro{?WPzy2pn zl{KHpPpJDNkEPWum;^UF{orlQ{aqfH{UBPYO`VJ0Iufy0HU`fel`m7;mB&MN5R4svT;AO_DuaWGyuP zPG5qHdnso=eF&wSUwCXw2K~VIP45U=QIWm8EAJ0nfOY1lpOx-gxCI1v;IAYFqIo=> zLzrSOPrfOK3qVgnD8IRs;8{s5z9G7zcN&EB;0mM6o{O@LBHaai!vvrCDMDO z_0pVUc0Uc+EmQ^&IE$QBF3k9nqEs>3>*-KbJ1+x-4DD}Xef(DjVa0(|Yf_&l0=T*1 z-+%no4}Hg|{f1Ro61=K(H1&x}4#S>>l9gW;GuERrC<=}g5zjZ$Sk@;+NX+s>5qPrx zIc@WU7q_oqYj76Hcb)#lVsl{oPq<^#lr}_Wx{K+Sv`3%O;;@K*Xhi9ml`s`M5v<17Aebp%kN=CYZZ7qsDj^So5DVP z4noWb*Wt^$XaWWbCa7LO`sP5z7vnSdjy=Ty?=Rt5f91!EO=P$qot#T01ySnoaES(U zwF`c3(GMdc|WqiUjtfBeuLXnevElFeO%aWY}KzfZ&xZi zJ+DI6s^1jaDCsXM&kJ#l7YK_{3f|(M@~gEeOu5Mx{R~@k zS}JlC>cgjGM|=l6Tn)c=8zLgEL;aRcz*-#pN}in>ZtJ$0anuf8&nIFr>Andnsh2fO z6hOfGo6uK2;ISayil)^nKIxvvh?ZK8qsjwL)Pg6KQ57g>Dm7;61WA#gzX#>*f!?xp&-AtVPkwLC25O`NSUer) zK}LC8Q)J?Xs&iF$>L#KTqm?r5N_5ER`n;BOx&Nv_O)odq{r6|ft6kSO2BF?B(eXY= z#^J7nt~%G@I8k=GqSui8TN3!tiQ#??_9)a@%uGW67=1YX*vT*gJ$ZzNIePc!Nr;p} ztS>b6PP`WbDMfHQrB_C^X$BQ=(kPr@KEeeuq1tA4TXctjN)$WIs6S<^P~y^1^3OK#<RzKvQ zmrqFd+}{zE(99KnR9*2-ppqflCCGmfbq}De70xyZv ATmS$7 literal 0 HcmV?d00001 From f4a2cab30534b2bc110e2985aff2659c2a1bdd76 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 3 May 2021 16:28:54 +0900 Subject: [PATCH 43/57] fsck: tests: add a testcase for cluster chains which containa a loop Add a testcase for cluster chains which contains a loop Signed-off-by: Hyunchul Lee --- tests/loop_chain/config | 1 + tests/loop_chain/exfat.img.tar.xz | Bin 0 -> 4052 bytes 2 files changed, 1 insertion(+) create mode 100644 tests/loop_chain/config create mode 100644 tests/loop_chain/exfat.img.tar.xz diff --git a/tests/loop_chain/config b/tests/loop_chain/config new file mode 100644 index 00000000..f62cec1e --- /dev/null +++ b/tests/loop_chain/config @@ -0,0 +1 @@ +#OPTS: -s diff --git a/tests/loop_chain/exfat.img.tar.xz b/tests/loop_chain/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..c863cdda5222b31677039e70a16c4c8a009f12a7 GIT binary patch literal 4052 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 TRN Date: Thu, 6 May 2021 17:33:10 +0900 Subject: [PATCH 44/57] fsck: enable reparing the first cluster of file is bad Enable repairing the first cluster of file is bad. Signed-off-by: Hyunchul Lee --- fsck/repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsck/repair.c b/fsck/repair.c index 0110c979..a99c92b0 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -40,7 +40,7 @@ static struct exfat_repair_problem problems[] = { {ER_DE_CHECKSUM, ERF_PREEN_YES, ERP_FIX}, {ER_FILE_VALID_SIZE, ERF_PREEN_YES, ERP_FIX}, {ER_FILE_INVALID_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, - {ER_FILE_FIRST_CLUS, ERF_DEFAULT_NO, ERP_TRUNCATE}, + {ER_FILE_FIRST_CLUS, ERF_PREEN_YES, 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_PREEN_YES, ERP_TRUNCATE}, From 76319a58954e96f324b25a5c71cdcdcbba834b08 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 6 May 2021 17:06:28 +0900 Subject: [PATCH 45/57] fsck: tests: add a testcase for the bad first cluster of a file Add a testcase for the bad first cluster of a file. Signed-off-by: Hyunchul Lee --- tests/bad_first_clu/exfat.img.tar.xz | Bin 0 -> 3232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/bad_first_clu/exfat.img.tar.xz diff --git a/tests/bad_first_clu/exfat.img.tar.xz b/tests/bad_first_clu/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..3dc29ec88f48824da4cd486d4fbf8cf25815a11b GIT binary patch literal 3232 zcmV;R3}5s8H+ooF000E$*0e?f03iVu0001VFXf})@Ba*6T>vtk2=9huXrK&lK<@Vg z0k5KkC?^p68%q|#fY(JMd)tS$S>qk$Ce?vr?f)sAB>2AG)i_TFiFWwYbgZZhJ z9TNZ6v0q?ffTDp6>9*7B-+Uu21qyx>F&pSLJbA@2WztH+my>bXlqg+#^d#l|BWLnx z?r4$$mOu29!ob(?OCoQUonT3Zq2N+R zdWmlfllEIRz#VZufYPwUO`oMXm76zkjZ|K=;FS3+(_1^AiGT?vx|^>!K0A);Ei;@z zI@FheH>p;!xMCOp{wNS?kS(QWiygnq=7_kGP808a<9W^0*jqHk;0v2xSz3Evk(GajRVWsSt zc=8I{x>kaYkcl`6Z4kB3$U>mrfYaNlo8ZDi-0s1#3592F`A279D zTb;b8%P6w2I90dt}n;v!rGBT;Xhem_Zcl;3S%uUm}=YfqO zZN}0bx?lpPfwBlRk9OrW8Bq~~R5I=vjzT96tOJ7XO zQFPp5uuSygCF6kn+BSrrI$=v`uAFody@)U13D^b`W{r*S$v6&28NJlC+HA5R#0vZF5k}qS<)L)V<4IZD>4vb+*!T)+Cquo$khpiA)z~lg0b0iyJaC2F zezM+rT2tiV*+ygYW;IIMk*T@?2mR!lVEb2_Qdp4i!Dq7}|3`k{BnM3h`TgDhHi)Md zG@7(T@^O8FIJGn5ASP#%9u%voFSZYRdQy8gp;sikLy%3G+QRKf9ybu8axKv5JBBk)bEE1CrinP{EO>}V$MuZ;UDx2b49}j|P3KJV9H4H>ALM>OHe? zWM_Mi9`+O2@5T3yJ=E3D?U#;+3rjHvJc&y~nm>vN#B(E{Ak81C4R^p+k)=2{mmt@} zwZw69>0zic5XN4UJBTn0L)!b?o2u?R-JW?}22nkAbLiDXVJhG-eS{nTPTB2G0e))u zA*I@0-h-(dbF;=kK0)hPJ^~-*G_R+w;pejyd)h294q4XPwUl-qE5sVH23*~QN{D$P z`JMmkH^+G>aj4hsaLRswNf&S{&9h>?#jJA8aq3l~2Fl#2)A0~-)(wPgsAGVz0{V!dZ{rU5pE-~{t9!)9sT?Pz33-_n6vVS;H(u>M+NGK z8BEof4Ibth-w%G6tK6&CXkK4;zYFr08KaPT?*$4)sTDJ{#`sm7ib~MS`G7s_2EO07 zW_}lL6~(BNFu@A+hN!EgILxss1K`qdy;CO2&fN8bs}1sJS_8|;18%_NKxo-gTB~RA zy&v_7kCgW9ahcCLo>23@EZK|KD-3Z_Uk)Wuv#do&6pT#5Wm^ST+V#i!?sxlK`;;DE zjwN*n1WdLSK)}D?{qLnrAg+ORK`mtpmzz8UExBZE!Zz>BQ58p_{fEN6k>}dUDWKgd z+MMx47zdW+i9;e!tF9z-d7F@AjfD#(d;-}%M`nGl0jGw>tSin_KDbmqHeB1&ARa%2 zD{2|*rkxK|cS0&dRAElp-j@1QbQdIQTx~|5Fb+3Mp#|9utpL>GR?}X_n$tQijj4Ik z7h`tl!P5MA`NsO!WahSA8HW3Q#ak^p>LxsHKA&$5u>N1b@iBETe=KpX3t?JLF()Qx358fTklfg>oO)X66;rgdDiYj66q13xVE-*)u6V(k2YSG?S%~^BG zY_PV?&c(#!VsiyeNvwRgT?v+4&TNh>+Q9GKYzMMO)@yUoV}W*eDJHWJo;;is@S>NSfB9Nr zv{uU{03!psE7Qt!la*u}r?lOXFZP8UpCS4ftLuq4y8r+y60*B{+X0{e0s9z$-~#|0 S)CPmG#Ao{g000001X)@}WJA*c literal 0 HcmV?d00001 From 0a8bd4e1e86e7ece2a5929ca75295a231b6efef3 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 6 May 2021 20:25:48 +0900 Subject: [PATCH 46/57] fsck: enable reparing the mismatch between size and chains Enable repairing the mismatch between size and chains. Signed-off-by: Hyunchul Lee --- fsck/repair.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fsck/repair.c b/fsck/repair.c index a99c92b0..0721067e 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -41,8 +41,8 @@ static struct exfat_repair_problem problems[] = { {ER_FILE_VALID_SIZE, ERF_PREEN_YES, ERP_FIX}, {ER_FILE_INVALID_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, {ER_FILE_FIRST_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, - {ER_FILE_SMALLER_SIZE, ERF_DEFAULT_NO, ERP_TRUNCATE}, - {ER_FILE_LARGER_SIZE, ERF_DEFAULT_NO, ERP_TRUNCATE}, + {ER_FILE_SMALLER_SIZE, ERF_PREEN_YES, ERP_TRUNCATE}, + {ER_FILE_LARGER_SIZE, ERF_PREEN_YES, ERP_TRUNCATE}, {ER_FILE_DUPLICATED_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, {ER_FILE_ZERO_NOFAT, ERF_PREEN_YES, ERP_FIX}, }; From 45d53d60512770bc88de0b9229773c02726258be Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 6 May 2021 20:27:33 +0900 Subject: [PATCH 47/57] fsck: tests: add a testcase for the mismatch between size and chain Add a testcase for the mismatch between size and chain. Signed-off-by: Hyunchul Lee --- tests/bad_file_size/exfat.img.tar.xz | Bin 0 -> 4052 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/bad_file_size/exfat.img.tar.xz diff --git a/tests/bad_file_size/exfat.img.tar.xz b/tests/bad_file_size/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..df7ff1471fc98f5800f96ebe1d858c91a4dd757f GIT binary patch literal 4052 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 VgLwA0dS^|K(-rZ5g`}2N{{b8_<*Wbz literal 0 HcmV?d00001 From 4b08e7a51ba52645f72e57933884ca361d531e66 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 8 May 2021 13:03:22 +0900 Subject: [PATCH 48/57] fsck: tests: add a testcase for the mismatch between bitmap and chains Add a testcase for the mismatch between bitmap and chains. Signed-off-by: Hyunchul Lee --- tests/bad_bitmap/exfat.img.tar.xz | Bin 0 -> 4056 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/bad_bitmap/exfat.img.tar.xz diff --git a/tests/bad_bitmap/exfat.img.tar.xz b/tests/bad_bitmap/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..df09d10185b39f33356749b4dddb3b55a539a827 GIT binary patch literal 4056 zcmciFWmFS@!Uo`hfFP63AtenuQosqqNEJcGP#E1PU86<1MmI=8`Ova+(b+O0Kh{OTXb^+BNjIX zNaT_qgj>DM2RbY6pWWJsxf7-A%Zr_N!~YV`BGNR!b<&-c;NL;}Nd5FglY~cH$ag&N zw4=Z2)4fb-d~Y!ek=TbQZvF+hL`2Nd0GSQ4(5-wC3Kr2rLZ;LV#p{wQeoywMkFW@g^vWK)0f zW18|>u4mW$``+uV4Lq?gY5aYJ$A655R8@-~KV^Vhcvd2QVGT*iF9sH>uCH`NJVrST zxs)!J;qvD~RuoSHhlL$}Ha_{&*8w&yr+%toqm)2TbwiVU?&^&=ryRXW6;z`xyY^qX z$`S*=>530K4hibA?&E`>pC(F+lYXk*ckXu4R0mEb(w0}5vA2%Z39uL;!ne$v1|`U= z6R|<-Id+=`Cg}BuPUQYL-qNbX;MNdqA}vB|CIufFe(Qs%a7y9RP}5e)L#6kwzRpjt z+sJlG&Jx!n^gVzUq3;b~7D*n@jrttFByHw@AG=MmpSSW!X*g2B2oOJ^Rl_fs;>sx> z&%5T5`61Q*L0LG^#|mpg$nL8?+`nn6fmO z-+O18z0tNo@6h% z@yBrs_>BH%XxaH9=T&)ynUKy81D(>%6@R!M-t6==f|f>}ggQI%@2S^XBBa+0M@pQs z$w3z*y;JKCCs+))w@3rU~>t*)5UlzBL>{+t^Y6UK= z6A7BhYRtYZZ2j_X2qT}eE#=RU>%`}bpVGj|=~>AbIpGfoWFeB6=q^u+oSN>iB>L(0 zGc4`B!fh=mXXs_^bxu)1)DMl0xEtw2R~x)-U9o%$`7Pj1iK73>nbi}wZC+snJxR+# z<)VlCZ?$+f^6waAu1ivQ$Q2jvR78koUV_Td0|#-{3k{Vt9V)0L4dhArAt6D+?LPhB zpmPK9TDI8seojWo_7kmbA5P=VtqeAd49oWXJbU^}q)ydW_$^J_;~E^<^UjpVs3H9+ z*@B!O6T`GBpEWRF&h5eMJpJ}BqbyLbCK-yv{X{1Q zkgj2_W_{X~ku(LQUUy!DPyZsAo_5hu* zX(LnuJu)5<>DL&csjpXKKFa%4*Z1lN;Lq(ToIBUnkpwIXt|^xd*Z zZF$~TPTiM8Q7Q!&LKMzKvnh$1%i3d%YJOzvmQ*vBtT1uBvg=7Xg7I9{a%m{`_t<3J zsJE=P_{{|~9U81~=*Y#qu^Wu5TtRnO&)LhIk~0SKx%-tFGarva)|4on7NWxA_SY|#|AA4jL6_yDY#^2k!)2~l-aIgg#rLx=# z!N|wsiURkt9T`yfTPwMFyJyXo%!>oW+qcVWBC&{m-@*)EU5n)lZJki&_l6YpXmzI! zI&=!ggv0m)1+zuB6nnEs(LJIG^Mtz-UYOvKD9CbAb8+iFovdhaKW%~oCg5tHX*rjU z_eDOi9=a&a3l>vjOGmw3T7;%q#%rEgP(+ea zTkY$K`Xqm5*0bj&avQ+zHldmcN|V1EAU=Aqemx((+qT2T{$%r3+w(x^%TbE$>h9oq zpXy47vf2BK-Ndf6(5h)r@07i$6Pq}ZH7QWVkBGWqjwhBo8tEVVUA1R}Fn~B<7S4cz z>Dz9xZnM{0)KnBVM}1==y%a3 z?J9&12MBYq`vX26d;WVo`xAvSy>H?c&!j0Pc#+VeJ}h}$dv>P_)IN3M6rZeB_wbJ) z7o+Ob9=e^);xf9g=lVKgtMB2j$137$mR7YWpr9IysJ5p#A85%bPJPa=Ld~YW#EcMQ ze@fbD*x)Q?;sLejm8h8%x{(DAm89P!GG&gXT_(84Bw|9KS~9@W%kO7JWNPu}ve_cf zSmt7#K~-GFJ0~av0q40JA*Uf$md12ACovuP9fy4C zzz@p1=}$dJ<1-9Yf+$6DA5X3A(`B?}MJ)Zvm~5qW6`?z6OS=vu86KY4?u<6c)E{+V)yi%u?}infQj%1lB?)t^a5$=WaF)nd?nT%$MxRawY@(c5uBB{<2c{-UXH06>X17d zO!4B8V+jdynCCN4WcO%lc9I+c;`p)fWCiFo1u3|Hx8rpx=W-js8JERAN)t)BRjWtx zmA-N%R$vSW^<6T~ukxL%*YIg>W(p16T${;iIETnLt+|DZqAAh=Q=oPSY{$k+ z2O$}zB~`7kW)UP+jM(G+)k9?4R}kndy1&0(^d%M1DZx8u-E7v<5%PQ5Cy3wGty@|9 ztiL~Szm!to1$y9T?5np9)H^_ogIOaecm8=((j9uR!*u&64eYE#s7XN(_b7{ZlA3*4 z#ujsE`bMzIA)B+T?#ocCcPB;ynye_&F1U&8P1V`%qk6ct_FQT60QUXHL1YY9gq|u; z)iN#|(;%BJDQY?s?!h0LE2a`&X|^=}6UQ}RcCsfdZhEqZ`?S2A&7VzGJ2U^r^%)^SOahR3m@cFsPEf|n2|n>bN{$D#<&LK} zNaIy*@?~OnaMOu7Qjz=XxU;}t=>}v<`rP^Wzh;8}CqVrtj{IM$(~A(K2|{FmkAPiw z_-A})wZAwhuQ^uS>AxeU|LsKIwfh*Qe+nG{=uctHgD9HR0*LB311GwOWw`Yk>-uc3 Li2oImAPoKibU^nr literal 0 HcmV?d00001 From b8cf7571b5b8ec1f0fd9eb87f69add0b2597c0a6 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 13 May 2021 18:02:07 +0900 Subject: [PATCH 49/57] fsck: tests: add a testcase for two chains which contain a same cluster Add a testcase for two chains which contains a same cluster. Signed-off-by: Hyunchul Lee --- tests/duplicate_clu/exfat.img.tar.xz | Bin 0 -> 4048 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/duplicate_clu/exfat.img.tar.xz diff --git a/tests/duplicate_clu/exfat.img.tar.xz b/tests/duplicate_clu/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..bf3fdd856992377d1f3e29af33ac19bc4208e7c0 GIT binary patch literal 4048 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 Date: Wed, 26 May 2021 16:43:39 +0900 Subject: [PATCH 50/57] fsck: repair unknown directory entries if unknown directory entries are found, Clear InUse bit in EntryType field. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 31 ++++++++++++++++++++++--------- fsck/repair.c | 1 + fsck/repair.h | 1 + 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 1e1d84d8..9c2508f8 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -89,9 +89,13 @@ static void usage(char *name) #define repair_file_ask(iter, inode, code, fmt, ...) \ ({ \ - exfat_resolve_path_parent(&path_resolve_ctx, \ - (iter)->parent, inode); \ - exfat_repair_ask(&exfat_fsck, code, \ + if (inode) \ + exfat_resolve_path_parent(&path_resolve_ctx, \ + (iter)->parent, inode); \ + else \ + exfat_resolve_path(&path_resolve_ctx, \ + (iter)->parent); \ + exfat_repair_ask(&exfat_fsck, code, \ "ERROR: %s: " fmt " at %#" PRIx64, \ path_resolve_ctx.local_path, \ ##__VA_ARGS__, \ @@ -951,18 +955,27 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) } else exfat_free_inode(node); break; + case EXFAT_LAST: + goto out; case EXFAT_VOLUME: case EXFAT_BITMAP: case EXFAT_UPCASE: - break; - case EXFAT_LAST: - goto out; + if (dir == exfat->root) + break; + /* fallthrough */ default: if (IS_EXFAT_DELETED(dentry->type)) break; - exfat_err("unknown entry type. 0x%x\n", dentry->type); - ret = -EINVAL; - goto err; + if (repair_file_ask(de_iter, NULL, ER_DE_UNKNOWN, + "unknown entry type %#x at %07" PRIx64, + dentry->type, + exfat_de_iter_file_offset(de_iter))) { + struct exfat_dentry *dentry; + + exfat_de_iter_get_dirty(de_iter, 0, &dentry); + dentry->type &= EXFAT_DELETE; + } + break; } exfat_de_iter_advance(de_iter, dentry_count); diff --git a/fsck/repair.c b/fsck/repair.c index 0721067e..97c3ed69 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -38,6 +38,7 @@ 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_DE_UNKNOWN, ERF_PREEN_YES, ERP_FIX}, {ER_FILE_VALID_SIZE, ERF_PREEN_YES, ERP_FIX}, {ER_FILE_INVALID_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, {ER_FILE_FIRST_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, diff --git a/fsck/repair.h b/fsck/repair.h index fbdc607a..4f365471 100644 --- a/fsck/repair.h +++ b/fsck/repair.h @@ -8,6 +8,7 @@ #define ER_BS_CHECKSUM 0x00000001 #define ER_BS_BOOT_REGION 0x00000002 #define ER_DE_CHECKSUM 0x00001001 +#define ER_DE_UNKNOWN 0x00001002 #define ER_FILE_VALID_SIZE 0x00002001 #define ER_FILE_INVALID_CLUS 0x00002002 #define ER_FILE_FIRST_CLUS 0x00002003 From c8504184a01cb859975fe3ff996c1377eb2875d1 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 4 Jun 2021 11:01:37 +0900 Subject: [PATCH 51/57] fsck: repair corrupted dentry sets Repair the following corrupted dentry sets: * invalid File dentry's SetChecksum. * invalid File dentry's SecondaryCount. * Absent Stream Extension dentry. * mismatch between the file size and the number of clusters. * invalid NameHash. * mismatch between the length of name and NameLength. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 158 ++++++++++++++++++++++++++++++++++++------------- fsck/repair.c | 12 +++- fsck/repair.h | 6 ++ lib/exfat_fs.c | 8 ++- 4 files changed, 137 insertions(+), 47 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 9c2508f8..8512a659 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -603,36 +603,87 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node) 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; + exfat_de_iter_get_dirty(iter, 0, &dentry); + dentry->file_checksum = cpu_to_le16(checksum); + ret = 1; } return valid ? ret : -EINVAL; } +static int check_name_dentry_set(struct exfat_de_iter *iter, + struct exfat_inode *inode) +{ + struct exfat_dentry *stream_de; + size_t name_len; + __u16 hash; + + exfat_de_iter_get(iter, 1, &stream_de); + + name_len = exfat_utf16_len(inode->name, NAME_BUFFER_SIZE); + if (stream_de->stream_name_len != name_len) { + if (repair_file_ask(iter, NULL, ER_DE_NAME_LEN, + "the name length of a file is wrong")) { + exfat_de_iter_get_dirty(iter, 1, &stream_de); + stream_de->stream_name_len = (__u8)name_len; + } else { + return -EINVAL; + } + } + + hash = exfat_calc_name_hash(iter->exfat, inode->name, (int)name_len); + if (cpu_to_le16(hash) != stream_de->stream_name_hash) { + if (repair_file_ask(iter, NULL, ER_DE_NAME_HASH, + "the name hash of a file is wrong")) { + exfat_de_iter_get_dirty(iter, 1, &stream_de); + stream_de->stream_name_hash = cpu_to_le16(hash); + } else { + return -EINVAL; + } + } + return 0; +} + static int read_file_dentry_set(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; + struct exfat_dentry *file_de, *stream_de, *dentry; + struct exfat_inode *node = NULL; int i, ret; - - /* TODO: mtime, atime, ... */ + bool need_delete = false; + uint16_t checksum; 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); + exfat_err("failed to get file dentry\n"); return -EINVAL; } + + checksum = file_calc_checksum(iter); + if (checksum != le16_to_cpu(file_de->file_checksum)) { + if (repair_file_ask(iter, NULL, ER_DE_CHECKSUM, + "the checksum of a file is wrong")) + need_delete = true; + *skip_dentries = 1; + goto skip_dset; + } + + if (file_de->file_num_ext < 2) { + if (repair_file_ask(iter, NULL, ER_DE_SECONDARY_COUNT, + "a file has too few secondary count. %d", + file_de->file_num_ext)) + need_delete = true; + *skip_dentries = 1; + goto skip_dset; + } + 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; + if (repair_file_ask(iter, NULL, ER_DE_STREAM, + "failed to get stream dentry")) + need_delete = true; + *skip_dentries = 2; + goto skip_dset; } *new_node = NULL; @@ -640,24 +691,28 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, if (!node) return -ENOMEM; - if (file_de->file_num_ext < 2) { - exfat_err("too few secondary count. %d\n", - file_de->file_num_ext); - exfat_free_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; + ret = exfat_de_iter_get(iter, i, &dentry); + if (ret || dentry->type != EXFAT_NAME) { + if (i > 2 && repair_file_ask(iter, NULL, ER_DE_NAME, + "failed to get name dentry")) { + exfat_de_iter_get_dirty(iter, 0, &file_de); + file_de->file_num_ext = i - 1; + break; + } + *skip_dentries = i + 1; + goto skip_dset; } memcpy(node->name + - (i-2) * ENTRY_NAME_MAX, name_de->name_unicode, - sizeof(name_de->name_unicode)); + (i - 2) * ENTRY_NAME_MAX, dentry->name_unicode, + sizeof(dentry->name_unicode)); + } + + ret = check_name_dentry_set(iter, node); + if (ret) { + *skip_dentries = file_de->file_num_ext + 1; + goto skip_dset; } node->first_clus = le32_to_cpu(stream_de->stream_start_clu); @@ -666,27 +721,41 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, node->size = le64_to_cpu(stream_de->stream_size); if (node->size < le64_to_cpu(stream_de->stream_valid_size)) { + *skip_dentries = file_de->file_num_ext + 1; 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)) { + "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; + goto skip_dset; } } *skip_dentries = (file_de->file_num_ext + 1); *new_node = node; return 0; -err: - *skip_dentries = 1; +skip_dset: + if (need_delete) { + exfat_de_iter_get_dirty(iter, 0, &dentry); + dentry->type &= EXFAT_DELETE; + } + for (i = 1; i < *skip_dentries; i++) { + exfat_de_iter_get(iter, i, &dentry); + if (dentry->type == EXFAT_FILE) + break; + if (need_delete) { + exfat_de_iter_get_dirty(iter, i, &dentry); + dentry->type &= EXFAT_DELETE; + } + } + *skip_dentries = i; *new_node = NULL; exfat_free_inode(node); - return ret; + return need_delete ? 1 : -EINVAL; } static int read_file(struct exfat_de_iter *de_iter, @@ -948,12 +1017,17 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) 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 - exfat_free_inode(node); + if (node) { + 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 { + exfat_free_inode(node); + } + } break; case EXFAT_LAST: goto out; diff --git a/fsck/repair.c b/fsck/repair.c index 97c3ed69..65f4a9ff 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -27,18 +27,26 @@ struct exfat_repair_problem { /* Prompt types */ #define ERP_FIX 0x00000001 #define ERP_TRUNCATE 0x00000002 +#define ERP_DELETE 0x00000003 static const char *prompts[] = { "Repair", "Fix", "Truncate", + "Delete", }; 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_DE_UNKNOWN, ERF_PREEN_YES, ERP_FIX}, + {ER_DE_CHECKSUM, ERF_PREEN_YES, ERP_DELETE}, + {ER_DE_UNKNOWN, ERF_PREEN_YES, ERP_DELETE}, + {ER_DE_FILE, ERF_PREEN_YES, ERP_DELETE}, + {ER_DE_SECONDARY_COUNT, ERF_PREEN_YES, ERP_DELETE}, + {ER_DE_STREAM, ERF_PREEN_YES, ERP_DELETE}, + {ER_DE_NAME, ERF_PREEN_YES, ERP_DELETE}, + {ER_DE_NAME_HASH, ERF_PREEN_YES, ERP_FIX}, + {ER_DE_NAME_LEN, ERF_PREEN_YES, ERP_FIX}, {ER_FILE_VALID_SIZE, ERF_PREEN_YES, ERP_FIX}, {ER_FILE_INVALID_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, {ER_FILE_FIRST_CLUS, ERF_PREEN_YES, ERP_TRUNCATE}, diff --git a/fsck/repair.h b/fsck/repair.h index 4f365471..4e9a6bf9 100644 --- a/fsck/repair.h +++ b/fsck/repair.h @@ -9,6 +9,12 @@ #define ER_BS_BOOT_REGION 0x00000002 #define ER_DE_CHECKSUM 0x00001001 #define ER_DE_UNKNOWN 0x00001002 +#define ER_DE_FILE 0x00001010 +#define ER_DE_SECONDARY_COUNT 0x00001011 +#define ER_DE_STREAM 0x00001020 +#define ER_DE_NAME 0x00001030 +#define ER_DE_NAME_HASH 0x00001031 +#define ER_DE_NAME_LEN 0x00001032 #define ER_FILE_VALID_SIZE 0x00002001 #define ER_FILE_INVALID_CLUS 0x00002002 #define ER_FILE_FIRST_CLUS 0x00002003 diff --git a/lib/exfat_fs.c b/lib/exfat_fs.c index baf2d127..41518b07 100644 --- a/lib/exfat_fs.c +++ b/lib/exfat_fs.c @@ -37,9 +37,11 @@ struct exfat_inode *exfat_alloc_inode(__u16 attr) void exfat_free_inode(struct exfat_inode *node) { - if (node->dentry_set) - free(node->dentry_set); - free(node); + if (node) { + if (node->dentry_set) + free(node->dentry_set); + free(node); + } } void exfat_free_children(struct exfat_inode *dir, bool file_only) From 5187a929a0bd59cd4adb31a4cd570cc15426514f Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 4 Jun 2021 11:06:42 +0900 Subject: [PATCH 52/57] fsck: tests: add testcase for repairing corrupted directory entries Add a testcase for repairing corrupted directory entries. Signed-off-by: Hyunchul Lee --- tests/bad_dentries/exfat.img.tar.xz | Bin 0 -> 9628 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/bad_dentries/exfat.img.tar.xz diff --git a/tests/bad_dentries/exfat.img.tar.xz b/tests/bad_dentries/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..32643caf9987934a819819f7d4eb45f11c73ac27 GIT binary patch literal 9628 zcmc)QQ*$K%mo?zz1bt%LcG6+T?$~z6wr$(CZFOv)*tXfR=l#BaFjF-(*ZX4CUYDz$ z&tIMzT3}$1$IH!P05B48OfWDoXQrc~@9!@Fk}eoaF7PJQ*gPNJPVo8};vlL9M9a+g zw!Ux&4iOhnkMS%1rCZada5qJDCS7=mfVjxloxC+A&bwsaTr?{wcz&~3RiaCIt8jZfZ?niwAwoG47yWml46@9XLDQ2LfA+P(Ic`va+Rr#Mu1dp^K%}7>6pElv zonXb|UvoZ0)hfasfWXGB0G#n*o*qC$*V0IEAU!|8z%pV%+k~eX4ofz%b~;t6;5QjR zRS~8Desn_tyR{@B(?G|YxnoVL_BDc`dIdyKxft@S5NIoF6nRxM87ryo#2~fdPxm!c zQS=+i!okaKq<_{~QfJ7fG4UjSZ2AY}b>4P~@MO4v4p`i*TpcBKvI7xc9QmGoZW-C1 zOX|sxxs>!sU)qF!R4&grebDK$>7T20OYulU6v{7;2^`E&CH2>K{r0lr@N)?$l{NYV z9n%d|*gBe_XQoOTJ!h`fnukZ)P{7yG^_n*>jvP)6!vL~?%M6_$H6R9k?aw{fkx;0O z|E0i>cO^P!5TQ1)@avW`-wTZ~43GaLt@Ta|&+Sk*Jn=xTR zD5rkm1_n5Na%aa|qj4G}dJa)hOH`vLb9e;ds(dNs&>aIB&n9h-#j#l-ikH~JhPDM` zMj3%F6i8}9K=cW`D5``eV`fuJYRh8=IO|(sxj)u<;PV}_mkZ|_Sb1MuD2cwnv&nrk zml#t!^)FfOc*S57$fXR}d^VgnCI`DVYAY$29~~X$6Xm%ZmFEz-U+#^vwA1-XmH<%f zcqxo%7^j$5Tp|9E8PEnnoeT+JSy6p$J*$|%u3`Japr>Y%>V(nsDK;-uKcTdL)kS)y zbHu@nC;E7J5($}{QDfsDDoYnbmOq)fmIZrj9$y(MkkX)ouw!$WIO*oplN9u|m>UN73Y!yo5 z@AT)vAeL$24yvIqCBdIW0KYq{v~sYa-2-tTpRKqRpSSQxQKKfhnZd=q(k`w=Wa@0t z{1D+O>-xc)C;78x6P4aqCk#{r`*6VNACCyP)#5dt5|rsTELyV)--c)34VI@E=FVUj z3Y+{@Xf05NqE63bVUA>urowT~&7`zNf)yVHLM#^5x8hp(p81onAcwfUG8!5SN$Mdn z$!4R=B~v56sg)^&|Gfn{Ofl8d7!X+e)Rfp$Su;L%MLwx7=%pBy8dqK2&bA0@S0@er zaiYg)?C9ZK(M3MbIaAbhC!{YUvpy!#LkX5qg-eO9k8#X32qJ<+Rz9j!RaDz1ew`}= z=Y2G1*DFps6u^jRL3wikbmPR`6RxLGBk5fsl`DHIR9QM2Rog9aX7j zeQrB#+#+;OSG#HR19p_WvDiQ2fcsOc&9nua`_+@F^V?YK!w(!5!?@}pb4wc{=mzoa ziVV~E`?{JK`~-Nivve39tloXoai(^jqpIIK15JU|`_>^)i*7Hn?Gwnj4+6W)4LMm|AQO5mj z6w!qRyp$*>`x*Vt?pXhnCtzYnD-G-Xs`1ms<8Ou3MY*bN;ZK9%B9!9oltG}oZ9p_5 z?>m=Qa-X=`dMPCg2`M3OsAm;qt*76ECZoy}r}&i<2s;C;xqI2BE`#udIj2kkz-JZ+ z*vt+thy!Wnwdw@bZFw0OZJj&&&Bg4yzOdfd!>%$&&d{Avg8L3|sSV-yO;sCqe5QV} zGB`4z?O1y;Wyj44lgW05Cs-^~w8FeIq6M`)HuxiGn)GD)*9z>^8I?nnSO2FROc9J0 z1#7gQE?1E+6c4K#SaY{&@S{-oIxIJ}VZW=rh$~t96@fUeH9wxCUuw*_n0H}qAhyD_ zq_pI<`QOKu07CVq3GCu)>a6uKffrau%66(^Kl}7Iuc?yL+dpaF1&CV66Z4Z0QM88_ zxa+2j=N@TU7P0Z#^ef$4el%Il$$tVTwrosviayA}sEUo+i-`+w=BT#fIHjun-C7O> z`B?tw)!mO{K65nVY_9qrad%r+a$EZE7Hb($b-1Ie!OB~ZGUay-!{4v9MC?Jaag+}a7B(zP<1*WlK^>HEXgqOAEupmhjQkFpTcVFK8 z)y^yqTgw-wMLOoVJ^$PWs6Xu~ee15Iz{lnDvn!-w=J!x;204sAvE2;uDTHbnB7#>j zOyMBaY{s?q*iRWY(!LP*FMWbDE&b&+@V3?S=CGb>itAfM^MJqE{L2(AxB?UoWDkPR zt_;6(r%$(I0l=Cv=YdEe|8p8ij zyQ{UM;wzL;t**J6xka+xU!W>zvTNN3J)LJsJ`076khoy$F>)&86A!h1D!01*Hr#ZH z?Tr3vt^g->WsV-ccMGZCr{7!GH3iWA)cyOL{evI+mlA1|fBWNwaVbW5N-GL(b^DPQ!Onqk6t6bP5*{S}RF%FId_$yE^57YKc)TJ@IXMsf zoNRwMlTFbReaW8hSY6pD$20q}KJbO240;q=iN~DvGD(;Ut|c}s)<~my`>VP}B}C>3 zc?pU%2}KI)9d~`B1-5yaT&Xb>$)B&`9mDQPZqI?5{faFFG{yeE7LESiKEsv~UMl_N zi;w4-xnH}8su{B&Wa-o`o<}@MCh2%(@JLzpUgJU0?A-7r7}1L|op%}^8m17Varz>2 z0=lv&C-T%(W5|4mBJppzd;n}#>dU8s9&p)$WQ+{fbrc)X-5X2-LOK^Ky+)mqQc~bc zgMyZi=-fMvx;qqgqWo}?y7^tUoGS`3Vmw8%ptP00WQU_O91l{~_;f`nBc2GX^kney z3+oJpYI#KEN(QXO&Fc*Cn>rxv6CgD_1oUm3z5C1{jS}}?J$~lkY%35 zF+bBq#e)5|NJ00>iEWF!2TMLRJKFOJXP)DKkq~>b=V^%Ygy&!tX-A2)ABmK}7dvazMUu0Svr4 zuVP0E`5%GZ3y7aEJLidnBD`iQq&%pC6mg^-)%iM!dbVJ|5i|+rHFQ6to7ov*VHSp^{Q7|Iol(IB-0=lXxn#;oZGZ1MEaN|a6VYq&Ax9Qe!>lIq^ast} z4x_j(sy^cCpGfO()m3<|7ibh{c-EALK5r0mr;+p$6xr$wy_bpS{^s!HtqAh|D^3Y~ zzGYb&e$WseEwi;_VheFnJdO%KLIH8YJN zt9eg>%h9JB8OqBh)jrJeNOwKfGWb0qJ$hnMvr0r56O$#TGl-FrXDT5{!w(&*F&q&} z(GWt3RL|c`WU64**VVK+9-6lFv{k;ZxSQ`K2RyNj;T)rn%jR8h+cDv0hy!Nwu)qz& zkEqrmaL!q&FpC~7O<4(>9qePF zYM>5KaQZaH(ax%p3&$rZ_DUUKJg$WAdvvHbvvZFt&#xQ04uz8mh@Wkz9jF@wy=3xI zHm>Ufh>N&a_Zt+EzX7_LwKO$%(` z0+Ib>>0%daf@7z62DD^FVm=v@Ez=51BeIV#eNll}Drg=O_x0@C89EBxe&)fEo{4H? z=B>YKQ?b-w`onu$=`EZ~A1G+Z++uD%zytE2RhBL7U=wuK4Np%svV4s_uPD39Z}&rO zX$A6S3m+(xcy4+jltPyEK>3(RpzhzDN+_PB+5#8ahoel`E2I~}Gp~Y!*OE?FULa5p$+|iu!_=N8Jnv=9|&p zdlHke{@l;am`%-*<_@0Iir+}i|Kzd9u(^|5N17EZ0$gsvmN*Y22=5W29?bU3e?gj*}VdEh&mDiEGNjpC}~5k9066L-e^R%S2D0z|F(4MLvZqXFb(0g_}%zq}>_Z z&Pfy@yvK#@n;81BXQasfNRT6H_-VM1N#uPZqo*DFQn)sfH;xd9K3X@vE07DMa$U{o z>@kO`Py+IsqfAB@xQQD<)M|I+`6G*1kR-y%XE3RW{mv4F5#gWqwYDCa~Q0rT3Up|5sW+2_X zOUkWNS(rnc94kamRz@dQp@Skjgak(+{&k=(%&t}5*xkMtJ+S@!qAi29VTkwc`tz$^ zG(y!HpyOi35W&+jE#Ksp*9d)$PuoyQ<+_ z!~N!G`b~2@ClE<=uoTyMOn~SYnSB|!(!vi~goGxNyBB;jERqX^e0hKbN`c|XOBl(h z9wc2yV%`nxs_~y#m1LU16WhUF)~_x^f{hFv*!*Yfd_K_TKhOTfWXsw!Awh?ynRnb( zD`|xT+)nDfp84W3|w7Mt?70;!irZW#yaSY zNe2#2ms1yTS4Eds?oXM%nJ0R?>FVB#_&_39+lpTT*oD8utv8Q%M!H~pL7>Hpfe@;% z>&aUk{_aRv2lk*{tt9D1^Y9?49ky=&{)I!bM_@{VD(gUV!(x9kFGgrI7c%2=zuJMP zCFWm)Qn`42`J5HR_GYlh*$5OK)>2+R-I+ zrc_ZIcaH#_=OliwZEHc-qNve#so_QNR_Gw{W_v(SziP3LUE9VRqG$v_)k7)2ZwyrSsdMxVfWc&} zqZnK8Z;WB2S-$5_rq+j68zjH)@tWelP%=te;LV**6EG%nz*^P1{(Le7;2H4&t77-2 zU%Zn2woG$Y>IK&pu1^=vc7FoN#P zx{%4BW*&)5W{Y`jwwWBbi@01f4&00{(J$?^g0iD=u*Q{CHQ&EDbLa}XLkC;DBcmV058#rzV zX6l_tsun)_c@hsYA@4=H7!qswi+Q zrui6Y8q>V&a@U~_CGz-SF2W()?b(Zd7r1e%a!VIoDgm6_D)^93sX#Pc>b4&l8xqk2 znwmv_%itbdRba~wcExE|e$do^TsG&|{+dyi7vjW5XH6o|?#V^>g!?x_Ba+224lB9< zUVkslGfnmlw10ei>K25|P<=3=lUfQdc9LG|4M>YM4{1{*P`U|2w2z1G4gEVt#Nx@` zz;S%#VnR7tauU(Qn081HJP`-q2bof{K!Tw#N2`ir-E^OEyoqjxF@J?fF~DHgxQJ4G zvwQmx1SDurc@yd6;nwC+A~6IXGN{>)X6&7(?@cNuh;*DSk_Feu97?H%!Xeg`f12W! zynwd8M?{vqumjqYi`i+_!t(9X(WU;vi`-dcjAWCB^^f3u_<#m;P-8q`*_qqoO)O`b z#2k45N(C`c6>(RZwxAFjkWbfuQ#F+yYjC`#7_Oo#x5v7>C^wdyfXA?Cd-k_{+b+hmup_ zGDNw5;f0x{Pf2+-t2#kGIS?yMV)xU9nzBj+Rx8tD*c~s?Aq)8y#(pE=Or(Yv3b5&m zQ7mDn{)Dc}Nqf=JRKjkRx#C8c?X#aBs#E^nIzi9n%xZXd(#6eF-`n%YNYJy%71fTm zWP@idk|fq5CJA{VgwT26%J4|zFCzUOhWkdoNh#Z7*^V2kJHfHHZ5#s2OQ{Dh@x zTX7CHW!uA1NbhvJel>rFTT%$_%CLbA#D>B+vr z7+!8+ztQLLlL@97`XTaN$Z8soWuMbBkpQR?;_Z>CB=D$v!L1)C;3#`7FXOM|l*Uk83U60zS?3Gr$%JNMIzq7{ZNCyuZ|rC=z|!xt-EYZ31A^ergs;u3Ix`>4Lv##p6GUGN>OGh@hMUkcU^~HmHr>%F_(< zQ*Hz!)TrwkVrX<62V2{Fbek&RlR`P!_GrPapWHnA6Qro4g5{U&9d|iA!5(?eUQKF{iUd9&NMcf z(@LVSqorzxhOR6|-YdS5*&mo^%z;dD_hAu1zK1HC?Uh8MYD>Q!VwSecn!WP4V(Ri6 zeTwhVfCvi2HpZ1TX-F4ewyTuf2P_@1b3x>286o)dO43>TP1E$Wt8mGb@Q~k~xaE+Z z&W=&R;OKn%MU5bzU2M>&X!U>EtNkh^? zLdMGx-sLAg54_Pb)^j6VWl{>QoRRJiEZTr?#wHc3apt4RsSA@w@1)TT|N79Y(D7P+}4Jyfle9Z(fXoNf6Hacn4|xs zup);@o6)BZ!baN5Ap*F=&B%EVjqpDq7v&Ke#e=o7HItRlurTbUPj4}TCV2nWaS!BSz8Ba>RXKnhy%HtoJ?eAxzH}Di?5_+gD8?(xVVP|1}XNvC?RJ-i6my zs9ObnY_Q}0`pi-rrh!G5?uA=a2OJuSU@;d~iPHY&TrK=dwU{>1$*Wu}oBJENGTsOv zp3W}Cypb${&z|3-ZaUyRu5PBS5T97yL%$Ftm@FD4c4+E{kU6yue@##Sn$EkCDO8Uw zgyf?k#~EwUY`Y4kKdhCC%En=?HDN{Lv3GK|{nRc;Rn z*s`!%fUZ@sBdPQb2@ilG3D}moXz<3>4cizwld6(2(1U73;0sE!QC}m*2LSsE4{8UQ z<Yq>R9o#XbKP0Su9!!kmJ z&bqm7Ts)+|L6X$Xlrz<06ps7ZBdka*E3$TG}eN;>^Na1~eUk;oW)+WND{i)eh z%wRG*oy~U3v%ov!aJ43W?D8u39VLv$Fx)}p)`%?aL{%UEK*qewa9v0I=2&sH70|E0 z%w<}9*w5^D{9T;xTb4+vg+&A$(@D4c_V+-`70mbmgZ|UqhrId z0AGWwZL=tw{8hldSz^e75+1ue6ZF=|-HGA->Bg1*M^|WH(EEKJzdsg2Cy|@wZ-xPq zKL^O0y-BVdVzsB~)@oPa#REO*1#9Gz&_{=PuLaWPvie|ns}g-y48NTNi7HCa7Z4F# z0jU_@iNa<>{?+P%S#q#KyI}HGSNC&kzD>gkF*-jy=|Hzm`yqW% zul249USghb6vW?Idv>W#Ih1Aq{Ml`WZMFHS)g+F#21%(*Efb|ttf}BBGN)5xRA-lk zc+sh`2F+!8dMk$}J;Qg*xhn*s-jO{XC)#O*Lv%uwg}!9awwzc# zaC8RV1^4>hR@^ra6m_j&Q#dbX-DVNfT*pcknjpdlROJiMDVHo2K>z@sNN2SuM+D(_&A?vy@NaMYJ@~2lq@35Jz_quP^c^Wz~bOTiKaH?BWavBg$>3XcrchvY-7m>Jj9;9CRzYmw z+;MGlvwJxxL^^=xq6ehD?XWYV%QmnpgO40|ayHP_+vwO+%sZzDqr5CRu=^>_4E ze*djDTBLE`e*dw7m_TJSemb&*MEy$ICa!3(1}O*cFqK9j04Zh&tB11)@h}C)mY#1) zh=n0>DrKLe)Iv1G?QRy3vFO&!l|t`KTMN^(Hn(bq(oP3Iwl6;;&v{LNq>>s2w&P%R z@Z(*6SC_=uy&}hN*x%#|ni6Ed0^0#PW$BU*W0^TK-cYxmYt!Zk#v{JmL0r4h#@ewL!bLb73h zdyEsjKXYpn{7DdW=y$Rx)DGdnKrHBQY$XvpBjTEb2=x3`4Xi;ZTBPv{Rm}8vyvYVq zG9p)l;oP~IOrKQnsViu4d(?JDQOV?A*kA#?rN7R^=rP#9dP8pnfUv)1%&tcN)WLWx zAPC`jZZ0&jj?i``l6>@A{rizs)zKxDO(xUVyYASnw6iz%Z3s@%DrIT{mXHTs+ zGj3j6472V3g53EZ{OWu=vZMbWW(5W~++I?>I{X3yf0Xi{?E^f~*vvHynY{!4-w{$% G>%RbLbhx|# literal 0 HcmV?d00001 From 0422e7b759355926edb10a634d2bf3e36774596c Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 7 Jun 2021 14:39:23 +0900 Subject: [PATCH 53/57] fsck: repair corrupted root if the cluster chain of root is broken or cyclic, repair the chain. Signed-off-by: Hyunchul Lee --- fsck/fsck.c | 61 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/fsck/fsck.c b/fsck/fsck.c index 8512a659..52b3f1b9 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -240,36 +240,60 @@ static int check_clus_chain(struct exfat_de_iter *de_iter, return 1; } -static bool root_get_clus_count(struct exfat *exfat, struct exfat_inode *node, - clus_t *clus_count) +static int root_check_clus_chain(struct exfat *exfat, + struct exfat_inode *node, + clus_t *clus_count) { - clus_t clus; + clus_t clus, next, prev = EXFAT_EOF_CLUSTER; + + if (!exfat_heap_clus(exfat, node->first_clus)) + goto out_trunc; clus = node->first_clus; *clus_count = 0; do { - if (!exfat_heap_clus(exfat, clus)) { - exfat_err("/: bad cluster. 0x%x\n", clus); - return false; - } - if (exfat_bitmap_get(exfat->alloc_bitmap, clus)) { - exfat_err("/: cluster is already allocated, or " - "there is a loop in cluster chain\n"); - return false; + if (exfat_repair_ask(&exfat_fsck, + ER_FILE_DUPLICATED_CLUS, + "ERROR: the cluster chain of root is cyclic")) + goto out_trunc; + return -EINVAL; } exfat_bitmap_set(exfat->alloc_bitmap, clus); - if (exfat_get_inode_next_clus(exfat, node, clus, &clus) != 0) { - exfat_err("/: broken cluster chain\n"); - return false; + if (exfat_get_inode_next_clus(exfat, node, clus, &next)) { + exfat_err("ERROR: failed to read the fat entry of root"); + goto out_trunc; } + if (next != EXFAT_EOF_CLUSTER && !exfat_heap_clus(exfat, next)) { + if (exfat_repair_ask(&exfat_fsck, + ER_FILE_INVALID_CLUS, + "ERROR: the cluster chain of root is broken")) { + if (next != EXFAT_BAD_CLUSTER) { + prev = clus; + (*clus_count)++; + } + goto out_trunc; + } + return -EINVAL; + } + + prev = clus; + clus = next; (*clus_count)++; } while (clus != EXFAT_EOF_CLUSTER); - return true; + + return 0; +out_trunc: + if (!exfat_heap_clus(exfat, prev)) { + exfat_err("ERROR: the start cluster of root is wrong\n"); + return -EINVAL; + } + node->size = *clus_count * exfat->clus_size; + return exfat_set_fat(exfat, prev, EXFAT_EOF_CLUSTER); } static int boot_region_checksum(int dev_fd, @@ -1157,22 +1181,23 @@ static int exfat_filesystem_check(struct exfat_fsck *fsck) static int exfat_root_dir_check(struct exfat *exfat) { struct exfat_inode *root; - clus_t clus_count; + clus_t clus_count = 0; int err; root = exfat_alloc_inode(ATTR_SUBDIR); if (!root) return -ENOMEM; + exfat->root = root; root->first_clus = le32_to_cpu(exfat->bs->bsx.root_cluster); - if (!root_get_clus_count(exfat, root, &clus_count)) { + if (root_check_clus_chain(exfat, root, &clus_count)) { exfat_err("failed to follow the cluster chain of root\n"); exfat_free_inode(root); + exfat->root = NULL; return -EINVAL; } root->size = clus_count * exfat->clus_size; - exfat->root = root; exfat_stat.dir_count++; exfat_debug("root directory: start cluster[0x%x] size[0x%" PRIx64 "]\n", root->first_clus, root->size); From 2d1d8749a07c23adff48114d682eb2722712da7c Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 7 Jun 2021 14:40:55 +0900 Subject: [PATCH 54/57] fsck: tests: testcases for repairing root Add a testcases for repairing root. Signed-off-by: Hyunchul Lee --- tests/bad_root/exfat.img.tar.xz | Bin 0 -> 4528 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/bad_root/exfat.img.tar.xz diff --git a/tests/bad_root/exfat.img.tar.xz b/tests/bad_root/exfat.img.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..de0066fce1780745da4b8978b2080ac9829ae855 GIT binary patch literal 4528 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 Date: Tue, 2 Aug 2022 08:31:03 +0900 Subject: [PATCH 55/57] tests: some modification of test_fsck.sh Add '-s' option to fsck option, And remove comparision between repaired image and expected image in test_fsck.sh Signed-off-by: Hyunchul Lee --- tests/file_invalid_clus/exfat.img.expected.xz | Bin 4048 -> 0 bytes .../exfat.img.expected.xz | Bin 48520 -> 0 bytes tests/test_fsck.sh | 27 ++++++------------ 3 files changed, 9 insertions(+), 18 deletions(-) delete mode 100644 tests/file_invalid_clus/exfat.img.expected.xz delete mode 100644 tests/large_file_invalid_clus/exfat.img.expected.xz diff --git a/tests/file_invalid_clus/exfat.img.expected.xz b/tests/file_invalid_clus/exfat.img.expected.xz deleted file mode 100644 index 08e992efdb1b8243f58c32e2e969afbd142502cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4048 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 diff --git a/tests/large_file_invalid_clus/exfat.img.expected.xz b/tests/large_file_invalid_clus/exfat.img.expected.xz deleted file mode 100644 index b31e7100ff379415e1361e5dee2cd0ef84f5b172..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48520 zcmeI5Rd8HOm!-wb%xEz)Tg=QXS#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 diff --git a/tests/test_fsck.sh b/tests/test_fsck.sh index 936db54a..c53d8f36 100755 --- a/tests/test_fsck.sh +++ b/tests/test_fsck.sh @@ -4,13 +4,18 @@ TESTCASE_DIR=$1 NEED_LOOPDEV=$2 IMAGE_FILE=exfat.img FSCK_PROG=fsck.exfat -FSCK_OPTS=-y +FSCK_PROG_2=fsck.exfat +FSCK_OPTS="-y -s" PASS_COUNT=0 cleanup() { echo "" echo "Passed ${PASS_COUNT} of ${TEST_COUNT}" - exit + if [ ${PASS_COUNT} -ne ${TEST_COUNT} ]; then + exit 1 + else + exit 0 + fi } if [ $# -eq 0 ]; then @@ -40,7 +45,7 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do # Run fsck for repair $FSCK_PROG $FSCK_OPTS "$DEV_FILE" - if [ $? -ne 1 ]; then + if [ $? -ne 1 ] && [ $? -ne 0 ]; then echo "" echo "Failed to repair ${TESTCASE_DIR}" if [ $NEED_LOOPDEV ]; then @@ -51,7 +56,7 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do echo "" # Run fsck again - $FSCK_PROG -n "$DEV_FILE" + $FSCK_PROG_2 "$DEV_FILE" if [ $? -ne 0 ]; then echo "" echo "Failed, corrupted ${TESTCASE_DIR}" @@ -61,20 +66,6 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do cleanup fi - if [ -e "${TESTCASE_DIR}/exfat.img.expected.xz" ]; then - EXPECTED_FILE=${IMAGE_FILE}.expected - unxz -cfk "${TESTCASE_DIR}/${EXPECTED_FILE}.xz" > "${EXPECTED_FILE}" - diff <(xxd "${IMAGE_FILE}") <(xxd "${EXPECTED_FILE}") - if [ $? -ne 0 ]; then - echo "" - echo "Failed ${TESTCASE_DIR}" - if [ $NEED_LOOPDEV ]; then - losetup -d "${DEV_FILE}" - fi - cleanup - fi - fi - echo "" echo "Passed ${TESTCASE_DIR}" PASS_COUNT=$((PASS_COUNT + 1)) From b9806defcc54b8ef37b024447d4d6debfcb89443 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 25 Aug 2022 16:42:30 +0900 Subject: [PATCH 56/57] exfatprogs: add exfat2img to dump a partition Add exfat2img to dump metadata of an exFAT filesystem. It dump boot sector, FAT, bitmap, all metadata which can reach from root directory. exfat2img -o sda1.dump /dev/sda1 exfat2img -o - /dev/sda1 | bzip2 > sda1.dump.bz When restoring a partition from a dump image generated from stdout, exfat2img should be used like the following: bzip2 -dc sda1.dump.bz2 | exfat2img -o /dev/sdb1 - Signed-off-by: Hyunchul Lee --- Android.bp | 1 + Makefile.am | 6 +- configure.ac | 1 + exfat2img/Makefile.am | 6 + exfat2img/exfat2img.c | 1059 +++++++++++++++++++++++++++++++++++++++++ manpages/exfat2img.8 | 31 ++ 6 files changed, 1102 insertions(+), 2 deletions(-) create mode 100644 exfat2img/Makefile.am create mode 100644 exfat2img/exfat2img.c create mode 100644 manpages/exfat2img.8 diff --git a/Android.bp b/Android.bp index 9f8716bc..d0e2594e 100644 --- a/Android.bp +++ b/Android.bp @@ -9,6 +9,7 @@ cc_library_headers { "tune", "label", "dump", + "exfat2img", ], } diff --git a/Makefile.am b/Makefile.am index 44f86354..3e36f55a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = lib mkfs fsck tune label dump +SUBDIRS = lib mkfs fsck tune label dump exfat2img # manpages dist_man8_MANS = \ @@ -10,7 +10,8 @@ dist_man8_MANS = \ manpages/tune.exfat.8 \ manpages/mkfs.exfat.8 \ manpages/exfatlabel.8 \ - manpages/dump.exfat.8 + manpages/dump.exfat.8 \ + manpages/exfat2img.8 # other stuff EXTRA_DIST = \ @@ -22,4 +23,5 @@ EXTRA_DIST = \ fsck/Android.bp \ label/Android.bp \ dump/Android.bp \ + exfat2img/Android.bp \ README.md diff --git a/configure.ac b/configure.ac index 05443096..bc207743 100644 --- a/configure.ac +++ b/configure.ac @@ -32,6 +32,7 @@ AC_CONFIG_FILES([ tune/Makefile label/Makefile dump/Makefile + exfat2img/Makefile ]) AC_OUTPUT diff --git a/exfat2img/Makefile.am b/exfat2img/Makefile.am new file mode 100644 index 00000000..8b5cee7f --- /dev/null +++ b/exfat2img/Makefile.am @@ -0,0 +1,6 @@ +AM_CFLAGS = -Wall -Wextra -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common +exfat2img_LDADD = $(top_builddir)/lib/libexfat.a + +sbin_PROGRAMS = exfat2img + +exfat2img_SOURCES = exfat2img.c diff --git a/exfat2img/exfat2img.c b/exfat2img/exfat2img.c new file mode 100644 index 00000000..bad76398 --- /dev/null +++ b/exfat2img/exfat2img.c @@ -0,0 +1,1059 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Hyunchul Lee + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_ondisk.h" +#include "libexfat.h" +#include "exfat_fs.h" +#include "exfat_dir.h" + +#define EXFAT_MAX_UPCASE_CHARS 0x10000 + +struct exfat2img_hdr { + __le32 magic; + __le32 major_version; + __le32 minor_version; + __le32 data_offset; + __le32 heap_clus_offset; + __le32 cluster_size; + __le32 cluster_count; + __le32 reserved[20]; +} __packed; + +#define EI_MAGIC 0xB67598DB +#define EI_CC_PAYLOAD_LEN 4 + +enum { + EI_CC_INVALID, + EI_CC_COPY_1, + EI_CC_COPY_2, /* followed by cluster count(4-byte) */ + EI_CC_SKIP_1, + EI_CC_SKIP_2, /* followed by cluster count(4-byte) */ +}; + +struct exfat2img { + int out_fd; + bool is_stdout; + off_t stdout_offset; + bool save_cc; + struct exfat_blk_dev bdev; + struct exfat *exfat; + struct buffer_desc *dump_bdesc; + struct buffer_desc *scan_bdesc; + struct exfat_de_iter de_iter; +}; + +struct exfat_stat { + long dir_count; + long file_count; + long error_count; + uint64_t written_bytes; +}; + +static struct exfat2img_hdr ei_hdr; +static struct exfat2img ei; +static struct exfat_stat exfat_stat; +static struct path_resolve_ctx path_resolve_ctx; + +static struct option opts[] = { + {"output", required_argument, NULL, 'o' }, + {"version", no_argument, NULL, 'V' }, + {"help", no_argument, NULL, 'h' }, + {NULL, 0, NULL, 0 } +}; + +static void usage(const char *name) +{ + fprintf(stderr, "Usage: %s [image-file]\n", name); + fprintf(stderr, "\t-o | --output Specify destination file\n"); + fprintf(stderr, "\t-V | --version Show version\n"); + fprintf(stderr, "\t-h | --help Show help\n"); + exit(EXIT_FAILURE); +} + +#define ei_err(parent, inode, fmt, ...) \ +({ \ + exfat_resolve_path_parent(&path_resolve_ctx, \ + parent, inode); \ + exfat_err("ERROR: %s: " fmt, \ + path_resolve_ctx.local_path, \ + ##__VA_ARGS__); \ +}) + +static void free_exfat2img(struct exfat2img *ei) +{ + if (ei->exfat) + exfat_free_exfat(ei->exfat); + if (ei->dump_bdesc) + exfat_free_buffer(ei->dump_bdesc, 2); + if (ei->scan_bdesc) + exfat_free_buffer(ei->scan_bdesc, 2); + if (ei->out_fd) + close(ei->out_fd); + if (ei->bdev.dev_fd) + close(ei->bdev.dev_fd); +} + +static int create_exfat2img(struct exfat2img *ei, + struct pbr *bs, + const char *out_path) +{ + int err; + + ei->exfat = exfat_alloc_exfat(&ei->bdev, bs); + if (!ei->exfat) + return -ENOMEM; + + ei->dump_bdesc = exfat_alloc_buffer(2, + ei->exfat->clus_size, + ei->exfat->sect_size); + if (!ei->dump_bdesc) { + err = -ENOMEM; + goto err; + } + + ei->scan_bdesc = exfat_alloc_buffer(2, + ei->exfat->clus_size, + ei->exfat->sect_size); + if (!ei->scan_bdesc) { + err = -ENOMEM; + goto err; + } + + if (strcmp(out_path, "-")) { + ei->out_fd = open(out_path, O_CREAT | O_TRUNC | O_RDWR, 0664); + } else { + ei->is_stdout = true; + ei->out_fd = fileno(stdout); + ei->save_cc = true; + } + if (ei->out_fd < 0) { + exfat_err("failed to open %s: %s\n", out_path, + strerror(errno)); + err = -errno; + goto err; + } + + return 0; +err: + free_exfat2img(ei); + return err; +} + +static int read_boot_sect(struct exfat_blk_dev *bdev, struct pbr **bs) +{ + struct pbr *pbr; + int err = 0; + unsigned int sect_size, clu_size; + + pbr = malloc(sizeof(struct pbr)); + + if (exfat_read(bdev->dev_fd, pbr, sizeof(*pbr), 0) != + (ssize_t)sizeof(*pbr)) { + exfat_err("failed to read a boot sector\n"); + err = -EIO; + goto err; + } + + err = -EINVAL; + if (memcmp(pbr->bpb.oem_name, "EXFAT ", 8) != 0) { + exfat_err("failed to find exfat file system\n"); + goto err; + } + + sect_size = 1 << pbr->bsx.sect_size_bits; + clu_size = 1 << (pbr->bsx.sect_size_bits + + pbr->bsx.sect_per_clus_bits); + + if (sect_size < 512 || sect_size > 4 * KB) { + exfat_err("too small or big sector size: %d\n", + sect_size); + goto err; + } + + if (clu_size < sect_size || clu_size > 32 * MB) { + exfat_err("too small or big cluster size: %d\n", + clu_size); + goto err; + } + + *bs = pbr; + return 0; +err: + free(pbr); + return err; +} + +/** + * @end: excluded. + */ +static ssize_t dump_range(struct exfat2img *ei, off_t start, off_t end) +{ + struct exfat *exfat = ei->exfat; + size_t len, total_len = 0; + ssize_t ret; + + if (ei->is_stdout) { + unsigned int sc, sc_offset; + unsigned int ec, ec_offset; + + if (exfat_o2c(ei->exfat, start, &sc, &sc_offset) < 0) + return -ERANGE; + if (exfat_o2c(ei->exfat, end - 1, &ec, &ec_offset) < 0) + return -ERANGE; + exfat_bitmap_set_range(ei->exfat, exfat->alloc_bitmap, + sc, ec - sc + 1); + return end - start; + } + + while (start < end) { + len = (size_t)MIN(end - start, exfat->clus_size); + + ret = exfat_read(exfat->blk_dev->dev_fd, + ei->dump_bdesc[0].buffer, + len, start); + if (ret != (ssize_t)len) { + exfat_err("failed to read %llu bytes at %llu\n", + (unsigned long long)len, + (unsigned long long)start); + return -EIO; + } + + ret = pwrite(ei->out_fd, ei->dump_bdesc[0].buffer, + len, start); + if (ret != (ssize_t)len) { + exfat_err("failed to write %llu bytes at %llu\n", + (unsigned long long)len, + (unsigned long long)start); + return -EIO; + } + + start += len; + total_len += len; + exfat_stat.written_bytes += len; + } + return total_len; +} + +static int dump_sectors(struct exfat2img *ei, + off_t start_sect, + off_t end_sect_excl) +{ + struct exfat *exfat = ei->exfat; + off_t s, e; + + s = exfat_s2o(exfat, start_sect); + e = exfat_s2o(exfat, end_sect_excl); + return dump_range(ei, s, e) <= 0 ? -EIO : 0; +} + +static int dump_clusters(struct exfat2img *ei, + clus_t start_clus, + clus_t end_clus_excl) +{ + struct exfat *exfat = ei->exfat; + off_t s, e; + + s = exfat_c2o(exfat, start_clus); + e = exfat_c2o(exfat, end_clus_excl); + return dump_range(ei, s, e) <= 0 ? -EIO : 0; +} + +static int dump_directory(struct exfat2img *ei, + struct exfat_inode *inode, size_t size, + clus_t *out_clus_count) +{ + struct exfat *exfat = ei->exfat; + clus_t clus, possible_count; + uint64_t max_count; + size_t dump_size; + off_t start_off, end_off; + + if (size == 0) + return -EINVAL; + + if (!(inode->attr & ATTR_SUBDIR)) + return -EINVAL; + + clus = inode->first_clus; + *out_clus_count = 0; + max_count = DIV_ROUND_UP(inode->size, exfat->clus_size); + + possible_count = (256 * MB) >> (exfat->bs->bsx.sect_per_clus_bits + + exfat->bs->bsx.sect_size_bits); + possible_count = MIN(possible_count, exfat->clus_count); + + while (exfat_heap_clus(exfat, clus) && *out_clus_count < possible_count) { + dump_size = MIN(size, exfat->clus_size); + start_off = exfat_c2o(exfat, clus); + end_off = start_off + DIV_ROUND_UP(dump_size, 512) * 512; + + if (dump_range(ei, start_off, end_off) < 0) + return -EIO; + + *out_clus_count += 1; + size -= dump_size; + if (size == 0) + break; + + if (inode->is_contiguous) { + if (*out_clus_count >= max_count) + break; + } + if (exfat_get_inode_next_clus(exfat, inode, clus, &clus)) + return -EINVAL; + } + return 0; +} + +static int dump_root(struct exfat2img *ei) +{ + struct exfat *exfat = ei->exfat; + struct exfat_inode *root; + clus_t clus_count = 0; + + root = exfat_alloc_inode(ATTR_SUBDIR); + if (!root) + return -ENOMEM; + + root->first_clus = le32_to_cpu(exfat->bs->bsx.root_cluster); + dump_directory(ei, root, (size_t)-1, &clus_count); + root->size = clus_count * exfat->clus_size; + + ei->exfat->root = root; + return 0; +} + +static int read_file_dentry_set(struct exfat_de_iter *iter, + struct exfat_inode **new_node, int *skip_dentries) +{ + struct exfat_dentry *file_de, *stream_de, *dentry; + struct exfat_inode *node = NULL; + int i, ret; + + ret = exfat_de_iter_get(iter, 0, &file_de); + if (ret || file_de->type != EXFAT_FILE) { + exfat_debug("failed to get file dentry\n"); + return -EINVAL; + } + + ret = exfat_de_iter_get(iter, 1, &stream_de); + if (ret || stream_de->type != EXFAT_STREAM) { + exfat_debug("failed to get stream dentry\n"); + *skip_dentries = 2; + goto skip_dset; + } + + *new_node = NULL; + node = exfat_alloc_inode(le16_to_cpu(file_de->file_attr)); + if (!node) + return -ENOMEM; + + for (i = 2; i <= file_de->file_num_ext; i++) { + ret = exfat_de_iter_get(iter, i, &dentry); + if (ret || dentry->type != EXFAT_NAME) + break; + memcpy(node->name + + (i - 2) * ENTRY_NAME_MAX, dentry->name_unicode, + sizeof(dentry->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); + + *skip_dentries = i; + *new_node = node; + return 0; +skip_dset: + *new_node = NULL; + exfat_free_inode(node); + return -EINVAL; +} + +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_dentry_set(de_iter, &node, dentry_count); + if (ret) + return ret; + + if (node->attr & ATTR_SUBDIR) + exfat_stat.dir_count++; + else + exfat_stat.file_count++; + *new_node = node; + return ret; +} + +static int read_bitmap(struct exfat2img *ei, struct exfat_de_iter *iter) +{ + struct exfat *exfat = ei->exfat; + struct exfat_dentry *dentry; + int ret; + + ret = exfat_de_iter_get(iter, 0, &dentry); + if (ret || dentry->type != EXFAT_BITMAP) { + exfat_debug("failed to get bimtap dentry\n"); + return -EINVAL; + } + + exfat_debug("start cluster %#x, size %#" PRIx64 "\n", + le32_to_cpu(dentry->bitmap_start_clu), + le64_to_cpu(dentry->bitmap_size)); + + if (!exfat_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 -EINVAL; + } + + exfat->disk_bitmap_clus = le32_to_cpu(dentry->bitmap_start_clu); + exfat->disk_bitmap_size = DIV_ROUND_UP(exfat->clus_count, 8); + + return dump_clusters(ei, + exfat->disk_bitmap_clus, + exfat->disk_bitmap_clus + + DIV_ROUND_UP(exfat->disk_bitmap_size, + exfat->clus_size)); +} + +static int read_upcase_table(struct exfat2img *ei, + struct exfat_de_iter *iter) +{ + struct exfat *exfat = ei->exfat; + struct exfat_dentry *dentry = NULL; + int retval; + ssize_t size; + + retval = exfat_de_iter_get(iter, 0, &dentry); + if (retval || dentry->type != EXFAT_UPCASE) { + exfat_debug("failed to get upcase dentry\n"); + return -EINVAL; + } + + if (!exfat_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 -EINVAL; + } + + size = EXFAT_MAX_UPCASE_CHARS * sizeof(__le16); + return dump_clusters(ei, le32_to_cpu(dentry->upcase_start_clu), + le32_to_cpu(dentry->upcase_start_clu) + + DIV_ROUND_UP(size, exfat->clus_size)); +} + +static int read_children(struct exfat2img *ei, struct exfat_inode *dir, + off_t *end_file_offset) +{ + struct exfat *exfat = ei->exfat; + struct exfat_inode *node = NULL; + struct exfat_dentry *dentry; + struct exfat_de_iter *de_iter; + int dentry_count; + int ret; + + *end_file_offset = 0; + de_iter = &ei->de_iter; + ret = exfat_de_iter_init(de_iter, exfat, dir, ei->scan_bdesc); + 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) { + ei_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++; + break; + } + + if (node) { + 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 { + exfat_free_inode(node); + } + } + break; + case EXFAT_LAST: + goto out; + case EXFAT_BITMAP: + if (dir == exfat->root) { + ret = read_bitmap(ei, de_iter); + if (ret) + exfat_debug("failed to read bitmap\n"); + } + break; + case EXFAT_UPCASE: + if (dir == exfat->root) { + ret = read_upcase_table(ei, de_iter); + if (ret) + exfat_debug("failed to upcase table\n"); + } + break; + case EXFAT_VOLUME: + default: + break; + } + + ret = exfat_de_iter_advance(de_iter, dentry_count); + } +out: + *end_file_offset = exfat_de_iter_file_offset(de_iter); + exfat_de_iter_flush(de_iter); + return 0; +err: + exfat_free_children(dir, false); + INIT_LIST_HEAD(&dir->children); + exfat_de_iter_flush(de_iter); + return ret; +} + +static int dump_filesystem(struct exfat2img *ei) +{ + struct exfat *exfat = ei->exfat; + struct exfat_inode *dir; + int ret = 0, dir_errors; + clus_t clus_count; + off_t end_file_offset; + + 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); + clus_count = 0; + + if (!(dir->attr & ATTR_SUBDIR)) { + ei_err(dir->parent, dir, + "failed to travel directories. the node is not directory\n"); + ret = -EINVAL; + goto out; + } + + dir_errors = read_children(ei, dir, &end_file_offset); + if (!dir_errors) { + dump_directory(ei, dir, (size_t)end_file_offset, + &clus_count); + } else if (dir_errors) { + dump_directory(ei, dir, (size_t)-1, + &clus_count); + exfat_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); + exfat_free_ancestors(dir); + } +out: + exfat_free_dir_list(exfat); + return ret; +} + +static int dump_bytes_to_stdout(struct exfat2img *ei, + off_t start, off_t end_excl, bool fill_zero) +{ + struct exfat *exfat = ei->exfat; + size_t len; + ssize_t ret; + + if (start != ei->stdout_offset) { + exfat_err("try to skip for stdout at %llu, expected: %llu\n", + (unsigned long long)start, + (unsigned long long)ei->stdout_offset); + return -EINVAL; + } + + while (start < end_excl) { + len = (size_t)MIN(end_excl - start, exfat->clus_size); + if (!fill_zero) { + ret = exfat_read(exfat->blk_dev->dev_fd, + ei->dump_bdesc[0].buffer, + len, start); + if (ret != (ssize_t)len) { + exfat_err("failed to read %llu bytes at %llu\n", + (unsigned long long)len, + (unsigned long long)start); + return -EIO; + } + + ret = write(ei->out_fd, ei->dump_bdesc[0].buffer, len); + if (ret != (ssize_t)len) { + exfat_err("failed to write %llu bytes at %llu\n", + (unsigned long long)len, + (unsigned long long)start); + return -EIO; + } + } else { + ret = write(ei->out_fd, exfat->zero_cluster, len); + if (ret != (ssize_t)len) { + exfat_err("failed to write %llu bytes at %llu\n", + (unsigned long long)len, + (unsigned long long)start); + return -EIO; + } + } + + start += len; + ei->stdout_offset += len; + exfat_stat.written_bytes += len; + } + return 0; +} + +static int dump_clusters_to_stdout(struct exfat2img *ei, + unsigned int start_clu, unsigned int end_clu, + bool fill_zero) +{ + unsigned int clu, clu_count; + unsigned char cc; + unsigned int cc_clu_count, cc_len; + off_t start_off, end_off_excl; + char buf[1 + EI_CC_PAYLOAD_LEN]; + + clu = start_clu; + clu_count = end_clu - start_clu + 1; + + if (ei->save_cc) { + /* if the count of clusters is less than 5, use SKIP_1 or COPY_2 */ + cc_clu_count = clu_count < 5 ? 1 : clu_count; + cc_len = cc_clu_count == 1 ? 1 : 1 + EI_CC_PAYLOAD_LEN; + if (fill_zero) + cc = cc_clu_count == 1 ? EI_CC_SKIP_1 : EI_CC_SKIP_2; + else + cc = cc_clu_count == 1 ? EI_CC_COPY_1 : EI_CC_COPY_2; + } else { + cc = EI_CC_INVALID; + cc_clu_count = clu_count; + } + + while (clu <= end_clu) { + if (cc != EI_CC_INVALID) { + buf[0] = cc; + *((__le32 *)&buf[1]) = + cpu_to_le32(cc_clu_count); + if (write(ei->out_fd, buf, cc_len) != (ssize_t)cc_len) { + exfat_err("failed to write cc %d : %u\n for %u ~ %u clusters\n", + cc, cc_clu_count, + start_clu, start_clu + cc_clu_count - 1); + } + } + + if (cc == EI_CC_COPY_1 || cc == EI_CC_COPY_2) { + start_off = exfat_c2o(ei->exfat, clu); + end_off_excl = exfat_c2o(ei->exfat, clu + cc_clu_count); + + if (dump_bytes_to_stdout(ei, start_off, end_off_excl, + false) < 0) + return -EIO; + } else { + ei->stdout_offset += (off_t)cc_clu_count * ei->exfat->clus_size; + } + clu += cc_clu_count; + } + + return 0; +} + +static int dump_to_stdout(struct exfat2img *ei) +{ + struct exfat *exfat = ei->exfat; + off_t start_off, end_off; + unsigned int clu, last_clu, next_clu; + unsigned int start_clu, end_clu; + + start_off = 0; + end_off = exfat_s2o(exfat, le32_to_cpu(exfat->bs->bsx.clu_offset)); + if (dump_bytes_to_stdout(ei, start_off, end_off, false) < 0) { + exfat_err("failed to dump boot sectors and FAT tables\n"); + return -EIO; + } + + clu = EXFAT_FIRST_CLUSTER; + last_clu = clu + exfat->clus_count; + while (clu < last_clu) { + /* read and write clusters for allocated ones */ + start_clu = 0; + while (clu < last_clu && + exfat_bitmap_get(exfat->alloc_bitmap, clu)) { + if (!start_clu) + start_clu = clu; + end_clu = clu; + clu++; + } + + if (start_clu) { + if (dump_clusters_to_stdout(ei, start_clu, end_clu, false) < 0) { + start_off = exfat_c2o(exfat, start_clu); + end_off = exfat_c2o(exfat, end_clu); + exfat_err("failed to dump range from %llx to %llx\n", + (unsigned long long)start_off, + (unsigned long long)end_off); + return -EIO; + } + } + + /* exit if all of the remaining clusters are free */ + if (clu >= last_clu) + break; + if (exfat_bitmap_find_one(exfat, exfat->alloc_bitmap, + clu, &next_clu)) + next_clu = EXFAT_FIRST_CLUSTER + exfat->clus_count; + + /* write zeroes for free clusters */ + start_clu = clu; + end_clu = next_clu - 1; + if (dump_clusters_to_stdout(ei, start_clu, end_clu, true) < 0) { + start_off = exfat_c2o(exfat, start_clu); + end_off = exfat_c2o(exfat, end_clu); + exfat_err("failed to dump zero range from %llx to %llx\n", + (unsigned long long)start_off, + (unsigned long long)end_off); + return -EIO; + } + + clu = next_clu; + } + + return 0; +} + +static int dump_header(struct exfat2img *ei) +{ + struct exfat *exfat = ei->exfat; + + ei_hdr.magic = cpu_to_le32(EI_MAGIC); + ei_hdr.major_version = cpu_to_le32(1); + ei_hdr.minor_version = cpu_to_le32(0); + ei_hdr.data_offset = cpu_to_le32(sizeof(struct exfat2img_hdr)); + ei_hdr.heap_clus_offset = + cpu_to_le32(le32_to_cpu(exfat->bs->bsx.clu_offset) * + exfat->sect_size); + ei_hdr.cluster_size = cpu_to_le32(exfat->clus_size); + ei_hdr.cluster_count = cpu_to_le32(exfat->clus_count); + + if (write(ei->out_fd, &ei_hdr, sizeof(ei_hdr)) != (ssize_t)sizeof(ei_hdr)) { + exfat_err("failed to write exfat2img header\n"); + return -EIO; + } + return 0; +} + +static ssize_t read_stream(int fd, void *buf, size_t len) +{ + size_t read_len = 0; + ssize_t ret; + + while (read_len < len) { + ret = read(fd, buf, len - read_len); + if (ret < 0) { + if (errno != -EAGAIN && errno != -EINTR) + return -1; + ret = 0; + } else if (ret == 0) { + return 0; + } + buf += (size_t)ret; + read_len += (size_t)ret; + } + return read_len; +} + +static int restore_from_stdin(struct exfat2img *ei) +{ + int in_fd, ret; + unsigned char cc; + unsigned int clu, end_clu; + unsigned int cc_clu_count; + unsigned int clus_size; + __le32 t_cc_clu_count; + off_t out_start_off, out_end_off_excl; + off_t in_start_off; + size_t len; + + in_fd = fileno(stdin); + if (in_fd < 0) { + exfat_err("failed to get fd from stdin\n"); + return in_fd; + } + + if (read_stream(in_fd, &ei_hdr, sizeof(ei_hdr)) != (ssize_t)sizeof(ei_hdr)) { + exfat_err("failed to read a header\n"); + return -EIO; + } + + if (le32_to_cpu(ei_hdr.magic) != EI_MAGIC) { + exfat_err("header has invalid magic %#x, expected %#x\n", + le32_to_cpu(ei_hdr.magic), EI_MAGIC); + return -EINVAL; + } + + clus_size = le32_to_cpu(ei_hdr.cluster_size); + + ei->out_fd = ei->bdev.dev_fd; + ei->dump_bdesc = exfat_alloc_buffer(2, clus_size, 512); + if (!ei->dump_bdesc) + return -ENOMEM; + + /* restore boot regions, and FAT tables */ + in_start_off = le32_to_cpu(ei_hdr.data_offset); + out_start_off = 0; + out_end_off_excl = le32_to_cpu(ei_hdr.heap_clus_offset); + while (out_start_off < out_end_off_excl) { + len = MIN(out_end_off_excl - out_start_off, clus_size); + if (read_stream(in_fd, ei->dump_bdesc[0].buffer, len) != (ssize_t)len) { + exfat_err("failed to read first meta region. %llu ~ %llu\n", + (unsigned long long)in_start_off, + (unsigned long long)in_start_off + len); + ret = -EIO; + goto out; + } + + if (pwrite(ei->out_fd, ei->dump_bdesc[0].buffer, len, out_start_off) + != (ssize_t)len) { + exfat_err("failed to write first meta region. %llu ~ %llu\n", + (unsigned long long)out_start_off, + (unsigned long long)out_start_off + len); + ret = -EIO; + goto out; + } + + out_start_off += len; + in_start_off += len; + } + + /* restore heap clusters */ + clu = 0; + while (clu < le32_to_cpu(ei_hdr.cluster_count)) { + if (read_stream(in_fd, &cc, sizeof(cc)) != (ssize_t)sizeof(cc)) { + exfat_err("failed to read cc at %llu\n", + (unsigned long long)in_start_off); + ret = -EIO; + goto out; + } + in_start_off += 1; + + if (cc == EI_CC_COPY_2 || cc == EI_CC_SKIP_2) { + if (read_stream(in_fd, &t_cc_clu_count, EI_CC_PAYLOAD_LEN) != + (ssize_t)EI_CC_PAYLOAD_LEN) { + exfat_err("failed to read cc cluster count at %llu\n", + (unsigned long long)in_start_off); + ret = -EIO; + goto out; + } + cc_clu_count = le32_to_cpu(t_cc_clu_count); + in_start_off += EI_CC_PAYLOAD_LEN; + } else if (cc == EI_CC_COPY_1 || cc == EI_CC_SKIP_1) { + cc_clu_count = 1; + } else { + exfat_err("unexpected cc %d at %llu\n", + cc, (unsigned long long)in_start_off); + ret = -EINVAL; + goto out; + } + + if (cc == EI_CC_COPY_1 || cc == EI_CC_COPY_2) { + end_clu = clu + cc_clu_count; + while (clu < end_clu) { + if (read_stream(in_fd, ei->dump_bdesc[0].buffer, + clus_size) != (ssize_t)clus_size) { + exfat_err("failed to read range %llu ~ %llu\n", + (unsigned long long)in_start_off, + (unsigned long long)in_start_off + clus_size); + ret = -EIO; + goto out; + } + if (pwrite(ei->out_fd, ei->dump_bdesc[0].buffer, + clus_size, out_start_off) != (ssize_t)clus_size) { + exfat_err("failed to write range %llu ~ %llu\n", + (unsigned long long)out_start_off, + (unsigned long long)out_start_off + clus_size); + ret = -EIO; + goto out; + } + + out_start_off += clus_size; + in_start_off += clus_size; + clu++; + } + } else { + out_start_off += (off_t)cc_clu_count * clus_size; + in_start_off += (off_t)cc_clu_count * clus_size; + if (lseek(ei->out_fd, out_start_off, SEEK_SET) == (off_t)-1) { + exfat_err("failed to seek to %llu\n", + (unsigned long long)out_start_off); + ret = -EIO; + goto out; + } + clu += cc_clu_count; + } + } +out: + fsync(ei->out_fd); + exfat_free_buffer(ei->dump_bdesc, 2); + return ret; +} + +int main(int argc, char * const argv[]) +{ + int err = 0, c; + const char *in_path, *out_path = NULL, *blkdev_path; + struct pbr *bs; + struct exfat_user_input ui; + off_t last_sect; + bool restore; + + print_level = EXFAT_ERROR; + + opterr = 0; + while ((c = getopt_long(argc, argv, "o:Vh", opts, NULL)) != EOF) { + switch (c) { + case 'o': + out_path = optarg; + break; + case 'V': + show_version(); + return 0; + case 'h': + /* Fall through */ + default: + usage(argv[0]); + break; + } + } + + show_version(); + if (!(optind == argc - 1 && out_path) && + !(optind == argc - 2 && !out_path)) + usage(argv[0]); + + in_path = argv[optind++]; + if (!out_path) + out_path = argv[optind++]; + + if (!strcmp(in_path, "-")) { + restore = true; + blkdev_path = out_path; + } else { + restore = false; + blkdev_path = in_path; + } + + memset(&ui, 0, sizeof(ui)); + snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", blkdev_path); + if (restore) + ui.writeable = true; + else + ui.writeable = false; + + if (exfat_get_blk_dev_info(&ui, &ei.bdev)) { + exfat_err("failed to open %s\n", ui.dev_name); + return EXIT_FAILURE; + } + + if (restore) + return restore_from_stdin(&ei); + + err = read_boot_sect(&ei.bdev, &bs); + if (err) { + close(ei.bdev.dev_fd); + return EXIT_FAILURE; + } + + err = create_exfat2img(&ei, bs, out_path); + if (err) + return EXIT_FAILURE; + + if (!ei.is_stdout) { + err = dump_sectors(&ei, 0, le32_to_cpu(ei.exfat->bs->bsx.clu_offset)); + if (err) { + exfat_err("failed to dump boot sectors, fat\n"); + goto out; + } + + last_sect = (off_t)le32_to_cpu(ei.exfat->bs->bsx.clu_offset) + + (le32_to_cpu(ei.exfat->bs->bsx.clu_count) << + ei.exfat->bs->bsx.sect_per_clus_bits) - 1; + err = dump_sectors(&ei, last_sect, last_sect + 1); + if (err) { + exfat_err("failed to dump last sector\n"); + goto out; + } + } + + err = dump_root(&ei); + if (err) { + exfat_err("failed to dump root\n"); + goto out; + } + + dump_filesystem(&ei); + + if (ei.is_stdout) { + err = dump_header(&ei); + if (err) + goto out; + err = dump_to_stdout(&ei); + if (err) + goto out; + } else { + err = fsync(ei.out_fd); + if (err) { + exfat_err("failed to fsync %s. %d\n", out_path, errno); + goto out; + } + close(ei.out_fd); + } + + printf("%ld files found, %ld directories dumped, %llu kbytes written\n", + exfat_stat.file_count, + exfat_stat.dir_count, + (unsigned long long)DIV_ROUND_UP(exfat_stat.written_bytes, 1024)); + +out: + free_exfat2img(&ei); + return err == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/manpages/exfat2img.8 b/manpages/exfat2img.8 new file mode 100644 index 00000000..1c7f288e --- /dev/null +++ b/manpages/exfat2img.8 @@ -0,0 +1,31 @@ +.TH exfat2img 8 +.SH NAME +exfat2img \- dump metadata of an exFAT filesystem +.SH SYNOPSIS +.B exfat2img +[ +.B \-o \fIpath\fB\ +] [ +.B \-V +] +.I device +.br +.B exfat2img \-V +.SH DESCRIPTION +.B exfat2img +dump metadata of exFAT filesystems for debugging. \fBexfat2img\fP dump boot sector, File Allcation Table, Bitmap and all metadata which can reach from root directory. + +.SH OPTIONS +.TP +.BI \-o\ \-\-output +Specify output result file. If filesystem to which output file is written does not support sparse file, you should use '-' in place of \fIpath\fP. +When restoring a partition from a dump image generated from stdout, exfat2img should be used. See Examples. +.TP +.B \-V +Prints the version number and exits. + +.SH EXAMPLES +.PP +.EX +.RB "$" " exfat2img -o - /dev/sda1 | bzip2 > sda1.dump.bz2" +.RB "$" " bzip2 -dc sda1.dump.bz2 | exfat2img -o /dev/sdb1 -" From d45d4da28836db69c2e9dd69191b6771aadbc2e2 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 1 Sep 2022 14:43:32 +0900 Subject: [PATCH 57/57] exfatprogs: modify .travis.yml to run fsck testcases Modify .travis.yml to run testcases for fsck repair in tests directory. Signed-off-by: Hyunchul Lee --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 90aff892..2d44bd1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ notifications: before_script: - sudo apt-get install linux-headers-$(uname -r) + - sudo apt-get install xz-utils - 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 @@ -21,11 +22,16 @@ script: - ./configure > /dev/null - make -j$((`nproc`+1)) > /dev/null - sudo make install > /dev/null + # build & install exfat - cd exfat_oot - make > /dev/null - sudo make install > /dev/null - sudo modprobe exfat - sudo mkdir -p /mnt/test + - cd .. + # run fsck repair testcases + - cd tests + - sudo ./test_fsck.sh # create file/director test - truncate -s 10G test.img - sudo losetup /dev/loop22 test.img