Axe the rpmsq debug code which was never getting built anyway
[platform/upstream/rpm.git] / rpmio / rpmfileutil.c
1 #include "system.h"
2
3 #if HAVE_GELF_H
4
5 #include <gelf.h>
6
7 #if !defined(DT_GNU_PRELINKED)
8 #define DT_GNU_PRELINKED        0x6ffffdf5
9 #endif
10 #if !defined(DT_GNU_LIBLIST)
11 #define DT_GNU_LIBLIST          0x6ffffef9
12 #endif
13
14 #endif
15
16 #if defined(HAVE_MMAP)
17 #include <sys/mman.h>
18 #endif
19
20 #include <sys/wait.h>
21 #include <errno.h>
22 #include <popt.h>
23 #include <ctype.h>
24
25 #include <rpm/rpmfileutil.h>
26 #include <rpm/rpmurl.h>
27 #include <rpm/rpmmacro.h>
28 #include <rpm/rpmlog.h>
29 #include <rpm/argv.h>
30
31 #include "rpmio/rpmio_internal.h"
32
33 #include "debug.h"
34
35 static const char *rpm_config_dir = NULL;
36
37 static int open_dso(const char * path, pid_t * pidp, rpm_loff_t *fsizep)
38 {
39     static const char * cmd = NULL;
40     static int initted = 0;
41     int fdno;
42
43     if (!initted) {
44         cmd = rpmExpand("%{?__prelink_undo_cmd}", NULL);
45         initted++;
46     }
47
48     if (pidp) *pidp = 0;
49
50     if (fsizep) {
51         struct stat sb, * st = &sb;
52         if (stat(path, st) < 0)
53             return -1;
54         *fsizep = st->st_size;
55     }
56
57     fdno = open(path, O_RDONLY);
58     if (fdno < 0)
59         return fdno;
60
61     if (!(cmd && *cmd))
62         return fdno;
63
64 #if HAVE_GELF_H && HAVE_LIBELF
65  {  Elf *elf = NULL;
66     Elf_Scn *scn = NULL;
67     Elf_Data *data = NULL;
68     GElf_Ehdr ehdr;
69     GElf_Shdr shdr;
70     GElf_Dyn dyn;
71     int bingo;
72
73     (void) elf_version(EV_CURRENT);
74
75     if ((elf = elf_begin (fdno, ELF_C_READ, NULL)) == NULL
76      || elf_kind(elf) != ELF_K_ELF
77      || gelf_getehdr(elf, &ehdr) == NULL
78      || !(ehdr.e_type == ET_DYN || ehdr.e_type == ET_EXEC))
79         goto exit;
80
81     bingo = 0;
82     while (!bingo && (scn = elf_nextscn(elf, scn)) != NULL) {
83         (void) gelf_getshdr(scn, &shdr);
84         if (shdr.sh_type != SHT_DYNAMIC)
85             continue;
86         while (!bingo && (data = elf_getdata (scn, data)) != NULL) {
87             int maxndx = data->d_size / shdr.sh_entsize;
88             int ndx;
89
90             for (ndx = 0; ndx < maxndx; ++ndx) {
91                 (void) gelf_getdyn (data, ndx, &dyn);
92                 if (!(dyn.d_tag == DT_GNU_PRELINKED || dyn.d_tag == DT_GNU_LIBLIST))
93                     continue;
94                 bingo = 1;
95                 break;
96             }
97         }
98     }
99
100     if (pidp != NULL && bingo) {
101         int pipes[2];
102         pid_t pid;
103         int xx;
104
105         xx = close(fdno);
106         pipes[0] = pipes[1] = -1;
107         xx = pipe(pipes);
108         if (!(pid = fork())) {
109             ARGV_t av, lib;
110             argvSplit(&av, cmd, " ");
111
112             xx = close(pipes[0]);
113             xx = dup2(pipes[1], STDOUT_FILENO);
114             xx = close(pipes[1]);
115             if ((lib = argvSearch(av, "library", NULL)) != NULL) {
116                 *lib = (char *) path;
117                 unsetenv("MALLOC_CHECK_");
118                 xx = execve(av[0], av+1, environ);
119             }
120             _exit(127);
121         }
122         *pidp = pid;
123         fdno = pipes[0];
124         xx = close(pipes[1]);
125     }
126
127 exit:
128     if (elf) (void) elf_end(elf);
129  }
130 #endif
131
132     return fdno;
133 }
134
135 int rpmDoDigest(int algo, const char * fn,int asAscii,
136                 unsigned char * digest, rpm_loff_t * fsizep)
137 {
138     const char * path;
139     urltype ut = urlPath(fn, &path);
140     unsigned char * dig = NULL;
141     size_t diglen;
142     unsigned char buf[32*BUFSIZ];
143     FD_t fd;
144     rpm_loff_t fsize = 0;
145     pid_t pid = 0;
146     int rc = 0;
147     int fdno;
148
149     fdno = open_dso(path, &pid, &fsize);
150     if (fdno < 0) {
151         rc = 1;
152         goto exit;
153     }
154
155     /* file to large (32 MB), do not mmap file */
156     if (fsize > (size_t) 32*1024*1024)
157       if (ut == URL_IS_PATH || ut == URL_IS_UNKNOWN)
158         ut = URL_IS_DASH; /* force fd io */
159
160     switch(ut) {
161     case URL_IS_PATH:
162     case URL_IS_UNKNOWN:
163 #ifdef HAVE_MMAP
164       if (pid == 0) {
165         int xx;
166         DIGEST_CTX ctx;
167         void * mapped;
168
169         if (fsize) {
170             mapped = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fdno, 0);
171             if (mapped == MAP_FAILED) {
172                 xx = close(fdno);
173                 rc = 1;
174                 break;
175             }
176
177 #ifdef  MADV_SEQUENTIAL
178             xx = madvise(mapped, fsize, MADV_SEQUENTIAL);
179 #endif
180         }
181
182         ctx = rpmDigestInit(algo, RPMDIGEST_NONE);
183         if (fsize)
184             xx = rpmDigestUpdate(ctx, mapped, fsize);
185         xx = rpmDigestFinal(ctx, (void **)&dig, &diglen, asAscii);
186         if (fsize)
187             xx = munmap(mapped, fsize);
188         xx = close(fdno);
189         break;
190       }
191 #endif
192     case URL_IS_HTTPS:
193     case URL_IS_HTTP:
194     case URL_IS_FTP:
195     case URL_IS_HKP:
196     case URL_IS_DASH:
197     default:
198         /* Either use the pipe to prelink -y or open the URL. */
199         fd = (pid != 0) ? fdDup(fdno) : Fopen(fn, "r.ufdio");
200         (void) close(fdno);
201         if (fd == NULL || Ferror(fd)) {
202             rc = 1;
203             if (fd != NULL)
204                 (void) Fclose(fd);
205             break;
206         }
207         
208         fdInitDigest(fd, algo, 0);
209         fsize = 0;
210         while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
211             fsize += rc;
212         fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii);
213         if (Ferror(fd))
214             rc = 1;
215
216         (void) Fclose(fd);
217         break;
218     }
219
220     /* Reap the prelink -y helper. */
221     if (pid) {
222         int status;
223         (void) waitpid(pid, &status, 0);
224         if (!WIFEXITED(status) || WEXITSTATUS(status))
225             rc = 1;
226     }
227
228 exit:
229     if (fsizep)
230         *fsizep = fsize;
231     if (!rc)
232         memcpy(digest, dig, diglen);
233     dig = _free(dig);
234
235     return rc;
236 }
237
238 FD_t rpmMkTemp(char *templ)
239 {
240     int sfd;
241     FD_t tfd = NULL;
242
243     sfd = mkstemp(templ);
244     if (sfd < 0) {
245         goto exit;
246     }
247
248     tfd = fdDup(sfd);
249     close(sfd);
250
251 exit:
252     return tfd;
253 }
254
255 FD_t rpmMkTempFile(const char * prefix, char **fn)
256 {
257     const char *tpmacro = "%{_tmppath}"; /* always set from rpmrc */
258     char *tempfn;
259     static int _initialized = 0;
260     FD_t tfd = NULL;
261
262     if (!prefix) prefix = "";
263
264     /* Create the temp directory if it doesn't already exist. */
265     if (!_initialized) {
266         _initialized = 1;
267         tempfn = rpmGenPath(prefix, tpmacro, NULL);
268         if (rpmioMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1))
269             goto exit;
270         free(tempfn);
271     }
272
273     tempfn = rpmGetPath(prefix, tpmacro, "/rpm-tmp.XXXXXX", NULL);
274     tfd = rpmMkTemp(tempfn);
275
276     if (tfd == NULL || Ferror(tfd)) {
277         rpmlog(RPMLOG_ERR, _("error creating temporary file %s: %m\n"), tempfn);
278         goto exit;
279     }
280
281 exit:
282     if (tfd != NULL && fn)
283         *fn = tempfn;
284     else
285         free(tempfn);
286
287     return tfd;
288 }
289
290 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
291 {
292     char *d, *de;
293     int rc;
294
295     if (path == NULL || *path == '\0')
296         return -1;
297     d = rstrcat(NULL, path);
298     if (d[strlen(d)-1] != '/') {
299         rstrcat(&d,"/");
300     }
301     de = d;
302     for (;(de=strchr(de+1,'/'));) {
303         struct stat st;
304         *de = '\0';
305         rc = stat(d, &st);
306         if (rc) {
307             if (errno != ENOENT)
308                 goto exit;
309             rc = mkdir(d, mode);
310             if (rc)
311                 goto exit;
312             rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n", path, mode);
313             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
314                 rc = chown(d, uid, gid);
315                 if (rc)
316                     goto exit;
317             }
318         } else if (!S_ISDIR(st.st_mode)) {
319             rc = ENOTDIR;
320             goto exit;
321         }
322         *de = '/';
323     }
324     rc = 0;
325 exit:
326     free(d);
327     return rc;
328 }
329
330 int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed)
331 {
332     FD_t fd;
333     ssize_t nb;
334     int rc = -1;
335     unsigned char magic[13];
336
337     *compressed = COMPRESSED_NOT;
338
339     fd = Fopen(file, "r.ufdio");
340     if (fd == NULL || Ferror(fd)) {
341         /* XXX Fstrerror */
342         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
343         if (fd) (void) Fclose(fd);
344         return 1;
345     }
346     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
347     if (nb < 0) {
348         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
349         rc = 1;
350     } else if (nb < sizeof(magic)) {
351         rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
352                 file, (unsigned)sizeof(magic));
353         rc = 0;
354     }
355     (void) Fclose(fd);
356     if (rc >= 0)
357         return rc;
358
359     rc = 0;
360
361     if ((magic[0] == 'B') && (magic[1] == 'Z')) {
362         *compressed = COMPRESSED_BZIP2;
363     } else if ((magic[0] == 0120) && (magic[1] == 0113) &&
364          (magic[2] == 0003) && (magic[3] == 0004)) {    /* pkzip */
365         *compressed = COMPRESSED_ZIP;
366     } else if ((magic[0] == 0xfd) && (magic[1] == 0x37) &&
367                (magic[2] == 0x7a) && (magic[3] == 0x58) &&
368                (magic[4] == 0x5a) && (magic[5] == 0x00)) {
369         /* new style xz (lzma) with magic */
370         *compressed = COMPRESSED_XZ;
371     } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
372         ((magic[0] == 0037) && (magic[1] == 0236)) ||   /* old gzip */
373         ((magic[0] == 0037) && (magic[1] == 0036)) ||   /* pack */
374         ((magic[0] == 0037) && (magic[1] == 0240)) ||   /* SCO lzh */
375         ((magic[0] == 0037) && (magic[1] == 0235))      /* compress */
376         ) {
377         *compressed = COMPRESSED_OTHER;
378     } else if (rpmFileHasSuffix(file, ".lzma")) {
379         *compressed = COMPRESSED_LZMA;
380     }
381
382     return rc;
383 }
384
385 /* @todo "../sbin/./../bin/" not correct. */
386 char *rpmCleanPath(char * path)
387 {
388     const char *s;
389     char *se, *t, *te;
390     int begin = 1;
391
392     if (path == NULL)
393         return NULL;
394
395 /*fprintf(stderr, "*** RCP %s ->\n", path); */
396     s = t = te = path;
397     while (*s != '\0') {
398 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
399         switch(*s) {
400         case ':':                       /* handle url's */
401             if (s[1] == '/' && s[2] == '/') {
402                 *t++ = *s++;
403                 *t++ = *s++;
404                 break;
405             }
406             begin=1;
407             break;
408         case '/':
409             /* Move parent dir forward */
410             for (se = te + 1; se < t && *se != '/'; se++)
411                 {};
412             if (se < t && *se == '/') {
413                 te = se;
414 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
415             }
416             while (s[1] == '/')
417                 s++;
418             while (t > path && t[-1] == '/')
419                 t--;
420             break;
421         case '.':
422             /* Leading .. is special */
423             /* Check that it is ../, so that we don't interpret */
424             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
425             /* in the case of "...", this ends up being processed*/
426             /* as "../.", and the last '.' is stripped.  This   */
427             /* would not be correct processing.                 */
428             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
429 /*fprintf(stderr, "    leading \"..\"\n"); */
430                 *t++ = *s++;
431                 break;
432             }
433             /* Single . is special */
434             if (begin && s[1] == '\0') {
435                 break;
436             }
437             /* Handle the ./ cases */
438             if (t > path && t[-1] == '/') {
439                 /* Trim embedded ./ */
440                 if (s[1] == '/') {
441                     s+=2;
442                     continue;
443                 }
444                 /* Trim trailing /. */
445                 if (s[1] == '\0') {
446                     s++;
447                     continue;
448                 }
449             }
450             /* Trim embedded /../ and trailing /.. */
451             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
452                 t = te;
453                 /* Move parent dir forward */
454                 if (te > path)
455                     for (--te; te > path && *te != '/'; te--)
456                         {};
457 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
458                 s++;
459                 s++;
460                 continue;
461             }
462             break;
463         default:
464             begin = 0;
465             break;
466         }
467         *t++ = *s++;
468     }
469
470     /* Trim trailing / (but leave single / alone) */
471     if (t > &path[1] && t[-1] == '/')
472         t--;
473     *t = '\0';
474
475 /*fprintf(stderr, "\t%s\n", path); */
476     return path;
477 }
478
479 /* Merge 3 args into path, any or all of which may be a url. */
480
481 char * rpmGenPath(const char * urlroot, const char * urlmdir,
482                 const char *urlfile)
483 {
484     char * xroot = rpmGetPath(urlroot, NULL);
485     const char * root = xroot;
486     char * xmdir = rpmGetPath(urlmdir, NULL);
487     const char * mdir = xmdir;
488     char * xfile = rpmGetPath(urlfile, NULL);
489     const char * file = xfile;
490     char * result;
491     char * url = NULL;
492     int nurl = 0;
493     int ut;
494
495     ut = urlPath(xroot, &root);
496     if (url == NULL && ut > URL_IS_DASH) {
497         url = xroot;
498         nurl = root - xroot;
499     }
500     if (root == NULL || *root == '\0') root = "/";
501
502     ut = urlPath(xmdir, &mdir);
503     if (url == NULL && ut > URL_IS_DASH) {
504         url = xmdir;
505         nurl = mdir - xmdir;
506     }
507     if (mdir == NULL || *mdir == '\0') mdir = "/";
508
509     ut = urlPath(xfile, &file);
510     if (url == NULL && ut > URL_IS_DASH) {
511         url = xfile;
512         nurl = file - xfile;
513     }
514
515     if (url && nurl > 0) {
516         char *t = rstrcat(NULL, url);
517         t[nurl] = '\0';
518         url = t;
519     } else
520         url = xstrdup("");
521
522     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
523
524     xroot = _free(xroot);
525     xmdir = _free(xmdir);
526     xfile = _free(xfile);
527     free(url);
528     return result;
529 }
530
531 /* Return concatenated and expanded canonical path. */
532
533 char * rpmGetPath(const char *path, ...)
534 {
535     va_list ap;
536     char *dest = NULL, *res;
537     const char *s;
538
539     if (path == NULL)
540         return xstrdup("");
541
542     va_start(ap, path);
543     for (s = path; s; s = va_arg(ap, const char *)) {
544         rstrcat(&dest, s);
545     }
546     va_end(ap);
547
548     res = rpmExpand(dest, NULL);
549     free(dest);
550
551     return rpmCleanPath(res);
552 }
553
554 int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr)
555 {
556     int ac = 0;
557     const char ** av = NULL;
558     int argc = 0;
559     ARGV_t argv = NULL;
560     char * globRoot = NULL;
561     const char *home = getenv("HOME");
562     int gflags = 0;
563 #ifdef ENABLE_NLS
564     char * old_collate = NULL;
565     char * old_ctype = NULL;
566     const char * t;
567 #endif
568     size_t maxb, nb;
569     int i, j;
570     int rc;
571
572     if (home != NULL && strlen(home) > 0) 
573         gflags |= GLOB_TILDE;
574
575     /* Can't use argvSplit() here, it doesn't handle whitespace etc escapes */
576     rc = poptParseArgvString(patterns, &ac, &av);
577     if (rc)
578         return rc;
579
580 #ifdef ENABLE_NLS
581     t = setlocale(LC_COLLATE, NULL);
582     if (t)
583         old_collate = xstrdup(t);
584     t = setlocale(LC_CTYPE, NULL);
585     if (t)
586         old_ctype = xstrdup(t);
587     (void) setlocale(LC_COLLATE, "C");
588     (void) setlocale(LC_CTYPE, "C");
589 #endif
590         
591     if (av != NULL)
592     for (j = 0; j < ac; j++) {
593         char * globURL;
594         const char * path;
595         int ut = urlPath(av[j], &path);
596         int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN);
597         glob_t gl;
598
599         if (!local || (!glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL)) {
600             argvAdd(&argv, av[j]);
601             continue;
602         }
603         
604         gl.gl_pathc = 0;
605         gl.gl_pathv = NULL;
606         
607         rc = glob(av[j], gflags, NULL, &gl);
608         if (rc)
609             goto exit;
610
611         /* XXX Prepend the URL leader for globs that have stripped it off */
612         maxb = 0;
613         for (i = 0; i < gl.gl_pathc; i++) {
614             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
615                 maxb = nb;
616         }
617         
618         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
619         maxb += nb;
620         maxb += 1;
621         globURL = globRoot = xmalloc(maxb);
622
623         switch (ut) {
624         case URL_IS_PATH:
625         case URL_IS_DASH:
626             strncpy(globRoot, av[j], nb);
627             break;
628         case URL_IS_HTTPS:
629         case URL_IS_HTTP:
630         case URL_IS_FTP:
631         case URL_IS_HKP:
632         case URL_IS_UNKNOWN:
633         default:
634             break;
635         }
636         globRoot += nb;
637         *globRoot = '\0';
638
639         for (i = 0; i < gl.gl_pathc; i++) {
640             const char * globFile = &(gl.gl_pathv[i][0]);
641             if (globRoot > globURL && globRoot[-1] == '/')
642                 while (*globFile == '/') globFile++;
643             strcpy(globRoot, globFile);
644             argvAdd(&argv, globURL);
645         }
646         globfree(&gl);
647         globURL = _free(globURL);
648     }
649
650     argc = argvCount(argv);
651     if (argc > 0) {
652         if (argvPtr)
653             *argvPtr = argv;
654         if (argcPtr)
655             *argcPtr = argc;
656         rc = 0;
657     } else
658         rc = 1;
659
660
661 exit:
662 #ifdef ENABLE_NLS       
663     if (old_collate) {
664         (void) setlocale(LC_COLLATE, old_collate);
665         old_collate = _free(old_collate);
666     }
667     if (old_ctype) {
668         (void) setlocale(LC_CTYPE, old_ctype);
669         old_ctype = _free(old_ctype);
670     }
671 #endif
672     av = _free(av);
673     if (rc || argvPtr == NULL) {
674         argvFree(argv);
675     }
676     return rc;
677 }
678
679 char * rpmEscapeSpaces(const char * s)
680 {
681     const char * se;
682     char * t;
683     char * te;
684     size_t nb = 0;
685
686     for (se = s; *se; se++) {
687         if (isspace(*se))
688             nb++;
689         nb++;
690     }
691     nb++;
692
693     t = te = xmalloc(nb);
694     for (se = s; *se; se++) {
695         if (isspace(*se))
696             *te++ = '\\';
697         *te++ = *se;
698     }
699     *te = '\0';
700     return t;
701 }
702
703 int rpmFileHasSuffix(const char *path, const char *suffix)
704 {
705     size_t plen = strlen(path);
706     size_t slen = strlen(suffix);
707     return (plen >= slen && rstreq(path+plen-slen, suffix));
708 }
709
710 char * rpmGetCwd(void)
711 {
712     int currDirLen = 0;
713     char * currDir = NULL;
714
715     do {
716         currDirLen += 128;
717         currDir = xrealloc(currDir, currDirLen);
718         memset(currDir, 0, currDirLen);
719     } while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE);
720
721     return currDir;
722 }
723
724 int rpmMkdirs(const char *root, const char *pathstr)
725 {
726     ARGV_t dirs = NULL;
727     int rc = 0;
728     argvSplit(&dirs, pathstr, ":");
729     
730     for (char **d = dirs; *d; d++) {
731         char *path = rpmGetPath(root ? root : "", *d, NULL);
732         if ((rc = rpmioMkpath(path, 0755, -1, -1)) != 0) {
733             const char *msg = _("failed to create directory");
734             /* try to be more informative if the failing part was a macro */
735             if (**d == '%') {
736                 rpmlog(RPMLOG_ERR, "%s %s: %s: %m\n", msg, *d, path);
737             } else {
738                 rpmlog(RPMLOG_ERR, "%s %s: %m\n", msg, path);
739             }
740         }
741         free(path);
742         if (rc) break;
743     }
744     argvFree(dirs);
745     return rc;
746 }
747
748 const char *rpmConfigDir(void)
749 {
750     if (rpm_config_dir == NULL) {
751         char *rpmenv = getenv("RPM_CONFIGDIR");
752         rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR;
753     }
754     return rpm_config_dir;
755 }