msm: consistency for equally ranked keys
[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 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/wait.h>
19 #include <errno.h>
20 #include <popt.h>
21 #include <ctype.h>
22
23 #include <rpm/rpmfileutil.h>
24 #include <rpm/rpmurl.h>
25 #include <rpm/rpmmacro.h>
26 #include <rpm/rpmlog.h>
27 #include <rpm/argv.h>
28
29 #include "rpmio/rpmio_internal.h"
30
31 #include "debug.h"
32
33 static const char *rpm_config_dir = NULL;
34
35 static int is_prelinked(int fdno)
36 {
37     int prelinked = 0;
38 #if HAVE_GELF_H && HAVE_LIBELF
39     Elf *elf = NULL;
40     Elf_Scn *scn = NULL;
41     Elf_Data *data = NULL;
42     GElf_Ehdr ehdr;
43     GElf_Shdr shdr;
44     GElf_Dyn dyn;
45
46     (void) elf_version(EV_CURRENT);
47
48     if ((elf = elf_begin (fdno, ELF_C_READ, NULL)) == NULL ||
49                elf_kind(elf) != ELF_K_ELF || gelf_getehdr(elf, &ehdr) == NULL ||
50                !(ehdr.e_type == ET_DYN || ehdr.e_type == ET_EXEC))
51         goto exit;
52
53     while (!prelinked && (scn = elf_nextscn(elf, scn)) != NULL) {
54         (void) gelf_getshdr(scn, &shdr);
55         if (shdr.sh_type != SHT_DYNAMIC)
56             continue;
57         while (!prelinked && (data = elf_getdata (scn, data)) != NULL) {
58             int maxndx = data->d_size / shdr.sh_entsize;
59
60             for (int ndx = 0; ndx < maxndx; ++ndx) {
61                 (void) gelf_getdyn (data, ndx, &dyn);
62                 if (!(dyn.d_tag == DT_GNU_PRELINKED || dyn.d_tag == DT_GNU_LIBLIST))
63                     continue;
64                 prelinked = 1;
65                 break;
66             }
67         }
68     }
69
70 exit:
71     if (elf) (void) elf_end(elf);
72 #endif
73     return prelinked;
74 }
75
76 static int open_dso(const char * path, pid_t * pidp, rpm_loff_t *fsizep)
77 {
78     static const char * cmd = NULL;
79     static int initted = 0;
80     int fdno;
81
82     if (!initted) {
83         cmd = rpmExpand("%{?__prelink_undo_cmd}", NULL);
84         initted++;
85     }
86
87     if (pidp) *pidp = 0;
88
89     if (fsizep) {
90         struct stat sb, * st = &sb;
91         if (stat(path, st) < 0)
92             return -1;
93         *fsizep = st->st_size;
94     }
95
96     fdno = open(path, O_RDONLY);
97     if (fdno < 0)
98         return fdno;
99
100     if (!(cmd && *cmd))
101         return fdno;
102
103     if (pidp != NULL && is_prelinked(fdno)) {
104         int pipes[2];
105         pid_t pid;
106         int xx;
107
108         xx = close(fdno);
109         pipes[0] = pipes[1] = -1;
110         xx = pipe(pipes);
111         if (!(pid = fork())) {
112             ARGV_t av, lib;
113             argvSplit(&av, cmd, " ");
114
115             xx = close(pipes[0]);
116             xx = dup2(pipes[1], STDOUT_FILENO);
117             xx = close(pipes[1]);
118             if ((lib = argvSearch(av, "library", NULL)) != NULL) {
119                 *lib = (char *) path;
120                 unsetenv("MALLOC_CHECK_");
121                 xx = execve(av[0], av+1, environ);
122             }
123             _exit(127);
124         }
125         *pidp = pid;
126         fdno = pipes[0];
127         xx = close(pipes[1]);
128     }
129
130     return fdno;
131 }
132
133 int rpmDoDigest(int algo, const char * fn,int asAscii,
134                 unsigned char * digest, rpm_loff_t * fsizep)
135 {
136     const char * path;
137     urltype ut = urlPath(fn, &path);
138     unsigned char * dig = NULL;
139     size_t diglen;
140     unsigned char buf[32*BUFSIZ];
141     FD_t fd;
142     rpm_loff_t fsize = 0;
143     pid_t pid = 0;
144     int rc = 0;
145     int fdno;
146
147     fdno = open_dso(path, &pid, &fsize);
148     if (fdno < 0) {
149         rc = 1;
150         goto exit;
151     }
152
153     switch(ut) {
154     case URL_IS_PATH:
155     case URL_IS_UNKNOWN:
156     case URL_IS_HTTPS:
157     case URL_IS_HTTP:
158     case URL_IS_FTP:
159     case URL_IS_HKP:
160     case URL_IS_DASH:
161     default:
162         /* Either use the pipe to prelink -y or open the URL. */
163         fd = (pid != 0) ? fdDup(fdno) : Fopen(fn, "r.ufdio");
164         (void) close(fdno);
165         if (fd == NULL || Ferror(fd)) {
166             rc = 1;
167             if (fd != NULL)
168                 (void) Fclose(fd);
169             break;
170         }
171         
172         fdInitDigest(fd, algo, 0);
173         fsize = 0;
174         while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
175             fsize += rc;
176         fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii);
177         if (Ferror(fd))
178             rc = 1;
179
180         (void) Fclose(fd);
181         break;
182     }
183
184     /* Reap the prelink -y helper. */
185     if (pid) {
186         int status;
187         (void) waitpid(pid, &status, 0);
188         if (!WIFEXITED(status) || WEXITSTATUS(status))
189             rc = 1;
190     }
191
192 exit:
193     if (fsizep)
194         *fsizep = fsize;
195     if (!rc)
196         memcpy(digest, dig, diglen);
197     dig = _free(dig);
198
199     return rc;
200 }
201
202 FD_t rpmMkTemp(char *templ)
203 {
204     mode_t mode;
205     int sfd;
206     FD_t tfd = NULL;
207
208     mode = umask(0077);
209     sfd = mkstemp(templ);
210     umask(mode);
211
212     if (sfd < 0) {
213         goto exit;
214     }
215
216     tfd = fdDup(sfd);
217     close(sfd);
218
219 exit:
220     return tfd;
221 }
222
223 FD_t rpmMkTempFile(const char * prefix, char **fn)
224 {
225     const char *tpmacro = "%{_tmppath}"; /* always set from rpmrc */
226     char *tempfn;
227     static int _initialized = 0;
228     FD_t tfd = NULL;
229
230     if (!prefix) prefix = "";
231
232     /* Create the temp directory if it doesn't already exist. */
233     if (!_initialized) {
234         _initialized = 1;
235         tempfn = rpmGenPath(prefix, tpmacro, NULL);
236         if (rpmioMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1))
237             goto exit;
238         free(tempfn);
239     }
240
241     tempfn = rpmGetPath(prefix, tpmacro, "/rpm-tmp.XXXXXX", NULL);
242     tfd = rpmMkTemp(tempfn);
243
244     if (tfd == NULL || Ferror(tfd)) {
245         rpmlog(RPMLOG_ERR, _("error creating temporary file %s: %m\n"), tempfn);
246         goto exit;
247     }
248
249 exit:
250     if (tfd != NULL && fn)
251         *fn = tempfn;
252     else
253         free(tempfn);
254
255     return tfd;
256 }
257
258 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
259 {
260     char *d, *de;
261     int rc;
262
263     if (path == NULL || *path == '\0')
264         return -1;
265     d = rstrcat(NULL, path);
266     if (d[strlen(d)-1] != '/') {
267         rstrcat(&d,"/");
268     }
269     de = d;
270     for (;(de=strchr(de+1,'/'));) {
271         struct stat st;
272         *de = '\0';
273         rc = stat(d, &st);
274         if (rc) {
275             if (errno != ENOENT)
276                 goto exit;
277             rc = mkdir(d, mode);
278             if (rc)
279                 goto exit;
280             rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n", path, mode);
281             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
282                 rc = chown(d, uid, gid);
283                 if (rc)
284                     goto exit;
285             }
286         } else if (!S_ISDIR(st.st_mode)) {
287             rc = ENOTDIR;
288             goto exit;
289         }
290         *de = '/';
291     }
292     rc = 0;
293 exit:
294     free(d);
295     return rc;
296 }
297
298 int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed)
299 {
300     FD_t fd;
301     ssize_t nb;
302     int rc = -1;
303     unsigned char magic[13];
304
305     *compressed = COMPRESSED_NOT;
306
307     fd = Fopen(file, "r.ufdio");
308     if (fd == NULL || Ferror(fd)) {
309         /* XXX Fstrerror */
310         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
311         if (fd) (void) Fclose(fd);
312         return 1;
313     }
314     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
315     if (nb < 0) {
316         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
317         rc = 1;
318     } else if (nb < sizeof(magic)) {
319         rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
320                 file, (unsigned)sizeof(magic));
321         rc = 0;
322     }
323     (void) Fclose(fd);
324     if (rc >= 0)
325         return rc;
326
327     rc = 0;
328
329     if ((magic[0] == 'B') && (magic[1] == 'Z')) {
330         *compressed = COMPRESSED_BZIP2;
331     } else if ((magic[0] == 'P') && (magic[1] == 'K') &&
332          (((magic[2] == 3) && (magic[3] == 4)) ||
333           ((magic[2] == '0') && (magic[3] == '0')))) {  /* pkzip */
334         *compressed = COMPRESSED_ZIP;
335     } else if ((magic[0] == 0xfd) && (magic[1] == 0x37) &&
336                (magic[2] == 0x7a) && (magic[3] == 0x58) &&
337                (magic[4] == 0x5a) && (magic[5] == 0x00)) {
338         /* new style xz (lzma) with magic */
339         *compressed = COMPRESSED_XZ;
340     } else if ((magic[0] == 'L') && (magic[1] == 'Z') &&
341                (magic[2] == 'I') && (magic[3] == 'P')) {
342         *compressed = COMPRESSED_LZIP;
343     } else if ((magic[0] == 'L') && (magic[1] == 'R') &&
344                (magic[2] == 'Z') && (magic[3] == 'I')) {
345         *compressed = COMPRESSED_LRZIP;
346     } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
347         ((magic[0] == 0037) && (magic[1] == 0236)) ||   /* old gzip */
348         ((magic[0] == 0037) && (magic[1] == 0036)) ||   /* pack */
349         ((magic[0] == 0037) && (magic[1] == 0240)) ||   /* SCO lzh */
350         ((magic[0] == 0037) && (magic[1] == 0235))      /* compress */
351         ) {
352         *compressed = COMPRESSED_OTHER;
353     } else if ((magic[0] == '7') && (magic[1] == 'z') &&
354                (magic[2] == 0xbc) && (magic[3] == 0xaf) &&
355                (magic[4] == 0x27) && (magic[5] == 0x1c)) {
356         *compressed = COMPRESSED_7ZIP;
357     } else if (rpmFileHasSuffix(file, ".lzma")) {
358         *compressed = COMPRESSED_LZMA;
359     }
360
361     return rc;
362 }
363
364 /* @todo "../sbin/./../bin/" not correct. */
365 char *rpmCleanPath(char * path)
366 {
367     const char *s;
368     char *se, *t, *te;
369     int begin = 1;
370
371     if (path == NULL)
372         return NULL;
373
374 /*fprintf(stderr, "*** RCP %s ->\n", path); */
375     s = t = te = path;
376     while (*s != '\0') {
377 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
378         switch(*s) {
379         case ':':                       /* handle url's */
380             if (s[1] == '/' && s[2] == '/') {
381                 *t++ = *s++;
382                 *t++ = *s++;
383                 break;
384             }
385             begin=1;
386             break;
387         case '/':
388             /* Move parent dir forward */
389             for (se = te + 1; se < t && *se != '/'; se++)
390                 {};
391             if (se < t && *se == '/') {
392                 te = se;
393 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
394             }
395             while (s[1] == '/')
396                 s++;
397             while (t > path && t[-1] == '/')
398                 t--;
399             break;
400         case '.':
401             /* Leading .. is special */
402             /* Check that it is ../, so that we don't interpret */
403             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
404             /* in the case of "...", this ends up being processed*/
405             /* as "../.", and the last '.' is stripped.  This   */
406             /* would not be correct processing.                 */
407             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
408 /*fprintf(stderr, "    leading \"..\"\n"); */
409                 *t++ = *s++;
410                 break;
411             }
412             /* Single . is special */
413             if (begin && s[1] == '\0') {
414                 break;
415             }
416             /* Handle the ./ cases */
417             if (t > path && t[-1] == '/') {
418                 /* Trim embedded ./ */
419                 if (s[1] == '/') {
420                     s+=2;
421                     continue;
422                 }
423                 /* Trim trailing /. */
424                 if (s[1] == '\0') {
425                     s++;
426                     continue;
427                 }
428             }
429             /* Trim embedded /../ and trailing /.. */
430             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
431                 t = te;
432                 /* Move parent dir forward */
433                 if (te > path)
434                     for (--te; te > path && *te != '/'; te--)
435                         {};
436 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
437                 s++;
438                 s++;
439                 continue;
440             }
441             break;
442         default:
443             begin = 0;
444             break;
445         }
446         *t++ = *s++;
447     }
448
449     /* Trim trailing / (but leave single / alone) */
450     if (t > &path[1] && t[-1] == '/')
451         t--;
452     *t = '\0';
453
454 /*fprintf(stderr, "\t%s\n", path); */
455     return path;
456 }
457
458 /* Merge 3 args into path, any or all of which may be a url. */
459
460 char * rpmGenPath(const char * urlroot, const char * urlmdir,
461                 const char *urlfile)
462 {
463     char * xroot = rpmGetPath(urlroot, NULL);
464     const char * root = xroot;
465     char * xmdir = rpmGetPath(urlmdir, NULL);
466     const char * mdir = xmdir;
467     char * xfile = rpmGetPath(urlfile, NULL);
468     const char * file = xfile;
469     char * result;
470     char * url = NULL;
471     int nurl = 0;
472     int ut;
473
474     ut = urlPath(xroot, &root);
475     if (url == NULL && ut > URL_IS_DASH) {
476         url = xroot;
477         nurl = root - xroot;
478     }
479     if (root == NULL || *root == '\0') root = "/";
480
481     ut = urlPath(xmdir, &mdir);
482     if (url == NULL && ut > URL_IS_DASH) {
483         url = xmdir;
484         nurl = mdir - xmdir;
485     }
486     if (mdir == NULL || *mdir == '\0') mdir = "/";
487
488     ut = urlPath(xfile, &file);
489     if (url == NULL && ut > URL_IS_DASH) {
490         url = xfile;
491         nurl = file - xfile;
492     }
493
494     if (url && nurl > 0) {
495         char *t = rstrcat(NULL, url);
496         t[nurl] = '\0';
497         url = t;
498     } else
499         url = xstrdup("");
500
501     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
502
503     free(xroot);
504     free(xmdir);
505     free(xfile);
506     free(url);
507     return result;
508 }
509
510 /* Return concatenated and expanded canonical path. */
511
512 char * rpmGetPath(const char *path, ...)
513 {
514     va_list ap;
515     char *dest = NULL, *res;
516     const char *s;
517
518     if (path == NULL)
519         return xstrdup("");
520
521     va_start(ap, path);
522     for (s = path; s; s = va_arg(ap, const char *)) {
523         rstrcat(&dest, s);
524     }
525     va_end(ap);
526
527     res = rpmExpand(dest, NULL);
528     free(dest);
529
530     return rpmCleanPath(res);
531 }
532
533 char * rpmEscapeSpaces(const char * s)
534 {
535     const char * se;
536     char * t;
537     char * te;
538     size_t nb = 0;
539
540     for (se = s; *se; se++) {
541         if (isspace(*se))
542             nb++;
543         nb++;
544     }
545     nb++;
546
547     t = te = xmalloc(nb);
548     for (se = s; *se; se++) {
549         if (isspace(*se))
550             *te++ = '\\';
551         *te++ = *se;
552     }
553     *te = '\0';
554     return t;
555 }
556
557 int rpmFileHasSuffix(const char *path, const char *suffix)
558 {
559     size_t plen = strlen(path);
560     size_t slen = strlen(suffix);
561     return (plen >= slen && rstreq(path+plen-slen, suffix));
562 }
563
564 char * rpmGetCwd(void)
565 {
566     int currDirLen = 0;
567     char * currDir = NULL;
568
569     do {
570         currDirLen += 128;
571         currDir = xrealloc(currDir, currDirLen);
572         memset(currDir, 0, currDirLen);
573     } while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE);
574
575     return currDir;
576 }
577
578 int rpmMkdirs(const char *root, const char *pathstr)
579 {
580     ARGV_t dirs = NULL;
581     int rc = 0;
582     argvSplit(&dirs, pathstr, ":");
583     
584     for (char **d = dirs; *d; d++) {
585         char *path = rpmGetPath(root ? root : "", *d, NULL);
586         if ((rc = rpmioMkpath(path, 0755, -1, -1)) != 0) {
587             const char *msg = _("failed to create directory");
588             /* try to be more informative if the failing part was a macro */
589             if (**d == '%') {
590                 rpmlog(RPMLOG_ERR, "%s %s: %s: %m\n", msg, *d, path);
591             } else {
592                 rpmlog(RPMLOG_ERR, "%s %s: %m\n", msg, path);
593             }
594         }
595         free(path);
596         if (rc) break;
597     }
598     argvFree(dirs);
599     return rc;
600 }
601
602 const char *rpmConfigDir(void)
603 {
604     if (rpm_config_dir == NULL) {
605         char *rpmenv = getenv("RPM_CONFIGDIR");
606         rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR;
607     }
608     return rpm_config_dir;
609 }