aboutsummaryrefslogtreecommitdiff
path: root/lib/dir.c
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-03 15:49:47 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-03 15:49:47 +0000
commit3cc3559fdf6f774cc522953484f91075352a84c8 (patch)
tree25a37c478eabdeaabeb7e5c38e6558dc272cc362 /lib/dir.c
parent5ff981c61150de1c5402b8db27252fa35b81167d (diff)
parent3888d132f2cfbf66afa3149706c77a48535c80fc (diff)
downloaderofs-utils-3cc3559fdf6f774cc522953484f91075352a84c8.tar.gz
Change-Id: I6cec967dc0f21b6fe4df1fa5bf2658478c23f1d7
Diffstat (limited to 'lib/dir.c')
-rw-r--r--lib/dir.c109
1 files changed, 105 insertions, 4 deletions
diff --git a/lib/dir.c b/lib/dir.c
index 46805b4..e6b9283 100644
--- a/lib/dir.c
+++ b/lib/dir.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
-#include "erofs/print.h"
-#include "erofs/dir.h"
#include <stdlib.h>
#include <sys/stat.h>
+#include "erofs/print.h"
+#include "erofs/dir.h"
static int traverse_dirents(struct erofs_dir_context *ctx,
void *dentry_blk, unsigned int lblk,
@@ -127,7 +127,7 @@ int erofs_iterate_dir(struct erofs_dir_context *ctx, bool fsck)
erofs_off_t pos;
char buf[EROFS_BLKSIZ];
- if ((dir->i_mode & S_IFMT) != S_IFDIR)
+ if (!S_ISDIR(dir->i_mode))
return -ENOTDIR;
ctx->flags &= ~EROFS_READDIR_ALL_SPECIAL_FOUND;
@@ -148,7 +148,7 @@ int erofs_iterate_dir(struct erofs_dir_context *ctx, bool fsck)
nameoff = le16_to_cpu(de->nameoff);
if (nameoff < sizeof(struct erofs_dirent) ||
- nameoff >= PAGE_SIZE) {
+ nameoff >= EROFS_BLKSIZ) {
erofs_err("invalid de[0].nameoff %u @ nid %llu, lblk %u",
nameoff, dir->nid | 0ULL, lblk);
return -EFSCORRUPTED;
@@ -167,3 +167,104 @@ int erofs_iterate_dir(struct erofs_dir_context *ctx, bool fsck)
}
return err;
}
+
+#define EROFS_PATHNAME_FOUND 1
+
+struct erofs_get_pathname_context {
+ struct erofs_dir_context ctx;
+ erofs_nid_t target_nid;
+ char *buf;
+ size_t size;
+ size_t pos;
+};
+
+static int erofs_get_pathname_iter(struct erofs_dir_context *ctx)
+{
+ int ret;
+ struct erofs_get_pathname_context *pathctx = (void *)ctx;
+ const char *dname = ctx->dname;
+ size_t len = ctx->de_namelen;
+ size_t pos = pathctx->pos;
+
+ if (ctx->dot_dotdot)
+ return 0;
+
+ if (ctx->de_nid == pathctx->target_nid) {
+ if (pos + len + 2 > pathctx->size) {
+ erofs_err("get_pathname buffer not large enough: len %zd, size %zd",
+ pos + len + 2, pathctx->size);
+ return -ERANGE;
+ }
+
+ pathctx->buf[pos++] = '/';
+ strncpy(pathctx->buf + pos, dname, len);
+ pathctx->buf[pos + len] = '\0';
+ return EROFS_PATHNAME_FOUND;
+ }
+
+ if (ctx->de_ftype == EROFS_FT_DIR || ctx->de_ftype == EROFS_FT_UNKNOWN) {
+ struct erofs_inode dir = { .nid = ctx->de_nid };
+
+ ret = erofs_read_inode_from_disk(&dir);
+ if (ret) {
+ erofs_err("read inode failed @ nid %llu", dir.nid | 0ULL);
+ return ret;
+ }
+
+ if (S_ISDIR(dir.i_mode)) {
+ ctx->dir = &dir;
+ pathctx->pos = pos + len + 1;
+ ret = erofs_iterate_dir(ctx, false);
+ pathctx->pos = pos;
+ if (ret == EROFS_PATHNAME_FOUND) {
+ pathctx->buf[pos++] = '/';
+ strncpy(pathctx->buf + pos, dname, len);
+ }
+ return ret;
+ } else if (ctx->de_ftype == EROFS_FT_DIR) {
+ erofs_err("i_mode and file_type are inconsistent @ nid %llu",
+ dir.nid | 0ULL);
+ }
+ }
+ return 0;
+}
+
+int erofs_get_pathname(erofs_nid_t nid, char *buf, size_t size)
+{
+ int ret;
+ struct erofs_inode root = { .nid = sbi.root_nid };
+ struct erofs_get_pathname_context pathctx = {
+ .ctx.flags = 0,
+ .ctx.dir = &root,
+ .ctx.cb = erofs_get_pathname_iter,
+ .target_nid = nid,
+ .buf = buf,
+ .size = size,
+ .pos = 0,
+ };
+
+ if (nid == root.nid) {
+ if (size < 2) {
+ erofs_err("get_pathname buffer not large enough: len 2, size %zd",
+ size);
+ return -ERANGE;
+ }
+
+ buf[0] = '/';
+ buf[1] = '\0';
+ return 0;
+ }
+
+ ret = erofs_read_inode_from_disk(&root);
+ if (ret) {
+ erofs_err("read inode failed @ nid %llu", root.nid | 0ULL);
+ return ret;
+ }
+
+ ret = erofs_iterate_dir(&pathctx.ctx, false);
+ if (ret == EROFS_PATHNAME_FOUND)
+ return 0;
+ if (!ret)
+ return -ENOENT;
+ return ret;
+}