mirror of https://github.com/pygos/pkg-utils.git
commit
6ec11b532e
36 changed files with 2931 additions and 0 deletions
@ -0,0 +1,19 @@
|
||||
.deps |
||||
.dirstamp |
||||
Makefile |
||||
Makefile.in |
||||
aclocal.m4 |
||||
autom4te.cache |
||||
compile |
||||
config.h.in |
||||
config.log |
||||
config.status |
||||
configure |
||||
depcomp |
||||
install-sh |
||||
missing |
||||
stamp-h1 |
||||
config.h |
||||
*.o |
||||
*~ |
||||
pkg |
@ -0,0 +1,50 @@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -D_GNU_SOURCE
|
||||
AM_CFLAGS = $(WARN_CFLAGS)
|
||||
|
||||
# application core
|
||||
GLOBALHDR = include/pkgformat.h include/util.h include/pkgreader.h
|
||||
GLOBALHDR += include/compressor.h include/command.h include/pkgwriter.h
|
||||
GLOBALHDR += include/image_entry.h
|
||||
|
||||
MAIN = main/pkg.c main/util.c main/compressor.c main/command.c main/pkgreader.c
|
||||
MAIN += main/pkgwriter.c main/image_entry.c main/image_entry_sort.c
|
||||
|
||||
pkg_SOURCES = $(GLOBALHDR) $(MAIN)
|
||||
pkg_CFLAGS = $(AM_CFLAGS)
|
||||
pkg_LDADD =
|
||||
|
||||
bin_PROGRAMS = pkg
|
||||
|
||||
EXTRA_DIST = autogen.sh
|
||||
|
||||
##### commands #####
|
||||
|
||||
# pack command
|
||||
pkg_SOURCES += main/cmd/pack/filelist.c main/cmd/pack/filelist_read.c
|
||||
pkg_SOURCES += main/cmd/pack/write_toc.c main/cmd/pack/write_files.c
|
||||
pkg_SOURCES += main/cmd/pack/pack.h main/cmd/pack/pack.c
|
||||
|
||||
# dump command
|
||||
pkg_SOURCES += main/cmd/dump/dump.c main/cmd/dump/dump.h main/cmd/dump/dump_toc.c
|
||||
|
||||
# unpack command
|
||||
pkg_SOURCES += main/cmd/unpack/unpack.c main/cmd/unpack/unpack.h
|
||||
pkg_SOURCES += main/cmd/unpack/mkdir_p.c main/cmd/unpack/create_hierarchy.c
|
||||
|
||||
# help command
|
||||
pkg_SOURCES += main/cmd/help.c
|
||||
|
||||
##### compressors #####
|
||||
|
||||
# dummy compressor
|
||||
pkg_SOURCES += main/compressors/none.c
|
||||
|
||||
# zlib compressor
|
||||
if WITH_ZLIB |
||||
pkg_SOURCES += main/compressors/zlib.c
|
||||
|
||||
pkg_CFLAGS += $(ZLIB_CFLAGS)
|
||||
pkg_LDADD += $(ZLIB_LIBS)
|
||||
endif |
@ -0,0 +1,56 @@
|
||||
AC_PREREQ([2.60]) |
||||
AC_INIT([pkgtool], [0.1], [david.oberhollenzer@tele2.at], pkgtool) |
||||
AC_CONFIG_MACRO_DIR([m4]) |
||||
AM_INIT_AUTOMAKE([foreign dist-xz subdir-objects]) |
||||
AM_SILENT_RULES([yes]) |
||||
AC_PROG_CC |
||||
AC_PROG_CC_C99 |
||||
AC_PROG_INSTALL |
||||
|
||||
m4_ifndef([PKG_PROG_PKG_CONFIG], |
||||
[m4_fatal([Could not locate the pkg-config autoconf |
||||
macros. These are usually located in /usr/share/aclocal/pkg.m4. |
||||
If your macros are in a different location, try setting the |
||||
environment variable AL_OPTS="-I/other/macro/dir" before running |
||||
./autogen.sh or autoreconf again. Make sure pkg-config is installed.])]) |
||||
PKG_PROG_PKG_CONFIG |
||||
|
||||
UL_WARN_ADD([-Wall]) |
||||
UL_WARN_ADD([-Wextra]) |
||||
UL_WARN_ADD([-Wunused]) |
||||
UL_WARN_ADD([-Wmissing-prototypes]) |
||||
UL_WARN_ADD([-Wmissing-declarations]) |
||||
UL_WARN_ADD([-Wwrite-strings]) |
||||
UL_WARN_ADD([-Wjump-misses-init]) |
||||
UL_WARN_ADD([-Wuninitialized]) |
||||
UL_WARN_ADD([-Winit-self]) |
||||
UL_WARN_ADD([-Wlogical-op]) |
||||
UL_WARN_ADD([-Wunused-but-set-parameter]) |
||||
UL_WARN_ADD([-Wunused-but-set-variable]) |
||||
UL_WARN_ADD([-Wunused-parameter]) |
||||
UL_WARN_ADD([-Wunused-result]) |
||||
UL_WARN_ADD([-Wunused-variable]) |
||||
UL_WARN_ADD([-Wduplicated-cond]) |
||||
UL_WARN_ADD([-Wduplicated-branches]) |
||||
UL_WARN_ADD([-Wrestrict]) |
||||
UL_WARN_ADD([-Wnull-dereference]) |
||||
UL_WARN_ADD([-pedantic]) |
||||
|
||||
AC_SUBST([WARN_CFLAGS]) |
||||
|
||||
##### search for dependencies ##### |
||||
|
||||
have_zlib="no" |
||||
have_lzma="no" |
||||
|
||||
PKG_CHECK_MODULES(ZLIB, [zlib], [have_zlib="yes"], []) |
||||
PKG_CHECK_MODULES(XZ, [liblzma >= 5.0.0], [have_lzma="yes"], []) |
||||
|
||||
AM_CONDITIONAL([WITH_ZLIB], [test "x$have_zlib" == "xyes"]) |
||||
AM_CONDITIONAL([WITH_LZMA], [test "x$have_lzma" == "xyes"]) |
||||
|
||||
##### generate output ##### |
||||
|
||||
AC_CONFIG_HEADERS([config.h]) |
||||
|
||||
AC_OUTPUT([Makefile]) |
@ -0,0 +1,31 @@
|
||||
#ifndef COMMAND_H |
||||
#define COMMAND_H |
||||
|
||||
typedef struct command_t { |
||||
struct command_t *next; |
||||
|
||||
const char *cmd; /* command name */ |
||||
const char *usage; /* list of possible arguments */ |
||||
const char *s_desc; /* short description used by help */ |
||||
const char *l_desc; /* long description used by help */ |
||||
|
||||
int (*run_cmd)(int argc, char **argv); |
||||
} command_t; |
||||
|
||||
void command_register(command_t *cmd); |
||||
|
||||
command_t *command_by_name(const char *name); |
||||
|
||||
void __attribute__((noreturn)) usage(int status); |
||||
|
||||
void tell_read_help(const char *cmd); |
||||
|
||||
int check_arguments(const char *cmd, int argc, int minc, int maxc); |
||||
|
||||
#define REGISTER_COMMAND(cmd) \ |
||||
static void __attribute__((constructor)) register_##cmd(void) \
|
||||
{ \
|
||||
command_register((command_t *)&cmd); \
|
||||
} |
||||
|
||||
#endif /* COMMAND_H */ |
@ -0,0 +1,43 @@
|
||||
#ifndef COMPRESSOR_H |
||||
#define COMPRESSOR_H |
||||
|
||||
#include <stddef.h> |
||||
#include <sys/types.h> |
||||
|
||||
#include "pkgformat.h" |
||||
|
||||
typedef struct compressor_stream_t { |
||||
ssize_t (*write)(struct compressor_stream_t *stream, const uint8_t *in, |
||||
size_t size); |
||||
|
||||
ssize_t (*read)(struct compressor_stream_t *stream, uint8_t *out, |
||||
size_t size); |
||||
|
||||
void (*flush)(struct compressor_stream_t *stream); |
||||
|
||||
void (*destroy)(struct compressor_stream_t *stream); |
||||
} compressor_stream_t; |
||||
|
||||
typedef struct compressor_t { |
||||
struct compressor_t *next; |
||||
const char *name; |
||||
PKG_COMPRESSION id; |
||||
|
||||
compressor_stream_t *(*compression_stream)(struct compressor_t *cmp); |
||||
|
||||
compressor_stream_t *(*uncompression_stream)(struct compressor_t *cmp); |
||||
} compressor_t; |
||||
|
||||
void compressor_register(compressor_t *compressor); |
||||
|
||||
compressor_t *compressor_by_name(const char *name); |
||||
|
||||
compressor_t *compressor_by_id(PKG_COMPRESSION id); |
||||
|
||||
#define REGISTER_COMPRESSOR(compressor) \ |
||||
static void __attribute__((constructor)) register_##compressor(void) \
|
||||
{ \
|
||||
compressor_register((compressor_t *)&compressor); \
|
||||
} |
||||
|
||||
#endif /* COMPRESSOR_H */ |
@ -0,0 +1,37 @@
|
||||
#ifndef IMAGE_ENTRY_H |
||||
#define IMAGE_ENTRY_H |
||||
|
||||
#include <sys/types.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "pkgreader.h" |
||||
|
||||
typedef struct image_entry_t { |
||||
struct image_entry_t *next; |
||||
char *name; |
||||
mode_t mode; |
||||
uid_t uid; |
||||
gid_t gid; |
||||
|
||||
union { |
||||
struct { |
||||
char *location; |
||||
uint64_t size; |
||||
uint32_t id; |
||||
} file; |
||||
|
||||
struct { |
||||
char *target; |
||||
} symlink; |
||||
} data; |
||||
} image_entry_t; |
||||
|
||||
void image_entry_free(image_entry_t *ent); |
||||
|
||||
void image_entry_free_list(image_entry_t *list); |
||||
|
||||
image_entry_t *image_entry_list_from_package(pkg_reader_t *pkg); |
||||
|
||||
image_entry_t *image_entry_sort(image_entry_t *list); |
||||
|
||||
#endif /* IMAGE_ENTRY_H */ |
@ -0,0 +1,56 @@
|
||||
#ifndef PKG_FORMAT_H |
||||
#define PKG_FORMAT_H |
||||
|
||||
#include <sys/types.h> |
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
#include <endian.h> |
||||
|
||||
typedef enum { |
||||
PKG_MAGIC_HEADER = 0x21676B70, |
||||
PKG_MAGIC_TOC = 0x21636F74, |
||||
PKG_MAGIC_DATA = 0x21746164, |
||||
} PKG_MAGIC; |
||||
|
||||
typedef enum { |
||||
PKG_COMPRESSION_NONE = 0, |
||||
PKG_COMPRESSION_ZLIB = 1, |
||||
} PKG_COMPRESSION; |
||||
|
||||
typedef struct { |
||||
uint32_t magic; |
||||
uint8_t compression; |
||||
uint8_t pad0; |
||||
uint8_t pad1; |
||||
uint8_t pad2; |
||||
uint64_t compressed_size; |
||||
uint64_t raw_size; |
||||
|
||||
/* uint8_t data[]; */ |
||||
} record_t; |
||||
|
||||
typedef struct { |
||||
uint32_t mode; |
||||
uint32_t uid; |
||||
uint32_t gid; |
||||
|
||||
uint16_t path_length; |
||||
/* uint8_t path[]; */ |
||||
} toc_entry_t; |
||||
|
||||
typedef struct { |
||||
uint16_t target_length; |
||||
/* uint8_t target[]; */ |
||||
} toc_symlink_extra_t; |
||||
|
||||
typedef struct { |
||||
uint64_t size; |
||||
uint32_t id; |
||||
} toc_file_extra_t; |
||||
|
||||
typedef struct { |
||||
uint32_t id; |
||||
/* uint8_t data[]; */ |
||||
} file_data_t; |
||||
|
||||
#endif /* PKG_FORMAT_H */ |
@ -0,0 +1,23 @@
|
||||
#ifndef PKGREADER_H |
||||
#define PKGREADER_H |
||||
|
||||
#include "pkgformat.h" |
||||
|
||||
typedef struct pkg_reader_t pkg_reader_t; |
||||
|
||||
pkg_reader_t *pkg_reader_open(const char *path); |
||||
|
||||
void pkg_reader_close(pkg_reader_t *reader); |
||||
|
||||
int pkg_reader_get_next_record(pkg_reader_t *reader); |
||||
|
||||
record_t *pkg_reader_current_record_header(pkg_reader_t *reader); |
||||
|
||||
ssize_t pkg_reader_read_payload(pkg_reader_t *reader, void *buffer, |
||||
size_t size); |
||||
|
||||
int pkg_reader_rewind(pkg_reader_t *reader); |
||||
|
||||
const char *pkg_reader_get_filename(pkg_reader_t *reader); |
||||
|
||||
#endif /* PKGREADER_H */ |
@ -0,0 +1,20 @@
|
||||
#ifndef PKGWRITER_H |
||||
#define PKGWRITER_H |
||||
|
||||
#include "pkgformat.h" |
||||
#include "compressor.h" |
||||
|
||||
typedef struct pkg_writer_t pkg_writer_t; |
||||
|
||||
pkg_writer_t *pkg_writer_open(const char *path); |
||||
|
||||
void pkg_writer_close(pkg_writer_t *writer); |
||||
|
||||
int pkg_writer_start_record(pkg_writer_t *writer, uint32_t magic, |
||||
compressor_t *cmp); |
||||
|
||||
int pkg_writer_write_payload(pkg_writer_t *wr, void *data, size_t size); |
||||
|
||||
int pkg_writer_end_record(pkg_writer_t *wr); |
||||
|
||||
#endif /* PKGWRITER_H */ |
@ -0,0 +1,12 @@
|
||||
#ifndef UTIL_H |
||||
#define UTIL_H |
||||
|
||||
#include <sys/types.h> |
||||
|
||||
int canonicalize_name(char *filename); |
||||
|
||||
ssize_t write_retry(int fd, void *data, size_t size); |
||||
|
||||
ssize_t read_retry(int fd, void *buffer, size_t size); |
||||
|
||||
#endif /* UTIL_H */ |
@ -0,0 +1,40 @@
|
||||
dnl Copyright (C) 2008-2011 Free Software Foundation, Inc. |
||||
dnl This file is free software; the Free Software Foundation |
||||
dnl gives unlimited permission to copy and/or distribute it, |
||||
dnl with or without modifications, as long as this notice is preserved. |
||||
|
||||
dnl From Simon Josefsson |
||||
dnl -- derivated from coreutils m4/warnings.m4 |
||||
|
||||
# UL_AS_VAR_APPEND(VAR, VALUE) |
||||
# ---------------------------- |
||||
# Provide the functionality of AS_VAR_APPEND if Autoconf does not have it. |
||||
m4_ifdef([AS_VAR_APPEND], |
||||
[m4_copy([AS_VAR_APPEND], [UL_AS_VAR_APPEND])], |
||||
[m4_define([UL_AS_VAR_APPEND], |
||||
[AS_VAR_SET([$1], [AS_VAR_GET([$1])$2])])]) |
||||
|
||||
# UL_ADD_WARN(COMPILER_OPTION [, VARNAME]) |
||||
# ------------------------ |
||||
# Adds parameter to WARN_CFLAGS (or to $VARNAME) if the compiler supports it. |
||||
AC_DEFUN([UL_WARN_ADD], [ |
||||
m4_define([warnvarname], m4_default([$2],WARN_CFLAGS)) |
||||
AS_VAR_PUSHDEF([ul_Warn], [ul_cv_warn_$1])dnl |
||||
AC_CACHE_CHECK([whether compiler handles $1], m4_defn([ul_Warn]), [ |
||||
# store AC_LANG_WERROR status, then turn it on |
||||
save_ac_[]_AC_LANG_ABBREV[]_werror_flag="${ac_[]_AC_LANG_ABBREV[]_werror_flag}" |
||||
AC_LANG_WERROR |
||||
|
||||
ul_save_CPPFLAGS="$CPPFLAGS" |
||||
CPPFLAGS="-Werror ${CPPFLAGS} $1" |
||||
AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])], |
||||
[AS_VAR_SET(ul_Warn, [yes])], |
||||
[AS_VAR_SET(ul_Warn, [no])]) |
||||
# restore AC_LANG_WERROR |
||||
ac_[]_AC_LANG_ABBREV[]_werror_flag="${save_ac_[]_AC_LANG_ABBREV[]_werror_flag}" |
||||
|
||||
CPPFLAGS="$ul_save_CPPFLAGS" |
||||
]) |
||||
AS_VAR_IF(ul_Warn, [yes], [UL_AS_VAR_APPEND(warnvarname, [" $1"])]) |
||||
]) |
||||
|
@ -0,0 +1,103 @@
|
||||
#include "dump.h" |
||||
|
||||
static const struct option long_opts[] = { |
||||
{ "format", required_argument, NULL, 'f' }, |
||||
{ "root", required_argument, NULL, 'r' }, |
||||
{ NULL, 0, NULL, 0 }, |
||||
}; |
||||
|
||||
static const char *short_opts = "f:r:"; |
||||
|
||||
static int cmd_dump(int argc, char **argv) |
||||
{ |
||||
TOC_FORMAT format = TOC_FORMAT_PRETTY; |
||||
image_entry_t *list = NULL; |
||||
const char *root = NULL; |
||||
int ret = EXIT_FAILURE; |
||||
pkg_reader_t *rd; |
||||
int i; |
||||
|
||||
for (;;) { |
||||
i = getopt_long(argc, argv, short_opts, long_opts, NULL); |
||||
if (i == -1) |
||||
break; |
||||
|
||||
switch (i) { |
||||
case 'f': |
||||
if (strcmp(optarg, "sqfs") == 0) { |
||||
format = TOC_FORMAT_SQFS; |
||||
} else if (strcmp(optarg, "initrd") == 0) { |
||||
format = TOC_FORMAT_INITRD; |
||||
} else { |
||||
fprintf(stderr, "unknown format '%s'\n", |
||||
optarg); |
||||
tell_read_help(argv[0]); |
||||
return EXIT_FAILURE; |
||||
} |
||||
break; |
||||
case 'r': |
||||
root = optarg; |
||||
break; |
||||
default: |
||||
tell_read_help(argv[0]); |
||||
return EXIT_FAILURE; |
||||
} |
||||
} |
||||
|
||||
if (optind >= argc) { |
||||
fputs("missing argument: package file\n", stderr); |
||||
tell_read_help(argv[0]); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
rd = pkg_reader_open(argv[optind++]); |
||||
if (rd == NULL) |
||||
return EXIT_FAILURE; |
||||
|
||||
if (optind < argc) |
||||
fputs("warning: ignoring extra arguments\n", stderr); |
||||
|
||||
list = image_entry_list_from_package(rd); |
||||
if (list == NULL) |
||||
goto out; |
||||
|
||||
if (dump_toc(list, root, format)) |
||||
goto out; |
||||
|
||||
ret = EXIT_SUCCESS; |
||||
out: |
||||
if (list != NULL) |
||||
image_entry_free_list(list); |
||||
pkg_reader_close(rd); |
||||
return ret; |
||||
} |
||||
|
||||
static command_t dump = { |
||||
.cmd = "dump", |
||||
.usage = "[OPTIONS...] <pkgfile>", |
||||
.s_desc = "read dump all information from a package file", |
||||
.l_desc = |
||||
"Parse a package file and dump information it contains, such as the list of\n" |
||||
"files, et cetera.\n" |
||||
"\n" |
||||
"Possible options:\n" |
||||
" --format, -f <format> Specify what format to use for printing the table\n" |
||||
" of contents. Default is a pretty printed, human\n" |
||||
" readable version.\n" |
||||
"\n" |
||||
" If \"sqfs\" is specified, a squashfs pseudo file\n" |
||||
" is genareated for setting permissions bits and\n" |
||||
" ownership appropriately.\n" |
||||
"\n" |
||||
" If \"initrd\" is specified, a format appropriate\n" |
||||
" for Linux gen_init_cpio is produced.\n" |
||||
"\n" |
||||
" --root, -r <path> If a format is used that requires absoulute input\n" |
||||
" paths (e.g. initrd), prefix all file paths with\n" |
||||
" this. Can be used, for instance to unpack a\n" |
||||
" package to a staging directory and generate a\n" |
||||
" a listing for Linux CONFIG_INITRAMFS_SOURCE.\n", |
||||
.run_cmd = cmd_dump, |
||||
}; |
||||
|
||||
REGISTER_COMMAND(dump) |
@ -0,0 +1,24 @@
|
||||
#ifndef DUMP_H |
||||
#define DUMP_H |
||||
|
||||
#include <sys/stat.h> |
||||
#include <getopt.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "image_entry.h" |
||||
#include "pkgformat.h" |
||||
#include "pkgreader.h" |
||||
#include "command.h" |
||||
#include "util.h" |
||||
|
||||
typedef enum { |
||||
TOC_FORMAT_PRETTY = 0, |
||||
TOC_FORMAT_SQFS = 1, |
||||
TOC_FORMAT_INITRD = 2, |
||||
} TOC_FORMAT; |
||||
|
||||
int dump_toc(image_entry_t *list, const char *root, TOC_FORMAT format); |
||||
|
||||
#endif /* DUMP_H */ |
@ -0,0 +1,101 @@
|
||||
#include "dump.h" |
||||
|
||||
typedef int (*print_fun_t)(image_entry_t *ent, const char *root); |
||||
|
||||
static int print_pretty(image_entry_t *ent, const char *root) |
||||
{ |
||||
(void)root; |
||||
|
||||
printf("%s\n", ent->name); |
||||
printf("\towner: %d/%d\n", ent->uid, ent->gid); |
||||
printf("\tpermissions: 0%04o\n", ent->mode & (~S_IFMT)); |
||||
|
||||
fputs("\ttype: ", stdout); |
||||
|
||||
switch (ent->mode & S_IFMT) { |
||||
case S_IFLNK: |
||||
fputs("symlink\n", stdout); |
||||
|
||||
printf("\ttarget: %s\n\n", ent->data.symlink.target); |
||||
break; |
||||
case S_IFREG: |
||||
fputs("file\n", stdout); |
||||
|
||||
printf("\tsize: %lu\n", (unsigned long)ent->data.file.size); |
||||
printf("\tid: %u\n\n", (unsigned int)ent->data.file.id); |
||||
break; |
||||
case S_IFDIR: |
||||
fputs("directory\n\n", stdout); |
||||
break; |
||||
default: |
||||
fputs("unknown\n\n", stdout); |
||||
goto fail_type; |
||||
} |
||||
return 0; |
||||
fail_type: |
||||
fputs("unknown file type in table of contents\n", stderr); |
||||
return -1; |
||||
} |
||||
|
||||
static int print_sqfs(image_entry_t *ent, const char *root) |
||||
{ |
||||
mode_t mode = ent->mode & (~S_IFMT); |
||||
(void)root; |
||||
|
||||
if ((ent->mode & S_IFMT) == S_IFLNK) |
||||
mode = 0777; |
||||
|
||||
printf("%s m %o %u %u\n", ent->name, mode, |
||||
(unsigned int)ent->uid, |
||||
(unsigned int)ent->gid); |
||||
return 0; |
||||
} |
||||
|
||||
static int print_initrd(image_entry_t *ent, const char *root) |
||||
{ |
||||
mode_t mode = ent->mode & (~S_IFMT); |
||||
|
||||
switch (ent->mode & S_IFMT) { |
||||
case S_IFLNK: |
||||
printf("slink /%s %s", ent->name, ent->data.symlink.target); |
||||
mode = 0777; |
||||
break; |
||||
case S_IFREG: |
||||
printf("file /%s ", ent->name); |
||||
|
||||
if (root == NULL) { |
||||
fputs(ent->name, stdout); |
||||
} else { |
||||
printf("%s/%s", root, ent->name); |
||||
} |
||||
break; |
||||
case S_IFDIR: |
||||
printf("dir /%s", ent->name); |
||||
break; |
||||
default: |
||||
fputs("unknown file type in table of contents\n", stderr); |
||||
return -1; |
||||
} |
||||
|
||||
printf(" 0%o %u %u\n", mode, |
||||
(unsigned int)ent->uid, (unsigned int)ent->gid); |
||||
return 0; |
||||
} |
||||
|
||||
static print_fun_t printers[] = { |
||||
print_pretty, |
||||
print_sqfs, |
||||
print_initrd, |
||||
}; |
||||
|
||||
int dump_toc(image_entry_t *list, const char *root, TOC_FORMAT format) |
||||
{ |
||||
while (list != NULL) { |
||||
if (printers[format](list, root)) |
||||
return -1; |
||||
|
||||
list = list->next; |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,120 @@
|
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <ctype.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "command.h" |
||||
|
||||
extern char *__progname; |
||||
|
||||
static void pretty_print(const char *str, int padd, int len) |
||||
{ |
||||
int i, brk; |
||||
next: |
||||
while (isspace(*str) && *str != '\n') |
||||
++str; |
||||
|
||||
if (!(*str)) |
||||
return; |
||||
|
||||
if (*str == '\n') { |
||||
fputc('\n', stdout); |
||||
++str; |
||||
len = padd; |
||||
goto next; |
||||
} |
||||
|
||||
for (i = 0, brk = 0; str[i]; ++i) { |
||||
if (str[i] == '<' || str[i] == '[') |
||||
++brk; |
||||
if (str[i] == '>' || str[i] == ']') |
||||
--brk; |
||||
if (!brk && isspace(str[i])) |
||||
break; |
||||
} |
||||
|
||||
if ((len + i) < 80) { |
||||
fwrite(str, 1, i, stdout); |
||||
str += i; |
||||
len += i; |
||||
|
||||
if ((len + 1) < 80) { |
||||
fputc(' ', stdout); |
||||
++len; |
||||
goto next; |
||||
} |
||||
} |
||||
|
||||
printf("\n%*s", padd, ""); |
||||
len = padd; |
||||
goto next; |
||||
} |
||||
|
||||
static void print_cmd_usage(const char *cmd, const char *usage) |
||||
{ |
||||
int padd; |
||||
|
||||
padd = printf("Usage: %s %s ", __progname, cmd); |
||||
|
||||
if ((strlen(usage) + padd) < 80) { |
||||
fputs(usage, stdout); |
||||
return; |
||||
} |
||||
|
||||
pretty_print(usage, padd, padd); |
||||
} |
||||
|
||||
static int cmd_help(int argc, char **argv) |
||||
{ |
||||
const char *help; |
||||
command_t *cmd; |
||||
|
||||
if (argc < 2) |
||||
usage(EXIT_SUCCESS); |
||||
|
||||
if (argc > 2) { |
||||
fprintf(stderr, "Too many arguments\n\n" |
||||
"Usage: %s help <command>", __progname); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
cmd = command_by_name(argv[1]); |
||||
|
||||
if (cmd == NULL) { |
||||
fprintf(stderr, "Unknown command '%s'\n\n" |
||||
"Try `%s help' for a list of available commands\n", |
||||
argv[1], __progname); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
print_cmd_usage(cmd->cmd, cmd->usage); |
||||
fputs("\n\n", stdout); |
||||
|
||||
if (cmd->l_desc != NULL) { |
||||
fputs(cmd->l_desc, stdout); |
||||
} else { |
||||
help = cmd->s_desc; |
||||
|
||||
if (islower(*help)) { |
||||
fputc(toupper(*(help++)), stdout); |
||||
pretty_print(help, 0, 1); |
||||
} else { |
||||
pretty_print(help, 0, 0); |
||||
} |
||||
} |
||||
|
||||
fputc('\n', stdout); |
||||
return EXIT_SUCCESS; |
||||
} |
||||
|
||||
static command_t help = { |
||||
.cmd = "help", |
||||
.usage = "<command>", |
||||
.s_desc = "print a help text for a command", |
||||
.l_desc = |
||||
"Print a help text for a specified command. If no command is specified,\n" |
||||
"a generic help text and a list of available commands is shown.\n", |
||||
.run_cmd = cmd_help, |
||||
}; |
||||
|
||||
REGISTER_COMMAND(help) |
@ -0,0 +1,183 @@
|
||||
#include "pack.h" |
||||
|
||||
static char *skipspace(char *str) |
||||
{ |
||||
while (isspace(*str)) |
||||
++str; |
||||
return str; |
||||
} |
||||
|
||||
static void complain(const input_file_t *f, const char *msg) |
||||
{ |
||||
fprintf(stderr, "%s: %zu: %s\n", f->filename, f->linenum, msg); |
||||
} |
||||
|
||||
static void oom(input_file_t *f) |
||||
{ |
||||
complain(f, "out of memory"); |
||||
} |
||||
|
||||
static image_entry_t *filelist_mkentry(input_file_t *f, mode_t filetype) |
||||
{ |
||||
char *line = f->line; |
||||
image_entry_t *ent; |
||||
size_t i; |
||||
|
||||
ent = calloc(1, sizeof(*ent)); |
||||
if (ent == NULL) { |
||||
oom(f); |
||||
return NULL; |
||||
} |
||||
|
||||
/* name */ |
||||
for (i = 0; !isspace(line[i]) && line[i] != '\0'; ++i) |
||||
; |
||||
|
||||
if (!isspace(line[i])) { |
||||
complain(f, "expected space after file name"); |
||||
goto fail; |
||||
} |
||||
|
||||
ent->name = calloc(1, i + 1); |
||||
if (ent->name == NULL) { |
||||
oom(f); |
||||
goto fail; |
||||
} |
||||
|
||||
memcpy(ent->name, line, i); |
||||
if (canonicalize_name(ent->name)) { |
||||
complain(f, "invalid file name"); |
||||
goto fail; |
||||
} |
||||
|
||||
if (ent->name[0] == '\0') { |
||||
complain(f, "refusing to add entry for '/'"); |
||||
goto fail; |
||||
} |
||||
|
||||
if (strlen(ent->name) > 0xFFFF) { |
||||
complain(f, "name too long"); |
||||
goto fail; |
||||
} |
||||
|
||||
line = skipspace(line + i); |
||||
|
||||
/* mode */ |
||||
if (!isdigit(*line)) { |
||||
complain(f, "expected numeric mode after file name"); |
||||
goto fail; |
||||
} |
||||
|
||||
while (isdigit(*line)) { |
||||
if (*line > '7') { |
||||
complain(f, "mode must be octal number"); |
||||
goto fail; |
||||
} |
||||
ent->mode = (ent->mode << 3) | (*(line++) - '0'); |
||||
} |
||||
|
||||
if (!isspace(*line)) { |
||||
complain(f, "expected space after file mode"); |
||||
goto fail; |
||||
} |
||||
|
||||
line = skipspace(line); |
||||
|
||||
ent->mode = (ent->mode & ~S_IFMT) | filetype; |
||||
|
||||
/* uid */ |
||||
if (!isdigit(*line)) { |
||||
complain(f, "expected numeric UID after mode"); |
||||
goto fail; |
||||
} |
||||
|
||||
while (isdigit(*line)) |
||||
ent->uid = (ent->uid * 10) + (*(line++) - '0'); |
||||
|
||||
if (!isspace(*line)) { |
||||
complain(f, "expected space after UID"); |
||||
goto fail; |
||||
} |
||||
|
||||
line = skipspace(line); |
||||
|
||||
/* gid */ |
||||
if (!isdigit(*line)) { |
||||
complain(f, "expected numeric GID after UID"); |
||||
goto fail; |
||||
} |
||||
|
||||
while (isdigit(*line)) |
||||
ent->gid = (ent->gid * 10) + (*(line++) - '0'); |
||||
|
||||
line = skipspace(line); |
||||
|
||||
/* remove processed data */ |
||||
memmove(f->line, line, strlen(line) + 1); |
||||
return ent; |
||||
fail: |
||||
free(ent->name); |
||||
free(ent); |
||||
return NULL; |
||||
} |
||||
|
||||
image_entry_t *filelist_mkdir(input_file_t *f) |
||||
{ |
||||
return filelist_mkentry(f, S_IFDIR); |
||||
} |
||||
|
||||
image_entry_t *filelist_mkslink(input_file_t *f) |
||||
{ |
||||
image_entry_t *ent = filelist_mkentry(f, S_IFLNK); |
||||
|
||||
if (ent == NULL) |
||||
return NULL; |
||||
|
||||
ent->data.symlink.target = strdup(f->line); |
||||
if (ent->data.symlink.target == NULL) { |
||||
oom(f); |
||||
goto fail; |
||||
} |
||||
|
||||
if (strlen(ent->data.symlink.target) > 0xFFFF) { |
||||
complain(f, "symlink target too long"); |
||||
goto fail; |
||||
} |
||||
|
||||
return ent; |
||||
fail: |
||||
image_entry_free(ent); |
||||
return NULL; |
||||
} |
||||
|
||||
image_entry_t *filelist_mkfile(input_file_t *f) |
||||
{ |
||||
image_entry_t *ent = filelist_mkentry(f, S_IFREG); |
||||
struct stat sb; |
||||
|
||||
if (ent == NULL) |
||||
return NULL; |
||||
|
||||
ent->data.file.location = strdup(f->line); |
||||
if (ent->data.file.location == NULL) { |
||||
oom(f); |
||||
goto fail; |
||||
} |
||||
|
||||
if (stat(ent->data.file.location, &sb) != 0) { |
||||
perror(ent->data.file.location); |
||||
goto fail; |
||||
} |
||||
|
||||
if (sizeof(off_t) > sizeof(uint64_t) && |
||||
sb.st_size > (off_t)(~((uint64_t)0))) { |
||||
complain(f, "input file is too big"); |
||||
goto fail; |
||||
} |
||||
|
||||
ent->data.file.size = sb.st_size; |
||||
return ent; |
||||
fail: |
||||
image_entry_free(ent); |
||||
return NULL; |
||||
} |
@ -0,0 +1,130 @@
|
||||
#include "pack.h" |
||||
|
||||
static const struct { |
||||
const char *name; |
||||
image_entry_t *(*handle)(input_file_t *f); |
||||
} line_hooks[] = { |
||||
{ "file", filelist_mkfile }, |
||||
{ "dir", filelist_mkdir }, |
||||
{ "slink", filelist_mkslink }, |
||||
}; |
||||
|
||||
#define NUM_LINE_HOOKS (sizeof(line_hooks) / sizeof(line_hooks[0])) |
||||
|
||||
static int prefetch_line(input_file_t *f) |
||||
{ |
||||
char *line = NULL; |
||||
size_t n = 0; |
||||
ssize_t ret; |
||||
|
||||
free(f->line); |
||||
f->line = NULL; |
||||
|
||||
errno = 0; |
||||
ret = getline(&line, &n, f->f); |
||||
|
||||
if (ret < 0) { |
||||
if (errno != 0) { |
||||
perror(f->filename); |
||||
free(line); |
||||
return -1; |
||||
} |
||||
free(line); |
||||
return 1; |
||||
} |
||||
|
||||
n = strlen(line); |
||||
while (n >0 && isspace(line[n - 1])) |
||||
--n; |
||||
line[n] = '\0'; |
||||
|
||||
f->line = line; |
||||
f->linenum += 1; |
||||
return 0; |
||||
} |
||||
|
||||
static void cleanup_file(input_file_t *f) |
||||
{ |
||||
fclose(f->f); |
||||
free(f->line); |
||||
} |
||||
|
||||
static int open_file(input_file_t *f, const char *filename) |
||||
{ |
||||
memset(f, 0, sizeof(*f)); |
||||
|
||||
f->filename = filename; |
||||
f->f = fopen(filename, "r"); |
||||
|
||||
if (f->f == NULL) { |
||||
perror(f->filename); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
image_entry_t *filelist_read(const char *filename) |
||||
{ |
||||
image_entry_t *ent, *list = NULL; |
||||
input_file_t f; |
||||
size_t i, len; |
||||
char *ptr; |
||||
int ret; |
||||
|
||||
if (open_file(&f, filename)) |
||||
return NULL; |
||||
|
||||
for (;;) { |
||||
ret = prefetch_line(&f); |
||||
if (ret < 0) |
||||
goto fail; |
||||
if (ret > 0) |
||||
break; |
||||
|
||||
for (ptr = f.line; isspace(*ptr); ++ptr) |
||||
; |
||||
|
||||
if (*ptr == '\0' || *ptr == '#') |
||||
continue; |
||||
|
||||
for (i = 0; i < NUM_LINE_HOOKS; ++i) { |
||||
len = strlen(line_hooks[i].name); |
||||
|
||||
if (strncmp(ptr, line_hooks[i].name, len) != 0) |
||||
continue; |
||||
if (!isspace(ptr[len]) && ptr[len] != '\0') |
||||
continue; |
||||
for (ptr += len; isspace(*ptr); ++ptr) |
||||
; |
||||
memmove(f.line, ptr, strlen(ptr) + 1); |
||||
break; |
||||
} |
||||
|
||||
if (i == NUM_LINE_HOOKS) { |
||||
fprintf(stderr, "%s: %zu: unknown entry type\n", |
||||
f.filename, f.linenum); |
||||
goto fail; |
||||
} |
||||
|
||||
ent = line_hooks[i].handle(&f); |
||||
if (ent == NULL) |
||||
goto fail; |
||||
|
||||
ent->next = list; |
||||
list = ent; |
||||
} |
||||
|
||||
if (list == NULL) { |
||||
fprintf(stderr, "%s: does not contain any entries\n", |
||||
f.filename); |
||||
goto fail; |
||||
} |
||||
|
||||
cleanup_file(&f); |
||||
return list; |
||||
fail: |
||||
cleanup_file(&f); |
||||
image_entry_free_list(list); |
||||
return NULL; |
||||
} |
@ -0,0 +1,163 @@
|
||||
#include "pack.h" |
||||
|
||||
static const struct option long_opts[] = { |
||||
{ "toc-compressor", required_argument, NULL, 't' }, |
||||
{ "file-compressor", required_argument, NULL, 'f' }, |
||||
{ "file-list", required_argument, NULL, 'l' }, |
||||
{ "output", required_argument, NULL, 'o' }, |
||||
{ NULL, 0, NULL, 0 }, |
||||
}; |
||||
|
||||
static const char *short_opts = "t:f:l:o:"; |
||||
|
||||
static compressor_t *get_default_compressor(void) |
||||
{ |
||||
compressor_t *cmp; |
||||
|
||||
cmp = compressor_by_id(PKG_COMPRESSION_ZLIB); |
||||
if (cmp != NULL) |
||||
return cmp; |
||||
|
||||
cmp = compressor_by_id(PKG_COMPRESSION_NONE); |
||||
if (cmp != NULL) |
||||
return cmp; |
||||
|
||||
return cmp; |
||||
} |
||||
|
||||
static compressor_t *try_get_compressor(const char *name) |
||||
{ |
||||
compressor_t *cmp = compressor_by_name(name); |
||||
|
||||
if (cmp == NULL) |
||||
fprintf(stderr, "unkown compressor: %s\n", name); |
||||
|
||||
return cmp; |
||||
} |
||||
|
||||
static int alloc_file_ids(image_entry_t *list) |
||||
{ |
||||
image_entry_t *ent; |
||||
uint64_t file_id = 0; |
||||
|
||||
for (ent = list; ent != NULL; ent = ent->next) { |
||||
if (!S_ISREG(ent->mode)) |
||||
continue; |
||||
|
||||
if (file_id > 0x00000000FFFFFFFFUL) { |
||||
fprintf(stderr, "too many input files\n"); |
||||
return -1; |
||||
} |
||||
|
||||
ent->data.file.id = file_id++; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int cmd_pack(int argc, char **argv) |
||||
{ |
||||
const char *filelist = NULL, *filename = NULL; |
||||
compressor_t *cmp_toc, *cmp_files; |
||||
image_entry_t *list; |
||||
pkg_writer_t *wr; |
||||
int i; |
||||
|
||||
cmp_toc = get_default_compressor(); |
||||
cmp_files = cmp_toc; |
||||
|
||||
if (cmp_toc == NULL) { |
||||
fputs("no compressor implementations available\n", stderr); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
for (;;) { |
||||
i = getopt_long(argc, argv, short_opts, long_opts, NULL); |
||||
if (i == -1) |
||||
break; |
||||
|
||||
switch (i) { |
||||
case 't': |
||||
cmp_toc = try_get_compressor(optarg); |
||||
if (cmp_toc == NULL) |
||||
return EXIT_FAILURE; |
||||
break; |
||||
case 'f': |
||||
cmp_files = try_get_compressor(optarg); |
||||
if (cmp_files == NULL) |
||||
return EXIT_FAILURE; |
||||
break; |
||||
case 'l': |
||||
filelist = optarg; |
||||
break; |
||||
case 'o': |
||||
filename = optarg; |
||||
break; |
||||
default: |
||||
tell_read_help(argv[0]); |
||||
return EXIT_FAILURE; |
||||
} |
||||
} |
||||
|
||||
if (filename == NULL) { |
||||
fputs("missing argument: output package file\n", stderr); |
||||
tell_read_help(argv[0]); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
if (filelist == NULL) { |
||||
fputs("missing argument: input file list\n", stderr); |
||||
tell_read_help(argv[0]); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
if (optind < argc) |
||||
fputs("warning: ignoring extra arguments\n", stderr); |
||||
|
||||
list = filelist_read(filelist); |
||||
if (list == NULL) |
||||
return EXIT_FAILURE; |
||||
|
||||
list = image_entry_sort(list); |
||||
|
||||
if (alloc_file_ids(list)) |
||||
goto fail_fp; |
||||
|
||||
wr = pkg_writer_open(filename); |
||||
if (wr == NULL) |
||||
goto fail_fp; |
||||
|
||||
if (write_toc(wr, list, cmp_toc)) |
||||
goto fail; |
||||
|
||||
if (write_files(wr, list, cmp_files)) |
||||
goto fail; |
||||
|
||||
pkg_writer_close(wr); |
||||
image_entry_free_list(list); |
||||
return EXIT_SUCCESS; |
||||
fail: |
||||
pkg_writer_close(wr); |
||||
fail_fp: |
||||
image_entry_free_list(list); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
static command_t pack = { |
||||
.cmd = "pack", |
||||
.usage = "OPTIONS...", |
||||
.s_desc = "generate a package file", |
||||
.l_desc = |
||||
"Read a list of files from the given lists file and generate a package.\n" |
||||
"Possible options:\n" |
||||
" --file-list, -l <path> Specify a file containing a list of input files.\n" |
||||
" --output, -o <path> Specify the path of the resulting package file.\n" |
||||
"\n" |
||||
" --toc-compressor, -t <compressor>\n" |
||||
" --file-compressor, -f <compressor>\n" |
||||
" Specify what compressor to use for compressing the table of contents,\n" |
||||
" or for compressing files respectively.\n", |
||||
.run_cmd = cmd_pack, |
||||
}; |
||||
|
||||
REGISTER_COMMAND(pack) |
@ -0,0 +1,43 @@
|
||||
#ifndef PACK_H |
||||
#define PACK_H |
||||
|
||||
#include <sys/types.h> |
||||
#include <sys/stat.h> |
||||
#include <stdbool.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
#include <getopt.h> |
||||
#include <unistd.h> |
||||
#include <stdio.h> |
||||
#include <errno.h> |
||||
#include <ctype.h> |
||||
#include <fcntl.h> |
||||
|
||||
#include "image_entry.h" |
||||
#include "compressor.h" |
||||
#include "pkgformat.h" |
||||
#include "pkgwriter.h" |
||||
#include "command.h" |
||||
#include "util.h" |
||||
|
||||
typedef struct { |
||||
FILE *f; |
||||
char *line; |
||||
const char *filename; |
||||
size_t linenum; |
||||
} input_file_t; |
||||
|
||||
image_entry_t *filelist_mkdir(input_file_t *f); |
||||
|
||||
image_entry_t *filelist_mkslink(input_file_t *f); |
||||
|
||||
image_entry_t *filelist_mkfile(input_file_t *f); |
||||
|
||||
image_entry_t *filelist_read(const char *filename); |
||||
|
||||
int write_toc(pkg_writer_t *wr, image_entry_t *list, compressor_t *cmp); |
||||
|
||||
int write_files(pkg_writer_t *wr, image_entry_t *list, compressor_t *cmp); |
||||
|
||||
#endif /* PACK_H */ |
@ -0,0 +1,64 @@
|
||||
#include "pack.h" |
||||
|
||||
static int write_file(pkg_writer_t *wr, image_entry_t *ent) |
||||
{ |
||||
uint64_t offset = 0; |
||||
uint8_t buffer[1024]; |
||||
file_data_t fdata; |
||||
ssize_t ret; |
||||
int fd; |
||||
|
||||
memset(&fdata, 0, sizeof(fdata)); |
||||
|
||||
fdata.id = htole32(ent->data.file.id); |
||||
|
||||
if (pkg_writer_write_payload(wr, &fdata, sizeof(fdata))) |
||||
return -1; |
||||
|
||||
fd = open(ent->data.file.location, O_RDONLY); |
||||
|
||||
if (fd < 0) { |
||||
perror(ent->data.file.location); |
||||
return -1; |
||||
} |
||||
|
||||
while (offset < ent->data.file.size) { |
||||
ret = read_retry(fd, buffer, sizeof(buffer)); |
||||
|
||||
if (ret < 0) { |
||||
perror(ent->data.file.location); |
||||
goto fail_fd; |
||||
} |
||||
|
||||
if (ret == 0) |
||||
break; |
||||
|
||||
if (pkg_writer_write_payload(wr, buffer, ret)) |
||||
goto fail_fd; |
||||
|
||||
offset += (uint64_t)ret; |
||||
} |
||||
|
||||
close(fd); |
||||
return 0; |
||||
fail_fd: |
||||
close(fd); |
||||
return -1; |
||||
} |
||||
|
||||
int write_files(pkg_writer_t *wr, image_entry_t *list, compressor_t *cmp) |
||||
{ |
||||
if (pkg_writer_start_record(wr, PKG_MAGIC_DATA, cmp)) |
||||
return -1; |
||||
|
||||
while (list != NULL) { |
||||
if (S_ISREG(list->mode)) { |
||||
if (write_file(wr, list)) |
||||
return -1; |
||||
} |
||||
|
||||
list = list->next; |
||||
} |
||||
|
||||
return pkg_writer_end_record(wr); |
||||
} |
@ -0,0 +1,71 @@
|
||||
#include "pack.h" |
||||
|
||||
static int write_entry(pkg_writer_t *wr, image_entry_t *it) |
||||
{ |
||||
toc_entry_t ent; |
||||
size_t len; |
||||
|
||||
len = strlen(it->name); |
||||
|
||||
memset(&ent, 0, sizeof(ent)); |
||||
ent.mode = htole32(it->mode); |
||||
ent.uid = htole32(it->uid); |
||||
ent.gid = htole32(it->gid); |
||||
ent.path_length = htole16(len); |
||||
|
||||
if (pkg_writer_write_payload(wr, &ent, sizeof(ent))) |
||||
return -1; |
||||
|
||||
if (pkg_writer_write_payload(wr, it->name, len)) |
||||
return -1; |
||||
|
||||
switch (it->mode & S_IFMT) { |
||||
case S_IFREG: { |
||||
toc_file_extra_t file; |
||||
|
||||
memset(&file, 0, sizeof(file)); |
||||
file.size = htole64(it->data.file.size); |
||||
file.id = htole32(it->data.file.id); |
||||
|
||||
if (pkg_writer_write_payload(wr, &file, sizeof(file))) |
||||
return -1; |
||||
break; |
||||
} |
||||
case S_IFLNK: { |
||||
toc_symlink_extra_t sym; |
||||
|
||||
len = strlen(it->data.symlink.target); |
||||
|
||||
memset(&sym, 0, sizeof(sym)); |
||||
sym.target_length = htole16(len); |
||||
|
||||
if (pkg_writer_write_payload(wr, &sym, sizeof(sym))) |
||||
return -1; |
||||
|
||||
if (pkg_writer_write_payload(wr, it->data.symlink.target, len)) |
||||
return -1; |
||||
break; |
||||
} |
||||
case S_IFDIR: |
||||
break; |
||||
default: |
||||
assert(0); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int write_toc(pkg_writer_t *wr, image_entry_t *list, compressor_t *cmp) |
||||
{ |
||||
if (pkg_writer_start_record(wr, PKG_MAGIC_TOC, cmp)) |
||||
return -1; |
||||
|
||||
while (list != NULL) { |
||||
if (write_entry(wr, list)) |
||||
return -1; |
||||
|
||||
list = list->next; |
||||
} |
||||
|
||||
return pkg_writer_end_record(wr); |
||||
} |
@ -0,0 +1,26 @@
|
||||
#include "unpack.h" |
||||
|
||||
int create_hierarchy(image_entry_t *list) |
||||
{ |
||||
image_entry_t *ent; |
||||
|
||||
for (ent = list; ent != NULL; ent = ent->next) { |
||||
if (S_ISDIR(ent->mode)) { |
||||
if (mkdir(ent->name, 0755)) { |
||||
perror(ent->name); |
||||
return -1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (ent = list; ent != NULL; ent = ent->next) { |
||||
if (S_ISLNK(ent->mode)) { |
||||
if (symlink(ent->data.symlink.target, ent->name)) { |
||||
perror(ent->name); |
||||
return -1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |