diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | extras/browse.c | 226 | ||||
-rw-r--r-- | extras/extract_one.c | 145 | ||||
-rw-r--r-- | extras/list_files.c | 160 |
4 files changed, 322 insertions, 211 deletions
diff --git a/Makefile.am b/Makefile.am index ab960c5..141973e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,7 @@ include bin/sqfs2tar/Makemodule.am include bin/sqfsdiff/Makemodule.am include bin/tar2sqfs/Makemodule.am -#include extras/Makemodule.am +include extras/Makemodule.am if HAVE_DOXYGEN @DX_RULES@ diff --git a/extras/browse.c b/extras/browse.c index a608021..22bdfb1 100644 --- a/extras/browse.c +++ b/extras/browse.c @@ -9,9 +9,9 @@ #include "sqfs/dir_reader.h" #include "sqfs/id_table.h" #include "sqfs/inode.h" +#include "sqfs/block.h" #include "sqfs/super.h" #include "sqfs/error.h" -#include "sqfs/block.h" #include "sqfs/dir.h" #include "sqfs/io.h" #include "compat.h" @@ -27,35 +27,79 @@ static sqfs_dir_reader_t *dr; static sqfs_super_t super; -static sqfs_inode_generic_t *working_dir; static sqfs_id_table_t *idtbl; static sqfs_data_reader_t *data; +static sqfs_u64 working_dir; -/*****************************************************************************/ - -static void change_directory(const char *dirname) +static int resolve_ref(const char *path, sqfs_u64 *out) { + sqfs_dir_reader_state_t state; sqfs_inode_generic_t *inode; + sqfs_dir_node_t *ent; + const char *end; + sqfs_u64 ref; + size_t len; int ret; - if (dirname == NULL || *dirname == '/') { - free(working_dir); - working_dir = NULL; + ref = *path == '/' ? super.root_inode_ref : working_dir; + + while (path != NULL && *path != '\0') { + if (*path == '/') { + while (*path == '/') + ++path; + continue; + } + + end = strchr(path, '/'); + len = (end == NULL) ? strlen(path) : (size_t)(end - path); - sqfs_dir_reader_get_root_inode(dr, &working_dir); + ret = sqfs_dir_reader_get_inode(dr, ref, &inode); + if (ret != 0) + return ret; + + ret = sqfs_dir_reader_open_dir(dr, inode, &state, 0); + sqfs_free(inode); + + if (ret != 0) + return ret; + + do { + ret = sqfs_dir_reader_read(dr, &state, &ent); + if (ret < 0) + return ret; + if (ret > 0) + return 1; + + ret = strncmp((const char *)ent->name, path, len); + free(ent); + if (ret == 0 && ent->name[len] != '\0') + return 1; + } while (ret != 0); + + ref = state.ent_ref; + path = end; } - if (dirname != NULL) { - ret = sqfs_dir_reader_find_by_path(dr, working_dir, - dirname, &inode); - if (ret != 0) { - printf("Error resolving '%s', error code %d\n", - dirname, ret); - return; - } + *out = ref; + return 0; +} - free(working_dir); - working_dir = inode; +/*****************************************************************************/ + +static void change_directory(const char *dirname) +{ + int ret; + + if (dirname == NULL) { + working_dir = super.root_inode_ref; + return; + } + + ret = resolve_ref(dirname, &working_dir); + if (ret < 0) { + fprintf(stderr, "%s: error resolving path: %d\n", dirname, ret); + } else if (ret > 0) { + fprintf(stderr, "%s: no such file or directory\n", dirname); } } @@ -63,50 +107,42 @@ static void change_directory(const char *dirname) static void list_directory(const char *dirname) { - sqfs_inode_generic_t *root, *inode; + sqfs_dir_reader_state_t state, init_state; size_t i, max_len, len, col_count; + sqfs_inode_generic_t *inode; sqfs_dir_node_t *ent; + sqfs_u64 ref; int ret; /* Get the directory inode we want to dump and open the directory */ if (dirname == NULL) { - ret = sqfs_dir_reader_open_dir(dr, working_dir, 0); - if (ret) - goto fail_open; - } else if (*dirname == '/') { - sqfs_dir_reader_get_root_inode(dr, &root); - - ret = sqfs_dir_reader_find_by_path(dr, root, dirname, &inode); - sqfs_free(root); - if (ret) - goto fail_resolve; - - ret = sqfs_dir_reader_open_dir(dr, inode, 0); - sqfs_free(inode); - if (ret) - goto fail_open; + ref = working_dir; } else { - ret = sqfs_dir_reader_find_by_path(dr, working_dir, - dirname, &inode); - if (ret) + ret = resolve_ref(dirname, &ref); + if (ret < 0) goto fail_resolve; - - ret = sqfs_dir_reader_open_dir(dr, inode, 0); - sqfs_free(inode); - if (ret) - goto fail_open; + if (ret > 0) + goto fail_no_ent; } + ret = sqfs_dir_reader_get_inode(dr, ref, &inode); + if (ret) + goto fail_open; + + ret = sqfs_dir_reader_open_dir(dr, inode, &state, 0); + sqfs_free(inode); + if (ret) + goto fail_open; + /* first pass over the directory to figure out column count/length */ + init_state = state; + for (max_len = 0; ; max_len = len > max_len ? len : max_len) { - ret = sqfs_dir_reader_read(dr, &ent); + ret = sqfs_dir_reader_read(dr, &state, &ent); if (ret > 0) break; - - if (ret < 0) { - fputs("Error while reading directory list\n", stderr); - break; - } + if (ret < 0) + goto fail_read; len = ent->size + 1; sqfs_free(ent); @@ -116,18 +152,15 @@ static void list_directory(const char *dirname) col_count = col_count < 1 ? 1 : col_count; i = 0; - /* second pass for printing directory contents */ - sqfs_dir_reader_rewind(dr); + state = init_state; + /* second pass for printing directory contents */ for (;;) { - ret = sqfs_dir_reader_read(dr, &ent); + ret = sqfs_dir_reader_read(dr, &state, &ent); if (ret > 0) break; - - if (ret < 0) { - fputs("Error while reading directory list\n", stderr); - break; - } + if (ret < 0) + goto fail_read; /* entries always use basic types only */ switch (ent->type) { @@ -174,12 +207,18 @@ static void list_directory(const char *dirname) fputc('\n', stdout); return; +fail_read: + fputs("Error while reading directory list\n", stderr); + return; fail_open: printf("Error opening '%s', error code %d\n", dirname, ret); return; fail_resolve: printf("Error resolving '%s', error code %d\n", dirname, ret); return; +fail_no_ent: + fprintf(stderr, "%s: no such file or directory\n", dirname); + return; } /*****************************************************************************/ @@ -221,13 +260,14 @@ static void mode_to_str(sqfs_u16 mode, char *p) static void stat_cmd(const char *filename) { - sqfs_inode_generic_t *inode, *root; + sqfs_inode_generic_t *inode; sqfs_dir_index_t *idx; sqfs_u32 uid, gid; const char *type; char buffer[64]; time_t timeval; struct tm *tm; + sqfs_u64 ref; size_t i; int ret; @@ -237,18 +277,13 @@ static void stat_cmd(const char *filename) return; } - if (*filename == '/') { - sqfs_dir_reader_get_root_inode(dr, &root); - ret = sqfs_dir_reader_find_by_path(dr, root, filename, &inode); - sqfs_free(root); - if (ret) - goto fail_resolve; - } else { - ret = sqfs_dir_reader_find_by_path(dr, working_dir, - filename, &inode); - if (ret) - goto fail_resolve; - } + ret = resolve_ref(filename, &ref); + if (ret) + goto fail_resolve; + + ret = sqfs_dir_reader_get_inode(dr, ref, &inode); + if (ret) + goto fail_resolve; /* some basic information */ switch (inode->base.type) { @@ -431,10 +466,10 @@ fail_resolve: static void cat_cmd(const char *filename) { - sqfs_inode_generic_t *inode, *root; - sqfs_u64 offset = 0; + sqfs_inode_generic_t *inode; + sqfs_istream_t *stream; char buffer[512]; - sqfs_s32 diff; + sqfs_u64 ref; int ret; /* get the inode of the file */ @@ -443,17 +478,23 @@ static void cat_cmd(const char *filename) return; } - if (*filename == '/') { - sqfs_dir_reader_get_root_inode(dr, &root); - ret = sqfs_dir_reader_find_by_path(dr, root, filename, &inode); - sqfs_free(root); - } else { - ret = sqfs_dir_reader_find_by_path(dr, working_dir, - filename, &inode); + ret = resolve_ref(filename, &ref); + if (ret) { + printf("Error resolving '%s', error code %d\n", filename, ret); + return; } + ret = sqfs_dir_reader_get_inode(dr, ref, &inode); if (ret) { - printf("Error resolving '%s', error code %d\n", filename, ret); + printf("Error reading inode for '%s', error code %d\n", + filename, ret); + return; + } + + ret = sqfs_data_reader_create_stream(data, inode, filename, &stream); + sqfs_free(inode); + if (ret) { + printf("Opening file '%s', error code %d\n", filename, ret); return; } @@ -462,21 +503,19 @@ static void cat_cmd(const char *filename) * or fragment access. */ for (;;) { - diff = sqfs_data_reader_read(data, inode, offset, buffer, - sizeof(buffer)); - if (diff == 0) + ret = sqfs_istream_read(stream, buffer, sizeof(buffer)); + if (ret == 0) break; - if (diff < 0) { + if (ret < 0) { printf("Error reading from file '%s', error code %d\n", - filename, diff); + filename, ret); break; } - fwrite(buffer, 1, diff, stdout); - offset += diff; + fwrite(buffer, 1, ret, stdout); } - sqfs_free(inode); + sqfs_drop(stream); } /*****************************************************************************/ @@ -518,6 +557,8 @@ int main(int argc, char **argv) goto out_fd; } + working_dir = super.root_inode_ref; + sqfs_compressor_config_init(&cfg, super.compression_id, super.block_size, SQFS_COMP_FLAG_UNCOMPRESS); @@ -550,11 +591,6 @@ int main(int argc, char **argv) goto out_id; } - if (sqfs_dir_reader_get_root_inode(dr, &working_dir)) { - fprintf(stderr, "%s: error reading root inode.\n", argv[1]); - goto out_dir; - } - /* create a data reader */ data = sqfs_data_reader_create(file, super.block_size, cmp, 0); @@ -613,8 +649,6 @@ int main(int argc, char **argv) out: sqfs_drop(data); out_dir: - if (working_dir != NULL) - free(working_dir); sqfs_drop(dr); out_id: sqfs_drop(idtbl); diff --git a/extras/extract_one.c b/extras/extract_one.c index d0fe1b9..d0dbafd 100644 --- a/extras/extract_one.c +++ b/extras/extract_one.c @@ -8,35 +8,82 @@ #include "sqfs/compressor.h" #include "sqfs/data_reader.h" #include "sqfs/dir_reader.h" +#include "sqfs/dir_entry.h" #include "sqfs/error.h" #include "sqfs/id_table.h" #include "sqfs/io.h" #include "sqfs/inode.h" #include "sqfs/super.h" -#include "sqfs/xattr_reader.h" #include <string.h> #include <stdlib.h> -#include <errno.h> #include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> + +static int find_file(sqfs_dir_iterator_t *it, const char *path, + sqfs_istream_t **out) +{ + const char *end = strchr(path, '/'); + size_t len = end == NULL ? strlen(path) : (size_t)(end - path); + + for (;;) { + sqfs_dir_entry_t *ent; + int ret; + + ret = it->next(it, &ent); + if (ret < 0) { + fputs("error reading directory entry\n", stderr); + return -1; + } + + if (ret > 0) { + fputs("entry not found\n", stderr); + return -1; + } + + if (strncmp(ent->name, path, len) != 0 || + ent->name[len] != '\0') { + free(ent); + continue; + } + + if (end == NULL) { + ret = it->open_file_ro(it, out); + if (ret) { + fprintf(stderr, "%s: error opening file %d\n", + ent->name, ret); + } + } else { + sqfs_dir_iterator_t *sub; + + ret = it->open_subdir(it, &sub); + if (ret != 0) { + fprintf(stderr, "%s: error opening subdir\n", + ent->name); + } else { + ret = find_file(sub, end + 1, out); + sqfs_drop(sub); + } + } + + free(ent); + return ret; + } +} int main(int argc, char **argv) { - sqfs_xattr_reader_t *xattr = NULL; + sqfs_inode_generic_t *iroot = NULL; sqfs_data_reader_t *data = NULL; sqfs_dir_reader_t *dirrd = NULL; sqfs_compressor_t *cmp = NULL; sqfs_id_table_t *idtbl = NULL; - sqfs_tree_node_t *n = NULL; sqfs_file_t *file = NULL; - sqfs_u8 *p, *output = NULL; + sqfs_dir_iterator_t *it = NULL; sqfs_compressor_config_t cfg; sqfs_super_t super; - sqfs_u64 file_size; int status = EXIT_FAILURE, ret; + sqfs_istream_t *is = NULL; + char buffer[512]; if (argc != 3) { fputs("Usage: extract_one <squashfs-file> <source-file-path>\n", stderr); @@ -56,8 +103,8 @@ int main(int argc, char **argv) } sqfs_compressor_config_init(&cfg, super.compression_id, - super.block_size, - SQFS_COMP_FLAG_UNCOMPRESS); + super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); ret = sqfs_compressor_create(&cfg, &cmp); if (ret != 0) { @@ -65,20 +112,6 @@ int main(int argc, char **argv) goto out; } - if (!(super.flags & SQFS_FLAG_NO_XATTRS)) { - xattr = sqfs_xattr_reader_create(0); - if (xattr == NULL) { - fprintf(stderr, "%s: error creating xattr reader: %d.\n", argv[1], SQFS_ERROR_ALLOC); - goto out; - } - - ret = sqfs_xattr_reader_load(xattr, &super, file, cmp); - if (ret) { - fprintf(stderr, "%s: error loading xattr reader: %d.\n", argv[1], ret); - goto out; - } - } - idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fprintf(stderr, "%s: error creating ID table: %d.\n", argv[1], ret); @@ -109,69 +142,43 @@ int main(int argc, char **argv) goto out; } - ret = sqfs_dir_reader_get_full_hierarchy(dirrd, idtbl, argv[2], 0, &n); - if (ret) { - fprintf(stderr, "%s: error reading filesystem hierarchy: %d.\n", argv[1], ret); + if (sqfs_dir_reader_get_root_inode(dirrd, &iroot)) { + fprintf(stderr, "%s: error reading root inode.\n", argv[1]); goto out; } - if (!S_ISREG(n->inode->base.mode)) { - fprintf(stderr, "/%s is not a file\n", argv[2]); - goto out; - } - - sqfs_inode_get_file_size(n->inode, &file_size); + ret = sqfs_dir_iterator_create(dirrd, idtbl, data, NULL, iroot, &it); + free(iroot); - output = p = malloc(file_size); - if (output == NULL) { - fprintf(stderr, "malloc failed: %d\n", errno); + if (ret) { + fprintf(stderr, "%s: error creating root iterator.\n", + argv[2]); goto out; } - for (size_t i = 0; i < sqfs_inode_get_file_block_count(n->inode); ++i) { - size_t chunk_size, read = (file_size < super.block_size) ? file_size : super.block_size; - sqfs_u8 *chunk; - - ret = sqfs_data_reader_get_block(data, n->inode, i, &chunk_size, &chunk); - if (ret) { - fprintf(stderr, "reading data block: %d\n", ret); - goto out; - } - - memcpy(p, chunk, chunk_size); - p += chunk_size; - free(chunk); - - file_size -= read; - } - - if (file_size > 0) { - size_t chunk_size; - sqfs_u8 *chunk; + if (find_file(it, argv[2], &is)) + goto out; - ret = sqfs_data_reader_get_fragment(data, n->inode, &chunk_size, &chunk); - if (ret) { - fprintf(stderr, "reading fragment block: %d\n", ret); + for (;;) { + ret = sqfs_istream_read(is, buffer, sizeof(buffer)); + if (ret < 0) { + fprintf(stderr, "%s: read error!\n", argv[2]); goto out; } + if (ret == 0) + break; - memcpy(p, chunk, chunk_size); - free(chunk); + fwrite(buffer, 1, ret, stdout); } - /* for example simplicity, assume this is a text file */ - fprintf(stdout, "%s\n", (char *)output); - status = EXIT_SUCCESS; out: - sqfs_dir_tree_destroy(n); + sqfs_drop(is); + sqfs_drop(it); sqfs_drop(data); sqfs_drop(dirrd); sqfs_drop(idtbl); - sqfs_drop(xattr); sqfs_drop(cmp); sqfs_drop(file); - free(output); - return status; } diff --git a/extras/list_files.c b/extras/list_files.c index 89a1f30..55b94e2 100644 --- a/extras/list_files.c +++ b/extras/list_files.c @@ -6,6 +6,7 @@ */ #include "sqfs/compressor.h" #include "sqfs/dir_reader.h" +#include "sqfs/dir_entry.h" #include "sqfs/id_table.h" #include "sqfs/inode.h" #include "sqfs/super.h" @@ -16,52 +17,114 @@ #include <errno.h> #include <stdio.h> -static void write_tree_dfs(const sqfs_tree_node_t *n) +static int count_entries(sqfs_dir_iterator_t *it, unsigned int *out) { - const sqfs_tree_node_t *p; - unsigned int mask, level; - int i; - - for (n = n->children; n != NULL; n = n->next) { - level = 0; - mask = 0; - - for (p = n->parent; p->parent != NULL; p = p->parent) { - if (p->next != NULL) - mask |= 1 << level; - ++level; + sqfs_dir_entry_t *ent; + int ret; + + *out = 0; + + for (;;) { + ret = it->next(it, &ent); + if (ret < 0) { + *out = 0; + return -1; } + if (ret > 0) + break; - for (i = level - 1; i >= 0; --i) + *out += 1; + free(ent); + } + + return 0; +} + +static void write_tree_dfs(sqfs_dir_iterator_t *it, + unsigned int mask, unsigned int level, + unsigned int count) +{ + sqfs_dir_entry_t *ent; + unsigned int i; + sqfs_u16 type; + + while (count--) { + for (i = 0; i < level; ++i) fputs(mask & (1 << i) ? "│ " : " ", stdout); - fputs(n->next == NULL ? "└─ " : "├─ ", stdout); - fputs((const char *)n->name, stdout); - - if (n->inode->base.type == SQFS_INODE_SLINK) { - printf(" ⭢ %.*s", - (int)n->inode->data.slink.target_size, - (const char *)n->inode->extra); - } else if (n->inode->base.type == SQFS_INODE_EXT_SLINK) { - printf(" ⭢ %.*s", - (int)n->inode->data.slink_ext.target_size, - (const char *)n->inode->extra); + if (it->next(it, &ent) != 0) + break; + + fputs(count == 0 ? "└─ " : "├─ ", stdout); + fputs(ent->name, stdout); + + type = ent->mode & SQFS_INODE_MODE_MASK; + free(ent); + + if (type == SQFS_INODE_MODE_LNK) { + char *target; + + if (it->read_link(it, &target) == 0) + printf(" ⭢ %s", target); + + free(target); } fputc('\n', stdout); - write_tree_dfs(n); + + if (type == SQFS_INODE_MODE_DIR) { + sqfs_dir_iterator_t *sub; + unsigned int sub_count; + + it->open_subdir(it, &sub); + count_entries(sub, &sub_count); + sub = sqfs_drop(sub); + + it->open_subdir(it, &sub); + write_tree_dfs(sub, + mask | (count > 0 ? (1 << level) : 0), + level + 1, sub_count); + sqfs_drop(sub); + } } } +static sqfs_dir_iterator_t *create_root_iterator(sqfs_dir_reader_t *dr, + sqfs_id_table_t *idtbl, + const char *filename) +{ + sqfs_inode_generic_t *iroot; + sqfs_dir_iterator_t *it; + int ret; + + if (sqfs_dir_reader_get_root_inode(dr, &iroot)) { + fprintf(stderr, "%s: error reading root inode.\n", + filename); + return NULL; + } + + ret = sqfs_dir_iterator_create(dr, idtbl, NULL, NULL, iroot, &it); + free(iroot); + + if (ret) { + fprintf(stderr, "%s: error creating root iterator.\n", + filename); + return NULL; + } + + return it; +} + int main(int argc, char **argv) { int ret, status = EXIT_FAILURE; + sqfs_dir_iterator_t *it = NULL; + sqfs_compressor_t *cmp = NULL; + sqfs_id_table_t *idtbl = NULL; + sqfs_dir_reader_t *dr = NULL; sqfs_compressor_config_t cfg; - sqfs_compressor_t *cmp; - sqfs_tree_node_t *root = NULL; - sqfs_id_table_t *idtbl; - sqfs_dir_reader_t *dr; - sqfs_file_t *file; + sqfs_file_t *file = NULL; + unsigned int root_count; sqfs_super_t super; /* open the SquashFS file we want to read */ @@ -79,7 +142,7 @@ int main(int argc, char **argv) process the compressor options */ if (sqfs_super_read(&super, file)) { fprintf(stderr, "%s: error reading super block.\n", argv[1]); - goto out_fd; + goto out; } sqfs_compressor_config_init(&cfg, super.compression_id, @@ -90,50 +153,57 @@ int main(int argc, char **argv) if (ret != 0) { fprintf(stderr, "%s: error creating compressor: %d.\n", argv[1], ret); - goto out_fd; + goto out; } /* Create and read the UID/GID mapping table */ idtbl = sqfs_id_table_create(0); if (idtbl == NULL) { fputs("Error creating ID table.\n", stderr); - goto out_cmp; + goto out; } if (sqfs_id_table_read(idtbl, file, &super, cmp)) { fprintf(stderr, "%s: error loading ID table.\n", argv[1]); - goto out_id; + goto out; } - /* create a directory reader and scan the entire directory hiearchy */ + /* create a directory reader */ dr = sqfs_dir_reader_create(&super, cmp, file, 0); if (dr == NULL) { fprintf(stderr, "%s: error creating directory reader.\n", argv[1]); - goto out_id; + goto out; } - if (sqfs_dir_reader_get_full_hierarchy(dr, idtbl, NULL, 0, &root)) { - fprintf(stderr, "%s: error loading directory tree.\n", + /* create a directory iterator for the root inode */ + it = create_root_iterator(dr, idtbl, argv[1]); + if (it == NULL) + goto out; + + if (count_entries(it, &root_count)) { + fprintf(stderr, "%s: error counting root iterator entries.\n", argv[1]); goto out; } + it = sqfs_drop(it); + + it = create_root_iterator(dr, idtbl, argv[1]); + if (it == NULL) + goto out; + /* fancy print the hierarchy */ printf("/\n"); - write_tree_dfs(root); + write_tree_dfs(it, 0, 0, root_count); /* cleanup */ status = EXIT_SUCCESS; out: - if (root != NULL) - sqfs_dir_tree_destroy(root); + sqfs_drop(it); sqfs_drop(dr); -out_id: sqfs_drop(idtbl); -out_cmp: sqfs_drop(cmp); -out_fd: sqfs_drop(file); return status; } |