Move important information up in -Si output
[pacman-ng.git] / lib / libalpm / util.c
blob033058a02b3ee8b2284132dd562c5e63f1b3d7d7
1 /*
2 * util.c
4 * Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
7 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
8 * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
9 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <ctype.h>
28 #include <dirent.h>
29 #include <time.h>
30 #include <syslog.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <sys/wait.h>
34 #include <locale.h> /* setlocale */
35 #include <fnmatch.h>
37 /* libarchive */
38 #include <archive.h>
39 #include <archive_entry.h>
41 #ifdef HAVE_LIBSSL
42 #include <openssl/md5.h>
43 #include <openssl/sha.h>
44 #else
45 #include "md5.h"
46 #include "sha2.h"
47 #endif
49 /* libalpm */
50 #include "util.h"
51 #include "log.h"
52 #include "alpm.h"
53 #include "alpm_list.h"
54 #include "handle.h"
55 #include "trans.h"
57 #ifndef HAVE_STRSEP
58 /** Extracts tokens from a string.
59 * Replaces strset which is not portable (missing on Solaris).
60 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com>
61 * Modifies str to point to the first character after the token if one is
62 * found, or NULL if one is not.
63 * @param str string containing delimited tokens to parse
64 * @param delim character delimiting tokens in str
65 * @return pointer to the first token in str if str is not NULL, NULL if
66 * str is NULL
68 char *strsep(char **str, const char *delims)
70 char *token;
72 if(*str==NULL) {
73 /* No more tokens */
74 return NULL;
77 token=*str;
78 while(**str!='\0') {
79 if(strchr(delims,**str)!=NULL) {
80 **str='\0';
81 (*str)++;
82 return token;
84 (*str)++;
86 /* There is no other token */
87 *str=NULL;
88 return token;
90 #endif
92 int _alpm_makepath(const char *path)
94 return _alpm_makepath_mode(path, 0755);
97 /** Creates a directory, including parents if needed, similar to 'mkdir -p'.
98 * @param path directory path to create
99 * @param mode permission mode for created directories
100 * @return 0 on success, 1 on error
102 int _alpm_makepath_mode(const char *path, mode_t mode)
104 char *ptr, *str;
105 mode_t oldmask;
106 int ret = 0;
108 STRDUP(str, path, return 1);
110 oldmask = umask(0000);
112 for(ptr = str; *ptr; ptr++) {
113 /* detect mid-path condition and zero length paths */
114 if(*ptr != '/' || ptr == str || ptr[-1] == '/') {
115 continue;
118 /* temporarily mask the end of the path */
119 *ptr = '\0';
121 if(mkdir(str, mode) < 0 && errno != EEXIST) {
122 ret = 1;
123 goto done;
126 /* restore path separator */
127 *ptr = '/';
130 /* end of the string. add the full path. It will already exist when the path
131 * passed in has a trailing slash. */
132 if(mkdir(str, mode) < 0 && errno != EEXIST) {
133 ret = 1;
136 done:
137 umask(oldmask);
138 free(str);
139 return ret;
142 /** Copies a file.
143 * @param src file path to copy from
144 * @param dest file path to copy to
145 * @return 0 on success, 1 on error
147 int _alpm_copyfile(const char *src, const char *dest)
149 char *buf;
150 int in, out, ret = 1;
151 ssize_t nread;
152 struct stat st;
154 MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
156 OPEN(in, src, O_RDONLY);
157 do {
158 out = open(dest, O_WRONLY | O_CREAT, 0000);
159 } while(out == -1 && errno == EINTR);
160 if(in < 0 || out < 0) {
161 goto cleanup;
164 if(fstat(in, &st) || fchmod(out, st.st_mode)) {
165 goto cleanup;
168 /* do the actual file copy */
169 while((nread = read(in, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
170 ssize_t nwrite = 0;
171 if(nread < 0) {
172 continue;
174 do {
175 nwrite = write(out, buf + nwrite, nread);
176 if(nwrite >= 0) {
177 nread -= nwrite;
178 } else if(errno != EINTR) {
179 goto cleanup;
181 } while(nread > 0);
183 ret = 0;
185 cleanup:
186 free(buf);
187 if(in >= 0) {
188 CLOSE(in);
190 if(out >= 0) {
191 CLOSE(out);
193 return ret;
196 /** Trim trailing newlines from a string (if any exist).
197 * @param str a single line of text
198 * @param len size of str, if known, else 0
199 * @return the length of the trimmed string
201 size_t _alpm_strip_newline(char *str, size_t len)
203 if(*str == '\0') {
204 return 0;
206 if(len == 0) {
207 len = strlen(str);
209 while(len > 0 && str[len - 1] == '\n') {
210 len--;
212 str[len] = '\0';
214 return len;
217 /* Compression functions */
219 /** Open an archive for reading and perform the necessary boilerplate.
220 * This takes care of creating the libarchive 'archive' struct, setting up
221 * compression and format options, opening a file descriptor, setting up the
222 * buffer size, and performing a stat on the path once opened.
223 * On error, no file descriptor is opened, and the archive pointer returned
224 * will be set to NULL.
225 * @param handle the context handle
226 * @param path the path of the archive to open
227 * @param buf space for a stat buffer for the given path
228 * @param archive pointer to place the created archive object
229 * @param error error code to set on failure to open archive
230 * @return -1 on failure, >=0 file descriptor on success
232 int _alpm_open_archive(alpm_handle_t *handle, const char *path,
233 struct stat *buf, struct archive **archive, alpm_errno_t error)
235 int fd;
236 size_t bufsize = ALPM_BUFFER_SIZE;
237 errno = 0;
239 if((*archive = archive_read_new()) == NULL) {
240 RET_ERR(handle, ALPM_ERR_LIBARCHIVE, -1);
243 archive_read_support_compression_all(*archive);
244 archive_read_support_format_all(*archive);
246 _alpm_log(handle, ALPM_LOG_DEBUG, "opening archive %s\n", path);
247 OPEN(fd, path, O_RDONLY);
248 if(fd < 0) {
249 _alpm_log(handle, ALPM_LOG_ERROR,
250 _("could not open file %s: %s\n"), path, strerror(errno));
251 goto error;
254 if(fstat(fd, buf) != 0) {
255 _alpm_log(handle, ALPM_LOG_ERROR,
256 _("could not stat file %s: %s\n"), path, strerror(errno));
257 goto error;
259 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
260 if(buf->st_blksize > ALPM_BUFFER_SIZE) {
261 bufsize = buf->st_blksize;
263 #endif
265 if(archive_read_open_fd(*archive, fd, bufsize) != ARCHIVE_OK) {
266 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
267 path, archive_error_string(*archive));
268 goto error;
271 return fd;
273 error:
274 archive_read_finish(*archive);
275 *archive = NULL;
276 if(fd >= 0) {
277 CLOSE(fd);
279 RET_ERR(handle, error, -1);
282 /** Unpack a specific file in an archive.
283 * @param handle the context handle
284 * @param archive the archive to unpack
285 * @param prefix where to extract the files
286 * @param filename a file within the archive to unpack
287 * @return 0 on success, 1 on failure
289 int _alpm_unpack_single(alpm_handle_t *handle, const char *archive,
290 const char *prefix, const char *filename)
292 alpm_list_t *list = NULL;
293 int ret = 0;
294 if(filename == NULL) {
295 return 1;
297 list = alpm_list_add(list, (void *)filename);
298 ret = _alpm_unpack(handle, archive, prefix, list, 1);
299 alpm_list_free(list);
300 return ret;
303 /** Unpack a list of files in an archive.
304 * @param handle the context handle
305 * @param path the archive to unpack
306 * @param prefix where to extract the files
307 * @param list a list of files within the archive to unpack or NULL for all
308 * @param breakfirst break after the first entry found
309 * @return 0 on success, 1 on failure
311 int _alpm_unpack(alpm_handle_t *handle, const char *path, const char *prefix,
312 alpm_list_t *list, int breakfirst)
314 int ret = 0;
315 mode_t oldmask;
316 struct archive *archive;
317 struct archive_entry *entry;
318 struct stat buf;
319 int fd, cwdfd;
321 fd = _alpm_open_archive(handle, path, &buf, &archive, ALPM_ERR_PKG_OPEN);
322 if(fd < 0) {
323 return 1;
326 oldmask = umask(0022);
328 /* save the cwd so we can restore it later */
329 OPEN(cwdfd, ".", O_RDONLY);
330 if(cwdfd < 0) {
331 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
334 /* just in case our cwd was removed in the upgrade operation */
335 if(chdir(prefix) != 0) {
336 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
337 prefix, strerror(errno));
338 ret = 1;
339 goto cleanup;
342 while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) {
343 const char *entryname;
344 mode_t mode;
346 entryname = archive_entry_pathname(entry);
348 /* If specific files were requested, skip entries that don't match. */
349 if(list) {
350 char *entry_prefix = strdup(entryname);
351 char *p = strstr(entry_prefix,"/");
352 if(p) {
353 *(p+1) = '\0';
355 char *found = alpm_list_find_str(list, entry_prefix);
356 free(entry_prefix);
357 if(!found) {
358 if(archive_read_data_skip(archive) != ARCHIVE_OK) {
359 ret = 1;
360 goto cleanup;
362 continue;
363 } else {
364 _alpm_log(handle, ALPM_LOG_DEBUG, "extracting: %s\n", entryname);
368 mode = archive_entry_mode(entry);
369 if(S_ISREG(mode)) {
370 archive_entry_set_perm(entry, 0644);
371 } else if(S_ISDIR(mode)) {
372 archive_entry_set_perm(entry, 0755);
375 /* Extract the archive entry. */
376 int readret = archive_read_extract(archive, entry, 0);
377 if(readret == ARCHIVE_WARN) {
378 /* operation succeeded but a non-critical error was encountered */
379 _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
380 entryname, archive_error_string(archive));
381 } else if(readret != ARCHIVE_OK) {
382 _alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"),
383 entryname, archive_error_string(archive));
384 ret = 1;
385 goto cleanup;
388 if(breakfirst) {
389 break;
393 cleanup:
394 umask(oldmask);
395 archive_read_finish(archive);
396 CLOSE(fd);
397 if(cwdfd >= 0) {
398 if(fchdir(cwdfd) != 0) {
399 _alpm_log(handle, ALPM_LOG_ERROR,
400 _("could not restore working directory (%s)\n"), strerror(errno));
402 CLOSE(cwdfd);
405 return ret;
408 /** Determine if there are files in a directory.
409 * @param handle the context handle
410 * @param path the full absolute directory path
411 * @param full_count whether to return an exact count of files
412 * @return a file count if full_count is != 0, else >0 if directory has
413 * contents, 0 if no contents, and -1 on error
415 ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path,
416 int full_count)
418 ssize_t files = 0;
419 struct dirent *ent;
420 DIR *dir = opendir(path);
422 if(!dir) {
423 if(errno == ENOTDIR) {
424 _alpm_log(handle, ALPM_LOG_DEBUG, "%s was not a directory\n", path);
425 } else {
426 _alpm_log(handle, ALPM_LOG_DEBUG, "could not read directory %s\n",
427 path);
429 return -1;
431 while((ent = readdir(dir)) != NULL) {
432 const char *name = ent->d_name;
434 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
435 continue;
438 files++;
440 if(!full_count) {
441 break;
445 closedir(dir);
446 return files;
449 /** Write formatted message to log.
450 * @param handle the context handle
451 * @param format formatted string to write out
452 * @param args formatting arguments
453 * @return 0 or number of characters written on success, vfprintf return value
454 * on error
456 int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args)
458 int ret = 0;
460 if(handle->usesyslog) {
461 /* we can't use a va_list more than once, so we need to copy it
462 * so we can use the original when calling vfprintf below. */
463 va_list args_syslog;
464 va_copy(args_syslog, args);
465 vsyslog(LOG_WARNING, fmt, args_syslog);
466 va_end(args_syslog);
469 if(handle->logstream) {
470 time_t t;
471 struct tm *tm;
473 t = time(NULL);
474 tm = localtime(&t);
476 /* Use ISO-8601 date format */
477 fprintf(handle->logstream, "[%04d-%02d-%02d %02d:%02d] ",
478 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
479 tm->tm_hour, tm->tm_min);
480 ret = vfprintf(handle->logstream, fmt, args);
481 fflush(handle->logstream);
484 return ret;
487 /** Execute a command with arguments in a chroot.
488 * @param handle the context handle
489 * @param cmd command to execute
490 * @param argv arguments to pass to cmd
491 * @return 0 on success, 1 on error
493 int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[])
495 pid_t pid;
496 int pipefd[2], cwdfd;
497 int retval = 0;
499 /* save the cwd so we can restore it later */
500 OPEN(cwdfd, ".", O_RDONLY);
501 if(cwdfd < 0) {
502 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
505 /* just in case our cwd was removed in the upgrade operation */
506 if(chdir(handle->root) != 0) {
507 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
508 handle->root, strerror(errno));
509 goto cleanup;
512 _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n",
513 cmd, handle->root);
515 /* Flush open fds before fork() to avoid cloning buffers */
516 fflush(NULL);
518 if(pipe(pipefd) == -1) {
519 _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
520 retval = 1;
521 goto cleanup;
524 /* fork- parent and child each have seperate code blocks below */
525 pid = fork();
526 if(pid == -1) {
527 _alpm_log(handle, ALPM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno));
528 retval = 1;
529 goto cleanup;
532 if(pid == 0) {
533 /* this code runs for the child only (the actual chroot/exec) */
534 CLOSE(1);
535 CLOSE(2);
536 while(dup2(pipefd[1], 1) == -1 && errno == EINTR);
537 while(dup2(pipefd[1], 2) == -1 && errno == EINTR);
538 CLOSE(pipefd[0]);
539 CLOSE(pipefd[1]);
541 /* use fprintf instead of _alpm_log to send output through the parent */
542 if(chroot(handle->root) != 0) {
543 fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
544 exit(1);
546 if(chdir("/") != 0) {
547 fprintf(stderr, _("could not change directory to %s (%s)\n"),
548 "/", strerror(errno));
549 exit(1);
551 umask(0022);
552 execv(cmd, argv);
553 /* execv only returns if there was an error */
554 fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
555 exit(1);
556 } else {
557 /* this code runs for the parent only (wait on the child) */
558 int status;
559 FILE *pipe_file;
561 CLOSE(pipefd[1]);
562 pipe_file = fdopen(pipefd[0], "r");
563 if(pipe_file == NULL) {
564 CLOSE(pipefd[0]);
565 retval = 1;
566 } else {
567 while(!feof(pipe_file)) {
568 char line[PATH_MAX];
569 if(fgets(line, PATH_MAX, pipe_file) == NULL)
570 break;
571 alpm_logaction(handle, "%s", line);
572 EVENT(handle, ALPM_EVENT_SCRIPTLET_INFO, line, NULL);
574 fclose(pipe_file);
577 while(waitpid(pid, &status, 0) == -1) {
578 if(errno != EINTR) {
579 _alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
580 retval = 1;
581 goto cleanup;
585 /* report error from above after the child has exited */
586 if(retval != 0) {
587 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno));
588 goto cleanup;
590 /* check the return status, make sure it is 0 (success) */
591 if(WIFEXITED(status)) {
592 _alpm_log(handle, ALPM_LOG_DEBUG, "call to waitpid succeeded\n");
593 if(WEXITSTATUS(status) != 0) {
594 _alpm_log(handle, ALPM_LOG_ERROR, _("command failed to execute correctly\n"));
595 retval = 1;
600 cleanup:
601 if(cwdfd >= 0) {
602 if(fchdir(cwdfd) != 0) {
603 _alpm_log(handle, ALPM_LOG_ERROR,
604 _("could not restore working directory (%s)\n"), strerror(errno));
606 CLOSE(cwdfd);
609 return retval;
612 /** Run ldconfig in a chroot.
613 * @param handle the context handle
614 * @return 0 on success, 1 on error
616 int _alpm_ldconfig(alpm_handle_t *handle)
618 char line[PATH_MAX];
620 _alpm_log(handle, ALPM_LOG_DEBUG, "running ldconfig\n");
622 snprintf(line, PATH_MAX, "%setc/ld.so.conf", handle->root);
623 if(access(line, F_OK) == 0) {
624 snprintf(line, PATH_MAX, "%ssbin/ldconfig", handle->root);
625 if(access(line, X_OK) == 0) {
626 char arg0[32];
627 char *argv[] = { arg0, NULL };
628 strcpy(arg0, "ldconfig");
629 return _alpm_run_chroot(handle, "/sbin/ldconfig", argv);
633 return 0;
636 /** Helper function for comparing strings using the alpm "compare func"
637 * signature.
638 * @param s1 first string to be compared
639 * @param s2 second string to be compared
640 * @return 0 if strings are equal, positive int if first unequal character
641 * has a greater value in s1, negative if it has a greater value in s2
643 int _alpm_str_cmp(const void *s1, const void *s2)
645 return strcmp(s1, s2);
648 /** Find a filename in a registered alpm cachedir.
649 * @param handle the context handle
650 * @param filename name of file to find
651 * @return malloced path of file, NULL if not found
653 char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename)
655 char path[PATH_MAX];
656 char *retpath;
657 alpm_list_t *i;
658 struct stat buf;
660 /* Loop through the cache dirs until we find a matching file */
661 for(i = handle->cachedirs; i; i = i->next) {
662 snprintf(path, PATH_MAX, "%s%s", (char *)i->data,
663 filename);
664 if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) {
665 retpath = strdup(path);
666 _alpm_log(handle, ALPM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
667 return retpath;
670 /* package wasn't found in any cachedir */
671 return NULL;
674 /** Check the alpm cachedirs for existance and find a writable one.
675 * If no valid cache directory can be found, use /tmp.
676 * @param handle the context handle
677 * @return pointer to a writable cache directory.
679 const char *_alpm_filecache_setup(alpm_handle_t *handle)
681 struct stat buf;
682 alpm_list_t *i;
683 char *cachedir;
684 const char *tmpdir;
686 /* Loop through the cache dirs until we find a usable directory */
687 for(i = handle->cachedirs; i; i = i->next) {
688 cachedir = i->data;
689 if(stat(cachedir, &buf) != 0) {
690 /* cache directory does not exist.... try creating it */
691 _alpm_log(handle, ALPM_LOG_WARNING, _("no %s cache exists, creating...\n"),
692 cachedir);
693 if(_alpm_makepath(cachedir) == 0) {
694 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
695 return cachedir;
697 } else if(!S_ISDIR(buf.st_mode)) {
698 _alpm_log(handle, ALPM_LOG_DEBUG,
699 "skipping cachedir, not a directory: %s\n", cachedir);
700 } else if(_alpm_access(handle, NULL, cachedir, W_OK) != 0) {
701 _alpm_log(handle, ALPM_LOG_DEBUG,
702 "skipping cachedir, not writable: %s\n", cachedir);
703 } else if(!(buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) {
704 _alpm_log(handle, ALPM_LOG_DEBUG,
705 "skipping cachedir, no write bits set: %s\n", cachedir);
706 } else {
707 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
708 return cachedir;
712 /* we didn't find a valid cache directory. use TMPDIR or /tmp. */
713 if((tmpdir = getenv("TMPDIR")) && stat(tmpdir, &buf) && S_ISDIR(buf.st_mode)) {
714 /* TMPDIR was good, we can use it */
715 } else {
716 tmpdir = "/tmp";
718 alpm_option_add_cachedir(handle, tmpdir);
719 cachedir = handle->cachedirs->prev->data;
720 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
721 _alpm_log(handle, ALPM_LOG_WARNING,
722 _("couldn't find or create package cache, using %s instead\n"), cachedir);
723 return cachedir;
726 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
727 * Linux lstat follows POSIX semantics and still performs a dereference on
728 * the first, and for uses of lstat in libalpm this is not what we want.
729 * @param path path to file to lstat
730 * @param buf structure to fill with stat information
731 * @return the return code from lstat
733 int _alpm_lstat(const char *path, struct stat *buf)
735 int ret;
736 size_t len = strlen(path);
738 /* strip the trailing slash if one exists */
739 if(len != 0 && path[len - 1] == '/') {
740 char *newpath = strdup(path);
741 newpath[len - 1] = '\0';
742 ret = lstat(newpath, buf);
743 free(newpath);
744 } else {
745 ret = lstat(path, buf);
748 return ret;
751 #ifdef HAVE_LIBSSL
752 /** Compute the MD5 message digest of a file.
753 * @param path file path of file to compute MD5 digest of
754 * @param output string to hold computed MD5 digest
755 * @return 0 on success, 1 on file open error, 2 on file read error
757 static int md5_file(const char *path, unsigned char output[16])
759 MD5_CTX ctx;
760 unsigned char *buf;
761 ssize_t n;
762 int fd;
764 MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
766 OPEN(fd, path, O_RDONLY);
767 if(fd < 0) {
768 free(buf);
769 return 1;
772 MD5_Init(&ctx);
774 while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
775 if(n < 0) {
776 continue;
778 MD5_Update(&ctx, buf, n);
781 CLOSE(fd);
782 free(buf);
784 if(n < 0) {
785 return 2;
788 MD5_Final(output, &ctx);
789 return 0;
792 /* third param is so we match the PolarSSL definition */
793 /** Compute the SHA-224 or SHA-256 message digest of a file.
794 * @param path file path of file to compute SHA2 digest of
795 * @param output string to hold computed SHA2 digest
796 * @param is224 use SHA-224 instead of SHA-256
797 * @return 0 on success, 1 on file open error, 2 on file read error
799 static int sha2_file(const char *path, unsigned char output[32], int is224)
801 SHA256_CTX ctx;
802 unsigned char *buf;
803 ssize_t n;
804 int fd;
806 MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
808 OPEN(fd, path, O_RDONLY);
809 if(fd < 0) {
810 free(buf);
811 return 1;
814 if(is224) {
815 SHA224_Init(&ctx);
816 } else {
817 SHA256_Init(&ctx);
820 while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
821 if(n < 0) {
822 continue;
824 if(is224) {
825 SHA224_Update(&ctx, buf, n);
826 } else {
827 SHA256_Update(&ctx, buf, n);
831 CLOSE(fd);
832 free(buf);
834 if(n < 0) {
835 return 2;
838 if(is224) {
839 SHA224_Final(output, &ctx);
840 } else {
841 SHA256_Final(output, &ctx);
843 return 0;
845 #endif
847 /** Create a string representing bytes in hexadecimal.
848 * @param bytes the bytes to represent in hexadecimal
849 * @param size number of bytes to consider
850 * @return a NULL terminated string with the hexadecimal representation of
851 * bytes or NULL on error. This string must be freed.
853 static char *hex_representation(unsigned char *bytes, size_t size)
855 static const char *hex_digits = "0123456789abcdef";
856 char *str;
857 size_t i;
859 MALLOC(str, 2 * size + 1, return NULL);
861 for (i = 0; i < size; i++) {
862 str[2 * i] = hex_digits[bytes[i] >> 4];
863 str[2 * i + 1] = hex_digits[bytes[i] & 0x0f];
866 str[2 * size] = '\0';
868 return str;
871 /** Get the md5 sum of file.
872 * @param filename name of the file
873 * @return the checksum on success, NULL on error
874 * @addtogroup alpm_misc
876 char SYMEXPORT *alpm_compute_md5sum(const char *filename)
878 unsigned char output[16];
880 ASSERT(filename != NULL, return NULL);
882 /* defined above for OpenSSL, otherwise defined in md5.h */
883 if(md5_file(filename, output) > 0) {
884 return NULL;
887 return hex_representation(output, 16);
890 /** Get the sha256 sum of file.
891 * @param filename name of the file
892 * @return the checksum on success, NULL on error
893 * @addtogroup alpm_misc
895 char SYMEXPORT *alpm_compute_sha256sum(const char *filename)
897 unsigned char output[32];
899 ASSERT(filename != NULL, return NULL);
901 /* defined above for OpenSSL, otherwise defined in sha2.h */
902 if(sha2_file(filename, output, 0) > 0) {
903 return NULL;
906 return hex_representation(output, 32);
909 /** Calculates a file's MD5 or SHA2 digest and compares it to an expected value.
910 * @param filepath path of the file to check
911 * @param expected hash value to compare against
912 * @param type digest type to use
913 * @return 0 if file matches the expected hash, 1 if they do not match, -1 on
914 * error
916 int _alpm_test_checksum(const char *filepath, const char *expected,
917 alpm_pkgvalidation_t type)
919 char *computed;
920 int ret;
922 if(type == ALPM_PKG_VALIDATION_MD5SUM) {
923 computed = alpm_compute_md5sum(filepath);
924 } else if(type == ALPM_PKG_VALIDATION_SHA256SUM) {
925 computed = alpm_compute_sha256sum(filepath);
926 } else {
927 return -1;
930 if(expected == NULL || computed == NULL) {
931 ret = -1;
932 } else if(strcmp(expected, computed) != 0) {
933 ret = 1;
934 } else {
935 ret = 0;
938 FREE(computed);
939 return ret;
942 /* Note: does NOT handle sparse files on purpose for speed. */
943 /** TODO.
944 * Does not handle sparse files on purpose for speed.
945 * @param a
946 * @param b
947 * @return
949 int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
951 /* ensure we start populating our line buffer at the beginning */
952 b->line_offset = b->line;
954 while(1) {
955 size_t block_remaining;
956 char *eol;
958 /* have we processed this entire block? */
959 if(b->block + b->block_size == b->block_offset) {
960 int64_t offset;
961 if(b->ret == ARCHIVE_EOF) {
962 /* reached end of archive on the last read, now we are out of data */
963 goto cleanup;
966 /* zero-copy - this is the entire next block of data. */
967 b->ret = archive_read_data_block(a, (void *)&b->block,
968 &b->block_size, &offset);
969 b->block_offset = b->block;
970 block_remaining = b->block_size;
972 /* error, cleanup */
973 if(b->ret < ARCHIVE_OK) {
974 goto cleanup;
976 } else {
977 block_remaining = b->block + b->block_size - b->block_offset;
980 /* look through the block looking for EOL characters */
981 eol = memchr(b->block_offset, '\n', block_remaining);
982 if(!eol) {
983 eol = memchr(b->block_offset, '\0', block_remaining);
986 /* allocate our buffer, or ensure our existing one is big enough */
987 if(!b->line) {
988 /* set the initial buffer to the read block_size */
989 CALLOC(b->line, b->block_size + 1, sizeof(char), b->ret = -ENOMEM; goto cleanup);
990 b->line_size = b->block_size + 1;
991 b->line_offset = b->line;
992 } else {
993 /* note: we know eol > b->block_offset and b->line_offset > b->line,
994 * so we know the result is unsigned and can fit in size_t */
995 size_t new = eol ? (size_t)(eol - b->block_offset) : block_remaining;
996 size_t needed = (size_t)((b->line_offset - b->line) + new + 1);
997 if(needed > b->max_line_size) {
998 b->ret = -ERANGE;
999 goto cleanup;
1001 if(needed > b->line_size) {
1002 /* need to realloc + copy data to fit total length */
1003 char *new_line;
1004 CALLOC(new_line, needed, sizeof(char), b->ret = -ENOMEM; goto cleanup);
1005 memcpy(new_line, b->line, b->line_size);
1006 b->line_size = needed;
1007 b->line_offset = new_line + (b->line_offset - b->line);
1008 free(b->line);
1009 b->line = new_line;
1013 if(eol) {
1014 size_t len = (size_t)(eol - b->block_offset);
1015 memcpy(b->line_offset, b->block_offset, len);
1016 b->line_offset[len] = '\0';
1017 b->block_offset = eol + 1;
1018 b->real_line_size = b->line_offset + len - b->line;
1019 /* this is the main return point; from here you can read b->line */
1020 return ARCHIVE_OK;
1021 } else {
1022 /* we've looked through the whole block but no newline, copy it */
1023 size_t len = (size_t)(b->block + b->block_size - b->block_offset);
1024 memcpy(b->line_offset, b->block_offset, len);
1025 b->line_offset += len;
1026 b->block_offset = b->block + b->block_size;
1027 /* there was no new data, return what is left; saved ARCHIVE_EOF will be
1028 * returned on next call */
1029 if(len == 0) {
1030 b->line_offset[0] = '\0';
1031 b->real_line_size = b->line_offset - b->line;
1032 return ARCHIVE_OK;
1037 cleanup:
1039 int ret = b->ret;
1040 FREE(b->line);
1041 memset(b, 0, sizeof(struct archive_read_buffer));
1042 return ret;
1046 /** Parse a full package specifier.
1047 * @param target package specifier to parse, such as: "pacman-4.0.1-2",
1048 * "pacman-4.01-2/", or "pacman-4.0.1-2/desc"
1049 * @param name to hold package name
1050 * @param version to hold package version
1051 * @param name_hash to hold package name hash
1052 * @return 0 on success, -1 on error
1054 int _alpm_splitname(const char *target, char **name, char **version,
1055 unsigned long *name_hash)
1057 /* the format of a db entry is as follows:
1058 * package-version-rel/
1059 * package-version-rel/desc (we ignore the filename portion)
1060 * package name can contain hyphens, so parse from the back- go back
1061 * two hyphens and we have split the version from the name.
1063 const char *pkgver, *end;
1065 if(target == NULL) {
1066 return -1;
1069 /* remove anything trailing a '/' */
1070 end = strchr(target, '/');
1071 if(!end) {
1072 end = target + strlen(target);
1075 /* do the magic parsing- find the beginning of the version string
1076 * by doing two iterations of same loop to lop off two hyphens */
1077 for(pkgver = end - 1; *pkgver && *pkgver != '-'; pkgver--);
1078 for(pkgver = pkgver - 1; *pkgver && *pkgver != '-'; pkgver--);
1079 if(*pkgver != '-' || pkgver == target) {
1080 return -1;
1083 /* copy into fields and return */
1084 if(version) {
1085 if(*version) {
1086 FREE(*version);
1088 /* version actually points to the dash, so need to increment 1 and account
1089 * for potential end character */
1090 STRNDUP(*version, pkgver + 1, end - pkgver - 1, return -1);
1093 if(name) {
1094 if(*name) {
1095 FREE(*name);
1097 STRNDUP(*name, target, pkgver - target, return -1);
1098 if(name_hash) {
1099 *name_hash = _alpm_hash_sdbm(*name);
1103 return 0;
1106 /** Hash the given string to an unsigned long value.
1107 * This is the standard sdbm hashing algorithm.
1108 * @param str string to hash
1109 * @return the hash value of the given string
1111 unsigned long _alpm_hash_sdbm(const char *str)
1113 unsigned long hash = 0;
1114 int c;
1116 if(!str) {
1117 return hash;
1119 while((c = *str++)) {
1120 hash = c + hash * 65599;
1123 return hash;
1126 /** Convert a string to a file offset.
1127 * This parses bare positive integers only.
1128 * @param line string to convert
1129 * @return off_t on success, -1 on error
1131 off_t _alpm_strtoofft(const char *line)
1133 char *end;
1134 unsigned long long result;
1135 errno = 0;
1137 /* we are trying to parse bare numbers only, no leading anything */
1138 if(!isdigit((unsigned char)line[0])) {
1139 return (off_t)-1;
1141 result = strtoull(line, &end, 10);
1142 if(result == 0 && end == line) {
1143 /* line was not a number */
1144 return (off_t)-1;
1145 } else if(result == ULLONG_MAX && errno == ERANGE) {
1146 /* line does not fit in unsigned long long */
1147 return (off_t)-1;
1148 } else if(*end) {
1149 /* line began with a number but has junk left over at the end */
1150 return (off_t)-1;
1153 return (off_t)result;
1156 /** Parses a date into an alpm_time_t struct.
1157 * @param line date to parse
1158 * @return time struct on success, 0 on error
1160 alpm_time_t _alpm_parsedate(const char *line)
1162 char *end;
1163 long long result;
1164 errno = 0;
1166 if(isalpha((unsigned char)line[0])) {
1167 /* initialize to null in case of failure */
1168 struct tm tmp_tm;
1169 memset(&tmp_tm, 0, sizeof(struct tm));
1170 setlocale(LC_TIME, "C");
1171 strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm);
1172 setlocale(LC_TIME, "");
1173 return (alpm_time_t)mktime(&tmp_tm);
1176 result = strtoll(line, &end, 10);
1177 if(result == 0 && end == line) {
1178 /* line was not a number */
1179 errno = EINVAL;
1180 return 0;
1181 } else if(errno == ERANGE) {
1182 /* line does not fit in long long */
1183 return 0;
1184 } else if(*end) {
1185 /* line began with a number but has junk left over at the end */
1186 errno = EINVAL;
1187 return 0;
1190 return (alpm_time_t)result;
1193 /** Wrapper around access() which takes a dir and file argument
1194 * separately and generates an appropriate error message.
1195 * If dir is NULL file will be treated as the whole path.
1196 * @param handle an alpm handle
1197 * @param dir directory path ending with and slash
1198 * @param file filename
1199 * @param amode access mode as described in access()
1200 * @return int value returned by access()
1202 int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int amode)
1204 size_t len = 0;
1205 int ret = 0;
1207 if(dir) {
1208 char *check_path;
1210 len = strlen(dir) + strlen(file) + 1;
1211 CALLOC(check_path, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, -1));
1212 snprintf(check_path, len, "%s%s", dir, file);
1214 ret = access(check_path, amode);
1215 free(check_path);
1216 } else {
1217 dir = "";
1218 ret = access(file, amode);
1221 if(ret != 0) {
1222 if(amode & R_OK) {
1223 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not readable: %s\n",
1224 dir, file, strerror(errno));
1226 if(amode & W_OK) {
1227 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not writable: %s\n",
1228 dir, file, strerror(errno));
1230 if(amode & X_OK) {
1231 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not executable: %s\n",
1232 dir, file, strerror(errno));
1234 if(amode == F_OK) {
1235 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" does not exist: %s\n",
1236 dir, file, strerror(errno));
1239 return ret;
1242 /** Checks whether a string matches a shell wildcard pattern.
1243 * Wrapper around fnmatch.
1244 * @param pattern pattern to match aganist
1245 * @param string string to check against pattern
1246 * @return 0 if string matches pattern, non-zero if they don't match and on
1247 * error
1249 int _alpm_fnmatch(const void *pattern, const void *string)
1251 return fnmatch(pattern, string, 0);
1254 void _alpm_alloc_fail(size_t size)
1256 fprintf(stderr, "alloc failure: could not allocate %zd bytes\n", size);
1259 #ifndef HAVE_STRNDUP
1260 /* A quick and dirty implementation derived from glibc */
1261 /** Determines the length of a fixed-size string.
1262 * @param s string to be measured
1263 * @param max maximum number of characters to search for the string end
1264 * @return length of s or max, whichever is smaller
1266 static size_t strnlen(const char *s, size_t max)
1268 register const char *p;
1269 for(p = s; *p && max--; ++p);
1270 return (p - s);
1273 /** Copies a string.
1274 * Returned string needs to be freed
1275 * @param s string to be copied
1276 * @param n maximum number of characters to copy
1277 * @return pointer to the new string on success, NULL on error
1279 char *strndup(const char *s, size_t n)
1281 size_t len = strnlen(s, n);
1282 char *new = (char *) malloc(len + 1);
1284 if(new == NULL)
1285 return NULL;
1287 new[len] = '\0';
1288 return (char *)memcpy(new, s, len);
1290 #endif
1292 /* vim: set ts=2 sw=2 noet: */