diff options
Diffstat (limited to 'lib/libalpm/util.c')
-rw-r--r-- | lib/libalpm/util.c | 713 |
1 files changed, 406 insertions, 307 deletions
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 0e5e8a00..0196f3bb 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -22,24 +22,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "config.h" - -#include <stdio.h> #include <stdlib.h> -#include <stdarg.h> -#include <string.h> #include <unistd.h> #include <ctype.h> #include <dirent.h> -#include <fcntl.h> #include <time.h> #include <syslog.h> #include <errno.h> #include <limits.h> -#include <sys/types.h> -#include <sys/stat.h> #include <sys/wait.h> #include <locale.h> /* setlocale */ +#include <fnmatch.h> /* libarchive */ #include <archive.h> @@ -62,8 +55,16 @@ #include "trans.h" #ifndef HAVE_STRSEP -/* This is a replacement for strsep which is not portable (missing on Solaris). - * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com> */ +/** Extracts tokens from a string. + * Replaces strset which is not portable (missing on Solaris). + * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com> + * Modifies str to point to the first character after the token if one is + * found, or NULL if one is not. + * @param str string containing delimited tokens to parse + * @param delim character delimiting tokens in str + * @return pointer to the first token in str if str is not NULL, NULL if + * str is NULL + */ char* strsep(char** str, const char* delims) { char* token; @@ -93,138 +94,118 @@ int _alpm_makepath(const char *path) return _alpm_makepath_mode(path, 0755); } -/* does the same thing as 'mkdir -p' */ +/** Creates a directory, including parents if needed, similar to 'mkdir -p'. + * @param path directory path to create + * @param mode permission mode for created directories + * @return 0 on success, 1 on error + */ int _alpm_makepath_mode(const char *path, mode_t mode) { - /* A bit of pointer hell here. Descriptions: - * orig - a copy of path so we can safely butcher it with strsep - * str - the current position in the path string (after the delimiter) - * ptr - the original position of str after calling strsep - * incr - incrementally generated path for use in stat/mkdir call - */ - char *orig, *str, *ptr, *incr; - mode_t oldmask = umask(0000); + char *ptr, *str; + mode_t oldmask; int ret = 0; - orig = strdup(path); - incr = calloc(strlen(orig) + 1, sizeof(char)); - str = orig; - while((ptr = strsep(&str, "/"))) { - if(strlen(ptr)) { - /* we have another path component- append the newest component to - * existing string and create one more level of dir structure */ - strcat(incr, "/"); - strcat(incr, ptr); - if(access(incr, F_OK)) { - if(mkdir(incr, mode)) { - ret = 1; - break; - } - } - } - } - free(orig); - free(incr); - umask(oldmask); - return ret; -} + STRDUP(str, path, return 1); -int _alpm_copyfile(const char *src, const char *dest) -{ - FILE *in, *out; - size_t len; - char *buf; - int ret = 0; + oldmask = umask(0000); - in = fopen(src, "rb"); - if(in == NULL) { - return 1; - } - out = fopen(dest, "wb"); - if(out == NULL) { - fclose(in); - return 1; - } + for(ptr = str; *ptr; ptr++) { + /* detect mid-path condition and zero length paths */ + if(*ptr != '/' || ptr == str || ptr[-1] == '/') { + continue; + } - MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, ret = 1; goto cleanup); + /* temporarily mask the end of the path */ + *ptr = '\0'; - /* do the actual file copy */ - while((len = fread(buf, 1, ALPM_BUFFER_SIZE, in))) { - size_t nwritten = 0; - nwritten = fwrite(buf, 1, len, out); - if((nwritten != len) || ferror(out)) { - ret = -1; - goto cleanup; + if(mkdir(str, mode) < 0 && errno != EEXIST) { + ret = 1; + goto done; } + + /* restore path separator */ + *ptr = '/'; } - /* chmod dest to permissions of src, as long as it is not a symlink */ - struct stat statbuf; - if(!stat(src, &statbuf)) { - if(! S_ISLNK(statbuf.st_mode)) { - fchmod(fileno(out), statbuf.st_mode); - } - } else { - /* stat was unsuccessful */ + /* end of the string. add the full path. It will already exist when the path + * passed in has a trailing slash. */ + if(mkdir(str, mode) < 0 && errno != EEXIST) { ret = 1; } -cleanup: - fclose(in); - fclose(out); - free(buf); +done: + umask(oldmask); + free(str); return ret; } -/* Trim whitespace and newlines from a string -*/ -char *_alpm_strtrim(char *str) +/** Copies a file. + * @param src file path to copy from + * @param dest file path to copy to + * @return 0 on success, 1 on error + */ +int _alpm_copyfile(const char *src, const char *dest) { - char *pch = str; + char *buf; + int in, out, ret = 1; + ssize_t nread; + struct stat st; - if(*str == '\0') { - /* string is empty, so we're done. */ - return str; + MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1); + + OPEN(in, src, O_RDONLY); + do { + out = open(dest, O_WRONLY | O_CREAT, 0000); + } while(out == -1 && errno == EINTR); + if(in < 0 || out < 0) { + goto cleanup; } - while(isspace((unsigned char)*pch)) { - pch++; + if(fstat(in, &st) || fchmod(out, st.st_mode)) { + goto cleanup; } - if(pch != str) { - size_t len = strlen(pch); - if(len) { - memmove(str, pch, len + 1); - } else { - *str = '\0'; + + /* do the actual file copy */ + while((nread = read(in, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) { + ssize_t nwrite = 0; + if(nread < 0) { + continue; } + do { + nwrite = write(out, buf + nwrite, nread); + if(nwrite >= 0) { + nread -= nwrite; + } else if(errno != EINTR) { + goto cleanup; + } + } while(nread > 0); } + ret = 0; - /* check if there wasn't anything but whitespace in the string. */ - if(*str == '\0') { - return str; +cleanup: + free(buf); + if(in >= 0) { + CLOSE(in); } - - pch = (str + (strlen(str) - 1)); - while(isspace((unsigned char)*pch)) { - pch--; + if(out >= 0) { + CLOSE(out); } - *++pch = '\0'; - - return str; + return ret; } -/** - * Trim trailing newline from a string (if one exists). +/** Trim trailing newlines from a string (if any exist). * @param str a single line of text + * @param len size of str, if known, else 0 * @return the length of the trimmed string */ -size_t _alpm_strip_newline(char *str) +size_t _alpm_strip_newline(char *str, size_t len) { - size_t len; if(*str == '\0') { return 0; } - len = strlen(str); + if(len == 0) { + len = strlen(str); + } while(len > 0 && str[len - 1] == '\n') { len--; } @@ -235,9 +216,70 @@ size_t _alpm_strip_newline(char *str) /* Compression functions */ -/** - * @brief Unpack a specific file in an archive. - * +/** Open an archive for reading and perform the necessary boilerplate. + * This takes care of creating the libarchive 'archive' struct, setting up + * compression and format options, opening a file descriptor, setting up the + * buffer size, and performing a stat on the path once opened. + * On error, no file descriptor is opened, and the archive pointer returned + * will be set to NULL. + * @param handle the context handle + * @param path the path of the archive to open + * @param buf space for a stat buffer for the given path + * @param archive pointer to place the created archive object + * @param error error code to set on failure to open archive + * @return -1 on failure, >=0 file descriptor on success + */ +int _alpm_open_archive(alpm_handle_t *handle, const char *path, + struct stat *buf, struct archive **archive, alpm_errno_t error) +{ + int fd; + size_t bufsize = ALPM_BUFFER_SIZE; + errno = 0; + + if((*archive = archive_read_new()) == NULL) { + RET_ERR(handle, ALPM_ERR_LIBARCHIVE, -1); + } + + archive_read_support_compression_all(*archive); + archive_read_support_format_all(*archive); + + _alpm_log(handle, ALPM_LOG_DEBUG, "opening archive %s\n", path); + OPEN(fd, path, O_RDONLY); + if(fd < 0) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not open file %s: %s\n"), path, strerror(errno)); + goto error; + } + + if(fstat(fd, buf) != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("could not stat file %s: %s\n"), path, strerror(errno)); + goto error; + } +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + if(buf->st_blksize > ALPM_BUFFER_SIZE) { + bufsize = buf->st_blksize; + } +#endif + + if(archive_read_open_fd(*archive, fd, bufsize) != ARCHIVE_OK) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), + path, archive_error_string(*archive)); + goto error; + } + + return fd; + +error: + archive_read_finish(*archive); + *archive = NULL; + if(fd >= 0) { + CLOSE(fd); + } + RET_ERR(handle, error, -1); +} + +/** Unpack a specific file in an archive. * @param handle the context handle * @param archive the archive to unpack * @param prefix where to extract the files @@ -258,46 +300,33 @@ int _alpm_unpack_single(alpm_handle_t *handle, const char *archive, return ret; } -/** - * @brief Unpack a list of files in an archive. - * +/** Unpack a list of files in an archive. * @param handle the context handle - * @param archive the archive to unpack + * @param path the archive to unpack * @param prefix where to extract the files * @param list a list of files within the archive to unpack or NULL for all * @param breakfirst break after the first entry found - * * @return 0 on success, 1 on failure */ -int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix, +int _alpm_unpack(alpm_handle_t *handle, const char *path, const char *prefix, alpm_list_t *list, int breakfirst) { int ret = 0; mode_t oldmask; - struct archive *_archive; + struct archive *archive; struct archive_entry *entry; - int cwdfd; - - if((_archive = archive_read_new()) == NULL) { - RET_ERR(handle, ALPM_ERR_LIBARCHIVE, 1); - } - - archive_read_support_compression_all(_archive); - archive_read_support_format_all(_archive); + struct stat buf; + int fd, cwdfd; - if(archive_read_open_filename(_archive, archive, - ALPM_BUFFER_SIZE) != ARCHIVE_OK) { - _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), archive, - archive_error_string(_archive)); - RET_ERR(handle, ALPM_ERR_PKG_OPEN, 1); + fd = _alpm_open_archive(handle, path, &buf, &archive, ALPM_ERR_PKG_OPEN); + if(fd < 0) { + return 1; } oldmask = umask(0022); /* save the cwd so we can restore it later */ - do { - cwdfd = open(".", O_RDONLY); - } while(cwdfd == -1 && errno == EINTR); + OPEN(cwdfd, ".", O_RDONLY); if(cwdfd < 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); } @@ -310,19 +339,12 @@ int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix, goto cleanup; } - while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) { - const struct stat *st; - const char *entryname; /* the name of the file in the archive */ + while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + const char *entryname; + mode_t mode; - st = archive_entry_stat(entry); entryname = archive_entry_pathname(entry); - if(S_ISREG(st->st_mode)) { - archive_entry_set_perm(entry, 0644); - } else if(S_ISDIR(st->st_mode)) { - archive_entry_set_perm(entry, 0755); - } - /* If specific files were requested, skip entries that don't match. */ if(list) { char *entry_prefix = strdup(entryname); @@ -333,7 +355,7 @@ int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix, char *found = alpm_list_find_str(list, entry_prefix); free(entry_prefix); if(!found) { - if(archive_read_data_skip(_archive) != ARCHIVE_OK) { + if(archive_read_data_skip(archive) != ARCHIVE_OK) { ret = 1; goto cleanup; } @@ -343,15 +365,22 @@ int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix, } } + mode = archive_entry_mode(entry); + if(S_ISREG(mode)) { + archive_entry_set_perm(entry, 0644); + } else if(S_ISDIR(mode)) { + archive_entry_set_perm(entry, 0755); + } + /* Extract the archive entry. */ - int readret = archive_read_extract(_archive, entry, 0); + int readret = archive_read_extract(archive, entry, 0); if(readret == ARCHIVE_WARN) { /* operation succeeded but a non-critical error was encountered */ _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"), - entryname, archive_error_string(_archive)); + entryname, archive_error_string(archive)); } else if(readret != ARCHIVE_OK) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"), - entryname, archive_error_string(_archive)); + entryname, archive_error_string(archive)); ret = 1; goto cleanup; } @@ -363,63 +392,20 @@ int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix, cleanup: umask(oldmask); - archive_read_finish(_archive); + archive_read_finish(archive); + CLOSE(fd); if(cwdfd >= 0) { if(fchdir(cwdfd) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"), strerror(errno)); } - close(cwdfd); + CLOSE(cwdfd); } return ret; } -/* does the same thing as 'rm -rf' */ -int _alpm_rmrf(const char *path) -{ - int errflag = 0; - struct dirent *dp; - DIR *dirp; - struct stat st; - - if(_alpm_lstat(path, &st) == 0) { - if(!S_ISDIR(st.st_mode)) { - if(!unlink(path)) { - return 0; - } else { - if(errno == ENOENT) { - return 0; - } else { - return 1; - } - } - } else { - dirp = opendir(path); - if(!dirp) { - return 1; - } - for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { - if(dp->d_name) { - if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) { - char name[PATH_MAX]; - sprintf(name, "%s/%s", path, dp->d_name); - errflag += _alpm_rmrf(name); - } - } - } - closedir(dirp); - if(rmdir(path)) { - errflag++; - } - } - return errflag; - } - return 0; -} - -/** - * Determine if there are files in a directory +/** Determine if there are files in a directory. * @param handle the context handle * @param path the full absolute directory path * @param full_count whether to return an exact count of files @@ -460,6 +446,13 @@ ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, return files; } +/** Write formatted message to log. + * @param handle the context handle + * @param format formatted string to write out + * @param args formatting arguments + * @return 0 or number of characters written on success, vfprintf return value + * on error + */ int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args) { int ret = 0; @@ -491,16 +484,20 @@ int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args) return ret; } -int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[]) +/** Execute a command with arguments in a chroot. + * @param handle the context handle + * @param cmd command to execute + * @param argv arguments to pass to cmd + * @return 0 on success, 1 on error + */ +int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) { pid_t pid; int pipefd[2], cwdfd; int retval = 0; /* save the cwd so we can restore it later */ - do { - cwdfd = open(".", O_RDONLY); - } while(cwdfd == -1 && errno == EINTR); + OPEN(cwdfd, ".", O_RDONLY); if(cwdfd < 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); } @@ -513,7 +510,7 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[] } _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", - path, handle->root); + cmd, handle->root); /* Flush open fds before fork() to avoid cloning buffers */ fflush(NULL); @@ -534,12 +531,12 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[] if(pid == 0) { /* this code runs for the child only (the actual chroot/exec) */ - close(1); - close(2); + 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]); + CLOSE(pipefd[0]); + CLOSE(pipefd[1]); /* use fprintf instead of _alpm_log to send output through the parent */ if(chroot(handle->root) != 0) { @@ -552,7 +549,8 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[] exit(1); } umask(0022); - execv(path, argv); + execv(cmd, argv); + /* execv only returns if there was an error */ fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno)); exit(1); } else { @@ -560,10 +558,10 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[] int status; FILE *pipe_file; - close(pipefd[1]); + CLOSE(pipefd[1]); pipe_file = fdopen(pipefd[0], "r"); if(pipe_file == NULL) { - close(pipefd[0]); + CLOSE(pipefd[0]); retval = 1; } else { while(!feof(pipe_file)) { @@ -605,12 +603,16 @@ cleanup: _alpm_log(handle, ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"), strerror(errno)); } - close(cwdfd); + CLOSE(cwdfd); } return retval; } +/** Run ldconfig in a chroot. + * @param handle the context handle + * @return 0 on success, 1 on error + */ int _alpm_ldconfig(alpm_handle_t *handle) { char line[PATH_MAX]; @@ -621,7 +623,9 @@ int _alpm_ldconfig(alpm_handle_t *handle) if(access(line, F_OK) == 0) { snprintf(line, PATH_MAX, "%ssbin/ldconfig", handle->root); if(access(line, X_OK) == 0) { - char *argv[] = { "ldconfig", NULL }; + char arg0[32]; + char *argv[] = { arg0, NULL }; + strcpy(arg0, "ldconfig"); return _alpm_run_chroot(handle, "/sbin/ldconfig", argv); } } @@ -629,8 +633,13 @@ int _alpm_ldconfig(alpm_handle_t *handle) return 0; } -/* Helper function for comparing strings using the - * alpm "compare func" signature */ +/** Helper function for comparing strings using the alpm "compare func" + * signature. + * @param s1 first string to be compared + * @param s2 second string to be compared + * @return 0 if strings are equal, positive int if first unequal character + * has a greater value in s1, negative if it has a greater value in s2 + */ int _alpm_str_cmp(const void *s1, const void *s2) { return strcmp(s1, s2); @@ -671,7 +680,8 @@ const char *_alpm_filecache_setup(alpm_handle_t *handle) { struct stat buf; alpm_list_t *i; - char *cachedir, *tmpdir; + char *cachedir; + const char *tmpdir; /* Loop through the cache dirs until we find a usable directory */ for(i = handle->cachedirs; i; i = i->next) { @@ -687,7 +697,7 @@ const char *_alpm_filecache_setup(alpm_handle_t *handle) } else if(!S_ISDIR(buf.st_mode)) { _alpm_log(handle, ALPM_LOG_DEBUG, "skipping cachedir, not a directory: %s\n", cachedir); - } else if(access(cachedir, W_OK) != 0) { + } else if(_alpm_access(handle, NULL, cachedir, W_OK) != 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "skipping cachedir, not writable: %s\n", cachedir); } else if(!(buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) { @@ -739,51 +749,64 @@ int _alpm_lstat(const char *path, struct stat *buf) } #ifdef HAVE_LIBSSL +/** Compute the MD5 message digest of a file. + * @param path file path of file to compute MD5 digest of + * @param output string to hold computed MD5 digest + * @return 0 on success, 1 on file open error, 2 on file read error + */ static int md5_file(const char *path, unsigned char output[16]) { - FILE *f; - size_t n; MD5_CTX ctx; unsigned char *buf; + ssize_t n; + int fd; - CALLOC(buf, ALPM_BUFFER_SIZE, sizeof(unsigned char), return 1); + MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1); - if((f = fopen(path, "rb")) == NULL) { + OPEN(fd, path, O_RDONLY); + if(fd < 0) { free(buf); return 1; } MD5_Init(&ctx); - while((n = fread(buf, 1, ALPM_BUFFER_SIZE, f)) > 0) { + while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) { + if(n < 0) { + continue; + } MD5_Update(&ctx, buf, n); } - MD5_Final(output, &ctx); - - memset(&ctx, 0, sizeof(MD5_CTX)); + CLOSE(fd); free(buf); - if(ferror(f) != 0) { - fclose(f); + if(n < 0) { return 2; } - fclose(f); + MD5_Final(output, &ctx); return 0; } /* third param is so we match the PolarSSL definition */ +/** Compute the SHA-224 or SHA-256 message digest of a file. + * @param path file path of file to compute SHA2 digest of + * @param output string to hold computed SHA2 digest + * @param is224 use SHA-224 instead of SHA-256 + * @return 0 on success, 1 on file open error, 2 on file read error + */ static int sha2_file(const char *path, unsigned char output[32], int is224) { - FILE *f; - size_t n; SHA256_CTX ctx; unsigned char *buf; + ssize_t n; + int fd; - CALLOC(buf, ALPM_BUFFER_SIZE, sizeof(unsigned char), return 1); + MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1); - if((f = fopen(path, "rb")) == NULL) { + OPEN(fd, path, O_RDONLY); + if(fd < 0) { free(buf); return 1; } @@ -794,7 +817,10 @@ static int sha2_file(const char *path, unsigned char output[32], int is224) SHA256_Init(&ctx); } - while((n = fread(buf, 1, ALPM_BUFFER_SIZE, f)) > 0) { + while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) { + if(n < 0) { + continue; + } if(is224) { SHA224_Update(&ctx, buf, n); } else { @@ -802,24 +828,45 @@ static int sha2_file(const char *path, unsigned char output[32], int is224) } } + CLOSE(fd); + free(buf); + + if(n < 0) { + return 2; + } + if(is224) { SHA224_Final(output, &ctx); } else { SHA256_Final(output, &ctx); } + return 0; +} +#endif - memset(&ctx, 0, sizeof(SHA256_CTX)); - free(buf); +/** Create a string representing bytes in hexadecimal. + * @param bytes the bytes to represent in hexadecimal + * @param size number of bytes to consider + * @return a NULL terminated string with the hexadecimal representation of + * bytes or NULL on error. This string must be freed. + */ +static char *hex_representation(unsigned char *bytes, size_t size) +{ + static const char *hex_digits = "0123456789abcdef"; + char *str; + size_t i; - if(ferror(f) != 0) { - fclose(f); - return 2; + MALLOC(str, 2 * size + 1, return NULL); + + for (i = 0; i < size; i++) { + str[2 * i] = hex_digits[bytes[i] >> 4]; + str[2 * i + 1] = hex_digits[bytes[i] & 0x0f]; } - fclose(f); - return 0; + str[2 * size] = '\0'; + + return str; } -#endif /** Get the md5 sum of file. * @param filename name of the file @@ -829,28 +876,15 @@ static int sha2_file(const char *path, unsigned char output[32], int is224) char SYMEXPORT *alpm_compute_md5sum(const char *filename) { unsigned char output[16]; - char *md5sum; - int ret, i; ASSERT(filename != NULL, return NULL); - /* allocate 32 chars plus 1 for null */ - CALLOC(md5sum, 33, sizeof(char), return NULL); /* defined above for OpenSSL, otherwise defined in md5.h */ - ret = md5_file(filename, output); - - if(ret > 0) { - free(md5sum); + if(md5_file(filename, output) > 0) { return NULL; } - /* Convert the result to something readable */ - for (i = 0; i < 16; i++) { - /* sprintf is acceptable here because we know our output */ - sprintf(md5sum +(i * 2), "%02x", output[i]); - } - - return md5sum; + return hex_representation(output, 16); } /** Get the sha256 sum of file. @@ -861,39 +895,33 @@ char SYMEXPORT *alpm_compute_md5sum(const char *filename) char SYMEXPORT *alpm_compute_sha256sum(const char *filename) { unsigned char output[32]; - char *sha256sum; - int ret, i; ASSERT(filename != NULL, return NULL); - /* allocate 64 chars plus 1 for null */ - CALLOC(sha256sum, 65, sizeof(char), return NULL); /* defined above for OpenSSL, otherwise defined in sha2.h */ - ret = sha2_file(filename, output, 0); - - if(ret > 0) { - free(sha256sum); + if(sha2_file(filename, output, 0) > 0) { return NULL; } - /* Convert the result to something readable */ - for (i = 0; i < 32; i++) { - /* sprintf is acceptable here because we know our output */ - sprintf(sha256sum +(i * 2), "%02x", output[i]); - } - - return sha256sum; + return hex_representation(output, 32); } +/** Calculates a file's MD5 or SHA2 digest and compares it to an expected value. + * @param filepath path of the file to check + * @param expected hash value to compare against + * @param type digest type to use + * @return 0 if file matches the expected hash, 1 if they do not match, -1 on + * error + */ int _alpm_test_checksum(const char *filepath, const char *expected, - enum _alpm_csum type) + alpm_pkgvalidation_t type) { char *computed; int ret; - if(type == ALPM_CSUM_MD5) { + if(type == ALPM_PKG_VALIDATION_MD5SUM) { computed = alpm_compute_md5sum(filepath); - } else if(type == ALPM_CSUM_SHA256) { + } else if(type == ALPM_PKG_VALIDATION_SHA256SUM) { computed = alpm_compute_sha256sum(filepath); } else { return -1; @@ -912,18 +940,24 @@ int _alpm_test_checksum(const char *filepath, const char *expected, } /* Note: does NOT handle sparse files on purpose for speed. */ +/** TODO. + * Does not handle sparse files on purpose for speed. + * @param a + * @param b + * @return + */ int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b) { - char *i = NULL; - int64_t offset; - int done = 0; - /* ensure we start populating our line buffer at the beginning */ b->line_offset = b->line; while(1) { + size_t block_remaining; + char *eol; + /* have we processed this entire block? */ if(b->block + b->block_size == b->block_offset) { + int64_t offset; if(b->ret == ARCHIVE_EOF) { /* reached end of archive on the last read, now we are out of data */ goto cleanup; @@ -933,20 +967,20 @@ int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b) b->ret = archive_read_data_block(a, (void *)&b->block, &b->block_size, &offset); b->block_offset = b->block; + block_remaining = b->block_size; /* error, cleanup */ if(b->ret < ARCHIVE_OK) { goto cleanup; } + } else { + block_remaining = b->block + b->block_size - b->block_offset; } - /* 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; - } + /* look through the block looking for EOL characters */ + eol = memchr(b->block_offset, '\n', block_remaining); + if(!eol) { + eol = memchr(b->block_offset, '\0', block_remaining); } /* allocate our buffer, or ensure our existing one is big enough */ @@ -956,29 +990,32 @@ int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b) 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); + /* note: we know eol > b->block_offset and b->line_offset > b->line, + * so we know the result is unsigned and can fit in size_t */ + size_t new = eol ? (size_t)(eol - b->block_offset) : block_remaining; + size_t needed = (size_t)((b->line_offset - b->line) + new + 1); if(needed > b->max_line_size) { b->ret = -ERANGE; goto cleanup; } if(needed > b->line_size) { /* need to realloc + copy data to fit total length */ - char *new; - CALLOC(new, needed, sizeof(char), b->ret = -ENOMEM; goto cleanup); - memcpy(new, b->line, b->line_size); + char *new_line; + CALLOC(new_line, needed, sizeof(char), b->ret = -ENOMEM; goto cleanup); + memcpy(new_line, b->line, b->line_size); b->line_size = needed; - b->line_offset = new + (b->line_offset - b->line); + b->line_offset = new_line + (b->line_offset - b->line); free(b->line); - b->line = new; + b->line = new_line; } } - if(done) { - size_t len = (size_t)(i - b->block_offset); + if(eol) { + size_t len = (size_t)(eol - b->block_offset); memcpy(b->line_offset, b->block_offset, len); b->line_offset[len] = '\0'; - b->block_offset = ++i; + b->block_offset = eol + 1; + b->real_line_size = b->line_offset + len - b->line; /* this is the main return point; from here you can read b->line */ return ARCHIVE_OK; } else { @@ -986,11 +1023,12 @@ int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b) 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; + b->block_offset = b->block + b->block_size; /* there was no new data, return what is left; saved ARCHIVE_EOF will be * returned on next call */ if(len == 0) { b->line_offset[0] = '\0'; + b->real_line_size = b->line_offset - b->line; return ARCHIVE_OK; } } @@ -1005,6 +1043,14 @@ cleanup: } } +/** Parse a full package specifier. + * @param target package specifier to parse, such as: "pacman-4.0.1-2", + * "pacman-4.01-2/", or "pacman-4.0.1-2/desc" + * @param name to hold package name + * @param version to hold package version + * @param name_hash to hold package name hash + * @return 0 on success, -1 on error + */ int _alpm_splitname(const char *target, char **name, char **version, unsigned long *name_hash) { @@ -1057,8 +1103,7 @@ int _alpm_splitname(const char *target, char **name, char **version, return 0; } -/** - * Hash the given string to an unsigned long value. +/** 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 @@ -1072,12 +1117,17 @@ unsigned long _alpm_hash_sdbm(const char *str) return hash; } while((c = *str++)) { - hash = c + (hash << 6) + (hash << 16) - hash; + hash = c + hash * 65599; } return hash; } +/** Convert a string to a file offset. + * This parses bare positive integers only. + * @param line string to convert + * @return off_t on success, -1 on error + */ off_t _alpm_strtoofft(const char *line) { char *end; @@ -1089,13 +1139,13 @@ off_t _alpm_strtoofft(const char *line) return (off_t)-1; } result = strtoull(line, &end, 10); - if (result == 0 && end == line) { + if(result == 0 && end == line) { /* line was not a number */ return (off_t)-1; - } else if (result == ULLONG_MAX && errno == ERANGE) { + } else if(result == ULLONG_MAX && errno == ERANGE) { /* line does not fit in unsigned long long */ return (off_t)-1; - } else if (*end) { + } else if(*end) { /* line began with a number but has junk left over at the end */ return (off_t)-1; } @@ -1103,8 +1153,16 @@ off_t _alpm_strtoofft(const char *line) return (off_t)result; } -time_t _alpm_parsedate(const char *line) +/** Parses a date into an alpm_time_t struct. + * @param line date to parse + * @return time struct on success, 0 on error + */ +alpm_time_t _alpm_parsedate(const char *line) { + char *end; + long long result; + errno = 0; + if(isalpha((unsigned char)line[0])) { /* initialize to null in case of failure */ struct tm tmp_tm; @@ -1112,13 +1170,27 @@ time_t _alpm_parsedate(const char *line) setlocale(LC_TIME, "C"); strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm); setlocale(LC_TIME, ""); - return mktime(&tmp_tm); + return (alpm_time_t)mktime(&tmp_tm); + } + + result = strtoll(line, &end, 10); + if(result == 0 && end == line) { + /* line was not a number */ + errno = EINVAL; + return 0; + } else if(errno == ERANGE) { + /* line does not fit in long long */ + return 0; + } else if(*end) { + /* line began with a number but has junk left over at the end */ + errno = EINVAL; + return 0; } - return (time_t)atol(line); + + return (alpm_time_t)result; } -/** - * Wrapper around access() which takes a dir and file argument +/** Wrapper around access() which takes a dir and file argument * separately and generates an appropriate error message. * If dir is NULL file will be treated as the whole path. * @param handle an alpm handle @@ -1127,13 +1199,12 @@ time_t _alpm_parsedate(const char *line) * @param amode access mode as described in access() * @return int value returned by access() */ - int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int amode) { size_t len = 0; int ret = 0; - if (dir) { + if(dir) { char *check_path; len = strlen(dir) + strlen(file) + 1; @@ -1148,19 +1219,19 @@ int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int a } if(ret != 0) { - if (amode & R_OK) { + if(amode & R_OK) { _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not readable: %s\n", dir, file, strerror(errno)); } - if (amode & W_OK) { + if(amode & W_OK) { _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not writable: %s\n", dir, file, strerror(errno)); } - if (amode & X_OK) { + if(amode & X_OK) { _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not executable: %s\n", dir, file, strerror(errno)); } - if (amode == F_OK) { + if(amode == F_OK) { _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" does not exist: %s\n", dir, file, strerror(errno)); } @@ -1168,8 +1239,30 @@ int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int a return ret; } +/** Checks whether a string matches a shell wildcard pattern. + * Wrapper around fnmatch. + * @param pattern pattern to match aganist + * @param string string to check against pattern + * @return 0 if string matches pattern, non-zero if they don't match and on + * error + */ +int _alpm_fnmatch(const void *pattern, const void *string) +{ + return fnmatch(pattern, string, 0); +} + +void _alpm_alloc_fail(size_t size) +{ + fprintf(stderr, "alloc failure: could not allocate %zd bytes\n", size); +} + #ifndef HAVE_STRNDUP /* A quick and dirty implementation derived from glibc */ +/** Determines the length of a fixed-size string. + * @param s string to be measured + * @param max maximum number of characters to search for the string end + * @return length of s or max, whichever is smaller + */ static size_t strnlen(const char *s, size_t max) { register const char *p; @@ -1177,6 +1270,12 @@ static size_t strnlen(const char *s, size_t max) return (p - s); } +/** Copies a string. + * Returned string needs to be freed + * @param s string to be copied + * @param n maximum number of characters to copy + * @return pointer to the new string on success, NULL on error + */ char *strndup(const char *s, size_t n) { size_t len = strnlen(s, n); |