1 /*
2  *  util.c
3  *
4  *  Copyright (c) 2006-2018 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>
10  *
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.
15  *
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.
20  *
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/>.
23  */
24 
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <ctype.h>
28 #include <dirent.h>
29 #include <time.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <sys/wait.h>
33 #include <sys/socket.h>
34 #include <fnmatch.h>
35 #include <poll.h>
36 #include <signal.h>
37 
38 /* libarchive */
39 #include <archive.h>
40 #include <archive_entry.h>
41 
42 #ifdef HAVE_LIBSSL
43 #include <openssl/md5.h>
44 #include <openssl/sha.h>
45 #endif
46 
47 #ifdef HAVE_LIBNETTLE
48 #include <nettle/md5.h>
49 #include <nettle/sha2.h>
50 #endif
51 
52 /* libalpm */
53 #include "util.h"
54 #include "log.h"
55 #include "libarchive-compat.h"
56 #include "alpm.h"
57 #include "alpm_list.h"
58 #include "handle.h"
59 #include "trans.h"
60 
61 #ifndef HAVE_STRSEP
62 /** Extracts tokens from a string.
63  * Replaces strset which is not portable (missing on Solaris).
64  * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com>
65  * Modifies str to point to the first character after the token if one is
66  * found, or NULL if one is not.
67  * @param str string containing delimited tokens to parse
68  * @param delim character delimiting tokens in str
69  * @return pointer to the first token in str if str is not NULL, NULL if
70  * str is NULL
71  */
strsep(char ** str,const char * delims)72 char *strsep(char **str, const char *delims)
73 {
74 	char *token;
75 
76 	if(*str == NULL) {
77 		/* No more tokens */
78 		return NULL;
79 	}
80 
81 	token = *str;
82 	while(**str != '\0') {
83 		if(strchr(delims, **str) != NULL) {
84 			**str = '\0';
85 			(*str)++;
86 			return token;
87 		}
88 		(*str)++;
89 	}
90 	/* There is no other token */
91 	*str = NULL;
92 	return token;
93 }
94 #endif
95 
_alpm_makepath(const char * path)96 int _alpm_makepath(const char *path)
97 {
98 	return _alpm_makepath_mode(path, 0755);
99 }
100 
101 /** Creates a directory, including parents if needed, similar to 'mkdir -p'.
102  * @param path directory path to create
103  * @param mode permission mode for created directories
104  * @return 0 on success, 1 on error
105  */
_alpm_makepath_mode(const char * path,mode_t mode)106 int _alpm_makepath_mode(const char *path, mode_t mode)
107 {
108 	char *ptr, *str;
109 	mode_t oldmask;
110 	int ret = 0;
111 
112 	STRDUP(str, path, return 1);
113 
114 	oldmask = umask(0000);
115 
116 	for(ptr = str; *ptr; ptr++) {
117 		/* detect mid-path condition and zero length paths */
118 		if(*ptr != '/' || ptr == str || ptr[-1] == '/') {
119 			continue;
120 		}
121 
122 		/* temporarily mask the end of the path */
123 		*ptr = '\0';
124 
125 		if(mkdir(str, mode) < 0 && errno != EEXIST) {
126 			ret = 1;
127 			goto done;
128 		}
129 
130 		/* restore path separator */
131 		*ptr = '/';
132 	}
133 
134 	/* end of the string. add the full path. It will already exist when the path
135 	 * passed in has a trailing slash. */
136 	if(mkdir(str, mode) < 0 && errno != EEXIST) {
137 		ret = 1;
138 	}
139 
140 done:
141 	umask(oldmask);
142 	free(str);
143 	return ret;
144 }
145 
146 /** Copies a file.
147  * @param src file path to copy from
148  * @param dest file path to copy to
149  * @return 0 on success, 1 on error
150  */
_alpm_copyfile(const char * src,const char * dest)151 int _alpm_copyfile(const char *src, const char *dest)
152 {
153 	char *buf;
154 	int in, out, ret = 1;
155 	ssize_t nread;
156 	struct stat st;
157 
158 	MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
159 
160 	OPEN(in, src, O_RDONLY | O_CLOEXEC);
161 	do {
162 		out = open(dest, O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC, 0000);
163 	} while(out == -1 && errno == EINTR);
164 	if(in < 0 || out < 0) {
165 		goto cleanup;
166 	}
167 
168 	if(fstat(in, &st) || fchmod(out, st.st_mode)) {
169 		goto cleanup;
170 	}
171 
172 	/* do the actual file copy */
173 	while((nread = read(in, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
174 		ssize_t nwrite = 0;
175 		if(nread < 0) {
176 			continue;
177 		}
178 		do {
179 			nwrite = write(out, buf + nwrite, nread);
180 			if(nwrite >= 0) {
181 				nread -= nwrite;
182 			} else if(errno != EINTR) {
183 				goto cleanup;
184 			}
185 		} while(nread > 0);
186 	}
187 	ret = 0;
188 
189 cleanup:
190 	free(buf);
191 	if(in >= 0) {
192 		close(in);
193 	}
194 	if(out >= 0) {
195 		close(out);
196 	}
197 	return ret;
198 }
199 
200 /** Trim trailing newlines from a string (if any exist).
201  * @param str a single line of text
202  * @param len size of str, if known, else 0
203  * @return the length of the trimmed string
204  */
_alpm_strip_newline(char * str,size_t len)205 size_t _alpm_strip_newline(char *str, size_t len)
206 {
207 	if(*str == '\0') {
208 		return 0;
209 	}
210 	if(len == 0) {
211 		len = strlen(str);
212 	}
213 	while(len > 0 && str[len - 1] == '\n') {
214 		len--;
215 	}
216 	str[len] = '\0';
217 
218 	return len;
219 }
220 
221 /* Compression functions */
222 
223 /** Open an archive for reading and perform the necessary boilerplate.
224  * This takes care of creating the libarchive 'archive' struct, setting up
225  * compression and format options, opening a file descriptor, setting up the
226  * buffer size, and performing a stat on the path once opened.
227  * On error, no file descriptor is opened, and the archive pointer returned
228  * will be set to NULL.
229  * @param handle the context handle
230  * @param path the path of the archive to open
231  * @param buf space for a stat buffer for the given path
232  * @param archive pointer to place the created archive object
233  * @param error error code to set on failure to open archive
234  * @return -1 on failure, >=0 file descriptor on success
235  */
_alpm_open_archive(alpm_handle_t * handle,const char * path,struct stat * buf,struct archive ** archive,alpm_errno_t error)236 int _alpm_open_archive(alpm_handle_t *handle, const char *path,
237 		struct stat *buf, struct archive **archive, alpm_errno_t error)
238 {
239 	int fd;
240 	size_t bufsize = ALPM_BUFFER_SIZE;
241 	errno = 0;
242 
243 	if((*archive = archive_read_new()) == NULL) {
244 		RET_ERR(handle, ALPM_ERR_LIBARCHIVE, -1);
245 	}
246 
247 	_alpm_archive_read_support_filter_all(*archive);
248 	archive_read_support_format_all(*archive);
249 
250 	_alpm_log(handle, ALPM_LOG_DEBUG, "opening archive %s\n", path);
251 	OPEN(fd, path, O_RDONLY | O_CLOEXEC);
252 	if(fd < 0) {
253 		_alpm_log(handle, ALPM_LOG_ERROR,
254 				_("could not open file %s: %s\n"), path, strerror(errno));
255 		goto error;
256 	}
257 
258 	if(fstat(fd, buf) != 0) {
259 		_alpm_log(handle, ALPM_LOG_ERROR,
260 				_("could not stat file %s: %s\n"), path, strerror(errno));
261 		goto error;
262 	}
263 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
264 	if(buf->st_blksize > ALPM_BUFFER_SIZE) {
265 		bufsize = buf->st_blksize;
266 	}
267 #endif
268 
269 	if(archive_read_open_fd(*archive, fd, bufsize) != ARCHIVE_OK) {
270 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
271 				path, archive_error_string(*archive));
272 		goto error;
273 	}
274 
275 	return fd;
276 
277 error:
278 	_alpm_archive_read_free(*archive);
279 	*archive = NULL;
280 	if(fd >= 0) {
281 		close(fd);
282 	}
283 	RET_ERR(handle, error, -1);
284 }
285 
286 /** Unpack a specific file in an archive.
287  * @param handle the context handle
288  * @param archive the archive to unpack
289  * @param prefix where to extract the files
290  * @param filename a file within the archive to unpack
291  * @return 0 on success, 1 on failure
292  */
_alpm_unpack_single(alpm_handle_t * handle,const char * archive,const char * prefix,const char * filename)293 int _alpm_unpack_single(alpm_handle_t *handle, const char *archive,
294 		const char *prefix, const char *filename)
295 {
296 	alpm_list_t *list = NULL;
297 	int ret = 0;
298 	if(filename == NULL) {
299 		return 1;
300 	}
301 	list = alpm_list_add(list, (void *)filename);
302 	ret = _alpm_unpack(handle, archive, prefix, list, 1);
303 	alpm_list_free(list);
304 	return ret;
305 }
306 
307 /** Unpack a list of files in an archive.
308  * @param handle the context handle
309  * @param path the archive to unpack
310  * @param prefix where to extract the files
311  * @param list a list of files within the archive to unpack or NULL for all
312  * @param breakfirst break after the first entry found
313  * @return 0 on success, 1 on failure
314  */
_alpm_unpack(alpm_handle_t * handle,const char * path,const char * prefix,alpm_list_t * list,int breakfirst)315 int _alpm_unpack(alpm_handle_t *handle, const char *path, const char *prefix,
316 		alpm_list_t *list, int breakfirst)
317 {
318 	int ret = 0;
319 	mode_t oldmask;
320 	struct archive *archive;
321 	struct archive_entry *entry;
322 	struct stat buf;
323 	int fd, cwdfd;
324 
325 	fd = _alpm_open_archive(handle, path, &buf, &archive, ALPM_ERR_PKG_OPEN);
326 	if(fd < 0) {
327 		return 1;
328 	}
329 
330 	oldmask = umask(0022);
331 
332 	/* save the cwd so we can restore it later */
333 	OPEN(cwdfd, ".", O_RDONLY | O_CLOEXEC);
334 	if(cwdfd < 0) {
335 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
336 	}
337 
338 	/* just in case our cwd was removed in the upgrade operation */
339 	if(chdir(prefix) != 0) {
340 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
341 				prefix, strerror(errno));
342 		ret = 1;
343 		goto cleanup;
344 	}
345 
346 	while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) {
347 		const char *entryname;
348 		mode_t mode;
349 
350 		entryname = archive_entry_pathname(entry);
351 
352 		/* If specific files were requested, skip entries that don't match. */
353 		if(list) {
354 			char *entry_prefix = strdup(entryname);
355 			char *p = strstr(entry_prefix,"/");
356 			if(p) {
357 				*(p + 1) = '\0';
358 			}
359 			char *found = alpm_list_find_str(list, entry_prefix);
360 			free(entry_prefix);
361 			if(!found) {
362 				if(archive_read_data_skip(archive) != ARCHIVE_OK) {
363 					ret = 1;
364 					goto cleanup;
365 				}
366 				continue;
367 			} else {
368 				_alpm_log(handle, ALPM_LOG_DEBUG, "extracting: %s\n", entryname);
369 			}
370 		}
371 
372 		mode = archive_entry_mode(entry);
373 		if(S_ISREG(mode)) {
374 			archive_entry_set_perm(entry, 0644);
375 		} else if(S_ISDIR(mode)) {
376 			archive_entry_set_perm(entry, 0755);
377 		}
378 
379 		/* Extract the archive entry. */
380 		int readret = archive_read_extract(archive, entry, 0);
381 		if(readret == ARCHIVE_WARN) {
382 			/* operation succeeded but a non-critical error was encountered */
383 			_alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
384 					entryname, archive_error_string(archive));
385 		} else if(readret != ARCHIVE_OK) {
386 			_alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"),
387 					entryname, archive_error_string(archive));
388 			ret = 1;
389 			goto cleanup;
390 		}
391 
392 		if(breakfirst) {
393 			break;
394 		}
395 	}
396 
397 cleanup:
398 	umask(oldmask);
399 	_alpm_archive_read_free(archive);
400 	close(fd);
401 	if(cwdfd >= 0) {
402 		if(fchdir(cwdfd) != 0) {
403 			_alpm_log(handle, ALPM_LOG_ERROR,
404 					_("could not restore working directory (%s)\n"), strerror(errno));
405 		}
406 		close(cwdfd);
407 	}
408 
409 	return ret;
410 }
411 
412 /** Determine if there are files in a directory.
413  * @param handle the context handle
414  * @param path the full absolute directory path
415  * @param full_count whether to return an exact count of files
416  * @return a file count if full_count is != 0, else >0 if directory has
417  * contents, 0 if no contents, and -1 on error
418  */
_alpm_files_in_directory(alpm_handle_t * handle,const char * path,int full_count)419 ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path,
420 		int full_count)
421 {
422 	ssize_t files = 0;
423 	struct dirent *ent;
424 	DIR *dir = opendir(path);
425 
426 	if(!dir) {
427 		if(errno == ENOTDIR) {
428 			_alpm_log(handle, ALPM_LOG_DEBUG, "%s was not a directory\n", path);
429 		} else {
430 			_alpm_log(handle, ALPM_LOG_DEBUG, "could not read directory %s\n",
431 					path);
432 		}
433 		return -1;
434 	}
435 	while((ent = readdir(dir)) != NULL) {
436 		const char *name = ent->d_name;
437 
438 		if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
439 			continue;
440 		}
441 
442 		files++;
443 
444 		if(!full_count) {
445 			break;
446 		}
447 	}
448 
449 	closedir(dir);
450 	return files;
451 }
452 
should_retry(int errnum)453 static int should_retry(int errnum)
454 {
455 	return errnum == EAGAIN
456 /* EAGAIN may be the same value as EWOULDBLOCK (POSIX.1) - prevent GCC warning */
457 #if EAGAIN != EWOULDBLOCK
458 	|| errnum == EWOULDBLOCK
459 #endif
460 	|| errnum == EINTR;
461 }
462 
_alpm_chroot_write_to_child(alpm_handle_t * handle,int fd,char * buf,ssize_t * buf_size,ssize_t buf_limit,_alpm_cb_io out_cb,void * cb_ctx)463 static int _alpm_chroot_write_to_child(alpm_handle_t *handle, int fd,
464 		char *buf, ssize_t *buf_size, ssize_t buf_limit,
465 		_alpm_cb_io out_cb, void *cb_ctx)
466 {
467 	ssize_t nwrite;
468 
469 	if(*buf_size == 0) {
470 		/* empty buffer, ask the callback for more */
471 		if((*buf_size = out_cb(buf, buf_limit, cb_ctx)) == 0) {
472 			/* no more to write, close the pipe */
473 			return -1;
474 		}
475 	}
476 
477 	nwrite = send(fd, buf, *buf_size, MSG_NOSIGNAL);
478 
479 	if(nwrite != -1) {
480 		/* write was successful, remove the written data from the buffer */
481 		*buf_size -= nwrite;
482 		memmove(buf, buf + nwrite, *buf_size);
483 	} else if(should_retry(errno)) {
484 		/* nothing written, try again later */
485 	} else {
486 		_alpm_log(handle, ALPM_LOG_ERROR,
487 				_("unable to write to pipe (%s)\n"), strerror(errno));
488 		return -1;
489 	}
490 
491 	return 0;
492 }
493 
_alpm_chroot_process_output(alpm_handle_t * handle,const char * line)494 static void _alpm_chroot_process_output(alpm_handle_t *handle, const char *line)
495 {
496 	alpm_event_scriptlet_info_t event = {
497 		.type = ALPM_EVENT_SCRIPTLET_INFO,
498 		.line = line
499 	};
500 	alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line);
501 	EVENT(handle, &event);
502 }
503 
_alpm_chroot_read_from_child(alpm_handle_t * handle,int fd,char * buf,ssize_t * buf_size,ssize_t buf_limit)504 static int _alpm_chroot_read_from_child(alpm_handle_t *handle, int fd,
505 		char *buf, ssize_t *buf_size, ssize_t buf_limit)
506 {
507 	ssize_t space = buf_limit - *buf_size - 2; /* reserve 2 for "\n\0" */
508 	ssize_t nread = read(fd, buf + *buf_size, space);
509 	if(nread > 0) {
510 		char *newline = memchr(buf + *buf_size, '\n', nread);
511 		*buf_size += nread;
512 		if(newline) {
513 			while(newline) {
514 				size_t linelen = newline - buf + 1;
515 				char old = buf[linelen];
516 				buf[linelen] = '\0';
517 				_alpm_chroot_process_output(handle, buf);
518 				buf[linelen] = old;
519 
520 				*buf_size -= linelen;
521 				memmove(buf, buf + linelen, *buf_size);
522 				newline = memchr(buf, '\n', *buf_size);
523 			}
524 		} else if(nread == space) {
525 			/* we didn't read a full line, but we're out of space */
526 			strcpy(buf + *buf_size, "\n");
527 			_alpm_chroot_process_output(handle, buf);
528 			*buf_size = 0;
529 		}
530 	} else if(nread == 0) {
531 		/* end-of-file */
532 		if(*buf_size) {
533 			strcpy(buf + *buf_size, "\n");
534 			_alpm_chroot_process_output(handle, buf);
535 		}
536 		return -1;
537 	} else if(should_retry(errno)) {
538 		/* nothing read, try again */
539 	} else {
540 		/* read error */
541 		if(*buf_size) {
542 			strcpy(buf + *buf_size, "\n");
543 			_alpm_chroot_process_output(handle, buf);
544 		}
545 		_alpm_log(handle, ALPM_LOG_ERROR,
546 				_("unable to read from pipe (%s)\n"), strerror(errno));
547 		return -1;
548 	}
549 	return 0;
550 }
551 
_alpm_reset_signals(void)552 static void _alpm_reset_signals(void)
553 {
554 	/* reset POSIX defined signals (see signal.h) */
555 	/* there are likely more but there is no easy way
556 	 * to get the full list of valid signals */
557 	int *i, signals[] = {
558 		SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL,
559 		SIGINT, SIGKILL, SIGPIPE, SIGQUIT, SIGSEGV, SIGSTOP, SIGTERM, SIGTSTP,
560 		SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2, SIGIO, SIGPROF, SIGSYS, SIGTRAP,
561 		SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ,
562 		0
563 	};
564 	struct sigaction def;
565 	def.sa_handler = SIG_DFL;
566 	for(i = signals; *i; i++) {
567 		sigaction(*i, &def, NULL);
568 	}
569 }
570 
571 /** Execute a command with arguments in a chroot.
572  * @param handle the context handle
573  * @param cmd command to execute
574  * @param argv arguments to pass to cmd
575  * @param stdin_cb callback to provide input to the chroot on stdin
576  * @param stdin_ctx context to be passed to @a stdin_cb
577  * @return 0 on success, 1 on error
578  */
_alpm_run_chroot(alpm_handle_t * handle,const char * cmd,char * const argv[],_alpm_cb_io stdin_cb,void * stdin_ctx)579 int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[],
580 		_alpm_cb_io stdin_cb, void *stdin_ctx)
581 {
582 	pid_t pid;
583 	int child2parent_pipefd[2], parent2child_pipefd[2];
584 	int cwdfd;
585 	int retval = 0;
586 
587 #define HEAD 1
588 #define TAIL 0
589 
590 	/* save the cwd so we can restore it later */
591 	OPEN(cwdfd, ".", O_RDONLY | O_CLOEXEC);
592 	if(cwdfd < 0) {
593 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
594 	}
595 
596 	/* just in case our cwd was removed in the upgrade operation */
597 	if(chdir(handle->root) != 0) {
598 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
599 				handle->root, strerror(errno));
600 		goto cleanup;
601 	}
602 
603 	_alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n",
604 			cmd, handle->root);
605 
606 	/* Flush open fds before fork() to avoid cloning buffers */
607 	fflush(NULL);
608 
609 	if(socketpair(AF_UNIX, SOCK_STREAM, 0, child2parent_pipefd) == -1) {
610 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
611 		retval = 1;
612 		goto cleanup;
613 	}
614 
615 	if(socketpair(AF_UNIX, SOCK_STREAM, 0, parent2child_pipefd) == -1) {
616 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
617 		retval = 1;
618 		goto cleanup;
619 	}
620 
621 	/* fork- parent and child each have separate code blocks below */
622 	pid = fork();
623 	if(pid == -1) {
624 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno));
625 		retval = 1;
626 		goto cleanup;
627 	}
628 
629 	if(pid == 0) {
630 		/* this code runs for the child only (the actual chroot/exec) */
631 		close(0);
632 		close(1);
633 		close(2);
634 		while(dup2(child2parent_pipefd[HEAD], 1) == -1 && errno == EINTR);
635 		while(dup2(child2parent_pipefd[HEAD], 2) == -1 && errno == EINTR);
636 		while(dup2(parent2child_pipefd[TAIL], 0) == -1 && errno == EINTR);
637 		close(parent2child_pipefd[TAIL]);
638 		close(parent2child_pipefd[HEAD]);
639 		close(child2parent_pipefd[TAIL]);
640 		close(child2parent_pipefd[HEAD]);
641 		if(cwdfd >= 0) {
642 			close(cwdfd);
643 		}
644 
645 		/* use fprintf instead of _alpm_log to send output through the parent */
646 		if(chroot(handle->root) != 0) {
647 			fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
648 			exit(1);
649 		}
650 		if(chdir("/") != 0) {
651 			fprintf(stderr, _("could not change directory to %s (%s)\n"),
652 					"/", strerror(errno));
653 			exit(1);
654 		}
655 		umask(0022);
656 		_alpm_reset_signals();
657 		execv(cmd, argv);
658 		/* execv only returns if there was an error */
659 		fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
660 		exit(1);
661 	} else {
662 		/* this code runs for the parent only (wait on the child) */
663 		int status;
664 		char obuf[PIPE_BUF]; /* writes <= PIPE_BUF are guaranteed atomic */
665 		char ibuf[LINE_MAX];
666 		ssize_t olen = 0, ilen = 0;
667 		nfds_t nfds = 2;
668 		struct pollfd fds[2], *child2parent = &(fds[0]), *parent2child = &(fds[1]);
669 		int poll_ret;
670 
671 		child2parent->fd = child2parent_pipefd[TAIL];
672 		child2parent->events = POLLIN;
673 		fcntl(child2parent->fd, F_SETFL, O_NONBLOCK);
674 		close(child2parent_pipefd[HEAD]);
675 		close(parent2child_pipefd[TAIL]);
676 
677 		if(stdin_cb) {
678 			parent2child->fd = parent2child_pipefd[HEAD];
679 			parent2child->events = POLLOUT;
680 			fcntl(parent2child->fd, F_SETFL, O_NONBLOCK);
681 		} else {
682 			parent2child->fd = -1;
683 			parent2child->events = 0;
684 			close(parent2child_pipefd[HEAD]);
685 		}
686 
687 #define STOP_POLLING(p) do { close(p->fd); p->fd = -1; } while(0)
688 
689 		while((child2parent->fd != -1 || parent2child->fd != -1)
690 				&& (poll_ret = poll(fds, nfds, -1)) != 0) {
691 			if(poll_ret == -1) {
692 				if(errno == EINTR) {
693 					continue;
694 				} else {
695 					break;
696 				}
697 			}
698 			if(child2parent->revents & POLLIN) {
699 				if(_alpm_chroot_read_from_child(handle, child2parent->fd,
700 							ibuf, &ilen, sizeof(ibuf)) != 0) {
701 					/* we encountered end-of-file or an error */
702 					STOP_POLLING(child2parent);
703 				}
704 			} else if(child2parent->revents) {
705 				/* anything but POLLIN indicates an error */
706 				STOP_POLLING(child2parent);
707 			}
708 			if(parent2child->revents & POLLOUT) {
709 				if(_alpm_chroot_write_to_child(handle, parent2child->fd, obuf, &olen,
710 							sizeof(obuf), stdin_cb, stdin_ctx) != 0) {
711 					STOP_POLLING(parent2child);
712 				}
713 			} else if(parent2child->revents) {
714 				/* anything but POLLOUT indicates an error */
715 				STOP_POLLING(parent2child);
716 			}
717 		}
718 		/* process anything left in the input buffer */
719 		if(ilen) {
720 			/* buffer would have already been flushed if it had a newline */
721 			strcpy(ibuf + ilen, "\n");
722 			_alpm_chroot_process_output(handle, ibuf);
723 		}
724 
725 #undef STOP_POLLING
726 #undef HEAD
727 #undef TAIL
728 
729 		if(parent2child->fd != -1) {
730 			close(parent2child->fd);
731 		}
732 		if(child2parent->fd != -1) {
733 			close(child2parent->fd);
734 		}
735 
736 		while(waitpid(pid, &status, 0) == -1) {
737 			if(errno != EINTR) {
738 				_alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
739 				retval = 1;
740 				goto cleanup;
741 			}
742 		}
743 
744 		/* check the return status, make sure it is 0 (success) */
745 		if(WIFEXITED(status)) {
746 			_alpm_log(handle, ALPM_LOG_DEBUG, "call to waitpid succeeded\n");
747 			if(WEXITSTATUS(status) != 0) {
748 				_alpm_log(handle, ALPM_LOG_ERROR, _("command failed to execute correctly\n"));
749 				retval = 1;
750 			}
751 		} else if(WIFSIGNALED(status) != 0) {
752 			char *signal_description = strsignal(WTERMSIG(status));
753 			/* strsignal can return NULL on some (non-Linux) platforms */
754 			if(signal_description == NULL) {
755 				signal_description = _("Unknown signal");
756 			}
757 			_alpm_log(handle, ALPM_LOG_ERROR, _("command terminated by signal %d: %s\n"),
758 						WTERMSIG(status), signal_description);
759 			retval = 1;
760 		}
761 	}
762 
763 cleanup:
764 	if(cwdfd >= 0) {
765 		if(fchdir(cwdfd) != 0) {
766 			_alpm_log(handle, ALPM_LOG_ERROR,
767 					_("could not restore working directory (%s)\n"), strerror(errno));
768 		}
769 		close(cwdfd);
770 	}
771 
772 	return retval;
773 }
774 
775 /** Run ldconfig in a chroot.
776  * @param handle the context handle
777  * @return 0 on success, 1 on error
778  */
_alpm_ldconfig(alpm_handle_t * handle)779 int _alpm_ldconfig(alpm_handle_t *handle)
780 {
781 	char line[PATH_MAX];
782 
783 	_alpm_log(handle, ALPM_LOG_DEBUG, "running ldconfig\n");
784 
785 	snprintf(line, PATH_MAX, "%setc/ld.so.conf", handle->root);
786 	if(access(line, F_OK) == 0) {
787 		snprintf(line, PATH_MAX, "%s%s", handle->root, LDCONFIG);
788 		if(access(line, X_OK) == 0) {
789 			char arg0[32];
790 			char *argv[] = { arg0, NULL };
791 			strcpy(arg0, "ldconfig");
792 			return _alpm_run_chroot(handle, LDCONFIG, argv, NULL, NULL);
793 		}
794 	}
795 
796 	return 0;
797 }
798 
799 /** Helper function for comparing strings using the alpm "compare func"
800  * signature.
801  * @param s1 first string to be compared
802  * @param s2 second string to be compared
803  * @return 0 if strings are equal, positive int if first unequal character
804  * has a greater value in s1, negative if it has a greater value in s2
805  */
_alpm_str_cmp(const void * s1,const void * s2)806 int _alpm_str_cmp(const void *s1, const void *s2)
807 {
808 	return strcmp(s1, s2);
809 }
810 
811 /** Find a filename in a registered alpm cachedir.
812  * @param handle the context handle
813  * @param filename name of file to find
814  * @return malloced path of file, NULL if not found
815  */
_alpm_filecache_find(alpm_handle_t * handle,const char * filename)816 char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename)
817 {
818 	char path[PATH_MAX];
819 	char *retpath;
820 	alpm_list_t *i;
821 	struct stat buf;
822 
823 	/* Loop through the cache dirs until we find a matching file */
824 	for(i = handle->cachedirs; i; i = i->next) {
825 		snprintf(path, PATH_MAX, "%s%s", (char *)i->data,
826 				filename);
827 		if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) {
828 			retpath = strdup(path);
829 			_alpm_log(handle, ALPM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
830 			return retpath;
831 		}
832 	}
833 	/* package wasn't found in any cachedir */
834 	return NULL;
835 }
836 
837 /** Check the alpm cachedirs for existence and find a writable one.
838  * If no valid cache directory can be found, use /tmp.
839  * @param handle the context handle
840  * @return pointer to a writable cache directory.
841  */
_alpm_filecache_setup(alpm_handle_t * handle)842 const char *_alpm_filecache_setup(alpm_handle_t *handle)
843 {
844 	struct stat buf;
845 	alpm_list_t *i;
846 	char *cachedir;
847 	const char *tmpdir;
848 
849 	/* Loop through the cache dirs until we find a usable directory */
850 	for(i = handle->cachedirs; i; i = i->next) {
851 		cachedir = i->data;
852 		if(stat(cachedir, &buf) != 0) {
853 			/* cache directory does not exist.... try creating it */
854 			_alpm_log(handle, ALPM_LOG_WARNING, _("no %s cache exists, creating...\n"),
855 					cachedir);
856 			if(_alpm_makepath(cachedir) == 0) {
857 				_alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
858 				return cachedir;
859 			}
860 		} else if(!S_ISDIR(buf.st_mode)) {
861 			_alpm_log(handle, ALPM_LOG_DEBUG,
862 					"skipping cachedir, not a directory: %s\n", cachedir);
863 		} else if(_alpm_access(handle, NULL, cachedir, W_OK) != 0) {
864 			_alpm_log(handle, ALPM_LOG_DEBUG,
865 					"skipping cachedir, not writable: %s\n", cachedir);
866 		} else if(!(buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) {
867 			_alpm_log(handle, ALPM_LOG_DEBUG,
868 					"skipping cachedir, no write bits set: %s\n", cachedir);
869 		} else {
870 			_alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
871 			return cachedir;
872 		}
873 	}
874 
875 	/* we didn't find a valid cache directory. use TMPDIR or /tmp. */
876 	if((tmpdir = getenv("TMPDIR")) && stat(tmpdir, &buf) && S_ISDIR(buf.st_mode)) {
877 		/* TMPDIR was good, we can use it */
878 	} else {
879 		tmpdir = "/tmp";
880 	}
881 	alpm_option_add_cachedir(handle, tmpdir);
882 	cachedir = handle->cachedirs->prev->data;
883 	_alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
884 	_alpm_log(handle, ALPM_LOG_WARNING,
885 			_("couldn't find or create package cache, using %s instead\n"), cachedir);
886 	return cachedir;
887 }
888 
889 #if defined  HAVE_LIBSSL || defined HAVE_LIBNETTLE
890 /** Compute the MD5 message digest of a file.
891  * @param path file path of file to compute  MD5 digest of
892  * @param output string to hold computed MD5 digest
893  * @return 0 on success, 1 on file open error, 2 on file read error
894  */
md5_file(const char * path,unsigned char output[16])895 static int md5_file(const char *path, unsigned char output[16])
896 {
897 #if HAVE_LIBSSL
898 	MD5_CTX ctx;
899 #else /* HAVE_LIBNETTLE */
900 	struct md5_ctx ctx;
901 #endif
902 	unsigned char *buf;
903 	ssize_t n;
904 	int fd;
905 
906 	MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
907 
908 	OPEN(fd, path, O_RDONLY | O_CLOEXEC);
909 	if(fd < 0) {
910 		free(buf);
911 		return 1;
912 	}
913 
914 #if HAVE_LIBSSL
915 	MD5_Init(&ctx);
916 #else /* HAVE_LIBNETTLE */
917 	md5_init(&ctx);
918 #endif
919 
920 	while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
921 		if(n < 0) {
922 			continue;
923 		}
924 #if HAVE_LIBSSL
925 		MD5_Update(&ctx, buf, n);
926 #else /* HAVE_LIBNETTLE */
927 		md5_update(&ctx, n, buf);
928 #endif
929 	}
930 
931 	close(fd);
932 	free(buf);
933 
934 	if(n < 0) {
935 		return 2;
936 	}
937 
938 #if HAVE_LIBSSL
939 	MD5_Final(output, &ctx);
940 #else /* HAVE_LIBNETTLE */
941 	md5_digest(&ctx, MD5_DIGEST_SIZE, output);
942 #endif
943 	return 0;
944 }
945 
946 /** Compute the SHA-256 message digest of a file.
947  * @param path file path of file to compute SHA256 digest of
948  * @param output string to hold computed SHA256 digest
949  * @return 0 on success, 1 on file open error, 2 on file read error
950  */
sha256_file(const char * path,unsigned char output[32])951 static int sha256_file(const char *path, unsigned char output[32])
952 {
953 #if HAVE_LIBSSL
954 	SHA256_CTX ctx;
955 #else /* HAVE_LIBNETTLE */
956 	struct sha256_ctx ctx;
957 #endif
958 	unsigned char *buf;
959 	ssize_t n;
960 	int fd;
961 
962 	MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
963 
964 	OPEN(fd, path, O_RDONLY | O_CLOEXEC);
965 	if(fd < 0) {
966 		free(buf);
967 		return 1;
968 	}
969 
970 #if HAVE_LIBSSL
971 	SHA256_Init(&ctx);
972 #else /* HAVE_LIBNETTLE */
973 	sha256_init(&ctx);
974 #endif
975 
976 	while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
977 		if(n < 0) {
978 			continue;
979 		}
980 #if HAVE_LIBSSL
981 		SHA256_Update(&ctx, buf, n);
982 #else /* HAVE_LIBNETTLE */
983 		sha256_update(&ctx, n, buf);
984 #endif
985 	}
986 
987 	close(fd);
988 	free(buf);
989 
990 	if(n < 0) {
991 		return 2;
992 	}
993 
994 #if HAVE_LIBSSL
995 	SHA256_Final(output, &ctx);
996 #else /* HAVE_LIBNETTLE */
997 	sha256_digest(&ctx, SHA256_DIGEST_SIZE, output);
998 #endif
999 	return 0;
1000 }
1001 #endif /* HAVE_LIBSSL || HAVE_LIBNETTLE */
1002 
1003 /** Create a string representing bytes in hexadecimal.
1004  * @param bytes the bytes to represent in hexadecimal
1005  * @param size number of bytes to consider
1006  * @return a NULL terminated string with the hexadecimal representation of
1007  * bytes or NULL on error. This string must be freed.
1008  */
hex_representation(unsigned char * bytes,size_t size)1009 static char *hex_representation(unsigned char *bytes, size_t size)
1010 {
1011 	static const char *hex_digits = "0123456789abcdef";
1012 	char *str;
1013 	size_t i;
1014 
1015 	MALLOC(str, 2 * size + 1, return NULL);
1016 
1017 	for(i = 0; i < size; i++) {
1018 		str[2 * i] = hex_digits[bytes[i] >> 4];
1019 		str[2 * i + 1] = hex_digits[bytes[i] & 0x0f];
1020 	}
1021 
1022 	str[2 * size] = '\0';
1023 
1024 	return str;
1025 }
1026 
1027 /** Get the md5 sum of file.
1028  * @param filename name of the file
1029  * @return the checksum on success, NULL on error
1030  * @addtogroup alpm_misc
1031  */
alpm_compute_md5sum(const char * filename)1032 char SYMEXPORT *alpm_compute_md5sum(const char *filename)
1033 {
1034 	unsigned char output[16];
1035 
1036 	ASSERT(filename != NULL, return NULL);
1037 
1038 	if(md5_file(filename, output) > 0) {
1039 		return NULL;
1040 	}
1041 
1042 	return hex_representation(output, 16);
1043 }
1044 
1045 /** Get the sha256 sum of file.
1046  * @param filename name of the file
1047  * @return the checksum on success, NULL on error
1048  * @addtogroup alpm_misc
1049  */
alpm_compute_sha256sum(const char * filename)1050 char SYMEXPORT *alpm_compute_sha256sum(const char *filename)
1051 {
1052 	unsigned char output[32];
1053 
1054 	ASSERT(filename != NULL, return NULL);
1055 
1056 	if(sha256_file(filename, output) > 0) {
1057 		return NULL;
1058 	}
1059 
1060 	return hex_representation(output, 32);
1061 }
1062 
1063 /** Calculates a file's MD5 or SHA-2 digest and compares it to an expected value.
1064  * @param filepath path of the file to check
1065  * @param expected hash value to compare against
1066  * @param type digest type to use
1067  * @return 0 if file matches the expected hash, 1 if they do not match, -1 on
1068  * error
1069  */
_alpm_test_checksum(const char * filepath,const char * expected,alpm_pkgvalidation_t type)1070 int _alpm_test_checksum(const char *filepath, const char *expected,
1071 		alpm_pkgvalidation_t type)
1072 {
1073 	char *computed;
1074 	int ret;
1075 
1076 	if(type == ALPM_PKG_VALIDATION_MD5SUM) {
1077 		computed = alpm_compute_md5sum(filepath);
1078 	} else if(type == ALPM_PKG_VALIDATION_SHA256SUM) {
1079 		computed = alpm_compute_sha256sum(filepath);
1080 	} else {
1081 		return -1;
1082 	}
1083 
1084 	if(expected == NULL || computed == NULL) {
1085 		ret = -1;
1086 	} else if(strcmp(expected, computed) != 0) {
1087 		ret = 1;
1088 	} else {
1089 		ret = 0;
1090 	}
1091 
1092 	FREE(computed);
1093 	return ret;
1094 }
1095 
1096 /* Note: does NOT handle sparse files on purpose for speed. */
1097 /** TODO.
1098  * Does not handle sparse files on purpose for speed.
1099  * @param a
1100  * @param b
1101  * @return
1102  */
_alpm_archive_fgets(struct archive * a,struct archive_read_buffer * b)1103 int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
1104 {
1105 	/* ensure we start populating our line buffer at the beginning */
1106 	b->line_offset = b->line;
1107 
1108 	while(1) {
1109 		size_t block_remaining;
1110 		char *eol;
1111 
1112 		/* have we processed this entire block? */
1113 		if(b->block + b->block_size == b->block_offset) {
1114 			int64_t offset;
1115 			if(b->ret == ARCHIVE_EOF) {
1116 				/* reached end of archive on the last read, now we are out of data */
1117 				goto cleanup;
1118 			}
1119 
1120 			/* zero-copy - this is the entire next block of data. */
1121 			b->ret = archive_read_data_block(a, (void *)&b->block,
1122 					&b->block_size, &offset);
1123 			b->block_offset = b->block;
1124 			block_remaining = b->block_size;
1125 
1126 			/* error, cleanup */
1127 			if(b->ret < ARCHIVE_OK) {
1128 				goto cleanup;
1129 			}
1130 		} else {
1131 			block_remaining = b->block + b->block_size - b->block_offset;
1132 		}
1133 
1134 		/* look through the block looking for EOL characters */
1135 		eol = memchr(b->block_offset, '\n', block_remaining);
1136 		if(!eol) {
1137 			eol = memchr(b->block_offset, '\0', block_remaining);
1138 		}
1139 
1140 		/* allocate our buffer, or ensure our existing one is big enough */
1141 		if(!b->line) {
1142 			/* set the initial buffer to the read block_size */
1143 			CALLOC(b->line, b->block_size + 1, sizeof(char), b->ret = -ENOMEM; goto cleanup);
1144 			b->line_size = b->block_size + 1;
1145 			b->line_offset = b->line;
1146 		} else {
1147 			/* note: we know eol > b->block_offset and b->line_offset > b->line,
1148 			 * so we know the result is unsigned and can fit in size_t */
1149 			size_t new = eol ? (size_t)(eol - b->block_offset) : block_remaining;
1150 			size_t needed = (size_t)((b->line_offset - b->line) + new + 1);
1151 			if(needed > b->max_line_size) {
1152 				b->ret = -ERANGE;
1153 				goto cleanup;
1154 			}
1155 			if(needed > b->line_size) {
1156 				/* need to realloc + copy data to fit total length */
1157 				char *new_line;
1158 				CALLOC(new_line, needed, sizeof(char), b->ret = -ENOMEM; goto cleanup);
1159 				memcpy(new_line, b->line, b->line_size);
1160 				b->line_size = needed;
1161 				b->line_offset = new_line + (b->line_offset - b->line);
1162 				free(b->line);
1163 				b->line = new_line;
1164 			}
1165 		}
1166 
1167 		if(eol) {
1168 			size_t len = (size_t)(eol - b->block_offset);
1169 			memcpy(b->line_offset, b->block_offset, len);
1170 			b->line_offset[len] = '\0';
1171 			b->block_offset = eol + 1;
1172 			b->real_line_size = b->line_offset + len - b->line;
1173 			/* this is the main return point; from here you can read b->line */
1174 			return ARCHIVE_OK;
1175 		} else {
1176 			/* we've looked through the whole block but no newline, copy it */
1177 			size_t len = (size_t)(b->block + b->block_size - b->block_offset);
1178 			memcpy(b->line_offset, b->block_offset, len);
1179 			b->line_offset += len;
1180 			b->block_offset = b->block + b->block_size;
1181 			/* there was no new data, return what is left; saved ARCHIVE_EOF will be
1182 			 * returned on next call */
1183 			if(len == 0) {
1184 				b->line_offset[0] = '\0';
1185 				b->real_line_size = b->line_offset - b->line;
1186 				return ARCHIVE_OK;
1187 			}
1188 		}
1189 	}
1190 
1191 cleanup:
1192 	{
1193 		int ret = b->ret;
1194 		FREE(b->line);
1195 		memset(b, 0, sizeof(struct archive_read_buffer));
1196 		return ret;
1197 	}
1198 }
1199 
1200 /** Parse a full package specifier.
1201  * @param target package specifier to parse, such as: "pacman-4.0.1-2",
1202  * "pacman-4.01-2/", or "pacman-4.0.1-2/desc"
1203  * @param name to hold package name
1204  * @param version to hold package version
1205  * @param name_hash to hold package name hash
1206  * @return 0 on success, -1 on error
1207  */
_alpm_splitname(const char * target,char ** name,char ** version,unsigned long * name_hash)1208 int _alpm_splitname(const char *target, char **name, char **version,
1209 		unsigned long *name_hash)
1210 {
1211 	/* the format of a db entry is as follows:
1212 	 *    package-version-rel/
1213 	 *    package-version-rel/desc (we ignore the filename portion)
1214 	 * package name can contain hyphens, so parse from the back- go back
1215 	 * two hyphens and we have split the version from the name.
1216 	 */
1217 	const char *pkgver, *end;
1218 
1219 	if(target == NULL) {
1220 		return -1;
1221 	}
1222 
1223 	/* remove anything trailing a '/' */
1224 	end = strchr(target, '/');
1225 	if(!end) {
1226 		end = target + strlen(target);
1227 	}
1228 
1229 	/* do the magic parsing- find the beginning of the version string
1230 	 * by doing two iterations of same loop to lop off two hyphens */
1231 	for(pkgver = end - 1; *pkgver && *pkgver != '-'; pkgver--);
1232 	for(pkgver = pkgver - 1; *pkgver && *pkgver != '-'; pkgver--);
1233 	if(*pkgver != '-' || pkgver == target) {
1234 		return -1;
1235 	}
1236 
1237 	/* copy into fields and return */
1238 	if(version) {
1239 		if(*version) {
1240 			FREE(*version);
1241 		}
1242 		/* version actually points to the dash, so need to increment 1 and account
1243 		 * for potential end character */
1244 		STRNDUP(*version, pkgver + 1, end - pkgver - 1, return -1);
1245 	}
1246 
1247 	if(name) {
1248 		if(*name) {
1249 			FREE(*name);
1250 		}
1251 		STRNDUP(*name, target, pkgver - target, return -1);
1252 		if(name_hash) {
1253 			*name_hash = _alpm_hash_sdbm(*name);
1254 		}
1255 	}
1256 
1257 	return 0;
1258 }
1259 
1260 /** Hash the given string to an unsigned long value.
1261  * This is the standard sdbm hashing algorithm.
1262  * @param str string to hash
1263  * @return the hash value of the given string
1264  */
_alpm_hash_sdbm(const char * str)1265 unsigned long _alpm_hash_sdbm(const char *str)
1266 {
1267 	unsigned long hash = 0;
1268 	int c;
1269 
1270 	if(!str) {
1271 		return hash;
1272 	}
1273 	while((c = *str++)) {
1274 		hash = c + hash * 65599;
1275 	}
1276 
1277 	return hash;
1278 }
1279 
1280 /** Convert a string to a file offset.
1281  * This parses bare positive integers only.
1282  * @param line string to convert
1283  * @return off_t on success, -1 on error
1284  */
_alpm_strtoofft(const char * line)1285 off_t _alpm_strtoofft(const char *line)
1286 {
1287 	char *end;
1288 	unsigned long long result;
1289 	errno = 0;
1290 
1291 	/* we are trying to parse bare numbers only, no leading anything */
1292 	if(!isdigit((unsigned char)line[0])) {
1293 		return (off_t)-1;
1294 	}
1295 	result = strtoull(line, &end, 10);
1296 	if(result == 0 && end == line) {
1297 		/* line was not a number */
1298 		return (off_t)-1;
1299 	} else if(result == ULLONG_MAX && errno == ERANGE) {
1300 		/* line does not fit in unsigned long long */
1301 		return (off_t)-1;
1302 	} else if(*end) {
1303 		/* line began with a number but has junk left over at the end */
1304 		return (off_t)-1;
1305 	}
1306 
1307 	return (off_t)result;
1308 }
1309 
1310 /** Parses a date into an alpm_time_t struct.
1311  * @param line date to parse
1312  * @return time struct on success, 0 on error
1313  */
_alpm_parsedate(const char * line)1314 alpm_time_t _alpm_parsedate(const char *line)
1315 {
1316 	char *end;
1317 	long long result;
1318 	errno = 0;
1319 
1320 	result = strtoll(line, &end, 10);
1321 	if(result == 0 && end == line) {
1322 		/* line was not a number */
1323 		errno = EINVAL;
1324 		return 0;
1325 	} else if(errno == ERANGE) {
1326 		/* line does not fit in long long */
1327 		return 0;
1328 	} else if(*end) {
1329 		/* line began with a number but has junk left over at the end */
1330 		errno = EINVAL;
1331 		return 0;
1332 	}
1333 
1334 	return (alpm_time_t)result;
1335 }
1336 
1337 /** Wrapper around access() which takes a dir and file argument
1338  * separately and generates an appropriate error message.
1339  * If dir is NULL file will be treated as the whole path.
1340  * @param handle an alpm handle
1341  * @param dir directory path ending with and slash
1342  * @param file filename
1343  * @param amode access mode as described in access()
1344  * @return int value returned by access()
1345  */
_alpm_access(alpm_handle_t * handle,const char * dir,const char * file,int amode)1346 int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int amode)
1347 {
1348 	size_t len = 0;
1349 	int ret = 0;
1350 
1351 	if(dir) {
1352 		char *check_path;
1353 
1354 		len = strlen(dir) + strlen(file) + 1;
1355 		CALLOC(check_path, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, -1));
1356 		snprintf(check_path, len, "%s%s", dir, file);
1357 
1358 		ret = access(check_path, amode);
1359 		free(check_path);
1360 	} else {
1361 		dir = "";
1362 		ret = access(file, amode);
1363 	}
1364 
1365 	if(ret != 0) {
1366 		if(amode & R_OK) {
1367 			_alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not readable: %s\n",
1368 					dir, file, strerror(errno));
1369 		}
1370 		if(amode & W_OK) {
1371 			_alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not writable: %s\n",
1372 					dir, file, strerror(errno));
1373 		}
1374 		if(amode & X_OK) {
1375 			_alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not executable: %s\n",
1376 					dir, file, strerror(errno));
1377 		}
1378 		if(amode == F_OK) {
1379 			_alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" does not exist: %s\n",
1380 					dir, file, strerror(errno));
1381 		}
1382 	}
1383 	return ret;
1384 }
1385 
1386 /** Checks whether a string matches at least one shell wildcard pattern.
1387  * Checks for matches with fnmatch. Matches are inverted by prepending
1388  * patterns with an exclamation mark. Preceding exclamation marks may be
1389  * escaped. Subsequent matches override previous ones.
1390  * @param patterns patterns to match against
1391  * @param string string to check against pattern
1392  * @return 0 if string matches pattern, negative if they don't match and
1393  * positive if the last match was inverted
1394  */
_alpm_fnmatch_patterns(alpm_list_t * patterns,const char * string)1395 int _alpm_fnmatch_patterns(alpm_list_t *patterns, const char *string)
1396 {
1397 	alpm_list_t *i;
1398 	char *pattern;
1399 	short inverted;
1400 
1401 	for(i = alpm_list_last(patterns); i; i = alpm_list_previous(i)) {
1402 		pattern = i->data;
1403 
1404 		inverted = pattern[0] == '!';
1405 		if(inverted || pattern[0] == '\\') {
1406 			pattern++;
1407 		}
1408 
1409 		if(_alpm_fnmatch(pattern, string) == 0) {
1410 			return inverted;
1411 		}
1412 	}
1413 
1414 	return -1;
1415 }
1416 
1417 /** Checks whether a string matches a shell wildcard pattern.
1418  * Wrapper around fnmatch.
1419  * @param pattern pattern to match against
1420  * @param string string to check against pattern
1421  * @return 0 if string matches pattern, non-zero if they don't match and on
1422  * error
1423  */
_alpm_fnmatch(const void * pattern,const void * string)1424 int _alpm_fnmatch(const void *pattern, const void *string)
1425 {
1426 	return fnmatch(pattern, string, 0);
1427 }
1428 
1429 /** Think of this as realloc with error handling. If realloc fails NULL will be
1430  * returned and data will not be changed.
1431  *
1432  * Newly created memory will be zeroed.
1433  *
1434  * @param data source memory space
1435  * @param current size of the space pointed to by data
1436  * @param required size you want
1437  * @return new memory; NULL on error
1438  */
_alpm_realloc(void ** data,size_t * current,const size_t required)1439 void *_alpm_realloc(void **data, size_t *current, const size_t required)
1440 {
1441 	char *newdata;
1442 
1443 	newdata = realloc(*data, required);
1444 	if(!newdata) {
1445 		_alpm_alloc_fail(required);
1446 		return NULL;
1447 	}
1448 
1449 	if (*current < required) {
1450 		/* ensure all new memory is zeroed out, in both the initial
1451 		 * allocation and later reallocs */
1452 		memset(newdata + *current, 0, required - *current);
1453 	}
1454 	*current = required;
1455 	*data = newdata;
1456 	return newdata;
1457 }
1458 
1459 /** This automatically grows data based on current/required.
1460  *
1461  * The memory space will be initialised to required bytes and doubled in size when required.
1462  *
1463  * Newly created memory will be zeroed.
1464  * @param data source memory space
1465  * @param current size of the space pointed to by data
1466  * @param required size you want
1467  * @return new memory if grown; old memory otherwise; NULL on error
1468  */
_alpm_greedy_grow(void ** data,size_t * current,const size_t required)1469 void *_alpm_greedy_grow(void **data, size_t *current, const size_t required)
1470 {
1471 	size_t newsize = 0;
1472 
1473 	if(*current >= required) {
1474 		return data;
1475 	}
1476 
1477 	if(*current == 0) {
1478 		newsize = required;
1479 	} else {
1480 		newsize = *current * 2;
1481 	}
1482 
1483 	/* check for overflows */
1484 	if (newsize < required) {
1485 		return NULL;
1486 	}
1487 
1488 	return _alpm_realloc(data, current, newsize);
1489 }
1490 
_alpm_alloc_fail(size_t size)1491 void _alpm_alloc_fail(size_t size)
1492 {
1493 	fprintf(stderr, "alloc failure: could not allocate %zu bytes\n", size);
1494 }
1495