aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHyunchul Lee <hyc.lee@gmail.com>2021-03-29 09:53:47 +0900
committerHyunchul Lee <hyc.lee@gmail.com>2022-08-26 05:57:05 +0900
commit139e7a93543372e28712dbe8a315a5dd1c34c82b (patch)
tree9620367769e45a63ff42b6926d5db955d3b96602
parentcba94136deb98eae024587220de1fa7ff32ef311 (diff)
downloadexfatprogs-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.h24
-rw-r--r--lib/exfat_dir.c125
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;
}