]> pd.if.org Git - zpackage/blob - lib/zpm.c
add zpm-hash program
[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 #include <errno.h>
10
11 #include "zpm.h"
12
13 #include "sha256.h"
14
15 #if 0
16 struct zpm {
17         sqlite3 *db;
18         char *path; /* path to package file */
19         char *version;
20         int release;
21         char *pkgname;
22         time_t installed; /* install time, 0 for not installed */
23 };
24
25 struct zpm_file {
26         char *path;
27         int mode;
28         uint32_t filetype;
29         char *tags;
30         char *owner;
31         char *group;
32         char *hash; /* could be fixed length */
33         time_t mtime;
34         struct zpm_file *next; /* so you can make a linked list */
35 };
36
37 /* NULL?  Create? */
38 /* associate with a package ? if only one?  first? */
39 int zpm_open(struct zpm *pkg, char *path);
40 int zpm_pkgname(char *base, char *version, int release); /* construct a package file name */
41
42 /* flags for preserving mode, owner, etc */
43 /* puts hash of import in hash */
44 /* path can be a hash, with an "INTERNAL" flag, i.e. internally import */
45 #define ZPM_MODE 0x1
46 #define ZPM_OWNER 0x2
47 #define ZPM_MTIME 0x4
48 #define ZPM_INTERNAL 0x8
49 #define ZPM_NOBLOB 0x10
50 /* don't run scripts on install */
51 #define ZPM_NOSCRIPTS 0x10
52 /* don't associate the file with a package, just do a raw insert */
53 /* otherwise, associate it with the current package */
54 #define ZPM_NOPACKAGE 0x20
55
56 int zpm_import(struct zpm *zp, char *path, uint32_t flags, uint8_t *hash);
57
58 /* link and unlink hashes to packages */
59 int zpm_link(struct zpm *pkg, char *path, char *hash, struct zpm_file *fileinfo);
60 int zpm_unlink(struct zpm *pkg, char *path);
61
62 /* tag a file.  relative to "current package" */
63 int zpm_tag(struct zpm *zp, char *path, char *tags);
64 /* should this be broken up into separage functions ? */
65 int zpm_md(struct zpm *zp, char *path, int mode, char *owner, char *group, time_t mtime);
66
67 /* export hash to dest */
68 int zpm_extract(struct zpm *pkg, char *hash, char *path, int mode);
69
70 /* export path to dest */
71 int zpm_export(struct zpm *zp, char *path, uint32_t flags, char *dest);
72
73 int zpm_close(struct zpm *zp);
74
75 /* attach a signature to a package */
76 int zpm_sign(struct zpm *z, size_t s, void *signature);
77
78 /* set the package info to the nth package, -1 to return count? */
79 /* further import/exports and such will be relative to this package */
80 int zpm_package(struct zpm *zp, int n);
81
82 /* get file information */
83 int zpm_stat(struct zpm *z, struct zpm_file *f, int n);
84
85 /* will also set the package context to the new package */
86 int zpm_newpkg(struct zpm *z, char *base, char *version, int release);
87
88 /* transactions */
89 int zpm_begin(struct zpm *z);
90 int zpm_commit(struct zpm *z);
91 int zpm_rollback(struct zpm *z);
92
93 /* higher level operations */
94
95 /* install or uninstall the package */
96 /* flag for keeping the blobs in local */
97 /* what about tag filtering */
98 int zpm_install(struct zpm *z, struct zpm *local, uint32_t flags);
99 int zpm_uninstall(struct zpm *local);
100
101 /* slurp up the actual blobs */
102 /* what about versioning them if they change */
103 int zpm_preserve(struct zpm *local);
104
105 /* check file integrity */
106 int zpm_checkinstall(struct zpm *local);
107
108 int zpm_merge(struct zpm *z, struct zpm *src, uint32_t flags);
109
110 void uncompresslzma(void *buf, size_t bufsize, FILE *out);
111 #define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
112 #endif
113
114 static char *dupstr(char *s) {
115         size_t n;
116         char *d;
117
118         n = strlen(s);
119         d = malloc(n+1);
120         if (d) {
121                 d = strcpy(d, s);
122         }
123         return d;
124 }
125
126 #if 0
127 int zpm_newpkg(struct zpm *z, char *base, char *version, int release) {
128         char *sql = "insert or ignore into packages (package,version,release) values (?,?,?)";
129         int rc;
130         sqlite3_stmt *ifile;
131
132         rc = sqlite3_prepare(db, sql, -1, &ifile,0);
133         if (rc != SQLITE_OK) {
134                 SQLERROR(sqlite3_errmsg(db));
135                 return 0;
136         }
137         rc = sqlite3_bind_text(ifile, 1, base, strlen(base), SQLITE_STATIC);
138         if (rc != SQLITE_OK) {
139                 SQLERROR(sqlite3_errmsg(db));
140                 fprintf(stderr, "cant bind package name\n");
141                 zpm_rollback(pkg);
142                 return 0;
143         }
144         sqlite3_bind_text(ifile, 2, version, strlen(version), SQLITE_STATIC);
145         sqlite3_bind_int(ifile, 3, release)
146
147         rc = sqlite3_step(ifile);
148
149         if (rc != SQLITE_DONE) {
150                 SQLERROR(sqlite3_errmsg(db));
151                 sqlite3_finalize(ifile);
152                 return 0;
153         }
154         sqlite3_finalize(ifile);
155         z->pkg = dupstr(base);
156         z->version = dupstr(version);
157         z->release = release;
158 }
159 #endif
160
161 int zpm_begin(struct zpm *z) {
162         char *errstr = 0;
163         sqlite3_exec(z->db, "begin;", NULL, NULL, &errstr);
164         if (errstr) {
165                 fprintf(stderr, "sqlite begin error: %s\n", errstr);
166                 sqlite3_free(errstr);
167                 return 0;
168         }
169         return 1;
170 }
171
172 int zpm_commit(struct zpm *z) {
173         char *errstr = 0;
174         sqlite3_exec(z->db, "commit;", NULL, NULL, &errstr);
175         if (errstr) {
176                 fprintf(stderr, "sqlite commit error: %s\n", errstr);
177                 sqlite3_free(errstr);
178                 return 0;
179         }
180         return 1;
181 }
182
183 int zpm_rollback(struct zpm *z) {
184         char *errstr = 0;
185         sqlite3_exec(z->db, "rollback;", NULL, NULL, &errstr);
186         if (errstr) {
187                 fprintf(stderr, "sqlite rollback error: %s\n", errstr);
188                 sqlite3_free(errstr);
189                 return 0;
190         }
191         return 1;
192 }
193
194 int zpm_db_set_pragma(struct zpm *db, int pragma, int value) {
195         int rc;
196         char *sql;
197         sqlite3_stmt *s;
198
199         switch (pragma) {
200                 case 1: sql = "pragma application_id = ?;"; break;
201                 case 2: sql = "pragma user_version = ?;"; break;
202                 default: return -1; break;
203         }
204
205         rc = sqlite3_prepare_v2(db->db, sql, -1, &s, 0);
206
207         if (rc != SQLITE_OK) {
208                 SQLERROR(sqlite3_errmsg(db->db));
209                 return -1;
210         }
211
212         sqlite3_bind_int(s, 1, value);
213         if (rc != SQLITE_OK) {
214                 SQLERROR(sqlite3_errmsg(db->db));
215                 fprintf(stderr, "cant bind pragma value\n");
216                 sqlite3_finalize(s);
217                 return -1;
218         }
219         rc = sqlite3_step(s);
220         if (rc != SQLITE_DONE) {
221                 SQLERROR(sqlite3_errmsg(db->db));
222                 fprintf(stderr, "cant set pragma\n");
223                 sqlite3_finalize(s);
224                 return -1;
225         }
226
227         sqlite3_finalize(s);
228         return value;
229 }
230
231 int zpm_db_pragma(struct zpm *db, int pragma) {
232         int rc;
233         int value = -1;
234         char *sql = 0;
235         sqlite3_stmt *s;
236
237         switch (pragma) {
238                 case 1: sql = "pragma application_id;"; break;
239                 case 2: sql = "pragma user_version;"; break;
240                 default: return -1; break;
241         }
242
243         rc = sqlite3_prepare_v2(db->db, sql, -1, &s, 0);
244
245         if (rc != SQLITE_OK) {
246                 SQLERROR(sqlite3_errmsg(db->db));
247                 fprintf(stderr, "%s, errnum = %d\n", sqlite3_errmsg(db->db), rc);
248                 /* TODO just abort? */
249                 return -1;
250         }
251
252         rc = sqlite3_step(s);
253         if (rc == SQLITE_ROW) {
254                 value = sqlite3_column_int(s, 0);
255         }
256
257         sqlite3_finalize(s);
258         return value;
259 }
260
261 static
262 #include "newdb.c"
263
264 int zpm_db_initialize(struct zpm *pkg) {
265         //fprintf(stderr, "initializing zpm database\n");
266         switch (sqlite3_exec(pkg->db, createdb, (int (*)(void *,int,char **,char **))0, NULL, NULL)) {
267                 case SQLITE_OK: break;
268                 default:
269                         SQLERROR(sqlite3_errmsg(pkg->db));
270                         return 0;
271                         break;
272         }
273         return 1;
274 }
275
276 /* NULL?  Create? */
277 int zpm_open(struct zpm *pkg, char *path) {
278         int rc;
279         char *errstr = 0;
280         sqlite3 *db = 0;
281         int appid, dbver;
282
283         pkg->db = 0;
284         pkg->path = 0;
285         pkg->version = 0;
286         pkg->release = 0;
287         pkg->pkgname = 0;
288         pkg->installed = 0;
289
290         rc = sqlite3_open(path, &db);
291         if (rc) {
292                 SQLERROR(sqlite3_errmsg(db));
293                 sqlite3_close(db);
294                 return 0;
295         }
296         pkg->db   = db;
297         pkg->path = dupstr(path);
298
299         appid = zpm_db_pragma(pkg, 1);
300         dbver = zpm_db_pragma(pkg, 2);
301
302         //fprintf(stderr, "db appid = %x, dbver = %d\n", appid, dbver);
303         switch (appid) {
304                 case 0: if (!zpm_db_initialize(pkg)) {
305                                 sqlite3_close(db);
306                                 return 1;
307                         };
308                         break;
309                 case 0x5a504442: break;
310                 default:
311                         fprintf(stderr, "unknown database type\n");
312                         sqlite3_close(db);
313                         return 0;
314                         break;
315         }
316         if (dbver > 1) {
317                 fprintf(stderr, "version %d zpm db detected, this program only works with version 1 databases\n", dbver);
318                         sqlite3_close(db);
319                         return 0;
320         }
321
322         sqlite3_exec(pkg->db, "pragma foreign_keys = ON;", NULL, NULL, &errstr);
323         if (errstr) {
324                 fprintf(stderr, "sqlite foreign key error: %s\n", errstr);
325                 sqlite3_free(errstr);
326                 sqlite3_close(db);
327                 return 0;
328         }
329
330
331         /* TODO if this is a new database, create structures */
332
333         /* get a package. what if more than one? what if none? */
334         return 1;
335 }
336
337 int zpm_close(struct zpm *pkg) {
338         if (pkg) {
339                 sqlite3_close(pkg->db);
340                 free(pkg->path);
341         }
342         /* TODO free any malloced names and such */
343         return 1;
344 }
345
346 /* set package struct variables, database, environment, then command line */
347 int zpm_readopts(struct zpm *pkg, int ac, char **av) {
348         char *ev;
349
350         if (!pkg) {
351                 return -1;
352         }
353
354         ev = getenv("ZPMPACKAGE");
355         if (ev) {
356                 pkg->pkgname = dupstr(ev);
357         }
358         ev = getenv("ZPMPKGREL");
359         if (ev) {
360                 pkg->release = strtol(ev, 0, 0);
361         }
362         ev = getenv("ZPMPKGVER");
363         if (ev) {
364                 pkg->version = dupstr(ev);
365         }
366
367         /* now, parse the options, return optind so the caller can adjust if needed */
368
369         return 1;
370 }
371
372 int zpm_extract(struct zpm *pkg, char *hash, char *path, int mode) {
373         int rc;
374
375         int blobsize;
376         //int64_t size;
377         void *xzdata;
378         int type;
379         FILE *out;
380         sqlite3_stmt *ifile;
381
382         /* TODO check null */
383         sqlite3 *db = pkg->db;
384
385         rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0);
386         if (rc != SQLITE_OK) {
387                 SQLERROR(sqlite3_errmsg(db));
388                 return 0;
389         }
390
391         /* hash, filename */
392
393         sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
394
395         rc = sqlite3_step(ifile);
396
397         if (rc == SQLITE_DONE) {
398                 /* didn't find a row */
399                 sqlite3_finalize(ifile);
400                 sqlite3_close(db);
401                 fprintf(stderr, "no such hash\n");
402                 return 0;
403         }
404         /* either way we're done with this now */
405
406         if (rc != SQLITE_ROW) {
407                 SQLERROR(sqlite3_errmsg(db));
408                 sqlite3_finalize(ifile);
409                 sqlite3_close(db);
410                 return 0;
411         }
412
413         type = sqlite3_column_type(ifile, 0);
414         if (type == SQLITE_NULL) {
415                 fprintf(stderr, "no file size\n");
416                 sqlite3_finalize(ifile);
417                 sqlite3_close(db);
418                 return 0;
419         }
420         type = sqlite3_column_type(ifile, 1);
421         if (type == SQLITE_NULL) {
422                 fprintf(stderr, "no file data\n");
423                 sqlite3_finalize(ifile);
424                 sqlite3_close(db);
425                 return 0;
426         }
427         //size = sqlite3_column_int64(ifile, 0);
428         xzdata = (void *)sqlite3_column_blob(ifile, 1);
429         blobsize = sqlite3_column_bytes(ifile, 1);
430
431         out = fopen(path, "w");
432         if (!out) {
433                 fprintf(stderr, "can't open output file %s\n", path);
434                 sqlite3_finalize(ifile);
435                 sqlite3_close(db);
436                 return 5;
437         }
438         //fwrite(xzdata, blobsize, 1, stdout);
439
440         //fprintf(stderr, "uncompressing %d bytes at %p, expect %lld\n", blobsize, xzdata, (long long int)size);
441         uncompresslzma(xzdata, blobsize, out);
442         fclose(out);
443
444         sqlite3_finalize(ifile);
445
446         return 0;
447         
448 }
449
450 /* flags 0, close mmap, flags 1, return mmap fd */
451 int zpm_hash(char *path, char *hash, uint32_t flags) {
452         int fd;
453         void *content;
454         struct stat sbuf;
455         hash_state md;
456         int j;
457         unsigned char tmp[32];
458
459         /* mmap the file */
460         fd = open(path, O_RDONLY);
461         if (fd == -1) {
462                 fprintf(stderr, "%s can't open %s: %s\n", __FUNCTION__, path,strerror(errno));
463                 return 0;
464         }
465         if (fstat(fd, &sbuf) == -1) {
466                 fprintf(stderr, "%s can't fstat %s: %s\n", __FUNCTION__, path,strerror(errno));
467                 return 0;
468         }
469         /* not a regular file? */
470         if (!S_ISREG(sbuf.st_mode)) {
471                 /* TODO this is ok, just stored differently */
472                 fprintf(stderr, "%s non-regular files unsupported %s\n", __FUNCTION__, path);
473                 return 0;
474         }
475
476         content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0);
477         close(fd);
478         if (!content) {
479                 fprintf(stderr, "%s can't mmap %s: %s\n", __FUNCTION__, path,strerror(errno));
480                 return 0;
481         }
482
483         /* get hash */
484         sha256_init(&md);
485         sha256_process(&md, content, sbuf.st_size);
486         sha256_done(&md, tmp);
487         for (j=0;j<32;j++) {
488                 sprintf(hash+j*2, "%02x", (unsigned)tmp[j]);
489         }
490         hash[64] = 0;
491         munmap(content, sbuf.st_size);
492         return 1;
493 }
494
495 #if 1
496 int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) {
497         int fd;
498         void *content;
499         struct stat sbuf;
500         unsigned char tmp[32];
501         hash_state md;
502         sqlite3_stmt *ifile;
503         int haverow = 0,havedata = 0;
504         int j,rc,type;
505         char hashbuf[65];
506
507         /* xz compress it */
508         size_t outlen = 0;
509         void *outbuf;
510
511         if (!pkg || !pkg->db || !path) {
512                 return 0;
513         }
514
515         /* use local if caller didn't pass in room */
516         if (!hash) {
517                 hash = hashbuf;
518         }
519
520         /* mmap the file */
521         fd = open(path, O_RDONLY);
522         if (fd == -1) {
523                 fprintf(stderr, "%s can't open %s: %s\n", __FUNCTION__, path,strerror(errno));
524                 return 0;
525         }
526         if (fstat(fd, &sbuf) == -1) {
527                 fprintf(stderr, "%s can't fstat %s: %s\n", __FUNCTION__, path,strerror(errno));
528                 return 0;
529         }
530         /* not a regular file? */
531         if (!S_ISREG(sbuf.st_mode)) {
532                 /* TODO this is ok, just stored differently */
533                 fprintf(stderr, "%s non-regular files unsupported %s\n", __FUNCTION__, path);
534                 return 0;
535         }
536
537         content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0);
538         if (!content) {
539                 fprintf(stderr, "%s can't mmap %s: %s\n", __FUNCTION__, path,strerror(errno));
540                 return 0;
541         }
542
543         /* get hash */
544         sha256_init(&md);
545         sha256_process(&md, content, sbuf.st_size);
546         sha256_done(&md, tmp);
547         for (j=0;j<32;j++) {
548                 sprintf(hash+j*2, "%02x", (unsigned)tmp[j]);
549         }
550         hash[64] = 0;
551         //fprintf(stderr, "file %s: %s\n", path, hash);
552
553         /* prepare and bind */
554         /* TODO check null */
555         sqlite3 *db = pkg->db;
556
557         rc = sqlite3_prepare_v2(db, "select size, content is not null from files where hash = ?", -1, &ifile,0);
558         if (rc != SQLITE_OK) {
559                 SQLERROR(sqlite3_errmsg(db));
560                 return 0;
561         }
562
563         /* hash, filename */
564
565         sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
566
567         rc = sqlite3_step(ifile);
568
569         if (rc != SQLITE_DONE) {
570                 if (rc != SQLITE_ROW) {
571                         /* didn't find a row */
572                         SQLERROR(sqlite3_errmsg(db));
573                         zpm_rollback(pkg);
574                         return 0;
575                 }
576                 haverow = 1;
577 //              fprintf(stderr, "have row for hash\n");
578                 type = sqlite3_column_type(ifile, 0);
579                 if (type == SQLITE_NULL) {
580                         /* TODO assert, this shouldn't be possible? */
581                         fprintf(stderr, "no file size\n");
582                         sqlite3_finalize(ifile);
583                         return 0;
584                 }
585                 type = sqlite3_column_type(ifile, 1);
586                 if (type == SQLITE_NULL) {
587                         /* TODO assert, this shouldn't be possible? */
588                         fprintf(stderr, "no file data\n");
589                         sqlite3_finalize(ifile);
590                         return 0;
591                         /* which is fine, just need to update the row then */
592                 }
593                 havedata = sqlite3_column_int(ifile, 1);
594         }
595
596         sqlite3_finalize(ifile);
597
598         if (!havedata) {
599                 /* compress */
600                 outbuf = compresslzma(content, sbuf.st_size, &outlen);
601                 if (!outbuf) {
602                         fprintf(stderr, "compresslzma failed\n");
603                         return 0;
604                 }
605                 //fprintf(stderr, "compressed to %zu\n", outlen);
606                 /* don't need the original file now */
607                 munmap(content, sbuf.st_size);
608                 close(fd);
609
610                 /* start a transaction */
611                 // do that outside of here 
612                 //zpm_begin(pkg);
613
614                 /* insert */
615                 if (haverow) {
616                         //fprintf(stderr, "adding file data\n");
617                         rc = sqlite3_prepare(db, "update files set size = ?, content = ? where hash = ?", -1, &ifile,0);
618                 } else {
619                         //fprintf(stderr, "creating new data row\n");
620                         rc = sqlite3_prepare(db, "insert into files (size, content, hash) values (?,?,?)", -1, &ifile,0);
621                 }
622                 if (rc != SQLITE_OK) {
623                         SQLERROR(sqlite3_errmsg(db));
624                         fprintf(stderr, "cant prepare data\n");
625                         zpm_rollback(pkg);
626                         return 0;
627                 }
628
629                 sqlite3_bind_int64(ifile, 1, (sqlite3_int64)sbuf.st_size);
630                 if (rc != SQLITE_OK) {
631                         SQLERROR(sqlite3_errmsg(db));
632                         fprintf(stderr, "cant bind size\n");
633                         zpm_rollback(pkg);
634                         return 0;
635                 }
636                 sqlite3_bind_blob64(ifile, 2, outbuf, (sqlite3_int64)outlen, SQLITE_STATIC);
637                 if (rc != SQLITE_OK) {
638                         SQLERROR(sqlite3_errmsg(db));
639                         fprintf(stderr, "cant bind content\n");
640                         zpm_rollback(pkg);
641                         return 0;
642                 }
643                 sqlite3_bind_text(ifile, 3, hash, 64, SQLITE_STATIC);
644                 if (rc != SQLITE_OK) {
645                         SQLERROR(sqlite3_errmsg(db));
646                         fprintf(stderr, "cant bind hash\n");
647                         zpm_rollback(pkg);
648                         return 0;
649                 }
650                 rc = sqlite3_step(ifile);
651                 if (rc != SQLITE_DONE) {
652                         SQLERROR(sqlite3_errmsg(db));
653                         sqlite3_finalize(ifile);
654                         zpm_rollback(pkg);
655                         return 0;
656                 }
657                 sqlite3_finalize(ifile);
658
659                 /* commit */
660                 //zpm_commit(pkg);
661
662         } else {
663                 /* don't need the original file now */
664                 munmap(content, sbuf.st_size);
665                 close(fd);
666         }
667
668
669         /* if package and not nopackage flag, add to package */
670         if (pkg->pkgname && (!ZPM_NOPACKAGE)) {
671                 /* TODO */
672         }
673
674         /* return */
675         return 1;
676 }
677 #endif
678
679 #if 0
680 int main(int ac, char **av){
681         sqlite3 *db = 0;
682         int rc;
683
684         int blobsize;
685         int64_t size;
686         void *xzdata;
687         int type;
688         FILE *out;
689         sqlite3_stmt *ifile;
690
691         char *hash;
692         char *filename;
693
694         if (ac < 3) {
695                 fprintf(stderr, "usage: db hash file\n");
696                 return 1;
697         }
698
699         rc = sqlite3_open(av[1], &db);
700         if (rc) {
701                 SQLERROR(sqlite3_errmsg(db));
702                 sqlite3_close(db);
703                 return 1;
704         }
705
706 }
707 #endif
708
709 #if 0
710 Packages are sqlite databases
711
712 get application id and userver
713
714 Primitive operations:
715
716 add path to repo
717 associate path with package
718 associate blob with path?
719 add blob to repo
720 * extract blob to a path
721 compare blob to filesystem path
722 create package with info
723
724 Extra primitives:
725
726 record elf information about blob
727 compress blob
728 uncompress blob
729 sign a package?  What are we verifying?
730 #endif