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.c675
1 files changed, 381 insertions, 294 deletions
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c
index 0e5e8a00..34f817e1 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,128 +94,106 @@ 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, 0755) < 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, 0755) < 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
* @return the length of the trimmed string
*/
@@ -235,9 +214,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 +298,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 +337,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 +353,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 +363,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 +390,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 +444,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 +482,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 +508,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 +529,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 +547,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 +556,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 +601,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];
@@ -629,8 +629,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);
@@ -687,7 +692,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 +744,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 +812,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 +823,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 +871,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,30 +890,24 @@ 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)
{
@@ -912,18 +935,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 +962,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,8 +985,10 @@ 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;
@@ -974,11 +1005,11 @@ int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
}
}
- 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;
/* this is the main return point; from here you can read b->line */
return ARCHIVE_OK;
} else {
@@ -986,7 +1017,7 @@ 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) {
@@ -1005,6 +1036,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 +1096,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 +1110,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 +1132,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 +1146,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 +1163,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 +1192,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 +1212,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 +1232,25 @@ 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);
+}
+
#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 +1258,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);