mirror of
https://github.com/pygos/pkg-utils.git
synced 2024-12-04 01:30:42 +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 |