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