rpm/rpmio/rpmio.c

1819 lines
39 KiB
C

/** \ingroup rpmio
* \file rpmio/rpmio.c
*/
#include "system.h"
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#if defined(__linux__)
#include <sys/personality.h>
#endif
#include <sys/utsname.h>
#include <sys/resource.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmmacro.h>
#include <rpm/rpmfileutil.h>
#include <rpm/rpmsw.h>
#include <rpm/rpmurl.h>
#include "rpmio/rpmio_internal.h"
#include "debug.h"
typedef struct FDSTACK_s * FDSTACK_t;
struct FDSTACK_s {
FDIO_t io;
void * fp;
int fdno;
int syserrno; /* last system errno encountered */
const char *errcookie; /* pointer to custom error string */
FDSTACK_t prev;
};
/** \ingroup rpmio
* Cumulative statistics for a descriptor.
*/
typedef struct {
struct rpmop_s ops[FDSTAT_MAX]; /*!< Cumulative statistics. */
} * FDSTAT_t;
/** \ingroup rpmio
* The FD_t File Handle data structure.
*/
struct _FD_s {
int nrefs;
int flags;
#define RPMIO_DEBUG_IO 0x40000000
int magic;
#define FDMAGIC 0x04463138
FDSTACK_t fps;
int urlType; /* ufdio: */
char *descr; /* file name (or other description) */
FDSTAT_t stats; /* I/O statistics */
rpmDigestBundle digests;
};
#define DBG(_f, _m, _x) \
\
if ((_rpmio_debug | ((_f) ? ((FD_t)(_f))->flags : 0)) & (_m)) fprintf _x \
#define DBGIO(_f, _x) DBG((_f), RPMIO_DEBUG_IO, _x)
static FDSTACK_t fdGetFps(FD_t fd)
{
return (fd != NULL) ? fd->fps : NULL;
}
static void fdSetFdno(FD_t fd, int fdno)
{
if (fd)
fd->fps->fdno = fdno;
}
static void fdPush(FD_t fd, FDIO_t io, void * fp, int fdno)
{
FDSTACK_t fps = xcalloc(1, sizeof(*fps));
fps->io = io;
fps->fp = fp;
fps->fdno = fdno;
fps->prev = fd->fps;
fd->fps = fps;
fdLink(fd);
}
static FDSTACK_t fdPop(FD_t fd)
{
FDSTACK_t fps = fd->fps;
fd->fps = fps->prev;
free(fps);
fps = fd->fps;
fdFree(fd);
return fps;
}
void fdSetBundle(FD_t fd, rpmDigestBundle bundle)
{
if (fd)
fd->digests = bundle;
}
rpmDigestBundle fdGetBundle(FD_t fd, int create)
{
rpmDigestBundle bundle = NULL;
if (fd) {
if (fd->digests == NULL && create)
fd->digests = rpmDigestBundleNew();
bundle = fd->digests;
}
return bundle;
}
/** \ingroup rpmio
* \name RPMIO Vectors.
*/
typedef ssize_t (*fdio_read_function_t) (FDSTACK_t fps, void *buf, size_t nbytes);
typedef ssize_t (*fdio_write_function_t) (FDSTACK_t fps, const void *buf, size_t nbytes);
typedef int (*fdio_seek_function_t) (FDSTACK_t fps, off_t pos, int whence);
typedef int (*fdio_close_function_t) (FDSTACK_t fps);
typedef FD_t (*fdio_open_function_t) (const char * path, int flags, mode_t mode);
typedef FD_t (*fdio_fdopen_function_t) (FD_t fd, int fdno, const char * fmode);
typedef int (*fdio_fflush_function_t) (FDSTACK_t fps);
typedef off_t (*fdio_ftell_function_t) (FDSTACK_t fps);
typedef int (*fdio_ferror_function_t) (FDSTACK_t fps);
typedef const char * (*fdio_fstrerr_function_t)(FDSTACK_t fps);
struct FDIO_s {
const char * ioname;
const char * name;
fdio_read_function_t read;
fdio_write_function_t write;
fdio_seek_function_t seek;
fdio_close_function_t close;
fdio_open_function_t _open;
fdio_fdopen_function_t _fdopen;
fdio_fflush_function_t _fflush;
fdio_ftell_function_t _ftell;
fdio_ferror_function_t _ferror;
fdio_fstrerr_function_t _fstrerr;
};
/* forward refs */
static const FDIO_t fdio;
static const FDIO_t ufdio;
static const FDIO_t gzdio;
#if HAVE_BZLIB_H
static const FDIO_t bzdio;
#endif
#ifdef HAVE_LZMA_H
static const FDIO_t xzdio;
static const FDIO_t lzdio;
#endif
#ifdef HAVE_ZSTD
static const FDIO_t zstdio;
#endif
/** \ingroup rpmio
* Update digest(s) attached to fd.
*/
static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen);
static FD_t fdNew(int fdno, const char *descr);
/**
*/
int _rpmio_debug = 0;
/* =============================================================== */
static const char * fdbg(FD_t fd)
{
static char buf[BUFSIZ];
char *be = buf;
buf[0] = '\0';
if (fd == NULL)
return buf;
*be++ = '\t';
for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fps->prev) {
FDIO_t iot = fps->io;
if (fps != fd->fps)
*be++ = ' ';
*be++ = '|';
*be++ = ' ';
/* plain fd io types dont have _fopen() method */
if (iot->_fdopen == NULL) {
sprintf(be, "%s %d fp %p", iot->ioname, fps->fdno, fps->fp);
} else {
sprintf(be, "%s %p fp %d", iot->ioname, fps->fp, fps->fdno);
}
be += strlen(be);
*be = '\0';
}
return buf;
}
static void fdstat_enter(FD_t fd, fdOpX opx)
{
if (fd->stats != NULL)
(void) rpmswEnter(fdOp(fd, opx), (ssize_t) 0);
}
static void fdstat_exit(FD_t fd, fdOpX opx, ssize_t rc)
{
if (rc == -1) {
FDSTACK_t fps = fdGetFps(fd);
fps->syserrno = errno;
}
if (fd->stats != NULL)
(void) rpmswExit(fdOp(fd, opx), rc);
}
static void fdstat_print(FD_t fd, const char * msg, FILE * fp)
{
static const int usec_scale = (1000*1000);
int opx;
if (fd == NULL || fd->stats == NULL) return;
for (opx = 0; opx < 4; opx++) {
rpmop op = &fd->stats->ops[opx];
if (op->count <= 0) continue;
switch (opx) {
case FDSTAT_READ:
if (msg) fprintf(fp, "%s:", msg);
fprintf(fp, "%8d reads, %8ld total bytes in %d.%06d secs\n",
op->count, (long)op->bytes,
(int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
break;
case FDSTAT_WRITE:
if (msg) fprintf(fp, "%s:", msg);
fprintf(fp, "%8d writes, %8ld total bytes in %d.%06d secs\n",
op->count, (long)op->bytes,
(int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
break;
case FDSTAT_SEEK:
break;
case FDSTAT_CLOSE:
break;
}
}
}
off_t fdSize(FD_t fd)
{
struct stat sb;
off_t rc = -1;
if (fd != NULL && fstat(Fileno(fd), &sb) == 0)
rc = sb.st_size;
return rc;
}
FD_t fdDup(int fdno)
{
FD_t fd;
int nfdno;
if ((nfdno = dup(fdno)) < 0)
return NULL;
fd = fdNew(nfdno, NULL);
DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
return fd;
}
/* Regular fd doesn't have fflush() equivalent but its not an error either */
static int fdFlush(FDSTACK_t fps)
{
return 0;
}
static int fdError(FDSTACK_t fps)
{
return fps->syserrno;
}
static int zfdError(FDSTACK_t fps)
{
return (fps->syserrno || fps->errcookie != NULL) ? -1 : 0;
}
static const char * fdStrerr(FDSTACK_t fps)
{
return (fps->syserrno != 0) ? strerror(fps->syserrno) : "";
}
static const char * zfdStrerr(FDSTACK_t fps)
{
return (fps->errcookie != NULL) ? fps->errcookie : "";
}
const char * Fdescr(FD_t fd)
{
if (fd == NULL)
return _("[none]");
/* Lazy lookup if description is not set (eg dupped fd) */
if (fd->descr == NULL) {
int fdno = fd->fps->fdno;
#if defined(__linux__)
/* Grab the path from /proc if we can */
char *procpath = NULL;
char buf[PATH_MAX];
ssize_t llen;
rasprintf(&procpath, "/proc/self/fd/%d", fdno);
llen = readlink(procpath, buf, sizeof(buf)-1);
free(procpath);
if (llen >= 1) {
buf[llen] = '\0';
/* Real paths in /proc are always absolute */
if (buf[0] == '/')
fd->descr = xstrdup(buf);
else
fd->descr = rstrscat(NULL, "[", buf, "]", NULL);
}
#endif
/* Still no description, base it on fdno which is always there */
if (fd->descr == NULL)
rasprintf(&(fd->descr), "[fd %d]", fdno);
}
return fd->descr;
}
FD_t fdLink(FD_t fd)
{
if (fd)
fd->nrefs++;
return fd;
}
FD_t fdFree( FD_t fd)
{
if (fd) {
if (--fd->nrefs > 0)
return fd;
fd->stats = _free(fd->stats);
if (fd->digests) {
fd->digests = rpmDigestBundleFree(fd->digests);
}
free(fd->fps);
free(fd->descr);
free(fd);
}
return NULL;
}
static FD_t fdNew(int fdno, const char *descr)
{
FD_t fd = xcalloc(1, sizeof(*fd));
fd->nrefs = 0;
fd->flags = 0;
fd->magic = FDMAGIC;
fd->urlType = URL_IS_UNKNOWN;
fd->stats = xcalloc(1, sizeof(*fd->stats));
fd->digests = NULL;
fd->descr = descr ? xstrdup(descr) : NULL;
fdPush(fd, fdio, NULL, fdno);
return fd;
}
static ssize_t fdRead(FDSTACK_t fps, void * buf, size_t count)
{
return read(fps->fdno, buf, count);
}
static ssize_t fdWrite(FDSTACK_t fps, const void * buf, size_t count)
{
if (count == 0)
return 0;
return write(fps->fdno, buf, count);
}
static int fdSeek(FDSTACK_t fps, off_t pos, int whence)
{
return lseek(fps->fdno, pos, whence);
}
static int fdClose(FDSTACK_t fps)
{
int fdno = fps->fdno;
int rc;
fps->fdno = -1;
rc = ((fdno >= 0) ? close(fdno) : -2);
return rc;
}
static FD_t fdOpen(const char *path, int flags, mode_t mode)
{
FD_t fd;
int fdno;
fdno = open(path, flags, mode);
if (fdno < 0) return NULL;
if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
(void) close(fdno);
return NULL;
}
fd = fdNew(fdno, path);
fd->flags = flags;
return fd;
}
static off_t fdTell(FDSTACK_t fps)
{
return lseek(fps->fdno, 0, SEEK_CUR);
}
static const struct FDIO_s fdio_s = {
"fdio", NULL,
fdRead, fdWrite, fdSeek, fdClose,
fdOpen, NULL, fdFlush, fdTell, fdError, fdStrerr,
};
static const FDIO_t fdio = &fdio_s ;
off_t ufdCopy(FD_t sfd, FD_t tfd)
{
char buf[BUFSIZ];
ssize_t rdbytes, wrbytes;
off_t total = 0;
while (1) {
rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
if (rdbytes > 0) {
wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, tfd);
if (wrbytes != rdbytes) {
total = -1;
break;
}
total += wrbytes;
} else {
if (rdbytes < 0)
total = -1;
break;
}
}
return total;
}
/*
* Deal with remote url's by fetching them with a helper application
* and treat as local file afterwards.
* TODO:
* - better error checking + reporting
* - curl & friends don't know about hkp://, transform to http?
*/
static FD_t urlOpen(const char * url, int flags, mode_t mode)
{
FD_t fd;
char *dest = NULL;
int rc = 1; /* assume failure */
fd = rpmMkTempFile(NULL, &dest);
if (fd == NULL) {
return NULL;
}
Fclose(fd);
rc = urlGetFile(url, dest);
if (rc == 0) {
fd = fdOpen(dest, flags, mode);
unlink(dest);
} else {
fd = NULL;
}
dest = _free(dest);
return fd;
}
static FD_t ufdOpen(const char * url, int flags, mode_t mode)
{
FD_t fd = NULL;
const char * path;
urltype urlType = urlPath(url, &path);
if (_rpmio_debug)
fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
switch (urlType) {
case URL_IS_FTP:
case URL_IS_HTTPS:
case URL_IS_HTTP:
case URL_IS_HKP:
fd = urlOpen(url, flags, mode);
/* we're dealing with local file when urlOpen() returns */
urlType = URL_IS_UNKNOWN;
break;
case URL_IS_DASH:
if ((flags & O_ACCMODE) == O_RDWR) {
fd = NULL;
} else {
fd = fdDup((flags & O_ACCMODE) == O_WRONLY ?
STDOUT_FILENO : STDIN_FILENO);
}
break;
case URL_IS_PATH:
case URL_IS_UNKNOWN:
default:
fd = fdOpen(path, flags, mode);
break;
}
if (fd != NULL) {
fd->fps->io = ufdio;
fd->urlType = urlType;
}
return fd;
}
static const struct FDIO_s ufdio_s = {
"ufdio", NULL,
fdRead, fdWrite, fdSeek, fdClose,
ufdOpen, NULL, fdFlush, fdTell, fdError, fdStrerr
};
static const FDIO_t ufdio = &ufdio_s ;
/* =============================================================== */
/* Support for GZIP library. */
#include <zlib.h>
static FD_t gzdFdopen(FD_t fd, int fdno, const char *fmode)
{
gzFile gzfile = gzdopen(fdno, fmode);
if (gzfile == NULL)
return NULL;
fdSetFdno(fd, -1); /* XXX skip the fdio close */
fdPush(fd, gzdio, gzfile, fdno); /* Push gzdio onto stack */
return fd;
}
static int gzdFlush(FDSTACK_t fps)
{
gzFile gzfile = fps->fp;
if (gzfile == NULL) return -2;
return gzflush(gzfile, Z_SYNC_FLUSH); /* XXX W2DO? */
}
static void gzdSetError(FDSTACK_t fps)
{
gzFile gzfile = fps->fp;
int zerror = 0;
fps->errcookie = gzerror(gzfile, &zerror);
if (zerror == Z_ERRNO) {
fps->syserrno = errno;
fps->errcookie = strerror(fps->syserrno);
}
}
static ssize_t gzdRead(FDSTACK_t fps, void * buf, size_t count)
{
gzFile gzfile = fps->fp;
ssize_t rc;
if (gzfile == NULL) return -2; /* XXX can't happen */
rc = gzread(gzfile, buf, count);
if (rc < 0)
gzdSetError(fps);
return rc;
}
static ssize_t gzdWrite(FDSTACK_t fps, const void * buf, size_t count)
{
gzFile gzfile;
ssize_t rc;
gzfile = fps->fp;
if (gzfile == NULL) return -2; /* XXX can't happen */
rc = gzwrite(gzfile, (void *)buf, count);
if (rc < 0)
gzdSetError(fps);
return rc;
}
/* XXX zlib-1.0.4 has not */
static int gzdSeek(FDSTACK_t fps, off_t pos, int whence)
{
off_t p = pos;
int rc;
#if HAVE_GZSEEK
gzFile gzfile = fps->fp;
if (gzfile == NULL) return -2; /* XXX can't happen */
rc = gzseek(gzfile, p, whence);
if (rc < 0)
gzdSetError(fps);
#else
rc = -2;
#endif
return rc;
}
static int gzdClose(FDSTACK_t fps)
{
gzFile gzfile = fps->fp;
int rc;
if (gzfile == NULL) return -2; /* XXX can't happen */
rc = gzclose(gzfile);
return (rc != 0) ? -1 : 0;
}
static off_t gzdTell(FDSTACK_t fps)
{
off_t pos = -1;
gzFile gzfile = fps->fp;
if (gzfile != NULL) {
#if HAVE_GZSEEK
pos = gztell(gzfile);
if (pos < 0)
gzdSetError(fps);
#else
pos = -2;
#endif
}
return pos;
}
static const struct FDIO_s gzdio_s = {
"gzdio", "gzip",
gzdRead, gzdWrite, gzdSeek, gzdClose,
NULL, gzdFdopen, gzdFlush, gzdTell, zfdError, zfdStrerr
};
static const FDIO_t gzdio = &gzdio_s ;
/* =============================================================== */
/* Support for BZIP2 library. */
#if HAVE_BZLIB_H
#include <bzlib.h>
static FD_t bzdFdopen(FD_t fd, int fdno, const char * fmode)
{
BZFILE *bzfile = BZ2_bzdopen(fdno, fmode);
if (bzfile == NULL)
return NULL;
fdSetFdno(fd, -1); /* XXX skip the fdio close */
fdPush(fd, bzdio, bzfile, fdno); /* Push bzdio onto stack */
return fd;
}
static int bzdFlush(FDSTACK_t fps)
{
return BZ2_bzflush(fps->fp);
}
static ssize_t bzdRead(FDSTACK_t fps, void * buf, size_t count)
{
BZFILE *bzfile = fps->fp;
ssize_t rc = 0;
if (bzfile)
rc = BZ2_bzread(bzfile, buf, count);
if (rc == -1) {
int zerror = 0;
if (bzfile) {
fps->errcookie = BZ2_bzerror(bzfile, &zerror);
}
}
return rc;
}
static ssize_t bzdWrite(FDSTACK_t fps, const void * buf, size_t count)
{
BZFILE *bzfile = fps->fp;
ssize_t rc;
rc = BZ2_bzwrite(bzfile, (void *)buf, count);
if (rc == -1) {
int zerror = 0;
fps->errcookie = BZ2_bzerror(bzfile, &zerror);
}
return rc;
}
static int bzdClose(FDSTACK_t fps)
{
BZFILE *bzfile = fps->fp;
if (bzfile == NULL) return -2;
/* bzclose() doesn't return errors */
BZ2_bzclose(bzfile);
return 0;
}
static const struct FDIO_s bzdio_s = {
"bzdio", "bzip2",
bzdRead, bzdWrite, NULL, bzdClose,
NULL, bzdFdopen, bzdFlush, NULL, zfdError, zfdStrerr
};
static const FDIO_t bzdio = &bzdio_s ;
#endif /* HAVE_BZLIB_H */
/* =============================================================== */
/* Support for LZMA library. */
#ifdef HAVE_LZMA_H
#include <sys/types.h>
#include <inttypes.h>
#include <lzma.h>
/* Multithreading support in stable API since xz 5.2.0 */
#if LZMA_VERSION >= 50020002
#define HAVE_LZMA_MT
#endif
#define kBufferSize (1 << 15)
typedef struct lzfile {
/* IO buffer */
uint8_t buf[kBufferSize];
lzma_stream strm;
FILE *file;
int encoding;
int eof;
} LZFILE;
static LZFILE *lzopen_internal(const char *mode, int fd, int xz)
{
int level = LZMA_PRESET_DEFAULT;
int encoding = 0;
FILE *fp;
LZFILE *lzfile;
lzma_ret ret;
lzma_stream init_strm = LZMA_STREAM_INIT;
uint64_t mem_limit = rpmExpandNumeric("%{_xz_memlimit}");
#ifdef HAVE_LZMA_MT
int threads = 0;
#endif
for (; *mode; mode++) {
if (*mode == 'w')
encoding = 1;
else if (*mode == 'r')
encoding = 0;
else if (*mode >= '0' && *mode <= '9')
level = *mode - '0';
else if (*mode == 'T') {
if (isdigit(*(mode+1))) {
#ifdef HAVE_LZMA_MT
threads = atoi(++mode);
#endif
/* skip past rest of digits in string that atoi()
* should've processed
* */
while (isdigit(*++mode));
}
#ifdef HAVE_LZMA_MT
else
threads = -1;
#endif
}
}
fp = fdopen(fd, encoding ? "w" : "r");
if (!fp)
return NULL;
lzfile = calloc(1, sizeof(*lzfile));
lzfile->file = fp;
lzfile->encoding = encoding;
lzfile->eof = 0;
lzfile->strm = init_strm;
if (encoding) {
if (xz) {
#ifdef HAVE_LZMA_MT
if (!threads) {
#endif
ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
#ifdef HAVE_LZMA_MT
} else {
if (threads == -1)
threads = rpmExpandNumeric("%{getncpus}");
lzma_mt mt_options = {
.flags = 0,
.threads = threads,
.block_size = 0,
.timeout = 0,
.preset = level,
.filters = NULL,
.check = LZMA_CHECK_SHA256 };
#if __WORDSIZE == 32
/* In 32 bit environment, required memory easily exceeds memory address
* space limit if compressing using multiple threads.
* By setting a memory limit, liblzma will automatically adjust number
* of threads to avoid exceeding memory.
*/
if (threads > 1) {
struct utsname u;
uint32_t memlimit = (SIZE_MAX>>1) + (SIZE_MAX>>3);
uint64_t memory_usage;
/* While a 32 bit linux kernel will have an address limit of 3GiB
* for processes (which is why set the memory limit to 2.5GiB as a safety
* margin), 64 bit kernels will have a limit of 4GiB for 32 bit binaries.
* Therefore the memory limit should be higher if running on a 64 bit
* kernel, so we increase it to 3,5GiB.
*/
uname(&u);
if (strstr(u.machine, "64") || strstr(u.machine, "s390x")
#if defined(__linux__)
|| ((personality(0xffffffff) & PER_MASK) == PER_LINUX32)
#endif
)
memlimit += (SIZE_MAX>>2);
/* keep reducing the number of threads until memory usage gets below limit */
while ((memory_usage = lzma_stream_encoder_mt_memusage(&mt_options)) > memlimit) {
/* number of threads shouldn't be able to hit zero with compression
* settings aailable to set through rpm... */
assert(--mt_options.threads != 0);
}
lzma_memlimit_set(&lzfile->strm, memlimit);
if (threads != (int)mt_options.threads)
rpmlog(RPMLOG_NOTICE,
"XZ: Adjusted the number of threads from %d to %d to not exceed the memory usage limit of %u bytes\n",
threads, mt_options.threads, memlimit);
}
#endif
ret = lzma_stream_encoder_mt(&lzfile->strm, &mt_options);
}
#endif
} else {
lzma_options_lzma options;
lzma_lzma_preset(&options, level);
ret = lzma_alone_encoder(&lzfile->strm, &options);
}
} else { /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
ret = lzma_auto_decoder(&lzfile->strm, mem_limit ? mem_limit : 100<<20, 0);
}
if (ret != LZMA_OK) {
switch (ret) {
case LZMA_MEM_ERROR:
rpmlog(RPMLOG_ERR, "liblzma: Memory allocation failed");
break;
case LZMA_DATA_ERROR:
rpmlog(RPMLOG_ERR, "liblzma: File size limits exceeded");
break;
default:
rpmlog(RPMLOG_ERR, "liblzma: <Unknown error (%d), possibly a bug", ret);
break;
}
fclose(fp);
free(lzfile);
return NULL;
}
return lzfile;
}
static int lzclose(LZFILE *lzfile)
{
lzma_ret ret;
size_t n;
int rc;
if (!lzfile)
return -1;
if (lzfile->encoding) {
for (;;) {
lzfile->strm.avail_out = kBufferSize;
lzfile->strm.next_out = lzfile->buf;
ret = lzma_code(&lzfile->strm, LZMA_FINISH);
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
return -1;
n = kBufferSize - lzfile->strm.avail_out;
if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
return -1;
if (ret == LZMA_STREAM_END)
break;
}
}
lzma_end(&lzfile->strm);
rc = fclose(lzfile->file);
free(lzfile);
return rc;
}
static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
{
lzma_ret ret;
int eof = 0;
if (!lzfile || lzfile->encoding)
return -1;
if (lzfile->eof)
return 0;
lzfile->strm.next_out = buf;
lzfile->strm.avail_out = len;
for (;;) {
if (!lzfile->strm.avail_in) {
lzfile->strm.next_in = lzfile->buf;
lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
if (!lzfile->strm.avail_in)
eof = 1;
}
ret = lzma_code(&lzfile->strm, LZMA_RUN);
if (ret == LZMA_STREAM_END) {
lzfile->eof = 1;
return len - lzfile->strm.avail_out;
}
if (ret != LZMA_OK)
return -1;
if (!lzfile->strm.avail_out)
return len;
if (eof)
return -1;
}
}
static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len)
{
lzma_ret ret;
size_t n;
if (!lzfile || !lzfile->encoding)
return -1;
if (!len)
return 0;
lzfile->strm.next_in = buf;
lzfile->strm.avail_in = len;
for (;;) {
lzfile->strm.next_out = lzfile->buf;
lzfile->strm.avail_out = kBufferSize;
ret = lzma_code(&lzfile->strm, LZMA_RUN);
if (ret != LZMA_OK)
return -1;
n = kBufferSize - lzfile->strm.avail_out;
if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
return -1;
if (!lzfile->strm.avail_in)
return len;
}
}
static FD_t xzdFdopen(FD_t fd, int fdno, const char * fmode)
{
LZFILE *lzfile = lzopen_internal(fmode, fdno, 1);
if (lzfile == NULL)
return NULL;
fdSetFdno(fd, -1); /* XXX skip the fdio close */
fdPush(fd, xzdio, lzfile, fdno);
return fd;
}
static FD_t lzdFdopen(FD_t fd, int fdno, const char * fmode)
{
LZFILE *lzfile = lzopen_internal(fmode, fdno, 0);
if (lzfile == NULL)
return NULL;
fdSetFdno(fd, -1); /* XXX skip the fdio close */
fdPush(fd, lzdio, lzfile, fdno);
return fd;
}
static int lzdFlush(FDSTACK_t fps)
{
LZFILE *lzfile = fps->fp;
return fflush(lzfile->file);
}
static ssize_t lzdRead(FDSTACK_t fps, void * buf, size_t count)
{
LZFILE *lzfile = fps->fp;
ssize_t rc = 0;
if (lzfile)
rc = lzread(lzfile, buf, count);
if (rc == -1) {
fps->errcookie = "Lzma: decoding error";
}
return rc;
}
static ssize_t lzdWrite(FDSTACK_t fps, const void * buf, size_t count)
{
LZFILE *lzfile = fps->fp;
ssize_t rc = 0;
rc = lzwrite(lzfile, (void *)buf, count);
if (rc < 0) {
fps->errcookie = "Lzma: encoding error";
}
return rc;
}
static int lzdClose(FDSTACK_t fps)
{
LZFILE *lzfile = fps->fp;
int rc;
if (lzfile == NULL) return -2;
rc = lzclose(lzfile);
return rc;
}
static struct FDIO_s xzdio_s = {
"xzdio", "xz",
lzdRead, lzdWrite, NULL, lzdClose,
NULL, xzdFdopen, lzdFlush, NULL, zfdError, zfdStrerr
};
static const FDIO_t xzdio = &xzdio_s;
static struct FDIO_s lzdio_s = {
"lzdio", "lzma",
lzdRead, lzdWrite, NULL, lzdClose,
NULL, lzdFdopen, lzdFlush, NULL, zfdError, zfdStrerr
};
static const FDIO_t lzdio = &lzdio_s;
#endif /* HAVE_LZMA_H */
/* =============================================================== */
/* Support for ZSTD library. */
#ifdef HAVE_ZSTD
#include <zstd.h>
typedef struct rpmzstd_s {
int flags; /*!< open flags. */
int fdno;
int level; /*!< compression level */
FILE * fp;
void * _stream; /*!< ZSTD_{C,D}Stream */
size_t nb;
void * b;
ZSTD_inBuffer zib; /*!< ZSTD_inBuffer */
ZSTD_outBuffer zob; /*!< ZSTD_outBuffer */
} * rpmzstd;
static rpmzstd rpmzstdNew(int fdno, const char *fmode)
{
int flags = 0;
int level = 3;
const char * s = fmode;
char stdio[32];
char *t = stdio;
char *te = t + sizeof(stdio) - 2;
int c;
switch ((c = *s++)) {
case 'a':
*t++ = (char)c;
flags &= ~O_ACCMODE;
flags |= O_WRONLY | O_CREAT | O_APPEND;
break;
case 'w':
*t++ = (char)c;
flags &= ~O_ACCMODE;
flags |= O_WRONLY | O_CREAT | O_TRUNC;
break;
case 'r':
*t++ = (char)c;
flags &= ~O_ACCMODE;
flags |= O_RDONLY;
break;
}
while ((c = *s++) != 0) {
switch (c) {
case '.':
break;
case '+':
if (t < te) *t++ = c;
flags &= ~O_ACCMODE;
flags |= O_RDWR;
continue;
break;
default:
if (c >= (int)'0' && c <= (int)'9') {
level = strtol(s-1, (char **)&s, 10);
if (level < 1){
level = 1;
rpmlog(RPMLOG_WARNING, "Invalid compression level for zstd. Using %i instead.\n", 1);
}
if (level > 19) {
level = 19;
rpmlog(RPMLOG_WARNING, "Invalid compression level for zstd. Using %i instead.\n", 19);
}
}
continue;
break;
}
break;
}
*t = '\0';
FILE * fp = fdopen(fdno, stdio);
if (fp == NULL)
return NULL;
void * _stream = NULL;
size_t nb = 0;
if ((flags & O_ACCMODE) == O_RDONLY) { /* decompressing */
if ((_stream = (void *) ZSTD_createDStream()) == NULL
|| ZSTD_isError(ZSTD_initDStream(_stream))) {
goto err;
}
nb = ZSTD_DStreamInSize();
} else { /* compressing */
if ((_stream = (void *) ZSTD_createCStream()) == NULL
|| ZSTD_isError(ZSTD_initCStream(_stream, level))) {
goto err;
}
nb = ZSTD_CStreamOutSize();
}
rpmzstd zstd = (rpmzstd) xcalloc(1, sizeof(*zstd));
zstd->flags = flags;
zstd->fdno = fdno;
zstd->level = level;
zstd->fp = fp;
zstd->_stream = _stream;
zstd->nb = nb;
zstd->b = xmalloc(nb);
return zstd;
err:
fclose(fp);
if ((flags & O_ACCMODE) == O_RDONLY)
ZSTD_freeDStream(_stream);
else
ZSTD_freeCStream(_stream);
return NULL;
}
static FD_t zstdFdopen(FD_t fd, int fdno, const char * fmode)
{
rpmzstd zstd = rpmzstdNew(fdno, fmode);
if (zstd == NULL)
return NULL;
fdSetFdno(fd, -1); /* XXX skip the fdio close */
fdPush(fd, zstdio, zstd, fdno); /* Push zstdio onto stack */
return fd;
}
static int zstdFlush(FDSTACK_t fps)
{
rpmzstd zstd = (rpmzstd) fps->fp;
assert(zstd);
int rc = -1;
if ((zstd->flags & O_ACCMODE) == O_RDONLY) { /* decompressing */
rc = 0;
} else { /* compressing */
/* close frame */
zstd->zob.dst = zstd->b;
zstd->zob.size = zstd->nb;
zstd->zob.pos = 0;
int xx = ZSTD_flushStream(zstd->_stream, &zstd->zob);
if (ZSTD_isError(xx))
fps->errcookie = ZSTD_getErrorName(xx);
else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp))
fps->errcookie = "zstdFlush fwrite failed.";
else
rc = 0;
}
return rc;
}
static ssize_t zstdRead(FDSTACK_t fps, void * buf, size_t count)
{
rpmzstd zstd = (rpmzstd) fps->fp;
assert(zstd);
ZSTD_outBuffer zob = { buf, count, 0 };
while (zob.pos < zob.size) {
/* Re-fill compressed data buffer. */
if (zstd->zib.pos >= zstd->zib.size) {
zstd->zib.size = fread(zstd->b, 1, zstd->nb, zstd->fp);
if (zstd->zib.size == 0)
break; /* EOF */
zstd->zib.src = zstd->b;
zstd->zib.pos = 0;
}
/* Decompress next chunk. */
int xx = ZSTD_decompressStream(zstd->_stream, &zob, &zstd->zib);
if (ZSTD_isError(xx)) {
fps->errcookie = ZSTD_getErrorName(xx);
return -1;
}
}
return zob.pos;
}
static ssize_t zstdWrite(FDSTACK_t fps, const void * buf, size_t count)
{
rpmzstd zstd = (rpmzstd) fps->fp;
assert(zstd);
ZSTD_inBuffer zib = { buf, count, 0 };
while (zib.pos < zib.size) {
/* Reset to beginning of compressed data buffer. */
zstd->zob.dst = zstd->b;
zstd->zob.size = zstd->nb;
zstd->zob.pos = 0;
/* Compress next chunk. */
int xx = ZSTD_compressStream(zstd->_stream, &zstd->zob, &zib);
if (ZSTD_isError(xx)) {
fps->errcookie = ZSTD_getErrorName(xx);
return -1;
}
/* Write compressed data buffer. */
if (zstd->zob.pos > 0) {
size_t nw = fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp);
if (nw != zstd->zob.pos) {
fps->errcookie = "zstdWrite fwrite failed.";
return -1;
}
}
}
return zib.pos;
}
static int zstdClose(FDSTACK_t fps)
{
rpmzstd zstd = (rpmzstd) fps->fp;
assert(zstd);
int rc = -2;
if ((zstd->flags & O_ACCMODE) == O_RDONLY) { /* decompressing */
rc = 0;
ZSTD_freeDStream(zstd->_stream);
} else { /* compressing */
/* close frame */
zstd->zob.dst = zstd->b;
zstd->zob.size = zstd->nb;
zstd->zob.pos = 0;
int xx = ZSTD_endStream(zstd->_stream, &zstd->zob);
if (ZSTD_isError(xx))
fps->errcookie = ZSTD_getErrorName(xx);
else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp))
fps->errcookie = "zstdClose fwrite failed.";
else
rc = 0;
ZSTD_freeCStream(zstd->_stream);
}
if (zstd->fp && fileno(zstd->fp) > 2)
(void) fclose(zstd->fp);
if (zstd->b) free(zstd->b);
free(zstd);
return rc;
}
static const struct FDIO_s zstdio_s = {
"zstdio", "zstd",
zstdRead, zstdWrite, NULL, zstdClose,
NULL, zstdFdopen, zstdFlush, NULL, zfdError, zfdStrerr
};
static const FDIO_t zstdio = &zstdio_s ;
#endif /* HAVE_ZSTD */
/* =============================================================== */
#define FDIOVEC(_fps, _vec) \
((_fps) && (_fps)->io) ? (_fps)->io->_vec : NULL
const char *Fstrerror(FD_t fd)
{
const char *err = "";
if (fd != NULL) {
FDSTACK_t fps = fdGetFps(fd);
fdio_fstrerr_function_t _fstrerr = FDIOVEC(fps, _fstrerr);
if (_fstrerr)
err = _fstrerr(fps);
} else if (errno){
err = strerror(errno);
}
return err;
}
ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd)
{
ssize_t rc = -1;
if (fd != NULL) {
FDSTACK_t fps = fdGetFps(fd);
fdio_read_function_t _read = FDIOVEC(fps, read);
fdstat_enter(fd, FDSTAT_READ);
do {
rc = (_read ? (*_read) (fps, buf, size * nmemb) : -2);
} while (rc == -1 && errno == EINTR);
fdstat_exit(fd, FDSTAT_READ, rc);
if (fd->digests && rc > 0)
fdUpdateDigests(fd, buf, rc);
}
DBGIO(fd, (stderr, "==>\tFread(%p,%p,%ld) rc %ld %s\n",
fd, buf, (long)size * nmemb, (long)rc, fdbg(fd)));
return rc;
}
ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
{
ssize_t rc = -1;
if (fd != NULL) {
FDSTACK_t fps = fdGetFps(fd);
fdio_write_function_t _write = FDIOVEC(fps, write);
fdstat_enter(fd, FDSTAT_WRITE);
do {
rc = (_write ? _write(fps, buf, size * nmemb) : -2);
} while (rc == -1 && errno == EINTR);
fdstat_exit(fd, FDSTAT_WRITE, rc);
if (fd->digests && rc > 0)
fdUpdateDigests(fd, buf, rc);
}
DBGIO(fd, (stderr, "==>\tFwrite(%p,%p,%ld) rc %ld %s\n",
fd, buf, (long)size * nmemb, (long)rc, fdbg(fd)));
return rc;
}
int Fseek(FD_t fd, off_t offset, int whence)
{
int rc = -1;
if (fd != NULL) {
FDSTACK_t fps = fdGetFps(fd);
fdio_seek_function_t _seek = FDIOVEC(fps, seek);
fdstat_enter(fd, FDSTAT_SEEK);
rc = (_seek ? _seek(fps, offset, whence) : -2);
fdstat_exit(fd, FDSTAT_SEEK, rc);
}
DBGIO(fd, (stderr, "==>\tFseek(%p,%ld,%d) rc %lx %s\n",
fd, (long)offset, whence, (unsigned long)rc, fdbg(fd)));
return rc;
}
int Fclose(FD_t fd)
{
int rc = 0, ec = 0;
if (fd == NULL)
return -1;
fd = fdLink(fd);
fdstat_enter(fd, FDSTAT_CLOSE);
for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fdPop(fd)) {
if (fps->fdno >= 0) {
fdio_close_function_t _close = FDIOVEC(fps, close);
rc = _close ? _close(fps) : -2;
if (ec == 0 && rc)
ec = rc;
}
/* Debugging stats for compresed types */
if ((_rpmio_debug || rpmIsDebug()) && fps->fdno == -1)
fdstat_print(fd, fps->io->ioname, stderr);
/* Leave freeing the last one after stats */
if (fps->prev == NULL)
break;
}
fdstat_exit(fd, FDSTAT_CLOSE, rc);
DBGIO(fd, (stderr, "==>\tFclose(%p) rc %lx %s\n",
(fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
fdPop(fd);
fdFree(fd);
return ec;
}
/**
* Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
* returns stdio[0] = NUL on error.
*
* - gzopen: [0-9] is compression level
* - gzopen: 'f' is filtered (Z_FILTERED)
* - gzopen: 'h' is Huffman encoding (Z_HUFFMAN_ONLY)
* - bzopen: [1-9] is block size (modulo 100K)
* - bzopen: 's' is smallmode
* - HACK: '.' terminates, rest is type of I/O
*/
static void cvtfmode (const char *m,
char *stdio, size_t nstdio,
char *other, size_t nother,
const char **end, int * f)
{
int flags = 0;
char c;
switch (*m) {
case 'a':
flags &= ~O_ACCMODE;
flags |= O_WRONLY | O_CREAT | O_APPEND;
if (--nstdio > 0) *stdio++ = *m;
break;
case 'w':
flags &= ~O_ACCMODE;
flags |= O_WRONLY | O_CREAT | O_TRUNC;
if (--nstdio > 0) *stdio++ = *m;
break;
case 'r':
flags &= ~O_ACCMODE;
flags |= O_RDONLY;
if (--nstdio > 0) *stdio++ = *m;
break;
default:
*stdio = '\0';
return;
break;
}
m++;
while ((c = *m++) != '\0') {
switch (c) {
case '.':
break;
case '+':
flags &= ~O_ACCMODE;
flags |= O_RDWR;
if (--nstdio > 0) *stdio++ = c;
continue;
break;
case 'b':
if (--nstdio > 0) *stdio++ = c;
continue;
break;
case 'x':
flags |= O_EXCL;
if (--nstdio > 0) *stdio++ = c;
continue;
break;
case '?':
flags |= RPMIO_DEBUG_IO;
if (--nother > 0) *other++ = c;
continue;
break;
default:
if (--nother > 0) *other++ = c;
continue;
break;
}
break;
}
*stdio = *other = '\0';
if (end != NULL)
*end = (*m != '\0' ? m : NULL);
if (f != NULL)
*f = flags;
}
static FDIO_t findIOT(const char *name)
{
static FDIO_t fdio_types[] = {
&fdio_s,
&ufdio_s,
&gzdio_s,
#if HAVE_BZLIB_H
&bzdio_s,
#endif
#if HAVE_LZMA_H
&xzdio_s,
&lzdio_s,
#endif
#ifdef HAVE_ZSTD
&zstdio_s,
#endif
NULL
};
FDIO_t iot = NULL;
for (FDIO_t *t = fdio_types; t && *t; t++) {
if (rstreq(name, (*t)->ioname) ||
((*t)->name && rstreq(name, (*t)->name))) {
iot = (*t);
break;
}
}
return iot;
}
FD_t Fdopen(FD_t ofd, const char *fmode)
{
char stdio[20], other[20], stdioz[40];
const char *end = NULL;
FDIO_t iot = NULL;
FD_t fd = ofd;
int fdno = Fileno(ofd);
if (_rpmio_debug)
fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
if (fd == NULL || fmode == NULL || fdno < 0)
return NULL;
cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
if (stdio[0] == '\0')
return NULL;
stdioz[0] = '\0';
strncat(stdioz, stdio, sizeof(stdioz) - strlen(stdioz) - 1);
strncat(stdioz, other, sizeof(stdioz) - strlen(stdioz) - 1);
if (end == NULL && other[0] == '\0')
return fd;
if (end && *end) {
iot = findIOT(end);
} else if (other[0] != '\0') {
for (end = other; *end && strchr("0123456789fh", *end); end++)
{};
if (*end == '\0')
iot = findIOT("gzdio");
}
if (iot && iot->_fdopen)
fd = iot->_fdopen(fd, fdno, stdioz);
DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
return fd;
}
FD_t Fopen(const char *path, const char *fmode)
{
char stdio[20], other[20];
const char *end = NULL;
mode_t perms = 0666;
int flags = 0;
FD_t fd = NULL;
if (path == NULL || fmode == NULL)
return NULL;
stdio[0] = '\0';
cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
if (stdio[0] == '\0')
return NULL;
if (end == NULL || rstreq(end, "fdio")) {
if (_rpmio_debug)
fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
fd = fdOpen(path, flags, perms);
} else {
if (_rpmio_debug)
fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
fd = ufdOpen(path, flags, perms);
}
/* Open compressed stream if necessary */
if (fd)
fd = Fdopen(fd, fmode);
DBGIO(fd, (stderr, "==>\tFopen(\"%s\",%x,0%o) %s\n",
path, (unsigned)flags, (unsigned)perms, fdbg(fd)));
return fd;
}
int Fflush(FD_t fd)
{
int rc = -1;
if (fd != NULL) {
FDSTACK_t fps = fdGetFps(fd);
fdio_fflush_function_t _fflush = FDIOVEC(fps, _fflush);
rc = (_fflush ? _fflush(fps) : -2);
}
return rc;
}
off_t Ftell(FD_t fd)
{
off_t pos = -1;
if (fd != NULL) {
FDSTACK_t fps = fdGetFps(fd);
fdio_ftell_function_t _ftell = FDIOVEC(fps, _ftell);
pos = (_ftell ? _ftell(fps) : -2);
}
return pos;
}
int Ferror(FD_t fd)
{
int rc = 0;
if (fd == NULL) return -1;
for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fps->prev) {
fdio_ferror_function_t _ferror = FDIOVEC(fps, _ferror);
rc = _ferror(fps);
if (rc)
break;
}
DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
return rc;
}
int Fileno(FD_t fd)
{
int rc = -1;
if (fd == NULL) return -1;
for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fps->prev) {
rc = fps->fdno;
if (rc != -1)
break;
}
DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
return rc;
}
/* XXX this is naive */
int Fcntl(FD_t fd, int op, void *lip)
{
return fcntl(Fileno(fd), op, lip);
}
rpmop fdOp(FD_t fd, fdOpX opx)
{
rpmop op = NULL;
if (fd != NULL && fd->stats != NULL && opx >= 0 && opx < FDSTAT_MAX)
op = fd->stats->ops + opx;
return op;
}
int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp)
{
static const ssize_t blenmax = (32 * BUFSIZ);
ssize_t blen = 0;
uint8_t * b = NULL;
ssize_t size;
FD_t fd;
int rc = 0;
fd = Fopen(fn, "r.ufdio");
if (fd == NULL || Ferror(fd)) {
rc = 2;
goto exit;
}
size = fdSize(fd);
blen = (size >= 0 ? size : blenmax);
if (blen) {
int nb;
b = xmalloc(blen+1);
b[0] = '\0';
nb = Fread(b, sizeof(*b), blen, fd);
if (Ferror(fd) || (size > 0 && nb != blen)) {
rc = 1;
goto exit;
}
if (blen == blenmax && nb < blen) {
blen = nb;
b = xrealloc(b, blen+1);
}
b[blen] = '\0';
}
exit:
if (fd) (void) Fclose(fd);
if (rc) {
if (b) free(b);
b = NULL;
blen = 0;
}
if (bp) *bp = b;
else if (b) free(b);
if (blenp) *blenp = blen;
return rc;
}
void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags)
{
return fdInitDigestID(fd, hashalgo, hashalgo, flags);
}
void fdInitDigestID(FD_t fd, int hashalgo, int id, rpmDigestFlags flags)
{
if (fd->digests == NULL) {
fd->digests = rpmDigestBundleNew();
}
fdstat_enter(fd, FDSTAT_DIGEST);
rpmDigestBundleAddID(fd->digests, hashalgo, id, flags);
fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
}
static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen)
{
if (fd && fd->digests) {
fdstat_enter(fd, FDSTAT_DIGEST);
rpmDigestBundleUpdate(fd->digests, buf, buflen);
fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) buflen);
}
}
void fdFiniDigest(FD_t fd, int id,
void ** datap, size_t * lenp, int asAscii)
{
if (fd && fd->digests) {
fdstat_enter(fd, FDSTAT_DIGEST);
rpmDigestBundleFinal(fd->digests, id, datap, lenp, asAscii);
fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
}
}
DIGEST_CTX fdDupDigest(FD_t fd, int id)
{
DIGEST_CTX ctx = NULL;
if (fd && fd->digests)
ctx = rpmDigestBundleDupCtx(fd->digests, id);
return ctx;
}
static void set_cloexec(int fd)
{
int flags = fcntl(fd, F_GETFD);
if (flags == -1 || (flags & FD_CLOEXEC))
return;
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
void rpmSetCloseOnExec(void)
{
const int min_fd = STDERR_FILENO; /* don't touch stdin/out/err */
int fd;
DIR *dir = opendir("/proc/self/fd");
if (dir == NULL) { /* /proc not available */
/* iterate over all possible fds, might be slow */
struct rlimit rl;
int open_max;
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
open_max = rl.rlim_max;
else
open_max = sysconf(_SC_OPEN_MAX);
if (open_max == -1)
open_max = 1024;
for (fd = min_fd + 1; fd < open_max; fd++)
set_cloexec(fd);
return;
}
/* iterate over fds obtained from /proc */
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
fd = atoi(entry->d_name);
if (fd > min_fd)
set_cloexec(fd);
}
closedir(dir);
return;
}