exfatprogs软件升级

Signed-off-by: psycho <wangmingxuan6@h-partners.com>
This commit is contained in:
psycho
2023-12-21 14:29:14 +08:00
parent 5249573f8a
commit d7e510c332
47 changed files with 3954 additions and 1360 deletions
+6
View File
@@ -7,6 +7,7 @@ notifications:
before_script:
- sudo apt-get install linux-headers-$(uname -r)
- sudo apt-get install xz-utils
- git clone --branch=exfat-next https://github.com/namjaejeon/exfat_oot
- ./.travis_get_mainline_kernel
- export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
@@ -21,11 +22,16 @@ script:
- ./configure > /dev/null
- make -j$((`nproc`+1)) > /dev/null
- sudo make install > /dev/null
# build & install exfat
- cd exfat_oot
- make > /dev/null
- sudo make install > /dev/null
- sudo modprobe exfat
- sudo mkdir -p /mnt/test
- cd ..
# run fsck repair testcases
- cd tests
- sudo ./test_fsck.sh
# create file/director test
- truncate -s 10G test.img
- sudo losetup /dev/loop22 test.img
+25 -3
View File
@@ -30,7 +30,7 @@ config("exfat-defaults") {
"-Wno-error",
"-D_FILE_OFFSET_BITS=64",
"-DPACKAGE=\"exfatprogs\"",
"-DVERSION=\"1.1.3\"",
"-DVERSION=\"1.2.0\"",
]
include_dirs = [
"dump",
@@ -39,12 +39,17 @@ config("exfat-defaults") {
"label",
"mkfs",
"tune",
"exfat2img",
]
}
ohos_shared_library("libexfat") {
configs = [ ":exfat-defaults" ]
sources = [ "lib/libexfat.c" ]
sources = [
"lib/exfat_dir.c",
"lib/exfat_fs.c",
"lib/libexfat.c",
]
include_dirs = [ "./libexfat" ]
@@ -81,7 +86,6 @@ ohos_executable("mkfs.exfat") {
ohos_executable("fsck.exfat") {
configs = [ ":exfat-defaults" ]
sources = [
"fsck/de_iter.c",
"fsck/fsck.c",
"fsck/repair.c",
]
@@ -152,3 +156,21 @@ ohos_executable("tune.exfat") {
subsystem_name = "thirdparty"
part_name = "exfatprogs"
}
###################################################
##Build exfat2img
ohos_executable("exfat2img") {
configs = [ ":exfat-defaults" ]
sources = [ "exfat2img/exfat2img.c" ]
include_dirs = [
"./lib",
"./mkfs",
"./fsck",
"./exfat2img",
]
deps = [ ":libexfat" ]
subsystem_name = "thirdparty"
part_name = "exfatprogs"
}
+4 -2
View File
@@ -2,7 +2,7 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = lib mkfs fsck tune label dump
SUBDIRS = lib mkfs fsck tune label dump exfat2img
# manpages
dist_man8_MANS = \
@@ -10,7 +10,8 @@ dist_man8_MANS = \
manpages/tune.exfat.8 \
manpages/mkfs.exfat.8 \
manpages/exfatlabel.8 \
manpages/dump.exfat.8
manpages/dump.exfat.8 \
manpages/exfat2img.8
# other stuff
EXTRA_DIST = \
@@ -22,4 +23,5 @@ EXTRA_DIST = \
fsck/Android.bp \
label/Android.bp \
dump/Android.bp \
exfat2img/Android.bp \
README.md
+23
View File
@@ -1,3 +1,26 @@
exfatprogs 1.2.0 - released 2022-10-28
======================================
CHANGES :
* fsck.exfat: Keep traveling files even if there is a corrupted
directory entry set.
* fsck.exfat: Introduce the option "b" to recover a boot sector even
if an exFAT filesystem is not found.
* fsck.exfat: Introduce the option "s" to create files in
"/LOST+FOUND", which have clusters allocated but was not belonged to
any files.
* fsck.exfat: Rename '.' and '..' entry name to the one user want.
NEW FEATURES :
* fsck.exfat: Repair corruptions of an exFAT filesystem. Please refer
to fsck.exfat manpage to see what kind of corruptions can be repaired.
* exfat2img: Dump metadata of an exFAT filesystem. Please refer to
exfat2img manpage to see how to use it.
BUG FIXES:
* fsck.exfat: Fix an infinite loop while traveling files.
* tune.exfat: Fix bitmap entry corruption when adding new volume lablel.
exfatprogs 1.1.3 - released 2021-11-11
======================================
+3 -4
View File
@@ -52,9 +52,8 @@
<policy name="projectPolicy" desc="">
<policyitem type="compatibility" name="GPL-2.0-or-later" path=".*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter" desc=""/>
<policyitem type="license" name="GPL-2.0-or-later" path=".*" desc=""/>
<policyitem type="copyright" name="Namjae Jeon" path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
<policyitem type="copyright" name="Hyunchul Lee" path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
<policyitem type="copyright" name="Huawei Device Co., Ltd." path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
<policyitem type="license" name="*" path=".*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter" desc=""/>
<policyitem type="copyright" name="*" path=".*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter" desc=""/>
</policy>
</policylist>
<filefilterlist>
@@ -62,7 +61,7 @@
<filteritem type="filename" name=".travis_get_mainline_kernel|Makefile.am|autogen.sh|configure.ac|test_fsck.sh" desc="License and Copyright Header Invalid"/>
<filteritem type="filepath" name="manpages/.*" desc="License and Copyright Header Invalid"/>
</filefilter>
<filefilter name="defaultPolicyFilter" desc="Filters for compatibilitylicense header policies">
<filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license header policies">
</filefilter>
<filefilter name="copyrightPolicyFilter" desc="Filters for copyright header policies">
<filteritem type="filename" name="list.h" desc="desc files"/>
+2 -2
View File
@@ -3,9 +3,9 @@
"Name" : "exfatprogs",
"License" : "GPL-2.0-or-later",
"License File" : "COPYING",
"Version Number" : "1.1.3",
"Version Number" : "1.2.0",
"Owner" : "Namjae Jeon",
"Upstream URL" : "https://github.com/exfatprogs/exfatprogs/releases/download/1.1.3/exfatprogs-1.1.3.tat.gz",
"Upstream URL" : "https://github.com/exfatprogs/exfatprogs/archive/refs/tags/1.2.0.tar.gz",
"Description" : "exFAT filesystem userspace utilities."
}
]
+11 -1
View File
@@ -53,7 +53,10 @@ Usage example:
Usage example:
1. check the consistency.
fsck.exfat /dev/sda1
2. repair and fix.(preparing)
2. repair a corrupted device and create files in /LOST+FOUND, which have clusters allocated but not belonged to any files when reparing the device.
fsck.exfat -p -s /dev/sda1
3. repair a corrupted device in the same way above, but answering yes to all questions.
fsck.exfat -y -s /dev/sda1
- tune.exfat:
Adjust tunable filesystem parameters on an exFAT filesystem
@@ -87,6 +90,12 @@ Usage example:
Usage example:
dump.exfat /dev/sda1
- exfat2img:
Dump metadata of an exFAT filesystem
Usage example:
exfat2img -o sda1.dump /dev/sda1
```
## Benchmarks
@@ -117,3 +126,4 @@ If you have any issues, please create [issues][1] or contact to [Namjae Jeon](ma
## Contributor information
* Please base your pull requests on the `exfat-next` branch.
* Make sure you add 'Signed-Off' information to your commits (e.g. `git commit --signoff`).
* Please check your code contribution using kernel dev-tool script [checkpatch](https://docs.kernel.org/dev-tools/checkpatch.html).
+4 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@ohos/exfatprogs",
"description": "exFAT filesystem userspace utilities.",
"version": "3.1",
"version": "4.1",
"license": "GPL-2.0-or-later",
"publishAs": "code-segment",
"segment": {
@@ -15,7 +15,9 @@
"subsystem": "thirdparty",
"syscap": [],
"features": [],
"adapted_system_type": [],
"adapted_system_type": [
"standard"
],
"rom": "",
"ram": "",
"deps": {
+1
View File
@@ -32,6 +32,7 @@ AC_CONFIG_FILES([
tune/Makefile
label/Makefile
dump/Makefile
exfat2img/Makefile
])
AC_OUTPUT
+6
View File
@@ -90,6 +90,12 @@ static int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd)
goto free_ppbr;
}
if (memcmp(ppbr->bpb.oem_name, "EXFAT ", 8) != 0) {
exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n");
ret = -EINVAL;
goto free_ppbr;
}
pbsx = &ppbr->bsx;
if (pbsx->sect_size_bits < EXFAT_MIN_SECT_SIZE_BITS ||
+6
View File
@@ -0,0 +1,6 @@
AM_CFLAGS = -Wall -Wextra -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common
exfat2img_LDADD = $(top_builddir)/lib/libexfat.a
sbin_PROGRAMS = exfat2img
exfat2img_SOURCES = exfat2img.c
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,6 +1,6 @@
AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common
AM_CFLAGS = -Wall -Wextra -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common
fsck_exfat_LDADD = $(top_builddir)/lib/libexfat.a
sbin_PROGRAMS = fsck.exfat
fsck_exfat_SOURCES = fsck.c repair.c fsck.h de_iter.c repair.h
fsck_exfat_SOURCES = fsck.c repair.c fsck.h repair.h
-313
View File
@@ -1,313 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 Hyunchul Lee <hyc.lee@gmail.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include "exfat_ondisk.h"
#include "libexfat.h"
#include "fsck.h"
static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
{
off_t device_offset;
struct exfat *exfat = iter->exfat;
struct buffer_desc *desc;
unsigned int i;
desc = &iter->buffer_desc[block & 0x01];
device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset;
for (i = 0; i < iter->read_size / iter->write_size; i++) {
if (desc->dirty[i]) {
if (exfat_write(exfat->blk_dev->dev_fd,
desc->buffer + i * iter->write_size,
iter->write_size,
device_offset + i * iter->write_size)
!= (ssize_t)iter->write_size)
return -EIO;
desc->dirty[i] = 0;
}
}
return 0;
}
static int read_ahead_first_blocks(struct exfat_de_iter *iter)
{
#ifdef POSIX_FADV_WILLNEED
struct exfat *exfat = iter->exfat;
clus_t clus_count;
unsigned int size;
clus_count = iter->parent->size / exfat->clus_size;
if (clus_count > 1) {
iter->ra_begin_offset = 0;
iter->ra_next_clus = 1;
size = exfat->clus_size;
} else {
iter->ra_begin_offset = 0;
iter->ra_next_clus = 0;
size = iter->ra_partial_size;
}
return posix_fadvise(exfat->blk_dev->dev_fd,
exfat_c2o(exfat, iter->parent->first_clus), size,
POSIX_FADV_WILLNEED);
#else
return -ENOTSUP;
#endif
}
/**
* read the next fragment in advance, and assume the fragment
* which covers @clus is already read.
*/
static int read_ahead_next_blocks(struct exfat_de_iter *iter,
clus_t clus, unsigned int offset, clus_t p_clus)
{
#ifdef POSIX_FADV_WILLNEED
struct exfat *exfat = iter->exfat;
off_t device_offset;
clus_t clus_count, ra_clus, ra_p_clus;
unsigned int size;
int ret = 0;
clus_count = iter->parent->size / exfat->clus_size;
if (clus + 1 < clus_count) {
ra_clus = clus + 1;
if (ra_clus == iter->ra_next_clus &&
offset >= iter->ra_begin_offset) {
ret = get_next_clus(exfat, iter->parent,
p_clus, &ra_p_clus);
if (ra_p_clus == EXFAT_EOF_CLUSTER)
return -EIO;
device_offset = exfat_c2o(exfat, ra_p_clus);
size = ra_clus + 1 < clus_count ?
exfat->clus_size : iter->ra_partial_size;
ret = posix_fadvise(exfat->blk_dev->dev_fd,
device_offset, size,
POSIX_FADV_WILLNEED);
iter->ra_next_clus = ra_clus + 1;
iter->ra_begin_offset = 0;
}
} else {
if (offset >= iter->ra_begin_offset &&
offset + iter->ra_partial_size <=
exfat->clus_size) {
device_offset = exfat_c2o(exfat, p_clus) +
offset + iter->ra_partial_size;
ret = posix_fadvise(exfat->blk_dev->dev_fd,
device_offset, iter->ra_partial_size,
POSIX_FADV_WILLNEED);
iter->ra_begin_offset =
offset + iter->ra_partial_size;
}
}
return ret;
#else
return -ENOTSUP;
#endif
}
static int read_ahead_next_dir_blocks(struct exfat_de_iter *iter)
{
#ifdef POSIX_FADV_WILLNEED
struct exfat *exfat = iter->exfat;
struct list_head *current;
struct exfat_inode *next_inode;
off_t offset;
if (list_empty(&exfat->dir_list))
return -EINVAL;
current = exfat->dir_list.next;
if (iter->parent == list_entry(current, struct exfat_inode, list) &&
current->next != &exfat->dir_list) {
next_inode = list_entry(current->next, struct exfat_inode,
list);
offset = exfat_c2o(exfat, next_inode->first_clus);
return posix_fadvise(exfat->blk_dev->dev_fd, offset,
iter->ra_partial_size,
POSIX_FADV_WILLNEED);
}
return 0;
#else
return -ENOTSUP;
#endif
}
static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block)
{
struct exfat *exfat = iter->exfat;
struct buffer_desc *desc, *prev_desc;
off_t device_offset;
ssize_t ret;
desc = &iter->buffer_desc[block & 0x01];
if (block == 0) {
desc->p_clus = iter->parent->first_clus;
desc->offset = 0;
}
/* if the buffer already contains dirty dentries, write it */
if (write_block(iter, block))
return -EIO;
if (block > 0) {
if (block > iter->parent->size / iter->read_size)
return EOF;
prev_desc = &iter->buffer_desc[(block-1) & 0x01];
if (prev_desc->offset + 2 * iter->read_size <=
exfat->clus_size) {
desc->p_clus = prev_desc->p_clus;
desc->offset = prev_desc->offset + iter->read_size;
} else {
ret = get_next_clus(exfat, iter->parent,
prev_desc->p_clus, &desc->p_clus);
desc->offset = 0;
if (!ret && desc->p_clus == EXFAT_EOF_CLUSTER)
return EOF;
else if (ret)
return ret;
}
}
device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset;
ret = exfat_read(exfat->blk_dev->dev_fd, desc->buffer,
iter->read_size, device_offset);
if (ret <= 0)
return ret;
/*
* if a buffer is filled with dentries, read blocks ahead of time,
* otherwise read blocks of the next directory in advance.
*/
if (desc->buffer[iter->read_size - 32] != EXFAT_LAST)
read_ahead_next_blocks(iter,
(block * iter->read_size) / exfat->clus_size,
(block * iter->read_size) % exfat->clus_size,
desc->p_clus);
else
read_ahead_next_dir_blocks(iter);
return ret;
}
int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
struct exfat_inode *dir)
{
iter->exfat = exfat;
iter->parent = dir;
iter->write_size = exfat->sect_size;
iter->read_size = exfat->clus_size <= 4*KB ? exfat->clus_size : 4*KB;
if (exfat->clus_size <= 32 * KB)
iter->ra_partial_size = MAX(4 * KB, exfat->clus_size / 2);
else
iter->ra_partial_size = exfat->clus_size / 4;
iter->ra_partial_size = MIN(iter->ra_partial_size, 8 * KB);
if (!iter->buffer_desc)
iter->buffer_desc = exfat->buffer_desc;
if (iter->parent->size == 0)
return EOF;
read_ahead_first_blocks(iter);
if (read_block(iter, 0) != (ssize_t)iter->read_size) {
exfat_err("failed to read directory entries.\n");
return -EIO;
}
iter->de_file_offset = 0;
iter->next_read_offset = iter->read_size;
iter->max_skip_dentries = 0;
return 0;
}
int exfat_de_iter_get(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry)
{
off_t next_de_file_offset;
ssize_t ret;
unsigned int block;
next_de_file_offset = iter->de_file_offset +
ith * sizeof(struct exfat_dentry);
block = (unsigned int)(next_de_file_offset / iter->read_size);
if (next_de_file_offset + sizeof(struct exfat_dentry) >
iter->parent->size)
return EOF;
/* the dentry must be in current, or next block which will be read */
if (block > iter->de_file_offset / iter->read_size + 1)
return -ERANGE;
/* read next cluster if needed */
if (next_de_file_offset >= iter->next_read_offset) {
ret = read_block(iter, block);
if (ret != (ssize_t)iter->read_size)
return ret;
iter->next_read_offset += iter->read_size;
}
if (ith + 1 > iter->max_skip_dentries)
iter->max_skip_dentries = ith + 1;
*dentry = (struct exfat_dentry *)
(iter->buffer_desc[block & 0x01].buffer +
next_de_file_offset % iter->read_size);
return 0;
}
int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry)
{
off_t next_file_offset;
unsigned int block;
int ret, sect_idx;
ret = exfat_de_iter_get(iter, ith, dentry);
if (!ret) {
next_file_offset = iter->de_file_offset +
ith * sizeof(struct exfat_dentry);
block = (unsigned int)(next_file_offset / iter->read_size);
sect_idx = (int)((next_file_offset % iter->read_size) /
iter->write_size);
iter->buffer_desc[block & 0x01].dirty[sect_idx] = 1;
}
return ret;
}
int exfat_de_iter_flush(struct exfat_de_iter *iter)
{
if (write_block(iter, 0) || write_block(iter, 1))
return -EIO;
return 0;
}
/*
* @skip_dentries must be the largest @ith + 1 of exfat_de_iter_get
* since the last call of exfat_de_iter_advance
*/
int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries)
{
if (skip_dentries != iter->max_skip_dentries)
return -EINVAL;
iter->max_skip_dentries = 0;
iter->de_file_offset = iter->de_file_offset +
skip_dentries * sizeof(struct exfat_dentry);
return 0;
}
off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter)
{
return iter->de_file_offset;
}
+804 -771
View File
File diff suppressed because it is too large Load Diff
+9 -73
View File
@@ -7,46 +7,6 @@
#include "list.h"
typedef __u32 clus_t;
struct exfat_inode {
struct exfat_inode *parent;
struct list_head children;
struct list_head sibling;
struct list_head list;
clus_t first_clus;
clus_t last_lclus;
clus_t last_pclus;
__u16 attr;
uint64_t size;
bool is_contiguous;
__le16 name[0]; /* only for directory */
};
#define EXFAT_NAME_MAX 255
#define NAME_BUFFER_SIZE ((EXFAT_NAME_MAX+1)*2)
struct buffer_desc {
clus_t p_clus;
unsigned int offset;
char *buffer;
char *dirty;
};
struct exfat_de_iter {
struct exfat *exfat;
struct exfat_inode *parent;
struct buffer_desc *buffer_desc; /* cluster * 2 */
clus_t ra_next_clus;
unsigned int ra_begin_offset;
unsigned int ra_partial_size;
unsigned int read_size; /* cluster size */
unsigned int write_size; /* sector size */
off_t de_file_offset;
off_t next_read_offset;
int max_skip_dentries;
};
enum fsck_ui_options {
FSCK_OPTS_REPAIR_ASK = 0x01,
FSCK_OPTS_REPAIR_YES = 0x02,
@@ -54,46 +14,22 @@ enum fsck_ui_options {
FSCK_OPTS_REPAIR_AUTO = 0x08,
FSCK_OPTS_REPAIR_WRITE = 0x0b,
FSCK_OPTS_REPAIR_ALL = 0x0f,
FSCK_OPTS_IGNORE_BAD_FS_NAME = 0x10,
FSCK_OPTS_RESCUE_CLUS = 0x20,
};
struct exfat {
struct exfat;
struct exfat_inode;
struct exfat_fsck {
struct exfat *exfat;
struct exfat_de_iter de_iter;
struct buffer_desc *buffer_desc; /* cluster * 2 */
enum fsck_ui_options options;
bool dirty:1;
bool dirty_fat:1;
struct exfat_blk_dev *blk_dev;
struct pbr *bs;
char volume_label[VOLUME_LABEL_BUFFER_SIZE];
struct exfat_inode *root;
struct list_head dir_list;
clus_t clus_count;
unsigned int clus_size;
unsigned int sect_size;
struct exfat_de_iter de_iter;
struct buffer_desc buffer_desc[2]; /* cluster * 2 */
char *alloc_bitmap;
char *disk_bitmap;
clus_t disk_bitmap_clus;
unsigned int disk_bitmap_size;
};
#define EXFAT_CLUSTER_SIZE(pbr) (1 << ((pbr)->bsx.sect_size_bits + \
(pbr)->bsx.sect_per_clus_bits))
#define EXFAT_SECTOR_SIZE(pbr) (1 << (pbr)->bsx.sect_size_bits)
/* fsck.c */
off_t exfat_c2o(struct exfat *exfat, unsigned int clus);
int get_next_clus(struct exfat *exfat, struct exfat_inode *node,
clus_t clus, clus_t *next);
/* de_iter.c */
int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
struct exfat_inode *dir);
int exfat_de_iter_get(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry);
int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry);
int exfat_de_iter_flush(struct exfat_de_iter *iter);
int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries);
off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter);
#endif
+81 -41
View File
@@ -5,16 +5,22 @@
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include "exfat_ondisk.h"
#include "libexfat.h"
#include "fsck.h"
#include "repair.h"
#include "exfat_fs.h"
#include "exfat_dir.h"
#include "fsck.h"
struct exfat_repair_problem {
er_problem_code_t prcode;
unsigned int flags;
unsigned int prompt_type;
unsigned int default_number;
unsigned int bypass_number;
unsigned int max_number;
};
/* Problem flags */
@@ -25,24 +31,36 @@ struct exfat_repair_problem {
/* Prompt types */
#define ERP_FIX 0x00000001
#define ERP_TRUNCATE 0x00000002
#define ERP_DELETE 0x00000003
#define ERP_RENAME 0x00000004
static const char *prompts[] = {
"Repair",
"Fix",
"Truncate",
"Delete",
"Select",
};
static struct exfat_repair_problem problems[] = {
{ER_BS_CHECKSUM, ERF_PREEN_YES, ERP_FIX},
{ER_BS_BOOT_REGION, 0, ERP_FIX},
{ER_DE_CHECKSUM, ERF_PREEN_YES, ERP_FIX},
{ER_FILE_VALID_SIZE, ERF_PREEN_YES, ERP_FIX},
{ER_FILE_INVALID_CLUS, ERF_DEFAULT_NO, ERP_TRUNCATE},
{ER_FILE_FIRST_CLUS, ERF_DEFAULT_NO, ERP_TRUNCATE},
{ER_FILE_SMALLER_SIZE, ERF_DEFAULT_NO, ERP_TRUNCATE},
{ER_FILE_LARGER_SIZE, ERF_DEFAULT_NO, ERP_TRUNCATE},
{ER_FILE_DUPLICATED_CLUS, ERF_DEFAULT_NO, ERP_TRUNCATE},
{ER_FILE_ZERO_NOFAT, ERF_PREEN_YES, ERP_FIX},
{ER_BS_CHECKSUM, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
{ER_BS_BOOT_REGION, 0, ERP_FIX, 0, 0, 0},
{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_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_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},
{ER_FILE_SMALLER_SIZE, ERF_PREEN_YES, ERP_TRUNCATE, 0, 0, 0},
{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},
};
static struct exfat_repair_problem *find_problem(er_problem_code_t prcode)
@@ -57,63 +75,85 @@ static struct exfat_repair_problem *find_problem(er_problem_code_t prcode)
return NULL;
}
static bool ask_repair(struct exfat *exfat, struct exfat_repair_problem *pr)
static int ask_repair(struct exfat_fsck *fsck, struct exfat_repair_problem *pr)
{
bool repair = false;
int repair = 0;
char answer[8];
if (exfat->options & FSCK_OPTS_REPAIR_NO ||
pr->flags & ERF_DEFAULT_NO)
repair = false;
else if (exfat->options & FSCK_OPTS_REPAIR_YES ||
pr->flags & ERF_DEFAULT_YES)
repair = true;
if (fsck->options & FSCK_OPTS_REPAIR_NO ||
pr->flags & ERF_DEFAULT_NO)
repair = 0;
else if (fsck->options & FSCK_OPTS_REPAIR_YES ||
pr->flags & ERF_DEFAULT_YES)
repair = 1;
else {
if (exfat->options & FSCK_OPTS_REPAIR_ASK) {
if (fsck->options & FSCK_OPTS_REPAIR_ASK) {
do {
printf(". %s (y/N)? ",
prompts[pr->prompt_type]);
if (pr->prompt_type & ERP_RENAME) {
printf("%s (Number: ?) ",
prompts[pr->prompt_type]);
} else {
printf(". %s (y/N)? ",
prompts[pr->prompt_type]);
}
fflush(stdout);
if (fgets(answer, sizeof(answer), stdin)) {
if (!fgets(answer, sizeof(answer), stdin))
continue;
if (pr->prompt_type & ERP_RENAME) {
unsigned int number = atoi(answer);
if (number > 0 && number < pr->max_number)
return number;
} else {
if (strcasecmp(answer, "Y\n") == 0)
return true;
else if (strcasecmp(answer, "\n") == 0
|| strcasecmp(answer, "N\n") == 0)
return false;
return 1;
else if (strcasecmp(answer, "\n") == 0 ||
strcasecmp(answer, "N\n") == 0)
return 0;
}
} while (1);
} else if (exfat->options & FSCK_OPTS_REPAIR_AUTO &&
pr->flags & ERF_PREEN_YES)
repair = true;
} else if (fsck->options & FSCK_OPTS_REPAIR_AUTO &&
pr->flags & ERF_PREEN_YES)
repair = 1;
}
printf(". %s (y/N)? %c\n", prompts[pr->prompt_type],
repair ? 'y' : 'n');
if (pr->prompt_type & ERP_RENAME) {
int print_num = repair ? pr->default_number : pr->bypass_number;
printf("%s (Number : %d)\n", prompts[pr->prompt_type],
print_num);
repair = print_num;
} else {
printf(". %s (y/N)? %c\n", prompts[pr->prompt_type],
repair ? 'y' : 'n');
}
return repair;
}
bool exfat_repair_ask(struct exfat *exfat, er_problem_code_t prcode,
const char *desc, ...)
int exfat_repair_ask(struct exfat_fsck *fsck, er_problem_code_t prcode,
const char *desc, ...)
{
struct exfat_repair_problem *pr = NULL;
va_list ap;
int repair;
pr = find_problem(prcode);
if (!pr) {
exfat_err("unknown problem code. %#x\n", prcode);
return false;
return 0;
}
va_start(ap, desc);
vprintf(desc, ap);
va_end(ap);
if (ask_repair(exfat, pr)) {
repair = ask_repair(fsck, pr);
if (repair) {
if (pr->prompt_type & ERP_TRUNCATE)
exfat->dirty_fat = true;
exfat->dirty = true;
return true;
} else
return false;
fsck->dirty_fat = true;
fsck->dirty = true;
}
return repair;
}
+11 -2
View File
@@ -8,6 +8,14 @@
#define ER_BS_CHECKSUM 0x00000001
#define ER_BS_BOOT_REGION 0x00000002
#define ER_DE_CHECKSUM 0x00001001
#define ER_DE_UNKNOWN 0x00001002
#define ER_DE_FILE 0x00001010
#define ER_DE_SECONDARY_COUNT 0x00001011
#define ER_DE_STREAM 0x00001020
#define ER_DE_NAME 0x00001030
#define ER_DE_NAME_HASH 0x00001031
#define ER_DE_NAME_LEN 0x00001032
#define ER_DE_DOT_NAME 0x00001033
#define ER_FILE_VALID_SIZE 0x00002001
#define ER_FILE_INVALID_CLUS 0x00002002
#define ER_FILE_FIRST_CLUS 0x00002003
@@ -17,8 +25,9 @@
#define ER_FILE_ZERO_NOFAT 0x00002007
typedef unsigned int er_problem_code_t;
struct exfat_fsck;
bool exfat_repair_ask(struct exfat *exfat, er_problem_code_t prcode,
const char *fmt, ...);
int exfat_repair_ask(struct exfat_fsck *fsck, er_problem_code_t prcode,
const char *fmt, ...);
#endif
+86
View File
@@ -0,0 +1,86 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2021 LG Electronics.
*
* Author(s): Hyunchul Lee <hyc.lee@gmail.com>
*/
#ifndef _DIR_H_
#define _DIR_H_
struct exfat;
struct exfat_inode;
struct exfat_dentry_loc;
struct buffer_desc;
struct exfat_de_iter {
struct exfat *exfat;
struct exfat_inode *parent;
struct buffer_desc *buffer_desc; /* cluster * 2 */
__u32 ra_next_clus;
unsigned int ra_begin_offset;
unsigned int ra_partial_size;
unsigned int read_size; /* cluster size */
unsigned int write_size; /* sector size */
off_t de_file_offset;
off_t next_read_offset;
int max_skip_dentries;
#define DOT_NAME_NUM_MAX 9999999
unsigned int dot_name_num;
};
struct exfat_lookup_filter {
struct {
uint8_t type;
/* return 0 if matched, return 1 if not matched,
* otherwise return errno
*/
int (*filter)(struct exfat_de_iter *iter,
void *param, int *dentry_count);
void *param;
} in;
struct {
struct exfat_dentry *dentry_set;
int dentry_count;
off_t file_offset;
/* device offset where the dentry_set locates, or
* the empty slot locates or EOF if not found.
*/
off_t dev_offset;
} out;
};
int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
struct exfat_inode *dir, struct buffer_desc *bd);
int exfat_de_iter_get(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry);
int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry);
int exfat_de_iter_flush(struct exfat_de_iter *iter);
int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries);
off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter);
off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter);
int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
struct exfat_lookup_filter *filter);
int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
const char *name, struct exfat_lookup_filter *filter_out);
int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent,
const char *name, unsigned short attr);
int exfat_update_file_dentry_set(struct exfat *exfat,
struct exfat_dentry *dset, int dcount,
const char *name,
clus_t start_clu, clus_t ccount);
int exfat_build_file_dentry_set(struct exfat *exfat, const char *name,
unsigned short attr, struct exfat_dentry **dentry_set,
int *dentry_count);
int exfat_add_dentry_set(struct exfat *exfat, struct exfat_dentry_loc *loc,
struct exfat_dentry *dset, int dcount,
bool need_next_loc);
void exfat_calc_dentry_checksum(struct exfat_dentry *dentry,
uint16_t *checksum, bool primary);
uint16_t exfat_calc_name_hash(struct exfat *exfat,
__le16 *name, int len);
#endif
+88
View File
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2021 LG Electronics.
*
* Author(s): Hyunchul Lee <hyc.lee@gmail.com>
*/
#ifndef _EXFAT_FS_H_
#define _EXFAT_FS_H_
#include "list.h"
struct exfat_dentry;
struct exfat_inode {
struct exfat_inode *parent;
struct list_head children;
struct list_head sibling;
struct list_head list;
clus_t first_clus;
__u16 attr;
uint64_t size;
bool is_contiguous;
struct exfat_dentry *dentry_set;
int dentry_count;
off_t dev_offset;
__le16 name[0]; /* only for directory */
};
#define EXFAT_NAME_MAX 255
#define NAME_BUFFER_SIZE ((EXFAT_NAME_MAX + 1) * 2)
struct exfat {
struct exfat_blk_dev *blk_dev;
struct pbr *bs;
char volume_label[VOLUME_LABEL_BUFFER_SIZE];
struct exfat_inode *root;
struct list_head dir_list;
clus_t clus_count;
unsigned int clus_size;
unsigned int sect_size;
char *disk_bitmap;
char *alloc_bitmap;
char *ohead_bitmap;
clus_t disk_bitmap_clus;
unsigned int disk_bitmap_size;
__u16 *upcase_table;
clus_t start_clu;
char *zero_cluster;
};
struct exfat_dentry_loc {
struct exfat_inode *parent;
off_t file_offset;
off_t dev_offset;
};
struct path_resolve_ctx {
struct exfat_inode *ancestors[255];
__le16 utf16_path[PATH_MAX + 2];
char local_path[PATH_MAX * MB_LEN_MAX + 1];
};
struct buffer_desc {
__u32 p_clus;
unsigned int offset;
char *buffer;
char *dirty;
};
struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs);
void exfat_free_exfat(struct exfat *exfat);
struct exfat_inode *exfat_alloc_inode(__u16 attr);
void exfat_free_inode(struct exfat_inode *node);
void exfat_free_children(struct exfat_inode *dir, bool file_only);
void exfat_free_file_children(struct exfat_inode *dir);
void exfat_free_ancestors(struct exfat_inode *child);
void exfat_free_dir_list(struct exfat *exfat);
int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child);
int exfat_resolve_path_parent(struct path_resolve_ctx *ctx,
struct exfat_inode *parent, struct exfat_inode *child);
struct buffer_desc *exfat_alloc_buffer(int count,
unsigned int clu_size, unsigned int sect_size);
void exfat_free_buffer(struct buffer_desc *bd, int count);
#endif
+5 -2
View File
@@ -39,6 +39,7 @@
#define DENTRY_SIZE_BITS 5
/* exFAT allows 8388608(256MB) directory entries */
#define MAX_EXFAT_DENTRIES 8388608
#define MIN_FILE_DENTRIES 3
/* dentry types */
#define MSDOS_DELETED 0xE5 /* deleted mark */
@@ -156,8 +157,10 @@ struct exfat_dentry {
__le16 access_date;
__u8 create_time_ms;
__u8 modify_time_ms;
__u8 access_time_ms;
__u8 reserved2[9];
__u8 create_tz;
__u8 modify_tz;
__u8 access_tz;
__u8 reserved2[7];
} __attribute__((packed)) file; /* file directory entry */
struct {
__u8 flags;
+63 -8
View File
@@ -10,6 +10,8 @@
#include <wchar.h>
#include <limits.h>
typedef __u32 clus_t;
#define KB (1024)
#define MB (1024*1024)
#define GB (1024UL*1024UL*1024UL)
@@ -35,6 +37,7 @@
#define VOLUME_LABEL_BUFFER_SIZE (VOLUME_LABEL_MAX_LEN*MB_LEN_MAX+1)
/* Upcase table macro */
#define EXFAT_UPCASE_TABLE_CHARS (0x10000)
#define EXFAT_UPCASE_TABLE_SIZE (5836)
/* Flags for tune.exfat and exfatlabel */
@@ -45,6 +48,10 @@
#define EXFAT_MAX_SECTOR_SIZE 4096
#define EXFAT_CLUSTER_SIZE(pbr) (1 << ((pbr)->bsx.sect_size_bits + \
(pbr)->bsx.sect_per_clus_bits))
#define EXFAT_SECTOR_SIZE(pbr) (1 << (pbr)->bsx.sect_size_bits)
enum {
BOOT_SEC_IDX = 0,
EXBOOT_SEC_IDX,
@@ -79,12 +86,51 @@ struct exfat_user_input {
unsigned int volume_serial;
};
struct exfat;
struct exfat_inode;
#ifdef WORDS_BIGENDIAN
typedef __u8 bitmap_t;
#else
typedef __u32 bitmap_t;
#endif
#define BITS_PER (sizeof(bitmap_t) * 8)
#define BIT_MASK(__c) (1 << ((__c) % BITS_PER))
#define BIT_ENTRY(__c) ((__c) / BITS_PER)
#define EXFAT_BITMAP_SIZE(__c_count) \
(DIV_ROUND_UP(__c_count, BITS_PER) * sizeof(bitmap_t))
static inline bool exfat_bitmap_get(char *bmap, clus_t c)
{
clus_t cc = c - EXFAT_FIRST_CLUSTER;
return ((bitmap_t *)(bmap))[BIT_ENTRY(cc)] & BIT_MASK(cc);
}
static inline void exfat_bitmap_set(char *bmap, clus_t c)
{
clus_t cc = c - EXFAT_FIRST_CLUSTER;
(((bitmap_t *)(bmap))[BIT_ENTRY(cc)] |= BIT_MASK(cc));
}
static inline void exfat_bitmap_clear(char *bmap, clus_t c)
{
clus_t cc = c - EXFAT_FIRST_CLUSTER;
(((bitmap_t *)(bmap))[BIT_ENTRY(cc)] &= ~BIT_MASK(cc));
}
void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap,
clus_t start_clus, clus_t count);
int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap,
clus_t start_clu, clus_t *next);
int exfat_bitmap_find_one(struct exfat *exfat, char *bmap,
clus_t start_clu, clus_t *next);
void show_version(void);
void exfat_set_bit(struct exfat_blk_dev *bd, char *bitmap,
unsigned int clu);
void exfat_clear_bit(struct exfat_blk_dev *bd, char *bitmap,
unsigned int clu);
wchar_t exfat_bad_char(wchar_t w);
void boot_calc_checksum(unsigned char *sector, unsigned short size,
bool is_boot_sec, __le32 *checksum);
@@ -99,9 +145,8 @@ ssize_t exfat_utf16_enc(const char *in_str, __u16 *out_str, size_t out_size);
ssize_t exfat_utf16_dec(const __u16 *in_str, size_t in_len,
char *out_str, size_t out_size);
off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd);
int exfat_show_volume_label(struct exfat_blk_dev *bd, off_t root_clu_off);
int exfat_set_volume_label(struct exfat_blk_dev *bd,
char *label_input, off_t root_clu_off);
int exfat_read_volume_label(struct exfat *exfat);
int exfat_set_volume_label(struct exfat *exfat, char *label_input);
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,
@@ -114,7 +159,17 @@ int exfat_set_volume_serial(struct exfat_blk_dev *bd,
struct exfat_user_input *ui);
unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd,
unsigned int clu_off, unsigned int clu);
int exfat_get_next_clus(struct exfat *exfat, clus_t clus, clus_t *next);
int exfat_get_inode_next_clus(struct exfat *exfat, struct exfat_inode *node,
clus_t clus, clus_t *next);
int exfat_set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus);
off_t exfat_s2o(struct exfat *exfat, off_t sect);
off_t exfat_c2o(struct exfat *exfat, unsigned int clus);
int exfat_o2c(struct exfat *exfat, off_t device_offset,
unsigned int *clu, unsigned int *offset);
bool exfat_heap_clus(struct exfat *exfat, clus_t clus);
int exfat_root_clus_count(struct exfat *exfat);
int read_boot_sect(struct exfat_blk_dev *bdev, struct pbr **bs);
/*
* Exfat Print
+1 -1
View File
@@ -5,6 +5,6 @@
#ifndef _VERSION_H
#define EXFAT_PROGS_VERSION "1.1.3"
#define EXFAT_PROGS_VERSION "1.2.0"
#endif /* !_VERSION_H */
+34 -6
View File
@@ -13,6 +13,7 @@
#include "exfat_ondisk.h"
#include "libexfat.h"
#include "exfat_fs.h"
static void usage(void)
{
@@ -39,7 +40,6 @@ int main(int argc, char *argv[])
struct exfat_blk_dev bd;
struct exfat_user_input ui;
bool version_only = false;
off_t root_clu_off;
int serial_mode = 0;
int flags = 0;
@@ -96,15 +96,43 @@ int main(int argc, char *argv[])
ret = exfat_set_volume_serial(&bd, &ui);
}
} else {
/* Mode to change or display volume label */
root_clu_off = exfat_get_root_entry_offset(&bd);
if (root_clu_off < 0)
struct exfat *exfat;
struct pbr *bs;
ret = read_boot_sect(&bd, &bs);
if (ret)
goto close_fd_out;
exfat = exfat_alloc_exfat(&bd, bs);
if (!exfat) {
free(bs);
ret = -ENOMEM;
goto close_fd_out;
}
exfat->root = exfat_alloc_inode(ATTR_SUBDIR);
if (!exfat->root) {
ret = -ENOMEM;
goto free_exfat;
}
exfat->root->first_clus = le32_to_cpu(exfat->bs->bsx.root_cluster);
if (exfat_root_clus_count(exfat)) {
exfat_err("failed to follow the cluster chain of root\n");
exfat_free_inode(exfat->root);
ret = -EINVAL;
goto free_exfat;
}
/* Mode to change or display volume label */
if (flags == EXFAT_GET_VOLUME_LABEL)
ret = exfat_show_volume_label(&bd, root_clu_off);
ret = exfat_read_volume_label(exfat);
else if (flags == EXFAT_SET_VOLUME_LABEL)
ret = exfat_set_volume_label(&bd, argv[2], root_clu_off);
ret = exfat_set_volume_label(exfat, argv[2]);
free_exfat:
if (exfat)
exfat_free_exfat(exfat);
}
close_fd_out:
+1 -1
View File
@@ -1,4 +1,4 @@
AM_CFLAGS = -Wall -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common
noinst_LIBRARIES = libexfat.a
libexfat_a_SOURCES = libexfat.c
libexfat_a_SOURCES = libexfat.c exfat_fs.c exfat_dir.c
+930
View File
@@ -0,0 +1,930 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2021 LG Electronics.
*
* Author(s): Hyunchul Lee <hyc.lee@gmail.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include "exfat_ondisk.h"
#include "libexfat.h"
#include "exfat_fs.h"
#include "exfat_dir.h"
static struct path_resolve_ctx path_resolve_ctx;
#define fsck_err(parent, inode, fmt, ...) \
({ \
exfat_resolve_path_parent(&path_resolve_ctx, \
parent, inode); \
exfat_err("ERROR: %s: " fmt, \
path_resolve_ctx.local_path, \
##__VA_ARGS__); \
})
static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
{
off_t device_offset;
struct exfat *exfat = iter->exfat;
struct buffer_desc *desc;
unsigned int i;
desc = &iter->buffer_desc[block & 0x01];
device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset;
for (i = 0; i < iter->read_size / iter->write_size; i++) {
if (desc->dirty[i]) {
if (exfat_write(exfat->blk_dev->dev_fd,
desc->buffer + i * iter->write_size,
iter->write_size,
device_offset + i * iter->write_size)
!= (ssize_t)iter->write_size)
return -EIO;
desc->dirty[i] = 0;
}
}
return 0;
}
static int read_ahead_first_blocks(struct exfat_de_iter *iter)
{
#ifdef POSIX_FADV_WILLNEED
struct exfat *exfat = iter->exfat;
clus_t clus_count;
unsigned int size;
clus_count = iter->parent->size / exfat->clus_size;
if (clus_count > 1) {
iter->ra_begin_offset = 0;
iter->ra_next_clus = 1;
size = exfat->clus_size;
} else {
iter->ra_begin_offset = 0;
iter->ra_next_clus = 0;
size = iter->ra_partial_size;
}
return posix_fadvise(exfat->blk_dev->dev_fd,
exfat_c2o(exfat, iter->parent->first_clus), size,
POSIX_FADV_WILLNEED);
#else
return -ENOTSUP;
#endif
}
/**
* read the next fragment in advance, and assume the fragment
* which covers @clus is already read.
*/
static int read_ahead_next_blocks(struct exfat_de_iter *iter,
clus_t clus, unsigned int offset, clus_t p_clus)
{
#ifdef POSIX_FADV_WILLNEED
struct exfat *exfat = iter->exfat;
off_t device_offset;
clus_t clus_count, ra_clus, ra_p_clus;
unsigned int size;
int ret = 0;
clus_count = iter->parent->size / exfat->clus_size;
if (clus + 1 < clus_count) {
ra_clus = clus + 1;
if (ra_clus == iter->ra_next_clus &&
offset >= iter->ra_begin_offset) {
ret = exfat_get_inode_next_clus(exfat, iter->parent,
p_clus, &ra_p_clus);
if (ret)
return ret;
if (ra_p_clus == EXFAT_EOF_CLUSTER)
return -EIO;
device_offset = exfat_c2o(exfat, ra_p_clus);
size = ra_clus + 1 < clus_count ?
exfat->clus_size : iter->ra_partial_size;
ret = posix_fadvise(exfat->blk_dev->dev_fd,
device_offset, size,
POSIX_FADV_WILLNEED);
iter->ra_next_clus = ra_clus + 1;
iter->ra_begin_offset = 0;
}
} else {
if (offset >= iter->ra_begin_offset &&
offset + iter->ra_partial_size <=
exfat->clus_size) {
device_offset = exfat_c2o(exfat, p_clus) +
offset + iter->ra_partial_size;
ret = posix_fadvise(exfat->blk_dev->dev_fd,
device_offset, iter->ra_partial_size,
POSIX_FADV_WILLNEED);
iter->ra_begin_offset =
offset + iter->ra_partial_size;
}
}
return ret;
#else
return -ENOTSUP;
#endif
}
static int read_ahead_next_dir_blocks(struct exfat_de_iter *iter)
{
#ifdef POSIX_FADV_WILLNEED
struct exfat *exfat = iter->exfat;
struct list_head *current;
struct exfat_inode *next_inode;
off_t offset;
if (list_empty(&exfat->dir_list))
return -EINVAL;
current = exfat->dir_list.next;
if (iter->parent == list_entry(current, struct exfat_inode, list) &&
current->next != &exfat->dir_list) {
next_inode = list_entry(current->next, struct exfat_inode,
list);
offset = exfat_c2o(exfat, next_inode->first_clus);
return posix_fadvise(exfat->blk_dev->dev_fd, offset,
iter->ra_partial_size,
POSIX_FADV_WILLNEED);
}
return 0;
#else
return -ENOTSUP;
#endif
}
static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block)
{
struct exfat *exfat = iter->exfat;
struct buffer_desc *desc, *prev_desc;
off_t device_offset;
ssize_t ret;
desc = &iter->buffer_desc[block & 0x01];
if (block == 0) {
desc->p_clus = iter->parent->first_clus;
desc->offset = 0;
}
/* if the buffer already contains dirty dentries, write it */
if (write_block(iter, block))
return -EIO;
if (block > 0) {
if (block > iter->parent->size / iter->read_size)
return EOF;
prev_desc = &iter->buffer_desc[(block-1) & 0x01];
if (prev_desc->offset + 2 * iter->read_size <=
exfat->clus_size) {
desc->p_clus = prev_desc->p_clus;
desc->offset = prev_desc->offset + iter->read_size;
} else {
ret = exfat_get_inode_next_clus(exfat, iter->parent,
prev_desc->p_clus, &desc->p_clus);
desc->offset = 0;
if (ret)
return ret;
else if (desc->p_clus == EXFAT_EOF_CLUSTER)
return EOF;
}
}
device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset;
ret = exfat_read(exfat->blk_dev->dev_fd, desc->buffer,
iter->read_size, device_offset);
if (ret <= 0)
return ret;
/*
* if a buffer is filled with dentries, read blocks ahead of time,
* otherwise read blocks of the next directory in advance.
*/
if (desc->buffer[iter->read_size - 32] != EXFAT_LAST)
read_ahead_next_blocks(iter,
(block * iter->read_size) / exfat->clus_size,
(block * iter->read_size) % exfat->clus_size,
desc->p_clus);
else
read_ahead_next_dir_blocks(iter);
return ret;
}
int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
struct exfat_inode *dir, struct buffer_desc *bd)
{
iter->exfat = exfat;
iter->parent = dir;
iter->write_size = exfat->sect_size;
iter->read_size = exfat->clus_size <= 4*KB ? exfat->clus_size : 4*KB;
if (exfat->clus_size <= 32 * KB)
iter->ra_partial_size = MAX(4 * KB, exfat->clus_size / 2);
else
iter->ra_partial_size = exfat->clus_size / 4;
iter->ra_partial_size = MIN(iter->ra_partial_size, 8 * KB);
iter->buffer_desc = bd;
iter->de_file_offset = 0;
iter->next_read_offset = iter->read_size;
iter->max_skip_dentries = 0;
iter->dot_name_num = 0;
if (iter->parent->size == 0)
return EOF;
read_ahead_first_blocks(iter);
if (read_block(iter, 0) != (ssize_t)iter->read_size) {
exfat_err("failed to read directory entries.\n");
return -EIO;
}
return 0;
}
int exfat_de_iter_get(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry)
{
off_t next_de_file_offset;
ssize_t ret;
unsigned int block;
next_de_file_offset = iter->de_file_offset +
ith * sizeof(struct exfat_dentry);
block = (unsigned int)(next_de_file_offset / iter->read_size);
if (next_de_file_offset + sizeof(struct exfat_dentry) >
iter->parent->size)
return EOF;
/* the dentry must be in current, or next block which will be read */
if (block > iter->de_file_offset / iter->read_size + 1)
return -ERANGE;
/* read next cluster if needed */
if (next_de_file_offset >= iter->next_read_offset) {
ret = read_block(iter, block);
if (ret != (ssize_t)iter->read_size)
return ret;
iter->next_read_offset += iter->read_size;
}
if (ith + 1 > iter->max_skip_dentries)
iter->max_skip_dentries = ith + 1;
*dentry = (struct exfat_dentry *)
(iter->buffer_desc[block & 0x01].buffer +
next_de_file_offset % iter->read_size);
return 0;
}
int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
int ith, struct exfat_dentry **dentry)
{
off_t next_file_offset;
unsigned int block;
int ret, sect_idx;
ret = exfat_de_iter_get(iter, ith, dentry);
if (!ret) {
next_file_offset = iter->de_file_offset +
ith * sizeof(struct exfat_dentry);
block = (unsigned int)(next_file_offset / iter->read_size);
sect_idx = (int)((next_file_offset % iter->read_size) /
iter->write_size);
iter->buffer_desc[block & 0x01].dirty[sect_idx] = 1;
}
return ret;
}
int exfat_de_iter_flush(struct exfat_de_iter *iter)
{
if (write_block(iter, 0) || write_block(iter, 1))
return -EIO;
return 0;
}
int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries)
{
if (skip_dentries > iter->max_skip_dentries)
return -EINVAL;
iter->max_skip_dentries = 0;
iter->de_file_offset = iter->de_file_offset +
skip_dentries * sizeof(struct exfat_dentry);
return 0;
}
off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter)
{
struct buffer_desc *bd;
unsigned int block;
if ((uint64_t)iter->de_file_offset >= iter->parent->size)
return EOF;
block = iter->de_file_offset / iter->read_size;
bd = &iter->buffer_desc[block & 0x01];
return exfat_c2o(iter->exfat, bd->p_clus) + bd->offset +
iter->de_file_offset % iter->read_size;
}
off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter)
{
return iter->de_file_offset;
}
/*
* try to find the dentry set matched with @filter. this function
* doesn't verify the dentry set.
*
* if found, return 0. if not found, return EOF. otherwise return errno.
*/
int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
struct exfat_lookup_filter *filter)
{
struct buffer_desc *bd = NULL;
struct exfat_dentry *dentry = NULL;
off_t free_file_offset = 0, free_dev_offset = 0;
struct exfat_de_iter de_iter;
int dentry_count;
int retval;
bool last_is_free = false;
bd = exfat_alloc_buffer(2, exfat->clus_size, exfat->sect_size);
if (!bd)
return -ENOMEM;
retval = exfat_de_iter_init(&de_iter, exfat, parent, bd);
if (retval == EOF || retval)
goto out;
filter->out.dentry_set = NULL;
while (1) {
retval = exfat_de_iter_get(&de_iter, 0, &dentry);
if (retval == EOF) {
break;
} else if (retval) {
fsck_err(parent->parent, parent,
"failed to get a dentry. %d\n", retval);
goto out;
}
dentry_count = 1;
if (dentry->type == filter->in.type) {
retval = 0;
if (filter->in.filter)
retval = filter->in.filter(&de_iter,
filter->in.param,
&dentry_count);
if (retval == 0) {
struct exfat_dentry *d;
int i;
filter->out.dentry_set = calloc(dentry_count,
sizeof(struct exfat_dentry));
if (!filter->out.dentry_set) {
retval = -ENOMEM;
goto out;
}
for (i = 0; i < dentry_count; i++) {
exfat_de_iter_get(&de_iter, i, &d);
memcpy(filter->out.dentry_set + i, d,
sizeof(struct exfat_dentry));
}
filter->out.dentry_count = dentry_count;
goto out;
} else if (retval < 0) {
goto out;
}
last_is_free = false;
} else if ((dentry->type == EXFAT_LAST ||
IS_EXFAT_DELETED(dentry->type))) {
if (!last_is_free) {
free_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;
}
exfat_de_iter_advance(&de_iter, dentry_count);
}
out:
if (retval == 0) {
filter->out.file_offset =
exfat_de_iter_file_offset(&de_iter);
filter->out.dev_offset =
exfat_de_iter_device_offset(&de_iter);
} else if (retval == EOF && last_is_free) {
filter->out.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;
}
static int filter_lookup_file(struct exfat_de_iter *de_iter,
void *param, int *dentry_count)
{
struct exfat_dentry *file_de, *stream_de, *name_de;
__le16 *name;
int retval, name_len;
int i;
retval = exfat_de_iter_get(de_iter, 0, &file_de);
if (retval || file_de->type != EXFAT_FILE)
return 1;
retval = exfat_de_iter_get(de_iter, 1, &stream_de);
if (retval || stream_de->type != EXFAT_STREAM)
return 1;
name = (__le16 *)param;
name_len = (int)exfat_utf16_len(name, PATH_MAX);
if (file_de->dentry.file.num_ext <
1 + (name_len + ENTRY_NAME_MAX - 1) / ENTRY_NAME_MAX)
return 1;
for (i = 2; i <= file_de->dentry.file.num_ext && name_len > 0; i++) {
int len;
retval = exfat_de_iter_get(de_iter, i, &name_de);
if (retval || name_de->type != EXFAT_NAME)
return 1;
len = MIN(name_len, ENTRY_NAME_MAX);
if (memcmp(name_de->dentry.name.unicode_0_14,
name, len * 2) != 0)
return 1;
name += len;
name_len -= len;
}
*dentry_count = i;
return 0;
}
int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
const char *name, struct exfat_lookup_filter *filter_out)
{
int retval;
__le16 utf16_name[PATH_MAX + 2] = {0, };
retval = (int)exfat_utf16_enc(name, utf16_name, sizeof(utf16_name));
if (retval < 0)
return retval;
filter_out->in.type = EXFAT_FILE;
filter_out->in.filter = filter_lookup_file;
filter_out->in.param = utf16_name;
retval = exfat_lookup_dentry_set(exfat, parent, filter_out);
if (retval < 0)
return retval;
return 0;
}
void exfat_calc_dentry_checksum(struct exfat_dentry *dentry,
uint16_t *checksum, bool primary)
{
unsigned int i;
uint8_t *bytes;
bytes = (uint8_t *)dentry;
*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[0];
*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[1];
i = primary ? 4 : 2;
for (; i < sizeof(*dentry); i++)
*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[i];
}
static uint16_t calc_dentry_set_checksum(struct exfat_dentry *dset, int dcount)
{
uint16_t checksum;
int i;
if (dcount < MIN_FILE_DENTRIES)
return 0;
checksum = 0;
exfat_calc_dentry_checksum(&dset[0], &checksum, true);
for (i = 1; i < dcount; i++)
exfat_calc_dentry_checksum(&dset[i], &checksum, false);
return checksum;
}
uint16_t exfat_calc_name_hash(struct exfat *exfat,
__le16 *name, int len)
{
int i;
__le16 ch;
uint16_t chksum = 0;
for (i = 0; i < len; i++) {
ch = exfat->upcase_table[le16_to_cpu(name[i])];
ch = cpu_to_le16(ch);
chksum = ((chksum << 15) | (chksum >> 1)) + (ch & 0xFF);
chksum = ((chksum << 15) | (chksum >> 1)) + (ch >> 8);
}
return chksum;
}
static void unix_time_to_exfat_time(time_t unix_time, __u8 *tz, __le16 *date,
__le16 *time, __u8 *time_ms)
{
struct tm tm;
__u16 t, d;
gmtime_r(&unix_time, &tm);
d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
*tz = 0x80;
*date = cpu_to_le16(d);
*time = cpu_to_le16(t);
if (time_ms)
*time_ms = (tm.tm_sec & 1) * 100;
}
int exfat_build_file_dentry_set(struct exfat *exfat, const char *name,
unsigned short attr, struct exfat_dentry **dentry_set,
int *dentry_count)
{
struct exfat_dentry *dset;
__le16 utf16_name[PATH_MAX + 2];
int retval;
int dcount, name_len, i;
__le16 e_date, e_time;
__u8 tz, e_time_ms;
memset(utf16_name, 0, sizeof(utf16_name));
retval = exfat_utf16_enc(name, utf16_name, sizeof(utf16_name));
if (retval < 0)
return retval;
name_len = retval / 2;
dcount = 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX);
dset = calloc(1, dcount * DENTRY_SIZE);
if (!dset)
return -ENOMEM;
dset[0].type = EXFAT_FILE;
dset[0].dentry.file.num_ext = dcount - 1;
dset[0].dentry.file.attr = cpu_to_le16(attr);
unix_time_to_exfat_time(time(NULL), &tz,
&e_date, &e_time, &e_time_ms);
dset[0].dentry.file.create_date = e_date;
dset[0].dentry.file.create_time = e_time;
dset[0].dentry.file.create_time_ms = e_time_ms;
dset[0].dentry.file.create_tz = tz;
dset[0].dentry.file.modify_date = e_date;
dset[0].dentry.file.modify_time = e_time;
dset[0].dentry.file.modify_time_ms = e_time_ms;
dset[0].dentry.file.modify_tz = tz;
dset[0].dentry.file.access_date = e_date;
dset[0].dentry.file.access_time = e_time;
dset[0].dentry.file.access_tz = tz;
dset[1].type = EXFAT_STREAM;
dset[1].dentry.stream.flags = 0x01;
dset[1].dentry.stream.name_len = (__u8)name_len;
dset[1].dentry.stream.name_hash =
cpu_to_le16(exfat_calc_name_hash(exfat, utf16_name, name_len));
for (i = 2; i < dcount; i++) {
dset[i].type = EXFAT_NAME;
memcpy(dset[i].dentry.name.unicode_0_14,
utf16_name + (i - 2) * ENTRY_NAME_MAX,
ENTRY_NAME_MAX * 2);
}
dset[0].dentry.file.checksum =
cpu_to_le16(calc_dentry_set_checksum(dset, dcount));
*dentry_set = dset;
*dentry_count = dcount;
return 0;
}
int exfat_update_file_dentry_set(struct exfat *exfat,
struct exfat_dentry *dset, int dcount,
const char *name,
clus_t start_clu, clus_t ccount)
{
int i, name_len;
__le16 utf16_name[PATH_MAX + 2];
if (dset[0].type != EXFAT_FILE || dcount < MIN_FILE_DENTRIES)
return -EINVAL;
if (name) {
name_len = (int)exfat_utf16_enc(name,
utf16_name, sizeof(utf16_name));
if (name_len < 0)
return name_len;
name_len /= 2;
if (dcount != 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX))
return -EINVAL;
dset[1].dentry.stream.name_len = (__u8)name_len;
dset[1].dentry.stream.name_hash =
exfat_calc_name_hash(exfat, utf16_name, name_len);
for (i = 2; i < dcount; i++) {
dset[i].type = EXFAT_NAME;
memcpy(dset[i].dentry.name.unicode_0_14,
utf16_name + (i - 2) * ENTRY_NAME_MAX,
ENTRY_NAME_MAX * 2);
}
}
dset[1].dentry.stream.valid_size = cpu_to_le64(ccount * exfat->clus_size);
dset[1].dentry.stream.size = cpu_to_le64(ccount * exfat->clus_size);
if (start_clu)
dset[1].dentry.stream.start_clu = cpu_to_le32(start_clu);
dset[0].dentry.file.checksum =
cpu_to_le16(calc_dentry_set_checksum(dset, dcount));
return 0;
}
static int find_free_cluster(struct exfat *exfat,
clus_t start, clus_t *new_clu)
{
clus_t end = le32_to_cpu(exfat->bs->bsx.clu_count) +
EXFAT_FIRST_CLUSTER;
if (!exfat_heap_clus(exfat, start))
return -EINVAL;
while (start < end) {
if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap,
start, new_clu))
break;
if (!exfat_bitmap_get(exfat->disk_bitmap, *new_clu))
return 0;
start = *new_clu + 1;
}
end = start;
start = EXFAT_FIRST_CLUSTER;
while (start < end) {
if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap,
start, new_clu))
goto out_nospc;
if (!exfat_bitmap_get(exfat->disk_bitmap, *new_clu))
return 0;
start = *new_clu + 1;
}
out_nospc:
*new_clu = EXFAT_EOF_CLUSTER;
return -ENOSPC;
}
static int exfat_map_cluster(struct exfat *exfat, struct exfat_inode *inode,
off_t file_off, clus_t *mapped_clu)
{
clus_t clu, next, count, last_count;
if (!exfat_heap_clus(exfat, inode->first_clus))
return -EINVAL;
clu = inode->first_clus;
next = EXFAT_EOF_CLUSTER;
count = 1;
if (file_off == EOF)
last_count = DIV_ROUND_UP(inode->size, exfat->clus_size);
else
last_count = file_off / exfat->clus_size + 1;
while (true) {
if (count * exfat->clus_size > inode->size)
return -EINVAL;
if (count == last_count) {
*mapped_clu = clu;
return 0;
}
if (exfat_get_inode_next_clus(exfat, inode, clu, &next))
return -EINVAL;
if (!exfat_heap_clus(exfat, clu))
return -EINVAL;
clu = next;
count++;
}
return -EINVAL;
}
static int exfat_write_dentry_set(struct exfat *exfat,
struct exfat_dentry *dset, int dcount,
off_t dev_off, off_t *next_dev_off)
{
clus_t clus;
unsigned int clus_off, dent_len, first_half_len, sec_half_len;
off_t first_half_off, sec_half_off = 0;
if (exfat_o2c(exfat, dev_off, &clus, &clus_off))
return -ERANGE;
dent_len = dcount * DENTRY_SIZE;
first_half_len = MIN(dent_len, exfat->clus_size - clus_off);
sec_half_len = dent_len - first_half_len;
first_half_off = dev_off;
if (sec_half_len) {
clus_t next_clus;
if (exfat_get_next_clus(exfat, clus, &next_clus))
return -EIO;
if (!exfat_heap_clus(exfat, next_clus))
return -EINVAL;
sec_half_off = exfat_c2o(exfat, next_clus);
}
if (exfat_write(exfat->blk_dev->dev_fd, dset, first_half_len,
first_half_off) != (ssize_t)first_half_len)
return -EIO;
if (sec_half_len) {
dset = (struct exfat_dentry *)((char *)dset + first_half_len);
if (exfat_write(exfat->blk_dev->dev_fd, dset, sec_half_len,
sec_half_off) != (ssize_t)sec_half_len)
return -EIO;
}
if (next_dev_off) {
if (sec_half_len)
*next_dev_off = sec_half_off + sec_half_len;
else
*next_dev_off = first_half_off + first_half_len;
}
return 0;
}
static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode,
clus_t *new_clu)
{
clus_t last_clu;
int err;
bool need_dset = inode != exfat->root;
if ((need_dset && !inode->dentry_set) || inode->is_contiguous)
return -EINVAL;
err = find_free_cluster(exfat, exfat->start_clu, new_clu);
if (err) {
exfat->start_clu = EXFAT_FIRST_CLUSTER;
exfat_err("failed to find an free cluster\n");
return -ENOSPC;
}
exfat->start_clu = *new_clu;
if (exfat_set_fat(exfat, *new_clu, EXFAT_EOF_CLUSTER))
return -EIO;
/* zero out the new cluster */
if (exfat_write(exfat->blk_dev->dev_fd, exfat->zero_cluster,
exfat->clus_size, exfat_c2o(exfat, *new_clu)) !=
(ssize_t)exfat->clus_size) {
exfat_err("failed to fill new cluster with zeroes\n");
return -EIO;
}
if (inode->size) {
err = exfat_map_cluster(exfat, inode, EOF, &last_clu);
if (err) {
exfat_err("failed to get the last cluster\n");
return err;
}
if (exfat_set_fat(exfat, last_clu, *new_clu))
return -EIO;
if (need_dset) {
err = exfat_update_file_dentry_set(exfat,
inode->dentry_set,
inode->dentry_count,
NULL, 0,
DIV_ROUND_UP(inode->size,
exfat->clus_size) + 1);
if (err)
return -EINVAL;
}
} else {
if (need_dset) {
err = exfat_update_file_dentry_set(exfat,
inode->dentry_set,
inode->dentry_count,
NULL, *new_clu, 1);
if (err)
return -EINVAL;
}
}
if (need_dset && exfat_write_dentry_set(exfat, inode->dentry_set,
inode->dentry_count,
inode->dev_offset, NULL))
return -EIO;
exfat_bitmap_set(exfat->alloc_bitmap, *new_clu);
if (inode->size == 0)
inode->first_clus = *new_clu;
inode->size += exfat->clus_size;
return 0;
}
int exfat_add_dentry_set(struct exfat *exfat, struct exfat_dentry_loc *loc,
struct exfat_dentry *dset, int dcount,
bool need_next_loc)
{
struct exfat_inode *parent = loc->parent;
off_t dev_off, next_dev_off;
if (parent->is_contiguous ||
(uint64_t)loc->file_offset > parent->size ||
(unsigned int)dcount * DENTRY_SIZE > exfat->clus_size)
return -EINVAL;
dev_off = loc->dev_offset;
if ((uint64_t)loc->file_offset + dcount * DENTRY_SIZE > parent->size) {
clus_t new_clus;
if (exfat_alloc_cluster(exfat, parent, &new_clus))
return -EIO;
if ((uint64_t)loc->file_offset == parent->size - exfat->clus_size)
dev_off = exfat_c2o(exfat, new_clus);
}
if (exfat_write_dentry_set(exfat, dset, dcount, dev_off, &next_dev_off))
return -EIO;
if (need_next_loc) {
loc->file_offset += dcount * DENTRY_SIZE;
loc->dev_offset = next_dev_off;
}
return 0;
}
int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent,
const char *name, unsigned short attr)
{
struct exfat_dentry *dset;
int err, dcount;
struct exfat_lookup_filter filter;
struct exfat_dentry_loc loc;
err = exfat_lookup_file(exfat, parent, name, &filter);
if (err == 0) {
dset = filter.out.dentry_set;
dcount = filter.out.dentry_count;
if ((le16_to_cpu(dset->dentry.file.attr) & attr) != attr)
err = -EEXIST;
goto out;
}
err = exfat_build_file_dentry_set(exfat, name, attr,
&dset, &dcount);
if (err)
return err;
loc.parent = parent;
loc.file_offset = filter.out.file_offset;
loc.dev_offset = filter.out.dev_offset;
err = exfat_add_dentry_set(exfat, &loc, dset, dcount, false);
out:
free(dset);
return err;
}
+304
View File
@@ -0,0 +1,304 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2021 LG Electronics.
*
* Author(s): Hyunchul Lee <hyc.lee@gmail.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "exfat_ondisk.h"
#include "libexfat.h"
#include "exfat_fs.h"
#include "exfat_dir.h"
struct exfat_inode *exfat_alloc_inode(__u16 attr)
{
struct exfat_inode *node;
int size;
size = offsetof(struct exfat_inode, name) + NAME_BUFFER_SIZE;
node = (struct exfat_inode *)calloc(1, size);
if (!node) {
exfat_err("failed to allocate exfat_node\n");
return NULL;
}
node->parent = NULL;
INIT_LIST_HEAD(&node->children);
INIT_LIST_HEAD(&node->sibling);
INIT_LIST_HEAD(&node->list);
node->attr = attr;
return node;
}
void exfat_free_inode(struct exfat_inode *node)
{
if (node) {
if (node->dentry_set)
free(node->dentry_set);
free(node);
}
}
void exfat_free_children(struct exfat_inode *dir, bool file_only)
{
struct exfat_inode *node, *i;
list_for_each_entry_safe(node, i, &dir->children, sibling) {
if (file_only) {
if (!(node->attr & ATTR_SUBDIR)) {
list_del(&node->sibling);
exfat_free_inode(node);
}
} else {
list_del(&node->sibling);
list_del(&node->list);
exfat_free_inode(node);
}
}
}
void exfat_free_file_children(struct exfat_inode *dir)
{
exfat_free_children(dir, true);
}
/* delete @child and all ancestors that does not have
* children
*/
void exfat_free_ancestors(struct exfat_inode *child)
{
struct exfat_inode *parent;
while (child && list_empty(&child->children)) {
if (!child->parent || !(child->attr & ATTR_SUBDIR))
return;
parent = child->parent;
list_del(&child->sibling);
exfat_free_inode(child);
child = parent;
}
return;
}
void exfat_free_dir_list(struct exfat *exfat)
{
struct exfat_inode *dir, *i;
list_for_each_entry_safe(dir, i, &exfat->dir_list, list) {
if (!dir->parent)
continue;
exfat_free_file_children(dir);
list_del(&dir->list);
exfat_free_inode(dir);
}
}
void exfat_free_exfat(struct exfat *exfat)
{
if (exfat) {
if (exfat->bs)
free(exfat->bs);
if (exfat->alloc_bitmap)
free(exfat->alloc_bitmap);
if (exfat->disk_bitmap)
free(exfat->disk_bitmap);
if (exfat->ohead_bitmap)
free(exfat->ohead_bitmap);
if (exfat->upcase_table)
free(exfat->upcase_table);
if (exfat->root)
exfat_free_inode(exfat->root);
if (exfat->zero_cluster)
free(exfat->zero_cluster);
free(exfat);
}
}
struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs)
{
struct exfat *exfat;
exfat = (struct exfat *)calloc(1, sizeof(*exfat));
if (!exfat)
return NULL;
INIT_LIST_HEAD(&exfat->dir_list);
exfat->blk_dev = blk_dev;
exfat->bs = bs;
exfat->clus_count = le32_to_cpu(bs->bsx.clu_count);
exfat->clus_size = EXFAT_CLUSTER_SIZE(bs);
exfat->sect_size = EXFAT_SECTOR_SIZE(bs);
/* TODO: bitmap could be very large. */
exfat->alloc_bitmap = (char *)calloc(1,
EXFAT_BITMAP_SIZE(exfat->clus_count));
if (!exfat->alloc_bitmap) {
exfat_err("failed to allocate bitmap\n");
goto err;
}
exfat->ohead_bitmap =
calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
if (!exfat->ohead_bitmap) {
exfat_err("failed to allocate bitmap\n");
goto err;
}
exfat->disk_bitmap =
calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
if (!exfat->disk_bitmap) {
exfat_err("failed to allocate bitmap\n");
goto err;
}
exfat->zero_cluster = calloc(1, exfat->clus_size);
if (!exfat->zero_cluster) {
exfat_err("failed to allocate a zero-filled cluster buffer\n");
goto err;
}
exfat->start_clu = EXFAT_FIRST_CLUSTER;
return exfat;
err:
exfat_free_exfat(exfat);
return NULL;
}
struct buffer_desc *exfat_alloc_buffer(int count,
unsigned int clu_size, unsigned int sect_size)
{
struct buffer_desc *bd;
int i;
bd = (struct buffer_desc *)calloc(sizeof(*bd), count);
if (!bd)
return NULL;
for (i = 0; i < count; i++) {
bd[i].buffer = (char *)malloc(clu_size);
if (!bd[i].buffer)
goto err;
bd[i].dirty = (char *)calloc(clu_size / sect_size, 1);
if (!bd[i].dirty)
goto err;
}
return bd;
err:
exfat_free_buffer(bd, count);
return NULL;
}
void exfat_free_buffer(struct buffer_desc *bd, int count)
{
int i;
for (i = 0; i < count; i++) {
if (bd[i].buffer)
free(bd[i].buffer);
if (bd[i].dirty)
free(bd[i].dirty);
}
free(bd);
}
/*
* get references of ancestors that include @child until the count of
* ancesters is not larger than @count and the count of characters of
* their names is not larger than @max_char_len.
* return true if root is reached.
*/
static bool get_ancestors(struct exfat_inode *child,
struct exfat_inode **ancestors, int count,
int max_char_len,
int *ancestor_count)
{
struct exfat_inode *dir;
int name_len, char_len;
int root_depth, depth, i;
root_depth = 0;
char_len = 0;
max_char_len += 1;
dir = child;
while (dir) {
name_len = exfat_utf16_len(dir->name, NAME_BUFFER_SIZE);
if (char_len + name_len > max_char_len)
break;
/* include '/' */
char_len += name_len + 1;
root_depth++;
dir = dir->parent;
}
depth = MIN(root_depth, count);
for (dir = child, i = depth - 1; i >= 0; dir = dir->parent, i--)
ancestors[i] = dir;
*ancestor_count = depth;
return !dir;
}
int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child)
{
int depth, i;
int name_len;
__le16 *utf16_path;
static const __le16 utf16_slash = cpu_to_le16(0x002F);
static const __le16 utf16_null = cpu_to_le16(0x0000);
size_t in_size;
ctx->local_path[0] = '\0';
get_ancestors(child,
ctx->ancestors,
sizeof(ctx->ancestors) / sizeof(ctx->ancestors[0]),
PATH_MAX,
&depth);
utf16_path = ctx->utf16_path;
for (i = 0; i < depth; i++) {
name_len = exfat_utf16_len(ctx->ancestors[i]->name,
NAME_BUFFER_SIZE);
memcpy((char *)utf16_path, (char *)ctx->ancestors[i]->name,
name_len * 2);
utf16_path += name_len;
memcpy((char *)utf16_path, &utf16_slash, sizeof(utf16_slash));
utf16_path++;
}
if (depth > 1)
utf16_path--;
memcpy((char *)utf16_path, &utf16_null, sizeof(utf16_null));
utf16_path++;
in_size = (utf16_path - ctx->utf16_path) * sizeof(__le16);
return exfat_utf16_dec(ctx->utf16_path, in_size,
ctx->local_path, sizeof(ctx->local_path));
}
int exfat_resolve_path_parent(struct path_resolve_ctx *ctx,
struct exfat_inode *parent, struct exfat_inode *child)
{
int ret;
struct exfat_inode *old;
old = child->parent;
child->parent = parent;
ret = exfat_resolve_path(ctx, child);
child->parent = old;
return ret;
}
+286 -88
View File
@@ -19,64 +19,57 @@
#include "exfat_ondisk.h"
#include "libexfat.h"
#include "version.h"
#define BITS_PER_LONG (sizeof(long) * CHAR_BIT)
#ifdef WORDS_BIGENDIAN
#define BITOP_LE_SWIZZLE ((BITS_PER_LONG - 1) & ~0x7)
#else
#define BITOP_LE_SWIZZLE 0
#endif
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#include "exfat_fs.h"
#include "exfat_dir.h"
unsigned int print_level = EXFAT_INFO;
static inline void set_bit(int nr, void *addr)
void exfat_bitmap_set_range(struct exfat *exfat, char *bitmap,
clus_t start_clus, clus_t count)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
clus_t clus;
*p |= mask;
if (!exfat_heap_clus(exfat, start_clus) ||
!exfat_heap_clus(exfat, start_clus + count - 1))
return;
clus = start_clus;
while (clus < start_clus + count) {
exfat_bitmap_set(bitmap, clus);
clus++;
}
}
static inline void clear_bit(int nr, void *addr)
static int exfat_bitmap_find_bit(struct exfat *exfat, char *bmap,
clus_t start_clu, clus_t *next,
int bit)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
clus_t last_clu;
*p &= ~mask;
last_clu = le32_to_cpu(exfat->bs->bsx.clu_count) +
EXFAT_FIRST_CLUSTER;
while (start_clu < last_clu) {
if (!!exfat_bitmap_get(bmap, start_clu) == bit) {
*next = start_clu;
return 0;
}
start_clu++;
}
return 1;
}
static inline void set_bit_le(int nr, void *addr)
int exfat_bitmap_find_zero(struct exfat *exfat, char *bmap,
clus_t start_clu, clus_t *next)
{
set_bit(nr ^ BITOP_LE_SWIZZLE, addr);
return exfat_bitmap_find_bit(exfat, bmap,
start_clu, next, 0);
}
static inline void clear_bit_le(int nr, void *addr)
int exfat_bitmap_find_one(struct exfat *exfat, char *bmap,
clus_t start_clu, clus_t *next)
{
clear_bit(nr ^ BITOP_LE_SWIZZLE, addr);
}
void exfat_set_bit(struct exfat_blk_dev *bd, char *bitmap,
unsigned int clu)
{
int b;
b = clu & ((bd->sector_size << 3) - 1);
set_bit_le(b, bitmap);
}
void exfat_clear_bit(struct exfat_blk_dev *bd, char *bitmap,
unsigned int clu)
{
int b;
b = clu & ((bd->sector_size << 3) - 1);
clear_bit_le(b, bitmap);
return exfat_bitmap_find_bit(exfat, bmap,
start_clu, next, 1);
}
wchar_t exfat_bad_char(wchar_t w)
@@ -374,6 +367,12 @@ off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd)
return -1;
}
if (memcmp(bs->bpb.oem_name, "EXFAT ", 8) != 0) {
exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n");
free(bs);
return -1;
}
sector_size = 1 << bs->bsx.sect_size_bits;
cluster_size = (1 << bs->bsx.sect_per_clus_bits) * sector_size;
root_clu_off = le32_to_cpu(bs->bsx.clu_offset) * sector_size +
@@ -405,75 +404,98 @@ char *exfat_conv_volume_label(struct exfat_dentry *vol_entry)
return volume_label;
}
int exfat_show_volume_label(struct exfat_blk_dev *bd, off_t root_clu_off)
int exfat_read_volume_label(struct exfat *exfat)
{
struct exfat_dentry *vol_entry;
char *volume_label;
int nbytes;
struct exfat_dentry *dentry;
int err;
__le16 disk_label[VOLUME_LABEL_MAX_LEN];
struct exfat_lookup_filter filter = {
.in.type = EXFAT_VOLUME,
.in.filter = NULL,
};
vol_entry = malloc(sizeof(struct exfat_dentry));
if (!vol_entry) {
exfat_err("failed to allocate memory\n");
return -ENOMEM;
err = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
if (err)
return err;
dentry = filter.out.dentry_set;
if (dentry->vol_char_cnt == 0)
goto out;
if (dentry->vol_char_cnt > VOLUME_LABEL_MAX_LEN) {
exfat_err("too long label. %d\n", dentry->vol_char_cnt);
err = -EINVAL;
goto out;
}
nbytes = exfat_read(bd->dev_fd, vol_entry,
sizeof(struct exfat_dentry), root_clu_off);
if (nbytes != sizeof(struct exfat_dentry)) {
exfat_err("volume entry read failed: %d\n", errno);
free(vol_entry);
return -1;
memcpy(disk_label, dentry->vol_label, sizeof(disk_label));
if (exfat_utf16_dec(disk_label, dentry->vol_char_cnt*2,
exfat->volume_label, sizeof(exfat->volume_label)) < 0) {
exfat_err("failed to decode volume label\n");
err = -EINVAL;
goto out;
}
volume_label = exfat_conv_volume_label(vol_entry);
if (!volume_label) {
free(vol_entry);
return -EINVAL;
}
exfat_info("label: %s\n", volume_label);
free(volume_label);
free(vol_entry);
return 0;
exfat_info("label: %s\n", exfat->volume_label);
out:
free(filter.out.dentry_set);
return err;
}
int exfat_set_volume_label(struct exfat_blk_dev *bd,
char *label_input, off_t root_clu_off)
int exfat_set_volume_label(struct exfat *exfat, char *label_input)
{
struct exfat_dentry vol;
int nbytes;
struct exfat_dentry *pvol;
struct exfat_dentry_loc loc;
__u16 volume_label[VOLUME_LABEL_MAX_LEN];
int volume_label_len;
int volume_label_len, dcount, err;
struct exfat_lookup_filter filter = {
.in.type = EXFAT_VOLUME,
.in.filter = NULL,
};
err = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
if (!err) {
pvol = filter.out.dentry_set;
dcount = filter.out.dentry_count;
memset(pvol->vol_label, 0, sizeof(pvol->vol_label));
} else {
pvol = calloc(sizeof(struct exfat_dentry), 1);
if (!pvol)
return -ENOMEM;
dcount = 1;
pvol->type = EXFAT_VOLUME;
}
volume_label_len = exfat_utf16_enc(label_input,
volume_label, sizeof(volume_label));
if (volume_label_len < 0) {
exfat_err("failed to encode volume label\n");
free(pvol);
return -1;
}
vol.type = EXFAT_VOLUME;
memset(vol.vol_label, 0, sizeof(vol.vol_label));
memcpy(vol.vol_label, volume_label, volume_label_len);
vol.vol_char_cnt = volume_label_len/2;
nbytes = exfat_write(bd->dev_fd, &vol, sizeof(struct exfat_dentry),
root_clu_off);
if (nbytes != sizeof(struct exfat_dentry)) {
exfat_err("volume entry write failed: %d\n", errno);
return -1;
}
fsync(bd->dev_fd);
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;
loc.dev_offset = filter.out.dev_offset;
err = exfat_add_dentry_set(exfat, &loc, pvol, dcount, false);
exfat_info("new label: %s\n", label_input);
return 0;
free(pvol);
return err;
}
int exfat_read_sector(struct exfat_blk_dev *bd, void *buf, unsigned int sec_off)
{
int ret;
unsigned long long offset = sec_off * bd->sector_size;
unsigned long long offset =
(unsigned long long)sec_off * bd->sector_size;
ret = pread(bd->dev_fd, buf, bd->sector_size, offset);
if (ret < 0) {
@@ -487,7 +509,8 @@ int exfat_write_sector(struct exfat_blk_dev *bd, void *buf,
unsigned int sec_off)
{
int bytes;
unsigned long long offset = sec_off * bd->sector_size;
unsigned long long offset =
(unsigned long long)sec_off * bd->sector_size;
bytes = pwrite(bd->dev_fd, buf, bd->sector_size, offset);
if (bytes != (int)bd->sector_size) {
@@ -546,6 +569,12 @@ int exfat_show_volume_serial(int fd)
goto free_ppbr;
}
if (memcmp(ppbr->bpb.oem_name, "EXFAT ", 8) != 0) {
exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n");
ret = -1;
goto free_ppbr;
}
exfat_info("volume serial : 0x%x\n", ppbr->bsx.vol_serial);
free_ppbr:
@@ -614,6 +643,12 @@ int exfat_set_volume_serial(struct exfat_blk_dev *bd,
goto free_ppbr;
}
if (memcmp(ppbr->bpb.oem_name, "EXFAT ", 8) != 0) {
exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n");
ret = -1;
goto free_ppbr;
}
bd->sector_size = 1 << ppbr->bsx.sect_size_bits;
ppbr->bsx.vol_serial = ui->volume_serial;
@@ -656,3 +691,166 @@ unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd,
return clu_off_sectnr * bd->sector_size +
(clu - EXFAT_RESERVED_CLUSTERS) * bd->cluster_size;
}
int exfat_get_next_clus(struct exfat *exfat, clus_t clus, clus_t *next)
{
off_t offset;
*next = EXFAT_EOF_CLUSTER;
if (!exfat_heap_clus(exfat, clus))
return -EINVAL;
offset = (off_t)le32_to_cpu(exfat->bs->bsx.fat_offset) <<
exfat->bs->bsx.sect_size_bits;
offset += sizeof(clus_t) * clus;
if (exfat_read(exfat->blk_dev->dev_fd, next, sizeof(*next), offset)
!= sizeof(*next))
return -EIO;
*next = le32_to_cpu(*next);
return 0;
}
int exfat_get_inode_next_clus(struct exfat *exfat, struct exfat_inode *node,
clus_t clus, clus_t *next)
{
*next = EXFAT_EOF_CLUSTER;
if (node->is_contiguous) {
if (!exfat_heap_clus(exfat, clus))
return -EINVAL;
*next = clus + 1;
return 0;
}
return exfat_get_next_clus(exfat, clus, next);
}
int exfat_set_fat(struct exfat *exfat, clus_t clus, clus_t next_clus)
{
off_t offset;
offset = le32_to_cpu(exfat->bs->bsx.fat_offset) <<
exfat->bs->bsx.sect_size_bits;
offset += sizeof(clus_t) * clus;
if (exfat_write(exfat->blk_dev->dev_fd, &next_clus, sizeof(next_clus),
offset) != sizeof(next_clus))
return -EIO;
return 0;
}
off_t exfat_s2o(struct exfat *exfat, off_t sect)
{
return sect << exfat->bs->bsx.sect_size_bits;
}
off_t exfat_c2o(struct exfat *exfat, unsigned int clus)
{
if (clus < EXFAT_FIRST_CLUSTER)
return ~0L;
return exfat_s2o(exfat, le32_to_cpu(exfat->bs->bsx.clu_offset) +
((off_t)(clus - EXFAT_FIRST_CLUSTER) <<
exfat->bs->bsx.sect_per_clus_bits));
}
int exfat_o2c(struct exfat *exfat, off_t device_offset,
unsigned int *clu, unsigned int *offset)
{
off_t heap_offset;
heap_offset = exfat_s2o(exfat, le32_to_cpu(exfat->bs->bsx.clu_offset));
if (device_offset < heap_offset)
return -ERANGE;
*clu = (unsigned int)((device_offset - heap_offset) /
exfat->clus_size) + EXFAT_FIRST_CLUSTER;
if (!exfat_heap_clus(exfat, *clu))
return -ERANGE;
*offset = (device_offset - heap_offset) % exfat->clus_size;
return 0;
}
bool exfat_heap_clus(struct exfat *exfat, clus_t clus)
{
return clus >= EXFAT_FIRST_CLUSTER &&
(clus - EXFAT_FIRST_CLUSTER) < exfat->clus_count;
}
int exfat_root_clus_count(struct exfat *exfat)
{
struct exfat_inode *node = exfat->root;
clus_t clus, next;
int clus_count = 0;
if (!exfat_heap_clus(exfat, node->first_clus))
return -EIO;
clus = node->first_clus;
do {
if (exfat_bitmap_get(exfat->alloc_bitmap, clus))
return -EINVAL;
exfat_bitmap_set(exfat->alloc_bitmap, clus);
if (exfat_get_inode_next_clus(exfat, node, clus, &next)) {
exfat_err("ERROR: failed to read the fat entry of root");
return -EIO;
}
if (next != EXFAT_EOF_CLUSTER && !exfat_heap_clus(exfat, next))
return -EINVAL;
clus = next;
clus_count++;
} while (clus != EXFAT_EOF_CLUSTER);
node->size = clus_count * exfat->clus_size;
return 0;
}
int read_boot_sect(struct exfat_blk_dev *bdev, struct pbr **bs)
{
struct pbr *pbr;
int err = 0;
unsigned int sect_size, clu_size;
pbr = malloc(sizeof(struct pbr));
if (exfat_read(bdev->dev_fd, pbr, sizeof(*pbr), 0) !=
(ssize_t)sizeof(*pbr)) {
exfat_err("failed to read a boot sector\n");
err = -EIO;
goto err;
}
err = -EINVAL;
if (memcmp(pbr->bpb.oem_name, "EXFAT ", 8) != 0) {
exfat_err("failed to find exfat file system\n");
goto err;
}
sect_size = 1 << pbr->bsx.sect_size_bits;
clu_size = 1 << (pbr->bsx.sect_size_bits +
pbr->bsx.sect_per_clus_bits);
if (sect_size < 512 || sect_size > 4 * KB) {
exfat_err("too small or big sector size: %d\n",
sect_size);
goto err;
}
if (clu_size < sect_size || clu_size > 32 * MB) {
exfat_err("too small or big cluster size: %d\n",
clu_size);
goto err;
}
*bs = pbr;
return 0;
err:
free(pbr);
return err;
}
+36
View File
@@ -0,0 +1,36 @@
.TH exfat2img 8
.SH NAME
exfat2img \- dump metadata of an exFAT filesystem
.SH SYNOPSIS
.B exfat2img
[
.B \-o \fIpath\fB\
] [
.B \-V
]
.I device
.br
.B exfat2img \-V
.SH DESCRIPTION
.B exfat2img
dump metadata of exFAT filesystems for debugging. \fBexfat2img\fP dump boot sector, File Allcation Table, Bitmap and all metadata which can reach from root directory.
.SH OPTIONS
.TP
.BI \-o\ \-\-output
Specify output result file. If filesystem to which output file is written does not support sparse file, you should use '-' in place of \fIpath\fP.
Because a dump image generated from stdout has a special format, when restoring a partition from it, exfat2img should be used. See Examples.
.TP
.B \-V
Prints the version number and exits.
.SH EXAMPLES
.PP
Dump metadata into a sparse file.
.EX
.RB "$" " exfat2img -o sda1.dump /dev/sda1
Dump metadata into standard out and restore a partition
.EX
.RB "$" " exfat2img -o - /dev/sda1 | bzip2 > sda1.dump.bz2"
.RB "$" " bzip2 -dc sda1.dump.bz2 | exfat2img -o /dev/sdb1 -"
+6 -8
View File
@@ -5,9 +5,8 @@ exfatlabel \- Get or Set volume label or volume serial of an exFAT filesystem
.B exfatlabel
[
.B \-i
.I volume-label
] [
.B \-v
.B \-V
]
.I device
[
@@ -21,15 +20,14 @@ Print or set volume label of an existing exFAT filesystem.
If there is a
.I label_string
in argument of exfatlabel, It will be written to volume label
field on given device. If not, exfatlabel will just print out
after reading volume label field from given device. If -i or
--volume-serial is given, It can be switched to volume serial
mode.
in the argument of exfatlabel, it will be written to the volume
label field on a given device. If not, exfatlabel will just print
it after reading the volume label field from the given device. If -i
or --volume-serial is given, it will switch to volume serial mode.
.PP
.SH OPTIONS
.TP
.BI \-i
.BI \-i\ \-\-volume-serial
Switch to volume serial mode.
.TP
.B \-V
+38 -1
View File
@@ -14,6 +14,8 @@ fsck.exfat \- check an exFAT filesystem
] [
.B \-y
] [
.B \-b
] [
.B \-v
]
.I device
@@ -22,7 +24,29 @@ fsck.exfat \- check an exFAT filesystem
.SH DESCRIPTION
.B fsck.exfat
checks an exFAT filesystem and repairs the filesystem
depending on the options passed.
depending on the options passed. The following corruptions can be repaired, and see the option, '-s'.
.IP -
Boot sector has invalid parameters. If backup boot sector is valid, replace the boot sector with it.
.IP -
Cluster is marked as free but belonged to a file, or vise versa. The bitmap for the cluster is marked properly.
.IP -
File size is abnormally large considering the count of clusters. The size is changed.
.IP -
File size is abnormally small considering the count of clusters. The remaining clusters are deleted.
.IP -
File's cluster chain has an invalid cluster number. The number are changed to EOF, and the file size is also changed.
.IP -
File's cluster chain contains a loop. The loop is broken.
.IP -
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.
.IP -
Bad hash value of a file name. The hash value is changed properly.
.IP -
Fields of directory entry set have invalid values. Directory entry set is deleted.
.PP
.SH OPTIONS
.TP
@@ -38,6 +62,9 @@ Repair the filesystem without user interaction if it can be done safely.
.BI \-r
Repair the filesystem interactively.
.TP
.BI \-s
Create files in /LOST+FOUND for orphan clusters. These files have clusters allocated but not belonged to any files when reparing the filesystem. clusters unused and contiguous in bitmap are allocated to the same file.
.TP
.BI \-v
Prints verbose debugging information while checking the exFAT filesystem.
.TP
@@ -46,6 +73,16 @@ Prints the version number and exits.
.TP
.B \-y
Repair the filesystem answering yes to all questions.
.TP
.B \-b
Try to repair the filesystem even if the exFAT filesystem is not found.
.SH EXAMPLES
.PP
repair a corrupted device and create files in /LOST+FOUND, which have clusters allocated but not belonged to any files when reparing the device.
.EX
.RB "$" " fsck.exfat -p -s /dev/sda1"
.SH SEE ALSO
.BR fsck (8),
.BR fstab (5),
+5 -4
View File
@@ -314,12 +314,13 @@ static int exfat_create_bitmap(struct exfat_blk_dev *bd)
char *bitmap;
unsigned int i, nbytes;
bitmap = calloc(finfo.bitmap_byte_len, sizeof(*bitmap));
bitmap = calloc(round_up(finfo.bitmap_byte_len, sizeof(bitmap_t)),
sizeof(*bitmap));
if (!bitmap)
return -1;
for (i = 0; i < finfo.used_clu_cnt - EXFAT_FIRST_CLUSTER; i++)
exfat_set_bit(bd, bitmap, i);
for (i = EXFAT_FIRST_CLUSTER; i < finfo.used_clu_cnt; i++)
exfat_bitmap_set(bitmap, i);
nbytes = pwrite(bd->dev_fd, bitmap, finfo.bitmap_byte_len, finfo.bitmap_byte_off);
if (nbytes != finfo.bitmap_byte_len) {
@@ -336,7 +337,7 @@ 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];
struct exfat_dentry ed[3] = {0};
int dentries_len = sizeof(struct exfat_dentry) * 3;
int nbytes;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
#OPTS: -s
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
#OPTS: -s
Binary file not shown.
Binary file not shown.
+24 -20
View File
@@ -1,15 +1,21 @@
#!/usr/bin/env bash
TESTCASE_DIR=$1
NEED_LOOPDEV=$2
IMAGE_FILE=exfat.img
FSCK_PROG=../build/sbin/fsck.exfat
FSCK_OPTS=-y
FSCK_PROG=fsck.exfat
FSCK_PROG_2=fsck.exfat
FSCK_OPTS="-y -s"
PASS_COUNT=0
cleanup() {
echo ""
echo "Passed ${PASS_COUNT} of ${TEST_COUNT}"
exit
if [ ${PASS_COUNT} -ne ${TEST_COUNT} ]; then
exit 1
else
exit 0
fi
}
if [ $# -eq 0 ]; then
@@ -31,43 +37,41 @@ for TESTCASE_DIR in $TESTCASE_DIRS; do
# Set up image file as loop device
tar -C . -xf "${TESTCASE_DIR}/${IMAGE_FILE}.tar.xz"
DEV_FILE=$(losetup -f "${IMAGE_FILE}" --show)
if [ "$NEED_LOOPDEV" ]; then
DEV_FILE=$(losetup -f "${IMAGE_FILE}" --show)
else
DEV_FILE=$IMAGE_FILE
fi
# Run fsck for repair
$FSCK_PROG $FSCK_OPTS "$DEV_FILE"
if [ $? -ne 1 ]; then
if [ $? -ne 1 ] && [ $? -ne 0 ]; then
echo ""
echo "Failed to repair ${TESTCASE_DIR}"
losetup -d "${DEV_FILE}"
if [ "$NEED_LOOPDEV" ]; then
losetup -d "${DEV_FILE}"
fi
cleanup
fi
echo ""
# Run fsck again
$FSCK_PROG -n "$DEV_FILE"
$FSCK_PROG_2 "$DEV_FILE"
if [ $? -ne 0 ]; then
echo ""
echo "Failed, corrupted ${TESTCASE_DIR}"
losetup -d "${DEV_FILE}"
cleanup
fi
if [ -e "${TESTCASE_DIR}/exfat.img.expected.xz" ]; then
EXPECTED_FILE=${IMAGE_FILE}.expected
unxz -cfk "${TESTCASE_DIR}/${EXPECTED_FILE}.xz" > "${EXPECTED_FILE}"
diff <(xxd "${IMAGE_FILE}") <(xxd "${EXPECTED_FILE}")
if [ $? -ne 0 ]; then
echo ""
echo "Failed ${TESTCASE_DIR}"
if [ "$NEED_LOOPDEV" ]; then
losetup -d "${DEV_FILE}"
cleanup
fi
cleanup
fi
echo ""
echo "Passed ${TESTCASE_DIR}"
PASS_COUNT=$((PASS_COUNT + 1))
losetup -d "${DEV_FILE}"
if [ "$NEED_LOOPDEV" ]; then
losetup -d "${DEV_FILE}"
fi
done
cleanup
+30 -5
View File
@@ -13,6 +13,7 @@
#include "exfat_ondisk.h"
#include "libexfat.h"
#include "exfat_fs.h"
static void usage(void)
{
@@ -49,7 +50,8 @@ int main(int argc, char *argv[])
bool version_only = false;
int flags = 0;
char label_input[VOLUME_LABEL_BUFFER_SIZE];
off_t root_clu_off;
struct exfat *exfat = NULL;
struct pbr *bs;
init_user_input(&ui);
@@ -109,16 +111,39 @@ int main(int argc, char *argv[])
goto close_fd_out;
}
root_clu_off = exfat_get_root_entry_offset(&bd);
if (root_clu_off < 0)
ret = read_boot_sect(&bd, &bs);
if (ret)
goto close_fd_out;
exfat = exfat_alloc_exfat(&bd, bs);
if (!exfat) {
free(bs);
ret = -ENOMEM;
goto close_fd_out;
}
exfat->root = exfat_alloc_inode(ATTR_SUBDIR);
if (!exfat->root) {
ret = -ENOMEM;
goto close_fd_out;
}
exfat->root->first_clus = le32_to_cpu(exfat->bs->bsx.root_cluster);
if (exfat_root_clus_count(exfat)) {
exfat_err("failed to follow the cluster chain of root\n");
exfat_free_inode(exfat->root);
ret = -EINVAL;
goto close_fd_out;
}
if (flags == EXFAT_GET_VOLUME_LABEL)
ret = exfat_show_volume_label(&bd, root_clu_off);
ret = exfat_read_volume_label(exfat);
else if (flags == EXFAT_SET_VOLUME_LABEL)
ret = exfat_set_volume_label(&bd, label_input, root_clu_off);
ret = exfat_set_volume_label(exfat, label_input);
close_fd_out:
close(bd.dev_fd);
if (exfat)
exfat_free_exfat(exfat);
out:
return ret;
}