diff options
Diffstat (limited to 'rpmio/rpmfileutil.c')
-rw-r--r-- | rpmio/rpmfileutil.c | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/rpmio/rpmfileutil.c b/rpmio/rpmfileutil.c new file mode 100644 index 0000000..3641341 --- /dev/null +++ b/rpmio/rpmfileutil.c @@ -0,0 +1,777 @@ +#include "system.h" + +#if HAVE_GELF_H + +#include <gelf.h> + +#if !defined(DT_GNU_PRELINKED) +#define DT_GNU_PRELINKED 0x6ffffdf5 +#endif +#if !defined(DT_GNU_LIBLIST) +#define DT_GNU_LIBLIST 0x6ffffef9 +#endif + +#endif + +#if defined(HAVE_MMAP) +#include <sys/mman.h> +#endif + +#include <sys/wait.h> +#include <errno.h> +#include <popt.h> +#include <ctype.h> + +#include <rpm/rpmfileutil.h> +#include <rpm/rpmurl.h> +#include <rpm/rpmmacro.h> +#include <rpm/rpmlog.h> +#include <rpm/argv.h> + +#include "rpmio/rpmio_internal.h" + +#include "debug.h" + +static const char *rpm_config_dir = NULL; + +static int open_dso(const char * path, pid_t * pidp, rpm_loff_t *fsizep) +{ + static const char * cmd = NULL; + static int initted = 0; + int fdno; + + if (!initted) { + cmd = rpmExpand("%{?__prelink_undo_cmd}", NULL); + initted++; + } + + if (pidp) *pidp = 0; + + if (fsizep) { + struct stat sb, * st = &sb; + if (stat(path, st) < 0) + return -1; + *fsizep = st->st_size; + } + + fdno = open(path, O_RDONLY); + if (fdno < 0) + return fdno; + + if (!(cmd && *cmd)) + return fdno; + +#if HAVE_GELF_H && HAVE_LIBELF + { Elf *elf = NULL; + Elf_Scn *scn = NULL; + Elf_Data *data = NULL; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + GElf_Dyn dyn; + int bingo; + + (void) elf_version(EV_CURRENT); + + if ((elf = elf_begin (fdno, ELF_C_READ, NULL)) == NULL + || elf_kind(elf) != ELF_K_ELF + || gelf_getehdr(elf, &ehdr) == NULL + || !(ehdr.e_type == ET_DYN || ehdr.e_type == ET_EXEC)) + goto exit; + + bingo = 0; + while (!bingo && (scn = elf_nextscn(elf, scn)) != NULL) { + (void) gelf_getshdr(scn, &shdr); + if (shdr.sh_type != SHT_DYNAMIC) + continue; + while (!bingo && (data = elf_getdata (scn, data)) != NULL) { + int maxndx = data->d_size / shdr.sh_entsize; + int ndx; + + for (ndx = 0; ndx < maxndx; ++ndx) { + (void) gelf_getdyn (data, ndx, &dyn); + if (!(dyn.d_tag == DT_GNU_PRELINKED || dyn.d_tag == DT_GNU_LIBLIST)) + continue; + bingo = 1; + break; + } + } + } + + if (pidp != NULL && bingo) { + int pipes[2]; + pid_t pid; + int xx; + + xx = close(fdno); + pipes[0] = pipes[1] = -1; + xx = pipe(pipes); + if (!(pid = fork())) { + ARGV_t av, lib; + argvSplit(&av, cmd, " "); + + xx = close(pipes[0]); + xx = dup2(pipes[1], STDOUT_FILENO); + xx = close(pipes[1]); + if ((lib = argvSearch(av, "library", NULL)) != NULL) { + *lib = (char *) path; + unsetenv("MALLOC_CHECK_"); + xx = execve(av[0], av+1, environ); + } + _exit(127); + } + *pidp = pid; + fdno = pipes[0]; + xx = close(pipes[1]); + } + +exit: + if (elf) (void) elf_end(elf); + } +#endif + + return fdno; +} + +int rpmDoDigest(int algo, const char * fn,int asAscii, + unsigned char * digest, rpm_loff_t * fsizep) +{ + const char * path; + urltype ut = urlPath(fn, &path); + unsigned char * dig = NULL; + size_t diglen; + unsigned char buf[32*BUFSIZ]; + FD_t fd; + rpm_loff_t fsize = 0; + pid_t pid = 0; + int rc = 0; + int fdno; + + fdno = open_dso(path, &pid, &fsize); + if (fdno < 0) { + rc = 1; + goto exit; + } + + /* file to large (32 MB), do not mmap file */ + if (fsize > (size_t) 32*1024*1024) + if (ut == URL_IS_PATH || ut == URL_IS_UNKNOWN) + ut = URL_IS_DASH; /* force fd io */ + + switch(ut) { + case URL_IS_PATH: + case URL_IS_UNKNOWN: +#ifdef HAVE_MMAP + if (pid == 0) { + int xx; + DIGEST_CTX ctx; + void * mapped; + + if (fsize) { + mapped = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fdno, 0); + if (mapped == MAP_FAILED) { + xx = close(fdno); + rc = 1; + break; + } + +#ifdef MADV_SEQUENTIAL + xx = madvise(mapped, fsize, MADV_SEQUENTIAL); +#endif + } + + ctx = rpmDigestInit(algo, RPMDIGEST_NONE); + if (fsize) + xx = rpmDigestUpdate(ctx, mapped, fsize); + xx = rpmDigestFinal(ctx, (void **)&dig, &diglen, asAscii); + if (fsize) + xx = munmap(mapped, fsize); + xx = close(fdno); + break; + } +#endif + case URL_IS_HTTPS: + case URL_IS_HTTP: + case URL_IS_FTP: + case URL_IS_HKP: + case URL_IS_DASH: + default: + /* Either use the pipe to prelink -y or open the URL. */ + fd = (pid != 0) ? fdDup(fdno) : Fopen(fn, "r.ufdio"); + (void) close(fdno); + if (fd == NULL || Ferror(fd)) { + rc = 1; + if (fd != NULL) + (void) Fclose(fd); + break; + } + + fdInitDigest(fd, algo, 0); + fsize = 0; + while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) + fsize += rc; + fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii); + if (Ferror(fd)) + rc = 1; + + (void) Fclose(fd); + break; + } + + /* Reap the prelink -y helper. */ + if (pid) { + int status; + (void) waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)) + rc = 1; + } + +exit: + if (fsizep) + *fsizep = fsize; + if (!rc) + memcpy(digest, dig, diglen); + dig = _free(dig); + + return rc; +} + +FD_t rpmMkTemp(char *templ) +{ + int sfd; + FD_t tfd = NULL; + + sfd = mkstemp(templ); + if (sfd < 0) { + goto exit; + } + + tfd = fdDup(sfd); + close(sfd); + +exit: + return tfd; +} + +FD_t rpmMkTempFile(const char * prefix, char **fn) +{ + const char *tpmacro = "%{_tmppath}"; /* always set from rpmrc */ + char *tempfn; + static int _initialized = 0; + FD_t tfd = NULL; + + if (!prefix) prefix = ""; + + /* Create the temp directory if it doesn't already exist. */ + if (!_initialized) { + _initialized = 1; + tempfn = rpmGenPath(prefix, tpmacro, NULL); + if (rpmioMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1)) + goto exit; + free(tempfn); + } + + tempfn = rpmGetPath(prefix, tpmacro, "/rpm-tmp.XXXXXX", NULL); + tfd = rpmMkTemp(tempfn); + + if (tfd == NULL || Ferror(tfd)) { + rpmlog(RPMLOG_ERR, _("error creating temporary file %s: %m\n"), tempfn); + goto exit; + } + +exit: + if (tfd != NULL && fn) + *fn = tempfn; + else + free(tempfn); + + return tfd; +} + +int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid) +{ + char *d, *de; + int rc; + + if (path == NULL || *path == '\0') + return -1; + d = rstrcat(NULL, path); + if (d[strlen(d)-1] != '/') { + rstrcat(&d,"/"); + } + de = d; + for (;(de=strchr(de+1,'/'));) { + struct stat st; + *de = '\0'; + rc = stat(d, &st); + if (rc) { + if (errno != ENOENT) + goto exit; + rc = mkdir(d, mode); + if (rc) + goto exit; + rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n", path, mode); + if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) { + rc = chown(d, uid, gid); + if (rc) + goto exit; + } + } else if (!S_ISDIR(st.st_mode)) { + rc = ENOTDIR; + goto exit; + } + *de = '/'; + } + rc = 0; +exit: + free(d); + return rc; +} + +int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed) +{ + FD_t fd; + ssize_t nb; + int rc = -1; + unsigned char magic[13]; + + *compressed = COMPRESSED_NOT; + + fd = Fopen(file, "r.ufdio"); + if (fd == NULL || Ferror(fd)) { + /* XXX Fstrerror */ + rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd)); + if (fd) (void) Fclose(fd); + return 1; + } + nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd); + if (nb < 0) { + rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd)); + rc = 1; + } else if (nb < sizeof(magic)) { + rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"), + file, (unsigned)sizeof(magic)); + rc = 0; + } + (void) Fclose(fd); + if (rc >= 0) + return rc; + + rc = 0; + + if ((magic[0] == 'B') && (magic[1] == 'Z')) { + *compressed = COMPRESSED_BZIP2; + } else if ((magic[0] == 'P') && (magic[1] == 'K') && + (((magic[2] == 3) && (magic[3] == 4)) || + ((magic[2] == '0') && (magic[3] == '0')))) { /* pkzip */ + *compressed = COMPRESSED_ZIP; + } else if ((magic[0] == 0xfd) && (magic[1] == 0x37) && + (magic[2] == 0x7a) && (magic[3] == 0x58) && + (magic[4] == 0x5a) && (magic[5] == 0x00)) { + /* new style xz (lzma) with magic */ + *compressed = COMPRESSED_XZ; + } else if ((magic[0] == 'L') && (magic[1] == 'Z') && + (magic[2] == 'I') && (magic[3] == 'P')) { + *compressed = COMPRESSED_LZIP; + } else if ((magic[0] == 'L') && (magic[1] == 'R') && + (magic[2] == 'Z') && (magic[3] == 'I')) { + *compressed = COMPRESSED_LRZIP; + } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */ + ((magic[0] == 0037) && (magic[1] == 0236)) || /* old gzip */ + ((magic[0] == 0037) && (magic[1] == 0036)) || /* pack */ + ((magic[0] == 0037) && (magic[1] == 0240)) || /* SCO lzh */ + ((magic[0] == 0037) && (magic[1] == 0235)) /* compress */ + ) { + *compressed = COMPRESSED_OTHER; + } else if (rpmFileHasSuffix(file, ".lzma")) { + *compressed = COMPRESSED_LZMA; + } + + return rc; +} + +/* @todo "../sbin/./../bin/" not correct. */ +char *rpmCleanPath(char * path) +{ + const char *s; + char *se, *t, *te; + int begin = 1; + + if (path == NULL) + return NULL; + +/*fprintf(stderr, "*** RCP %s ->\n", path); */ + s = t = te = path; + while (*s != '\0') { +/*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */ + switch(*s) { + case ':': /* handle url's */ + if (s[1] == '/' && s[2] == '/') { + *t++ = *s++; + *t++ = *s++; + break; + } + begin=1; + break; + case '/': + /* Move parent dir forward */ + for (se = te + 1; se < t && *se != '/'; se++) + {}; + if (se < t && *se == '/') { + te = se; +/*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */ + } + while (s[1] == '/') + s++; + while (t > path && t[-1] == '/') + t--; + break; + case '.': + /* Leading .. is special */ + /* Check that it is ../, so that we don't interpret */ + /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */ + /* in the case of "...", this ends up being processed*/ + /* as "../.", and the last '.' is stripped. This */ + /* would not be correct processing. */ + if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) { +/*fprintf(stderr, " leading \"..\"\n"); */ + *t++ = *s++; + break; + } + /* Single . is special */ + if (begin && s[1] == '\0') { + break; + } + /* Handle the ./ cases */ + if (t > path && t[-1] == '/') { + /* Trim embedded ./ */ + if (s[1] == '/') { + s+=2; + continue; + } + /* Trim trailing /. */ + if (s[1] == '\0') { + s++; + continue; + } + } + /* Trim embedded /../ and trailing /.. */ + if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) { + t = te; + /* Move parent dir forward */ + if (te > path) + for (--te; te > path && *te != '/'; te--) + {}; +/*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */ + s++; + s++; + continue; + } + break; + default: + begin = 0; + break; + } + *t++ = *s++; + } + + /* Trim trailing / (but leave single / alone) */ + if (t > &path[1] && t[-1] == '/') + t--; + *t = '\0'; + +/*fprintf(stderr, "\t%s\n", path); */ + return path; +} + +/* Merge 3 args into path, any or all of which may be a url. */ + +char * rpmGenPath(const char * urlroot, const char * urlmdir, + const char *urlfile) +{ + char * xroot = rpmGetPath(urlroot, NULL); + const char * root = xroot; + char * xmdir = rpmGetPath(urlmdir, NULL); + const char * mdir = xmdir; + char * xfile = rpmGetPath(urlfile, NULL); + const char * file = xfile; + char * result; + char * url = NULL; + int nurl = 0; + int ut; + + ut = urlPath(xroot, &root); + if (url == NULL && ut > URL_IS_DASH) { + url = xroot; + nurl = root - xroot; + } + if (root == NULL || *root == '\0') root = "/"; + + ut = urlPath(xmdir, &mdir); + if (url == NULL && ut > URL_IS_DASH) { + url = xmdir; + nurl = mdir - xmdir; + } + if (mdir == NULL || *mdir == '\0') mdir = "/"; + + ut = urlPath(xfile, &file); + if (url == NULL && ut > URL_IS_DASH) { + url = xfile; + nurl = file - xfile; + } + + if (url && nurl > 0) { + char *t = rstrcat(NULL, url); + t[nurl] = '\0'; + url = t; + } else + url = xstrdup(""); + + result = rpmGetPath(url, root, "/", mdir, "/", file, NULL); + + xroot = _free(xroot); + xmdir = _free(xmdir); + xfile = _free(xfile); + free(url); + return result; +} + +/* Return concatenated and expanded canonical path. */ + +char * rpmGetPath(const char *path, ...) +{ + va_list ap; + char *dest = NULL, *res; + const char *s; + + if (path == NULL) + return xstrdup(""); + + va_start(ap, path); + for (s = path; s; s = va_arg(ap, const char *)) { + rstrcat(&dest, s); + } + va_end(ap); + + res = rpmExpand(dest, NULL); + free(dest); + + return rpmCleanPath(res); +} + +int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr) +{ + int ac = 0; + const char ** av = NULL; + int argc = 0; + ARGV_t argv = NULL; + char * globRoot = NULL; + const char *home = getenv("HOME"); + int gflags = 0; +#ifdef ENABLE_NLS + char * old_collate = NULL; + char * old_ctype = NULL; + const char * t; +#endif + size_t maxb, nb; + int i, j; + int rc; + + if (home != NULL && strlen(home) > 0) + gflags |= GLOB_TILDE; + + /* Can't use argvSplit() here, it doesn't handle whitespace etc escapes */ + rc = poptParseArgvString(patterns, &ac, &av); + if (rc) + return rc; + +#ifdef ENABLE_NLS + t = setlocale(LC_COLLATE, NULL); + if (t) + old_collate = xstrdup(t); + t = setlocale(LC_CTYPE, NULL); + if (t) + old_ctype = xstrdup(t); + (void) setlocale(LC_COLLATE, "C"); + (void) setlocale(LC_CTYPE, "C"); +#endif + + if (av != NULL) + for (j = 0; j < ac; j++) { + char * globURL; + const char * path; + int ut = urlPath(av[j], &path); + int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN); + size_t plen = strlen(path); + int flags = gflags; + int dir_only = (plen > 0 && path[plen-1] == '/'); + glob_t gl; + + if (!local || (!glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL)) { + argvAdd(&argv, av[j]); + continue; + } + +#ifdef GLOB_ONLYDIR + if (dir_only) + flags |= GLOB_ONLYDIR; +#endif + + gl.gl_pathc = 0; + gl.gl_pathv = NULL; + + rc = glob(av[j], flags, NULL, &gl); + if (rc) + goto exit; + + /* XXX Prepend the URL leader for globs that have stripped it off */ + maxb = 0; + for (i = 0; i < gl.gl_pathc; i++) { + if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb) + maxb = nb; + } + + nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0); + maxb += nb; + maxb += 1; + globURL = globRoot = xmalloc(maxb); + + switch (ut) { + case URL_IS_PATH: + case URL_IS_DASH: + strncpy(globRoot, av[j], nb); + break; + case URL_IS_HTTPS: + case URL_IS_HTTP: + case URL_IS_FTP: + case URL_IS_HKP: + case URL_IS_UNKNOWN: + default: + break; + } + globRoot += nb; + *globRoot = '\0'; + + for (i = 0; i < gl.gl_pathc; i++) { + const char * globFile = &(gl.gl_pathv[i][0]); + + if (dir_only) { + struct stat sb; + if (lstat(gl.gl_pathv[i], &sb) || !S_ISDIR(sb.st_mode)) + continue; + } + + if (globRoot > globURL && globRoot[-1] == '/') + while (*globFile == '/') globFile++; + strcpy(globRoot, globFile); + argvAdd(&argv, globURL); + } + globfree(&gl); + globURL = _free(globURL); + } + + argc = argvCount(argv); + if (argc > 0) { + if (argvPtr) + *argvPtr = argv; + if (argcPtr) + *argcPtr = argc; + rc = 0; + } else + rc = 1; + + +exit: +#ifdef ENABLE_NLS + if (old_collate) { + (void) setlocale(LC_COLLATE, old_collate); + old_collate = _free(old_collate); + } + if (old_ctype) { + (void) setlocale(LC_CTYPE, old_ctype); + old_ctype = _free(old_ctype); + } +#endif + av = _free(av); + if (rc || argvPtr == NULL) { + argvFree(argv); + } + return rc; +} + +char * rpmEscapeSpaces(const char * s) +{ + const char * se; + char * t; + char * te; + size_t nb = 0; + + for (se = s; *se; se++) { + if (isspace(*se)) + nb++; + nb++; + } + nb++; + + t = te = xmalloc(nb); + for (se = s; *se; se++) { + if (isspace(*se)) + *te++ = '\\'; + *te++ = *se; + } + *te = '\0'; + return t; +} + +int rpmFileHasSuffix(const char *path, const char *suffix) +{ + size_t plen = strlen(path); + size_t slen = strlen(suffix); + return (plen >= slen && rstreq(path+plen-slen, suffix)); +} + +char * rpmGetCwd(void) +{ + int currDirLen = 0; + char * currDir = NULL; + + do { + currDirLen += 128; + currDir = xrealloc(currDir, currDirLen); + memset(currDir, 0, currDirLen); + } while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE); + + return currDir; +} + +int rpmMkdirs(const char *root, const char *pathstr) +{ + ARGV_t dirs = NULL; + int rc = 0; + argvSplit(&dirs, pathstr, ":"); + + for (char **d = dirs; *d; d++) { + char *path = rpmGetPath(root ? root : "", *d, NULL); + if ((rc = rpmioMkpath(path, 0755, -1, -1)) != 0) { + const char *msg = _("failed to create directory"); + /* try to be more informative if the failing part was a macro */ + if (**d == '%') { + rpmlog(RPMLOG_ERR, "%s %s: %s: %m\n", msg, *d, path); + } else { + rpmlog(RPMLOG_ERR, "%s %s: %m\n", msg, path); + } + } + free(path); + if (rc) break; + } + argvFree(dirs); + return rc; +} + +const char *rpmConfigDir(void) +{ + if (rpm_config_dir == NULL) { + char *rpmenv = getenv("RPM_CONFIGDIR"); + rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR; + } + return rpm_config_dir; +} |