升级1.2.5

Signed-off-by: fangzhiyi18 <fangzhiyi1@huawei.com>
This commit is contained in:
fangzhiyi18
2024-12-05 14:37:28 +08:00
parent 4103af5d5f
commit c18288ed11
34 changed files with 1299 additions and 462 deletions
+11
View File
@@ -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
+71
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
+1 -1
View File
@@ -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",
+1
View File
@@ -16,6 +16,7 @@ dist_man8_MANS = \
# other stuff
EXTRA_DIST = \
include \
tests \
Android.bp \
lib/Android.bp \
mkfs/Android.bp \
+77
View File
@@ -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
View File
@@ -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
View File
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+2
View File
@@ -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
View File
@@ -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;
}
+7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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 -
+31
View File
@@ -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
+12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)