/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * dir_tree_iterator.c * * Copyright (C) 2023 David Oberhollenzer */ #include "config.h" #include "util/dir_tree_iterator.h" #include "util/util.h" #include "sqfs/error.h" #include #include typedef struct dir_stack_t { struct dir_stack_t *next; dir_iterator_t *dir; char name[]; } dir_stack_t; typedef struct { dir_iterator_t base; int state; dir_stack_t *top; } dir_tree_iterator_t; static void pop(dir_tree_iterator_t *it) { if (it->top != NULL) { dir_stack_t *ent = it->top; it->top = it->top->next; sqfs_drop(ent->dir); free(ent); } } static int push(dir_tree_iterator_t *it, const char *name, dir_iterator_t *dir) { dir_stack_t *ent = alloc_flex(sizeof(*ent), 1, strlen(name) + 1); if (ent == NULL) return SQFS_ERROR_ALLOC; strcpy(ent->name, name); ent->dir = sqfs_grab(dir); ent->next = it->top; it->top = ent; return 0; } /*****************************************************************************/ static void destroy(sqfs_object_t *obj) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)obj; while (it->top != NULL) pop(it); free(it); } static int next(dir_iterator_t *base, dir_entry_t **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; dir_iterator_t *sub = NULL; dir_entry_t *ent = NULL; dir_stack_t *sit = NULL; size_t plen = 0; int ret; *out = NULL; if (it->state != 0) return it->state; for (;;) { if (it->top == NULL) { ret = 1; goto fail; } ret = it->top->dir->next(it->top->dir, &ent); if (ret < 0) goto fail; if (ret > 0) { pop(it); continue; } if (!strcmp(ent->name, ".") || !strcmp(ent->name, "..")) { free(ent); ent = NULL; continue; } break; } for (sit = it->top; sit != NULL; sit = sit->next) { size_t len = strlen(sit->name); if (len > 0) plen += len + 1; } if (plen > 0) { size_t slen = strlen(ent->name) + 1; void *new = realloc(ent, sizeof(*ent) + plen + slen); char *dst; if (new == NULL) { ret = SQFS_ERROR_ALLOC; goto fail; } ent = new; memmove(ent->name + plen, ent->name, slen); dst = ent->name + plen; for (sit = it->top; sit != NULL; sit = sit->next) { size_t len = strlen(sit->name); if (len > 0) { *(--dst) = '/'; dst -= len; memcpy(dst, sit->name, len); } } } if (S_ISDIR(ent->mode)) { ret = it->top->dir->open_subdir(it->top->dir, &sub); if (ret != 0) goto fail; ret = push(it, ent->name + plen, sub); sqfs_drop(sub); if (ret != 0) goto fail; } *out = ent; return it->state; fail: free(ent); it->state = ret; return it->state; } static int read_link(dir_iterator_t *base, char **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; if (it->top == NULL) { *out = NULL; return SQFS_ERROR_NO_ENTRY; } return it->top->dir->read_link(it->top->dir, out); } static int open_subdir(dir_iterator_t *base, dir_iterator_t **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; if (it->top == NULL) { *out = NULL; return SQFS_ERROR_NO_ENTRY; } return it->top->dir->open_subdir(it->top->dir, out); } dir_iterator_t *dir_tree_iterator_create(const char *path) { dir_tree_iterator_t *it = calloc(1, sizeof(*it)); dir_iterator_t *dir; int ret; if (it == NULL) { perror(path); return NULL; } dir = dir_iterator_create(path); if (dir == NULL) goto fail; ret = push(it, "", dir); dir = sqfs_drop(dir); if (ret != 0) { fprintf(stderr, "%s: out of memory\n", path); goto fail; } sqfs_object_init(it, destroy, NULL); ((dir_iterator_t *)it)->dev = it->top->dir->dev; ((dir_iterator_t *)it)->next = next; ((dir_iterator_t *)it)->read_link = read_link; ((dir_iterator_t *)it)->open_subdir = open_subdir; return (dir_iterator_t *)it; fail: free(it); return NULL; } void dir_tree_iterator_skip(dir_iterator_t *base) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; pop(it); }