diff options
author | Hyunchul Lee <hyc.lee@gmail.com> | 2021-03-29 09:53:47 +0900 |
---|---|---|
committer | Hyunchul Lee <hyc.lee@gmail.com> | 2022-08-26 05:57:05 +0900 |
commit | 139e7a93543372e28712dbe8a315a5dd1c34c82b (patch) | |
tree | 9620367769e45a63ff42b6926d5db955d3b96602 | |
parent | cba94136deb98eae024587220de1fa7ff32ef311 (diff) | |
download | exfatprogs-139e7a93543372e28712dbe8a315a5dd1c34c82b.tar.gz |
fsck: add dentry set lookup function
The specified dentry set can be found
using exfat_lookup_dentry_set() with
parent.
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
-rw-r--r-- | include/exfat_dir.h | 24 | ||||
-rw-r--r-- | lib/exfat_dir.c | 125 |
2 files changed, 140 insertions, 9 deletions
diff --git a/include/exfat_dir.h b/include/exfat_dir.h index ce010d2..746d818 100644 --- a/include/exfat_dir.h +++ b/include/exfat_dir.h @@ -24,6 +24,26 @@ struct exfat_de_iter { int max_skip_dentries; }; +struct exfat_lookup_filter { + struct { + uint8_t type; + /* return 0 if matched, return 1 if not matched, + * otherwise return errno + */ + int (*filter)(struct exfat_de_iter *iter, + void *param, int *dentry_count); + void *param; + } in; + struct { + struct exfat_dentry *dentry_set; + int dentry_count; + /* device offset where the dentry_set locates, or + * the empty slot locates or EOF if not found. + */ + off_t dentry_d_offset; + } out; +}; + int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, struct exfat_inode *dir, struct buffer_desc *bd); int exfat_de_iter_get(struct exfat_de_iter *iter, @@ -32,5 +52,9 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter, int ith, struct exfat_dentry **dentry); int exfat_de_iter_flush(struct exfat_de_iter *iter); int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries); +off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter); + +int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, + struct exfat_lookup_filter *filter); #endif diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 6257842..5742e2f 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -6,12 +6,24 @@ #include <stdio.h> #include <errno.h> #include <fcntl.h> +#include <string.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; @@ -216,8 +228,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, iter->ra_partial_size = exfat->clus_size / 4; iter->ra_partial_size = MIN(iter->ra_partial_size, 8 * KB); - if (!iter->buffer_desc) - iter->buffer_desc = bd; + iter->buffer_desc = bd; if (iter->parent->size == 0) return EOF; @@ -296,13 +307,9 @@ int exfat_de_iter_flush(struct exfat_de_iter *iter) return 0; } -/* - * @skip_dentries must be the largest @ith + 1 of exfat_de_iter_get - * since the last call of exfat_de_iter_advance - */ int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries) { - if (skip_dentries != iter->max_skip_dentries) + if (skip_dentries > iter->max_skip_dentries) return -EINVAL; iter->max_skip_dentries = 0; @@ -311,7 +318,107 @@ int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries) return 0; } -off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter) +off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter) { - return iter->de_file_offset; + struct buffer_desc *bd; + unsigned int block; + + if ((uint64_t)iter->de_file_offset >= iter->parent->size) + return EOF; + + block = iter->de_file_offset / iter->read_size; + bd = &iter->buffer_desc[block & 0x01]; + return exfat_c2o(iter->exfat, bd->p_clus) + bd->offset + + iter->de_file_offset % iter->read_size; +} + +/* + * try to find the dentry set matched with @filter. this function + * doesn't verify the dentry set. + * + * if found, return 0. if not found, return EOF. otherwise return errno. + */ +int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, + struct exfat_lookup_filter *filter) +{ + struct buffer_desc *bd = NULL; + struct exfat_dentry *dentry = NULL; + off_t free_offset = 0; + struct exfat_de_iter de_iter; + int dentry_count; + int retval; + bool last_is_free = false; + + bd = exfat_alloc_buffer(2, exfat->clus_size, exfat->sect_size); + if (!bd) + return -ENOMEM; + + retval = exfat_de_iter_init(&de_iter, exfat, parent, bd); + if (retval == EOF || retval) + goto out; + + filter->out.dentry_set = NULL; + while (1) { + retval = exfat_de_iter_get(&de_iter, 0, &dentry); + if (retval == EOF) { + break; + } else if (retval) { + fsck_err(parent->parent, parent, + "failed to get a dentry. %d\n", retval); + goto out; + } + + dentry_count = 1; + if (dentry->type == filter->in.type) { + retval = 0; + if (filter->in.filter) + retval = filter->in.filter(&de_iter, + filter->in.param, + &dentry_count); + + if (retval == 0) { + struct exfat_dentry *d; + int i; + + filter->out.dentry_set = calloc(dentry_count, + sizeof(struct exfat_dentry)); + if (!filter->out.dentry_set) { + retval = -ENOMEM; + goto out; + } + for (i = 0; i < dentry_count; i++) { + exfat_de_iter_get(&de_iter, i, &d); + memcpy(filter->out.dentry_set + i, d, + sizeof(struct exfat_dentry)); + } + filter->out.dentry_count = dentry_count; + goto out; + } else if (retval < 0) { + goto out; + } + last_is_free = false; + } else if ((dentry->type == EXFAT_LAST || + IS_EXFAT_DELETED(dentry->type))) { + if (!last_is_free) { + free_offset = exfat_de_iter_device_offset(&de_iter); + last_is_free = true; + } + } else { + last_is_free = false; + } + + exfat_de_iter_advance(&de_iter, dentry_count); + } + +out: + if (retval == 0) + filter->out.dentry_d_offset = + exfat_de_iter_device_offset(&de_iter); + else if (retval == EOF && last_is_free) + filter->out.dentry_d_offset = free_offset; + else + filter->out.dentry_d_offset = EOF; + if (bd) + exfat_free_buffer(bd, 2); + return retval; } |