libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * util.c 00003 * 00004 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> 00005 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> 00006 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> 00007 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> 00008 * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org> 00009 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org> 00010 * 00011 * This program is free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00023 */ 00024 00025 #include <stdlib.h> 00026 #include <unistd.h> 00027 #include <ctype.h> 00028 #include <dirent.h> 00029 #include <time.h> 00030 #include <syslog.h> 00031 #include <errno.h> 00032 #include <limits.h> 00033 #include <sys/wait.h> 00034 #include <locale.h> /* setlocale */ 00035 #include <fnmatch.h> 00036 00037 /* libarchive */ 00038 #include <archive.h> 00039 #include <archive_entry.h> 00040 00041 #ifdef HAVE_LIBSSL 00042 #include <openssl/md5.h> 00043 #include <openssl/sha.h> 00044 #else 00045 #include "md5.h" 00046 #include "sha2.h" 00047 #endif 00048 00049 /* libalpm */ 00050 #include "util.h" 00051 #include "log.h" 00052 #include "alpm.h" 00053 #include "alpm_list.h" 00054 #include "handle.h" 00055 #include "trans.h" 00056 00057 #ifndef HAVE_STRSEP 00058 /** Extracts tokens from a string. 00059 * Replaces strset which is not portable (missing on Solaris). 00060 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com> 00061 * Modifies str to point to the first character after the token if one is 00062 * found, or NULL if one is not. 00063 * @param str string containing delimited tokens to parse 00064 * @param delim character delimiting tokens in str 00065 * @return pointer to the first token in str if str is not NULL, NULL if 00066 * str is NULL 00067 */ 00068 char* strsep(char** str, const char* delims) 00069 { 00070 char* token; 00071 00072 if(*str==NULL) { 00073 /* No more tokens */ 00074 return NULL; 00075 } 00076 00077 token=*str; 00078 while(**str!='\0') { 00079 if(strchr(delims,**str)!=NULL) { 00080 **str='\0'; 00081 (*str)++; 00082 return token; 00083 } 00084 (*str)++; 00085 } 00086 /* There is no other token */ 00087 *str=NULL; 00088 return token; 00089 } 00090 #endif 00091 00092 int _alpm_makepath(const char *path) 00093 { 00094 return _alpm_makepath_mode(path, 0755); 00095 } 00096 00097 /** Creates a directory, including parents if needed, similar to 'mkdir -p'. 00098 * @param path directory path to create 00099 * @param mode permission mode for created directories 00100 * @return 0 on success, 1 on error 00101 */ 00102 int _alpm_makepath_mode(const char *path, mode_t mode) 00103 { 00104 /* A bit of pointer hell here. Descriptions: 00105 * orig - a copy of path so we can safely butcher it with strsep 00106 * str - the current position in the path string (after the delimiter) 00107 * ptr - the original position of str after calling strsep 00108 * incr - incrementally generated path for use in stat/mkdir call 00109 */ 00110 char *orig, *str, *ptr, *incr; 00111 mode_t oldmask = umask(0000); 00112 int ret = 0; 00113 00114 orig = strdup(path); 00115 incr = calloc(strlen(orig) + 1, sizeof(char)); 00116 str = orig; 00117 while((ptr = strsep(&str, "/"))) { 00118 if(strlen(ptr)) { 00119 /* we have another path component- append the newest component to 00120 * existing string and create one more level of dir structure */ 00121 strcat(incr, "/"); 00122 strcat(incr, ptr); 00123 if(access(incr, F_OK)) { 00124 if(mkdir(incr, mode)) { 00125 ret = 1; 00126 break; 00127 } 00128 } 00129 } 00130 } 00131 free(orig); 00132 free(incr); 00133 umask(oldmask); 00134 return ret; 00135 } 00136 00137 /** Copies a file. 00138 * @param src file path to copy from 00139 * @param dest file path to copy to 00140 * @return 0 on success, 1 on error 00141 */ 00142 int _alpm_copyfile(const char *src, const char *dest) 00143 { 00144 char *buf; 00145 int in, out, ret = 1; 00146 ssize_t nread; 00147 struct stat st; 00148 00149 MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1); 00150 00151 OPEN(in, src, O_RDONLY); 00152 do { 00153 out = open(dest, O_WRONLY | O_CREAT, 0000); 00154 } while(out == -1 && errno == EINTR); 00155 if(in < 0 || out < 0) { 00156 goto cleanup; 00157 } 00158 00159 if(fstat(in, &st) || fchmod(out, st.st_mode)) { 00160 goto cleanup; 00161 } 00162 00163 /* do the actual file copy */ 00164 while((nread = read(in, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) { 00165 ssize_t nwrite = 0; 00166 if(nread < 0) { 00167 continue; 00168 } 00169 do { 00170 nwrite = write(out, buf + nwrite, nread); 00171 if(nwrite >= 0) { 00172 nread -= nwrite; 00173 } else if(errno != EINTR) { 00174 goto cleanup; 00175 } 00176 } while(nread > 0); 00177 } 00178 ret = 0; 00179 00180 cleanup: 00181 free(buf); 00182 if(in >= 0) { 00183 CLOSE(in); 00184 } 00185 if(out >= 0) { 00186 CLOSE(out); 00187 } 00188 return ret; 00189 } 00190 00191 /** Trim trailing newlines from a string (if any exist). 00192 * @param str a single line of text 00193 * @return the length of the trimmed string 00194 */ 00195 size_t _alpm_strip_newline(char *str) 00196 { 00197 size_t len; 00198 if(*str == '\0') { 00199 return 0; 00200 } 00201 len = strlen(str); 00202 while(len > 0 && str[len - 1] == '\n') { 00203 len--; 00204 } 00205 str[len] = '\0'; 00206 00207 return len; 00208 } 00209 00210 /* Compression functions */ 00211 00212 /** Open an archive for reading and perform the necessary boilerplate. 00213 * This takes care of creating the libarchive 'archive' struct, setting up 00214 * compression and format options, opening a file descriptor, setting up the 00215 * buffer size, and performing a stat on the path once opened. 00216 * On error, no file descriptor is opened, and the archive pointer returned 00217 * will be set to NULL. 00218 * @param handle the context handle 00219 * @param path the path of the archive to open 00220 * @param buf space for a stat buffer for the given path 00221 * @param archive pointer to place the created archive object 00222 * @param error error code to set on failure to open archive 00223 * @return -1 on failure, >=0 file descriptor on success 00224 */ 00225 int _alpm_open_archive(alpm_handle_t *handle, const char *path, 00226 struct stat *buf, struct archive **archive, alpm_errno_t error) 00227 { 00228 int fd; 00229 size_t bufsize = ALPM_BUFFER_SIZE; 00230 00231 if((*archive = archive_read_new()) == NULL) { 00232 RET_ERR(handle, ALPM_ERR_LIBARCHIVE, -1); 00233 } 00234 00235 archive_read_support_compression_all(*archive); 00236 archive_read_support_format_all(*archive); 00237 00238 _alpm_log(handle, ALPM_LOG_DEBUG, "opening archive %s\n", path); 00239 OPEN(fd, path, O_RDONLY); 00240 if(fd < 0) { 00241 _alpm_log(handle, ALPM_LOG_ERROR, 00242 _("could not open file %s: %s\n"), path, strerror(errno)); 00243 goto error; 00244 } 00245 00246 if(fstat(fd, buf) != 0) { 00247 _alpm_log(handle, ALPM_LOG_ERROR, 00248 _("could not stat file %s: %s\n"), path, strerror(errno)); 00249 goto error; 00250 } 00251 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 00252 if(buf->st_blksize > ALPM_BUFFER_SIZE) { 00253 bufsize = buf->st_blksize; 00254 } 00255 #endif 00256 00257 if(archive_read_open_fd(*archive, fd, bufsize) != ARCHIVE_OK) { 00258 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), 00259 path, archive_error_string(*archive)); 00260 goto error; 00261 } 00262 00263 return fd; 00264 00265 error: 00266 archive_read_finish(*archive); 00267 *archive = NULL; 00268 if(fd >= 0) { 00269 CLOSE(fd); 00270 } 00271 RET_ERR(handle, error, -1); 00272 } 00273 00274 /** Unpack a specific file in an archive. 00275 * @param handle the context handle 00276 * @param archive the archive to unpack 00277 * @param prefix where to extract the files 00278 * @param filename a file within the archive to unpack 00279 * @return 0 on success, 1 on failure 00280 */ 00281 int _alpm_unpack_single(alpm_handle_t *handle, const char *archive, 00282 const char *prefix, const char *filename) 00283 { 00284 alpm_list_t *list = NULL; 00285 int ret = 0; 00286 if(filename == NULL) { 00287 return 1; 00288 } 00289 list = alpm_list_add(list, (void *)filename); 00290 ret = _alpm_unpack(handle, archive, prefix, list, 1); 00291 alpm_list_free(list); 00292 return ret; 00293 } 00294 00295 /** Unpack a list of files in an archive. 00296 * @param handle the context handle 00297 * @param path the archive to unpack 00298 * @param prefix where to extract the files 00299 * @param list a list of files within the archive to unpack or NULL for all 00300 * @param breakfirst break after the first entry found 00301 * @return 0 on success, 1 on failure 00302 */ 00303 int _alpm_unpack(alpm_handle_t *handle, const char *path, const char *prefix, 00304 alpm_list_t *list, int breakfirst) 00305 { 00306 int ret = 0; 00307 mode_t oldmask; 00308 struct archive *archive; 00309 struct archive_entry *entry; 00310 struct stat buf; 00311 int fd, cwdfd; 00312 00313 fd = _alpm_open_archive(handle, path, &buf, &archive, ALPM_ERR_PKG_OPEN); 00314 if(fd < 0) { 00315 return 1; 00316 } 00317 00318 oldmask = umask(0022); 00319 00320 /* save the cwd so we can restore it later */ 00321 OPEN(cwdfd, ".", O_RDONLY); 00322 if(cwdfd < 0) { 00323 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); 00324 } 00325 00326 /* just in case our cwd was removed in the upgrade operation */ 00327 if(chdir(prefix) != 0) { 00328 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), 00329 prefix, strerror(errno)); 00330 ret = 1; 00331 goto cleanup; 00332 } 00333 00334 while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { 00335 const char *entryname; 00336 mode_t mode; 00337 00338 entryname = archive_entry_pathname(entry); 00339 00340 /* If specific files were requested, skip entries that don't match. */ 00341 if(list) { 00342 char *entry_prefix = strdup(entryname); 00343 char *p = strstr(entry_prefix,"/"); 00344 if(p) { 00345 *(p+1) = '\0'; 00346 } 00347 char *found = alpm_list_find_str(list, entry_prefix); 00348 free(entry_prefix); 00349 if(!found) { 00350 if(archive_read_data_skip(archive) != ARCHIVE_OK) { 00351 ret = 1; 00352 goto cleanup; 00353 } 00354 continue; 00355 } else { 00356 _alpm_log(handle, ALPM_LOG_DEBUG, "extracting: %s\n", entryname); 00357 } 00358 } 00359 00360 mode = archive_entry_mode(entry); 00361 if(S_ISREG(mode)) { 00362 archive_entry_set_perm(entry, 0644); 00363 } else if(S_ISDIR(mode)) { 00364 archive_entry_set_perm(entry, 0755); 00365 } 00366 00367 /* Extract the archive entry. */ 00368 int readret = archive_read_extract(archive, entry, 0); 00369 if(readret == ARCHIVE_WARN) { 00370 /* operation succeeded but a non-critical error was encountered */ 00371 _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"), 00372 entryname, archive_error_string(archive)); 00373 } else if(readret != ARCHIVE_OK) { 00374 _alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"), 00375 entryname, archive_error_string(archive)); 00376 ret = 1; 00377 goto cleanup; 00378 } 00379 00380 if(breakfirst) { 00381 break; 00382 } 00383 } 00384 00385 cleanup: 00386 umask(oldmask); 00387 archive_read_finish(archive); 00388 CLOSE(fd); 00389 if(cwdfd >= 0) { 00390 if(fchdir(cwdfd) != 0) { 00391 _alpm_log(handle, ALPM_LOG_ERROR, 00392 _("could not restore working directory (%s)\n"), strerror(errno)); 00393 } 00394 CLOSE(cwdfd); 00395 } 00396 00397 return ret; 00398 } 00399 00400 /** Recursively removes a path similar to 'rm -rf'. 00401 * @param path path to remove 00402 * @return 0 on success, number of paths that could not be removed on error 00403 */ 00404 int _alpm_rmrf(const char *path) 00405 { 00406 int errflag = 0; 00407 struct dirent *dp; 00408 DIR *dirp; 00409 char name[PATH_MAX]; 00410 struct stat st; 00411 00412 if(_alpm_lstat(path, &st) == 0) { 00413 if(!S_ISDIR(st.st_mode)) { 00414 if(!unlink(path)) { 00415 return 0; 00416 } else { 00417 if(errno == ENOENT) { 00418 return 0; 00419 } else { 00420 return 1; 00421 } 00422 } 00423 } else { 00424 dirp = opendir(path); 00425 if(!dirp) { 00426 return 1; 00427 } 00428 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 00429 if(dp->d_ino) { 00430 sprintf(name, "%s/%s", path, dp->d_name); 00431 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) { 00432 errflag += _alpm_rmrf(name); 00433 } 00434 } 00435 } 00436 closedir(dirp); 00437 if(rmdir(path)) { 00438 errflag++; 00439 } 00440 } 00441 return errflag; 00442 } 00443 return 0; 00444 } 00445 00446 /** Determine if there are files in a directory. 00447 * @param handle the context handle 00448 * @param path the full absolute directory path 00449 * @param full_count whether to return an exact count of files 00450 * @return a file count if full_count is != 0, else >0 if directory has 00451 * contents, 0 if no contents, and -1 on error 00452 */ 00453 ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, 00454 int full_count) 00455 { 00456 ssize_t files = 0; 00457 struct dirent *ent; 00458 DIR *dir = opendir(path); 00459 00460 if(!dir) { 00461 if(errno == ENOTDIR) { 00462 _alpm_log(handle, ALPM_LOG_DEBUG, "%s was not a directory\n", path); 00463 } else { 00464 _alpm_log(handle, ALPM_LOG_DEBUG, "could not read directory %s\n", 00465 path); 00466 } 00467 return -1; 00468 } 00469 while((ent = readdir(dir)) != NULL) { 00470 const char *name = ent->d_name; 00471 00472 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { 00473 continue; 00474 } 00475 00476 files++; 00477 00478 if(!full_count) { 00479 break; 00480 } 00481 } 00482 00483 closedir(dir); 00484 return files; 00485 } 00486 00487 /** Write formatted message to log. 00488 * @param handle the context handle 00489 * @param format formatted string to write out 00490 * @param args formatting arguments 00491 * @return 0 or number of characters written on success, vfprintf return value 00492 * on error 00493 */ 00494 int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args) 00495 { 00496 int ret = 0; 00497 00498 if(handle->usesyslog) { 00499 /* we can't use a va_list more than once, so we need to copy it 00500 * so we can use the original when calling vfprintf below. */ 00501 va_list args_syslog; 00502 va_copy(args_syslog, args); 00503 vsyslog(LOG_WARNING, fmt, args_syslog); 00504 va_end(args_syslog); 00505 } 00506 00507 if(handle->logstream) { 00508 time_t t; 00509 struct tm *tm; 00510 00511 t = time(NULL); 00512 tm = localtime(&t); 00513 00514 /* Use ISO-8601 date format */ 00515 fprintf(handle->logstream, "[%04d-%02d-%02d %02d:%02d] ", 00516 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, 00517 tm->tm_hour, tm->tm_min); 00518 ret = vfprintf(handle->logstream, fmt, args); 00519 fflush(handle->logstream); 00520 } 00521 00522 return ret; 00523 } 00524 00525 /** Execute a command with arguments in a chroot. 00526 * @param handle the context handle 00527 * @param cmd command to execute 00528 * @param argv arguments to pass to cmd 00529 * @return 0 on success, 1 on error 00530 */ 00531 int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) 00532 { 00533 pid_t pid; 00534 int pipefd[2], cwdfd; 00535 int retval = 0; 00536 00537 /* save the cwd so we can restore it later */ 00538 OPEN(cwdfd, ".", O_RDONLY); 00539 if(cwdfd < 0) { 00540 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); 00541 } 00542 00543 /* just in case our cwd was removed in the upgrade operation */ 00544 if(chdir(handle->root) != 0) { 00545 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), 00546 handle->root, strerror(errno)); 00547 goto cleanup; 00548 } 00549 00550 _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", 00551 cmd, handle->root); 00552 00553 /* Flush open fds before fork() to avoid cloning buffers */ 00554 fflush(NULL); 00555 00556 if(pipe(pipefd) == -1) { 00557 _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); 00558 retval = 1; 00559 goto cleanup; 00560 } 00561 00562 /* fork- parent and child each have seperate code blocks below */ 00563 pid = fork(); 00564 if(pid == -1) { 00565 _alpm_log(handle, ALPM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno)); 00566 retval = 1; 00567 goto cleanup; 00568 } 00569 00570 if(pid == 0) { 00571 /* this code runs for the child only (the actual chroot/exec) */ 00572 CLOSE(1); 00573 CLOSE(2); 00574 while(dup2(pipefd[1], 1) == -1 && errno == EINTR); 00575 while(dup2(pipefd[1], 2) == -1 && errno == EINTR); 00576 CLOSE(pipefd[0]); 00577 CLOSE(pipefd[1]); 00578 00579 /* use fprintf instead of _alpm_log to send output through the parent */ 00580 if(chroot(handle->root) != 0) { 00581 fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno)); 00582 exit(1); 00583 } 00584 if(chdir("/") != 0) { 00585 fprintf(stderr, _("could not change directory to %s (%s)\n"), 00586 "/", strerror(errno)); 00587 exit(1); 00588 } 00589 umask(0022); 00590 execv(cmd, argv); 00591 /* execv only returns if there was an error */ 00592 fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno)); 00593 exit(1); 00594 } else { 00595 /* this code runs for the parent only (wait on the child) */ 00596 int status; 00597 FILE *pipe_file; 00598 00599 CLOSE(pipefd[1]); 00600 pipe_file = fdopen(pipefd[0], "r"); 00601 if(pipe_file == NULL) { 00602 CLOSE(pipefd[0]); 00603 retval = 1; 00604 } else { 00605 while(!feof(pipe_file)) { 00606 char line[PATH_MAX]; 00607 if(fgets(line, PATH_MAX, pipe_file) == NULL) 00608 break; 00609 alpm_logaction(handle, "%s", line); 00610 EVENT(handle, ALPM_EVENT_SCRIPTLET_INFO, line, NULL); 00611 } 00612 fclose(pipe_file); 00613 } 00614 00615 while(waitpid(pid, &status, 0) == -1) { 00616 if(errno != EINTR) { 00617 _alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno)); 00618 retval = 1; 00619 goto cleanup; 00620 } 00621 } 00622 00623 /* report error from above after the child has exited */ 00624 if(retval != 0) { 00625 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno)); 00626 goto cleanup; 00627 } 00628 /* check the return status, make sure it is 0 (success) */ 00629 if(WIFEXITED(status)) { 00630 _alpm_log(handle, ALPM_LOG_DEBUG, "call to waitpid succeeded\n"); 00631 if(WEXITSTATUS(status) != 0) { 00632 _alpm_log(handle, ALPM_LOG_ERROR, _("command failed to execute correctly\n")); 00633 retval = 1; 00634 } 00635 } 00636 } 00637 00638 cleanup: 00639 if(cwdfd >= 0) { 00640 if(fchdir(cwdfd) != 0) { 00641 _alpm_log(handle, ALPM_LOG_ERROR, 00642 _("could not restore working directory (%s)\n"), strerror(errno)); 00643 } 00644 CLOSE(cwdfd); 00645 } 00646 00647 return retval; 00648 } 00649 00650 /** Run ldconfig in a chroot. 00651 * @param handle the context handle 00652 * @return 0 on success, 1 on error 00653 */ 00654 int _alpm_ldconfig(alpm_handle_t *handle) 00655 { 00656 char line[PATH_MAX]; 00657 00658 _alpm_log(handle, ALPM_LOG_DEBUG, "running ldconfig\n"); 00659 00660 snprintf(line, PATH_MAX, "%setc/ld.so.conf", handle->root); 00661 if(access(line, F_OK) == 0) { 00662 snprintf(line, PATH_MAX, "%ssbin/ldconfig", handle->root); 00663 if(access(line, X_OK) == 0) { 00664 char *argv[] = { "ldconfig", NULL }; 00665 return _alpm_run_chroot(handle, "/sbin/ldconfig", argv); 00666 } 00667 } 00668 00669 return 0; 00670 } 00671 00672 /** Helper function for comparing strings using the alpm "compare func" 00673 * signature. 00674 * @param s1 first string to be compared 00675 * @param s2 second string to be compared 00676 * @return 0 if strings are equal, positive int if first unequal character 00677 * has a greater value in s1, negative if it has a greater value in s2 00678 */ 00679 int _alpm_str_cmp(const void *s1, const void *s2) 00680 { 00681 return strcmp(s1, s2); 00682 } 00683 00684 /** Find a filename in a registered alpm cachedir. 00685 * @param handle the context handle 00686 * @param filename name of file to find 00687 * @return malloced path of file, NULL if not found 00688 */ 00689 char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename) 00690 { 00691 char path[PATH_MAX]; 00692 char *retpath; 00693 alpm_list_t *i; 00694 struct stat buf; 00695 00696 /* Loop through the cache dirs until we find a matching file */ 00697 for(i = handle->cachedirs; i; i = i->next) { 00698 snprintf(path, PATH_MAX, "%s%s", (char *)i->data, 00699 filename); 00700 if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) { 00701 retpath = strdup(path); 00702 _alpm_log(handle, ALPM_LOG_DEBUG, "found cached pkg: %s\n", retpath); 00703 return retpath; 00704 } 00705 } 00706 /* package wasn't found in any cachedir */ 00707 return NULL; 00708 } 00709 00710 /** Check the alpm cachedirs for existance and find a writable one. 00711 * If no valid cache directory can be found, use /tmp. 00712 * @param handle the context handle 00713 * @return pointer to a writable cache directory. 00714 */ 00715 const char *_alpm_filecache_setup(alpm_handle_t *handle) 00716 { 00717 struct stat buf; 00718 alpm_list_t *i; 00719 char *cachedir, *tmpdir; 00720 00721 /* Loop through the cache dirs until we find a usable directory */ 00722 for(i = handle->cachedirs; i; i = i->next) { 00723 cachedir = i->data; 00724 if(stat(cachedir, &buf) != 0) { 00725 /* cache directory does not exist.... try creating it */ 00726 _alpm_log(handle, ALPM_LOG_WARNING, _("no %s cache exists, creating...\n"), 00727 cachedir); 00728 if(_alpm_makepath(cachedir) == 0) { 00729 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir); 00730 return cachedir; 00731 } 00732 } else if(!S_ISDIR(buf.st_mode)) { 00733 _alpm_log(handle, ALPM_LOG_DEBUG, 00734 "skipping cachedir, not a directory: %s\n", cachedir); 00735 } else if(access(cachedir, W_OK) != 0) { 00736 _alpm_log(handle, ALPM_LOG_DEBUG, 00737 "skipping cachedir, not writable: %s\n", cachedir); 00738 } else if(!(buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) { 00739 _alpm_log(handle, ALPM_LOG_DEBUG, 00740 "skipping cachedir, no write bits set: %s\n", cachedir); 00741 } else { 00742 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir); 00743 return cachedir; 00744 } 00745 } 00746 00747 /* we didn't find a valid cache directory. use TMPDIR or /tmp. */ 00748 if((tmpdir = getenv("TMPDIR")) && stat(tmpdir, &buf) && S_ISDIR(buf.st_mode)) { 00749 /* TMPDIR was good, we can use it */ 00750 } else { 00751 tmpdir = "/tmp"; 00752 } 00753 alpm_option_add_cachedir(handle, tmpdir); 00754 cachedir = handle->cachedirs->prev->data; 00755 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir); 00756 _alpm_log(handle, ALPM_LOG_WARNING, 00757 _("couldn't find or create package cache, using %s instead\n"), cachedir); 00758 return cachedir; 00759 } 00760 00761 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink. 00762 * Linux lstat follows POSIX semantics and still performs a dereference on 00763 * the first, and for uses of lstat in libalpm this is not what we want. 00764 * @param path path to file to lstat 00765 * @param buf structure to fill with stat information 00766 * @return the return code from lstat 00767 */ 00768 int _alpm_lstat(const char *path, struct stat *buf) 00769 { 00770 int ret; 00771 size_t len = strlen(path); 00772 00773 /* strip the trailing slash if one exists */ 00774 if(len != 0 && path[len - 1] == '/') { 00775 char *newpath = strdup(path); 00776 newpath[len - 1] = '\0'; 00777 ret = lstat(newpath, buf); 00778 free(newpath); 00779 } else { 00780 ret = lstat(path, buf); 00781 } 00782 00783 return ret; 00784 } 00785 00786 #ifdef HAVE_LIBSSL 00787 /** Compute the MD5 message digest of a file. 00788 * @param path file path of file to compute MD5 digest of 00789 * @param output string to hold computed MD5 digest 00790 * @return 0 on success, 1 on file open error, 2 on file read error 00791 */ 00792 static int md5_file(const char *path, unsigned char output[16]) 00793 { 00794 MD5_CTX ctx; 00795 unsigned char *buf; 00796 ssize_t n; 00797 int fd; 00798 00799 MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1); 00800 00801 OPEN(fd, path, O_RDONLY); 00802 if(fd < 0) { 00803 free(buf); 00804 return 1; 00805 } 00806 00807 MD5_Init(&ctx); 00808 00809 while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) { 00810 if(n < 0) { 00811 continue; 00812 } 00813 MD5_Update(&ctx, buf, n); 00814 } 00815 00816 CLOSE(fd); 00817 free(buf); 00818 00819 if(n < 0) { 00820 return 2; 00821 } 00822 00823 MD5_Final(output, &ctx); 00824 return 0; 00825 } 00826 00827 /* third param is so we match the PolarSSL definition */ 00828 /** Compute the SHA-224 or SHA-256 message digest of a file. 00829 * @param path file path of file to compute SHA2 digest of 00830 * @param output string to hold computed SHA2 digest 00831 * @param is224 use SHA-224 instead of SHA-256 00832 * @return 0 on success, 1 on file open error, 2 on file read error 00833 */ 00834 static int sha2_file(const char *path, unsigned char output[32], int is224) 00835 { 00836 SHA256_CTX ctx; 00837 unsigned char *buf; 00838 ssize_t n; 00839 int fd; 00840 00841 MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1); 00842 00843 OPEN(fd, path, O_RDONLY); 00844 if(fd < 0) { 00845 free(buf); 00846 return 1; 00847 } 00848 00849 if(is224) { 00850 SHA224_Init(&ctx); 00851 } else { 00852 SHA256_Init(&ctx); 00853 } 00854 00855 while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) { 00856 if(n < 0) { 00857 continue; 00858 } 00859 if(is224) { 00860 SHA224_Update(&ctx, buf, n); 00861 } else { 00862 SHA256_Update(&ctx, buf, n); 00863 } 00864 } 00865 00866 CLOSE(fd); 00867 free(buf); 00868 00869 if(n < 0) { 00870 return 2; 00871 } 00872 00873 if(is224) { 00874 SHA224_Final(output, &ctx); 00875 } else { 00876 SHA256_Final(output, &ctx); 00877 } 00878 return 0; 00879 } 00880 #endif 00881 00882 /** Create a string representing bytes in hexadecimal. 00883 * @param bytes the bytes to represent in hexadecimal 00884 * @param size number of bytes to consider 00885 * @return a NULL terminated string with the hexadecimal representation of 00886 * bytes or NULL on error. This string must be freed. 00887 */ 00888 static char *hex_representation(unsigned char *bytes, size_t size) 00889 { 00890 static const char *hex_digits = "0123456789abcdef"; 00891 char *str; 00892 size_t i; 00893 00894 MALLOC(str, 2 * size + 1, return NULL); 00895 00896 for (i = 0; i < size; i++) { 00897 str[2 * i] = hex_digits[bytes[i] >> 4]; 00898 str[2 * i + 1] = hex_digits[bytes[i] & 0x0f]; 00899 } 00900 00901 str[2 * size] = '\0'; 00902 00903 return str; 00904 } 00905 00906 /** Get the md5 sum of file. 00907 * @param filename name of the file 00908 * @return the checksum on success, NULL on error 00909 * @addtogroup alpm_misc 00910 */ 00911 char SYMEXPORT *alpm_compute_md5sum(const char *filename) 00912 { 00913 unsigned char output[16]; 00914 00915 ASSERT(filename != NULL, return NULL); 00916 00917 /* defined above for OpenSSL, otherwise defined in md5.h */ 00918 if(md5_file(filename, output) > 0) { 00919 return NULL; 00920 } 00921 00922 return hex_representation(output, 16); 00923 } 00924 00925 /** Get the sha256 sum of file. 00926 * @param filename name of the file 00927 * @return the checksum on success, NULL on error 00928 * @addtogroup alpm_misc 00929 */ 00930 char SYMEXPORT *alpm_compute_sha256sum(const char *filename) 00931 { 00932 unsigned char output[32]; 00933 00934 ASSERT(filename != NULL, return NULL); 00935 00936 /* defined above for OpenSSL, otherwise defined in sha2.h */ 00937 if(sha2_file(filename, output, 0) > 0) { 00938 return NULL; 00939 } 00940 00941 return hex_representation(output, 32); 00942 } 00943 00944 /** Calculates a file's MD5 or SHA2 digest and compares it to an expected value. 00945 * @param filepath path of the file to check 00946 * @param expected hash value to compare against 00947 * @param type digest type to use 00948 * @return 0 if file matches the expected hash, 1 if they do not match, -1 on 00949 * error 00950 */ 00951 int _alpm_test_checksum(const char *filepath, const char *expected, 00952 enum _alpm_csum type) 00953 { 00954 char *computed; 00955 int ret; 00956 00957 if(type == ALPM_CSUM_MD5) { 00958 computed = alpm_compute_md5sum(filepath); 00959 } else if(type == ALPM_CSUM_SHA256) { 00960 computed = alpm_compute_sha256sum(filepath); 00961 } else { 00962 return -1; 00963 } 00964 00965 if(expected == NULL || computed == NULL) { 00966 ret = -1; 00967 } else if(strcmp(expected, computed) != 0) { 00968 ret = 1; 00969 } else { 00970 ret = 0; 00971 } 00972 00973 FREE(computed); 00974 return ret; 00975 } 00976 00977 /* Note: does NOT handle sparse files on purpose for speed. */ 00978 /** TODO. 00979 * Does not handle sparse files on purpose for speed. 00980 * @param a 00981 * @param b 00982 * @return 00983 */ 00984 int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b) 00985 { 00986 /* ensure we start populating our line buffer at the beginning */ 00987 b->line_offset = b->line; 00988 00989 while(1) { 00990 size_t block_remaining; 00991 char *eol; 00992 00993 /* have we processed this entire block? */ 00994 if(b->block + b->block_size == b->block_offset) { 00995 int64_t offset; 00996 if(b->ret == ARCHIVE_EOF) { 00997 /* reached end of archive on the last read, now we are out of data */ 00998 goto cleanup; 00999 } 01000 01001 /* zero-copy - this is the entire next block of data. */ 01002 b->ret = archive_read_data_block(a, (void *)&b->block, 01003 &b->block_size, &offset); 01004 b->block_offset = b->block; 01005 block_remaining = b->block_size; 01006 01007 /* error, cleanup */ 01008 if(b->ret < ARCHIVE_OK) { 01009 goto cleanup; 01010 } 01011 } else { 01012 block_remaining = b->block + b->block_size - b->block_offset; 01013 } 01014 01015 /* look through the block looking for EOL characters */ 01016 eol = memchr(b->block_offset, '\n', block_remaining); 01017 if(!eol) { 01018 eol = memchr(b->block_offset, '\0', block_remaining); 01019 } 01020 01021 /* allocate our buffer, or ensure our existing one is big enough */ 01022 if(!b->line) { 01023 /* set the initial buffer to the read block_size */ 01024 CALLOC(b->line, b->block_size + 1, sizeof(char), b->ret = -ENOMEM; goto cleanup); 01025 b->line_size = b->block_size + 1; 01026 b->line_offset = b->line; 01027 } else { 01028 /* note: we know eol > b->block_offset and b->line_offset > b->line, 01029 * so we know the result is unsigned and can fit in size_t */ 01030 size_t new = eol ? (size_t)(eol - b->block_offset) : block_remaining; 01031 size_t needed = (size_t)((b->line_offset - b->line) + new + 1); 01032 if(needed > b->max_line_size) { 01033 b->ret = -ERANGE; 01034 goto cleanup; 01035 } 01036 if(needed > b->line_size) { 01037 /* need to realloc + copy data to fit total length */ 01038 char *new; 01039 CALLOC(new, needed, sizeof(char), b->ret = -ENOMEM; goto cleanup); 01040 memcpy(new, b->line, b->line_size); 01041 b->line_size = needed; 01042 b->line_offset = new + (b->line_offset - b->line); 01043 free(b->line); 01044 b->line = new; 01045 } 01046 } 01047 01048 if(eol) { 01049 size_t len = (size_t)(eol - b->block_offset); 01050 memcpy(b->line_offset, b->block_offset, len); 01051 b->line_offset[len] = '\0'; 01052 b->block_offset = eol + 1; 01053 /* this is the main return point; from here you can read b->line */ 01054 return ARCHIVE_OK; 01055 } else { 01056 /* we've looked through the whole block but no newline, copy it */ 01057 size_t len = (size_t)(b->block + b->block_size - b->block_offset); 01058 memcpy(b->line_offset, b->block_offset, len); 01059 b->line_offset += len; 01060 b->block_offset = b->block + b->block_size; 01061 /* there was no new data, return what is left; saved ARCHIVE_EOF will be 01062 * returned on next call */ 01063 if(len == 0) { 01064 b->line_offset[0] = '\0'; 01065 return ARCHIVE_OK; 01066 } 01067 } 01068 } 01069 01070 cleanup: 01071 { 01072 int ret = b->ret; 01073 FREE(b->line); 01074 memset(b, 0, sizeof(struct archive_read_buffer)); 01075 return ret; 01076 } 01077 } 01078 01079 /** Parse a full package specifier. 01080 * @param target package specifier to parse, such as: "pacman-4.0.1-2", 01081 * "pacman-4.01-2/", or "pacman-4.0.1-2/desc" 01082 * @param name to hold package name 01083 * @param version to hold package version 01084 * @param name_hash to hold package name hash 01085 * @return 0 on success, -1 on error 01086 */ 01087 int _alpm_splitname(const char *target, char **name, char **version, 01088 unsigned long *name_hash) 01089 { 01090 /* the format of a db entry is as follows: 01091 * package-version-rel/ 01092 * package-version-rel/desc (we ignore the filename portion) 01093 * package name can contain hyphens, so parse from the back- go back 01094 * two hyphens and we have split the version from the name. 01095 */ 01096 const char *pkgver, *end; 01097 01098 if(target == NULL) { 01099 return -1; 01100 } 01101 01102 /* remove anything trailing a '/' */ 01103 end = strchr(target, '/'); 01104 if(!end) { 01105 end = target + strlen(target); 01106 } 01107 01108 /* do the magic parsing- find the beginning of the version string 01109 * by doing two iterations of same loop to lop off two hyphens */ 01110 for(pkgver = end - 1; *pkgver && *pkgver != '-'; pkgver--); 01111 for(pkgver = pkgver - 1; *pkgver && *pkgver != '-'; pkgver--); 01112 if(*pkgver != '-' || pkgver == target) { 01113 return -1; 01114 } 01115 01116 /* copy into fields and return */ 01117 if(version) { 01118 if(*version) { 01119 FREE(*version); 01120 } 01121 /* version actually points to the dash, so need to increment 1 and account 01122 * for potential end character */ 01123 STRNDUP(*version, pkgver + 1, end - pkgver - 1, return -1); 01124 } 01125 01126 if(name) { 01127 if(*name) { 01128 FREE(*name); 01129 } 01130 STRNDUP(*name, target, pkgver - target, return -1); 01131 if(name_hash) { 01132 *name_hash = _alpm_hash_sdbm(*name); 01133 } 01134 } 01135 01136 return 0; 01137 } 01138 01139 /** Hash the given string to an unsigned long value. 01140 * This is the standard sdbm hashing algorithm. 01141 * @param str string to hash 01142 * @return the hash value of the given string 01143 */ 01144 unsigned long _alpm_hash_sdbm(const char *str) 01145 { 01146 unsigned long hash = 0; 01147 int c; 01148 01149 if(!str) { 01150 return hash; 01151 } 01152 while((c = *str++)) { 01153 hash = c + hash * 65599; 01154 } 01155 01156 return hash; 01157 } 01158 01159 /** Convert a string to a file offset. 01160 * This parses bare positive integers only. 01161 * @param line string to convert 01162 * @return off_t on success, -1 on error 01163 */ 01164 off_t _alpm_strtoofft(const char *line) 01165 { 01166 char *end; 01167 unsigned long long result; 01168 errno = 0; 01169 01170 /* we are trying to parse bare numbers only, no leading anything */ 01171 if(line[0] < '0' || line[0] > '9') { 01172 return (off_t)-1; 01173 } 01174 result = strtoull(line, &end, 10); 01175 if(result == 0 && end == line) { 01176 /* line was not a number */ 01177 return (off_t)-1; 01178 } else if(result == ULLONG_MAX && errno == ERANGE) { 01179 /* line does not fit in unsigned long long */ 01180 return (off_t)-1; 01181 } else if(*end) { 01182 /* line began with a number but has junk left over at the end */ 01183 return (off_t)-1; 01184 } 01185 01186 return (off_t)result; 01187 } 01188 01189 /** Parses a date into an alpm_time_t struct. 01190 * @param line date to parse 01191 * @return time struct on success, 0 on error 01192 */ 01193 alpm_time_t _alpm_parsedate(const char *line) 01194 { 01195 char *end; 01196 long long result; 01197 errno = 0; 01198 01199 if(isalpha((unsigned char)line[0])) { 01200 /* initialize to null in case of failure */ 01201 struct tm tmp_tm; 01202 memset(&tmp_tm, 0, sizeof(struct tm)); 01203 setlocale(LC_TIME, "C"); 01204 strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm); 01205 setlocale(LC_TIME, ""); 01206 return (alpm_time_t)mktime(&tmp_tm); 01207 } 01208 01209 result = strtoll(line, &end, 10); 01210 if(result == 0 && end == line) { 01211 /* line was not a number */ 01212 errno = EINVAL; 01213 return 0; 01214 } else if(errno == ERANGE) { 01215 /* line does not fit in long long */ 01216 return 0; 01217 } else if(*end) { 01218 /* line began with a number but has junk left over at the end */ 01219 errno = EINVAL; 01220 return 0; 01221 } 01222 01223 return (alpm_time_t)result; 01224 } 01225 01226 /** Wrapper around access() which takes a dir and file argument 01227 * separately and generates an appropriate error message. 01228 * If dir is NULL file will be treated as the whole path. 01229 * @param handle an alpm handle 01230 * @param dir directory path ending with and slash 01231 * @param file filename 01232 * @param amode access mode as described in access() 01233 * @return int value returned by access() 01234 */ 01235 int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int amode) 01236 { 01237 size_t len = 0; 01238 int ret = 0; 01239 01240 if(dir) { 01241 char *check_path; 01242 01243 len = strlen(dir) + strlen(file) + 1; 01244 CALLOC(check_path, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, -1)); 01245 snprintf(check_path, len, "%s%s", dir, file); 01246 01247 ret = access(check_path, amode); 01248 free(check_path); 01249 } else { 01250 dir = ""; 01251 ret = access(file, amode); 01252 } 01253 01254 if(ret != 0) { 01255 if(amode & R_OK) { 01256 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not readable: %s\n", 01257 dir, file, strerror(errno)); 01258 } 01259 if(amode & W_OK) { 01260 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not writable: %s\n", 01261 dir, file, strerror(errno)); 01262 } 01263 if(amode & X_OK) { 01264 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not executable: %s\n", 01265 dir, file, strerror(errno)); 01266 } 01267 if(amode == F_OK) { 01268 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" does not exist: %s\n", 01269 dir, file, strerror(errno)); 01270 } 01271 } 01272 return ret; 01273 } 01274 01275 /** Checks whether a string matches a shell wildcard pattern. 01276 * Wrapper around fnmatch. 01277 * @param pattern pattern to match aganist 01278 * @param string string to check against pattern 01279 * @return 0 if string matches pattern, non-zero if they don't match and on 01280 * error 01281 */ 01282 int _alpm_fnmatch(const void *pattern, const void *string) 01283 { 01284 return fnmatch(pattern, string, 0); 01285 } 01286 01287 #ifndef HAVE_STRNDUP 01288 /* A quick and dirty implementation derived from glibc */ 01289 /** Determines the length of a fixed-size string. 01290 * @param s string to be measured 01291 * @param max maximum number of characters to search for the string end 01292 * @return length of s or max, whichever is smaller 01293 */ 01294 static size_t strnlen(const char *s, size_t max) 01295 { 01296 register const char *p; 01297 for(p = s; *p && max--; ++p); 01298 return (p - s); 01299 } 01300 01301 /** Copies a string. 01302 * Returned string needs to be freed 01303 * @param s string to be copied 01304 * @param n maximum number of characters to copy 01305 * @return pointer to the new string on success, NULL on error 01306 */ 01307 char *strndup(const char *s, size_t n) 01308 { 01309 size_t len = strnlen(s, n); 01310 char *new = (char *) malloc(len + 1); 01311 01312 if(new == NULL) 01313 return NULL; 01314 01315 new[len] = '\0'; 01316 return (char *)memcpy(new, s, len); 01317 } 01318 #endif 01319 01320 /* vim: set ts=2 sw=2 noet: */