summaryrefslogtreecommitdiffstats
path: root/lib/libalpm/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libalpm/util.c')
-rw-r--r--lib/libalpm/util.c378
1 files changed, 304 insertions, 74 deletions
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c
index 32eaa442..458f750a 100644
--- a/lib/libalpm/util.c
+++ b/lib/libalpm/util.c
@@ -1,7 +1,7 @@
/*
* util.c
*
- * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
+ * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
* Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
* Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
* Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
@@ -38,18 +38,24 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
+#include <locale.h> /* setlocale */
/* libarchive */
#include <archive.h>
#include <archive_entry.h>
+#ifdef HAVE_LIBSSL
+#include <openssl/md5.h>
+#else
+#include "md5.h"
+#endif
+
/* libalpm */
#include "util.h"
#include "log.h"
#include "package.h"
#include "alpm.h"
#include "alpm_list.h"
-#include "md5.h"
#include "handle.h"
#ifndef HAVE_STRSEP
@@ -143,7 +149,15 @@ int _alpm_copyfile(const char *src, const char *dest)
/* do the actual file copy */
while((len = fread(buf, 1, CPBUFSIZE, in))) {
- fwrite(buf, 1, len, out);
+ size_t nwritten = 0;
+ nwritten = fwrite(buf, 1, len, out);
+ if((nwritten != len) || ferror(out)) {
+ pm_errno = PM_ERR_WRITE;
+ _alpm_log(PM_LOG_ERROR, _("error writing to file '%s': %s\n"),
+ dest, strerror(errno));
+ ret = -1;
+ goto cleanup;
+ }
}
/* chmod dest to permissions of src, as long as it is not a symlink */
@@ -197,7 +211,7 @@ char *_alpm_strtrim(char *str)
}
/* Create a lock file */
-int _alpm_lckmk()
+int _alpm_lckmk(void)
{
int fd;
char *dir, *ptr;
@@ -227,7 +241,7 @@ int _alpm_lckmk()
}
/* Remove a lock file */
-int _alpm_lckrm()
+int _alpm_lckrm(void)
{
const char *file = alpm_option_get_lockfile();
if(unlink(file) == -1 && errno != ENOENT) {
@@ -347,7 +361,7 @@ int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int
int readret = archive_read_extract(_archive, entry, 0);
if(readret == ARCHIVE_WARN) {
/* operation succeeded but a non-critical error was encountered */
- _alpm_log(PM_LOG_DEBUG, "warning extracting %s (%s)\n",
+ _alpm_log(PM_LOG_WARNING, _("warning given while extracting %s (%s)\n"),
entryname, archive_error_string(_archive));
} else if(readret != ARCHIVE_OK) {
_alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"),
@@ -364,8 +378,8 @@ int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int
cleanup:
umask(oldmask);
archive_read_finish(_archive);
- if(restore_cwd) {
- chdir(cwd);
+ if(restore_cwd && chdir(cwd) != 0) {
+ _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
}
return(ret);
}
@@ -398,7 +412,7 @@ int _alpm_rmrf(const char *path)
for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
if(dp->d_ino) {
sprintf(name, "%s/%s", path, dp->d_name);
- if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) {
+ if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
errflag += _alpm_rmrf(name);
}
}
@@ -444,10 +458,11 @@ int _alpm_logaction(int usesyslog, FILE *f, const char *fmt, va_list args)
return(ret);
}
-int _alpm_run_chroot(const char *root, const char *cmd)
+int _alpm_run_chroot(const char *root, const char *path, char *const argv[])
{
char cwd[PATH_MAX];
pid_t pid;
+ int pipefd[2];
int restore_cwd = 0;
int retval = 0;
@@ -466,11 +481,17 @@ int _alpm_run_chroot(const char *root, const char *cmd)
goto cleanup;
}
- _alpm_log(PM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", cmd, root);
+ _alpm_log(PM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", path, root);
/* Flush open fds before fork() to avoid cloning buffers */
fflush(NULL);
+ if(pipe(pipefd) == -1) {
+ _alpm_log(PM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
+ retval = 1;
+ goto cleanup;
+ }
+
/* fork- parent and child each have seperate code blocks below */
pid = fork();
if(pid == -1) {
@@ -480,62 +501,74 @@ int _alpm_run_chroot(const char *root, const char *cmd)
}
if(pid == 0) {
- FILE *pipe;
/* this code runs for the child only (the actual chroot/exec) */
- _alpm_log(PM_LOG_DEBUG, "chrooting in %s\n", root);
+ close(1);
+ close(2);
+ while(dup2(pipefd[1], 1) == -1 && errno == EINTR);
+ while(dup2(pipefd[1], 2) == -1 && errno == EINTR);
+ close(pipefd[0]);
+ close(pipefd[1]);
+
+ /* use fprintf instead of _alpm_log to send output through the parent */
if(chroot(root) != 0) {
- _alpm_log(PM_LOG_ERROR, _("could not change the root directory (%s)\n"),
- strerror(errno));
+ fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
exit(1);
}
if(chdir("/") != 0) {
- _alpm_log(PM_LOG_ERROR, _("could not change directory to / (%s)\n"),
- strerror(errno));
+ fprintf(stderr, _("could not change directory to / (%s)\n"), strerror(errno));
exit(1);
}
umask(0022);
- pipe = popen(cmd, "r");
- if(!pipe) {
- _alpm_log(PM_LOG_ERROR, _("call to popen failed (%s)\n"),
- strerror(errno));
- exit(1);
- }
- while(!feof(pipe)) {
- char line[PATH_MAX];
- if(fgets(line, PATH_MAX, pipe) == NULL)
- break;
- alpm_logaction("%s", line);
- EVENT(handle->trans, PM_TRANS_EVT_SCRIPTLET_INFO, line, NULL);
- }
- retval = pclose(pipe);
- exit(WEXITSTATUS(retval));
+ execv(path, argv);
+ fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
+ exit(1);
} else {
/* this code runs for the parent only (wait on the child) */
- pid_t retpid;
int status;
- do {
- retpid = waitpid(pid, &status, 0);
- } while(retpid == -1 && errno == EINTR);
- if(retpid == -1) {
- _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"),
- strerror(errno));
+ FILE *pipe;
+
+ close(pipefd[1]);
+ pipe = fdopen(pipefd[0], "r");
+ if(pipe == NULL) {
+ close(pipefd[0]);
retval = 1;
- goto cleanup;
} else {
- /* check the return status, make sure it is 0 (success) */
- if(WIFEXITED(status)) {
- _alpm_log(PM_LOG_DEBUG, "call to waitpid succeeded\n");
- if(WEXITSTATUS(status) != 0) {
- _alpm_log(PM_LOG_ERROR, _("command failed to execute correctly\n"));
- retval = 1;
- }
+ while(!feof(pipe)) {
+ char line[PATH_MAX];
+ if(fgets(line, PATH_MAX, pipe) == NULL)
+ break;
+ alpm_logaction("%s", line);
+ EVENT(handle->trans, PM_TRANS_EVT_SCRIPTLET_INFO, line, NULL);
+ }
+ fclose(pipe);
+ }
+
+ while(waitpid(pid, &status, 0) == -1) {
+ if(errno != EINTR) {
+ _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
+ retval = 1;
+ goto cleanup;
+ }
+ }
+
+ /* report error from above after the child has exited */
+ if(retval != 0) {
+ _alpm_log(PM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno));
+ goto cleanup;
+ }
+ /* check the return status, make sure it is 0 (success) */
+ if(WIFEXITED(status)) {
+ _alpm_log(PM_LOG_DEBUG, "call to waitpid succeeded\n");
+ if(WEXITSTATUS(status) != 0) {
+ _alpm_log(PM_LOG_ERROR, _("command failed to execute correctly\n"));
+ retval = 1;
}
}
}
cleanup:
- if(restore_cwd) {
- chdir(cwd);
+ if(restore_cwd && chdir(cwd) != 0) {
+ _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
}
return(retval);
@@ -551,7 +584,8 @@ int _alpm_ldconfig(const char *root)
if(access(line, F_OK) == 0) {
snprintf(line, PATH_MAX, "%ssbin/ldconfig", root);
if(access(line, X_OK) == 0) {
- _alpm_run_chroot(root, "/sbin/ldconfig");
+ char *argv[] = { "ldconfig", NULL };
+ _alpm_run_chroot(root, "/sbin/ldconfig", argv);
}
}
@@ -574,19 +608,20 @@ char *_alpm_filecache_find(const char* filename)
char path[PATH_MAX];
char *retpath;
alpm_list_t *i;
+ struct stat buf;
/* Loop through the cache dirs until we find a matching file */
for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) {
snprintf(path, PATH_MAX, "%s%s", (char*)alpm_list_getdata(i),
filename);
- if(access(path, R_OK) == 0) {
+ if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) {
retpath = strdup(path);
_alpm_log(PM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
return(retpath);
}
}
/* package wasn't found in any cachedir */
- return(NULL);
+ RET_ERR(PM_ERR_PKG_NOT_FOUND, NULL);
}
/** Check the alpm cachedirs for existance and find a writable one.
@@ -635,7 +670,7 @@ int _alpm_lstat(const char *path, struct stat *buf)
{
int ret;
char *newpath = strdup(path);
- int len = strlen(newpath);
+ size_t len = strlen(newpath);
/* strip the trailing slash if one exists */
if(len != 0 && newpath[len - 1] == '/') {
@@ -648,6 +683,42 @@ int _alpm_lstat(const char *path, struct stat *buf)
return(ret);
}
+#ifdef HAVE_LIBSSL
+static int md5_file(const char *path, unsigned char output[16])
+{
+ FILE *f;
+ size_t n;
+ MD5_CTX ctx;
+ unsigned char *buf;
+
+ CALLOC(buf, 8192, sizeof(unsigned char), return(1));
+
+ if((f = fopen(path, "rb")) == NULL) {
+ free(buf);
+ return(1);
+ }
+
+ MD5_Init(&ctx);
+
+ while((n = fread(buf, 1, sizeof(buf), f)) > 0) {
+ MD5_Update(&ctx, buf, n);
+ }
+
+ MD5_Final(output, &ctx);
+
+ memset(&ctx, 0, sizeof(MD5_CTX));
+ free(buf);
+
+ if(ferror(f) != 0) {
+ fclose(f);
+ return(2);
+ }
+
+ fclose(f);
+ return(0);
+}
+#endif
+
/** Get the md5 sum of file.
* @param filename name of the file
* @return the checksum on success, NULL on error
@@ -665,6 +736,7 @@ char SYMEXPORT *alpm_compute_md5sum(const char *filename)
/* allocate 32 chars plus 1 for null */
md5sum = calloc(33, sizeof(char));
+ /* defined above for OpenSSL, otherwise defined in md5.h */
ret = md5_file(filename, output);
if (ret > 0) {
@@ -701,33 +773,191 @@ int _alpm_test_md5sum(const char *filepath, const char *md5sum)
return(ret);
}
-char *_alpm_archive_fgets(char *line, size_t size, struct archive *a)
+/* Note: does NOT handle sparse files on purpose for speed. */
+int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
{
- /* for now, just read one char at a time until we get to a
- * '\n' char. we can optimize this later with an internal
- * buffer. */
- /* leave room for zero terminator */
- char *last = line + size - 1;
- char *i;
-
- for(i = line; i < last; i++) {
- int ret = archive_read_data(a, i, 1);
- /* special check for first read- if null, return null,
- * this indicates EOF */
- if(i == line && (ret <= 0 || *i == '\0')) {
- return(NULL);
+ char *i = NULL;
+ int64_t offset;
+ int done = 0;
+
+ while(1) {
+ /* have we processed this entire block? */
+ if(b->block + b->block_size == b->block_offset) {
+ if(b->ret == ARCHIVE_EOF) {
+ /* reached end of archive on the last read, now we are out of data */
+ goto cleanup;
+ }
+
+ /* zero-copy - this is the entire next block of data. */
+ b->ret = archive_read_data_block(a, (void*)&b->block,
+ &b->block_size, &offset);
+ b->block_offset = b->block;
+
+ /* error or end of archive with no data read, cleanup */
+ if(b->ret < ARCHIVE_OK ||
+ (b->block_size == 0 && b->ret == ARCHIVE_EOF)) {
+ goto cleanup;
+ }
}
- /* check if read value was null or newline */
- if(ret <= 0 || *i == '\0' || *i == '\n') {
- last = i + 1;
- break;
+
+ /* loop through the block looking for EOL characters */
+ for(i = b->block_offset; i < (b->block + b->block_size); i++) {
+ /* check if read value was null or newline */
+ if(*i == '\0' || *i == '\n') {
+ done = 1;
+ break;
+ }
}
+
+ /* allocate our buffer, or ensure our existing one is big enough */
+ if(!b->line) {
+ /* set the initial buffer to the read block_size */
+ CALLOC(b->line, b->block_size + 1, sizeof(char),
+ RET_ERR(PM_ERR_MEMORY, -1));
+ b->line_size = b->block_size + 1;
+ b->line_offset = b->line;
+ } else {
+ size_t needed = (size_t)((b->line_offset - b->line)
+ + (i - b->block_offset) + 1);
+ if(needed > b->max_line_size) {
+ RET_ERR(PM_ERR_MEMORY, -1);
+ }
+ if(needed > b->line_size) {
+ /* need to realloc + copy data to fit total length */
+ char *new;
+ CALLOC(new, needed, sizeof(char), RET_ERR(PM_ERR_MEMORY, -1));
+ memcpy(new, b->line, b->line_size);
+ b->line_size = needed;
+ b->line_offset = new + (b->line_offset - b->line);
+ free(b->line);
+ b->line = new;
+ }
+ }
+
+ if(done) {
+ size_t len = (size_t)(i - b->block_offset);
+ memcpy(b->line_offset, b->block_offset, len);
+ b->line_offset[len] = '\0';
+ b->block_offset = ++i;
+ /* this is the main return point; from here you can read b->line */
+ return(ARCHIVE_OK);
+ } else {
+ /* we've looked through the whole block but no newline, copy it */
+ size_t len = (size_t)(b->block + b->block_size - b->block_offset);
+ memcpy(b->line_offset, b->block_offset, len);
+ b->line_offset += len;
+ b->block_offset = i;
+ }
+ }
+
+cleanup:
+ {
+ int ret = b->ret;
+ FREE(b->line);
+ memset(b, 0, sizeof(b));
+ return(ret);
}
+}
- /* always null terminate the buffer */
- *last = '\0';
+int _alpm_splitname(const char *target, pmpkg_t *pkg)
+{
+ /* the format of a db entry is as follows:
+ * package-version-rel/
+ * package name can contain hyphens, so parse from the back- go back
+ * two hyphens and we have split the version from the name.
+ */
+ const char *version, *end;
+
+ if(target == NULL || pkg == NULL) {
+ return(-1);
+ }
+ end = target + strlen(target);
- return(line);
+ /* remove any trailing '/' */
+ while (*(end - 1) == '/') {
+ --end;
+ }
+
+ /* do the magic parsing- find the beginning of the version string
+ * by doing two iterations of same loop to lop off two hyphens */
+ for(version = end - 1; *version && *version != '-'; version--);
+ for(version = version - 1; *version && *version != '-'; version--);
+ if(*version != '-' || version == target) {
+ return(-1);
+ }
+
+ /* copy into fields and return */
+ if(pkg->version) {
+ FREE(pkg->version);
+ }
+ /* version actually points to the dash, so need to increment 1 and account
+ * for potential end character */
+ STRNDUP(pkg->version, version + 1, end - version - 1,
+ RET_ERR(PM_ERR_MEMORY, -1));
+
+ if(pkg->name) {
+ FREE(pkg->name);
+ }
+ STRNDUP(pkg->name, target, version - target, RET_ERR(PM_ERR_MEMORY, -1));
+ pkg->name_hash = _alpm_hash_sdbm(pkg->name);
+
+ return(0);
+}
+
+/**
+ * Hash the given string to an unsigned long value.
+ * This is the standard sdbm hashing algorithm.
+ * @param str string to hash
+ * @return the hash value of the given string
+ */
+unsigned long _alpm_hash_sdbm(const char *str)
+{
+ unsigned long hash = 0;
+ int c;
+
+ if(!str) {
+ return(hash);
+ }
+ while((c = *str++)) {
+ hash = c + (hash << 6) + (hash << 16) - hash;
+ }
+
+ return(hash);
}
+long _alpm_parsedate(const char *line)
+{
+ if(isalpha((unsigned char)line[0])) {
+ /* initialize to null in case of failure */
+ struct tm tmp_tm = { 0 };
+ setlocale(LC_TIME, "C");
+ strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm);
+ setlocale(LC_TIME, "");
+ return(mktime(&tmp_tm));
+ }
+ return(atol(line));
+}
+
+#ifndef HAVE_STRNDUP
+/* A quick and dirty implementation derived from glibc */
+static size_t strnlen(const char *s, size_t max)
+{
+ register const char *p;
+ for(p = s; *p && max--; ++p);
+ return(p - s);
+}
+
+char *strndup(const char *s, size_t n)
+{
+ size_t len = strnlen(s, n);
+ char *new = (char *) malloc(len + 1);
+
+ if (new == NULL)
+ return NULL;
+
+ new[len] = '\0';
+ return (char *) memcpy(new, s, len);
+}
+#endif
+
/* vim: set ts=2 sw=2 noet: */