]> pd.if.org Git - zpackage/blob - zpm-add.c
fix recursive zpm-add
[zpackage] / zpm-add.c
1 #define _POSIX_C_SOURCE 200809L
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <dirent.h>
9 #include <unistd.h>
10 #include <pwd.h>
11 #include <grp.h>
12 #include <errno.h>
13 #include <fcntl.h>
14
15 #include "zpm.h"
16 #include "sqlite/sqlite3.h"
17
18 struct opts {
19         char *package, *version;
20         int release;
21         int complete, addcontent, isconfig, opt_l, recursive, verbose;
22         int followsymlinks;
23         int xargs, noclear;
24         mode_t clmode;
25
26         char *cltype, *clhash, *clmtime, *prefix, *strip, *cltarget;
27         char *clgroup, *cluser;
28 };
29
30 #if 0
31 static void warn(char *fmt, ...) {
32         va_list args;
33
34         va_start(args, fmt);
35         vfprintf(stderr, fmt, args);
36         va_end(args);
37         fprintf(stderr, "\n");
38 }
39 #endif
40
41 static int dieval = EXIT_FAILURE;
42
43 static void die(char *fmt, ...) {
44         va_list args;
45
46         va_start(args, fmt);
47         vfprintf(stderr, fmt, args);
48         va_end(args);
49         fprintf(stderr, "\n");
50         exit(dieval);
51 }
52
53 char *cleanpath(char *path, char *strip, char *prefix) {
54         char *s = path;
55         char *t = s;
56         size_t len = 0;
57         char *new;
58
59         if (strip) {
60                 len = strlen(strip);
61                 if (strncmp(path, strip, len)) {
62                         /* strip is not a prefix */
63                         return 0;
64                 }
65                 s += len;
66         }
67
68         len += strlen(s)+1;
69
70         new = malloc(len);
71         if (!new) {
72                 return NULL;
73         }
74         memset(new, 0, len);
75
76         t = new;
77
78         if (*s == 0) {
79                 /* path is only stripped */
80                 return new;
81         }
82
83         if (prefix) {
84                 strcpy(new, prefix);
85                 t = new + strlen(prefix);
86         }
87
88         /* skip leading curdir */
89         if (*s && *s == '.' && s[1] && s[1] == '/') {
90                 s += 2;
91         }
92
93         /* skip leading slashes */
94         while (*s == '/') {
95                 s++;
96         }
97
98         if (*t != '/') {
99                 *t++ = '/';
100         }
101
102         for (; *s; s++) {
103                 /* skip multiple slashes */
104                 if (*s == '/' && s[1] && s[1] == '/') {
105                         continue;
106                 }
107                 /* skip trailing slash */
108                 if (*s == '/' && !s[1]) {
109                         continue;
110                 }
111
112                 /* skip curdirs */
113                 if (*s == '/' && s[1] && s[1] == '.' && s[2] && s[2] == '/') {
114                         s += 1;
115                         continue;
116                 }
117
118                 if (*s == '/' && s[1] && s[1] == '.' && s[2] == 0) {
119                         s += 1;
120                         continue;
121                 }
122
123                 *t++ = *s;
124         }
125         *t = 0;
126
127         return new;
128 }
129
130 char *getuser(uid_t uid) {
131         struct passwd *pw;
132
133         pw = getpwuid(uid);
134         if (pw) {
135                 return strdup(pw->pw_name);
136         }
137         return 0;
138 }
139
140 char *getgroup(uid_t uid) {
141         struct passwd *pw;
142
143         pw = getpwuid(uid);
144         if (pw) {
145                 return strdup(pw->pw_name);
146         }
147         return 0;
148 }
149
150 void free_file(struct zpm_file *file) {
151         free(file->path);
152         free(file->owner);
153         free(file->group);
154         free(file->target);
155         file->path = file->owner = file->group = file->target = 0;
156 }
157
158 int stat_file(struct zpm_file *file, char *path, struct opts *opt) {
159                 struct stat st;
160                 int rv;
161
162                 file->data = path;
163
164                 errno = 0;
165                 if (opt->followsymlinks) {
166                         rv = stat(path, &st);
167                 } else {
168                         rv = lstat(path, &st);
169                 }
170
171                 if (rv == -1) {
172                         return 0;
173                 }
174
175                 switch (st.st_mode & S_IFMT) {
176                         case S_IFBLK: file->type = 'b'; break;
177                         case S_IFCHR: file->type = 'c'; break;
178                         case S_IFDIR: file->type = 'd'; break;
179                         case S_IFIFO: file->type = 'p'; break;
180                         case S_IFLNK: file->type = 'l'; break;
181                         case S_IFREG: file->type = 'r'; break;
182                         case S_IFSOCK: file->type = 's'; break;
183                         default: file->type = 0; break;
184                 }
185
186                 file->configuration = opt->isconfig;
187
188                 if (file->type == 0) {
189                         return 0;
190                 }
191
192                 if (file->type == 'l') {
193                         if (opt->cltarget) {
194                                 file->target = strdup(opt->cltarget);
195                         } else {
196                                 char linkval[4096];
197                                 size_t n;
198                                 n = readlink(path, linkval, sizeof linkval);
199                                 if (n >= sizeof linkval) {
200                                         return 0;
201                                 }
202                                 file->target = strdup(linkval);
203                         }
204                 }
205                 
206                 /* strip and prefix */
207                 file->path = cleanpath(path, opt->strip, opt->prefix);
208                 if (file->path == 0) {
209                         free_file(file);
210                         return 0;
211                 }
212
213                 if (opt->clmode) {
214                         file->mode = opt->clmode;
215                 } else {
216                         file->mode = st.st_mode;
217                 }
218
219                 if (opt->cluser) {
220                         file->owner = strdup(opt->cluser);
221                 } else {
222                         file->owner = getuser(st.st_uid);
223                 }
224
225                 if (file->owner == 0) {
226                         free_file(file);
227                         return 0;
228                 }
229
230                 if (opt->clgroup) {
231                         file->group = strdup(opt->clgroup);
232                 } else {
233                         file->group = getgroup(st.st_gid);
234                 }
235                 if (file->group == 0) {
236                         free_file(file);
237                         return 0;
238                 }
239
240                 file->mtime = st.st_mtime;
241
242                 return 1;
243 }
244
245 int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt);
246
247 char *pathcat(char *dir, char *path) {
248         size_t dirlen = 0, pathlen = 0;
249         char *cat;
250
251         /* chop off trailing / on dir */
252         if (dir) {
253                 dirlen = strlen(dir);
254                 while (dirlen && dir[dirlen-1] == '/') {
255                         dirlen--;
256                 }
257         }
258
259         if (path) {
260                 pathlen = strlen(path);
261                 while (*path && *path == '/') {
262                         path++;
263                         pathlen--;
264                 }
265         }
266
267         cat = malloc(dirlen + pathlen + 2);
268         if (cat) {
269                 strncpy(cat, dir, dirlen);
270                 cat[dirlen] = '/';
271                 strcpy(cat+dirlen+1, path);
272         }
273         return cat;
274 }
275
276 int add_dir(struct zpm *zpm, char *path, struct opts *opt) {
277         DIR *dir;
278         struct dirent *de;
279         struct zpm_file file = { 0 };
280         int fail = 0;
281
282         char *dpath = 0;
283
284         dir = opendir(path);
285
286         if (!dir) {
287                 zpm_seterror(zpm, "can't open dir %s: %s", path, strerror(errno));
288                 return 0;
289         }
290
291         while ((de = readdir(dir))) {
292                 if (!strcmp(de->d_name, ".")) {
293                         continue;
294                 }
295                 if (!strcmp(de->d_name, "..")) {
296                         continue;
297                 }
298
299                 dpath = pathcat(path, de->d_name);
300                 if (!dpath) {
301                         zpm_seterror(zpm, "pathcat failed");
302                         fail = 1;
303                         break;
304                 }
305                 
306                 if (!stat_file(&file, dpath, opt)) {
307                         zpm_seterror(zpm, "stat %s failed: %s",
308                                         dpath, strerror(errno));
309                         fail = 1;
310                         break;
311                 }
312
313                 if (!add_file(zpm, &file, opt)) {
314                         free_file(&file);
315                         fail = 1;
316                         break;
317                 }
318                 free_file(&file);
319                 free(dpath);
320         }
321         closedir(dir);
322         return fail ? 0 : 1;
323 }
324
325 int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt) {
326         char hash[ZPM_HASH_STRLEN+1];
327
328         if (file->path[0] != 0) {
329                 if (file->type == 'r') {
330                         if (opt->addcontent) {
331                                 if (zpm_import(zpm, file->data, 0, hash)) {
332                                         strcpy(file->hash, hash);
333                                 } else {
334                                         return 0;
335                                 }
336                         } else {
337                                 zpm_hash(file->data, hash, 0);
338                                 strcpy(file->hash, hash);
339                         }
340                 }
341
342                 zpm_db_run(zpm, "insert or replace into packagefiles "
343                                 "(package,version,release,path,mode,mtime,username,"
344                                 "groupname,filetype,hash,configuration,target)"
345                                 " values "
346                                 "(%Q, %Q, %d, %Q, %o, %d, %Q, %Q, '%c', %Q, %d, %Q)",
347                                 opt->package, opt->version, opt->release, file->path,
348                                 file->mode, (int)file->mtime, file->owner, file->group,
349                                 file->type,
350                                 file->type == 'r' ? file->hash : NULL,
351                                 file->configuration,
352                                 file->type == 'l' ? file->target : NULL
353                           );
354
355                 if (zpm->error) {
356                         return 0;
357                 }
358
359                 if (opt->verbose > 1) {
360                         printf("%c%o %s:%s %s\n", file->type, file->mode, file->owner, file->group, file->path);
361                 } else if (opt->verbose > 0) {
362
363                         printf("%s\n", file->path);
364                         fflush(stdout);
365                 }
366         }
367
368         if (opt->recursive && file->type == 'd') {
369                 if (!add_dir(zpm, file->data, opt)) {
370                         return 0;
371                 }
372         }
373
374         return 1;
375 }
376
377 int main(int ac, char **av) {
378         struct zpm zpm;
379         struct opts opt = { 0 };
380         int i;
381         int option;
382
383         char hash[ZPM_HASH_STRLEN+1];
384
385         char *dbfile = getenv("ZPMDB");
386
387         if (!dbfile) {
388                 dbfile = "/var/lib/zpm/local.db";
389         }
390
391         opt.addcontent = 1;
392
393         while ((option = getopt(ac, av, "CF:H:M:NP:S:T:cf:g:hlm:ru:vxz")) != -1) {
394                 switch (option) {
395                         case 'C': opt.complete = 1; break;
396                         case 'F': opt.cltype = optarg; break;
397                         case 'H': opt.clhash = optarg; break;
398                         case 'M': opt.clmtime = optarg; break;
399                         case 'N': opt.addcontent = 0; break;
400                         case 'P': opt.prefix = optarg; break;
401                         case 'S': opt.strip = optarg; break;
402                         case 'T': opt.cltarget = optarg; break;
403                         case 'c': opt.isconfig = 1; break;
404                         case 'f': dbfile = optarg; break;
405                         case 'g': opt.clgroup = optarg; break;
406                         case 'h': opt.followsymlinks = 1; break;
407                         case 'l': opt.opt_l = 1; break;
408                         case 'm': opt.clmode = strtol(optarg, NULL, 8); break;
409                         case 'r': opt.recursive = 1; break;
410                         case 'u': opt.cluser = optarg; break;
411                         case 'v': opt.verbose++; break;
412                         case 'x': opt.xargs = 1; dieval = 255; break;
413                         case 'z': opt.noclear = 1; break;
414                         default:
415                                   exit(EXIT_FAILURE);
416                                   break;
417                 }
418         }
419
420         if (ac < optind) {
421                 exit(EXIT_FAILURE);
422         }
423
424         if (!zpm_open(&zpm, dbfile)) {
425                 die("can't open zpm db %s", dbfile);
426         }
427
428 #if 0
429         i = sqlite3_config(SQLITE_CONFIG_MMAP_SIZE,98222080);
430         if (i != SQLITE_OK) {
431                 exit(3);
432         }
433 #endif
434
435         /* package is first arg */
436         char *pkgstr;
437         char *pkgid;
438         pkgstr = av[optind++];
439         pkgid = zpm_findpkg(&zpm, pkgstr, NULL);
440         /* TODO if not found, error */
441         if (!pkgid) {
442                 die("package %s not found\n", pkgstr);
443         }
444
445         char package[128];
446         char version[64];
447         int release;
448
449         zpm_parse_package(pkgid, package, version, &release);
450         opt.package = package;
451         opt.version = version;
452         opt.release = release;
453
454         if (opt.verbose) {
455                 printf("adding to %s %s\n", dbfile, pkgid);
456         }
457         fflush(stdout);
458
459         //zpm_begin(&zpm);
460
461         struct zpm_file file;
462         for (i=optind; av[i]; i++) {
463                 if (!stat_file(&file, av[i], &opt)) {
464                         die("can't stat %s: %s", av[i], strerror(errno));
465                 }
466                 file.data = av[i];
467                 if (add_file(&zpm, &file, &opt)) {
468                         free_file(&file);
469                 } else {
470                         die("error: %s", zpm.errmsg);
471                 }
472         }
473
474         if (opt.complete) {
475                 zpm_db_run(&zpm, "update packages set build_time = %d where package = %Q and version = %Q and release = %d", time(NULL), package, version, release);
476                 zpm_package_hash(&zpm, pkgid, hash);
477                 zpm_package_sethash(&zpm, pkgid, hash);
478         } else if (!opt.noclear) {
479                 zpm_db_run(&zpm, "update packages set build_time = null, hash = null where package = %Q and version = %Q and release = %d", package, version, release);
480         }
481         /* TODO error check */
482
483         //zpm_commit(&zpm);
484         zpm_close(&zpm);
485         return 0;
486 }