]> pd.if.org Git - zpackage/blobdiff - zpm-extractfile.c
large commit of C work
[zpackage] / zpm-extractfile.c
diff --git a/zpm-extractfile.c b/zpm-extractfile.c
new file mode 100644 (file)
index 0000000..68ab0e1
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * add a file/files to an sqlite db
+ * in the 'files' table.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+
+#include <sqlite3.h>
+#include "sha256.h"
+
+#include "lzma.h"
+
+
+void uncompresslzma(void *buf, size_t bufsize, FILE *out) {
+       lzma_stream s = LZMA_STREAM_INIT;
+       lzma_stream *strm;
+       
+       uint8_t outbuf[BUFSIZ];
+
+       int ret;
+
+       strm = &s;
+
+       ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
+       /* The only reasonable error here is LZMA_MEM_ERROR. */
+       if (ret != LZMA_OK) {
+               fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
+                               : "Internal error (bug)");
+               exit(EXIT_FAILURE);
+       }
+
+       strm->avail_in = bufsize;
+       strm->next_in = buf;
+       strm->avail_out = BUFSIZ;
+       strm->next_out = outbuf;
+
+        lzma_action action = LZMA_RUN;
+
+       while (1) {
+               ret = lzma_code(strm, action);
+
+               // Write and check write error before checking decoder error.
+               // This way as much data as possible gets written to output
+               // even if decoder detected an error.
+               if (strm->avail_out == 0 || ret != LZMA_OK) {
+                       const size_t write_size = BUFSIZ - strm->avail_out;
+
+                       if (fwrite(outbuf, 1, write_size, out) != write_size) {
+                               // Wouldn't be a surprise if writing to stderr
+                               // would fail too but at least try to show an
+                               // error message.
+                               fprintf(stderr, "Cannot write to output file stream: "
+                                               "%s", strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+
+                       strm->next_out = outbuf;
+                       strm->avail_out = BUFSIZ;
+               }
+
+               if (ret != LZMA_OK) {
+                       if (ret == LZMA_STREAM_END) {
+                               // lzma_stream_decoder() already guarantees
+                               // that there's no trailing garbage.
+                               assert(strm->avail_in == 0);
+                               //assert(action == LZMA_FINISH);
+                               return;
+                       }
+
+                       const char *msg;
+                       switch (ret) {
+                               case LZMA_MEM_ERROR:
+                                       msg = strerror(ENOMEM);
+                                       break;
+
+                               case LZMA_FORMAT_ERROR:
+                                       msg = "File format not recognized";
+                                       break;
+
+                               case LZMA_OPTIONS_ERROR:
+                                       // FIXME: Better message?
+                                       msg = "Unsupported compression options";
+                                       break;
+
+                               case LZMA_DATA_ERROR:
+                                       msg = "File is corrupt";
+                                       break;
+
+                               case LZMA_BUF_ERROR:
+                                       msg = "Unexpected end of input";
+                                       break;
+
+                               default:
+                                       msg = "Internal error (bug)";
+                                       break;
+                       }
+
+                       fprintf(stderr, "xz: %s\n", msg);
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+#define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
+
+int main(int ac, char **av){
+       sqlite3 *db = 0;
+       int rc;
+
+       int blobsize;
+       int64_t size;
+       void *xzdata;
+       int type;
+       FILE *out;
+       sqlite3_stmt *ifile;
+
+       char *hash;
+       char *filename;
+
+       if (ac < 3) {
+               fprintf(stderr, "usage: db hash file\n");
+               return 1;
+       }
+
+       rc = sqlite3_open(av[1], &db);
+       if (rc) {
+               SQLERROR(sqlite3_errmsg(db));
+               sqlite3_close(db);
+               return 1;
+       }
+
+       rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0);
+       if (rc != SQLITE_OK) {
+               SQLERROR(sqlite3_errmsg(db));
+               return 1;
+       }
+
+       /* hash, filename */
+       hash = av[2];
+       filename = av[3];
+
+       sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
+
+       rc = sqlite3_step(ifile);
+
+       if (rc == SQLITE_DONE) {
+               /* didn't find a row */
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               fprintf(stderr, "no such hash\n");
+               return 1;
+       }
+       /* either way we're done with this now */
+
+       if (rc != SQLITE_ROW) {
+               SQLERROR(sqlite3_errmsg(db));
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 2;
+       }
+
+       type = sqlite3_column_type(ifile, 0);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file size\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 3;
+       }
+       type = sqlite3_column_type(ifile, 1);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file data\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 4;
+       }
+       size = sqlite3_column_int64(ifile, 0);
+       xzdata = (void *)sqlite3_column_blob(ifile, 1);
+       blobsize = sqlite3_column_bytes(ifile, 1);
+
+       out = fopen(filename, "w");
+       if (!out) {
+               fprintf(stderr, "can't open output file %s\n", filename);
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 5;
+       }
+       //fwrite(xzdata, blobsize, 1, stdout);
+
+       fprintf(stderr, "uncompressing %d bytes at %p, expect %lld\n", blobsize, xzdata, (long long int)size);
+       uncompresslzma(xzdata, blobsize, out);
+       fclose(out);
+
+       sqlite3_finalize(ifile);
+       sqlite3_close(db);
+       return 0;
+}
+void *compresslzma(void *buf, size_t bufsize, size_t *len) {
+       /* xz compress it */
+       size_t outsize;
+       void *outbuf;
+       size_t outlen = 0;
+
+       outsize = lzma_stream_buffer_bound(bufsize);
+       outbuf = malloc(outsize);
+       if (!outbuf) {
+               *len = 0;
+               return NULL;
+       }
+       /* TODO adjust encoding level for size */
+       lzma_easy_buffer_encode(6, LZMA_CHECK_CRC64, NULL,
+                       buf, bufsize,
+                       outbuf, &outlen, outsize
+                       );
+       *len = outlen;
+       return outbuf;
+}
+
+static int callback(void *NotUsed, int argc, char **argv, char **azColName){
+       int i;
+       for(i=0; i<argc; i++){
+               printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+       }
+       printf("\n");
+       return 0;
+}
+
+static char *create_table = "create table if not exists files (hash text primary key, size integer, compression text, content blob)";
+
+struct dbh {
+       sqlite3 *db;
+       char *errmsg;
+       int rc;
+};
+
+#define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
+static int begin(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "begin;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+
+static int commit(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "commit;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+static int rollback(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "rollback;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+
+int main(int ac, char **av){
+       sqlite3 *db = 0;
+       char *errmsg = 0;
+       int rc, i, j;
+
+       rc = sqlite3_open(av[1], &db);
+       if (rc) {
+               SQLERROR(sqlite3_errmsg(db));
+               sqlite3_close(db);
+               return 1;
+       }
+
+       rc = sqlite3_exec(db, create_table, callback, 0, &errmsg);
+       if (rc != SQLITE_OK) {
+               SQLERROR(errmsg);
+               sqlite3_free(errmsg);
+               sqlite3_close(db);
+               return 1;
+       }
+
+       sqlite3_stmt *ifile;
+
+       rc = sqlite3_prepare(db, "insert or ignore into files (hash,size,compression,content) values (?,?,?,?)", -1, &ifile,0);
+       if (rc != SQLITE_OK) {
+               SQLERROR(sqlite3_errmsg(db));
+               return 1;
+       }
+
+       begin(db);
+       /* insert each file */
+       for (i = 2; i < ac; i++) {
+               int fd;
+               void *content;
+               struct stat sbuf;
+               unsigned char tmp[32];
+               char hash[65];
+               hash_state md;
+
+               fd = open(av[i], O_RDONLY);
+               if (fd == -1) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(1);
+               }
+               if (fstat(fd, &sbuf) == -1) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(1);
+               }
+               /* not a regular file? */
+               if (!S_ISREG(sbuf.st_mode)) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(1);
+               }
+
+               content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0);
+               if (!content) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(2);
+               }
+
+               sha256_init(&md);
+               sha256_process(&md, content, sbuf.st_size);
+               sha256_done(&md, tmp);
+               for (j=0;j<32;j++) {
+                       sprintf(hash+j*2, "%02x", (unsigned)tmp[j]);
+               }
+               hash[64] = 0;
+               //fprintf(stderr, "file %s: %s\n", av[i], hash);
+
+               void *xzcompressed;
+               size_t compresslen;
+
+               xzcompressed = compresslzma(content, sbuf.st_size, &compresslen);
+
+               sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
+               sqlite3_bind_int64(ifile, 2, sbuf.st_size);
+               sqlite3_bind_text(ifile, 3, "xz", 2, SQLITE_STATIC);
+               sqlite3_bind_blob64(ifile, 4, xzcompressed, compresslen, SQLITE_STATIC);
+
+               rc = sqlite3_step(ifile);
+
+               /* either way we're done with this now */
+               munmap(content, sbuf.st_size);
+
+               if (rc != SQLITE_DONE) {
+                       SQLERROR(sqlite3_errmsg(db));
+                       sqlite3_finalize(ifile);
+                       rollback(db);
+                       sqlite3_close(db);
+                       return 1;
+               }
+               sqlite3_reset(ifile);
+               free(xzcompressed);
+               printf("%s\n", hash);
+       }
+       sqlite3_finalize(ifile);
+       commit(db);
+
+       sqlite3_close(db);
+       return 0;
+}