rpm/rpmio/rpmfileutil.c

843 lines
19 KiB
C
Raw Normal View History

#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
#include "rpmio/rpmio_internal.h"
#include <rpm/rpmfileutil.h>
#include <rpm/rpmurl.h>
#include <rpm/rpmmacro.h>
#include <rpm/rpmlog.h>
#include <rpm/argv.h>
static int open_dso(const char * path, pid_t * pidp, size_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 = path;
unsetenv("MALLOC_CHECK_");
xx = execve(av[0], (char *const *)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(pgpHashAlgo algo, const char * fn,int asAscii,
unsigned char * digest, size_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;
size_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) {
DIGEST_CTX ctx;
void * mapped;
if (fsize) {
mapped = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fdno, 0);
if (mapped == (void *)-1) {
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;
}
int rpmMkTempFile(const char * prefix, const char ** fnptr, FD_t * fdptr)
{
const char * tpmacro = "%{?_tmppath:%{_tmppath}}%{!?_tmppath:" LOCALSTATEDIR "/tmp}";
const char * tempfn = NULL;
const char * tfn = NULL;
static int _initialized = 0;
int temput;
FD_t fd = NULL;
int ran;
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 errxit;
}
/* XXX should probably use mkstemp here */
srand(time(NULL));
ran = rand() % 100000;
/* maybe this should use link/stat? */
do {
char tfnbuf[64];
#ifndef NOTYET
sprintf(tfnbuf, "rpm-tmp.%d", ran++);
tempfn = _free(tempfn);
tempfn = rpmGenPath(prefix, tpmacro, tfnbuf);
#else
strcpy(tfnbuf, "rpm-tmp.XXXXXX");
tempfn = _free(tempfn);
tempfn = rpmGenPath(prefix, tpmacro, mktemp(tfnbuf));
#endif
temput = urlPath(tempfn, &tfn);
if (*tfn == '\0') goto errxit;
switch (temput) {
case URL_IS_DASH:
case URL_IS_HKP:
goto errxit;
break;
case URL_IS_HTTPS:
case URL_IS_HTTP:
case URL_IS_FTP:
default:
break;
}
fd = Fopen(tempfn, "w+x.ufdio");
/* XXX FIXME: errno may not be correct for ufdio */
} while ((fd == NULL || Ferror(fd)) && errno == EEXIST);
if (fd == NULL || Ferror(fd))
goto errxit;
switch(temput) {
case URL_IS_PATH:
case URL_IS_UNKNOWN:
{ struct stat sb, sb2;
if (!stat(tfn, &sb) && S_ISLNK(sb.st_mode)) {
rpmlog(RPMLOG_ERR, _("error creating temporary file %s\n"), tfn);
goto errxit;
}
if (sb.st_nlink != 1) {
rpmlog(RPMLOG_ERR, _("error creating temporary file %s\n"), tfn);
goto errxit;
}
if (fstat(Fileno(fd), &sb2) == 0) {
if (sb2.st_ino != sb.st_ino || sb2.st_dev != sb.st_dev) {
rpmlog(RPMLOG_ERR, _("error creating temporary file %s\n"), tfn);
goto errxit;
}
}
} break;
default:
break;
}
if (fnptr)
*fnptr = tempfn;
else
tempfn = _free(tempfn);
*fdptr = fd;
return 0;
errxit:
tempfn = _free(tempfn);
if (fd != NULL) (void) Fclose(fd);
return 1;
}
int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
{
char * d, * de;
int created = 0;
int rc;
if (path == NULL)
return -1;
d = alloca(strlen(path)+2);
de = stpcpy(d, path);
de[1] = '\0';
for (de = d; *de != '\0'; de++) {
struct stat st;
char savec;
while (*de && *de != '/') de++;
savec = de[1];
de[1] = '\0';
rc = stat(d, &st);
if (rc) {
switch(errno) {
default:
return errno;
break;
case ENOENT:
break;
}
rc = mkdir(d, mode);
if (rc)
return errno;
created = 1;
if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
rc = chown(d, uid, gid);
if (rc)
return errno;
}
} else if (!S_ISDIR(st.st_mode)) {
return ENOTDIR;
}
de[1] = savec;
}
rc = 0;
if (created)
rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n",
path, mode);
return rc;
}
int isCompressed(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] == 0120) && (magic[1] == 0113) &&
(magic[2] == 0003) && (magic[3] == 0004)) { /* pkzip */
*compressed = COMPRESSED_ZIP;
} else if ((magic[ 9] == 0x00) && (magic[10] == 0x00) &&
(magic[11] == 0x00) && (magic[12] == 0x00)) {
/* lzma */
/* FIXME: lzma doesn't have a magic,
* consider to additionally check the filename */
*compressed = COMPRESSED_LZMA;
} 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;
}
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;
}
/* Trim embedded ./ , trailing /. */
if ((t[-1] == '/' && s[1] == '\0') || (t > path && t[-1] == '/' && s[1] == '/')) {
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)
{
const char * xroot = rpmGetPath(urlroot, NULL);
const char * root = xroot;
const char * xmdir = rpmGetPath(urlmdir, NULL);
const char * mdir = xmdir;
const char * xfile = rpmGetPath(urlfile, NULL);
const char * file = xfile;
char * result;
const 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 = strncpy(alloca(nurl+1), url, nurl);
t[nurl] = '\0';
url = t;
} else
url = "";
result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
xroot = _free(xroot);
xmdir = _free(xmdir);
xfile = _free(xfile);
return result;
}
/* Return concatenated and expanded canonical path. */
char * rpmGetPath(const char *path, ...)
{
char buf[BUFSIZ];
const char * s;
char * t, * te;
va_list ap;
if (path == NULL)
return xstrdup("");
buf[0] = '\0';
t = buf;
te = stpcpy(t, path);
*te = '\0';
va_start(ap, path);
while ((s = va_arg(ap, const char *)) != NULL) {
te = stpcpy(te, s);
*te = '\0';
}
va_end(ap);
(void) expandMacros(NULL, NULL, buf, sizeof(buf));
(void) rpmCleanPath(buf);
return xstrdup(buf); /* XXX xstrdup has side effects. */
}
/* =============================================================== */
/* XXX dupe'd to avoid change in linkage conventions. */
#define POPT_ERROR_NOARG -10 /*!< missing argument */
#define POPT_ERROR_BADQUOTE -15 /*!< error in paramter quoting */
#define POPT_ERROR_MALLOC -21 /*!< memory allocation failed */
#define POPT_ARGV_ARRAY_GROW_DELTA 5
static int XpoptDupArgv(int argc, const char **argv,
int * argcPtr, const char *** argvPtr)
{
size_t nb = (argc + 1) * sizeof(*argv);
const char ** argv2;
char * dst;
int i;
if (argc <= 0 || argv == NULL) /* XXX can't happen */
return POPT_ERROR_NOARG;
for (i = 0; i < argc; i++) {
if (argv[i] == NULL)
return POPT_ERROR_NOARG;
nb += strlen(argv[i]) + 1;
}
dst = malloc(nb);
if (dst == NULL) /* XXX can't happen */
return POPT_ERROR_MALLOC;
argv2 = (void *) dst;
dst += (argc + 1) * sizeof(*argv);
for (i = 0; i < argc; i++) {
argv2[i] = dst;
dst += strlen(strcpy(dst, argv[i])) + 1;
}
argv2[argc] = NULL;
if (argvPtr) {
*argvPtr = argv2;
} else {
free(argv2);
argv2 = NULL;
}
if (argcPtr)
*argcPtr = argc;
return 0;
}
static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
{
const char * src;
char quote = '\0';
int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
const char ** argv = malloc(sizeof(*argv) * argvAlloced);
int argc = 0;
int buflen = strlen(s) + 1;
char * buf = memset(alloca(buflen), 0, buflen);
int rc = POPT_ERROR_MALLOC;
if (argv == NULL) return rc;
argv[argc] = buf;
for (src = s; *src != '\0'; src++) {
if (quote == *src) {
quote = '\0';
} else if (quote != '\0') {
if (*src == '\\') {
src++;
if (!*src) {
rc = POPT_ERROR_BADQUOTE;
goto exit;
}
if (*src != quote) *buf++ = '\\';
}
*buf++ = *src;
} else if (isspace(*src)) {
if (*argv[argc] != '\0') {
buf++, argc++;
if (argc == argvAlloced) {
argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
argv = realloc(argv, sizeof(*argv) * argvAlloced);
if (argv == NULL) goto exit;
}
argv[argc] = buf;
}
} else switch (*src) {
case '"':
case '\'':
quote = *src;
break;
case '\\':
src++;
if (!*src) {
rc = POPT_ERROR_BADQUOTE;
goto exit;
}
default:
*buf++ = *src;
break;
}
}
if (strlen(argv[argc])) {
argc++, buf++;
}
rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
exit:
if (argv) free(argv);
return rc;
}
/* =============================================================== */
static int _debug = 0;
int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
{
int ac = 0;
const char ** av = NULL;
int argc = 0;
const char ** argv = NULL;
char * globRoot = NULL;
const char *home = getenv("HOME");
int gflags = 0;
#ifdef ENABLE_NLS
const char * old_collate = NULL;
const 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;
rc = XpoptParseArgvString(patterns, &ac, &av);
if (rc)
return rc;
2007-12-07 17:34:33 +08:00
#ifdef ENABLE_NLS
2007-12-07 17:34:33 +08:00
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++) {
const char * globURL;
const char * path;
int ut = urlPath(av[j], &path);
int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN);
glob_t gl;
if (!local || (!glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL)) {
argv = xrealloc(argv, (argc+2) * sizeof(*argv));
argv[argc] = xstrdup(av[j]);
if (_debug)
fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
argc++;
continue;
}
gl.gl_pathc = 0;
gl.gl_pathv = NULL;
rc = glob(av[j], gflags, 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';
if (_debug)
fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
if (argv != NULL)
for (i = 0; i < gl.gl_pathc; i++) {
const char * globFile = &(gl.gl_pathv[i][0]);
if (globRoot > globURL && globRoot[-1] == '/')
while (*globFile == '/') globFile++;
strcpy(globRoot, globFile);
if (_debug)
fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
argv[argc++] = xstrdup(globURL);
}
globfree(&gl);
globURL = _free(globURL);
}
if (argv != NULL && argc > 0) {
argv[argc] = NULL;
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) {
if (argv != NULL)
for (i = 0; i < argc; i++)
argv[i] = _free(argv[i]);
argv = _free(argv);
}
return rc;
}