/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * dir_tree_iterator.c * * Copyright (C) 2023 David Oberhollenzer */ #include "config.h" #include "io/dir_iterator.h" #include "util/util.h" #include "sqfs/error.h" #include "sqfs/io.h" #include #include typedef struct { sqfs_dir_iterator_t base; dir_tree_cfg_t cfg; int state; sqfs_dir_iterator_t *rec; } dir_tree_iterator_t; static bool should_skip(const dir_tree_iterator_t *dir, const sqfs_dir_entry_t *ent) { unsigned int type_mask; if ((dir->cfg.flags & DIR_SCAN_ONE_FILESYSTEM)) { if (ent->flags & SQFS_DIR_ENTRY_FLAG_MOUNT_POINT) return true; } switch (ent->mode & S_IFMT) { case S_IFSOCK: type_mask = DIR_SCAN_NO_SOCK; break; case S_IFLNK: type_mask = DIR_SCAN_NO_SLINK; break; case S_IFREG: type_mask = DIR_SCAN_NO_FILE; break; case S_IFBLK: type_mask = DIR_SCAN_NO_BLK; break; case S_IFCHR: type_mask = DIR_SCAN_NO_CHR; break; case S_IFIFO: type_mask = DIR_SCAN_NO_FIFO; break; default: type_mask = 0; break; } return (dir->cfg.flags & type_mask) != 0; } static sqfs_dir_entry_t *expand_path(const dir_tree_iterator_t *it, sqfs_dir_entry_t *ent) { if (it->cfg.prefix != NULL && it->cfg.prefix[0] != '\0') { size_t plen = strlen(it->cfg.prefix) + 1; size_t slen = strlen(ent->name) + 1; void *new = realloc(ent, sizeof(*ent) + plen + slen); if (new == NULL) { free(ent); return NULL; } ent = new; memmove(ent->name + plen, ent->name, slen); memcpy(ent->name, it->cfg.prefix, plen - 1); ent->name[plen - 1] = '/'; } return ent; } static void apply_changes(const dir_tree_iterator_t *it, sqfs_dir_entry_t *ent) { if (!(it->cfg.flags & DIR_SCAN_KEEP_TIME)) ent->mtime = it->cfg.def_mtime; if (!(it->cfg.flags & DIR_SCAN_KEEP_UID)) ent->uid = it->cfg.def_uid; if (!(it->cfg.flags & DIR_SCAN_KEEP_GID)) ent->gid = it->cfg.def_gid; if (!(it->cfg.flags & DIR_SCAN_KEEP_MODE)) { ent->mode &= ~(07777); ent->mode |= it->cfg.def_mode & 07777; } } /*****************************************************************************/ static void destroy(sqfs_object_t *obj) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)obj; sqfs_drop(it->rec); free(it); } static int next(sqfs_dir_iterator_t *base, sqfs_dir_entry_t **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; sqfs_dir_entry_t *ent; int ret; if (it->state) return it->state; retry: *out = NULL; ent = NULL; for (;;) { ret = it->rec->next(it->rec, &ent); if (ret != 0) { it->state = ret; return ret; } if (!should_skip(it, ent)) break; if (S_ISDIR(ent->mode)) it->rec->ignore_subdir(it->rec); free(ent); ent = NULL; } ent = expand_path(it, ent); if (ent == NULL) { it->state = SQFS_ERROR_ALLOC; return it->state; } apply_changes(it, ent); if (S_ISDIR(ent->mode)) { if (it->cfg.flags & DIR_SCAN_NO_RECURSION) it->rec->ignore_subdir(it->rec); if (it->cfg.flags & DIR_SCAN_NO_DIR) { free(ent); goto retry; } } if (it->cfg.name_pattern != NULL) { if (it->cfg.flags & DIR_SCAN_MATCH_FULL_PATH) { ret = fnmatch(it->cfg.name_pattern, ent->name, FNM_PATHNAME); } else { const char *name = strrchr(ent->name, '/'); name = (name == NULL) ? ent->name : (name + 1); ret = fnmatch(it->cfg.name_pattern, name, 0); } if (ret != 0) { free(ent); goto retry; } } *out = ent; return it->state; } static int read_link(sqfs_dir_iterator_t *base, char **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; if (it->state) return it->state; return it->rec->read_link(it->rec, out); } static int open_subdir(sqfs_dir_iterator_t *base, sqfs_dir_iterator_t **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; if (it->state) return it->state; return it->rec->open_subdir(it->rec, out); } static void ignore_subdir(sqfs_dir_iterator_t *base) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; if (it->state == 0) it->rec->ignore_subdir(it->rec); } static int open_file_ro(sqfs_dir_iterator_t *base, sqfs_istream_t **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; if (it->state) return it->state; return it->rec->open_file_ro(it->rec, out); } static int read_xattr(sqfs_dir_iterator_t *base, sqfs_xattr_t **out) { dir_tree_iterator_t *it = (dir_tree_iterator_t *)base; if (it->state) return it->state; return it->rec->read_xattr(it->rec, out); } sqfs_dir_iterator_t *dir_tree_iterator_create(const char *path, const dir_tree_cfg_t *cfg) { dir_tree_iterator_t *it = calloc(1, sizeof(*it)); sqfs_dir_iterator_t *dir; int ret; if (it == NULL) { perror(path); return NULL; } it->cfg = *cfg; ret = sqfs_dir_iterator_create_native(&dir, path, 0); if (ret) { perror(path); goto fail; } ret = sqfs_dir_iterator_create_recursive(&it->rec, dir); sqfs_drop(dir); if (ret) goto fail_oom; if (!(cfg->flags & DIR_SCAN_NO_HARDLINKS)) { ret = sqfs_hard_link_filter_create(&dir, it->rec); sqfs_drop(it->rec); it->rec = dir; if (ret) goto fail_oom; } sqfs_object_init(it, destroy, NULL); ((sqfs_dir_iterator_t *)it)->next = next; ((sqfs_dir_iterator_t *)it)->read_link = read_link; ((sqfs_dir_iterator_t *)it)->open_subdir = open_subdir; ((sqfs_dir_iterator_t *)it)->ignore_subdir = ignore_subdir; ((sqfs_dir_iterator_t *)it)->open_file_ro = open_file_ro; ((sqfs_dir_iterator_t *)it)->read_xattr = read_xattr; return (sqfs_dir_iterator_t *)it; fail_oom: fprintf(stderr, "%s: out of memory\n", path); fail: free(it); return NULL; }