mirror of
https://github.com/pygos/pkg-utils.git
synced 2024-12-30 16:40:52 +01:00
Initial commit
Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
commit
6ec11b532e
36 changed files with 2931 additions and 0 deletions
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
|
@ -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
|
50
Makefile.am
Normal file
50
Makefile.am
Normal file
|
@ -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
|
3
autogen.sh
Executable file
3
autogen.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
autoreconf --force --install --symlink
|
56
configure.ac
Normal file
56
configure.ac
Normal file
|
@ -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])
|
31
include/command.h
Normal file
31
include/command.h
Normal file
|
@ -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 */
|
43
include/compressor.h
Normal file
43
include/compressor.h
Normal file
|
@ -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 */
|
37
include/image_entry.h
Normal file
37
include/image_entry.h
Normal file
|
@ -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 */
|
56
include/pkgformat.h
Normal file
56
include/pkgformat.h
Normal file
|
@ -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 */
|
23
include/pkgreader.h
Normal file
23
include/pkgreader.h
Normal file
|
@ -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 */
|
20
include/pkgwriter.h
Normal file
20
include/pkgwriter.h
Normal file
|
@ -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 */
|
12
include/util.h
Normal file
12
include/util.h
Normal file
|
@ -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 */
|
40
m4/compiler.m4
Normal file
40
m4/compiler.m4
Normal file
|
@ -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"])])
|
||||
])
|
||||
|
103
main/cmd/dump/dump.c
Normal file
103
main/cmd/dump/dump.c
Normal file
|
@ -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)
|
24
main/cmd/dump/dump.h
Normal file
24
main/cmd/dump/dump.h
Normal file
|
@ -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 */
|
101
main/cmd/dump/dump_toc.c
Normal file
101
main/cmd/dump/dump_toc.c
Normal file
|
@ -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;
|
||||
}
|
120
main/cmd/help.c
Normal file
120
main/cmd/help.c
Normal file
|
@ -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)
|
183
main/cmd/pack/filelist.c
Normal file
183
main/cmd/pack/filelist.c
Normal file
|
@ -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;
|
||||
}
|
130
main/cmd/pack/filelist_read.c
Normal file
130
main/cmd/pack/filelist_read.c
Normal file
|
@ -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;
|
||||
}
|
163
main/cmd/pack/pack.c
Normal file
163
main/cmd/pack/pack.c
Normal file
|
@ -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)
|
43
main/cmd/pack/pack.h
Normal file
43
main/cmd/pack/pack.h
Normal file
|
@ -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 */
|
64
main/cmd/pack/write_files.c
Normal file
64
main/cmd/pack/write_files.c
Normal file
|
@ -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);
|
||||
}
|
71
main/cmd/pack/write_toc.c
Normal file
71
main/cmd/pack/write_toc.c
Normal file
|
@ -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);
|
||||
}
|
26
main/cmd/unpack/create_hierarchy.c
Normal file
26
main/cmd/unpack/create_hierarchy.c
Normal file
|
@ -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;
|
||||
}
|
34
main/cmd/unpack/mkdir_p.c
Normal file
34
main/cmd/unpack/mkdir_p.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "unpack.h"
|
||||
|
||||
int mkdir_p(const char *path)
|
||||
{
|
||||
size_t i, len;
|
||||
char *buffer;
|
||||
|
||||
while (*path == '/')
|
||||
++path;
|
||||
|
||||
if (*path == '\0')
|
||||
return 0;
|
||||
|
||||
len = strlen(path) + 1;
|
||||
buffer = alloca(len);
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (path[i] == '/' || path[i] == '\0') {
|
||||
buffer[i] = '\0';
|
||||
|
||||
if (mkdir(buffer, 0755) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
fprintf(stderr, "mkdir %s: %s\n",
|
||||
buffer, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer[i] = path[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
249
main/cmd/unpack/unpack.c
Normal file
249
main/cmd/unpack/unpack.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
#include "unpack.h"
|
||||
|
||||
|
||||
static const struct option long_opts[] = {
|
||||
{ "root", required_argument, NULL, 'r' },
|
||||
{ "no-chown", required_argument, NULL, 'o' },
|
||||
{ "no-chmod", required_argument, NULL, 'm' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
static const char *short_opts = "r:om";
|
||||
|
||||
static int set_root(const char *path)
|
||||
{
|
||||
if (mkdir_p(path))
|
||||
return -1;
|
||||
|
||||
if (chdir(path)) {
|
||||
fprintf(stderr, "cd %s: %s\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static image_entry_t *get_file_entry(image_entry_t *list, uint32_t id)
|
||||
{
|
||||
while (list != NULL) {
|
||||
if (S_ISREG(list->mode) && list->data.file.id == id)
|
||||
return list;
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int unpack_files(image_entry_t *list, pkg_reader_t *rd)
|
||||
{
|
||||
uint8_t buffer[2048];
|
||||
image_entry_t *meta;
|
||||
file_data_t frec;
|
||||
ssize_t ret;
|
||||
size_t diff;
|
||||
uint64_t i;
|
||||
int fd;
|
||||
|
||||
for (;;) {
|
||||
ret = pkg_reader_read_payload(rd, &frec, sizeof(frec));
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if ((size_t)ret < sizeof(frec))
|
||||
goto fail_trunc;
|
||||
|
||||
frec.id = le32toh(frec.id);
|
||||
|
||||
meta = get_file_entry(list, frec.id);
|
||||
if (meta == NULL) {
|
||||
fprintf(stderr, "%s: missing meta information for "
|
||||
"file %u\n", pkg_reader_get_filename(rd),
|
||||
(unsigned int)frec.id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open(meta->name, O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||
if (fd < 0) {
|
||||
perror(meta->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < meta->data.file.size; i += ret) {
|
||||
if ((meta->data.file.size - i) <
|
||||
(uint64_t)sizeof(buffer)) {
|
||||
diff = meta->data.file.size - i;
|
||||
} else {
|
||||
diff = sizeof(buffer);
|
||||
}
|
||||
|
||||
ret = pkg_reader_read_payload(rd, buffer, diff);
|
||||
if (ret < 0)
|
||||
goto fail_fd;
|
||||
if ((size_t)ret < diff)
|
||||
goto fail_trunc;
|
||||
|
||||
ret = write_retry(fd, buffer, diff);
|
||||
if (ret < 0) {
|
||||
perror(meta->name);
|
||||
goto fail_fd;
|
||||
}
|
||||
|
||||
if ((size_t)ret < diff) {
|
||||
fprintf(stderr, "%s: truncated write\n",
|
||||
pkg_reader_get_filename(rd));
|
||||
goto fail_fd;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail_fd:
|
||||
close(fd);
|
||||
return -1;
|
||||
fail_trunc:
|
||||
fprintf(stderr, "%s: truncated file data record\n",
|
||||
pkg_reader_get_filename(rd));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int change_permissions(image_entry_t *list, int flags)
|
||||
{
|
||||
while (list != NULL) {
|
||||
if (S_ISLNK(list->mode)) {
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(flags & FLAG_NO_CHMOD)) {
|
||||
if (chmod(list->name, list->mode)) {
|
||||
fprintf(stderr, "%s: chmod: %s\n", list->name,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & FLAG_NO_CHOWN)) {
|
||||
if (chown(list->name, list->uid, list->gid)) {
|
||||
fprintf(stderr, "%s: chown: %s\n", list->name,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_unpack(int argc, char **argv)
|
||||
{
|
||||
const char *root = NULL, *filename;
|
||||
image_entry_t *list = NULL;
|
||||
int i, ret, flags = 0;
|
||||
pkg_reader_t *rd;
|
||||
record_t *hdr;
|
||||
|
||||
for (;;) {
|
||||
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
||||
if (i == -1)
|
||||
break;
|
||||
|
||||
switch (i) {
|
||||
case 'r':
|
||||
root = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
flags |= FLAG_NO_CHOWN;
|
||||
break;
|
||||
case 'm':
|
||||
flags |= FLAG_NO_CHMOD;
|
||||
break;
|
||||
default:
|
||||
tell_read_help(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
fputs("missing argument: package file\n", stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
filename = argv[optind++];
|
||||
|
||||
if (optind < argc)
|
||||
fputs("warning: ignoring extra arguments\n", stderr);
|
||||
|
||||
rd = pkg_reader_open(filename);
|
||||
if (rd == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
list = image_entry_list_from_package(rd);
|
||||
if (list == NULL) {
|
||||
pkg_reader_close(rd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
list = image_entry_sort(list);
|
||||
|
||||
if (pkg_reader_rewind(rd))
|
||||
goto fail;
|
||||
|
||||
if (root != NULL && set_root(root) != 0)
|
||||
goto fail;
|
||||
|
||||
if (create_hierarchy(list))
|
||||
goto fail;
|
||||
|
||||
for (;;) {
|
||||
ret = pkg_reader_get_next_record(rd);
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
hdr = pkg_reader_current_record_header(rd);
|
||||
|
||||
if (hdr->magic == PKG_MAGIC_DATA) {
|
||||
if (unpack_files(list, rd))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (change_permissions(list, flags))
|
||||
goto fail;
|
||||
|
||||
image_entry_free_list(list);
|
||||
pkg_reader_close(rd);
|
||||
return EXIT_SUCCESS;
|
||||
fail:
|
||||
image_entry_free_list(list);
|
||||
pkg_reader_close(rd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static command_t unpack = {
|
||||
.cmd = "unpack",
|
||||
.usage = "[OPTIONS...] <pkgfile>",
|
||||
.s_desc = "unpack the contents of a package file",
|
||||
.l_desc =
|
||||
"The unpack command extracts the file hierarchy stored in a package into\n"
|
||||
"a destination directory (default: current working directory).\n"
|
||||
"\n"
|
||||
"Possible options:\n"
|
||||
" --root, -r <director> A root directory to unpack the package. Defaults\n"
|
||||
" to the current working directory if not set.\n"
|
||||
" --no-chown, -o Do not change ownership of the extracted data.\n"
|
||||
" Keep the uid/gid of the user who runs the program.\n"
|
||||
" --no-chmod, -m Do not change permission flags of the extarcted\n"
|
||||
" data. Use 0644 for all files and 0755 for all\n"
|
||||
" directories.\n",
|
||||
.run_cmd = cmd_unpack,
|
||||
};
|
||||
|
||||
REGISTER_COMMAND(unpack)
|
28
main/cmd/unpack/unpack.h
Normal file
28
main/cmd/unpack/unpack.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef UNPACK_H
|
||||
#define UNPACK_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "image_entry.h"
|
||||
#include "pkgreader.h"
|
||||
#include "command.h"
|
||||
#include "util.h"
|
||||
|
||||
enum {
|
||||
FLAG_NO_CHOWN = 0x01,
|
||||
FLAG_NO_CHMOD = 0x02,
|
||||
};
|
||||
|
||||
int create_hierarchy(image_entry_t *list);
|
||||
|
||||
int mkdir_p(const char *path);
|
||||
|
||||
#endif /* UNPACK_H */
|
70
main/command.c
Normal file
70
main/command.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "command.h"
|
||||
|
||||
extern char *__progname;
|
||||
|
||||
static command_t *commands;
|
||||
|
||||
void command_register(command_t *cmd)
|
||||
{
|
||||
cmd->next = commands;
|
||||
commands = cmd;
|
||||
}
|
||||
|
||||
command_t *command_by_name(const char *name)
|
||||
{
|
||||
command_t *cmd;
|
||||
|
||||
for (cmd = commands; cmd != NULL; cmd = cmd->next) {
|
||||
if (strcmp(cmd->cmd, name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void usage(int status)
|
||||
{
|
||||
FILE *stream = (status == EXIT_SUCCESS ? stdout : stderr);
|
||||
int padd = 0, len;
|
||||
command_t *cmd;
|
||||
|
||||
fprintf(stream, "usage: %s <command> [args...]\n\n"
|
||||
"Available commands:\n\n", __progname);
|
||||
|
||||
for (cmd = commands; cmd != NULL; cmd = cmd->next) {
|
||||
len = strlen(cmd->cmd);
|
||||
|
||||
padd = len > padd ? len : padd;
|
||||
}
|
||||
|
||||
for (cmd = commands; cmd != NULL; cmd = cmd->next) {
|
||||
fprintf(stream, "%*s - %s\n",
|
||||
(int)padd + 1, cmd->cmd, cmd->s_desc);
|
||||
}
|
||||
|
||||
fprintf(stream, "\nTry `%s help <command>' for more information "
|
||||
"on a specific command\n", __progname);
|
||||
exit(status);
|
||||
}
|
||||
|
||||
void tell_read_help(const char *cmd)
|
||||
{
|
||||
fprintf(stderr, "Try `%s help %s' for more information.\n",
|
||||
__progname, cmd);
|
||||
}
|
||||
|
||||
int check_arguments(const char *cmd, int argc, int minc, int maxc)
|
||||
{
|
||||
if (argc >= minc && argc <= maxc)
|
||||
return 0;
|
||||
|
||||
fprintf(stderr, "Too %s arguments for `%s'\n",
|
||||
argc > maxc ? "many" : "few", cmd);
|
||||
tell_read_help(cmd);
|
||||
return -1;
|
||||
}
|
31
main/compressor.c
Normal file
31
main/compressor.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "compressor.h"
|
||||
|
||||
static compressor_t *list = NULL;
|
||||
|
||||
void compressor_register(compressor_t *compressor)
|
||||
{
|
||||
compressor->next = list;
|
||||
list = compressor;
|
||||
}
|
||||
|
||||
compressor_t *compressor_by_name(const char *name)
|
||||
{
|
||||
compressor_t *it = list;
|
||||
|
||||
while (it != NULL && strcmp(it->name, name) != 0)
|
||||
it = it->next;
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
compressor_t *compressor_by_id(PKG_COMPRESSION id)
|
||||
{
|
||||
compressor_t *it = list;
|
||||
|
||||
while (it != NULL && it->id != id)
|
||||
it = it->next;
|
||||
|
||||
return it;
|
||||
}
|
90
main/compressors/none.c
Normal file
90
main/compressors/none.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "compressor.h"
|
||||
|
||||
#define CHUNK_SIZE 16384
|
||||
|
||||
typedef struct {
|
||||
compressor_stream_t base;
|
||||
uint8_t chunk[CHUNK_SIZE];
|
||||
size_t used;
|
||||
} dummy_stream_t;
|
||||
|
||||
static ssize_t dummy_write(compressor_stream_t *base,
|
||||
const uint8_t *in, size_t size)
|
||||
{
|
||||
dummy_stream_t *dummy = (dummy_stream_t *)base;
|
||||
|
||||
if (size > (CHUNK_SIZE - dummy->used))
|
||||
size = CHUNK_SIZE - dummy->used;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(dummy->chunk + dummy->used, in, size);
|
||||
dummy->used += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dummy_read(compressor_stream_t *base,
|
||||
uint8_t *out, size_t size)
|
||||
{
|
||||
dummy_stream_t *dummy = (dummy_stream_t *)base;
|
||||
|
||||
if (size > dummy->used)
|
||||
size = dummy->used;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(out, dummy->chunk, size);
|
||||
|
||||
if (size < dummy->used) {
|
||||
memmove(dummy->chunk, dummy->chunk + size,
|
||||
dummy->used - size);
|
||||
}
|
||||
|
||||
dummy->used -= size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static void dummy_flush(compressor_stream_t *base)
|
||||
{
|
||||
(void)base;
|
||||
}
|
||||
|
||||
static void dummy_destroy(compressor_stream_t *base)
|
||||
{
|
||||
free(base);
|
||||
}
|
||||
|
||||
static compressor_stream_t *create_dummy_stream(compressor_t *cmp)
|
||||
{
|
||||
dummy_stream_t *dummy = calloc(1, sizeof(*dummy));
|
||||
compressor_stream_t *base;
|
||||
(void)cmp;
|
||||
|
||||
if (dummy == NULL) {
|
||||
perror("creating dummy compressor stream");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
base = (compressor_stream_t *)dummy;
|
||||
base->write = dummy_write;
|
||||
base->read = dummy_read;
|
||||
base->flush = dummy_flush;
|
||||
base->destroy = dummy_destroy;
|
||||
return base;
|
||||
}
|
||||
|
||||
static compressor_t none = {
|
||||
.name = "none",
|
||||
.id = PKG_COMPRESSION_NONE,
|
||||
.compression_stream = create_dummy_stream,
|
||||
.uncompression_stream = create_dummy_stream,
|
||||
};
|
||||
|
||||
REGISTER_COMPRESSOR(none)
|
168
main/compressors/zlib.c
Normal file
168
main/compressors/zlib.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "compressor.h"
|
||||
|
||||
#define CHUNK_SIZE 16384
|
||||
|
||||
typedef struct {
|
||||
compressor_stream_t base;
|
||||
z_stream strm;
|
||||
uint8_t chunk[CHUNK_SIZE];
|
||||
int used;
|
||||
int flush_mode;
|
||||
bool compress;
|
||||
bool eof;
|
||||
bool error;
|
||||
} zlib_stream_t;
|
||||
|
||||
static ssize_t zlib_write(compressor_stream_t *base,
|
||||
const uint8_t *in, size_t size)
|
||||
{
|
||||
zlib_stream_t *zlib = (zlib_stream_t *)base;
|
||||
|
||||
if (size > (size_t)(CHUNK_SIZE - zlib->used))
|
||||
size = CHUNK_SIZE - zlib->used;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(zlib->chunk + zlib->used, in, size);
|
||||
zlib->used += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t zlib_read(compressor_stream_t *base,
|
||||
uint8_t *out, size_t size)
|
||||
{
|
||||
zlib_stream_t *zlib = (zlib_stream_t *)base;
|
||||
int ret, have, total = 0, processed;
|
||||
|
||||
if (zlib->error)
|
||||
return -1;
|
||||
if (zlib->eof)
|
||||
return 0;
|
||||
|
||||
zlib->strm.avail_in = zlib->used;
|
||||
zlib->strm.next_in = zlib->chunk;
|
||||
|
||||
do {
|
||||
zlib->strm.avail_out = size;
|
||||
zlib->strm.next_out = out;
|
||||
|
||||
if (zlib->compress) {
|
||||
ret = deflate(&zlib->strm, zlib->flush_mode);
|
||||
} else {
|
||||
ret = inflate(&zlib->strm, zlib->flush_mode);
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case Z_STREAM_END:
|
||||
zlib->eof = true;
|
||||
break;
|
||||
case Z_BUF_ERROR:
|
||||
total += (size - zlib->strm.avail_out);
|
||||
goto out_remove;
|
||||
case Z_OK:
|
||||
break;
|
||||
default:
|
||||
zlib->error = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
have = size - zlib->strm.avail_out;
|
||||
|
||||
out += have;
|
||||
size -= have;
|
||||
total += have;
|
||||
} while (zlib->strm.avail_out == 0 && !zlib->eof);
|
||||
out_remove:
|
||||
processed = zlib->used - zlib->strm.avail_in;
|
||||
|
||||
if (processed < zlib->used) {
|
||||
memmove(zlib->chunk, zlib->chunk + processed,
|
||||
zlib->used - processed);
|
||||
}
|
||||
|
||||
zlib->used -= processed;
|
||||
return total;
|
||||
}
|
||||
|
||||
static void zlib_flush(compressor_stream_t *base)
|
||||
{
|
||||
zlib_stream_t *zlib = (zlib_stream_t *)base;
|
||||
|
||||
zlib->flush_mode = Z_FINISH;
|
||||
}
|
||||
|
||||
static void zlib_destroy(compressor_stream_t *base)
|
||||
{
|
||||
zlib_stream_t *zlib = (zlib_stream_t *)base;
|
||||
|
||||
if (zlib->compress) {
|
||||
deflateEnd(&zlib->strm);
|
||||
} else {
|
||||
inflateEnd(&zlib->strm);
|
||||
}
|
||||
|
||||
free(zlib);
|
||||
}
|
||||
|
||||
static compressor_stream_t *create_stream(bool compress)
|
||||
{
|
||||
zlib_stream_t *zlib = calloc(1, sizeof(*zlib));
|
||||
compressor_stream_t *base;
|
||||
int ret;
|
||||
|
||||
if (zlib == NULL) {
|
||||
perror("creating zlib stream");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
zlib->flush_mode = Z_NO_FLUSH;
|
||||
zlib->compress = compress;
|
||||
|
||||
base = (compressor_stream_t *)zlib;
|
||||
base->write = zlib_write;
|
||||
base->read = zlib_read;
|
||||
base->flush = zlib_flush;
|
||||
base->destroy = zlib_destroy;
|
||||
|
||||
if (compress) {
|
||||
ret = deflateInit(&zlib->strm, Z_BEST_COMPRESSION);
|
||||
} else {
|
||||
ret = inflateInit(&zlib->strm);
|
||||
}
|
||||
|
||||
if (ret != Z_OK) {
|
||||
fputs("internal error creating zlib stream\n", stderr);
|
||||
free(zlib);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
static compressor_stream_t *zlib_compress(compressor_t *cmp)
|
||||
{
|
||||
(void)cmp;
|
||||
return create_stream(true);
|
||||
}
|
||||
|
||||
static compressor_stream_t *zlib_uncompress(compressor_t *cmp)
|
||||
{
|
||||
(void)cmp;
|
||||
return create_stream(false);
|
||||
}
|
||||
|
||||
static compressor_t zlib = {
|
||||
.name = "zlib",
|
||||
.id = PKG_COMPRESSION_ZLIB,
|
||||
.compression_stream = zlib_compress,
|
||||
.uncompression_stream = zlib_uncompress,
|
||||
};
|
||||
|
||||
REGISTER_COMPRESSOR(zlib)
|
204
main/image_entry.c
Normal file
204
main/image_entry.c
Normal file
|
@ -0,0 +1,204 @@
|
|||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "image_entry.h"
|
||||
#include "util.h"
|
||||
|
||||
void image_entry_free(image_entry_t *ent)
|
||||
{
|
||||
if (ent != NULL) {
|
||||
switch (ent->mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
free(ent->data.file.location);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
free(ent->data.symlink.target);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(ent->name);
|
||||
free(ent);
|
||||
}
|
||||
}
|
||||
|
||||
void image_entry_free_list(image_entry_t *list)
|
||||
{
|
||||
image_entry_t *ent;
|
||||
|
||||
while (list != NULL) {
|
||||
ent = list;
|
||||
list = list->next;
|
||||
|
||||
image_entry_free(ent);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_extra(pkg_reader_t *pkg, image_entry_t *ent)
|
||||
{
|
||||
switch (ent->mode & S_IFMT) {
|
||||
case S_IFLNK: {
|
||||
toc_symlink_extra_t extra;
|
||||
char *path;
|
||||
int ret;
|
||||
|
||||
ret = pkg_reader_read_payload(pkg, &extra, sizeof(extra));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if ((size_t)ret < sizeof(extra))
|
||||
goto fail_trunc;
|
||||
|
||||
extra.target_length = le16toh(extra.target_length);
|
||||
|
||||
path = malloc(extra.target_length + 1);
|
||||
if (path == NULL)
|
||||
goto fail_oom;
|
||||
|
||||
ret = pkg_reader_read_payload(pkg, path, extra.target_length);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if ((size_t)ret < extra.target_length)
|
||||
goto fail_trunc;
|
||||
|
||||
path[extra.target_length] = '\0';
|
||||
ent->data.symlink.target = path;
|
||||
break;
|
||||
}
|
||||
case S_IFREG: {
|
||||
toc_file_extra_t extra;
|
||||
int ret;
|
||||
|
||||
ret = pkg_reader_read_payload(pkg, &extra, sizeof(extra));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if ((size_t)ret < sizeof(extra))
|
||||
goto fail_trunc;
|
||||
|
||||
ent->data.file.size = le64toh(extra.size);
|
||||
ent->data.file.id = le32toh(extra.id);
|
||||
break;
|
||||
}
|
||||
case S_IFDIR:
|
||||
break;
|
||||
default:
|
||||
goto fail_unknown;
|
||||
}
|
||||
return 0;
|
||||
fail_unknown:
|
||||
fprintf(stderr, "%s: unsupported file type in table of contents\n",
|
||||
pkg_reader_get_filename(pkg));
|
||||
return -1;
|
||||
fail_oom:
|
||||
fputs("out of memory\n", stderr);
|
||||
return -1;
|
||||
fail_trunc:
|
||||
fprintf(stderr, "%s: truncated extra data in table of contents\n",
|
||||
pkg_reader_get_filename(pkg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
image_entry_t *image_entry_list_from_package(pkg_reader_t *pkg)
|
||||
{
|
||||
image_entry_t *list = NULL, *end = NULL, *imgent;
|
||||
toc_entry_t ent;
|
||||
record_t *hdr;
|
||||
ssize_t ret;
|
||||
char *path;
|
||||
|
||||
if (pkg_reader_rewind(pkg))
|
||||
return NULL;
|
||||
|
||||
do {
|
||||
if (pkg_reader_get_next_record(pkg) <= 0)
|
||||
return NULL;
|
||||
|
||||
hdr = pkg_reader_current_record_header(pkg);
|
||||
} while (hdr->magic != PKG_MAGIC_TOC);
|
||||
|
||||
for (;;) {
|
||||
ret = pkg_reader_read_payload(pkg, &ent, sizeof(ent));
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if ((size_t)ret < sizeof(ent))
|
||||
goto fail_trunc;
|
||||
|
||||
ent.mode = le32toh(ent.mode);
|
||||
ent.uid = le32toh(ent.uid);
|
||||
ent.gid = le32toh(ent.gid);
|
||||
ent.path_length = le16toh(ent.path_length);
|
||||
|
||||
path = malloc(ent.path_length + 1);
|
||||
if (path == NULL)
|
||||
goto fail_oom;
|
||||
|
||||
ret = pkg_reader_read_payload(pkg, path, ent.path_length);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if ((size_t)ret < ent.path_length)
|
||||
goto fail_trunc;
|
||||
path[ent.path_length] = '\0';
|
||||
|
||||
if (canonicalize_name(path)) {
|
||||
fprintf(stderr,
|
||||
"%s: invalid file path '%s' in package\n",
|
||||
pkg_reader_get_filename(pkg), path);
|
||||
free(path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
imgent = calloc(1, sizeof(*imgent));
|
||||
if (imgent == NULL) {
|
||||
free(path);
|
||||
goto fail_oom;
|
||||
}
|
||||
|
||||
imgent->name = path;
|
||||
imgent->mode = ent.mode;
|
||||
imgent->uid = ent.uid;
|
||||
imgent->gid = ent.gid;
|
||||
|
||||
if (read_extra(pkg, imgent)) {
|
||||
image_entry_free(imgent);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (list) {
|
||||
end->next = imgent;
|
||||
end = imgent;
|
||||
} else {
|
||||
list = end = imgent;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ret = pkg_reader_get_next_record(pkg);
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
hdr = pkg_reader_current_record_header(pkg);
|
||||
if (hdr->magic == PKG_MAGIC_TOC)
|
||||
goto fail_multi;
|
||||
}
|
||||
|
||||
return list;
|
||||
fail_oom:
|
||||
fputs("out of memory\n", stderr);
|
||||
goto fail;
|
||||
fail_trunc:
|
||||
fprintf(stderr, "%s: truncated entry in table of contents\n",
|
||||
pkg_reader_get_filename(pkg));
|
||||
goto fail;
|
||||
fail_multi:
|
||||
fprintf(stderr, "%s: multiple table of contents entries found\n",
|
||||
pkg_reader_get_filename(pkg));
|
||||
goto fail;
|
||||
fail:
|
||||
image_entry_free_list(list);
|
||||
return NULL;
|
||||
}
|
69
main/image_entry_sort.c
Normal file
69
main/image_entry_sort.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "image_entry.h"
|
||||
|
||||
static int compare_ent(image_entry_t *a, image_entry_t *b)
|
||||
{
|
||||
if (S_ISDIR(a->mode)) {
|
||||
if (S_ISDIR(b->mode))
|
||||
goto out_len;
|
||||
return -1;
|
||||
}
|
||||
if (S_ISDIR(b->mode))
|
||||
return 1;
|
||||
|
||||
if (S_ISREG(a->mode)) {
|
||||
if (S_ISREG(b->mode)) {
|
||||
if (a->data.file.size > b->data.file.size)
|
||||
return 1;
|
||||
if (a->data.file.size < b->data.file.size)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (S_ISREG(b->mode))
|
||||
return 1;
|
||||
out_len:
|
||||
return (int)strlen(a->name) - (int)strlen(b->name);
|
||||
}
|
||||
|
||||
static image_entry_t *insert_sorted(image_entry_t *list, image_entry_t *ent)
|
||||
{
|
||||
image_entry_t *it, *prev;
|
||||
|
||||
if (list == NULL || compare_ent(list, ent) > 0) {
|
||||
ent->next = list;
|
||||
return ent;
|
||||
}
|
||||
|
||||
it = list->next;
|
||||
prev = list;
|
||||
|
||||
while (it != NULL) {
|
||||
if (compare_ent(it, ent) > 0)
|
||||
break;
|
||||
|
||||
prev = it;
|
||||
it = it->next;
|
||||
}
|
||||
|
||||
prev->next = ent;
|
||||
ent->next = it;
|
||||
return list;
|
||||
}
|
||||
|
||||
image_entry_t *image_entry_sort(image_entry_t *list)
|
||||
{
|
||||
image_entry_t *sorted = NULL, *ent;
|
||||
|
||||
while (list != NULL) {
|
||||
ent = list;
|
||||
list = list->next;
|
||||
|
||||
sorted = insert_sorted(sorted, ent);
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
21
main/pkg.c
Normal file
21
main/pkg.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "command.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
command_t *cmd;
|
||||
|
||||
if (argc < 2)
|
||||
usage(EXIT_SUCCESS);
|
||||
|
||||
cmd = command_by_name(argv[1]);
|
||||
|
||||
if (cmd == NULL) {
|
||||
fprintf(stderr, "Unknown command '%s'\n\n", argv[1]);
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return cmd->run_cmd(argc - 1, argv + 1);
|
||||
}
|
267
main/pkgreader.c
Normal file
267
main/pkgreader.c
Normal file
|
@ -0,0 +1,267 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "pkgreader.h"
|
||||
#include "compressor.h"
|
||||
|
||||
struct pkg_reader_t {
|
||||
int fd;
|
||||
bool have_eof;
|
||||
bool have_error;
|
||||
uint64_t offset_compressed;
|
||||
uint64_t offset_raw;
|
||||
compressor_stream_t *stream;
|
||||
const char *path;
|
||||
|
||||
record_t current;
|
||||
};
|
||||
|
||||
static int read_header(pkg_reader_t *rd)
|
||||
{
|
||||
ssize_t diff = read_retry(rd->fd, &rd->current, sizeof(rd->current));
|
||||
|
||||
if (diff == 0) {
|
||||
rd->have_eof = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (diff < 0)
|
||||
goto fail_io;
|
||||
|
||||
if ((size_t)diff < sizeof(rd->current))
|
||||
goto fail_trunc;
|
||||
|
||||
rd->offset_raw = 0;
|
||||
rd->offset_compressed = 0;
|
||||
|
||||
rd->current.magic = le32toh(rd->current.magic);
|
||||
rd->current.compressed_size = le64toh(rd->current.compressed_size);
|
||||
rd->current.raw_size = le64toh(rd->current.raw_size);
|
||||
return 1;
|
||||
fail_trunc:
|
||||
rd->have_error = true;
|
||||
fprintf(stderr, "%s: package file seems to be truncated\n", rd->path);
|
||||
return -1;
|
||||
fail_io:
|
||||
rd->have_error = true;
|
||||
fprintf(stderr, "%s: reading from package file: %s\n",
|
||||
rd->path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int prefetch_compressed(pkg_reader_t *rd)
|
||||
{
|
||||
uint8_t buffer[1024];
|
||||
compressor_t *cmp;
|
||||
ssize_t ret;
|
||||
size_t diff;
|
||||
|
||||
if (rd->stream == NULL) {
|
||||
cmp = compressor_by_id(rd->current.compression);
|
||||
|
||||
if (cmp == NULL)
|
||||
goto fail_comp;
|
||||
|
||||
rd->stream = cmp->uncompression_stream(cmp);
|
||||
if (rd->stream == NULL) {
|
||||
rd->have_error = true;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
while (rd->offset_compressed < rd->current.compressed_size) {
|
||||
diff = rd->current.compressed_size - rd->offset_compressed;
|
||||
|
||||
if (diff > sizeof(buffer))
|
||||
diff = sizeof(buffer);
|
||||
|
||||
ret = read_retry(rd->fd, buffer, diff);
|
||||
if (ret < 0)
|
||||
goto fail_io;
|
||||
if ((size_t)ret < diff)
|
||||
goto fail_trunc;
|
||||
|
||||
ret = rd->stream->write(rd->stream, buffer, diff);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
rd->offset_compressed += ret;
|
||||
|
||||
if ((size_t)ret < diff) {
|
||||
if (lseek(rd->fd, -((long)(diff - ret)),
|
||||
SEEK_CUR) == -1)
|
||||
goto fail_io;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail_trunc:
|
||||
rd->have_error = true;
|
||||
fprintf(stderr, "%s: truncated record in package file\n", rd->path);
|
||||
return -1;
|
||||
fail_io:
|
||||
rd->have_error = true;
|
||||
fprintf(stderr, "%s: reading from package file: %s\n",
|
||||
rd->path, strerror(errno));
|
||||
return -1;
|
||||
fail_comp:
|
||||
fprintf(stderr, "%s: package uses unsupported compression\n",
|
||||
rd->path);
|
||||
rd->have_error = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkg_reader_t *pkg_reader_open(const char *path)
|
||||
{
|
||||
pkg_reader_t *rd = calloc(1, sizeof(*rd));
|
||||
int ret;
|
||||
|
||||
if (rd == NULL) {
|
||||
fputs("out of memory\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rd->path = path;
|
||||
rd->fd = open(path, O_RDONLY);
|
||||
if (rd->fd < 0) {
|
||||
perror(path);
|
||||
free(rd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = read_header(rd);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if (ret == 0 || rd->current.magic != PKG_MAGIC_HEADER)
|
||||
goto fail_header;
|
||||
|
||||
return rd;
|
||||
fail_header:
|
||||
fprintf(stderr, "%s: missing package header\n", path);
|
||||
fail:
|
||||
pkg_reader_close(rd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pkg_reader_close(pkg_reader_t *rd)
|
||||
{
|
||||
if (rd->stream != NULL)
|
||||
rd->stream->destroy(rd->stream);
|
||||
|
||||
close(rd->fd);
|
||||
free(rd);
|
||||
}
|
||||
|
||||
int pkg_reader_get_next_record(pkg_reader_t *rd)
|
||||
{
|
||||
uint64_t skip;
|
||||
int ret;
|
||||
|
||||
if (rd->have_eof)
|
||||
return 0;
|
||||
|
||||
if (rd->have_error)
|
||||
return -1;
|
||||
|
||||
skip = rd->current.compressed_size - rd->offset_compressed;
|
||||
|
||||
if (lseek(rd->fd, skip, SEEK_CUR) == -1)
|
||||
goto fail_io;
|
||||
|
||||
if (rd->stream != NULL) {
|
||||
rd->stream->destroy(rd->stream);
|
||||
rd->stream = NULL;
|
||||
}
|
||||
|
||||
ret = read_header(rd);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
if (rd->current.magic == PKG_MAGIC_HEADER)
|
||||
goto fail_second_hdr;
|
||||
|
||||
return 1;
|
||||
fail_io:
|
||||
rd->have_error = true;
|
||||
fprintf(stderr, "%s: reading from packet file: %s\n",
|
||||
rd->path, strerror(errno));
|
||||
return -1;
|
||||
fail_second_hdr:
|
||||
rd->have_error = true;
|
||||
fprintf(stderr, "%s: found extra header record inside package\n",
|
||||
rd->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
record_t *pkg_reader_current_record_header(pkg_reader_t *rd)
|
||||
{
|
||||
return (rd->have_error || rd->have_eof) ? NULL : &rd->current;
|
||||
}
|
||||
|
||||
ssize_t pkg_reader_read_payload(pkg_reader_t *rd, void *out, size_t size)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
if (rd->have_error)
|
||||
return -1;
|
||||
|
||||
if (rd->have_eof || rd->offset_raw == rd->current.raw_size)
|
||||
return 0;
|
||||
|
||||
if (prefetch_compressed(rd))
|
||||
return -1;
|
||||
|
||||
if (size >= (rd->current.raw_size - rd->offset_raw))
|
||||
rd->stream->flush(rd->stream);
|
||||
|
||||
ret = rd->stream->read(rd->stream, out, size);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
rd->offset_raw += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pkg_reader_rewind(pkg_reader_t *rd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (lseek(rd->fd, 0, SEEK_SET) == -1) {
|
||||
perror(rd->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rd->stream != NULL) {
|
||||
rd->stream->destroy(rd->stream);
|
||||
rd->stream = NULL;
|
||||
}
|
||||
|
||||
rd->have_eof = false;
|
||||
rd->have_error = false;
|
||||
|
||||
ret = read_header(rd);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (ret == 0 || rd->current.magic != PKG_MAGIC_HEADER)
|
||||
goto fail_header;
|
||||
|
||||
return 0;
|
||||
fail_header:
|
||||
fprintf(stderr, "%s: missing package header\n", rd->path);
|
||||
fail:
|
||||
rd->have_error = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *pkg_reader_get_filename(pkg_reader_t *rd)
|
||||
{
|
||||
return rd->path;
|
||||
}
|
181
main/pkgwriter.c
Normal file
181
main/pkgwriter.c
Normal file
|
@ -0,0 +1,181 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "pkgwriter.h"
|
||||
#include "util.h"
|
||||
|
||||
struct pkg_writer_t {
|
||||
const char *path;
|
||||
int fd;
|
||||
|
||||
off_t start;
|
||||
record_t current;
|
||||
compressor_stream_t *stream;
|
||||
};
|
||||
|
||||
static int write_header(pkg_writer_t *wr)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
wr->current.magic = htole32(wr->current.magic);
|
||||
wr->current.compressed_size = htole64(wr->current.compressed_size);
|
||||
wr->current.raw_size = htole64(wr->current.raw_size);
|
||||
|
||||
ret = write_retry(wr->fd, &wr->current, sizeof(wr->current));
|
||||
if (ret < 0)
|
||||
goto fail_fop;
|
||||
|
||||
if ((size_t)ret < sizeof(wr->current)) {
|
||||
fprintf(stderr,
|
||||
"record header was truncated while writing to %s\n",
|
||||
wr->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail_fop:
|
||||
perror(wr->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int flush_to_file(pkg_writer_t *wr)
|
||||
{
|
||||
uint8_t buffer[2048];
|
||||
ssize_t count, ret;
|
||||
|
||||
for (;;) {
|
||||
count = wr->stream->read(wr->stream, buffer, sizeof(buffer));
|
||||
if (count == 0)
|
||||
break;
|
||||
if (count < 0) {
|
||||
fprintf(stderr, "%s: error compressing data\n",
|
||||
wr->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = write_retry(wr->fd, buffer, count);
|
||||
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "%s: writing to package file: %s\n",
|
||||
wr->path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret < count) {
|
||||
fprintf(stderr,
|
||||
"%s: data written to file was truncated\n",
|
||||
wr->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wr->current.compressed_size += count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pkg_writer_t *pkg_writer_open(const char *path)
|
||||
{
|
||||
pkg_writer_t *wr = calloc(1, sizeof(*wr));
|
||||
|
||||
if (wr == NULL) {
|
||||
fputs("out of memory\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wr->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||
if (wr->fd == -1) {
|
||||
perror(path);
|
||||
free(wr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wr->path = path;
|
||||
|
||||
wr->current.magic = PKG_MAGIC_HEADER;
|
||||
wr->current.compression = PKG_COMPRESSION_NONE;
|
||||
if (write_header(wr)) {
|
||||
close(wr->fd);
|
||||
free(wr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wr;
|
||||
}
|
||||
|
||||
void pkg_writer_close(pkg_writer_t *wr)
|
||||
{
|
||||
close(wr->fd);
|
||||
free(wr);
|
||||
}
|
||||
|
||||
int pkg_writer_start_record(pkg_writer_t *wr, uint32_t magic,
|
||||
compressor_t *cmp)
|
||||
{
|
||||
wr->start = lseek(wr->fd, 0, SEEK_CUR);
|
||||
if (wr->start == -1) {
|
||||
perror(wr->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&wr->current, 0, sizeof(wr->current));
|
||||
if (write_header(wr))
|
||||
return -1;
|
||||
|
||||
wr->stream = cmp->compression_stream(cmp);
|
||||
if (wr->stream == NULL)
|
||||
return -1;
|
||||
|
||||
wr->current.magic = magic;
|
||||
wr->current.compression = cmp->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pkg_writer_write_payload(pkg_writer_t *wr, void *data, size_t size)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
while (size > 0) {
|
||||
ret = wr->stream->write(wr->stream, data, size);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
if ((size_t)ret < size) {
|
||||
if (flush_to_file(wr))
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (char *)data + ret;
|
||||
size -= ret;
|
||||
wr->current.raw_size += ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pkg_writer_end_record(pkg_writer_t *wr)
|
||||
{
|
||||
wr->stream->flush(wr->stream);
|
||||
if (flush_to_file(wr))
|
||||
return -1;
|
||||
|
||||
if (lseek(wr->fd, wr->start, SEEK_SET) == -1)
|
||||
goto fail_seek;
|
||||
|
||||
if (write_header(wr))
|
||||
return -1;
|
||||
|
||||
if (lseek(wr->fd, 0, SEEK_END) == -1)
|
||||
goto fail_seek;
|
||||
|
||||
wr->stream->destroy(wr->stream);
|
||||
wr->stream = NULL;
|
||||
return 0;
|
||||
fail_seek:
|
||||
perror(wr->path);
|
||||
return -1;
|
||||
}
|
101
main/util.c
Normal file
101
main/util.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int canonicalize_name(char *filename)
|
||||
{
|
||||
char *ptr = filename;
|
||||
int i;
|
||||
|
||||
while (*ptr == '/')
|
||||
++ptr;
|
||||
|
||||
if (ptr != filename) {
|
||||
memmove(filename, ptr, strlen(ptr) + 1);
|
||||
ptr = filename;
|
||||
}
|
||||
|
||||
while (*ptr != '\0') {
|
||||
if (*ptr == '/') {
|
||||
for (i = 0; ptr[i] == '/'; ++i)
|
||||
;
|
||||
|
||||
if (i > 1)
|
||||
memmove(ptr + 1, ptr + i, strlen(ptr + i) + 1);
|
||||
}
|
||||
|
||||
if (ptr[0] == '/' && ptr[1] == '\0') {
|
||||
*ptr = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
++ptr;
|
||||
}
|
||||
|
||||
ptr = filename;
|
||||
|
||||
while (*ptr != '\0') {
|
||||
if (ptr[0] == '.') {
|
||||
if (ptr[1] == '/' || ptr[1] == '\0')
|
||||
return -1;
|
||||
|
||||
if (ptr[1] == '.' &&
|
||||
(ptr[2] == '/' || ptr[2] == '\0')) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
while (*ptr != '\0' && *ptr != '/')
|
||||
++ptr;
|
||||
if (*ptr == '/')
|
||||
++ptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t write_retry(int fd, void *data, size_t size)
|
||||
{
|
||||
ssize_t ret, total = 0;
|
||||
|
||||
while (size > 0) {
|
||||
ret = write(fd, data, size);
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (char *)data + ret;
|
||||
size -= ret;
|
||||
total += ret;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
ssize_t read_retry(int fd, void *buffer, size_t size)
|
||||
{
|
||||
ssize_t ret, total = 0;
|
||||
|
||||
while (size > 0) {
|
||||
ret = read(fd, buffer, size);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
total += ret;
|
||||
size -= ret;
|
||||
buffer = (char *)buffer + ret;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
Loading…
Reference in a new issue