exfat_de_iter_device_offset(iter)); \
})
-static int check_clus_chain(struct exfat_de_iter *de_iter,
- struct exfat_inode *node)
+static int check_clus_chain(struct exfat_de_iter *de_iter, int stream_idx,
+ struct exfat_inode *node)
{
struct exfat *exfat = de_iter->exfat;
struct exfat_dentry *stream_de;
- clus_t clus, prev, next, new_clus;
+ clus_t clus, prev, next;
uint64_t count, max_count;
- int err;
clus = node->first_clus;
prev = EXFAT_EOF_CLUSTER;
count = 0;
max_count = DIV_ROUND_UP(node->size, exfat->clus_size);
- if (node->size == 0 && node->first_clus == EXFAT_FREE_CLUSTER) {
- /* locate a cluster for the empty dir if the dir starts with EXFAT_FREE_CLUSTER */
- if (node->attr & ATTR_SUBDIR) {
- if (repair_file_ask(de_iter, node,
- ER_DE_FIRST_CLUS,
- "size %#" PRIx64 ", but the first cluster %#x",
- node->size, node->first_clus))
- goto allocate_cluster;
- return -EINVAL;
- }
+ if (node->size == 0 && node->first_clus == EXFAT_FREE_CLUSTER)
return 0;
- }
+
/* the first cluster is wrong */
if ((node->size == 0 && node->first_clus != EXFAT_FREE_CLUSTER) ||
(node->size > 0 && !exfat_heap_clus(exfat, node->first_clus))) {
}
return 0;
-allocate_cluster:
- exfat_de_iter_get_dirty(de_iter, 1, &stream_de);
- err = exfat_find_free_cluster(exfat, exfat->start_clu, &new_clus);
- if (err) {
- exfat->start_clu = EXFAT_FIRST_CLUSTER;
- exfat_err("failed to find a free cluster\n");
- return -ENOSPC;
- }
- exfat->start_clu = new_clus;
-
- if (exfat_set_fat(exfat, new_clus, 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_clus)) !=
- (ssize_t)exfat->clus_size) {
- exfat_err("failed to fill new cluster with zeroes\n");
- return -EIO;
- }
-
- /* modify the number of cluster form 0 to 1 */
- count = 1;
- stream_de->stream_start_clu = cpu_to_le32(new_clus);
- stream_de->stream_size = cpu_to_le64(count * exfat->clus_size);
- stream_de->stream_valid_size = cpu_to_le64(count * exfat->clus_size);
- stream_de->dentry.stream.flags |= EXFAT_SF_CONTIGUOUS;
- node->first_clus = new_clus;
- node->size = count * exfat->clus_size;
- node->is_contiguous = true;
- exfat_bitmap_set(exfat->alloc_bitmap, new_clus);
- return 1;
truncate_file:
node->size = count * exfat->clus_size;
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);
{
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;
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;
}
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;
}
/*
uint16_t checksum;
bool valid = true;
- ret = check_clus_chain(iter, node);
+ ret = check_clus_chain(iter, 1, node);
if (ret < 0)
return ret;
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);
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;
+ char filename[PATH_MAX + 1] = {0};
+
+ 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;
+
+ ret = exfat_utf16_dec(inode->name, NAME_BUFFER_SIZE, filename,
+ PATH_MAX);
+ if (ret < 0) {
+ exfat_err("failed to decode filename\n");
+ return ret;
+ }
+
+ return exfat_repair_rename_ask(&exfat_fsck, iter, filename,
+ 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);
"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;
}
"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;
-}
-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;
- }
- }
+ if (BITMAP_GET(iter->name_hash_bitmap, hash)) {
+ ret = handle_duplicated_filename(iter, inode);
+ } else
+ BITMAP_SET(iter->name_hash_bitmap, hash);
- 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;
+ return ret;
}
const __le16 MSDOS_DOT[ENTRY_NAME_MAX] = {cpu_to_le16(46), 0, };
int strm_name_len)
{
char *filename;
- char error_msg[150];
- int num;
if (!memcmp(dentry->name_unicode, MSDOS_DOT, 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 name_len, 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;
-
- memcpy(dentry->name_unicode, utf16_name, ENTRY_NAME_MAX * 2);
- name_len = exfat_utf16_len(utf16_name, ENTRY_NAME_MAX * 2);
- hash = exfat_calc_name_hash(iter->exfat, utf16_name, (int)name_len);
- exfat_de_iter_get_dirty(iter, 1, &stream_de);
- stream_de->stream_name_len = (__u8)name_len;
- stream_de->stream_name_hash = cpu_to_le16(hash);
- }
-
- 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,
{
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);
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;
if (!node)
return -ENOMEM;
- for (i = 2; i <= file_de->file_num_ext; 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 +
}
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) {
}
}
+ 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);
}
}
+ 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;
{
struct exfat_lookup_filter filter = {
.in.type = EXFAT_BITMAP,
+ .in.dentry_count = 0,
.in.filter = NULL,
.in.param = NULL,
};
{
struct exfat_lookup_filter filter = {
.in.type = EXFAT_UPCASE,
+ .in.dentry_count = 0,
.in.filter = NULL,
.in.param = NULL,
};
goto out;
}
- upcase = (__le16 *)malloc(size);
+ upcase = malloc(size);
if (!upcase) {
exfat_err("failed to allocate upcase table\n");
retval = -ENOMEM;
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;
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) {
case EXFAT_VOLUME:
case EXFAT_BITMAP:
case EXFAT_UPCASE:
+ case EXFAT_GUID:
if (dir == exfat->root)
break;
/* fallthrough */
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);
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)) {
}
out:
exfat_free_dir_list(exfat);
+ free(fsck->name_hash_bitmap);
return ret;
}
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) {
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");
}
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.
*/
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;
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)
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);