diff options
Diffstat (limited to 'lib/libalpm/util.c')
-rw-r--r-- | lib/libalpm/util.c | 415 |
1 files changed, 302 insertions, 113 deletions
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 32eaa442..9281f0e6 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> @@ -28,7 +28,6 @@ #include <stdlib.h> #include <stdarg.h> #include <string.h> -#include <fcntl.h> #include <unistd.h> #include <ctype.h> #include <dirent.h> @@ -38,18 +37,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 +148,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 */ @@ -196,46 +209,6 @@ char *_alpm_strtrim(char *str) return(str); } -/* Create a lock file */ -int _alpm_lckmk() -{ - int fd; - char *dir, *ptr; - const char *file = alpm_option_get_lockfile(); - - /* create the dir of the lockfile first */ - dir = strdup(file); - ptr = strrchr(dir, '/'); - if(ptr) { - *ptr = '\0'; - } - _alpm_makepath(dir); - FREE(dir); - - do { - fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000); - } while (fd == -1 && errno == EINTR); - if(fd > 0) { - FILE *f = fdopen(fd, "w"); - fprintf(f, "%ld\n", (long)getpid()); - fflush(f); - fsync(fd); - fclose(f); - return(fd); - } - return(-1); -} - -/* Remove a lock file */ -int _alpm_lckrm() -{ - const char *file = alpm_option_get_lockfile(); - if(unlink(file) == -1 && errno != ENOENT) { - return(-1); - } - return(0); -} - /* Compression functions */ /** @@ -347,7 +320,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 +337,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 +371,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 +417,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 +440,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 +460,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 +543,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 +567,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 +629,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 +642,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 +695,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 +732,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); + } +} + +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); - /* always null terminate the buffer */ - *last = '\0'; + /* remove any trailing '/' */ + while (*(end - 1) == '/') { + --end; + } - return(line); + /* 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: */ |