From 4f381aa35d66185b97bfba62d4002857660118f1 Mon Sep 17 00:00:00 2001 From: Gaspartcho <93390411+Gaspartcho@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:59:56 +0100 Subject: Update extract: .tgz and +100 char name support (#73) * added tar support * fixed small mistake * added support for utar + removed microtar as submodule * will finish this tomorow * added support for gnu and other * re-done all small changes * fixed pax format (hopefully) * added a r to 'curent_read' --- .gitmodules | 4 - lib/microtar | 1 - lib/microtar/LICENSE | 21 +++ lib/microtar/README.md | 100 ++++++++++ lib/microtar/src/microtar.c | 434 ++++++++++++++++++++++++++++++++++++++++++++ lib/microtar/src/microtar.h | 99 ++++++++++ src/lpm.c | 150 +++++++++++++-- src/lpm.lua | 2 +- 8 files changed, 794 insertions(+), 17 deletions(-) delete mode 160000 lib/microtar create mode 100644 lib/microtar/LICENSE create mode 100644 lib/microtar/README.md create mode 100644 lib/microtar/src/microtar.c create mode 100644 lib/microtar/src/microtar.h diff --git a/.gitmodules b/.gitmodules index c171b3a..b83fa2d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,10 +15,6 @@ path = lib/libzip url = https://github.com/nih-at/libzip.git shallow = true -[submodule "lib/microtar"] - path = lib/microtar - url = https://github.com/rxi/microtar.git - shallow = true [submodule "lib/mbedtls"] path = lib/mbedtls url = https://github.com/Mbed-TLS/mbedtls.git diff --git a/lib/microtar b/lib/microtar deleted file mode 160000 index 27076e1..0000000 --- a/lib/microtar +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 27076e1b9290e9c7842bb7890a54fcf172406c84 diff --git a/lib/microtar/LICENSE b/lib/microtar/LICENSE new file mode 100644 index 0000000..3502bc5 --- /dev/null +++ b/lib/microtar/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2017 rxi +Copyright (c) 2024 Gaspartcho + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/microtar/README.md b/lib/microtar/README.md new file mode 100644 index 0000000..78d2f05 --- /dev/null +++ b/lib/microtar/README.md @@ -0,0 +1,100 @@ +# microtar (a fork) +A lightweight tar library written in ANSI C +Initially forked from https://github.com/rxi/microtar + + +## Basic Usage +The library consists of `microtar.c` and `microtar.h`. These two files can be +dropped into an existing project and compiled along with it. + + +#### Reading +```c +mtar_t tar; +mtar_header_t h; +char *p; + +/* Open archive for reading */ +mtar_open(&tar, "test.tar", "r"); + +/* Print all file names and sizes */ +while ( (mtar_read_header(&tar, &h)) != MTAR_ENULLRECORD ) { + printf("%s (%d bytes)\n", h.name, h.size); + mtar_next(&tar); +} + +/* Load and print contents of file "test.txt" */ +mtar_find(&tar, "test.txt", &h); +p = calloc(1, h.size + 1); +mtar_read_data(&tar, p, h.size); +printf("%s", p); +free(p); + +/* Close archive */ +mtar_close(&tar); +``` + +#### Writing +```c +mtar_t tar; +const char *str1 = "Hello world"; +const char *str2 = "Goodbye world"; + +/* Open archive for writing */ +mtar_open(&tar, "test.tar", "w"); + +/* Write strings to files `test1.txt` and `test2.txt` */ +mtar_write_file_header(&tar, "test1.txt", strlen(str1)); +mtar_write_data(&tar, str1, strlen(str1)); +mtar_write_file_header(&tar, "test2.txt", strlen(str2)); +mtar_write_data(&tar, str2, strlen(str2)); + +/* Finalize -- this needs to be the last thing done before closing */ +mtar_finalize(&tar); + +/* Close archive */ +mtar_close(&tar); +``` + + +## Error handling +All functions which return an `int` will return `MTAR_ESUCCESS` if the operation +is successful. If an error occurs an error value less-than-zero will be +returned; this value can be passed to the function `mtar_strerror()` to get its +corresponding error string. + + +## Wrapping a stream +If you want to read or write from something other than a file, the `mtar_t` +struct can be manually initialized with your own callback functions and a +`stream` pointer. + +All callback functions are passed a pointer to the `mtar_t` struct as their +first argument. They should return `MTAR_ESUCCESS` if the operation succeeds +without an error, or an integer below zero if an error occurs. + +After the `stream` field has been set, all required callbacks have been set and +all unused fields have been zeroset the `mtar_t` struct can be safely used with +the microtar functions. `mtar_open` *should not* be called if the `mtar_t` +struct was initialized manually. + +#### Reading +The following callbacks should be set for reading an archive from a stream: + +Name | Arguments | Description +--------|------------------------------------------|--------------------------- +`read` | `mtar_t *tar, void *data, unsigned size` | Read data from the stream +`seek` | `mtar_t *tar, unsigned pos` | Set the position indicator +`close` | `mtar_t *tar` | Close the stream + +#### Writing +The following callbacks should be set for writing an archive to a stream: + +Name | Arguments | Description +--------|------------------------------------------------|--------------------- +`write` | `mtar_t *tar, const void *data, unsigned size` | Write data to the stream + + +## License +This library is free software; you can redistribute it and/or modify it under +the terms of the MIT license. See [LICENSE](LICENSE) for details. diff --git a/lib/microtar/src/microtar.c b/lib/microtar/src/microtar.c new file mode 100644 index 0000000..1cd9920 --- /dev/null +++ b/lib/microtar/src/microtar.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2017 rxi + * Copyright (c) 2024 Gaspartcho + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "microtar.h" + +typedef struct { + // Original tar header fields + char file_path[100]; + char file_mode[8]; + char owner_user_id[8]; + char owner_group_id[8]; + char file_size[12]; + char file_mtime[12]; + char header_checksum[8]; + char file_type; + char link_path[100]; + + // New UStar fields + char magic_bytes[6]; + char version[2]; + char owner_user_name[32]; + char owner_group_name[32]; + char device_major_number[8]; + char device_minor_number[8]; + char prefix[155]; + + char padding[12]; +} mtar_raw_header_t; + + +static unsigned round_up(unsigned n, unsigned incr) { + return n + (incr - n % incr) % incr; +} + + +static unsigned checksum(const mtar_raw_header_t* rh) { + unsigned i; + unsigned char *p = (unsigned char*) rh; + unsigned res = 256; + for (i = 0; i < offsetof(mtar_raw_header_t, header_checksum); i++) { + res += p[i]; + } + for (i = offsetof(mtar_raw_header_t, file_type); i < sizeof(*rh); i++) { + res += p[i]; + } + return res; +} + + +static int tread(mtar_t *tar, void *data, unsigned size) { + int err = tar->read(tar, data, size); + tar->pos += size; + return err; +} + + +static int twrite(mtar_t *tar, const void *data, unsigned size) { + int err = tar->write(tar, data, size); + tar->pos += size; + return err; +} + + +static int write_null_bytes(mtar_t *tar, int n) { + int i, err; + char nul = '\0'; + for (i = 0; i < n; i++) { + err = twrite(tar, &nul, 1); + if (err) { + return err; + } + } + return MTAR_ESUCCESS; +} + +static int read_filename(char *dest, const char *name, const char *prefix) { + // If there's no prefix, use name directly + if (prefix[0] == '\0') { + strcpy(dest, name); + dest[100] = '\0'; + return MTAR_ESUCCESS; + } + + // If there is a prefix, the path is: '/' + strcpy(dest, prefix); + strcat(dest, "/"); + strcat(dest, name); + dest[256] = '\0'; + + return MTAR_ESUCCESS; +} + + +static int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) { + unsigned chksum1, chksum2; + + /* If the checksum starts with a null byte we assume the record is NULL */ + if (*rh->header_checksum == '\0') { + return MTAR_ENULLRECORD; + } + + /* Build and compare checksum */ + chksum1 = checksum(rh); + sscanf(rh->header_checksum, "%o", &chksum2); + if (chksum1 != chksum2) { + return MTAR_EBADCHKSUM; + } + + /* Load raw header into header */ + sscanf(rh->file_mode, "%o", &h->mode); + sscanf(rh->owner_user_id, "%o", &h->owner); + sscanf(rh->file_size, "%o", &h->size); + sscanf(rh->file_mtime, "%o", &h->mtime); + h->type = rh->file_type; + read_filename(h->name, rh->file_path, rh->prefix); + read_filename(h->linkname, rh->link_path, rh->prefix); + + return MTAR_ESUCCESS; +} + + +static int header_to_raw(mtar_raw_header_t *rh, const mtar_header_t *h) { + unsigned chksum; + + /* Load header into raw header */ + memset(rh, 0, sizeof(*rh)); + sprintf(rh->file_mode , "%o", h->mode); + sprintf(rh->owner_user_id, "%o", h->owner); + sprintf(rh->file_size, "%o", h->size); + sprintf(rh->file_mtime, "%o", h->mtime); + rh->file_type = h->type ? h->type : MTAR_TREG; + strcpy(rh->file_path, h->name); + strcpy(rh->link_path, h->linkname); + + + /* Calculate and write checksum */ + chksum = checksum(rh); + sprintf(rh->header_checksum, "%06o", chksum); + rh->header_checksum[7] = ' '; + + return MTAR_ESUCCESS; +} + + +const char* mtar_strerror(int err) { + switch (err) { + case MTAR_ESUCCESS : return "success"; + case MTAR_EFAILURE : return "failure"; + case MTAR_EOPENFAIL : return "could not open"; + case MTAR_EREADFAIL : return "could not read"; + case MTAR_EWRITEFAIL : return "could not write"; + case MTAR_ESEEKFAIL : return "could not seek"; + case MTAR_EBADCHKSUM : return "bad checksum"; + case MTAR_ENULLRECORD : return "null record"; + case MTAR_ENOTFOUND : return "file not found"; + } + return "unknown error"; +} + + +static int file_write(mtar_t *tar, const void *data, unsigned size) { + unsigned res = fwrite(data, 1, size, tar->stream); + return (res == size) ? MTAR_ESUCCESS : MTAR_EWRITEFAIL; +} + +static int file_read(mtar_t *tar, void *data, unsigned size) { + unsigned res = fread(data, 1, size, tar->stream); + return (res == size) ? MTAR_ESUCCESS : MTAR_EREADFAIL; +} + +static int file_seek(mtar_t *tar, unsigned offset) { + int res = fseek(tar->stream, offset, SEEK_SET); + return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL; +} + +static int file_close(mtar_t *tar) { + fclose(tar->stream); + return MTAR_ESUCCESS; +} + + +int mtar_open(mtar_t *tar, const char *filename, const char *mode) { + int err; + mtar_header_t h; + + /* Init tar struct and functions */ + memset(tar, 0, sizeof(*tar)); + tar->write = file_write; + tar->read = file_read; + tar->seek = file_seek; + tar->close = file_close; + + /* Assure mode is always binary */ + if ( strchr(mode, 'r') ) mode = "rb"; + if ( strchr(mode, 'w') ) mode = "wb"; + if ( strchr(mode, 'a') ) mode = "ab"; + /* Open file */ + tar->stream = fopen(filename, mode); + if (!tar->stream) { + return MTAR_EOPENFAIL; + } + /* Read first header to check it is valid if mode is `r` */ + if (*mode == 'r') { + err = mtar_read_header(tar, &h); + if (err != MTAR_ESUCCESS) { + mtar_close(tar); + return err; + } + } + + /* Return ok */ + return MTAR_ESUCCESS; +} + + +int mtar_close(mtar_t *tar) { + return tar->close(tar); +} + + +int mtar_seek(mtar_t *tar, unsigned pos) { + int err = tar->seek(tar, pos); + tar->pos = pos; + return err; +} + + +int mtar_rewind(mtar_t *tar) { + tar->remaining_data = 0; + tar->last_header = 0; + return mtar_seek(tar, 0); +} + + +int mtar_next(mtar_t *tar) { + int err, n; + mtar_header_t h; + /* Load header */ + err = mtar_read_header(tar, &h); + if (err) { + return err; + } + /* Seek to next record */ + n = round_up(h.size, 512) + sizeof(mtar_raw_header_t); + return mtar_seek(tar, tar->pos + n); +} + + +int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h) { + int err; + mtar_header_t header; + /* Start at beginning */ + err = mtar_rewind(tar); + if (err) { + return err; + } + /* Iterate all files until we hit an error or find the file */ + while ( (err = mtar_read_header(tar, &header)) == MTAR_ESUCCESS ) { + if ( !strcmp(header.name, name) ) { + if (h) { + *h = header; + } + return MTAR_ESUCCESS; + } + mtar_next(tar); + } + /* Return error */ + if (err == MTAR_ENULLRECORD) { + err = MTAR_ENOTFOUND; + } + return err; +} + + +int mtar_read_header(mtar_t *tar, mtar_header_t *h) { + int err; + mtar_raw_header_t rh; + /* Save header position */ + tar->last_header = tar->pos; + /* Read raw header */ + err = tread(tar, &rh, sizeof(rh)); + if (err) { + return err; + } + /* Seek back to start of header */ + err = mtar_seek(tar, tar->last_header); + if (err) { + return err; + } + /* Load raw header into header struct and return */ + return raw_to_header(h, &rh); +} + + +int mtar_update_header(mtar_header_t *h, mtar_header_t *oh) { + if (oh->mode) h->mode = oh->mode; + if (oh->owner) h->owner = oh->owner; + if (oh->size) h->size = oh->size; + if (oh->mtime) h->mtime = oh->mtime; + if (oh->type) h->type = oh->type; + + if (oh->name[0] != '\0') strcpy(h->name, oh->name); + if (oh->linkname[0] != '\0') strcpy(h->linkname, oh->linkname); + + return MTAR_ESUCCESS; +} + + +int mtar_clear_header(mtar_header_t *h){ + h->mode = 0; + h->owner = 0; + h->size = 0; + h->mtime = 0; + h->type = 0; + bzero(h->name, strlen(h->name)); + bzero(h->linkname, strlen(h->linkname)); + + return MTAR_ESUCCESS; +} + + +int mtar_read_data(mtar_t *tar, void *ptr, unsigned size) { + int err; + /* If we have no remaining data then this is the first read, we get the size, + * set the remaining data and seek to the beginning of the data */ + if (tar->remaining_data == 0) { + mtar_header_t h; + /* Read header */ + err = mtar_read_header(tar, &h); + if (err) { + return err; + } + /* Seek past header and init remaining data */ + err = mtar_seek(tar, tar->pos + sizeof(mtar_raw_header_t)); + if (err) { + return err; + } + tar->remaining_data = h.size; + } + /* Read data */ + err = tread(tar, ptr, size); + if (err) { + return err; + } + tar->remaining_data -= size; + /* If there is no remaining data we've finished reading and seek back to the + * header */ + if (tar->remaining_data == 0) { + return mtar_seek(tar, tar->last_header); + } + return MTAR_ESUCCESS; +} + + +int mtar_write_header(mtar_t *tar, const mtar_header_t *h) { + mtar_raw_header_t rh; + /* Build raw header and write */ + header_to_raw(&rh, h); + tar->remaining_data = h->size; + return twrite(tar, &rh, sizeof(rh)); +} + + +int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size) { + mtar_header_t h; + /* Build header */ + memset(&h, 0, sizeof(h)); + strcpy(h.name, name); + h.size = size; + h.type = MTAR_TREG; + h.mode = 0664; + /* Write header */ + return mtar_write_header(tar, &h); +} + + +int mtar_write_dir_header(mtar_t *tar, const char *name) { + mtar_header_t h; + /* Build header */ + memset(&h, 0, sizeof(h)); + strcpy(h.name, name); + h.type = MTAR_TDIR; + h.mode = 0775; + /* Write header */ + return mtar_write_header(tar, &h); +} + + +int mtar_write_data(mtar_t *tar, const void *data, unsigned size) { + int err; + /* Write data */ + err = twrite(tar, data, size); + if (err) { + return err; + } + tar->remaining_data -= size; + /* Write padding if we've written all the data for this file */ + if (tar->remaining_data == 0) { + return write_null_bytes(tar, round_up(tar->pos, 512) - tar->pos); + } + return MTAR_ESUCCESS; +} + + +int mtar_finalize(mtar_t *tar) { + /* Write two NULL records */ + return write_null_bytes(tar, sizeof(mtar_raw_header_t) * 2); +} + diff --git a/lib/microtar/src/microtar.h b/lib/microtar/src/microtar.h new file mode 100644 index 0000000..1b3c09d --- /dev/null +++ b/lib/microtar/src/microtar.h @@ -0,0 +1,99 @@ + +/** + * Copyright (c) 2017 rxi + * Copyright (c) 2024 Gaspartcho + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `microtar.c` for details. + */ + +#ifndef MICROTAR_H +#define MICROTAR_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#define MTAR_VERSION "0.1.0" + +enum { + MTAR_ESUCCESS = 0, + MTAR_EFAILURE = -1, + MTAR_EOPENFAIL = -2, + MTAR_EREADFAIL = -3, + MTAR_EWRITEFAIL = -4, + MTAR_ESEEKFAIL = -5, + MTAR_EBADCHKSUM = -6, + MTAR_ENULLRECORD = -7, + MTAR_ENOTFOUND = -8 +}; + +enum { + MTAR_TREG = '0', + MTAR_TLNK = '1', + MTAR_TSYM = '2', + MTAR_TCHR = '3', + MTAR_TBLK = '4', + MTAR_TDIR = '5', + MTAR_TFIFO = '6', + MTAR_TEHR = 'x', // PAX file format + MTAR_TEHRA = 'g', // PAX file format + MTAR_TGFP = 'K', // GNU file format + MTAR_TGLP = 'L' // GNU file format +}; + +typedef struct { + unsigned mode; + unsigned owner; + unsigned size; + unsigned mtime; + unsigned type; + char name[4096]; + char linkname[4096]; +} mtar_header_t; + + +typedef struct mtar_t mtar_t; + +struct mtar_t { + int (*read)(mtar_t *tar, void *data, unsigned size); + int (*write)(mtar_t *tar, const void *data, unsigned size); + int (*seek)(mtar_t *tar, unsigned pos); + int (*close)(mtar_t *tar); + void *stream; + unsigned pos; + unsigned remaining_data; + unsigned last_header; +}; + + +const char* mtar_strerror(int err); + +int mtar_open(mtar_t *tar, const char *filename, const char *mode); +int mtar_close(mtar_t *tar); + +int mtar_seek(mtar_t *tar, unsigned pos); +int mtar_rewind(mtar_t *tar); +int mtar_next(mtar_t *tar); +int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h); +int mtar_read_header(mtar_t *tar, mtar_header_t *h); +int mtar_update_header(mtar_header_t *h, mtar_header_t *oh); +int mtar_clear_header(mtar_header_t *h); +int mtar_read_data(mtar_t *tar, void *ptr, unsigned size); + +int mtar_write_header(mtar_t *tar, const mtar_header_t *h); +int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size); +int mtar_write_dir_header(mtar_t *tar, const char *name); +int mtar_write_data(mtar_t *tar, const void *data, unsigned size); +int mtar_finalize(mtar_t *tar); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/lpm.c b/src/lpm.c index cecd061..11084ed 100644 --- a/src/lpm.c +++ b/src/lpm.c @@ -668,6 +668,7 @@ static int lpm_extract(lua_State* L) { if (strstr(src, ".zip")) { int zip_error_code; zip_t* archive = zip_open(src, ZIP_RDONLY, &zip_error_code); + if (!archive) { zip_error_t zip_error; zip_error_init_with_code(&zip_error, zip_error_code); @@ -675,22 +676,27 @@ static int lpm_extract(lua_State* L) { zip_error_fini(&zip_error); return lua_error(L); } + zip_int64_t entries = zip_get_num_entries(archive, 0); for (zip_int64_t i = 0; i < entries; ++i) { zip_file_t* zip_file = zip_fopen_index(archive, i, 0); const char* zip_name = zip_get_name(archive, i, ZIP_FL_ENC_GUESS); + if (!zip_file) { lua_pushfstring(L, "can't read zip archive file %s: %s", zip_name, zip_strerror(archive)); zip_close(archive); return lua_error(L); } + char target[MAX_PATH]; int target_length = snprintf(target, sizeof(target), "%s/%s", dst, zip_name); + if (mkdirp(target, target_length)) { zip_fclose(zip_file); zip_close(archive); return luaL_error(L, "can't extract zip archive file %s, can't create directory %s: %s", src, target, strerror(errno)); } + if (target[target_length-1] != '/') { FILE* file = lua_fopen(L, target, "wb"); if (!file) { @@ -702,6 +708,7 @@ static int lpm_extract(lua_State* L) { mode_t m = S_IRUSR | S_IRGRP | S_IROTH; zip_uint8_t os; zip_uint32_t attr; + zip_file_get_external_attributes(archive, i, 0, &os, &attr); if (os == ZIP_OPSYS_DOS) { if (0 == (attr & FA_RDONLY)) @@ -711,52 +718,73 @@ static int lpm_extract(lua_State* L) { } else { m = (attr >> 16); } + if (chmod(target, m)) { zip_fclose(zip_file); zip_close(archive); return luaL_error(L, "can't chmod file %s: %s", target, strerror(errno)); } + while (1) { char buffer[8192]; zip_int64_t length = zip_fread(zip_file, buffer, sizeof(buffer)); + if (length == -1) { lua_pushfstring(L, "can't read zip archive file %s: %s", zip_name, zip_file_strerror(zip_file)); zip_fclose(zip_file); zip_close(archive); return lua_error(L); } + if (length == 0) break; fwrite(buffer, sizeof(char), length, file); } + fclose(file); } zip_fclose(zip_file); } zip_close(archive); - } else { + } + + else { char actual_src[PATH_MAX]; - if (strstr(src, ".gz")) { + + if (strstr(src, ".gz") || strstr(src, ".tgz")) { gzFile gzfile = gzopen(src, "rb"); + if (!gzfile) return luaL_error(L, "can't open tar.gz archive %s: %s", src, strerror(errno)); + char buffer[8192]; - int len = strlen(src) - 3; - if (!strstr(src, ".tar")) - strcpy(actual_src, dst); - else + int len = strlen(src) - 3;; + + if (strstr(src, ".tar")) strncpy(actual_src, src, len < PATH_MAX ? len : PATH_MAX); + else if (strstr(src, ".tgz")) { + strncpy(actual_src, src, len < PATH_MAX ? len : PATH_MAX); + strcat(actual_src, "tar"); + len = strlen(src); + } + else{ + strcpy(actual_src, dst); + } + actual_src[len] = 0; FILE* file = lua_fopen(L, actual_src, "wb"); + if (!file) { gzclose(gzfile); return luaL_error(L, "can't open %s for writing: %s", actual_src, strerror(errno)); } + while (1) { int length = gzread(gzfile, buffer, sizeof(buffer)); if (length == 0) break; fwrite(buffer, sizeof(char), length, file); } + char error[128]; error[0] = 0; if (!gzeof(gzfile)) { @@ -764,57 +792,157 @@ static int lpm_extract(lua_State* L) { strncpy(error, gzerror(gzfile, &error_number), sizeof(error)); error[sizeof(error)-1] = 0; } + fclose(file); gzclose(gzfile); + if (error[0]) return luaL_error(L, "can't unzip gzip archive %s: %s", src, error); - } else { + + } else strcpy(actual_src, src); - } - if (strstr(src, ".tar")) { - mtar_t tar = {0}; + + if (strstr(src, ".tar") || strstr(src, ".tgz")) { /* It's incredibly slow to do it this way, probably because of all the seeking. For now, just gunzip the whole file at once, and then untar it. tar.read = gzip_read; tar.seek = gzip_seek; tar.close = gzip_close;*/ + + mtar_t tar = {0}; int err; if ((err = mtar_open(&tar, actual_src, "r"))) return luaL_error(L, "can't open tar archive %s: %s", src, mtar_strerror(err)); + mtar_header_t h; + mtar_header_t before_h; + mtar_header_t allways_h; + int has_ext_before = 0; + int has_ext_allways = 0; + while ((mtar_read_header(&tar, &h)) != MTAR_ENULLRECORD ) { if (h.type == MTAR_TREG) { + + if (has_ext_before) { + mtar_update_header(&h, &before_h); + has_ext_before = 0; + mtar_clear_header(&before_h); + } + if (has_ext_allways) + mtar_update_header(&h, &allways_h); + char target[MAX_PATH]; int target_length = snprintf(target, sizeof(target), "%s/%s", dst, h.name); + if (mkdirp(target, target_length)) { mtar_close(&tar); return luaL_error(L, "can't extract tar archive file %s, can't create directory %s: %s", src, target, strerror(errno)); } + char buffer[8192]; FILE* file = fopen(target, "wb"); if (!file) { mtar_close(&tar); return luaL_error(L, "can't extract tar archive file %s, can't create file %s: %s", src, target, strerror(errno)); } + if (chmod(target, h.mode)) return luaL_error(L, "can't extract tar archive file %s, can't chmod file %s: %s", src, target, strerror(errno)); + int remaining = h.size; while (remaining > 0) { int read_size = remaining < sizeof(buffer) ? remaining : sizeof(buffer); + if (mtar_read_data(&tar, buffer, read_size) != MTAR_ESUCCESS) { fclose(file); mtar_close(&tar); return luaL_error(L, "can't write file %s: %s", target, strerror(errno)); } + fwrite(buffer, sizeof(char), read_size, file); remaining -= read_size; } + fclose(file); } + + else if (h.type == MTAR_TEHR || h.type == MTAR_TEHRA) { + mtar_header_t *h_to_change; + if (h.type == MTAR_TEHR) + h_to_change = &before_h; + else + h_to_change = &allways_h; + + char buffer[4096] = {0}; + char current_read[8192] = {0}; // If a line is more than 8192 char long, will not work! + char last_read[4096] = {0}; + int remaining = h.size; + + has_ext_before = 1; + + while (remaining > 0) { + int read_size = remaining < sizeof(buffer) ? remaining : sizeof(buffer); + remaining -= read_size; + + if (mtar_read_data(&tar, buffer, read_size) != MTAR_ESUCCESS) { + mtar_close(&tar); + return luaL_error(L, "Error while reading extended: %s", strerror(errno)); + } + + strcpy(current_read, last_read); + current_read[strlen(last_read)] = '\0'; + strcat(current_read, buffer); + current_read[strlen(last_read) + read_size] = '\0'; + + char *n_line_ptr = NULL; + char **l_line_ptr = NULL; + char *line = strtok_r(current_read, "\n", &n_line_ptr); + + while (line != NULL) { + char *in_line_ptr = NULL; + strtok_r(line, " ", &in_line_ptr); + char *header_key = strtok_r(NULL, "=", &in_line_ptr); + char *header_val = strtok_r(NULL, "=", &in_line_ptr); + + if (!strcmp(header_key, "path")) strcpy(h_to_change->name, header_val); + if (!strcmp(header_key, "linkpath")) strcpy(h_to_change->linkname, header_val); + // possibility to add more later + + l_line_ptr = &n_line_ptr; + line = strtok_r(NULL, "\n", &n_line_ptr); + } + + if (current_read[strlen(last_read) + read_size - 1] != '\n') + strcpy(last_read, strtok_r(current_read, "\n", l_line_ptr)); + else + bzero(last_read, strlen(last_read)); + } + } + + else if (h.type == MTAR_TGFP) { + has_ext_before = 1; + int read_size = before_h.size < sizeof(before_h.name) ? before_h.size : sizeof(before_h.name); + + if (mtar_read_data(&tar, before_h.name, read_size) != MTAR_ESUCCESS) { + mtar_close(&tar); + return luaL_error(L, "Error while reading GNU extended: %s", strerror(errno)); + } + } + + else if (h.type == MTAR_TGLP) { + has_ext_before = 1; + int read_size = before_h.size < sizeof(before_h.linkname) ? before_h.size : sizeof(before_h.linkname); + + if (mtar_read_data(&tar, before_h.linkname, read_size) != MTAR_ESUCCESS) { + mtar_close(&tar); + return luaL_error(L, "Error while reading GNU extended: %s", strerror(errno)); + } + } + mtar_next(&tar); } mtar_close(&tar); - if (strstr(src, ".gz")) + if (strstr(src, ".gz") || strstr(src, ".tgz")) unlink(actual_src); } } diff --git a/src/lpm.lua b/src/lpm.lua index 7beb181..3f2a6ab 100644 --- a/src/lpm.lua +++ b/src/lpm.lua @@ -873,7 +873,7 @@ function Addon:install(bottle, installing) else common.get(file.url, temporary_path, file.checksum, write_progress_bar) local basename = common.basename(target_path) - local is_archive = basename:find("%.zip$") or basename:find("%.tar%.gz$") + local is_archive = basename:find("%.zip$") or basename:find("%.tar%.gz$") or basename:find("%.tgz$") local target = temporary_path if is_archive or basename:find("%.gz$") then log_action("Extracting file " .. basename .. " in " .. install_path) -- cgit v1.2.3