]> pd.if.org Git - zpackage/blob - lib/zpm.c
large commit of C work
[zpackage] / lib / zpm.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <sys/mman.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9
10 #include "zpm.h"
11
12 #include "sha256.h"
13 #if 0
14 struct zpm {
15         sqlite3 *db;
16         char *path; /* path to package file */
17         char *version;
18         int release;
19         char *pkgname;
20         time_t installed; /* install time, 0 for not installed */
21 };
22
23 struct zpm_file {
24         char *path;
25         int mode;
26         uint32_t filetype;
27         char *tags;
28         char *owner;
29         char *group;
30         char *hash; /* could be fixed length */
31         time_t mtime;
32         struct zpm_file *next; /* so you can make a linked list */
33 };
34
35 /* NULL?  Create? */
36 /* associate with a package ? if only one?  first? */
37 int zpm_open(struct zpm *pkg, char *path);
38 int zpm_pkgname(char *base, char *version, int release); /* construct a package file name */
39
40 /* flags for preserving mode, owner, etc */
41 /* puts hash of import in hash */
42 /* path can be a hash, with an "INTERNAL" flag, i.e. internally import */
43 #define ZPM_MODE 0x1
44 #define ZPM_OWNER 0x2
45 #define ZPM_MTIME 0x4
46 #define ZPM_INTERNAL 0x8
47 #define ZPM_NOBLOB 0x10
48 /* don't run scripts on install */
49 #define ZPM_NOSCRIPTS 0x10
50 /* don't associate the file with a package, just do a raw insert */
51 /* otherwise, associate it with the current package */
52 #define ZPM_NOPACKAGE 0x20
53
54 int zpm_import(struct zpm *zp, char *path, uint32_t flags, uint8_t *hash);
55
56 /* link and unlink hashes to packages */
57 int zpm_link(struct zpm *pkg, char *path, char *hash, struct zpm_file *fileinfo);
58 int zpm_unlink(struct zpm *pkg, char *path);
59
60 /* tag a file.  relative to "current package" */
61 int zpm_tag(struct zpm *zp, char *path, char *tags);
62 /* should this be broken up into separage functions ? */
63 int zpm_md(struct zpm *zp, char *path, int mode, char *owner, char *group, time_t mtime);
64
65 /* export hash to dest */
66 int zpm_extract(struct zpm *pkg, char *hash, char *path, int mode);
67
68 /* export path to dest */
69 int zpm_export(struct zpm *zp, char *path, uint32_t flags, char *dest);
70
71 int zpm_close(struct zpm *zp);
72
73 /* attach a signature to a package */
74 int zpm_sign(struct zpm *z, size_t s, void *signature);
75
76 /* set the package info to the nth package, -1 to return count? */
77 /* further import/exports and such will be relative to this package */
78 int zpm_package(struct zpm *zp, int n);
79
80 /* get file information */
81 int zpm_stat(struct zpm *z, struct zpm_file *f, int n);
82
83 /* will also set the package context to the new package */
84 int zpm_newpkg(struct zpm *z, char *base, char *version, int release);
85
86 /* transactions */
87 int zpm_begin(struct zpm *z);
88 int zpm_commit(struct zpm *z);
89 int zpm_rollback(struct zpm *z);
90
91 /* higher level operations */
92
93 /* install or uninstall the package */
94 /* flag for keeping the blobs in local */
95 /* what about tag filtering */
96 int zpm_install(struct zpm *z, struct zpm *local, uint32_t flags);
97 int zpm_uninstall(struct zpm *local);
98
99 /* slurp up the actual blobs */
100 /* what about versioning them if they change */
101 int zpm_preserve(struct zpm *local);
102
103 /* check file integrity */
104 int zpm_checkinstall(struct zpm *local);
105
106 int zpm_merge(struct zpm *z, struct zpm *src, uint32_t flags);
107
108 void uncompresslzma(void *buf, size_t bufsize, FILE *out);
109 #define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
110 #endif
111
112 static char *dupstr(char *s) {
113         size_t n;
114         char *d;
115
116         n = strlen(s);
117         d = malloc(n+1);
118         if (d) {
119                 d = strcpy(d, s);
120         }
121         return d;
122 }
123
124 #if 0
125 int zpm_newpkg(struct zpm *z, char *base, char *version, int release) {
126         char *sql = "insert or ignore into packages (package,version,release) values (?,?,?)";
127         int rc;
128         sqlite3_stmt *ifile;
129
130         rc = sqlite3_prepare(db, sql, -1, &ifile,0);
131         if (rc != SQLITE_OK) {
132                 SQLERROR(sqlite3_errmsg(db));
133                 return 0;
134         }
135         rc = sqlite3_bind_text(ifile, 1, base, strlen(base), SQLITE_STATIC);
136         if (rc != SQLITE_OK) {
137                 SQLERROR(sqlite3_errmsg(db));
138                 fprintf(stderr, "cant bind package name\n");
139                 zpm_rollback(pkg);
140                 return 0;
141         }
142         sqlite3_bind_text(ifile, 2, version, strlen(version), SQLITE_STATIC);
143         sqlite3_bind_int(ifile, 3, release)
144
145         rc = sqlite3_step(ifile);
146
147         if (rc != SQLITE_DONE) {
148                 SQLERROR(sqlite3_errmsg(db));
149                 sqlite3_finalize(ifile);
150                 return 0;
151         }
152         sqlite3_finalize(ifile);
153         z->pkg = dupstr(base);
154         z->version = dupstr(version);
155         z->release = release;
156 }
157 #endif
158
159 int zpm_begin(struct zpm *z) {
160         char *errstr = 0;
161         sqlite3_exec(z->db, "begin;", NULL, NULL, &errstr);
162         if (errstr) {
163                 fprintf(stderr, "sqlite begin error: %s\n", errstr);
164                 sqlite3_free(errstr);
165                 return 0;
166         }
167         return 1;
168 }
169
170 int zpm_commit(struct zpm *z) {
171         char *errstr = 0;
172         sqlite3_exec(z->db, "commit;", NULL, NULL, &errstr);
173         if (errstr) {
174                 fprintf(stderr, "sqlite commit error: %s\n", errstr);
175                 sqlite3_free(errstr);
176                 return 0;
177         }
178         return 1;
179 }
180
181 int zpm_rollback(struct zpm *z) {
182         char *errstr = 0;
183         sqlite3_exec(z->db, "rollback;", NULL, NULL, &errstr);
184         if (errstr) {
185                 fprintf(stderr, "sqlite rollback error: %s\n", errstr);
186                 sqlite3_free(errstr);
187                 return 0;
188         }
189         return 1;
190 }
191
192 /* NULL?  Create? */
193 int zpm_open(struct zpm *pkg, char *path) {
194         int rc;
195         char *errstr = 0;
196         sqlite3 *db = 0;
197
198         pkg->db = 0;
199         pkg->path = 0;
200         pkg->version = 0;
201         pkg->release = 0;
202         pkg->pkgname = 0;
203         pkg->installed = 0;
204
205         /* TODO some way to determine if the DB is newly created ? */
206         /* could check for tables, if there are any, then check version, etc */
207         rc = sqlite3_open(path, &db);
208         if (rc) {
209                 SQLERROR(sqlite3_errmsg(db));
210                 sqlite3_close(db);
211                 return 0;
212         }
213         sqlite3_exec(pkg->db, "pragma foreign_keys = ON;", NULL, NULL, &errstr);
214         if (errstr) {
215                 fprintf(stderr, "sqlite foreign key error: %s\n", errstr);
216                 sqlite3_free(errstr);
217                 sqlite3_close(db);
218                 return 0;
219         }
220
221         pkg->path = dupstr(path);
222         pkg->db   = db;
223
224         /* TODO if this is a new database, create structures */
225
226         /* get a package. what if more than one? what if none? */
227         return 1;
228 }
229
230 int zpm_close(struct zpm *pkg) {
231         if (pkg) {
232                 sqlite3_close(pkg->db);
233                 free(pkg->path);
234         }
235         /* TODO free any malloced names and such */
236         return 1;
237 }
238
239 int zpm_extract(struct zpm *pkg, char *hash, char *path, int mode) {
240         int rc;
241
242         int blobsize;
243         int64_t size;
244         void *xzdata;
245         int type;
246         FILE *out;
247         sqlite3_stmt *ifile;
248
249         /* TODO check null */
250         sqlite3 *db = pkg->db;
251
252         rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0);
253         if (rc != SQLITE_OK) {
254                 SQLERROR(sqlite3_errmsg(db));
255                 return 0;
256         }
257
258         /* hash, filename */
259
260         sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
261
262         rc = sqlite3_step(ifile);
263
264         if (rc == SQLITE_DONE) {
265                 /* didn't find a row */
266                 sqlite3_finalize(ifile);
267                 sqlite3_close(db);
268                 fprintf(stderr, "no such hash\n");
269                 return 0;
270         }
271         /* either way we're done with this now */
272
273         if (rc != SQLITE_ROW) {
274                 SQLERROR(sqlite3_errmsg(db));
275                 sqlite3_finalize(ifile);
276                 sqlite3_close(db);
277                 return 0;
278         }
279
280         type = sqlite3_column_type(ifile, 0);
281         if (type == SQLITE_NULL) {
282                 fprintf(stderr, "no file size\n");
283                 sqlite3_finalize(ifile);
284                 sqlite3_close(db);
285                 return 0;
286         }
287         type = sqlite3_column_type(ifile, 1);
288         if (type == SQLITE_NULL) {
289                 fprintf(stderr, "no file data\n");
290                 sqlite3_finalize(ifile);
291                 sqlite3_close(db);
292                 return 0;
293         }
294         size = sqlite3_column_int64(ifile, 0);
295         xzdata = (void *)sqlite3_column_blob(ifile, 1);
296         blobsize = sqlite3_column_bytes(ifile, 1);
297
298         out = fopen(path, "w");
299         if (!out) {
300                 fprintf(stderr, "can't open output file %s\n", path);
301                 sqlite3_finalize(ifile);
302                 sqlite3_close(db);
303                 return 5;
304         }
305         //fwrite(xzdata, blobsize, 1, stdout);
306
307         fprintf(stderr, "uncompressing %d bytes at %p, expect %lld\n", blobsize, xzdata, (long long int)size);
308         uncompresslzma(xzdata, blobsize, out);
309         fclose(out);
310
311         sqlite3_finalize(ifile);
312
313         return 0;
314         
315 }
316
317 #if 1
318 int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) {
319         int fd;
320         void *content;
321         struct stat sbuf;
322         unsigned char tmp[32];
323         hash_state md;
324         sqlite3_stmt *ifile;
325         int haverow,havedata;
326         int j,rc,type;
327         char hashbuf[65];
328
329         /* xz compress it */
330         size_t outlen = 0;
331         void *outbuf;
332
333         if (!pkg || !pkg->db || !path) {
334                 return 0;
335         }
336
337         /* use local if caller didn't pass in room */
338         if (!hash) {
339                 hash = hashbuf;
340         }
341
342         /* mmap the file */
343         fd = open(path, O_RDONLY);
344         if (fd == -1) {
345                 return 0;
346         }
347         if (fstat(fd, &sbuf) == -1) {
348                 return 0;
349         }
350         /* not a regular file? */
351         if (!S_ISREG(sbuf.st_mode)) {
352                 /* TODO this is ok, just stored differently */
353                 return 0;
354         }
355
356         content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0);
357         if (!content) {
358                 return 0;
359         }
360
361         /* get hash */
362         sha256_init(&md);
363         sha256_process(&md, content, sbuf.st_size);
364         sha256_done(&md, tmp);
365         for (j=0;j<32;j++) {
366                 sprintf(hash+j*2, "%02x", (unsigned)tmp[j]);
367         }
368         hash[64] = 0;
369 //      fprintf(stderr, "file %s: %s\n", path, hash);
370
371         /* compress */
372         outbuf = compresslzma(content, sbuf.st_size, &outlen);
373 //      fprintf(stderr, "compressed to %zu\n", outlen);
374
375         /* don't need the original file now */
376         munmap(content, sbuf.st_size);
377         close(fd);
378
379         /* prepare and bind */
380         /* TODO check null */
381         sqlite3 *db = pkg->db;
382
383         rc = sqlite3_prepare(db, "select size, content is not null from files where hash = ?", -1, &ifile,0);
384         if (rc != SQLITE_OK) {
385                 SQLERROR(sqlite3_errmsg(db));
386                 return 1;
387         }
388
389         /* hash, filename */
390
391         sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
392
393         rc = sqlite3_step(ifile);
394
395         if (rc != SQLITE_DONE) {
396                 if (rc != SQLITE_ROW) {
397                         /* didn't find a row */
398                         SQLERROR(sqlite3_errmsg(db));
399                         zpm_rollback(pkg);
400                         return 0;
401                 }
402                 haverow = 1;
403 //              fprintf(stderr, "have row for hash\n");
404                 type = sqlite3_column_type(ifile, 0);
405                 if (type == SQLITE_NULL) {
406                         /* TODO assert, this shouldn't be possible? */
407                         fprintf(stderr, "no file size\n");
408                         sqlite3_finalize(ifile);
409                         return 0;
410                 }
411                 type = sqlite3_column_type(ifile, 1);
412                 if (type == SQLITE_NULL) {
413                         /* TODO assert, this shouldn't be possible? */
414                         fprintf(stderr, "no file data\n");
415                         sqlite3_finalize(ifile);
416                         return 0;
417                         /* which is fine, just need to update the row then */
418                 }
419                 havedata = sqlite3_column_int(ifile, 1);
420         }
421
422         sqlite3_finalize(ifile);
423
424         if (!havedata) {
425                 /* start a transaction */
426                 // do that outside of here 
427                 //zpm_begin(pkg);
428
429                 /* insert */
430                 if (haverow) {
431                         rc = sqlite3_prepare(db, "update files set size = ?, content = ? where hash = ?", -1, &ifile,0);
432                 } else {
433 //                      fprintf(stderr, "missing file data\n");
434                         rc = sqlite3_prepare(db, "insert into files (size, content, hash) values (?,?,?)", -1, &ifile,0);
435                 }
436                 if (rc != SQLITE_OK) {
437                         SQLERROR(sqlite3_errmsg(db));
438                         fprintf(stderr, "cant prepare data\n");
439                         zpm_rollback(pkg);
440                         return 0;
441                 }
442
443                 sqlite3_bind_int64(ifile, 1, (sqlite3_int64)sbuf.st_size);
444                 if (rc != SQLITE_OK) {
445                         SQLERROR(sqlite3_errmsg(db));
446                         fprintf(stderr, "cant bind size\n");
447                         zpm_rollback(pkg);
448                         return 0;
449                 }
450                 sqlite3_bind_blob64(ifile, 2, outbuf, (sqlite3_int64)outlen, SQLITE_STATIC);
451                 if (rc != SQLITE_OK) {
452                         SQLERROR(sqlite3_errmsg(db));
453                         fprintf(stderr, "cant bind content\n");
454                         zpm_rollback(pkg);
455                         return 0;
456                 }
457                 sqlite3_bind_text(ifile, 3, hash, 64, SQLITE_STATIC);
458                 if (rc != SQLITE_OK) {
459                         SQLERROR(sqlite3_errmsg(db));
460                         fprintf(stderr, "cant bind hash\n");
461                         zpm_rollback(pkg);
462                         return 0;
463                 }
464                 rc = sqlite3_step(ifile);
465                 if (rc != SQLITE_DONE) {
466                         SQLERROR(sqlite3_errmsg(db));
467                         sqlite3_finalize(ifile);
468                         zpm_rollback(pkg);
469                         return 0;
470                 }
471                 sqlite3_finalize(ifile);
472
473                 /* commit */
474                 //zpm_commit(pkg);
475
476         }
477
478         /* if package and not nopackage flag, add to package */
479         if (pkg->pkgname && (!ZPM_NOPACKAGE)) {
480                 /* TODO */
481         }
482
483         /* return */
484         return 1;
485 }
486 #endif
487
488 #if 0
489 int main(int ac, char **av){
490         sqlite3 *db = 0;
491         int rc;
492
493         int blobsize;
494         int64_t size;
495         void *xzdata;
496         int type;
497         FILE *out;
498         sqlite3_stmt *ifile;
499
500         char *hash;
501         char *filename;
502
503         if (ac < 3) {
504                 fprintf(stderr, "usage: db hash file\n");
505                 return 1;
506         }
507
508         rc = sqlite3_open(av[1], &db);
509         if (rc) {
510                 SQLERROR(sqlite3_errmsg(db));
511                 sqlite3_close(db);
512                 return 1;
513         }
514
515 }
516 #endif
517
518 #if 0
519 Packages are sqlite databases
520
521 get application id and userver
522
523 Primitive operations:
524
525 add path to repo
526 associate path with package
527 associate blob with path?
528 add blob to repo
529 * extract blob to a path
530 compare blob to filesystem path
531 create package with info
532
533 Extra primitives:
534
535 record elf information about blob
536 compress blob
537 uncompress blob
538 sign a package?  What are we verifying?
539 #endif