mirror of
https://github.com/openharmony/third_party_exfatprogs.git
synced 2026-07-01 08:41:12 -04:00
@@ -0,0 +1,11 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,71 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- exfat-next
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- exfat-next
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: before test
|
||||
run: |
|
||||
sudo apt-get install linux-headers-$(uname -r) xz-utils \
|
||||
gcc-mips-linux-gnu qemu-system-mips \
|
||||
qemu-user
|
||||
git clone https://github.com/namjaejeon/linux-exfat-oot
|
||||
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
|
||||
export PATH=/usr/local/lib:$PATH
|
||||
- name: build test & install exfatprogs
|
||||
run: |
|
||||
./autogen.sh > /dev/null
|
||||
./configure > /dev/null
|
||||
make -j$((`nproc`+1)) > /dev/null
|
||||
sudo make install > /dev/null
|
||||
make distclean > /dev/null
|
||||
./configure --host=mips-linux-gnu CFLAGS=--static > /dev/null
|
||||
make -j$((`nproc`+1)) > /dev/null
|
||||
- name: run fsck repair testcases
|
||||
run: |
|
||||
cd tests
|
||||
export FSCK1="qemu-mips ../fsck/fsck.exfat"
|
||||
export FSCK2="fsck.exfat"
|
||||
sudo -E ./test_fsck.sh
|
||||
export FSCK1="fsck.exfat"
|
||||
export FSCK2="qemu-mips ../fsck/fsck.exfat"
|
||||
sudo -E ./test_fsck.sh
|
||||
- name: create file/director test
|
||||
run: |
|
||||
cd linux-exfat-oot
|
||||
make > /dev/null
|
||||
sudo make install > /dev/null
|
||||
sudo modprobe exfat
|
||||
sudo mkdir -p /mnt/test
|
||||
cd ..
|
||||
truncate -s 10G test.img
|
||||
sudo losetup /dev/loop22 test.img
|
||||
sudo mkfs.exfat /dev/loop22
|
||||
sudo mount -t exfat /dev/loop22 /mnt/test/
|
||||
cd /mnt/test/
|
||||
i=1;while [ $i -le 10000 ];do sudo touch file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
|
||||
sync
|
||||
sudo rm -rf *
|
||||
i=1;while [ $i -le 10000 ];do sudo mkdir dir$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
|
||||
sync
|
||||
sudo rm -rf *
|
||||
i=1;while [ $i -le 10000 ];do sudo touch file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
|
||||
i=1;while [ $i -le 10000 ];do sudo mkdir dir$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
|
||||
sync
|
||||
sudo fsck.exfat /dev/loop22
|
||||
sudo find . -delete
|
||||
sudo fsck.exfat /dev/loop22
|
||||
cd -
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
*.o
|
||||
*.a
|
||||
compile_commands.json
|
||||
checkpatch.pl
|
||||
.cache
|
||||
|
||||
# http://www.gnu.org/software/automake
|
||||
|
||||
Makefile.in
|
||||
Makefile
|
||||
/ar-lib
|
||||
/mdate-sh
|
||||
/py-compile
|
||||
/test-driver
|
||||
/ylwrap
|
||||
.deps/
|
||||
.dirstamp
|
||||
|
||||
# http://www.gnu.org/software/autoconf
|
||||
|
||||
autom4te.cache
|
||||
/autoscan.log
|
||||
/autoscan-*.log
|
||||
/aclocal.m4
|
||||
/config.cache
|
||||
/config.h.in
|
||||
/config.h
|
||||
/config.log
|
||||
/config.status
|
||||
/configure
|
||||
/configure.scan
|
||||
/stamp-h1
|
||||
/build-aux
|
||||
|
||||
# https://www.gnu.org/software/libtool/
|
||||
|
||||
/libtool
|
||||
|
||||
# http://www.gnu.org/software/m4/
|
||||
|
||||
/m4
|
||||
|
||||
# Binaries
|
||||
|
||||
/dump/dump.exfat
|
||||
/exfat2img/exfat2img
|
||||
/fsck/fsck.exfat
|
||||
/label/exfatlabel
|
||||
/mkfs/mkfs.exfat
|
||||
/tune/tune.exfat
|
||||
+1
-2
@@ -6,8 +6,7 @@ notifications:
|
||||
- email: true
|
||||
|
||||
before_script:
|
||||
- sudo apt-get install linux-headers-$(uname -r)
|
||||
- sudo apt-get install xz-utils
|
||||
- sudo apt-get install linux-headers-$(uname -r) 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
|
||||
|
||||
Executable → Regular
@@ -30,7 +30,7 @@ config("exfat-defaults") {
|
||||
"-Wno-error",
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
"-DPACKAGE=\"exfatprogs\"",
|
||||
"-DVERSION=\"1.2.0\"",
|
||||
"-DVERSION=\"1.2.5\"",
|
||||
]
|
||||
include_dirs = [
|
||||
"dump",
|
||||
|
||||
@@ -16,6 +16,7 @@ dist_man8_MANS = \
|
||||
# other stuff
|
||||
EXTRA_DIST = \
|
||||
include \
|
||||
tests \
|
||||
Android.bp \
|
||||
lib/Android.bp \
|
||||
mkfs/Android.bp \
|
||||
|
||||
@@ -1,3 +1,80 @@
|
||||
exfatprogs 1.2.5 - released 2024-08-06
|
||||
======================================
|
||||
|
||||
CHANGES :
|
||||
* exfatprogs: remove the limitation that the device
|
||||
path length cannot exceed 254 bytes.
|
||||
* exfatprogs: include the test images in the release
|
||||
package.
|
||||
|
||||
NEW FEATURES :
|
||||
* fsck.exfat: check and repair the filename which has
|
||||
invalid characters.
|
||||
|
||||
BUG FIXES :
|
||||
* tune.exfat: check whether the volume has invalid
|
||||
characters correctly.
|
||||
* fsck.exfat: check whether the filename and volume
|
||||
has invalid characters correctly.
|
||||
* fsck.exfat: fix endianess issues which happen
|
||||
in the big-endian system.
|
||||
|
||||
exfatprogs 1.2.4 - released 2024-06-17
|
||||
======================================
|
||||
|
||||
BUG FIXES :
|
||||
* tune.exfat: Fix "invalid serial number" error when
|
||||
setting an serial number.
|
||||
* fsck.exfat: Fix memory leak in an error path
|
||||
|
||||
exfatprogs 1.2.3 - released 2024-05-23
|
||||
======================================
|
||||
|
||||
CHANGES :
|
||||
* dump.exfat: Report sector size in bytes and cluster size in
|
||||
terms of sectors.
|
||||
* fsck.exfat: Show checksum value if the SetChecksum of File
|
||||
directory entry is invalid.
|
||||
* mkfs.exfat: Improve FAT length calculation to reduce
|
||||
the FAT size.
|
||||
|
||||
NEW FEATURES :
|
||||
* mkfs.exfat: Add the option "--sector-size".
|
||||
* fsck.exfat: Support checking and repairing VendorAllcation and
|
||||
VendorExtension directory entries.
|
||||
|
||||
BUG FIXES :
|
||||
* exfatprogs: Remove unnecessary memory allocations.
|
||||
* fsck.exfat: Fix corruption that can occur if the cluster size
|
||||
is 512-byte.
|
||||
* fsck.exfat: Fix the SecondaryCount of File directory entry
|
||||
when the count of Name directory entries is 17 or higher.
|
||||
* tune.exfat: Fix an error that accepts invalid serial numbers.
|
||||
|
||||
exfatprogs 1.2.2 - released 2023-10-26
|
||||
======================================
|
||||
|
||||
CHANGES :
|
||||
* exfat2img: Allow dumps for read-only devices.
|
||||
* fsck.exfat: Revert Repairing zero size directory.
|
||||
|
||||
NEW FEATURES :
|
||||
* fsck.exfat: Repair duplicated filename.
|
||||
* mkfs.exfat: Add the option "q" to print only error messages.
|
||||
* mkfs.exfat: Add the option "U" to set volume GUID.
|
||||
* tune.exfat: Add the option "U" / "-u" to set or print volume GUID.
|
||||
|
||||
BUG FIXES:
|
||||
* fsck.exfat: Fix some out-of-bounds memory accesses.
|
||||
* fsck.exfat: Change not to delete volume GUID directory entry.
|
||||
|
||||
exfatprogs 1.2.1 - released 2023-05-17
|
||||
======================================
|
||||
|
||||
CHANGES :
|
||||
* fsck.exfat: Repair zero size directory.
|
||||
* fsck.exfat: Four small clean-ups.
|
||||
|
||||
exfatprogs 1.2.0 - released 2022-10-28
|
||||
======================================
|
||||
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@
|
||||
"Name" : "exfatprogs",
|
||||
"License" : "GPL-2.0-or-later",
|
||||
"License File" : "COPYING",
|
||||
"Version Number" : "1.2.0",
|
||||
"Version Number" : "1.2.5",
|
||||
"Owner" : "Namjae Jeon",
|
||||
"Upstream URL" : "https://github.com/exfatprogs/exfatprogs/archive/refs/tags/1.2.0.tar.gz",
|
||||
"Upstream URL" : "https://github.com/exfatprogs/exfatprogs/archive/refs/tags/1.2.5.tar.gz",
|
||||
"Description" : "exFAT filesystem userspace utilities."
|
||||
}
|
||||
]
|
||||
|
||||
Executable → Regular
+5
-5
@@ -131,8 +131,8 @@ static int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd)
|
||||
exfat_info("Cluster Count: \t\t\t\t%u\n", total_clus);
|
||||
exfat_info("Root Cluster (cluster offset): \t\t%u\n", root_clu);
|
||||
exfat_info("Volume Serial: \t\t\t\t0x%x\n", le32_to_cpu(pbsx->vol_serial));
|
||||
exfat_info("Sector Size Bits: \t\t\t%u\n", pbsx->sect_size_bits);
|
||||
exfat_info("Sector per Cluster bits: \t\t%u\n\n", pbsx->sect_per_clus_bits);
|
||||
exfat_info("Bytes per Sector: \t\t\t%u\n", 1 << pbsx->sect_size_bits);
|
||||
exfat_info("Sectors per Cluster: \t\t\t%u\n\n", 1 << pbsx->sect_per_clus_bits);
|
||||
|
||||
bd->cluster_size =
|
||||
1 << (pbsx->sect_per_clus_bits + pbsx->sect_size_bits);
|
||||
@@ -223,6 +223,7 @@ int main(int argc, char *argv[])
|
||||
bool version_only = false;
|
||||
|
||||
init_user_input(&ui);
|
||||
ui.writeable = false;
|
||||
|
||||
if (!setlocale(LC_CTYPE, ""))
|
||||
exfat_err("failed to init locale/codeset\n");
|
||||
@@ -243,11 +244,10 @@ int main(int argc, char *argv[])
|
||||
if (version_only)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (argc < 2)
|
||||
if (argc - optind != 1)
|
||||
usage();
|
||||
|
||||
memset(ui.dev_name, 0, sizeof(ui.dev_name));
|
||||
snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[1]);
|
||||
ui.dev_name = argv[1];
|
||||
|
||||
ret = exfat_get_blk_dev_info(&ui, &bd);
|
||||
if (ret < 0)
|
||||
|
||||
+41
-56
@@ -52,7 +52,7 @@ struct exfat2img {
|
||||
bool save_cc;
|
||||
struct exfat_blk_dev bdev;
|
||||
struct exfat *exfat;
|
||||
struct buffer_desc *dump_bdesc;
|
||||
void *dump_cluster;
|
||||
struct buffer_desc *scan_bdesc;
|
||||
struct exfat_de_iter de_iter;
|
||||
};
|
||||
@@ -96,12 +96,12 @@ static void usage(const char *name)
|
||||
|
||||
static void free_exfat2img(struct exfat2img *ei)
|
||||
{
|
||||
if (ei->scan_bdesc)
|
||||
exfat_free_buffer(ei->exfat, ei->scan_bdesc);
|
||||
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->dump_cluster)
|
||||
free(ei->dump_cluster);
|
||||
if (ei->out_fd)
|
||||
close(ei->out_fd);
|
||||
if (ei->bdev.dev_fd)
|
||||
@@ -118,17 +118,13 @@ static int create_exfat2img(struct exfat2img *ei,
|
||||
if (!ei->exfat)
|
||||
return -ENOMEM;
|
||||
|
||||
ei->dump_bdesc = exfat_alloc_buffer(2,
|
||||
ei->exfat->clus_size,
|
||||
ei->exfat->sect_size);
|
||||
if (!ei->dump_bdesc) {
|
||||
ei->dump_cluster = malloc(ei->exfat->clus_size);
|
||||
if (!ei->dump_cluster) {
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ei->scan_bdesc = exfat_alloc_buffer(2,
|
||||
ei->exfat->clus_size,
|
||||
ei->exfat->sect_size);
|
||||
ei->scan_bdesc = exfat_alloc_buffer(ei->exfat);
|
||||
if (!ei->scan_bdesc) {
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
@@ -180,8 +176,7 @@ static ssize_t dump_range(struct exfat2img *ei, off_t start, off_t 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);
|
||||
ei->dump_cluster, len, start);
|
||||
if (ret != (ssize_t)len) {
|
||||
exfat_err("failed to read %llu bytes at %llu\n",
|
||||
(unsigned long long)len,
|
||||
@@ -189,8 +184,7 @@ static ssize_t dump_range(struct exfat2img *ei, off_t start, off_t end)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = pwrite(ei->out_fd, ei->dump_bdesc[0].buffer,
|
||||
len, start);
|
||||
ret = pwrite(ei->out_fd, ei->dump_cluster, len, start);
|
||||
if (ret != (ssize_t)len) {
|
||||
exfat_err("failed to write %llu bytes at %llu\n",
|
||||
(unsigned long long)len,
|
||||
@@ -552,7 +546,7 @@ out:
|
||||
}
|
||||
|
||||
static int dump_bytes_to_stdout(struct exfat2img *ei,
|
||||
off_t start, off_t end_excl, bool fill_zero)
|
||||
off_t start, off_t end_excl)
|
||||
{
|
||||
struct exfat *exfat = ei->exfat;
|
||||
size_t len;
|
||||
@@ -567,32 +561,21 @@ static int dump_bytes_to_stdout(struct exfat2img *ei,
|
||||
|
||||
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 = exfat_read(exfat->blk_dev->dev_fd, ei->dump_cluster,
|
||||
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;
|
||||
}
|
||||
ret = write(ei->out_fd, ei->dump_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;
|
||||
@@ -644,8 +627,7 @@ static int dump_clusters_to_stdout(struct exfat2img *ei,
|
||||
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)
|
||||
if (dump_bytes_to_stdout(ei, start_off, end_off_excl) < 0)
|
||||
return -EIO;
|
||||
} else {
|
||||
ei->stdout_offset += (off_t)cc_clu_count * ei->exfat->clus_size;
|
||||
@@ -665,7 +647,7 @@ static int dump_to_stdout(struct exfat2img *ei)
|
||||
|
||||
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) {
|
||||
if (dump_bytes_to_stdout(ei, start_off, end_off) < 0) {
|
||||
exfat_err("failed to dump boot sectors and FAT tables\n");
|
||||
return -EIO;
|
||||
}
|
||||
@@ -754,7 +736,7 @@ static ssize_t read_stream(int fd, void *buf, size_t len)
|
||||
} else if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
buf += (size_t)ret;
|
||||
buf = (char *)buf + (size_t)ret;
|
||||
read_len += (size_t)ret;
|
||||
}
|
||||
return read_len;
|
||||
@@ -762,7 +744,7 @@ static ssize_t read_stream(int fd, void *buf, size_t len)
|
||||
|
||||
static int restore_from_stdin(struct exfat2img *ei)
|
||||
{
|
||||
int in_fd, ret;
|
||||
int in_fd, ret = 0;
|
||||
unsigned char cc;
|
||||
unsigned int clu, end_clu;
|
||||
unsigned int cc_clu_count;
|
||||
@@ -792,8 +774,8 @@ static int restore_from_stdin(struct exfat2img *ei)
|
||||
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)
|
||||
ei->dump_cluster = malloc(clus_size);
|
||||
if (!ei->dump_cluster)
|
||||
return -ENOMEM;
|
||||
|
||||
/* restore boot regions, and FAT tables */
|
||||
@@ -802,7 +784,7 @@ static int restore_from_stdin(struct exfat2img *ei)
|
||||
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) {
|
||||
if (read_stream(in_fd, ei->dump_cluster, 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);
|
||||
@@ -810,7 +792,7 @@ static int restore_from_stdin(struct exfat2img *ei)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pwrite(ei->out_fd, ei->dump_bdesc[0].buffer, len, out_start_off)
|
||||
if (pwrite(ei->out_fd, ei->dump_cluster, len, out_start_off)
|
||||
!= (ssize_t)len) {
|
||||
exfat_err("failed to write first meta region. %llu ~ %llu\n",
|
||||
(unsigned long long)out_start_off,
|
||||
@@ -856,7 +838,7 @@ static int restore_from_stdin(struct exfat2img *ei)
|
||||
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,
|
||||
if (read_stream(in_fd, ei->dump_cluster,
|
||||
clus_size) != (ssize_t)clus_size) {
|
||||
exfat_err("failed to read range %llu ~ %llu\n",
|
||||
(unsigned long long)in_start_off,
|
||||
@@ -864,7 +846,7 @@ static int restore_from_stdin(struct exfat2img *ei)
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (pwrite(ei->out_fd, ei->dump_bdesc[0].buffer,
|
||||
if (pwrite(ei->out_fd, ei->dump_cluster,
|
||||
clus_size, out_start_off) != (ssize_t)clus_size) {
|
||||
exfat_err("failed to write range %llu ~ %llu\n",
|
||||
(unsigned long long)out_start_off,
|
||||
@@ -890,8 +872,11 @@ static int restore_from_stdin(struct exfat2img *ei)
|
||||
}
|
||||
}
|
||||
out:
|
||||
fsync(ei->out_fd);
|
||||
exfat_free_buffer(ei->dump_bdesc, 2);
|
||||
if (fsync(ei->out_fd)) {
|
||||
exfat_err("failed to fsync: %d\n", errno);
|
||||
ret = -EIO;
|
||||
}
|
||||
free(ei->dump_cluster);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -941,7 +926,7 @@ int main(int argc, char * const argv[])
|
||||
}
|
||||
|
||||
memset(&ui, 0, sizeof(ui));
|
||||
snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", blkdev_path);
|
||||
ui.dev_name = blkdev_path;
|
||||
if (restore)
|
||||
ui.writeable = true;
|
||||
else
|
||||
|
||||
+234
-212
@@ -102,7 +102,7 @@ static void usage(char *name)
|
||||
exfat_de_iter_device_offset(iter)); \
|
||||
})
|
||||
|
||||
static int check_clus_chain(struct exfat_de_iter *de_iter,
|
||||
static int check_clus_chain(struct exfat_de_iter *de_iter, int stream_idx,
|
||||
struct exfat_inode *node)
|
||||
{
|
||||
struct exfat *exfat = de_iter->exfat;
|
||||
@@ -220,8 +220,8 @@ truncate_file:
|
||||
if (!exfat_heap_clus(exfat, prev))
|
||||
node->first_clus = EXFAT_FREE_CLUSTER;
|
||||
|
||||
exfat_de_iter_get_dirty(de_iter, 1, &stream_de);
|
||||
if (count * exfat->clus_size <
|
||||
exfat_de_iter_get_dirty(de_iter, stream_idx, &stream_de);
|
||||
if (stream_idx == 1 && count * exfat->clus_size <
|
||||
le64_to_cpu(stream_de->stream_valid_size))
|
||||
stream_de->stream_valid_size = cpu_to_le64(
|
||||
count * exfat->clus_size);
|
||||
@@ -371,9 +371,10 @@ static int read_boot_region(struct exfat_blk_dev *bd, struct pbr **pbr,
|
||||
{
|
||||
struct pbr *bs;
|
||||
int ret = -EINVAL;
|
||||
unsigned long long clu_max_count;
|
||||
|
||||
*pbr = NULL;
|
||||
bs = (struct pbr *)malloc(sizeof(struct pbr));
|
||||
bs = malloc(sizeof(struct pbr));
|
||||
if (!bs) {
|
||||
exfat_err("failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
@@ -434,12 +435,13 @@ static int read_boot_region(struct exfat_blk_dev *bd, struct pbr **pbr,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(bs->bsx.clu_count) * EXFAT_CLUSTER_SIZE(bs) >
|
||||
bd->size) {
|
||||
clu_max_count = (le64_to_cpu(bs->bsx.vol_length) - le32_to_cpu(bs->bsx.clu_offset)) >>
|
||||
bs->bsx.sect_per_clus_bits;
|
||||
if (le32_to_cpu(bs->bsx.clu_count) > clu_max_count) {
|
||||
if (verbose)
|
||||
exfat_err("too large cluster count: %u, expected: %u\n",
|
||||
exfat_err("too large cluster count: %u, expected: %llu\n",
|
||||
le32_to_cpu(bs->bsx.clu_count),
|
||||
bd->num_clusters);
|
||||
MIN(clu_max_count, EXFAT_MAX_NUM_CLUSTER));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -565,21 +567,24 @@ restore:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint16_t file_calc_checksum(struct exfat_de_iter *iter)
|
||||
static int file_calc_checksum(struct exfat_de_iter *iter, uint16_t *checksum)
|
||||
{
|
||||
uint16_t checksum;
|
||||
struct exfat_dentry *file_de, *de;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
checksum = 0;
|
||||
exfat_de_iter_get(iter, 0, &file_de);
|
||||
*checksum = 0;
|
||||
ret = exfat_de_iter_get(iter, 0, &file_de);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
exfat_calc_dentry_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);
|
||||
exfat_calc_dentry_checksum(de, &checksum, false);
|
||||
ret = exfat_de_iter_get(iter, i, &de);
|
||||
if (ret)
|
||||
return ret;
|
||||
exfat_calc_dentry_checksum(de, checksum, false);
|
||||
}
|
||||
return checksum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -594,7 +599,7 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node)
|
||||
uint16_t checksum;
|
||||
bool valid = true;
|
||||
|
||||
ret = check_clus_chain(iter, node);
|
||||
ret = check_clus_chain(iter, 1, node);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -624,7 +629,9 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node)
|
||||
valid = false;
|
||||
}
|
||||
|
||||
checksum = file_calc_checksum(iter);
|
||||
ret = file_calc_checksum(iter, &checksum);
|
||||
if (ret)
|
||||
return ret;
|
||||
exfat_de_iter_get(iter, 0, &dentry);
|
||||
if (checksum != le16_to_cpu(dentry->file_checksum)) {
|
||||
exfat_de_iter_get_dirty(iter, 0, &dentry);
|
||||
@@ -635,178 +642,97 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node)
|
||||
return valid ? ret : -EINVAL;
|
||||
}
|
||||
|
||||
static int handle_duplicated_filename(struct exfat_de_iter *iter,
|
||||
struct exfat_inode *inode)
|
||||
{
|
||||
int ret;
|
||||
struct exfat_lookup_filter filter;
|
||||
|
||||
ret = exfat_lookup_file_by_utf16name(iter->exfat, iter->parent,
|
||||
inode->name, &filter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
free(filter.out.dentry_set);
|
||||
|
||||
/* Hash is same, but filename is not same */
|
||||
if (exfat_de_iter_device_offset(iter) == filter.out.dev_offset)
|
||||
return 0;
|
||||
|
||||
return exfat_repair_rename_ask(&exfat_fsck, iter, inode->name,
|
||||
ER_DE_DUPLICATED_NAME, "filename is duplicated");
|
||||
}
|
||||
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
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 (name_len && 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;
|
||||
ret = 1;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = exfat_check_name(inode->name, stream_de->stream_name_len);
|
||||
if (ret != stream_de->stream_name_len) {
|
||||
char err_msg[36];
|
||||
|
||||
snprintf(err_msg, sizeof(err_msg),
|
||||
"filename has invalid character '%c'",
|
||||
le16_to_cpu(inode->name[ret]));
|
||||
|
||||
return exfat_repair_rename_ask(&exfat_fsck, iter, inode->name,
|
||||
ER_DE_INVALID_NAME, err_msg);
|
||||
}
|
||||
|
||||
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);
|
||||
ret = 1;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
if (BITMAP_GET(iter->name_hash_bitmap, hash)) {
|
||||
ret = handle_duplicated_filename(iter, inode);
|
||||
} else
|
||||
BITMAP_SET(iter->name_hash_bitmap, hash);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_bad_char(char w)
|
||||
{
|
||||
return (w < 0x0020) || (w == '*') || (w == '?') || (w == '<') ||
|
||||
(w == '>') || (w == '|') || (w == '"') || (w == ':') ||
|
||||
(w == '/') || (w == '\\');
|
||||
}
|
||||
|
||||
static char *get_rename_from_user(struct exfat_de_iter *iter)
|
||||
{
|
||||
char *rename = malloc(ENTRY_NAME_MAX + 2);
|
||||
|
||||
if (!rename)
|
||||
return NULL;
|
||||
|
||||
retry:
|
||||
/* +2 means LF(Line Feed) and NULL terminator */
|
||||
memset(rename, 0x1, ENTRY_NAME_MAX + 2);
|
||||
printf("New name: ");
|
||||
if (fgets(rename, ENTRY_NAME_MAX + 2, stdin)) {
|
||||
int i, len, err;
|
||||
struct exfat_lookup_filter filter;
|
||||
|
||||
len = strlen(rename);
|
||||
/* Remove LF in filename */
|
||||
rename[len - 1] = '\0';
|
||||
for (i = 0; i < len - 1; i++) {
|
||||
if (check_bad_char(rename[i])) {
|
||||
printf("filename contain invalid character(%c)\n", rename[i]);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
exfat_de_iter_flush(iter);
|
||||
err = exfat_lookup_file(iter->exfat, iter->parent, rename, &filter);
|
||||
if (!err) {
|
||||
printf("file(%s) already exists, retry to insert name\n", rename);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
return rename;
|
||||
}
|
||||
|
||||
static char *generate_rename(struct exfat_de_iter *iter)
|
||||
{
|
||||
char *rename;
|
||||
|
||||
if (iter->dot_name_num > DOT_NAME_NUM_MAX)
|
||||
return NULL;
|
||||
|
||||
rename = malloc(ENTRY_NAME_MAX + 1);
|
||||
if (!rename)
|
||||
return NULL;
|
||||
|
||||
while (1) {
|
||||
struct exfat_lookup_filter filter;
|
||||
int err;
|
||||
|
||||
snprintf(rename, ENTRY_NAME_MAX + 1, "FILE%07d.CHK",
|
||||
iter->dot_name_num++);
|
||||
err = exfat_lookup_file(iter->exfat, iter->parent, rename,
|
||||
&filter);
|
||||
if (!err)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
return rename;
|
||||
}
|
||||
|
||||
const __le16 MSDOS_DOT[ENTRY_NAME_MAX] = {cpu_to_le16(46), 0, };
|
||||
const __le16 MSDOS_DOTDOT[ENTRY_NAME_MAX] = {cpu_to_le16(46), cpu_to_le16(46), 0, };
|
||||
|
||||
static int handle_dot_dotdot_filename(struct exfat_de_iter *iter,
|
||||
struct exfat_dentry *dentry,
|
||||
__le16 *filename,
|
||||
int strm_name_len)
|
||||
{
|
||||
char *filename;
|
||||
char error_msg[150];
|
||||
int num;
|
||||
int i;
|
||||
|
||||
if (!memcmp(dentry->name_unicode, MSDOS_DOT, strm_name_len * 2))
|
||||
filename = ".";
|
||||
else if (!memcmp(dentry->name_unicode, MSDOS_DOTDOT,
|
||||
strm_name_len * 2))
|
||||
filename = "..";
|
||||
else
|
||||
return 0;
|
||||
|
||||
sprintf(error_msg, "ERROR: '%s' filename is not allowed.\n"
|
||||
" [1] Insert the name you want to rename.\n"
|
||||
" [2] Automatically renames filename.\n"
|
||||
" [3] Bypass this check(No repair)\n", filename);
|
||||
ask_again:
|
||||
num = exfat_repair_ask(&exfat_fsck, ER_DE_DOT_NAME,
|
||||
error_msg);
|
||||
if (num) {
|
||||
__le16 utf16_name[ENTRY_NAME_MAX];
|
||||
char *rename = NULL;
|
||||
__u16 hash;
|
||||
struct exfat_dentry *stream_de;
|
||||
int ret;
|
||||
|
||||
switch (num) {
|
||||
case 1:
|
||||
rename = get_rename_from_user(iter);
|
||||
break;
|
||||
case 2:
|
||||
rename = generate_rename(iter);
|
||||
break;
|
||||
case 3:
|
||||
break;
|
||||
default:
|
||||
exfat_info("select 1 or 2 number instead of %d\n", num);
|
||||
goto ask_again;
|
||||
}
|
||||
|
||||
if (!rename)
|
||||
return -EINVAL;
|
||||
|
||||
exfat_info("%s filename is renamed to %s\n", filename, rename);
|
||||
|
||||
exfat_de_iter_get_dirty(iter, 2, &dentry);
|
||||
|
||||
memset(utf16_name, 0, sizeof(utf16_name));
|
||||
ret = exfat_utf16_enc(rename, utf16_name, sizeof(utf16_name));
|
||||
free(rename);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret >>= 1;
|
||||
memcpy(dentry->name_unicode, utf16_name, ENTRY_NAME_MAX * 2);
|
||||
hash = exfat_calc_name_hash(iter->exfat, utf16_name, ret);
|
||||
exfat_de_iter_get_dirty(iter, 1, &stream_de);
|
||||
stream_de->stream_name_len = (__u8)ret;
|
||||
stream_de->stream_name_hash = cpu_to_le16(hash);
|
||||
for (i = 0; i < strm_name_len; i++) {
|
||||
if (filename[i] != UTF16_DOT)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (filename[i])
|
||||
return 0;
|
||||
|
||||
return exfat_repair_rename_ask(&exfat_fsck, iter, filename,
|
||||
ER_DE_DOT_NAME, "filename is not allowed");
|
||||
}
|
||||
|
||||
static int read_file_dentry_set(struct exfat_de_iter *iter,
|
||||
@@ -814,8 +740,8 @@ static int read_file_dentry_set(struct exfat_de_iter *iter,
|
||||
{
|
||||
struct exfat_dentry *file_de, *stream_de, *dentry;
|
||||
struct exfat_inode *node = NULL;
|
||||
int i, ret;
|
||||
bool need_delete = false;
|
||||
int i, j, ret, name_de_count;
|
||||
bool need_delete = false, need_copy_up = false;
|
||||
uint16_t checksum;
|
||||
|
||||
ret = exfat_de_iter_get(iter, 0, &file_de);
|
||||
@@ -824,10 +750,11 @@ static int read_file_dentry_set(struct exfat_de_iter *iter,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
checksum = file_calc_checksum(iter);
|
||||
if (checksum != le16_to_cpu(file_de->file_checksum)) {
|
||||
ret = file_calc_checksum(iter, &checksum);
|
||||
if (ret || checksum != le16_to_cpu(file_de->file_checksum)) {
|
||||
if (repair_file_ask(iter, NULL, ER_DE_CHECKSUM,
|
||||
"the checksum of a file is wrong"))
|
||||
"the checksum %#x of a file is wrong, expected: %#x",
|
||||
le16_to_cpu(file_de->file_checksum), checksum))
|
||||
need_delete = true;
|
||||
*skip_dentries = 1;
|
||||
goto skip_dset;
|
||||
@@ -856,17 +783,22 @@ static int read_file_dentry_set(struct exfat_de_iter *iter,
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 2; i <= MIN(file_de->file_num_ext, 1 + MAX_NAME_DENTRIES); i++) {
|
||||
name_de_count = DIV_ROUND_UP(stream_de->stream_name_len, ENTRY_NAME_MAX);
|
||||
for (i = 2; i <= MIN(name_de_count + 1, file_de->file_num_ext); i++) {
|
||||
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;
|
||||
if (repair_file_ask(iter, NULL, ER_DE_NAME,
|
||||
"failed to get name dentry")) {
|
||||
if (i == 2) {
|
||||
need_delete = 1;
|
||||
*skip_dentries = i + 1;
|
||||
goto skip_dset;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
*skip_dentries = i + 1;
|
||||
goto skip_dset;
|
||||
}
|
||||
*skip_dentries = i + 1;
|
||||
goto skip_dset;
|
||||
}
|
||||
|
||||
memcpy(node->name +
|
||||
@@ -875,13 +807,18 @@ static int read_file_dentry_set(struct exfat_de_iter *iter,
|
||||
}
|
||||
|
||||
ret = check_name_dentry_set(iter, node);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
*skip_dentries = file_de->file_num_ext + 1;
|
||||
goto skip_dset;
|
||||
} else if (ret) {
|
||||
exfat_de_iter_get(iter, 1, &stream_de);
|
||||
if (DIV_ROUND_UP(stream_de->stream_name_len, ENTRY_NAME_MAX) !=
|
||||
name_de_count)
|
||||
i = DIV_ROUND_UP(stream_de->stream_name_len, ENTRY_NAME_MAX) + 2;
|
||||
}
|
||||
|
||||
if (file_de->file_num_ext == 2 && stream_de->stream_name_len <= 2) {
|
||||
ret = handle_dot_dotdot_filename(iter, dentry,
|
||||
ret = handle_dot_dotdot_filename(iter, node->name,
|
||||
stream_de->stream_name_len);
|
||||
if (ret < 0) {
|
||||
*skip_dentries = file_de->file_num_ext + 1;
|
||||
@@ -889,6 +826,69 @@ static int read_file_dentry_set(struct exfat_de_iter *iter,
|
||||
}
|
||||
}
|
||||
|
||||
for (j = i; i <= file_de->file_num_ext; i++) {
|
||||
exfat_de_iter_get(iter, i, &dentry);
|
||||
if (dentry->type == EXFAT_VENDOR_EXT ||
|
||||
dentry->type == EXFAT_VENDOR_ALLOC) {
|
||||
char zeroes[EXFAT_GUID_LEN] = {0};
|
||||
/*
|
||||
* Vendor GUID should not be zero, But Windows fsck
|
||||
* also does not check and fix it.
|
||||
*/
|
||||
if (!memcmp(dentry->dentry.vendor_ext.guid,
|
||||
zeroes, EXFAT_GUID_LEN))
|
||||
repair_file_ask(iter, NULL, ER_VENDOR_GUID,
|
||||
"Vendor Extension has zero filled GUID");
|
||||
if (dentry->type == EXFAT_VENDOR_ALLOC) {
|
||||
struct exfat_inode *vendor_node;
|
||||
|
||||
/* verify cluster chain */
|
||||
vendor_node = exfat_alloc_inode(0);
|
||||
if (!vendor_node) {
|
||||
*skip_dentries = i + i;
|
||||
goto skip_dset;
|
||||
}
|
||||
vendor_node->first_clus =
|
||||
le32_to_cpu(dentry->dentry.vendor_alloc.start_clu);
|
||||
vendor_node->is_contiguous = ((dentry->dentry.vendor_alloc.flags
|
||||
& EXFAT_SF_CONTIGUOUS) != 0);
|
||||
vendor_node->size =
|
||||
le64_to_cpu(dentry->dentry.vendor_alloc.size);
|
||||
if (check_clus_chain(iter, i, vendor_node) < 0) {
|
||||
exfat_free_inode(vendor_node);
|
||||
*skip_dentries = i + 1;
|
||||
goto skip_dset;
|
||||
}
|
||||
if (vendor_node->size == 0 &&
|
||||
vendor_node->is_contiguous) {
|
||||
exfat_de_iter_get_dirty(iter, i, &dentry);
|
||||
dentry->stream_flags &= ~EXFAT_SF_CONTIGUOUS;
|
||||
|
||||
}
|
||||
exfat_free_inode(vendor_node);
|
||||
}
|
||||
|
||||
if (need_copy_up) {
|
||||
struct exfat_dentry *src_de;
|
||||
|
||||
exfat_de_iter_get_dirty(iter, j, &src_de);
|
||||
memcpy(src_de, dentry, sizeof(struct exfat_dentry));
|
||||
}
|
||||
j++;
|
||||
} else {
|
||||
if (need_copy_up) {
|
||||
continue;
|
||||
} else if (repair_file_ask(iter, NULL, ER_DE_UNKNOWN,
|
||||
"unknown entry type %#x", dentry->type)) {
|
||||
j = i;
|
||||
need_copy_up = true;
|
||||
} else {
|
||||
*skip_dentries = i + 1;
|
||||
goto skip_dset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node->first_clus = le32_to_cpu(stream_de->stream_start_clu);
|
||||
node->is_contiguous =
|
||||
((stream_de->stream_flags & EXFAT_SF_CONTIGUOUS) != 0);
|
||||
@@ -909,6 +909,18 @@ static int read_file_dentry_set(struct exfat_de_iter *iter,
|
||||
}
|
||||
}
|
||||
|
||||
if (file_de->file_num_ext != j - 1) {
|
||||
if (repair_file_ask(iter, node, ER_DE_SECONDARY_COUNT,
|
||||
"SecondaryCount %d is different with %d",
|
||||
file_de->file_num_ext, j - 1)) {
|
||||
exfat_de_iter_get_dirty(iter, 0, &file_de);
|
||||
file_de->file_num_ext = j - 1;
|
||||
} else {
|
||||
*skip_dentries = file_de->file_num_ext + 1;
|
||||
goto skip_dset;
|
||||
}
|
||||
}
|
||||
|
||||
*skip_dentries = (file_de->file_num_ext + 1);
|
||||
*new_node = node;
|
||||
return 0;
|
||||
@@ -962,6 +974,7 @@ static int read_bitmap(struct exfat *exfat)
|
||||
{
|
||||
struct exfat_lookup_filter filter = {
|
||||
.in.type = EXFAT_BITMAP,
|
||||
.in.dentry_count = 0,
|
||||
.in.filter = NULL,
|
||||
.in.param = NULL,
|
||||
};
|
||||
@@ -993,7 +1006,7 @@ static int read_bitmap(struct exfat *exfat)
|
||||
exfat->disk_bitmap_size = DIV_ROUND_UP(exfat->clus_count, 8);
|
||||
|
||||
exfat_bitmap_set_range(exfat, exfat->alloc_bitmap,
|
||||
le64_to_cpu(dentry->bitmap_start_clu),
|
||||
le32_to_cpu(dentry->bitmap_start_clu),
|
||||
DIV_ROUND_UP(exfat->disk_bitmap_size,
|
||||
exfat->clus_size));
|
||||
free(filter.out.dentry_set);
|
||||
@@ -1022,9 +1035,8 @@ static int decompress_upcase_table(const __le16 *in_table, size_t in_len,
|
||||
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;
|
||||
++i;
|
||||
k += le16_to_cpu(in_table[i]);
|
||||
} else {
|
||||
out_table[k++] = ch;
|
||||
}
|
||||
@@ -1036,6 +1048,7 @@ static int read_upcase_table(struct exfat *exfat)
|
||||
{
|
||||
struct exfat_lookup_filter filter = {
|
||||
.in.type = EXFAT_UPCASE,
|
||||
.in.dentry_count = 0,
|
||||
.in.filter = NULL,
|
||||
.in.param = NULL,
|
||||
};
|
||||
@@ -1067,7 +1080,7 @@ static int read_upcase_table(struct exfat *exfat)
|
||||
goto out;
|
||||
}
|
||||
|
||||
upcase = (__le16 *)malloc(size);
|
||||
upcase = malloc(size);
|
||||
if (!upcase) {
|
||||
exfat_err("failed to allocate upcase table\n");
|
||||
retval = -ENOMEM;
|
||||
@@ -1096,8 +1109,7 @@ static int read_upcase_table(struct exfat *exfat)
|
||||
DIV_ROUND_UP(le64_to_cpu(dentry->upcase_size),
|
||||
exfat->clus_size));
|
||||
|
||||
exfat->upcase_table = calloc(1,
|
||||
sizeof(uint16_t) * EXFAT_UPCASE_TABLE_CHARS);
|
||||
exfat->upcase_table = calloc(EXFAT_UPCASE_TABLE_CHARS, sizeof(uint16_t));
|
||||
if (!exfat->upcase_table) {
|
||||
retval = -EIO;
|
||||
goto out;
|
||||
@@ -1129,6 +1141,10 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir)
|
||||
else if (ret)
|
||||
return ret;
|
||||
|
||||
de_iter->name_hash_bitmap = fsck->name_hash_bitmap;
|
||||
memset(fsck->name_hash_bitmap, 0,
|
||||
EXFAT_BITMAP_SIZE(EXFAT_MAX_HASH_COUNT));
|
||||
|
||||
while (1) {
|
||||
ret = exfat_de_iter_get(de_iter, 0, &dentry);
|
||||
if (ret == EOF) {
|
||||
@@ -1169,6 +1185,7 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir)
|
||||
case EXFAT_VOLUME:
|
||||
case EXFAT_BITMAP:
|
||||
case EXFAT_UPCASE:
|
||||
case EXFAT_GUID:
|
||||
if (dir == exfat->root)
|
||||
break;
|
||||
/* fallthrough */
|
||||
@@ -1176,9 +1193,7 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir)
|
||||
if (IS_EXFAT_DELETED(dentry->type))
|
||||
break;
|
||||
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))) {
|
||||
"unknown entry type %#x", dentry->type)) {
|
||||
struct exfat_dentry *dentry;
|
||||
|
||||
exfat_de_iter_get_dirty(de_iter, 0, &dentry);
|
||||
@@ -1258,6 +1273,12 @@ static int exfat_filesystem_check(struct exfat_fsck *fsck)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
fsck->name_hash_bitmap = malloc(EXFAT_BITMAP_SIZE(EXFAT_MAX_HASH_COUNT));
|
||||
if (!fsck->name_hash_bitmap) {
|
||||
exfat_err("failed to allocate name hash bitmap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_add(&exfat->root->list, &exfat->dir_list);
|
||||
|
||||
while (!list_empty(&exfat->dir_list)) {
|
||||
@@ -1286,6 +1307,7 @@ static int exfat_filesystem_check(struct exfat_fsck *fsck)
|
||||
}
|
||||
out:
|
||||
exfat_free_dir_list(exfat);
|
||||
free(fsck->name_hash_bitmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1316,7 +1338,6 @@ static int exfat_root_dir_check(struct exfat *exfat)
|
||||
err = exfat_read_volume_label(exfat);
|
||||
if (err && err != EOF)
|
||||
exfat_err("failed to read volume label\n");
|
||||
err = 0;
|
||||
|
||||
err = read_bitmap(exfat);
|
||||
if (err) {
|
||||
@@ -1385,9 +1406,40 @@ static int rescue_orphan_clusters(struct exfat_fsck *fsck)
|
||||
struct exfat_dentry_loc loc;
|
||||
struct exfat_lookup_filter lf = {
|
||||
.in.type = EXFAT_INVAL,
|
||||
.in.dentry_count = 0,
|
||||
.in.filter = NULL,
|
||||
};
|
||||
|
||||
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];
|
||||
|
||||
/* no orphan clusters */
|
||||
if (exfat_bitmap_find_one(exfat, exfat->ohead_bitmap,
|
||||
EXFAT_FIRST_CLUSTER, &s_clu))
|
||||
return 0;
|
||||
|
||||
err = exfat_create_file(exfat_fsck.exfat,
|
||||
exfat_fsck.exfat->root,
|
||||
"LOST+FOUND",
|
||||
ATTR_SUBDIR);
|
||||
if (err) {
|
||||
exfat_err("failed to create LOST+FOUND directory\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (fsync(exfat_fsck.exfat->blk_dev->dev_fd) != 0) {
|
||||
exfat_err("failed to sync()\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = read_lostfound(exfat, &lostfound);
|
||||
if (err) {
|
||||
exfat_err("failed to find LOST+FOUND\n");
|
||||
@@ -1413,17 +1465,6 @@ static int rescue_orphan_clusters(struct exfat_fsck *fsck)
|
||||
}
|
||||
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.
|
||||
*/
|
||||
@@ -1575,7 +1616,7 @@ int main(int argc, char * const argv[])
|
||||
|
||||
exfat_fsck.options = ui.options;
|
||||
|
||||
snprintf(ui.ei.dev_name, sizeof(ui.ei.dev_name), "%s", argv[optind]);
|
||||
ui.ei.dev_name = argv[optind];
|
||||
ret = exfat_get_blk_dev_info(&ui.ei, &bd);
|
||||
if (ret < 0) {
|
||||
exfat_err("failed to open %s. %d\n", ui.ei.dev_name, ret);
|
||||
@@ -1594,9 +1635,7 @@ int main(int argc, char * const argv[])
|
||||
goto err;
|
||||
}
|
||||
|
||||
exfat_fsck.buffer_desc = exfat_alloc_buffer(2,
|
||||
exfat_fsck.exfat->clus_size,
|
||||
exfat_fsck.exfat->sect_size);
|
||||
exfat_fsck.buffer_desc = exfat_alloc_buffer(exfat_fsck.exfat);
|
||||
if (!exfat_fsck.buffer_desc) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
@@ -1615,23 +1654,6 @@ 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)
|
||||
@@ -1673,7 +1695,7 @@ err:
|
||||
exit_code = FSCK_EXIT_NO_ERRORS;
|
||||
|
||||
if (exfat_fsck.buffer_desc)
|
||||
exfat_free_buffer(exfat_fsck.buffer_desc, 2);
|
||||
exfat_free_buffer(exfat_fsck.exfat, exfat_fsck.buffer_desc);
|
||||
if (exfat_fsck.exfat)
|
||||
exfat_free_exfat(exfat_fsck.exfat);
|
||||
close(bd.dev_fd);
|
||||
|
||||
@@ -28,6 +28,8 @@ struct exfat_fsck {
|
||||
enum fsck_ui_options options;
|
||||
bool dirty:1;
|
||||
bool dirty_fat:1;
|
||||
|
||||
char *name_hash_bitmap;
|
||||
};
|
||||
|
||||
off_t exfat_c2o(struct exfat *exfat, unsigned int clus);
|
||||
|
||||
+141
-2
@@ -6,12 +6,12 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "exfat_ondisk.h"
|
||||
#include "libexfat.h"
|
||||
#include "repair.h"
|
||||
#include "exfat_fs.h"
|
||||
#include "exfat_dir.h"
|
||||
#include "fsck.h"
|
||||
|
||||
struct exfat_repair_problem {
|
||||
@@ -48,12 +48,14 @@ static struct exfat_repair_problem problems[] = {
|
||||
{ER_DE_CHECKSUM, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
|
||||
{ER_DE_UNKNOWN, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
|
||||
{ER_DE_FILE, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
|
||||
{ER_DE_SECONDARY_COUNT, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
|
||||
{ER_DE_SECONDARY_COUNT, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
|
||||
{ER_DE_STREAM, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
|
||||
{ER_DE_NAME, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
|
||||
{ER_DE_NAME_HASH, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
|
||||
{ER_DE_NAME_LEN, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
|
||||
{ER_DE_DOT_NAME, ERF_PREEN_YES, ERP_RENAME, 2, 3, 4},
|
||||
{ER_DE_DUPLICATED_NAME, ERF_PREEN_YES, ERP_RENAME, 2, 3, 4},
|
||||
{ER_DE_INVALID_NAME, ERF_PREEN_YES, ERP_RENAME, 2, 3, 4},
|
||||
{ER_FILE_VALID_SIZE, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
|
||||
{ER_FILE_INVALID_CLUS, ERF_PREEN_YES, ERP_TRUNCATE, 0, 0, 0},
|
||||
{ER_FILE_FIRST_CLUS, ERF_PREEN_YES, ERP_TRUNCATE, 0, 0, 0},
|
||||
@@ -61,6 +63,7 @@ static struct exfat_repair_problem problems[] = {
|
||||
{ER_FILE_LARGER_SIZE, ERF_PREEN_YES, ERP_TRUNCATE, 0, 0, 0},
|
||||
{ER_FILE_DUPLICATED_CLUS, ERF_PREEN_YES, ERP_TRUNCATE, 0, 0, 0},
|
||||
{ER_FILE_ZERO_NOFAT, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
|
||||
{ER_VENDOR_GUID, ERF_DEFAULT_NO, ERP_FIX, 0, 0, 0},
|
||||
};
|
||||
|
||||
static struct exfat_repair_problem *find_problem(er_problem_code_t prcode)
|
||||
@@ -157,3 +160,139 @@ int exfat_repair_ask(struct exfat_fsck *fsck, er_problem_code_t prcode,
|
||||
}
|
||||
return repair;
|
||||
}
|
||||
|
||||
static int get_rename_from_user(struct exfat_de_iter *iter,
|
||||
__le16 *utf16_name, int name_size)
|
||||
{
|
||||
int len = 0;
|
||||
char *rename = malloc(ENTRY_NAME_MAX + 2);
|
||||
|
||||
if (!rename)
|
||||
return -ENOMEM;
|
||||
|
||||
retry:
|
||||
/* +2 means LF(Line Feed) and NULL terminator */
|
||||
memset(rename, 0x1, ENTRY_NAME_MAX + 2);
|
||||
printf("New name: ");
|
||||
if (fgets(rename, ENTRY_NAME_MAX + 2, stdin)) {
|
||||
int err;
|
||||
struct exfat_lookup_filter filter;
|
||||
|
||||
len = strlen(rename);
|
||||
/* Remove LF in filename */
|
||||
rename[len - 1] = '\0';
|
||||
|
||||
memset(utf16_name, 0, name_size);
|
||||
len = exfat_utf16_enc(rename, utf16_name, name_size);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
|
||||
err = exfat_check_name(utf16_name, len >> 1);
|
||||
if (err != len >> 1) {
|
||||
printf("filename contain invalid character(%c)\n",
|
||||
le16_to_cpu(utf16_name[err]));
|
||||
goto retry;
|
||||
}
|
||||
|
||||
exfat_de_iter_flush(iter);
|
||||
err = exfat_lookup_file(iter->exfat, iter->parent, rename, &filter);
|
||||
if (!err) {
|
||||
printf("file(%s) already exists, retry to insert name\n", rename);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(rename);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int generate_rename(struct exfat_de_iter *iter, __le16 *utf16_name,
|
||||
int name_size)
|
||||
{
|
||||
int err;
|
||||
char *rename;
|
||||
|
||||
if (iter->invalid_name_num > INVALID_NAME_NUM_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
rename = malloc(ENTRY_NAME_MAX + 1);
|
||||
if (!rename)
|
||||
return -ENOMEM;
|
||||
|
||||
while (1) {
|
||||
struct exfat_lookup_filter filter;
|
||||
|
||||
snprintf(rename, ENTRY_NAME_MAX + 1, "FILE%07d.CHK",
|
||||
iter->invalid_name_num++);
|
||||
err = exfat_lookup_file(iter->exfat, iter->parent, rename,
|
||||
&filter);
|
||||
if (!err)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
memset(utf16_name, 0, name_size);
|
||||
err = exfat_utf16_enc(rename, utf16_name, name_size);
|
||||
free(rename);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int exfat_repair_rename_ask(struct exfat_fsck *fsck, struct exfat_de_iter *iter,
|
||||
__le16 *uname, er_problem_code_t prcode, char *error_msg)
|
||||
{
|
||||
int num;
|
||||
char old_name[PATH_MAX + 1] = {0};
|
||||
|
||||
if (exfat_utf16_dec(uname, NAME_BUFFER_SIZE, old_name, PATH_MAX) <= 0) {
|
||||
exfat_err("failed to decode filename\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ask_again:
|
||||
num = exfat_repair_ask(fsck, prcode, "ERROR: '%s' %s.\n%s",
|
||||
old_name, error_msg,
|
||||
" [1] Insert the name you want to rename.\n"
|
||||
" [2] Automatically renames filename.\n"
|
||||
" [3] Bypass this check(No repair)\n");
|
||||
if (num) {
|
||||
__le16 utf16_name[ENTRY_NAME_MAX];
|
||||
__u16 hash;
|
||||
struct exfat_dentry *dentry;
|
||||
int ret;
|
||||
|
||||
switch (num) {
|
||||
case 1:
|
||||
ret = get_rename_from_user(iter, utf16_name,
|
||||
sizeof(utf16_name));
|
||||
break;
|
||||
case 2:
|
||||
ret = generate_rename(iter, utf16_name,
|
||||
sizeof(utf16_name));
|
||||
break;
|
||||
case 3:
|
||||
return -EINVAL;
|
||||
default:
|
||||
exfat_info("select 1 or 2 number instead of %d\n", num);
|
||||
goto ask_again;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
exfat_de_iter_get_dirty(iter, 2, &dentry);
|
||||
|
||||
ret >>= 1;
|
||||
memcpy(dentry->name_unicode, utf16_name, ENTRY_NAME_MAX * 2);
|
||||
hash = exfat_calc_name_hash(iter->exfat, utf16_name, ret);
|
||||
exfat_de_iter_get_dirty(iter, 1, &dentry);
|
||||
dentry->stream_name_len = (__u8)ret;
|
||||
dentry->stream_name_hash = cpu_to_le16(hash);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef _REPAIR_H
|
||||
#define _REPAIR_H
|
||||
|
||||
#include "exfat_dir.h"
|
||||
|
||||
#define ER_BS_CHECKSUM 0x00000001
|
||||
#define ER_BS_BOOT_REGION 0x00000002
|
||||
#define ER_DE_CHECKSUM 0x00001001
|
||||
@@ -16,6 +18,8 @@
|
||||
#define ER_DE_NAME_HASH 0x00001031
|
||||
#define ER_DE_NAME_LEN 0x00001032
|
||||
#define ER_DE_DOT_NAME 0x00001033
|
||||
#define ER_DE_DUPLICATED_NAME 0x00001034
|
||||
#define ER_DE_INVALID_NAME 0x00001035
|
||||
#define ER_FILE_VALID_SIZE 0x00002001
|
||||
#define ER_FILE_INVALID_CLUS 0x00002002
|
||||
#define ER_FILE_FIRST_CLUS 0x00002003
|
||||
@@ -23,6 +27,7 @@
|
||||
#define ER_FILE_LARGER_SIZE 0x00002005
|
||||
#define ER_FILE_DUPLICATED_CLUS 0x00002006
|
||||
#define ER_FILE_ZERO_NOFAT 0x00002007
|
||||
#define ER_VENDOR_GUID 0x00003001
|
||||
|
||||
typedef unsigned int er_problem_code_t;
|
||||
struct exfat_fsck;
|
||||
@@ -30,4 +35,6 @@ struct exfat_fsck;
|
||||
int exfat_repair_ask(struct exfat_fsck *fsck, er_problem_code_t prcode,
|
||||
const char *fmt, ...);
|
||||
|
||||
int exfat_repair_rename_ask(struct exfat_fsck *fsck, struct exfat_de_iter *iter,
|
||||
__le16 *uname, er_problem_code_t prcode, char *error_msg);
|
||||
#endif
|
||||
|
||||
+19
-5
@@ -16,7 +16,7 @@ struct buffer_desc;
|
||||
struct exfat_de_iter {
|
||||
struct exfat *exfat;
|
||||
struct exfat_inode *parent;
|
||||
struct buffer_desc *buffer_desc; /* cluster * 2 */
|
||||
struct buffer_desc *buffer_desc;
|
||||
__u32 ra_next_clus;
|
||||
unsigned int ra_begin_offset;
|
||||
unsigned int ra_partial_size;
|
||||
@@ -25,13 +25,16 @@ struct exfat_de_iter {
|
||||
off_t de_file_offset;
|
||||
off_t next_read_offset;
|
||||
int max_skip_dentries;
|
||||
#define DOT_NAME_NUM_MAX 9999999
|
||||
unsigned int dot_name_num;
|
||||
#define INVALID_NAME_NUM_MAX 9999999
|
||||
unsigned int invalid_name_num;
|
||||
|
||||
char *name_hash_bitmap; /* bitmap of children's name hashes */
|
||||
};
|
||||
|
||||
struct exfat_lookup_filter {
|
||||
struct {
|
||||
uint8_t type;
|
||||
int dentry_count;
|
||||
/* return 0 if matched, return 1 if not matched,
|
||||
* otherwise return errno
|
||||
*/
|
||||
@@ -43,8 +46,15 @@ struct exfat_lookup_filter {
|
||||
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.
|
||||
/*
|
||||
* If the dentry_set found:
|
||||
* - device offset where the dentry_set locates.
|
||||
* If the dentry_set not found:
|
||||
* - device offset where the first empty dentry_set locates
|
||||
* if in.dentry_count > 0 and there are enough empty dentry.
|
||||
* - device offset where the last empty dentry_set locates
|
||||
* if in.dentry_count = 0 or no enough empty dentry.
|
||||
* - EOF if no empty dentry_set.
|
||||
*/
|
||||
off_t dev_offset;
|
||||
} out;
|
||||
@@ -65,6 +75,10 @@ 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_lookup_file_by_utf16name(struct exfat *exfat,
|
||||
struct exfat_inode *parent,
|
||||
__le16 *utf16_name,
|
||||
struct exfat_lookup_filter *filter_out);
|
||||
|
||||
int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent,
|
||||
const char *name, unsigned short attr);
|
||||
|
||||
+10
-5
@@ -45,7 +45,8 @@ struct exfat {
|
||||
unsigned int disk_bitmap_size;
|
||||
__u16 *upcase_table;
|
||||
clus_t start_clu;
|
||||
char *zero_cluster;
|
||||
unsigned int buffer_count;
|
||||
struct buffer_desc *lookup_buffer; /* for dentry set lookup */
|
||||
};
|
||||
|
||||
struct exfat_dentry_loc {
|
||||
@@ -64,7 +65,7 @@ struct buffer_desc {
|
||||
__u32 p_clus;
|
||||
unsigned int offset;
|
||||
char *buffer;
|
||||
char *dirty;
|
||||
char dirty[EXFAT_BITMAP_SIZE(4 * KB / 512)];
|
||||
};
|
||||
|
||||
struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs);
|
||||
@@ -82,7 +83,11 @@ 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);
|
||||
struct buffer_desc *exfat_alloc_buffer(struct exfat *exfat);
|
||||
void exfat_free_buffer(const struct exfat *exfat, struct buffer_desc *bd);
|
||||
|
||||
static inline unsigned int exfat_get_read_size(const struct exfat *exfat)
|
||||
{
|
||||
return MIN(exfat->clus_size, 4 * KB);
|
||||
}
|
||||
#endif
|
||||
|
||||
+36
-6
@@ -7,23 +7,29 @@
|
||||
#define _EXFAT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <byteswap.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define UTF16_NULL 0x0000
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
|
||||
#define cpu_to_le32(x) \
|
||||
((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
|
||||
(((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
|
||||
#define cpu_to_le64(x) (cpu_to_le32((uint64_t)(x)) << 32 | \
|
||||
cpu_to_le32((uint64_t)(x) >> 32))
|
||||
#define cpu_to_le16(x) bswap_16(x)
|
||||
#define cpu_to_le32(x) bswap_32(x)
|
||||
#define cpu_to_le64(x) bswap_64(x)
|
||||
|
||||
#define UTF16_DOT 0x2E00 /* . */
|
||||
#define UTF16_SLASH 0x2F00 /* / */
|
||||
#else
|
||||
#define cpu_to_le16(x) (x)
|
||||
#define cpu_to_le32(x) (x)
|
||||
#define cpu_to_le64(x) (x)
|
||||
|
||||
#define UTF16_DOT 0x002E /* . */
|
||||
#define UTF16_SLASH 0x002F /* / */
|
||||
#endif
|
||||
|
||||
#define le64_to_cpu(x) ((uint64_t)cpu_to_le64(x))
|
||||
@@ -41,6 +47,7 @@
|
||||
#define MAX_EXFAT_DENTRIES 8388608
|
||||
#define MIN_FILE_DENTRIES 3
|
||||
#define MAX_NAME_DENTRIES 17
|
||||
#define MAX_EXT_DENTRIES 0xFF
|
||||
|
||||
/* dentry types */
|
||||
#define MSDOS_DELETED 0xE5 /* deleted mark */
|
||||
@@ -60,6 +67,8 @@
|
||||
#define EXFAT_STREAM 0xC0 /* stream entry */
|
||||
#define EXFAT_NAME 0xC1 /* file name entry */
|
||||
#define EXFAT_ACL 0xC2 /* stream entry */
|
||||
#define EXFAT_VENDOR_EXT 0xE0
|
||||
#define EXFAT_VENDOR_ALLOC 0xE1
|
||||
|
||||
/* checksum types */
|
||||
#define CS_DIR_ENTRY 0
|
||||
@@ -134,6 +143,7 @@ struct pbr {
|
||||
};
|
||||
|
||||
#define VOLUME_LABEL_MAX_LEN 11
|
||||
#define EXFAT_GUID_LEN 16
|
||||
#define ENTRY_NAME_MAX 15
|
||||
|
||||
struct exfat_dentry {
|
||||
@@ -191,6 +201,26 @@ struct exfat_dentry {
|
||||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __attribute__((packed)) upcase; /* up-case table directory entry */
|
||||
struct {
|
||||
__u8 num_ext;
|
||||
__le16 checksum;
|
||||
__u16 flags;
|
||||
__u8 guid[EXFAT_GUID_LEN];
|
||||
__u8 reserved[10];
|
||||
} __attribute__((packed)) guid; /* volume GUID directory entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 guid[EXFAT_GUID_LEN];
|
||||
__u8 vendor_defined[14];
|
||||
} __attribute__((packed)) vendor_ext ; /* vendor extension entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 guid[EXFAT_GUID_LEN];
|
||||
__u8 vendor_defined[2];
|
||||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __attribute__((packed)) vendor_alloc; /* vendor allocation entry */
|
||||
|
||||
} __attribute__((packed)) dentry;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
+24
-3
@@ -45,6 +45,8 @@ typedef __u32 clus_t;
|
||||
#define EXFAT_SET_VOLUME_LABEL 0x02
|
||||
#define EXFAT_GET_VOLUME_SERIAL 0x03
|
||||
#define EXFAT_SET_VOLUME_SERIAL 0x04
|
||||
#define EXFAT_GET_VOLUME_GUID 0x05
|
||||
#define EXFAT_SET_VOLUME_GUID 0x06
|
||||
|
||||
#define EXFAT_MAX_SECTOR_SIZE 4096
|
||||
|
||||
@@ -52,6 +54,8 @@ typedef __u32 clus_t;
|
||||
(pbr)->bsx.sect_per_clus_bits))
|
||||
#define EXFAT_SECTOR_SIZE(pbr) (1 << (pbr)->bsx.sect_size_bits)
|
||||
|
||||
#define EXFAT_MAX_HASH_COUNT (UINT16_MAX + 1)
|
||||
|
||||
enum {
|
||||
BOOT_SEC_IDX = 0,
|
||||
EXBOOT_SEC_IDX,
|
||||
@@ -74,8 +78,9 @@ struct exfat_blk_dev {
|
||||
};
|
||||
|
||||
struct exfat_user_input {
|
||||
char dev_name[255];
|
||||
const char *dev_name;
|
||||
bool writeable;
|
||||
unsigned int sector_size;
|
||||
unsigned int cluster_size;
|
||||
unsigned int sec_per_clu;
|
||||
unsigned int boundary_align;
|
||||
@@ -84,6 +89,7 @@ struct exfat_user_input {
|
||||
__u16 volume_label[VOLUME_LABEL_MAX_LEN];
|
||||
int volume_label_len;
|
||||
unsigned int volume_serial;
|
||||
const char *guid;
|
||||
};
|
||||
|
||||
struct exfat;
|
||||
@@ -102,18 +108,27 @@ typedef __u32 bitmap_t;
|
||||
#define EXFAT_BITMAP_SIZE(__c_count) \
|
||||
(DIV_ROUND_UP(__c_count, BITS_PER) * sizeof(bitmap_t))
|
||||
|
||||
#define BITMAP_GET(bmap, bit) \
|
||||
(((bitmap_t *)(bmap))[BIT_ENTRY(bit)] & BIT_MASK(bit))
|
||||
|
||||
#define BITMAP_SET(bmap, bit) \
|
||||
(((bitmap_t *)(bmap))[BIT_ENTRY(bit)] |= BIT_MASK(bit))
|
||||
|
||||
#define BITMAP_CLEAR(bmap, bit) \
|
||||
(((bitmap_t *)(bmap))[BIT_ENTRY(bit)] &= ~BIT_MASK(bit))
|
||||
|
||||
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);
|
||||
return BITMAP_GET(bmap, 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));
|
||||
BITMAP_SET(bmap, cc);
|
||||
}
|
||||
|
||||
static inline void exfat_bitmap_clear(char *bmap, clus_t c)
|
||||
@@ -139,6 +154,7 @@ int exfat_get_blk_dev_info(struct exfat_user_input *ui,
|
||||
struct exfat_blk_dev *bd);
|
||||
ssize_t exfat_read(int fd, void *buf, size_t size, off_t offset);
|
||||
ssize_t exfat_write(int fd, void *buf, size_t size, off_t offset);
|
||||
ssize_t exfat_write_zero(int fd, size_t size, off_t offset);
|
||||
|
||||
size_t exfat_utf16_len(const __le16 *str, size_t max_size);
|
||||
ssize_t exfat_utf16_enc(const char *in_str, __u16 *out_str, size_t out_size);
|
||||
@@ -147,6 +163,9 @@ ssize_t exfat_utf16_dec(const __u16 *in_str, size_t in_len,
|
||||
off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd);
|
||||
int exfat_read_volume_label(struct exfat *exfat);
|
||||
int exfat_set_volume_label(struct exfat *exfat, char *label_input);
|
||||
int __exfat_set_volume_guid(struct exfat_dentry *dentry, const char *guid);
|
||||
int exfat_read_volume_guid(struct exfat *exfat);
|
||||
int exfat_set_volume_guid(struct exfat *exfat, const char *guid);
|
||||
int exfat_read_sector(struct exfat_blk_dev *bd, void *buf,
|
||||
unsigned int sec_off);
|
||||
int exfat_write_sector(struct exfat_blk_dev *bd, void *buf,
|
||||
@@ -170,6 +189,8 @@ int exfat_o2c(struct exfat *exfat, off_t device_offset,
|
||||
bool exfat_heap_clus(struct exfat *exfat, clus_t clus);
|
||||
int exfat_root_clus_count(struct exfat *exfat);
|
||||
int read_boot_sect(struct exfat_blk_dev *bdev, struct pbr **bs);
|
||||
int exfat_parse_ulong(const char *s, unsigned long *out);
|
||||
int exfat_check_name(__le16 *utf16_name, int len);
|
||||
|
||||
/*
|
||||
* Exfat Print
|
||||
|
||||
+1
-1
@@ -5,6 +5,6 @@
|
||||
|
||||
#ifndef _VERSION_H
|
||||
|
||||
#define EXFAT_PROGS_VERSION "1.2.0"
|
||||
#define EXFAT_PROGS_VERSION "1.2.5"
|
||||
|
||||
#endif /* !_VERSION_H */
|
||||
|
||||
+15
-5
@@ -42,6 +42,7 @@ int main(int argc, char *argv[])
|
||||
bool version_only = false;
|
||||
int serial_mode = 0;
|
||||
int flags = 0;
|
||||
unsigned long volume_serial;
|
||||
|
||||
init_user_input(&ui);
|
||||
|
||||
@@ -77,11 +78,11 @@ int main(int argc, char *argv[])
|
||||
if (version_only)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (argc < 2)
|
||||
if (argc - optind != 1 && flags != EXFAT_SET_VOLUME_LABEL &&
|
||||
flags != EXFAT_SET_VOLUME_SERIAL)
|
||||
usage();
|
||||
|
||||
memset(ui.dev_name, 0, sizeof(ui.dev_name));
|
||||
snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[serial_mode + 1]);
|
||||
ui.dev_name = argv[serial_mode + 1];
|
||||
|
||||
ret = exfat_get_blk_dev_info(&ui, &bd);
|
||||
if (ret < 0)
|
||||
@@ -92,7 +93,17 @@ int main(int argc, char *argv[])
|
||||
if (flags == EXFAT_GET_VOLUME_SERIAL) {
|
||||
ret = exfat_show_volume_serial(bd.dev_fd);
|
||||
} else if (flags == EXFAT_SET_VOLUME_SERIAL) {
|
||||
ui.volume_serial = strtoul(argv[3], NULL, 0);
|
||||
ret = exfat_parse_ulong(argv[3], &volume_serial);
|
||||
if (volume_serial > UINT_MAX)
|
||||
ret = -ERANGE;
|
||||
|
||||
|
||||
if (ret < 0) {
|
||||
exfat_err("invalid serial number(%s)\n", argv[3]);
|
||||
goto close_fd_out;
|
||||
}
|
||||
|
||||
ui.volume_serial = volume_serial;
|
||||
ret = exfat_set_volume_serial(&bd, &ui);
|
||||
}
|
||||
} else {
|
||||
@@ -105,7 +116,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
exfat = exfat_alloc_exfat(&bd, bs);
|
||||
if (!exfat) {
|
||||
free(bs);
|
||||
ret = -ENOMEM;
|
||||
goto close_fd_out;
|
||||
}
|
||||
|
||||
+87
-54
@@ -27,6 +27,12 @@ static struct path_resolve_ctx path_resolve_ctx;
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
static inline struct buffer_desc *exfat_de_iter_get_buffer(
|
||||
struct exfat_de_iter *iter, unsigned int block)
|
||||
{
|
||||
return &iter->buffer_desc[block % iter->exfat->buffer_count];
|
||||
}
|
||||
|
||||
static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
|
||||
{
|
||||
off_t device_offset;
|
||||
@@ -34,18 +40,19 @@ static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
|
||||
struct buffer_desc *desc;
|
||||
unsigned int i;
|
||||
|
||||
desc = &iter->buffer_desc[block & 0x01];
|
||||
device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset;
|
||||
desc = exfat_de_iter_get_buffer(iter, block);
|
||||
|
||||
for (i = 0; i < iter->read_size / iter->write_size; i++) {
|
||||
if (desc->dirty[i]) {
|
||||
if (BITMAP_GET(desc->dirty, i)) {
|
||||
device_offset = exfat_c2o(exfat, desc->p_clus) +
|
||||
desc->offset;
|
||||
if (exfat_write(exfat->blk_dev->dev_fd,
|
||||
desc->buffer + i * iter->write_size,
|
||||
iter->write_size,
|
||||
device_offset + i * iter->write_size)
|
||||
!= (ssize_t)iter->write_size)
|
||||
return -EIO;
|
||||
desc->dirty[i] = 0;
|
||||
BITMAP_CLEAR(desc->dirty, i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -168,7 +175,7 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block)
|
||||
off_t device_offset;
|
||||
ssize_t ret;
|
||||
|
||||
desc = &iter->buffer_desc[block & 0x01];
|
||||
desc = exfat_de_iter_get_buffer(iter, block);
|
||||
if (block == 0) {
|
||||
desc->p_clus = iter->parent->first_clus;
|
||||
desc->offset = 0;
|
||||
@@ -182,7 +189,7 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block)
|
||||
if (block > iter->parent->size / iter->read_size)
|
||||
return EOF;
|
||||
|
||||
prev_desc = &iter->buffer_desc[(block-1) & 0x01];
|
||||
prev_desc = exfat_de_iter_get_buffer(iter, block - 1);
|
||||
if (prev_desc->offset + 2 * iter->read_size <=
|
||||
exfat->clus_size) {
|
||||
desc->p_clus = prev_desc->p_clus;
|
||||
@@ -224,7 +231,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
|
||||
iter->exfat = exfat;
|
||||
iter->parent = dir;
|
||||
iter->write_size = exfat->sect_size;
|
||||
iter->read_size = exfat->clus_size <= 4*KB ? exfat->clus_size : 4*KB;
|
||||
iter->read_size = exfat_get_read_size(exfat);
|
||||
if (exfat->clus_size <= 32 * KB)
|
||||
iter->ra_partial_size = MAX(4 * KB, exfat->clus_size / 2);
|
||||
else
|
||||
@@ -236,7 +243,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
|
||||
iter->de_file_offset = 0;
|
||||
iter->next_read_offset = iter->read_size;
|
||||
iter->max_skip_dentries = 0;
|
||||
iter->dot_name_num = 0;
|
||||
iter->invalid_name_num = 0;
|
||||
|
||||
if (iter->parent->size == 0)
|
||||
return EOF;
|
||||
@@ -256,6 +263,7 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
|
||||
off_t next_de_file_offset;
|
||||
ssize_t ret;
|
||||
unsigned int block;
|
||||
struct buffer_desc *bd;
|
||||
|
||||
next_de_file_offset = iter->de_file_offset +
|
||||
ith * sizeof(struct exfat_dentry);
|
||||
@@ -264,9 +272,6 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
|
||||
if (next_de_file_offset + sizeof(struct exfat_dentry) >
|
||||
iter->parent->size)
|
||||
return EOF;
|
||||
/* the dentry must be in current, or next block which will be read */
|
||||
if (block > iter->de_file_offset / iter->read_size + 1)
|
||||
return -ERANGE;
|
||||
|
||||
/* read next cluster if needed */
|
||||
if (next_de_file_offset >= iter->next_read_offset) {
|
||||
@@ -279,8 +284,8 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
|
||||
if (ith + 1 > iter->max_skip_dentries)
|
||||
iter->max_skip_dentries = ith + 1;
|
||||
|
||||
*dentry = (struct exfat_dentry *)
|
||||
(iter->buffer_desc[block & 0x01].buffer +
|
||||
bd = exfat_de_iter_get_buffer(iter, block);
|
||||
*dentry = (struct exfat_dentry *)(bd->buffer +
|
||||
next_de_file_offset % iter->read_size);
|
||||
return 0;
|
||||
}
|
||||
@@ -291,6 +296,7 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
|
||||
off_t next_file_offset;
|
||||
unsigned int block;
|
||||
int ret, sect_idx;
|
||||
struct buffer_desc *bd;
|
||||
|
||||
ret = exfat_de_iter_get(iter, ith, dentry);
|
||||
if (!ret) {
|
||||
@@ -299,7 +305,8 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
|
||||
block = (unsigned int)(next_file_offset / iter->read_size);
|
||||
sect_idx = (int)((next_file_offset % iter->read_size) /
|
||||
iter->write_size);
|
||||
iter->buffer_desc[block & 0x01].dirty[sect_idx] = 1;
|
||||
bd = exfat_de_iter_get_buffer(iter, block);
|
||||
BITMAP_SET(bd->dirty, sect_idx);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -307,8 +314,11 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
|
||||
|
||||
int exfat_de_iter_flush(struct exfat_de_iter *iter)
|
||||
{
|
||||
if (write_block(iter, 0) || write_block(iter, 1))
|
||||
return -EIO;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < iter->exfat->buffer_count; i++)
|
||||
if (write_block(iter, i))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -332,7 +342,7 @@ off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter)
|
||||
return EOF;
|
||||
|
||||
block = iter->de_file_offset / iter->read_size;
|
||||
bd = &iter->buffer_desc[block & 0x01];
|
||||
bd = exfat_de_iter_get_buffer(iter, block);
|
||||
return exfat_c2o(iter->exfat, bd->p_clus) + bd->offset +
|
||||
iter->de_file_offset % iter->read_size;
|
||||
}
|
||||
@@ -355,13 +365,15 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
|
||||
struct exfat_dentry *dentry = NULL;
|
||||
off_t free_file_offset = 0, free_dev_offset = 0;
|
||||
struct exfat_de_iter de_iter;
|
||||
int dentry_count;
|
||||
int dentry_count, empty_dentry_count = 0;
|
||||
int retval;
|
||||
bool last_is_free = false;
|
||||
|
||||
bd = exfat_alloc_buffer(2, exfat->clus_size, exfat->sect_size);
|
||||
if (!bd)
|
||||
return -ENOMEM;
|
||||
if (!exfat->lookup_buffer) {
|
||||
exfat->lookup_buffer = exfat_alloc_buffer(exfat);
|
||||
if (!exfat->lookup_buffer)
|
||||
return -ENOMEM;
|
||||
}
|
||||
bd = exfat->lookup_buffer;
|
||||
|
||||
retval = exfat_de_iter_init(&de_iter, exfat, parent, bd);
|
||||
if (retval == EOF || retval)
|
||||
@@ -378,6 +390,12 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!IS_EXFAT_DELETED(dentry->type)) {
|
||||
if (filter->in.dentry_count == 0 ||
|
||||
empty_dentry_count < filter->in.dentry_count)
|
||||
empty_dentry_count = 0;
|
||||
}
|
||||
|
||||
dentry_count = 1;
|
||||
if (dentry->type == filter->in.type) {
|
||||
retval = 0;
|
||||
@@ -406,18 +424,17 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
|
||||
} 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) {
|
||||
} else if (IS_EXFAT_DELETED(dentry->type)) {
|
||||
if (empty_dentry_count == 0) {
|
||||
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 {
|
||||
last_is_free = false;
|
||||
|
||||
if (filter->in.dentry_count == 0 ||
|
||||
empty_dentry_count < filter->in.dentry_count)
|
||||
empty_dentry_count++;
|
||||
}
|
||||
|
||||
exfat_de_iter_advance(&de_iter, dentry_count);
|
||||
@@ -429,15 +446,13 @@ out:
|
||||
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) {
|
||||
} else if (retval == EOF && empty_dentry_count) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -471,7 +486,7 @@ static int filter_lookup_file(struct exfat_de_iter *de_iter,
|
||||
if (retval || name_de->type != EXFAT_NAME)
|
||||
return 1;
|
||||
|
||||
len = MIN(name_len, ENTRY_NAME_MAX);
|
||||
len = MIN(name_len + 1, ENTRY_NAME_MAX);
|
||||
if (memcmp(name_de->dentry.name.unicode_0_14,
|
||||
name, len * 2) != 0)
|
||||
return 1;
|
||||
@@ -484,6 +499,25 @@ static int filter_lookup_file(struct exfat_de_iter *de_iter,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_lookup_file_by_utf16name(struct exfat *exfat,
|
||||
struct exfat_inode *parent,
|
||||
__le16 *utf16_name,
|
||||
struct exfat_lookup_filter *filter_out)
|
||||
{
|
||||
int retval;
|
||||
|
||||
filter_out->in.type = EXFAT_FILE;
|
||||
filter_out->in.filter = filter_lookup_file;
|
||||
filter_out->in.param = utf16_name;
|
||||
filter_out->in.dentry_count = 0;
|
||||
|
||||
retval = exfat_lookup_dentry_set(exfat, parent, filter_out);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
|
||||
const char *name, struct exfat_lookup_filter *filter_out)
|
||||
{
|
||||
@@ -494,15 +528,8 @@ int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
|
||||
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;
|
||||
return exfat_lookup_file_by_utf16name(exfat, parent, utf16_name,
|
||||
filter_out);
|
||||
}
|
||||
|
||||
void exfat_calc_dentry_checksum(struct exfat_dentry *dentry,
|
||||
@@ -513,12 +540,17 @@ void exfat_calc_dentry_checksum(struct exfat_dentry *dentry,
|
||||
|
||||
bytes = (uint8_t *)dentry;
|
||||
|
||||
*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[0];
|
||||
*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[1];
|
||||
/* use += to avoid promotion to int; UBSan complaints about signed overflow */
|
||||
*checksum = (*checksum << 15) | (*checksum >> 1);
|
||||
*checksum += bytes[0];
|
||||
*checksum = (*checksum << 15) | (*checksum >> 1);
|
||||
*checksum += bytes[1];
|
||||
|
||||
i = primary ? 4 : 2;
|
||||
for (; i < sizeof(*dentry); i++)
|
||||
*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[i];
|
||||
for (; i < sizeof(*dentry); i++) {
|
||||
*checksum = (*checksum << 15) | (*checksum >> 1);
|
||||
*checksum += bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t calc_dentry_set_checksum(struct exfat_dentry *dset, int dcount)
|
||||
@@ -545,10 +577,12 @@ uint16_t exfat_calc_name_hash(struct exfat *exfat,
|
||||
|
||||
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);
|
||||
/* use += to avoid promotion to int; UBSan complaints about signed overflow */
|
||||
chksum = (chksum << 15) | (chksum >> 1);
|
||||
chksum += ch & 0xFF;
|
||||
chksum = (chksum << 15) | (chksum >> 1);
|
||||
chksum += ch >> 8;
|
||||
}
|
||||
return chksum;
|
||||
}
|
||||
@@ -588,7 +622,7 @@ int exfat_build_file_dentry_set(struct exfat *exfat, const char *name,
|
||||
|
||||
name_len = retval / 2;
|
||||
dcount = 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX);
|
||||
dset = calloc(1, dcount * DENTRY_SIZE);
|
||||
dset = calloc(dcount, DENTRY_SIZE);
|
||||
if (!dset)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -657,7 +691,7 @@ int exfat_update_file_dentry_set(struct exfat *exfat,
|
||||
|
||||
dset[1].dentry.stream.name_len = (__u8)name_len;
|
||||
dset[1].dentry.stream.name_hash =
|
||||
exfat_calc_name_hash(exfat, utf16_name, name_len);
|
||||
cpu_to_le16(exfat_calc_name_hash(exfat, utf16_name, name_len));
|
||||
|
||||
for (i = 2; i < dcount; i++) {
|
||||
dset[i].type = EXFAT_NAME;
|
||||
@@ -816,9 +850,8 @@ static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode,
|
||||
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) {
|
||||
if (exfat_write_zero(exfat->blk_dev->dev_fd, exfat->clus_size,
|
||||
exfat_c2o(exfat, *new_clu))) {
|
||||
exfat_err("failed to fill new cluster with zeroes\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
+26
-35
@@ -22,7 +22,7 @@ struct exfat_inode *exfat_alloc_inode(__u16 attr)
|
||||
int size;
|
||||
|
||||
size = offsetof(struct exfat_inode, name) + NAME_BUFFER_SIZE;
|
||||
node = (struct exfat_inode *)calloc(1, size);
|
||||
node = calloc(1, size);
|
||||
if (!node) {
|
||||
exfat_err("failed to allocate exfat_node\n");
|
||||
return NULL;
|
||||
@@ -117,8 +117,8 @@ void exfat_free_exfat(struct exfat *exfat)
|
||||
free(exfat->upcase_table);
|
||||
if (exfat->root)
|
||||
exfat_free_inode(exfat->root);
|
||||
if (exfat->zero_cluster)
|
||||
free(exfat->zero_cluster);
|
||||
if (exfat->lookup_buffer)
|
||||
free(exfat->lookup_buffer);
|
||||
free(exfat);
|
||||
}
|
||||
}
|
||||
@@ -127,9 +127,11 @@ 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)
|
||||
exfat = calloc(1, sizeof(*exfat));
|
||||
if (!exfat) {
|
||||
free(bs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&exfat->dir_list);
|
||||
exfat->blk_dev = blk_dev;
|
||||
@@ -139,32 +141,26 @@ struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *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));
|
||||
exfat->alloc_bitmap = calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
|
||||
if (!exfat->alloc_bitmap) {
|
||||
exfat_err("failed to allocate bitmap\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
exfat->ohead_bitmap =
|
||||
calloc(1, 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));
|
||||
exfat->disk_bitmap = calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
|
||||
if (!exfat->disk_bitmap) {
|
||||
exfat_err("failed to allocate bitmap\n");
|
||||
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->buffer_count = ((MAX_EXT_DENTRIES + 1) * DENTRY_SIZE) /
|
||||
exfat_get_read_size(exfat) + 1;
|
||||
|
||||
exfat->start_clu = EXFAT_FIRST_CLUSTER;
|
||||
return exfat;
|
||||
@@ -173,39 +169,36 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct buffer_desc *exfat_alloc_buffer(int count,
|
||||
unsigned int clu_size, unsigned int sect_size)
|
||||
struct buffer_desc *exfat_alloc_buffer(struct exfat *exfat)
|
||||
{
|
||||
struct buffer_desc *bd;
|
||||
int i;
|
||||
unsigned int i;
|
||||
unsigned int read_size = exfat_get_read_size(exfat);
|
||||
|
||||
bd = (struct buffer_desc *)calloc(sizeof(*bd), count);
|
||||
bd = calloc(exfat->buffer_count, sizeof(*bd));
|
||||
if (!bd)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
bd[i].buffer = (char *)malloc(clu_size);
|
||||
for (i = 0; i < exfat->buffer_count; i++) {
|
||||
bd[i].buffer = malloc(read_size);
|
||||
if (!bd[i].buffer)
|
||||
goto err;
|
||||
bd[i].dirty = (char *)calloc(clu_size / sect_size, 1);
|
||||
if (!bd[i].dirty)
|
||||
goto err;
|
||||
|
||||
memset(&bd[i].dirty, 0, sizeof(bd[i].dirty));
|
||||
}
|
||||
return bd;
|
||||
err:
|
||||
exfat_free_buffer(bd, count);
|
||||
exfat_free_buffer(exfat, bd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void exfat_free_buffer(struct buffer_desc *bd, int count)
|
||||
void exfat_free_buffer(const struct exfat *exfat, struct buffer_desc *bd)
|
||||
{
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
for (i = 0; i < exfat->buffer_count; i++) {
|
||||
if (bd[i].buffer)
|
||||
free(bd[i].buffer);
|
||||
if (bd[i].dirty)
|
||||
free(bd[i].dirty);
|
||||
}
|
||||
free(bd);
|
||||
}
|
||||
@@ -256,8 +249,6 @@ 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';
|
||||
@@ -275,13 +266,13 @@ int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child)
|
||||
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 = UTF16_SLASH;
|
||||
utf16_path++;
|
||||
}
|
||||
|
||||
if (depth > 1)
|
||||
utf16_path--;
|
||||
memcpy((char *)utf16_path, &utf16_null, sizeof(utf16_null));
|
||||
*utf16_path = UTF16_NULL;
|
||||
utf16_path++;
|
||||
|
||||
in_size = (utf16_path - ctx->utf16_path) * sizeof(__le16);
|
||||
|
||||
+244
-10
@@ -15,6 +15,7 @@
|
||||
#include <errno.h>
|
||||
#include <wchar.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "exfat_ondisk.h"
|
||||
#include "libexfat.h"
|
||||
@@ -186,7 +187,9 @@ int exfat_get_blk_dev_info(struct exfat_user_input *ui,
|
||||
if (!ui->boundary_align)
|
||||
ui->boundary_align = DEFAULT_BOUNDARY_ALIGNMENT;
|
||||
|
||||
if (ioctl(fd, BLKSSZGET, &bd->sector_size) < 0)
|
||||
if (ui->sector_size)
|
||||
bd->sector_size = ui->sector_size;
|
||||
else if (ioctl(fd, BLKSSZGET, &bd->sector_size) < 0)
|
||||
bd->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
bd->sector_size_bits = sector_size_bits(bd->sector_size);
|
||||
bd->num_sectors = blk_dev_size / bd->sector_size;
|
||||
@@ -217,6 +220,24 @@ ssize_t exfat_write(int fd, void *buf, size_t size, off_t offset)
|
||||
return pwrite(fd, buf, size, offset);
|
||||
}
|
||||
|
||||
ssize_t exfat_write_zero(int fd, size_t size, off_t offset)
|
||||
{
|
||||
const char zero_buf[4 * KB] = {0};
|
||||
|
||||
lseek(fd, offset, SEEK_SET);
|
||||
|
||||
while (size > 0) {
|
||||
int iter_size = MIN(size, sizeof(zero_buf));
|
||||
|
||||
if (iter_size != write(fd, zero_buf, iter_size))
|
||||
return -EIO;
|
||||
|
||||
size -= iter_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t exfat_utf16_len(const __le16 *str, size_t max_size)
|
||||
{
|
||||
size_t i = 0;
|
||||
@@ -354,7 +375,7 @@ off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd)
|
||||
unsigned int cluster_size, sector_size;
|
||||
off_t root_clu_off;
|
||||
|
||||
bs = (struct pbr *)malloc(EXFAT_MAX_SECTOR_SIZE);
|
||||
bs = malloc(EXFAT_MAX_SECTOR_SIZE);
|
||||
if (!bs) {
|
||||
exfat_err("failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
@@ -411,6 +432,7 @@ int exfat_read_volume_label(struct exfat *exfat)
|
||||
__le16 disk_label[VOLUME_LABEL_MAX_LEN];
|
||||
struct exfat_lookup_filter filter = {
|
||||
.in.type = EXFAT_VOLUME,
|
||||
.in.dentry_count = 0,
|
||||
.in.filter = NULL,
|
||||
};
|
||||
|
||||
@@ -452,6 +474,7 @@ int exfat_set_volume_label(struct exfat *exfat, char *label_input)
|
||||
|
||||
struct exfat_lookup_filter filter = {
|
||||
.in.type = EXFAT_VOLUME,
|
||||
.in.dentry_count = 1,
|
||||
.in.filter = NULL,
|
||||
};
|
||||
|
||||
@@ -461,7 +484,7 @@ int exfat_set_volume_label(struct exfat *exfat, char *label_input)
|
||||
dcount = filter.out.dentry_count;
|
||||
memset(pvol->vol_label, 0, sizeof(pvol->vol_label));
|
||||
} else {
|
||||
pvol = calloc(sizeof(struct exfat_dentry), 1);
|
||||
pvol = calloc(1, sizeof(struct exfat_dentry));
|
||||
if (!pvol)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -473,12 +496,20 @@ int exfat_set_volume_label(struct exfat *exfat, char *label_input)
|
||||
volume_label, sizeof(volume_label));
|
||||
if (volume_label_len < 0) {
|
||||
exfat_err("failed to encode volume label\n");
|
||||
free(pvol);
|
||||
return -1;
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pvol->vol_char_cnt = volume_label_len/2;
|
||||
err = exfat_check_name(volume_label, pvol->vol_char_cnt);
|
||||
if (err != pvol->vol_char_cnt) {
|
||||
exfat_err("volume label contain invalid character(%c)\n",
|
||||
le16_to_cpu(label_input[err]));
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(pvol->vol_label, volume_label, volume_label_len);
|
||||
pvol->vol_char_cnt = volume_label_len/2;
|
||||
|
||||
loc.parent = exfat->root;
|
||||
loc.file_offset = filter.out.file_offset;
|
||||
@@ -486,11 +517,173 @@ int exfat_set_volume_label(struct exfat *exfat, char *label_input)
|
||||
err = exfat_add_dentry_set(exfat, &loc, pvol, dcount, false);
|
||||
exfat_info("new label: %s\n", label_input);
|
||||
|
||||
out:
|
||||
free(pvol);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void print_guid(const char *msg, const __u8 *guid)
|
||||
{
|
||||
exfat_info("%s: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
|
||||
msg,
|
||||
guid[0], guid[1], guid[2], guid[3],
|
||||
guid[4], guid[5], guid[5], guid[7],
|
||||
guid[8], guid[9], guid[10], guid[11],
|
||||
guid[12], guid[13], guid[14], guid[15]);
|
||||
}
|
||||
|
||||
static int set_guid(__u8 *guid, const char *input)
|
||||
{
|
||||
int i, j, zero_len = 0;
|
||||
int len = strlen(input);
|
||||
|
||||
if (len != EXFAT_GUID_LEN * 2 && len != EXFAT_GUID_LEN * 2 + 4) {
|
||||
exfat_err("invalid format for volume guid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < len; i++) {
|
||||
unsigned char ch = input[i];
|
||||
|
||||
if (ch >= '0' && ch <= '9')
|
||||
ch -= '0';
|
||||
else if (ch >= 'a' && ch <= 'f')
|
||||
ch -= 'a' - 0xA;
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
ch -= 'A' - 0xA;
|
||||
else if (ch == '-' && len == EXFAT_GUID_LEN * 2 + 4 &&
|
||||
(i == 8 || i == 13 || i == 18 || i == 23))
|
||||
continue;
|
||||
else {
|
||||
exfat_err("invalid character '%c' for volume GUID\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (j & 1)
|
||||
guid[j >> 1] |= ch;
|
||||
else
|
||||
guid[j >> 1] = ch << 4;
|
||||
|
||||
j++;
|
||||
|
||||
if (ch == 0)
|
||||
zero_len++;
|
||||
}
|
||||
|
||||
if (zero_len == EXFAT_GUID_LEN * 2) {
|
||||
exfat_err("%s is invalid for volume GUID\n", input);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_read_volume_guid(struct exfat *exfat)
|
||||
{
|
||||
int err;
|
||||
uint16_t checksum = 0;
|
||||
struct exfat_dentry *dentry;
|
||||
struct exfat_lookup_filter filter = {
|
||||
.in.type = EXFAT_GUID,
|
||||
.in.dentry_count = 1,
|
||||
.in.filter = NULL,
|
||||
};
|
||||
|
||||
err = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dentry = filter.out.dentry_set;
|
||||
exfat_calc_dentry_checksum(dentry, &checksum, true);
|
||||
|
||||
if (cpu_to_le16(checksum) == dentry->dentry.guid.checksum)
|
||||
print_guid("GUID", dentry->dentry.guid.guid);
|
||||
else
|
||||
exfat_info("GUID is corrupted, please delete it or set a new one\n");
|
||||
|
||||
free(dentry);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int __exfat_set_volume_guid(struct exfat_dentry *dentry, const char *guid)
|
||||
{
|
||||
int err;
|
||||
uint16_t checksum = 0;
|
||||
|
||||
memset(dentry, 0, sizeof(*dentry));
|
||||
dentry->type = EXFAT_GUID;
|
||||
|
||||
err = set_guid(dentry->dentry.guid.guid, guid);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
exfat_calc_dentry_checksum(dentry, &checksum, true);
|
||||
dentry->dentry.guid.checksum = cpu_to_le16(checksum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create/Update/Delete GUID dentry in root directory
|
||||
*
|
||||
* create/update GUID if @guid is not NULL.
|
||||
* delete GUID if @guid is NULL.
|
||||
*/
|
||||
int exfat_set_volume_guid(struct exfat *exfat, const char *guid)
|
||||
{
|
||||
struct exfat_dentry *dentry;
|
||||
struct exfat_dentry_loc loc;
|
||||
int err;
|
||||
|
||||
struct exfat_lookup_filter filter = {
|
||||
.in.type = EXFAT_GUID,
|
||||
.in.dentry_count = 1,
|
||||
.in.filter = NULL,
|
||||
};
|
||||
|
||||
err = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
|
||||
if (!err) {
|
||||
/* GUID entry is found */
|
||||
dentry = filter.out.dentry_set;
|
||||
} else {
|
||||
/* no GUID to delete */
|
||||
if (guid == NULL)
|
||||
return 0;
|
||||
|
||||
dentry = calloc(1, sizeof(*dentry));
|
||||
if (!dentry)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (guid) {
|
||||
/* Set GUID */
|
||||
err = __exfat_set_volume_guid(dentry, guid);
|
||||
if (err)
|
||||
goto out;
|
||||
} else {
|
||||
/* Delete GUID */
|
||||
dentry->type &= ~EXFAT_INVAL;
|
||||
}
|
||||
|
||||
loc.parent = exfat->root;
|
||||
loc.file_offset = filter.out.file_offset;
|
||||
loc.dev_offset = filter.out.dev_offset;
|
||||
err = exfat_add_dentry_set(exfat, &loc, dentry, 1, false);
|
||||
if (!err) {
|
||||
if (guid)
|
||||
print_guid("new GUID", dentry->dentry.guid.guid);
|
||||
else
|
||||
exfat_info("GUID is deleted\n");
|
||||
}
|
||||
|
||||
out:
|
||||
free(dentry);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int exfat_read_sector(struct exfat_blk_dev *bd, void *buf, unsigned int sec_off)
|
||||
{
|
||||
int ret;
|
||||
@@ -575,7 +768,7 @@ int exfat_show_volume_serial(int fd)
|
||||
goto free_ppbr;
|
||||
}
|
||||
|
||||
exfat_info("volume serial : 0x%x\n", ppbr->bsx.vol_serial);
|
||||
exfat_info("volume serial : 0x%x\n", le32_to_cpu(ppbr->bsx.vol_serial));
|
||||
|
||||
free_ppbr:
|
||||
free(ppbr);
|
||||
@@ -650,7 +843,7 @@ int exfat_set_volume_serial(struct exfat_blk_dev *bd,
|
||||
}
|
||||
|
||||
bd->sector_size = 1 << ppbr->bsx.sect_size_bits;
|
||||
ppbr->bsx.vol_serial = ui->volume_serial;
|
||||
ppbr->bsx.vol_serial = cpu_to_le32(ui->volume_serial);
|
||||
|
||||
/* update main boot sector */
|
||||
ret = exfat_write_sector(bd, (char *)ppbr, BOOT_SEC_IDX);
|
||||
@@ -735,6 +928,8 @@ int exfat_set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus)
|
||||
exfat->bs->bsx.sect_size_bits;
|
||||
offset += sizeof(clus_t) * clus;
|
||||
|
||||
next_clus = cpu_to_le32(next_clus);
|
||||
|
||||
if (exfat_write(exfat->blk_dev->dev_fd, &next_clus, sizeof(next_clus),
|
||||
offset) != sizeof(next_clus))
|
||||
return -EIO;
|
||||
@@ -748,8 +943,7 @@ off_t exfat_s2o(struct exfat *exfat, off_t sect)
|
||||
|
||||
off_t exfat_c2o(struct exfat *exfat, unsigned int clus)
|
||||
{
|
||||
if (clus < EXFAT_FIRST_CLUSTER)
|
||||
return ~0L;
|
||||
assert(clus >= EXFAT_FIRST_CLUSTER);
|
||||
|
||||
return exfat_s2o(exfat, le32_to_cpu(exfat->bs->bsx.clu_offset) +
|
||||
((off_t)(clus - EXFAT_FIRST_CLUSTER) <<
|
||||
@@ -818,6 +1012,10 @@ int read_boot_sect(struct exfat_blk_dev *bdev, struct pbr **bs)
|
||||
unsigned int sect_size, clu_size;
|
||||
|
||||
pbr = malloc(sizeof(struct pbr));
|
||||
if (!pbr) {
|
||||
exfat_err("failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (exfat_read(bdev->dev_fd, pbr, sizeof(*pbr), 0) !=
|
||||
(ssize_t)sizeof(*pbr)) {
|
||||
@@ -854,3 +1052,39 @@ err:
|
||||
free(pbr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int exfat_parse_ulong(const char *s, unsigned long *out)
|
||||
{
|
||||
char *endptr;
|
||||
|
||||
errno = 0;
|
||||
|
||||
*out = strtoul(s, &endptr, 0);
|
||||
|
||||
if (errno)
|
||||
return -errno;
|
||||
|
||||
if (s == endptr || *endptr != '\0')
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int check_bad_utf16_char(unsigned short w)
|
||||
{
|
||||
return (w < 0x0020) || (w == '*') || (w == '?') || (w == '<') ||
|
||||
(w == '>') || (w == '|') || (w == '"') || (w == ':') ||
|
||||
(w == '/') || (w == '\\');
|
||||
}
|
||||
|
||||
int exfat_check_name(__le16 *utf16_name, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (check_bad_utf16_char(le16_to_cpu(utf16_name[i])))
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ Files share the same cluster. Cluster chains for files except one are broken.
|
||||
.IP -
|
||||
Start cluster number is invalid. The cluster number and file size are changed to 0.
|
||||
.IP -
|
||||
Checksum value of direcotry entry set is invalid. Directory entry set is deleted.
|
||||
Checksum value of directory entry set is invalid. Directory entry set is deleted.
|
||||
.IP -
|
||||
Bad hash value of a file name. The hash value is changed properly.
|
||||
.IP -
|
||||
|
||||
@@ -7,6 +7,9 @@ mkfs.exfat \- create an exFAT filesystem
|
||||
.B \-b
|
||||
.I boundary_alignment
|
||||
] [
|
||||
.B \-s
|
||||
.I sector_size
|
||||
] [
|
||||
.B \-c
|
||||
.I cluster_size
|
||||
] [
|
||||
@@ -17,6 +20,9 @@ mkfs.exfat \- create an exFAT filesystem
|
||||
.B \-L
|
||||
.I volume_label
|
||||
] [
|
||||
.B \-U
|
||||
.I volume_guid
|
||||
] [
|
||||
.B \-\-pack\-bitmap
|
||||
] [
|
||||
.B \-v
|
||||
@@ -67,12 +73,31 @@ _
|
||||
>128 GiB \[<=]512 GiB 256 KiB 32 MiB
|
||||
>512 GiB \[<=]2 TiB 512 KiB 64 MiB
|
||||
.TE
|
||||
The default is always 1 MiB.
|
||||
.TP
|
||||
.BR \-s ", " \-\-sector\-size =\fIsize\fR
|
||||
Specifies the sector size of the exFAT file system.
|
||||
The \fIsize\fR argument is specified in bytes or may be specified with
|
||||
\fBk\fR/\fBK\fR suffix for kibibytes and must either 512, 1024, 2048 or 4096
|
||||
bytes.
|
||||
The default value is the sector size reported by the device, or 512 bytes if the
|
||||
device sector size cannot be determined.
|
||||
.TP
|
||||
.BR \-c ", " \-\-cluster\-size =\fIsize\fR
|
||||
Specifies the cluster size of the exFAT file system.
|
||||
The \fIsize\fR argument is specified in bytes or may be specified with
|
||||
\fBm\fR/\fBM\fR suffix for mebibytes or \fBk\fR/\fBK\fR suffix for kibibytes
|
||||
and must be a power of two.
|
||||
The default value is described in the following table:
|
||||
.TS
|
||||
center;
|
||||
cb1s6cb,nnn.
|
||||
Card Capacity Range Cluster Size
|
||||
_
|
||||
\[<=]256 MiB 4 KiB
|
||||
>256 MiB \[<=]32 GiB 32 KiB
|
||||
>32 GiB 128 KiB
|
||||
.TE
|
||||
.TP
|
||||
.BR \-f ", " \-\-full\-format
|
||||
Performs a full format.
|
||||
@@ -84,6 +109,9 @@ Prints the help and exit.
|
||||
.BR \-L ", " \-\-volume\-label =\fIlabel\fR
|
||||
Specifies the volume label to be associated with the exFAT filesystem.
|
||||
.TP
|
||||
.BR \-U ", " \-\-volume\-guid =\fIguid\fR
|
||||
Specifies the volume GUID to be associated with the exFAT filesystem.
|
||||
.TP
|
||||
.B \-\-pack\-bitmap
|
||||
Attempts to relocate the exFAT allocation bitmap so that it ends at the
|
||||
alignment boundary immediately following the FAT rather than beginning at that
|
||||
@@ -101,6 +129,9 @@ allocation unit with the FAT.
|
||||
If there is insufficient space for the bitmap there, then this option will have
|
||||
no effect, and the bitmap will be aligned at the boundary as by default.
|
||||
.TP
|
||||
.BR \-q ", " \-\-quiet
|
||||
Prints only error messages while creating the exFAT filesystem.
|
||||
.TP
|
||||
.BR \-v ", " \-\-verbose
|
||||
Prints verbose debugging information while creating the exFAT filesystem.
|
||||
.TP
|
||||
|
||||
@@ -10,6 +10,12 @@ tune.exfat \- adjust tunable filesystem parameters on an exFAT filesystem
|
||||
.B \-L
|
||||
.I set-label
|
||||
] [
|
||||
.B \-u
|
||||
.I print-guid
|
||||
] [
|
||||
.B \-U
|
||||
.I set-guid
|
||||
] [
|
||||
.B \-i
|
||||
.I print-serial
|
||||
] [
|
||||
@@ -33,6 +39,12 @@ Print the volume label of the exFAT filesystem.
|
||||
.BI \-L " set-label"
|
||||
Set the volume label of the filesystem to the provided argument.
|
||||
.TP
|
||||
.BI \-u " print-guid"
|
||||
Print the volume GUID of the exFAT filesystem.
|
||||
.TP
|
||||
.BI \-U " set-guid"
|
||||
Set the volume GUID of the filesystem to the provided argument.
|
||||
.TP
|
||||
.BI \-i " print-serial"
|
||||
Print the volume serial of the exFAT filesystem.
|
||||
.TP
|
||||
|
||||
+100
-45
@@ -312,15 +312,27 @@ static int exfat_create_fat_table(struct exfat_blk_dev *bd,
|
||||
static int exfat_create_bitmap(struct exfat_blk_dev *bd)
|
||||
{
|
||||
char *bitmap;
|
||||
unsigned int i, nbytes;
|
||||
unsigned int full_bytes, rem_bits, zero_offset;
|
||||
unsigned int nbytes;
|
||||
|
||||
bitmap = calloc(round_up(finfo.bitmap_byte_len, sizeof(bitmap_t)),
|
||||
sizeof(*bitmap));
|
||||
bitmap = malloc(finfo.bitmap_byte_len);
|
||||
if (!bitmap)
|
||||
return -1;
|
||||
|
||||
for (i = EXFAT_FIRST_CLUSTER; i < finfo.used_clu_cnt; i++)
|
||||
exfat_bitmap_set(bitmap, i);
|
||||
full_bytes = finfo.used_clu_cnt / 8;
|
||||
rem_bits = finfo.used_clu_cnt % 8;
|
||||
zero_offset = full_bytes;
|
||||
|
||||
memset(bitmap, 0xff, full_bytes);
|
||||
|
||||
if (rem_bits != 0) {
|
||||
bitmap[full_bytes] = (1 << rem_bits) - 1;
|
||||
++zero_offset;
|
||||
}
|
||||
|
||||
if (zero_offset < finfo.bitmap_byte_len)
|
||||
memset(bitmap + zero_offset, 0, finfo.bitmap_byte_len - zero_offset);
|
||||
|
||||
|
||||
nbytes = pwrite(bd->dev_fd, bitmap, finfo.bitmap_byte_len, finfo.bitmap_byte_off);
|
||||
if (nbytes != finfo.bitmap_byte_len) {
|
||||
@@ -337,8 +349,8 @@ static int exfat_create_bitmap(struct exfat_blk_dev *bd)
|
||||
static int exfat_create_root_dir(struct exfat_blk_dev *bd,
|
||||
struct exfat_user_input *ui)
|
||||
{
|
||||
struct exfat_dentry ed[3] = {0};
|
||||
int dentries_len = sizeof(struct exfat_dentry) * 3;
|
||||
struct exfat_dentry ed[4] = {0};
|
||||
int dentries_len = sizeof(ed);
|
||||
int nbytes;
|
||||
|
||||
/* Set volume label entry */
|
||||
@@ -347,17 +359,29 @@ static int exfat_create_root_dir(struct exfat_blk_dev *bd,
|
||||
memcpy(ed[0].vol_label, ui->volume_label, ui->volume_label_len);
|
||||
ed[0].vol_char_cnt = ui->volume_label_len/2;
|
||||
|
||||
/* Set volume GUID entry */
|
||||
if (ui->guid) {
|
||||
if (__exfat_set_volume_guid(&ed[1], ui->guid))
|
||||
return -1;
|
||||
} else {
|
||||
/*
|
||||
* Since a single empty entry cannot be allocated for a
|
||||
* file, this can reserve the entry for volume GUID.
|
||||
*/
|
||||
ed[1].type = EXFAT_GUID & ~EXFAT_INVAL;
|
||||
}
|
||||
|
||||
/* Set bitmap entry */
|
||||
ed[1].type = EXFAT_BITMAP;
|
||||
ed[1].bitmap_flags = 0;
|
||||
ed[1].bitmap_start_clu = cpu_to_le32(EXFAT_FIRST_CLUSTER);
|
||||
ed[1].bitmap_size = cpu_to_le64(finfo.bitmap_byte_len);
|
||||
ed[2].type = EXFAT_BITMAP;
|
||||
ed[2].bitmap_flags = 0;
|
||||
ed[2].bitmap_start_clu = cpu_to_le32(EXFAT_FIRST_CLUSTER);
|
||||
ed[2].bitmap_size = cpu_to_le64(finfo.bitmap_byte_len);
|
||||
|
||||
/* Set upcase table entry */
|
||||
ed[2].type = EXFAT_UPCASE;
|
||||
ed[2].upcase_checksum = cpu_to_le32(0xe619d30d);
|
||||
ed[2].upcase_start_clu = cpu_to_le32(finfo.ut_start_clu);
|
||||
ed[2].upcase_size = cpu_to_le64(EXFAT_UPCASE_TABLE_SIZE);
|
||||
ed[3].type = EXFAT_UPCASE;
|
||||
ed[3].upcase_checksum = cpu_to_le32(0xe619d30d);
|
||||
ed[3].upcase_start_clu = cpu_to_le32(finfo.ut_start_clu);
|
||||
ed[3].upcase_size = cpu_to_le64(EXFAT_UPCASE_TABLE_SIZE);
|
||||
|
||||
nbytes = pwrite(bd->dev_fd, ed, dentries_len, finfo.root_byte_off);
|
||||
if (nbytes != dentries_len) {
|
||||
@@ -373,11 +397,14 @@ static void usage(void)
|
||||
{
|
||||
fputs("Usage: mkfs.exfat\n"
|
||||
"\t-L | --volume-label=label Set volume label\n"
|
||||
"\t-U | --volume-guid=guid Set volume GUID\n"
|
||||
"\t-s | --sector-size=size(or suffixed by 'K') Specify sector size\n"
|
||||
"\t-c | --cluster-size=size(or suffixed by 'K' or 'M') Specify cluster size\n"
|
||||
"\t-b | --boundary-align=size(or suffixed by 'K' or 'M') Specify boundary alignment\n"
|
||||
"\t --pack-bitmap Move bitmap into FAT segment\n"
|
||||
"\t-f | --full-format Full format\n"
|
||||
"\t-V | --version Show version\n"
|
||||
"\t-q | --quiet Print only errors\n"
|
||||
"\t-v | --verbose Print debug\n"
|
||||
"\t-h | --help Show help\n",
|
||||
stderr);
|
||||
@@ -389,11 +416,14 @@ static void usage(void)
|
||||
|
||||
static const struct option opts[] = {
|
||||
{"volume-label", required_argument, NULL, 'L' },
|
||||
{"volume-guid", required_argument, NULL, 'U' },
|
||||
{"sector-size", required_argument, NULL, 's' },
|
||||
{"cluster-size", required_argument, NULL, 'c' },
|
||||
{"boundary-align", required_argument, NULL, 'b' },
|
||||
{"pack-bitmap", no_argument, NULL, PACK_BITMAP },
|
||||
{"full-format", no_argument, NULL, 'f' },
|
||||
{"version", no_argument, NULL, 'V' },
|
||||
{"quiet", no_argument, NULL, 'q' },
|
||||
{"verbose", no_argument, NULL, 'v' },
|
||||
{"help", no_argument, NULL, 'h' },
|
||||
{"?", no_argument, NULL, '?' },
|
||||
@@ -437,7 +467,9 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd,
|
||||
struct exfat_user_input *ui)
|
||||
{
|
||||
unsigned long long total_clu_cnt;
|
||||
unsigned long long max_clusters;
|
||||
int clu_len;
|
||||
int num_fats = 1;
|
||||
|
||||
if (ui->cluster_size < bd->sector_size) {
|
||||
exfat_err("cluster size (%u bytes) is smaller than sector size (%u bytes)\n",
|
||||
@@ -451,14 +483,17 @@ 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;
|
||||
|
||||
max_clusters = (bd->size - finfo.fat_byte_off - 8 * num_fats - 1) /
|
||||
(ui->cluster_size + 4 * num_fats) + 1;
|
||||
/* Prevent integer overflow when computing the FAT length */
|
||||
if (bd->num_clusters > UINT32_MAX / 4) {
|
||||
if (max_clusters > UINT_MAX / 4 - 2) {
|
||||
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.fat_byte_len = round_up((max_clusters + 2) * 4, bd->sector_size);
|
||||
finfo.clu_byte_off = round_up(bd->offset + finfo.fat_byte_off +
|
||||
finfo.fat_byte_len, ui->boundary_align) - bd->offset;
|
||||
finfo.fat_byte_len * num_fats, ui->boundary_align) - bd->offset;
|
||||
if (bd->size <= finfo.clu_byte_off) {
|
||||
exfat_err("boundary alignment is too big\n");
|
||||
return -1;
|
||||
@@ -492,35 +527,21 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd,
|
||||
static int exfat_zero_out_disk(struct exfat_blk_dev *bd,
|
||||
struct exfat_user_input *ui)
|
||||
{
|
||||
int nbytes;
|
||||
int ret;
|
||||
unsigned long long total_written = 0;
|
||||
char *buf;
|
||||
unsigned int chunk_size = ui->cluster_size;
|
||||
unsigned long long size;
|
||||
|
||||
if (ui->quick)
|
||||
size = finfo.root_byte_off + chunk_size;
|
||||
size = finfo.root_byte_off + ui->cluster_size;
|
||||
else
|
||||
size = bd->size;
|
||||
|
||||
buf = malloc(chunk_size);
|
||||
if (!buf)
|
||||
return -1;
|
||||
ret = exfat_write_zero(bd->dev_fd, size, 0);
|
||||
if (ret) {
|
||||
exfat_err("write failed(errno : %d)\n", errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(buf, 0, chunk_size);
|
||||
lseek(bd->dev_fd, 0, SEEK_SET);
|
||||
do {
|
||||
|
||||
nbytes = write(bd->dev_fd, buf, chunk_size);
|
||||
if (nbytes <= 0) {
|
||||
if (nbytes < 0)
|
||||
exfat_err("write failed(errno : %d)\n", errno);
|
||||
break;
|
||||
}
|
||||
total_written += nbytes;
|
||||
} while (total_written < size);
|
||||
|
||||
free(buf);
|
||||
exfat_debug("zero out written size : %llu, disk size : %llu\n",
|
||||
total_written, bd->size);
|
||||
return 0;
|
||||
@@ -605,6 +626,7 @@ int main(int argc, char *argv[])
|
||||
struct exfat_blk_dev bd;
|
||||
struct exfat_user_input ui;
|
||||
bool version_only = false;
|
||||
bool quiet = false;
|
||||
|
||||
init_user_input(&ui);
|
||||
|
||||
@@ -612,7 +634,7 @@ int main(int argc, char *argv[])
|
||||
exfat_err("failed to init locale/codeset\n");
|
||||
|
||||
opterr = 0;
|
||||
while ((c = getopt_long(argc, argv, "n:L:c:b:fVvh", opts, NULL)) != EOF)
|
||||
while ((c = getopt_long(argc, argv, "n:L:U:s:c:b:fVqvh", opts, NULL)) != EOF)
|
||||
switch (c) {
|
||||
/*
|
||||
* Make 'n' option fallthrough to 'L' option for for backward
|
||||
@@ -629,6 +651,26 @@ int main(int argc, char *argv[])
|
||||
ui.volume_label_len = ret;
|
||||
break;
|
||||
}
|
||||
case 'U':
|
||||
if (*optarg != '\0' && *optarg != '\r')
|
||||
ui.guid = optarg;
|
||||
break;
|
||||
case 's':
|
||||
ret = parse_size(optarg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
else if (ret & (ret - 1)) {
|
||||
exfat_err("sector size(%d) is not a power of 2\n",
|
||||
ret);
|
||||
goto out;
|
||||
} else if ((ret & 0x1e00) == 0) {
|
||||
exfat_err("sector size(%d) must be 512, 1024, "
|
||||
"2048 or 4096 bytes\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
ui.sector_size = ret;
|
||||
break;
|
||||
case 'c':
|
||||
ret = parse_size(optarg);
|
||||
if (ret < 0)
|
||||
@@ -664,6 +706,10 @@ int main(int argc, char *argv[])
|
||||
case 'V':
|
||||
version_only = true;
|
||||
break;
|
||||
case 'q':
|
||||
print_level = EXFAT_ERROR;
|
||||
quiet = true;
|
||||
break;
|
||||
case 'v':
|
||||
print_level = EXFAT_DEBUG;
|
||||
break;
|
||||
@@ -673,16 +719,25 @@ int main(int argc, char *argv[])
|
||||
usage();
|
||||
}
|
||||
|
||||
show_version();
|
||||
if (version_only)
|
||||
if (version_only) {
|
||||
show_version();
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (!quiet) {
|
||||
show_version();
|
||||
}
|
||||
|
||||
if (argc - optind != 1) {
|
||||
usage();
|
||||
}
|
||||
|
||||
memset(ui.dev_name, 0, sizeof(ui.dev_name));
|
||||
snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[optind]);
|
||||
if (ui.sector_size && ui.cluster_size && ui.sector_size > ui.cluster_size) {
|
||||
exfat_err("cluster size (%u bytes) is smaller than sector size (%u bytes)\n",
|
||||
ui.cluster_size, ui.sector_size);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ui.dev_name = argv[optind];
|
||||
|
||||
ret = exfat_get_blk_dev_info(&ui, &bd);
|
||||
if (ret < 0)
|
||||
@@ -708,6 +763,6 @@ out:
|
||||
if (!ret)
|
||||
exfat_info("\nexFAT format complete!\n");
|
||||
else
|
||||
exfat_info("\nexFAT format fail!\n");
|
||||
exfat_err("\nexFAT format fail!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable → Regular
+17
-6
@@ -3,8 +3,8 @@
|
||||
TESTCASE_DIR=$1
|
||||
NEED_LOOPDEV=$2
|
||||
IMAGE_FILE=exfat.img
|
||||
FSCK_PROG=fsck.exfat
|
||||
FSCK_PROG_2=fsck.exfat
|
||||
FSCK_PROG=${FSCK1:-"fsck.exfat"}
|
||||
FSCK_PROG_2=${FSCK2:-"fsck.exfat"}
|
||||
FSCK_OPTS="-y -s"
|
||||
PASS_COUNT=0
|
||||
|
||||
@@ -37,18 +37,29 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do
|
||||
|
||||
# Set up image file as loop device
|
||||
tar -C . -xf "${TESTCASE_DIR}/${IMAGE_FILE}.tar.xz"
|
||||
if [ "$NEED_LOOPDEV" ]; then
|
||||
if [ $NEED_LOOPDEV ]; then
|
||||
DEV_FILE=$(losetup -f "${IMAGE_FILE}" --show)
|
||||
else
|
||||
DEV_FILE=$IMAGE_FILE
|
||||
fi
|
||||
|
||||
# Run fsck to detect corruptions
|
||||
$FSCK_PROG "$DEV_FILE" | grep -q "ERROR:\|corrupted"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo ""
|
||||
echo "Failed to detect corruption for ${TESTCASE_DIR}"
|
||||
if [ $NEED_LOOPDEV ]; then
|
||||
losetup -d "${DEV_FILE}"
|
||||
fi
|
||||
cleanup
|
||||
fi
|
||||
|
||||
# Run fsck for repair
|
||||
$FSCK_PROG $FSCK_OPTS "$DEV_FILE"
|
||||
if [ $? -ne 1 ] && [ $? -ne 0 ]; then
|
||||
echo ""
|
||||
echo "Failed to repair ${TESTCASE_DIR}"
|
||||
if [ "$NEED_LOOPDEV" ]; then
|
||||
if [ $NEED_LOOPDEV ]; then
|
||||
losetup -d "${DEV_FILE}"
|
||||
fi
|
||||
cleanup
|
||||
@@ -60,7 +71,7 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do
|
||||
if [ $? -ne 0 ]; then
|
||||
echo ""
|
||||
echo "Failed, corrupted ${TESTCASE_DIR}"
|
||||
if [ "$NEED_LOOPDEV" ]; then
|
||||
if [ $NEED_LOOPDEV ]; then
|
||||
losetup -d "${DEV_FILE}"
|
||||
fi
|
||||
cleanup
|
||||
@@ -70,7 +81,7 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do
|
||||
echo "Passed ${TESTCASE_DIR}"
|
||||
PASS_COUNT=$((PASS_COUNT + 1))
|
||||
|
||||
if [ "$NEED_LOOPDEV" ]; then
|
||||
if [ $NEED_LOOPDEV ]; then
|
||||
losetup -d "${DEV_FILE}"
|
||||
fi
|
||||
done
|
||||
|
||||
+32
-6
@@ -20,6 +20,8 @@ static void usage(void)
|
||||
fprintf(stderr, "Usage: tune.exfat\n");
|
||||
fprintf(stderr, "\t-l | --print-label Print volume label\n");
|
||||
fprintf(stderr, "\t-L | --set-label=label Set volume label\n");
|
||||
fprintf(stderr, "\t-u | --print-guid Print volume GUID\n");
|
||||
fprintf(stderr, "\t-U | --set-guid=guid Set volume GUID\n");
|
||||
fprintf(stderr, "\t-i | --print-serial Print volume serial\n");
|
||||
fprintf(stderr, "\t-I | --set-serial=value Set volume serial\n");
|
||||
fprintf(stderr, "\t-V | --version Show version\n");
|
||||
@@ -32,6 +34,8 @@ static void usage(void)
|
||||
static struct option opts[] = {
|
||||
{"print-label", no_argument, NULL, 'l' },
|
||||
{"set-label", required_argument, NULL, 'L' },
|
||||
{"print-guid", no_argument, NULL, 'u' },
|
||||
{"set-guid", required_argument, NULL, 'U' },
|
||||
{"print-serial", no_argument, NULL, 'i' },
|
||||
{"set-serial", required_argument, NULL, 'I' },
|
||||
{"version", no_argument, NULL, 'V' },
|
||||
@@ -45,6 +49,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
int ret = EXIT_FAILURE;
|
||||
unsigned long volume_serial;
|
||||
struct exfat_blk_dev bd;
|
||||
struct exfat_user_input ui;
|
||||
bool version_only = false;
|
||||
@@ -59,7 +64,7 @@ int main(int argc, char *argv[])
|
||||
exfat_err("failed to init locale/codeset\n");
|
||||
|
||||
opterr = 0;
|
||||
while ((c = getopt_long(argc, argv, "I:iL:lVvh", opts, NULL)) != EOF)
|
||||
while ((c = getopt_long(argc, argv, "I:iL:lU:uVvh", opts, NULL)) != EOF)
|
||||
switch (c) {
|
||||
case 'l':
|
||||
flags = EXFAT_GET_VOLUME_LABEL;
|
||||
@@ -69,11 +74,29 @@ int main(int argc, char *argv[])
|
||||
optarg);
|
||||
flags = EXFAT_SET_VOLUME_LABEL;
|
||||
break;
|
||||
case 'u':
|
||||
flags = EXFAT_GET_VOLUME_GUID;
|
||||
break;
|
||||
case 'U':
|
||||
if (*optarg != '\0' && *optarg != '\r')
|
||||
ui.guid = optarg;
|
||||
flags = EXFAT_SET_VOLUME_GUID;
|
||||
break;
|
||||
case 'i':
|
||||
flags = EXFAT_GET_VOLUME_SERIAL;
|
||||
break;
|
||||
case 'I':
|
||||
ui.volume_serial = strtoul(optarg, NULL, 0);
|
||||
ret = exfat_parse_ulong(optarg, &volume_serial);
|
||||
if (volume_serial > UINT_MAX)
|
||||
ret = -ERANGE;
|
||||
|
||||
|
||||
if (ret < 0) {
|
||||
exfat_err("invalid serial number(%s)\n", optarg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ui.volume_serial = volume_serial;
|
||||
flags = EXFAT_SET_VOLUME_SERIAL;
|
||||
break;
|
||||
case 'V':
|
||||
@@ -92,11 +115,10 @@ int main(int argc, char *argv[])
|
||||
if (version_only)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (argc < 3)
|
||||
if (argc < 3 || argc - optind != 1)
|
||||
usage();
|
||||
|
||||
memset(ui.dev_name, 0, sizeof(ui.dev_name));
|
||||
snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[argc - 1]);
|
||||
ui.dev_name = argv[argc - 1];
|
||||
|
||||
ret = exfat_get_blk_dev_info(&ui, &bd);
|
||||
if (ret < 0)
|
||||
@@ -117,7 +139,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
exfat = exfat_alloc_exfat(&bd, bs);
|
||||
if (!exfat) {
|
||||
free(bs);
|
||||
ret = -ENOMEM;
|
||||
goto close_fd_out;
|
||||
}
|
||||
@@ -140,6 +161,11 @@ int main(int argc, char *argv[])
|
||||
ret = exfat_read_volume_label(exfat);
|
||||
else if (flags == EXFAT_SET_VOLUME_LABEL)
|
||||
ret = exfat_set_volume_label(exfat, label_input);
|
||||
else if (flags == EXFAT_GET_VOLUME_GUID)
|
||||
ret = exfat_read_volume_guid(exfat);
|
||||
else if (flags == EXFAT_SET_VOLUME_GUID)
|
||||
ret = exfat_set_volume_guid(exfat, ui.guid);
|
||||
|
||||
close_fd_out:
|
||||
close(bd.dev_fd);
|
||||
if (exfat)
|
||||
|
||||
Reference in New Issue
Block a user