diff options
-rw-r--r-- | include/fstree.h | 48 | ||||
-rw-r--r-- | lib/fstree/fstree.c | 140 | ||||
-rw-r--r-- | lib/fstree/fstree_from_dir.c | 174 | ||||
-rw-r--r-- | lib/fstree/node_stat.c | 7 | ||||
-rw-r--r-- | lib/sqfs/deserialize_fstree.c | 10 | ||||
-rw-r--r-- | lib/sqfs/write_inode.c | 2 |
6 files changed, 142 insertions, 239 deletions
diff --git a/include/fstree.h b/include/fstree.h index 66a850e..f23ab70 100644 --- a/include/fstree.h +++ b/include/fstree.h @@ -137,10 +137,7 @@ struct tree_node_t { /* Encapsulates a file system tree */ struct fstree_t { - uint32_t default_uid; - uint32_t default_gid; - uint32_t default_mode; - uint32_t default_mtime; + struct stat defaults; size_t block_size; size_t inode_tbl_size; @@ -169,40 +166,35 @@ int fstree_init(fstree_t *fs, size_t block_size, uint32_t mtime, void fstree_cleanup(fstree_t *fs); /* - Add a generic node to an fstree. + Create a tree node from a struct stat, node name and extra data. - The new node is inserted by path. If some components of the path don't - exist, they are created as directories with default permissions, like - mkdir -p would, and marked as implcitily created. A subsequent call that - tries to create an existing tree node will fail, except if the target - is an implicitly created directory node and the call tries to create it - as a directory (this will simply overwrite the permissions and ownership). - The implicitly created flag is then cleared. Subsequent attempts to create - an existing directory again will then also fail. + For symlinks, the extra part is interpreted as target. For regular files, it + is interpreted as input path (can be NULL). The name doesn't have to be null + terminated, a length has to be specified. This function does not print anything to stderr, instead it sets an appropriate errno value. - `extra_len` specifies an additional number of bytes to allocate for payload - data in the tree node. + The resulting node can be freed with a single free() call. */ -tree_node_t *fstree_add(fstree_t *fs, const char *path, uint16_t mode, - uint32_t uid, uint32_t gid, size_t extra_len); +tree_node_t *fstree_mknode(fstree_t *fs, tree_node_t *parent, const char *name, + size_t name_len, const char *extra, + const struct stat *sb); /* - A wrappter around fstree_add for regular files. + Add a node to an fstree at a specific path. - This function internally computes the number of extra payload bytes - requiered and sets up the payload pointers propperly. -*/ -tree_node_t *fstree_add_file(fstree_t *fs, const char *path, uint16_t mode, - uint32_t uid, uint32_t gid, uint64_t filesz, - const char *input); + If some components of the path don't exist, they are created as directories + with default permissions, like mkdir -p would, and marked as implcitily + created. A subsequent call that tries to create an existing tree node will + fail, except if the target is an implicitly created directory node and the + call tries to create it as a directory (this will simply overwrite the + permissions and ownership). The implicitly created flag is then cleared. + Subsequent attempts to create an existing directory again will then also + fail. -/* - internally calls fstree_add or fstree_add_file depending on the given stat - structure and sets the link target for symlinks or input file for regular - files from the given extra string. + This function does not print anything to stderr, instead it sets an + appropriate errno value. Internally it uses fstree_mknode to create the node. */ tree_node_t *fstree_add_generic(fstree_t *fs, const char *path, const struct stat *sb, const char *extra); diff --git a/lib/fstree/fstree.c b/lib/fstree/fstree.c index 3f6387a..12b32fb 100644 --- a/lib/fstree/fstree.c +++ b/lib/fstree/fstree.c @@ -6,19 +6,26 @@ #include <stdio.h> #include <errno.h> -static tree_node_t *mknode(tree_node_t *parent, const char *name, - size_t name_len, size_t extra_len, - uint16_t mode, uint32_t uid, uint32_t gid) +tree_node_t *fstree_mknode(fstree_t *fs, tree_node_t *parent, const char *name, + size_t name_len, const char *extra, + const struct stat *sb) { - size_t size = sizeof(tree_node_t) + extra_len; + size_t size = sizeof(tree_node_t); tree_node_t *n; + char *ptr; - switch (mode & S_IFMT) { + switch (sb->st_mode & S_IFMT) { + case S_IFLNK: + size += strlen(extra) + 1; + break; case S_IFDIR: size += sizeof(*n->data.dir); break; case S_IFREG: size += sizeof(*n->data.file); + size += (sb->st_size / fs->block_size) * sizeof(uint32_t); + if (extra != NULL) + size += strlen(extra) + 1; break; } @@ -32,19 +39,32 @@ static tree_node_t *mknode(tree_node_t *parent, const char *name, n->parent = parent; } - n->uid = uid; - n->gid = gid; - n->mode = mode; + n->uid = sb->st_uid; + n->gid = sb->st_gid; + n->mode = sb->st_mode; - switch (mode & S_IFMT) { + switch (sb->st_mode & S_IFMT) { case S_IFDIR: n->data.dir = (dir_info_t *)n->payload; break; case S_IFREG: n->data.file = (file_info_t *)n->payload; + n->data.file->size = sb->st_size; + if (extra == NULL) + break; + + ptr = (char *)n->data.file->blocksizes; + ptr += (sb->st_size / fs->block_size) * sizeof(uint32_t); + n->data.file->input_file = ptr; + strcpy(n->data.file->input_file, extra); break; case S_IFLNK: n->data.slink_target = (char *)n->payload; + strcpy(n->data.slink_target, extra); + break; + case S_IFBLK: + case S_IFCHR: + n->data.devno = sb->st_rdev; break; } @@ -103,9 +123,8 @@ static tree_node_t *get_parent_node(fstree_t *fs, tree_node_t *root, n = child_by_name(root, path, end - path); if (n == NULL) { - n = mknode(root, path, end - path, 0, - S_IFDIR | fs->default_mode, - fs->default_uid, fs->default_gid); + n = fstree_mknode(fs, root, path, end - path, NULL, + &fs->defaults); if (n == NULL) return NULL; @@ -119,91 +138,32 @@ static tree_node_t *get_parent_node(fstree_t *fs, tree_node_t *root, return root; } -tree_node_t *fstree_add(fstree_t *fs, const char *path, uint16_t mode, - uint32_t uid, uint32_t gid, size_t extra_len) +tree_node_t *fstree_add_generic(fstree_t *fs, const char *path, + const struct stat *sb, const char *extra) { tree_node_t *child, *parent; const char *name; - name = strrchr(path, '/'); - name = (name == NULL ? path : (name + 1)); - parent = get_parent_node(fs, fs->root, path); if (parent == NULL) return NULL; + name = strrchr(path, '/'); + name = (name == NULL ? path : (name + 1)); + child = child_by_name(parent, name, strlen(name)); if (child != NULL) { - if (S_ISDIR(child->mode) && S_ISDIR(mode) && - child->data.dir->created_implicitly) { - child->data.dir->created_implicitly = false; - return child; - } - - errno = EEXIST; - return NULL; - } - - return mknode(parent, name, strlen(name), extra_len, mode, uid, gid); -} - -tree_node_t *fstree_add_file(fstree_t *fs, const char *path, uint16_t mode, - uint32_t uid, uint32_t gid, uint64_t filesz, - const char *input) -{ - tree_node_t *node; - size_t count, extra; - char *ptr; - - count = filesz / fs->block_size; - extra = sizeof(uint32_t) * count; - - if (input != NULL) - extra += strlen(input) + 1; - - mode &= 07777; - node = fstree_add(fs, path, S_IFREG | mode, uid, gid, extra); - - if (node != NULL) { - if (input != NULL) { - ptr = (char *)(node->data.file->blocksizes + count); - strcpy(ptr, input); - node->data.file->input_file = ptr; - } else { - node->data.file->input_file = NULL; + if (!S_ISDIR(child->mode) || !S_ISDIR(sb->st_mode) || + !child->data.dir->created_implicitly) { + errno = EEXIST; + return NULL; } - node->data.file->size = filesz; - } - return node; -} - -tree_node_t *fstree_add_generic(fstree_t *fs, const char *path, - const struct stat *sb, const char *extra) -{ - size_t payload = 0; - tree_node_t *node; - - if (S_ISREG(sb->st_mode)) { - return fstree_add_file(fs, path, sb->st_mode, sb->st_uid, - sb->st_gid, sb->st_size, extra); - } - - if (S_ISLNK(sb->st_mode)) - payload = strlen(extra) + 1; - - node = fstree_add(fs, path, sb->st_mode, sb->st_uid, - sb->st_gid, payload); - if (node == NULL) - return NULL; - - if (S_ISLNK(sb->st_mode)) { - strcpy(node->data.slink_target, extra); - } else if (S_ISBLK(sb->st_mode) || S_ISCHR(sb->st_mode)) { - node->data.devno = sb->st_rdev; + child->data.dir->created_implicitly = false; + return child; } - return node; + return fstree_mknode(fs, parent, name, strlen(name), extra, sb); } int fstree_add_xattr(fstree_t *fs, tree_node_t *node, @@ -347,10 +307,13 @@ int fstree_init(fstree_t *fs, size_t block_size, uint32_t mtime, { memset(fs, 0, sizeof(*fs)); - fs->default_uid = default_uid; - fs->default_gid = default_gid; - fs->default_mode = default_mode & 07777; - fs->default_mtime = mtime; + fs->defaults.st_uid = default_uid; + fs->defaults.st_gid = default_gid; + fs->defaults.st_mode = S_IFDIR | (default_mode & 07777); + fs->defaults.st_mtime = mtime; + fs->defaults.st_ctime = mtime; + fs->defaults.st_atime = mtime; + fs->defaults.st_blksize = block_size; fs->block_size = block_size; if (str_table_init(&fs->xattr_keys, FSTREE_XATTR_KEY_BUCKETS)) @@ -361,8 +324,7 @@ int fstree_init(fstree_t *fs, size_t block_size, uint32_t mtime, return -1; } - fs->root = mknode(NULL, "", 0, 0, S_IFDIR | fs->default_mode, - default_uid, default_gid); + fs->root = fstree_mknode(fs, NULL, "", 0, NULL, &fs->defaults); if (fs->root == NULL) { perror("initializing file system tree"); diff --git a/lib/fstree/fstree_from_dir.c b/lib/fstree/fstree_from_dir.c index fb906f1..ea1ebe9 100644 --- a/lib/fstree/fstree_from_dir.c +++ b/lib/fstree/fstree_from_dir.c @@ -7,55 +7,57 @@ #include <unistd.h> #include <string.h> #include <stdlib.h> +#include <assert.h> #include <stdio.h> #include <errno.h> #include <fcntl.h> -static size_t path_size(tree_node_t *n) +static char *get_file_path(tree_node_t *n, const char *name) { - size_t size = 0; + char *ptr, *new; - while (n != NULL) { - size += strlen(n->name) + 1; - n = n->parent; + if (n->parent == NULL) { + ptr = strdup(name); + if (ptr == NULL) + goto fail; + return ptr; } - return size; -} + ptr = fstree_get_path(n); + if (ptr == NULL) + goto fail; -static char *print_path(char *ptr, tree_node_t *n) -{ - if (n->parent != NULL) { - ptr = print_path(ptr, n->parent); - *(ptr++) = '/'; - strcpy(ptr, n->name); - return ptr + strlen(ptr); - } + assert(canonicalize_name(ptr) == 0); + + new = realloc(ptr, strlen(ptr) + strlen(name) + 2); + if (new == NULL) + goto fail; + ptr = new; + strcat(ptr, "/"); + strcat(ptr, name); return ptr; +fail: + perror("getting absolute file path"); + free(ptr); + return NULL; } -static int populate_dir(tree_node_t *root, int fd, size_t blocksize, - const char *rootdir) +static int populate_dir(fstree_t *fs, tree_node_t *root) { - size_t size, blockcount; + char *extra = NULL; struct dirent *ent; struct stat sb; tree_node_t *n; - ssize_t ret; - void *ptr; DIR *dir; - int cfd; - dir = fdopendir(fd); + dir = opendir("."); if (dir == NULL) { - perror("fdopendir"); - close(fd); + perror("opendir"); return -1; } for (;;) { - n = NULL; errno = 0; ent = readdir(dir); @@ -70,120 +72,70 @@ static int populate_dir(tree_node_t *root, int fd, size_t blocksize, if (!strcmp(ent->d_name, "..") || !strcmp(ent->d_name, ".")) continue; - if (fstatat(fd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { + if (fstatat(AT_FDCWD, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { perror(ent->d_name); goto fail; } - size = sizeof(tree_node_t) + strlen(ent->d_name) + 1; - - switch (sb.st_mode & S_IFMT) { - case S_IFLNK: - size += sb.st_size + 1; - break; - case S_IFREG: - blockcount = sb.st_size / blocksize + 1; - - size += sizeof(file_info_t); - size += sizeof(uint32_t) * blockcount; + if (S_ISLNK(sb.st_mode)) { + extra = alloca(sb.st_size + 1); + if (extra == NULL) + goto fail_rdlink; - size += path_size(root) + strlen(ent->d_name) + 1; - size += strlen(rootdir) + 2; - break; - case S_IFDIR: - size += sizeof(dir_info_t); - break; + if (readlink(ent->d_name, extra, sb.st_size) < 0) + goto fail_rdlink; + } else if (S_ISREG(sb.st_mode)) { + extra = get_file_path(root, ent->d_name); + if (extra == NULL) + goto fail; } - n = calloc(1, size); + n = fstree_mknode(fs, root, ent->d_name, strlen(ent->d_name), + extra, &sb); if (n == NULL) { - perror("allocating tree node"); + perror("creating tree node"); goto fail; } - n->uid = sb.st_uid; - n->gid = sb.st_gid; - n->mode = sb.st_mode; - n->parent = root; - - ptr = n->payload; - - switch (sb.st_mode & S_IFMT) { - case S_IFLNK: - ret = readlinkat(fd, ent->d_name, ptr, sb.st_size); - if (ret < 0) { - perror("readlink"); - goto fail; - } - - n->data.slink_target = ptr; - ptr = (char *)ptr + strlen(ptr) + 1; - break; - case S_IFBLK: - case S_IFCHR: - n->data.devno = sb.st_rdev; - break; - case S_IFDIR: - n->data.dir = ptr; - ptr = (char *)ptr + sizeof(dir_info_t); - break; - case S_IFREG: - n->data.file = ptr; - ptr = (char *)ptr + sizeof(file_info_t); - ptr = (char *)ptr + sizeof(uint32_t) * blockcount; - - n->data.file->input_file = ptr; - n->data.file->size = sb.st_size; - - strcpy(ptr, rootdir); - ptr = (char *)ptr + strlen(ptr); - ptr = print_path(ptr, root); - - *((char*)ptr) = '/'; - strcpy((char *)ptr + 1, ent->d_name); - ptr = (char *)ptr + strlen(ptr) + 1; - break; - } - - n->name = ptr; - strcpy(ptr, ent->d_name); - - n->next = root->data.dir->children; - root->data.dir->children = n; + free(extra); + extra = NULL; } + closedir(dir); + for (n = root->data.dir->children; n != NULL; n = n->next) { if (S_ISDIR(n->mode)) { - cfd = openat(fd, n->name, O_RDONLY | O_DIRECTORY); - if (cfd < 0) { - perror(n->name); - goto fail_dir; - } + if (pushd(n->name)) + return -1; - if (populate_dir(n, cfd, blocksize, rootdir)) { - close(cfd); - goto fail_dir; - } + if (populate_dir(fs, n)) + return -1; + + if (popd()) + return -1; } } - closedir(dir); return 0; +fail_rdlink: + perror("readlink"); fail: - free(n); -fail_dir: closedir(dir); + free(extra); return -1; } int fstree_from_dir(fstree_t *fs, const char *path) { - int fd = open(path, O_RDONLY | O_DIRECTORY); + int ret; - if (fd < 0) { - perror(path); + if (pushd(path)) return -1; - } - return populate_dir(fs->root, fd, fs->block_size, path); + ret = populate_dir(fs, fs->root); + + if (popd()) + ret = -1; + + return ret; } diff --git a/lib/fstree/node_stat.c b/lib/fstree/node_stat.c index a6ec097..8f62a0f 100644 --- a/lib/fstree/node_stat.c +++ b/lib/fstree/node_stat.c @@ -7,17 +7,12 @@ void fstree_node_stat(fstree_t *fs, tree_node_t *node, struct stat *sb) { tree_node_t *n; - memset(sb, 0, sizeof(*sb)); - + *sb = fs->defaults; sb->st_ino = node->inode_num; sb->st_mode = node->mode; sb->st_nlink = 1; sb->st_uid = node->uid; sb->st_gid = node->gid; - sb->st_blksize = fs->block_size; - sb->st_mtime = fs->default_mtime; - sb->st_atime = sb->st_mtime; - sb->st_ctime = sb->st_mtime; switch (node->mode & S_IFMT) { case S_IFDIR: diff --git a/lib/sqfs/deserialize_fstree.c b/lib/sqfs/deserialize_fstree.c index 95dce98..2994007 100644 --- a/lib/sqfs/deserialize_fstree.c +++ b/lib/sqfs/deserialize_fstree.c @@ -158,10 +158,12 @@ int deserialize_fstree(fstree_t *out, sqfs_super_t *super, compressor_t *cmp, memset(out, 0, sizeof(*out)); out->block_size = super->block_size; - out->default_uid = 0; - out->default_gid = 0; - out->default_mode = 0755; - out->default_mtime = super->modification_time; + out->defaults.st_uid = 0; + out->defaults.st_gid = 0; + out->defaults.st_mode = 0755; + out->defaults.st_mtime = super->modification_time; + out->defaults.st_ctime = super->modification_time; + out->defaults.st_atime = super->modification_time; out->root = tree_node_from_inode(root, &idtbl, "", super->block_size); free(root); diff --git a/lib/sqfs/write_inode.c b/lib/sqfs/write_inode.c index 961013c..ee4ebc6 100644 --- a/lib/sqfs/write_inode.c +++ b/lib/sqfs/write_inode.c @@ -95,7 +95,7 @@ int meta_writer_write_inode(fstree_t *fs, id_table_t *idtbl, meta_writer_t *im, base.mode = htole16(node->mode); base.uid_idx = htole16(uid_idx); base.gid_idx = htole16(gid_idx); - base.mod_time = htole32(fs->default_mtime); + base.mod_time = htole32(fs->defaults.st_mtime); base.inode_number = htole32(node->inode_num); if (meta_writer_append(im, &base, sizeof(base))) { |