]> pd.if.org Git - zpackage/blob - zpm-add.c
add null byte on readlink string
[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                                 linkval[n] = 0;
203                                 file->target = strdup(linkval);
204                         }
205                 }
206                 
207                 /* strip and prefix */
208                 file->path = cleanpath(path, opt->strip, opt->prefix);
209                 if (file->path == 0) {
210                         free_file(file);
211                         return 0;
212                 }
213
214                 if (opt->clmode) {
215                         file->mode = opt->clmode;
216                 } else {
217                         file->mode = st.st_mode;
218                 }
219
220                 if (opt->cluser) {
221                         file->owner = strdup(opt->cluser);
222                 } else {
223                         file->owner = getuser(st.st_uid);
224                 }
225
226                 if (file->owner == 0) {
227                         free_file(file);
228                         return 0;
229                 }
230
231                 if (opt->clgroup) {
232                         file->group = strdup(opt->clgroup);
233                 } else {
234                         file->group = getgroup(st.st_gid);
235                 }
236                 if (file->group == 0) {
237                         free_file(file);
238                         return 0;
239                 }
240
241                 file->mtime = st.st_mtime;
242
243                 return 1;
244 }
245
246 int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt);
247
248 char *pathcat(char *dir, char *path) {
249         size_t dirlen = 0, pathlen = 0;
250         char *cat;
251
252         /* chop off trailing / on dir */
253         if (dir) {
254                 dirlen = strlen(dir);
255                 while (dirlen && dir[dirlen-1] == '/') {
256                         dirlen--;
257                 }
258         }
259
260         if (path) {
261                 pathlen = strlen(path);
262                 while (*path && *path == '/') {
263                         path++;
264                         pathlen--;
265                 }
266         }
267
268         cat = malloc(dirlen + pathlen + 2);
269         if (cat) {
270                 strncpy(cat, dir, dirlen);
271                 cat[dirlen] = '/';
272                 strcpy(cat+dirlen+1, path);
273         }
274         return cat;
275 }
276
277 int add_dir(struct zpm *zpm, char *path, struct opts *opt) {
278         DIR *dir;
279         struct dirent *de;
280         struct zpm_file file = { 0 };
281         int fail = 0;
282
283         char *dpath = 0;
284
285         dir = opendir(path);
286
287         if (!dir) {
288                 zpm_seterror(zpm, "can't open dir %s: %s", path, strerror(errno));
289                 return 0;
290         }
291
292         while ((de = readdir(dir))) {
293                 if (!strcmp(de->d_name, ".")) {
294                         continue;
295                 }
296                 if (!strcmp(de->d_name, "..")) {
297                         continue;
298                 }
299
300                 dpath = pathcat(path, de->d_name);
301                 if (!dpath) {
302                         zpm_seterror(zpm, "pathcat failed");
303                         fail = 1;
304                         break;
305                 }
306                 
307                 if (!stat_file(&file, dpath, opt)) {
308                         zpm_seterror(zpm, "stat %s failed: %s",
309                                         dpath, strerror(errno));
310                         fail = 1;
311                         break;
312                 }
313
314                 if (!add_file(zpm, &file, opt)) {
315                         free_file(&file);
316                         fail = 1;
317                         break;
318                 }
319                 free_file(&file);
320                 free(dpath);
321         }
322         closedir(dir);
323         return fail ? 0 : 1;
324 }
325
326 int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt) {
327         char hash[ZPM_HASH_STRLEN+1];
328
329         if (file->path[0] != 0) {
330                 if (file->type == 'r') {
331                         if (opt->addcontent) {
332                                 if (zpm_import(zpm, file->data, 0, hash)) {
333                                         strcpy(file->hash, hash);
334                                 } else {
335                                         return 0;
336                                 }
337                         } else {
338                                 zpm_hash(file->data, hash, 0);
339                                 strcpy(file->hash, hash);
340                         }
341                 }
342
343                 zpm_db_run(zpm, "insert or replace into packagefiles "
344                                 "(package,version,release,path,mode,mtime,username,"
345                                 "groupname,filetype,hash,configuration,target)"
346                                 " values "
347                                 "(%Q, %Q, %d, %Q, %o, %d, %Q, %Q, '%c', %Q, %d, %Q)",
348                                 opt->package, opt->version, opt->release, file->path,
349                                 file->mode, (int)file->mtime, file->owner, file->group,
350                                 file->type,
351                                 file->type == 'r' ? file->hash : NULL,
352                                 file->configuration,
353                                 file->type == 'l' ? file->target : NULL
354                           );
355
356                 if (zpm->error) {
357                         return 0;
358                 }
359
360                 if (opt->verbose > 1) {
361                         printf("%c%o %s:%s %s\n", file->type, file->mode, file->owner, file->group, file->path);
362                 } else if (opt->verbose > 0) {
363
364                         printf("%s\n", file->path);
365                         fflush(stdout);
366                 }
367         }
368
369         if (opt->recursive && file->type == 'd') {
370                 if (!add_dir(zpm, file->data, opt)) {
371                         return 0;
372                 }
373         }
374
375         return 1;
376 }
377
378 int main(int ac, char **av) {
379         struct zpm zpm;
380         struct opts opt = { 0 };
381         int i;
382         int option;
383
384         char hash[ZPM_HASH_STRLEN+1];
385
386         char *dbfile = getenv("ZPMDB");
387
388         if (!dbfile) {
389                 dbfile = "/var/lib/zpm/local.db";
390         }
391
392         opt.addcontent = 1;
393
394         while ((option = getopt(ac, av, "CF:H:M:NP:S:T:cf:g:hlm:ru:vxz")) != -1) {
395                 switch (option) {
396                         case 'C': opt.complete = 1; break;
397                         case 'F': opt.cltype = optarg; break;
398                         case 'H': opt.clhash = optarg; break;
399                         case 'M': opt.clmtime = optarg; break;
400                         case 'N': opt.addcontent = 0; break;
401                         case 'P': opt.prefix = optarg; break;
402                         case 'S': opt.strip = optarg; break;
403                         case 'T': opt.cltarget = optarg; break;
404                         case 'c': opt.isconfig = 1; break;
405                         case 'f': dbfile = optarg; break;
406                         case 'g': opt.clgroup = optarg; break;
407                         case 'h': opt.followsymlinks = 1; break;
408                         case 'l': opt.opt_l = 1; break;
409                         case 'm': opt.clmode = strtol(optarg, NULL, 8); break;
410                         case 'r': opt.recursive = 1; break;
411                         case 'u': opt.cluser = optarg; break;
412                         case 'v': opt.verbose++; break;
413                         case 'x': opt.xargs = 1; dieval = 255; break;
414                         case 'z': opt.noclear = 1; break;
415                         default:
416                                   exit(EXIT_FAILURE);
417                                   break;
418                 }
419         }
420
421         if (ac < optind) {
422                 exit(EXIT_FAILURE);
423         }
424
425         if (!zpm_open(&zpm, dbfile)) {
426                 die("can't open zpm db %s", dbfile);
427         }
428
429 #if 0
430         i = sqlite3_config(SQLITE_CONFIG_MMAP_SIZE,98222080);
431         if (i != SQLITE_OK) {
432                 exit(3);
433         }
434 #endif
435
436         /* package is first arg */
437         char *pkgstr;
438         char *pkgid;
439         pkgstr = av[optind++];
440         pkgid = zpm_findpkg(&zpm, pkgstr, NULL);
441         /* TODO if not found, error */
442         if (!pkgid) {
443                 die("package %s not found\n", pkgstr);
444         }
445
446         char package[128];
447         char version[64];
448         int release;
449
450         zpm_parse_package(pkgid, package, version, &release);
451         opt.package = package;
452         opt.version = version;
453         opt.release = release;
454
455         if (opt.verbose) {
456                 printf("adding to %s %s\n", dbfile, pkgid);
457         }
458         fflush(stdout);
459
460         //zpm_begin(&zpm);
461
462         struct zpm_file file;
463         for (i=optind; av[i]; i++) {
464                 if (!stat_file(&file, av[i], &opt)) {
465                         die("can't stat %s: %s", av[i], strerror(errno));
466                 }
467                 file.data = av[i];
468                 if (add_file(&zpm, &file, &opt)) {
469                         free_file(&file);
470                 } else {
471                         die("error: %s", zpm.errmsg);
472                 }
473         }
474
475         if (opt.complete) {
476                 zpm_db_run(&zpm, "update packages set build_time = %d where package = %Q and version = %Q and release = %d", time(NULL), package, version, release);
477                 zpm_package_hash(&zpm, pkgid, hash);
478                 zpm_package_sethash(&zpm, pkgid, hash);
479         } else if (!opt.noclear) {
480                 zpm_db_run(&zpm, "update packages set build_time = null, hash = null where package = %Q and version = %Q and release = %d", package, version, release);
481         }
482         /* TODO error check */
483
484         //zpm_commit(&zpm);
485         zpm_close(&zpm);
486         return 0;
487 }