]> pd.if.org Git - zpackage/blob - zpm-extractfile.c
add support for notes
[zpackage] / zpm-extractfile.c
1 /*
2  * add a file/files to an sqlite db
3  * in the 'files' table.
4  */
5
6 #include <stdio.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <errno.h>
12
13 #include <sys/mman.h>
14
15 #include <sqlite3.h>
16 #include "sha256.h"
17
18 #include "lzma.h"
19
20
21 void uncompresslzma(void *buf, size_t bufsize, FILE *out) {
22         lzma_stream s = LZMA_STREAM_INIT;
23         lzma_stream *strm;
24         
25         uint8_t outbuf[BUFSIZ];
26
27         int ret;
28
29         strm = &s;
30
31         ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
32         /* The only reasonable error here is LZMA_MEM_ERROR. */
33         if (ret != LZMA_OK) {
34                 fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
35                                 : "Internal error (bug)");
36                 exit(EXIT_FAILURE);
37         }
38
39         strm->avail_in = bufsize;
40         strm->next_in = buf;
41         strm->avail_out = BUFSIZ;
42         strm->next_out = outbuf;
43
44         lzma_action action = LZMA_RUN;
45
46         while (1) {
47                 ret = lzma_code(strm, action);
48
49                 // Write and check write error before checking decoder error.
50                 // This way as much data as possible gets written to output
51                 // even if decoder detected an error.
52                 if (strm->avail_out == 0 || ret != LZMA_OK) {
53                         const size_t write_size = BUFSIZ - strm->avail_out;
54
55                         if (fwrite(outbuf, 1, write_size, out) != write_size) {
56                                 // Wouldn't be a surprise if writing to stderr
57                                 // would fail too but at least try to show an
58                                 // error message.
59                                 fprintf(stderr, "Cannot write to output file stream: "
60                                                 "%s", strerror(errno));
61                                 exit(EXIT_FAILURE);
62                         }
63
64                         strm->next_out = outbuf;
65                         strm->avail_out = BUFSIZ;
66                 }
67
68                 if (ret != LZMA_OK) {
69                         if (ret == LZMA_STREAM_END) {
70                                 // lzma_stream_decoder() already guarantees
71                                 // that there's no trailing garbage.
72                                 assert(strm->avail_in == 0);
73                                 //assert(action == LZMA_FINISH);
74                                 return;
75                         }
76
77                         const char *msg;
78                         switch (ret) {
79                                 case LZMA_MEM_ERROR:
80                                         msg = strerror(ENOMEM);
81                                         break;
82
83                                 case LZMA_FORMAT_ERROR:
84                                         msg = "File format not recognized";
85                                         break;
86
87                                 case LZMA_OPTIONS_ERROR:
88                                         // FIXME: Better message?
89                                         msg = "Unsupported compression options";
90                                         break;
91
92                                 case LZMA_DATA_ERROR:
93                                         msg = "File is corrupt";
94                                         break;
95
96                                 case LZMA_BUF_ERROR:
97                                         msg = "Unexpected end of input";
98                                         break;
99
100                                 default:
101                                         msg = "Internal error (bug)";
102                                         break;
103                         }
104
105                         fprintf(stderr, "xz: %s\n", msg);
106                         exit(EXIT_FAILURE);
107                 }
108         }
109 }
110
111 #define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
112
113 int main(int ac, char **av){
114         sqlite3 *db = 0;
115         int rc;
116
117         int blobsize;
118         int64_t size;
119         void *xzdata;
120         int type;
121         FILE *out;
122         sqlite3_stmt *ifile;
123
124         char *hash;
125         char *filename;
126
127         if (ac < 3) {
128                 fprintf(stderr, "usage: db hash file\n");
129                 return 1;
130         }
131
132         rc = sqlite3_open(av[1], &db);
133         if (rc) {
134                 SQLERROR(sqlite3_errmsg(db));
135                 sqlite3_close(db);
136                 return 1;
137         }
138
139         rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0);
140         if (rc != SQLITE_OK) {
141                 SQLERROR(sqlite3_errmsg(db));
142                 return 1;
143         }
144
145         /* hash, filename */
146         hash = av[2];
147         filename = av[3];
148
149         sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
150
151         rc = sqlite3_step(ifile);
152
153         if (rc == SQLITE_DONE) {
154                 /* didn't find a row */
155                 sqlite3_finalize(ifile);
156                 sqlite3_close(db);
157                 fprintf(stderr, "no such hash\n");
158                 return 1;
159         }
160         /* either way we're done with this now */
161
162         if (rc != SQLITE_ROW) {
163                 SQLERROR(sqlite3_errmsg(db));
164                 sqlite3_finalize(ifile);
165                 sqlite3_close(db);
166                 return 2;
167         }
168
169         type = sqlite3_column_type(ifile, 0);
170         if (type == SQLITE_NULL) {
171                 fprintf(stderr, "no file size\n");
172                 sqlite3_finalize(ifile);
173                 sqlite3_close(db);
174                 return 3;
175         }
176         type = sqlite3_column_type(ifile, 1);
177         if (type == SQLITE_NULL) {
178                 fprintf(stderr, "no file data\n");
179                 sqlite3_finalize(ifile);
180                 sqlite3_close(db);
181                 return 4;
182         }
183         size = sqlite3_column_int64(ifile, 0);
184         xzdata = (void *)sqlite3_column_blob(ifile, 1);
185         blobsize = sqlite3_column_bytes(ifile, 1);
186
187         out = fopen(filename, "w");
188         if (!out) {
189                 fprintf(stderr, "can't open output file %s\n", filename);
190                 sqlite3_finalize(ifile);
191                 sqlite3_close(db);
192                 return 5;
193         }
194         //fwrite(xzdata, blobsize, 1, stdout);
195
196         fprintf(stderr, "uncompressing %d bytes at %p, expect %lld\n", blobsize, xzdata, (long long int)size);
197         uncompresslzma(xzdata, blobsize, out);
198         fclose(out);
199
200         sqlite3_finalize(ifile);
201         sqlite3_close(db);
202         return 0;
203 }
204 void *compresslzma(void *buf, size_t bufsize, size_t *len) {
205         /* xz compress it */
206         size_t outsize;
207         void *outbuf;
208         size_t outlen = 0;
209
210         outsize = lzma_stream_buffer_bound(bufsize);
211         outbuf = malloc(outsize);
212         if (!outbuf) {
213                 *len = 0;
214                 return NULL;
215         }
216         /* TODO adjust encoding level for size */
217         lzma_easy_buffer_encode(6, LZMA_CHECK_CRC64, NULL,
218                         buf, bufsize,
219                         outbuf, &outlen, outsize
220                         );
221         *len = outlen;
222         return outbuf;
223 }
224
225 static int callback(void *NotUsed, int argc, char **argv, char **azColName){
226         int i;
227         for(i=0; i<argc; i++){
228                 printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
229         }
230         printf("\n");
231         return 0;
232 }
233
234 static char *create_table = "create table if not exists files (hash text primary key, size integer, compression text, content blob)";
235
236 struct dbh {
237         sqlite3 *db;
238         char *errmsg;
239         int rc;
240 };
241
242 #define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
243 static int begin(sqlite3 *db) {
244         int rc;
245         char *err;
246
247         rc = sqlite3_exec(db, "begin;", callback, 0, &err);
248         if (rc != SQLITE_OK) {
249                 SQLERROR(err);
250                 sqlite3_free(err);
251         }
252         return rc;
253 }
254
255 static int commit(sqlite3 *db) {
256         int rc;
257         char *err;
258
259         rc = sqlite3_exec(db, "commit;", callback, 0, &err);
260         if (rc != SQLITE_OK) {
261                 SQLERROR(err);
262                 sqlite3_free(err);
263         }
264         return rc;
265 }
266 static int rollback(sqlite3 *db) {
267         int rc;
268         char *err;
269
270         rc = sqlite3_exec(db, "rollback;", callback, 0, &err);
271         if (rc != SQLITE_OK) {
272                 SQLERROR(err);
273                 sqlite3_free(err);
274         }
275         return rc;
276 }
277
278 int main(int ac, char **av){
279         sqlite3 *db = 0;
280         char *errmsg = 0;
281         int rc, i, j;
282
283         rc = sqlite3_open(av[1], &db);
284         if (rc) {
285                 SQLERROR(sqlite3_errmsg(db));
286                 sqlite3_close(db);
287                 return 1;
288         }
289
290         rc = sqlite3_exec(db, create_table, callback, 0, &errmsg);
291         if (rc != SQLITE_OK) {
292                 SQLERROR(errmsg);
293                 sqlite3_free(errmsg);
294                 sqlite3_close(db);
295                 return 1;
296         }
297
298         sqlite3_stmt *ifile;
299
300         rc = sqlite3_prepare(db, "insert or ignore into files (hash,size,compression,content) values (?,?,?,?)", -1, &ifile,0);
301         if (rc != SQLITE_OK) {
302                 SQLERROR(sqlite3_errmsg(db));
303                 return 1;
304         }
305
306         begin(db);
307         /* insert each file */
308         for (i = 2; i < ac; i++) {
309                 int fd;
310                 void *content;
311                 struct stat sbuf;
312                 unsigned char tmp[32];
313                 char hash[65];
314                 hash_state md;
315
316                 fd = open(av[i], O_RDONLY);
317                 if (fd == -1) {
318                         rollback(db);
319                         sqlite3_close(db);
320                         exit(1);
321                 }
322                 if (fstat(fd, &sbuf) == -1) {
323                         rollback(db);
324                         sqlite3_close(db);
325                         exit(1);
326                 }
327                 /* not a regular file? */
328                 if (!S_ISREG(sbuf.st_mode)) {
329                         rollback(db);
330                         sqlite3_close(db);
331                         exit(1);
332                 }
333
334                 content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0);
335                 if (!content) {
336                         rollback(db);
337                         sqlite3_close(db);
338                         exit(2);
339                 }
340
341                 sha256_init(&md);
342                 sha256_process(&md, content, sbuf.st_size);
343                 sha256_done(&md, tmp);
344                 for (j=0;j<32;j++) {
345                         sprintf(hash+j*2, "%02x", (unsigned)tmp[j]);
346                 }
347                 hash[64] = 0;
348                 //fprintf(stderr, "file %s: %s\n", av[i], hash);
349
350                 void *xzcompressed;
351                 size_t compresslen;
352
353                 xzcompressed = compresslzma(content, sbuf.st_size, &compresslen);
354
355                 sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
356                 sqlite3_bind_int64(ifile, 2, sbuf.st_size);
357                 sqlite3_bind_text(ifile, 3, "xz", 2, SQLITE_STATIC);
358                 sqlite3_bind_blob64(ifile, 4, xzcompressed, compresslen, SQLITE_STATIC);
359
360                 rc = sqlite3_step(ifile);
361
362                 /* either way we're done with this now */
363                 munmap(content, sbuf.st_size);
364
365                 if (rc != SQLITE_DONE) {
366                         SQLERROR(sqlite3_errmsg(db));
367                         sqlite3_finalize(ifile);
368                         rollback(db);
369                         sqlite3_close(db);
370                         return 1;
371                 }
372                 sqlite3_reset(ifile);
373                 free(xzcompressed);
374                 printf("%s\n", hash);
375         }
376         sqlite3_finalize(ifile);
377         commit(db);
378
379         sqlite3_close(db);
380         return 0;
381 }