/** * Check the PGP signature for the given file path. * If base64_sig is provided, it will be used as the signature data after * decoding. If base64_sig is NULL, expect a signature file next to path * (e.g. "%s.sig"). * * The return value will be 0 if nothing abnormal happened during the signature * check, and -1 if an error occurred while checking signatures or if a * signature could not be found; pm_errno will be set. Note that "abnormal" * does not include a failed signature; the value in siglist should be checked * to determine if the signature(s) are good. * @param handle the context handle * @param path the full path to a file * @param base64_sig optional PGP signature data in base64 encoding * @param siglist a pointer to storage for signature results * @return 0 in normal cases, -1 if the something failed in the check process */ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, const char *base64_sig, alpm_siglist_t *siglist) { int ret = -1, sigcount; gpgme_error_t err = 0; gpgme_ctx_t ctx; gpgme_data_t filedata, sigdata; gpgme_verify_result_t verify_result; gpgme_signature_t gpgsig; char *sigpath = NULL; unsigned char *decoded_sigdata = NULL; FILE *file = NULL, *sigfile = NULL; if(!path || _alpm_access(handle, NULL, path, R_OK) != 0) { RET_ERR(handle, ALPM_ERR_NOT_A_FILE, -1); } if(!siglist) { RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1); } siglist->count = 0; if(!base64_sig) { sigpath = _alpm_sigpath(handle, path); if(_alpm_access(handle, NULL, sigpath, R_OK) != 0 || (sigfile = fopen(sigpath, "rb")) == NULL) { _alpm_log(handle, ALPM_LOG_DEBUG, "sig path %s could not be opened\n", sigpath); handle->pm_errno = ALPM_ERR_SIG_MISSING; goto error; } } /* does the file we are verifying exist? */ file = fopen(path, "rb"); if(file == NULL) { handle->pm_errno = ALPM_ERR_NOT_A_FILE; goto error; } if(init_gpgme(handle)) { /* pm_errno was set in gpgme_init() */ goto error; } _alpm_log(handle, ALPM_LOG_DEBUG, "checking signature for %s\n", path); memset(&ctx, 0, sizeof(ctx)); memset(&sigdata, 0, sizeof(sigdata)); memset(&filedata, 0, sizeof(filedata)); err = gpgme_new(&ctx); CHECK_ERR(); /* create our necessary data objects to verify the signature */ err = gpgme_data_new_from_stream(&filedata, file); CHECK_ERR(); /* next create data object for the signature */ if(base64_sig) { /* memory-based, we loaded it from a sync DB */ size_t data_len; int decode_ret = decode_signature(base64_sig, &decoded_sigdata, &data_len); if(decode_ret) { handle->pm_errno = ALPM_ERR_SIG_INVALID; goto gpg_error; } err = gpgme_data_new_from_mem(&sigdata, (char *)decoded_sigdata, data_len, 0); } else { /* file-based, it is on disk */ err = gpgme_data_new_from_stream(&sigdata, sigfile); } CHECK_ERR(); /* here's where the magic happens */ err = gpgme_op_verify(ctx, sigdata, filedata, NULL); CHECK_ERR(); verify_result = gpgme_op_verify_result(ctx); CHECK_ERR(); if(!verify_result || !verify_result->signatures) { _alpm_log(handle, ALPM_LOG_DEBUG, "no signatures returned\n"); handle->pm_errno = ALPM_ERR_SIG_MISSING; goto gpg_error; } for(gpgsig = verify_result->signatures, sigcount = 0; gpgsig; gpgsig = gpgsig->next, sigcount++); _alpm_log(handle, ALPM_LOG_DEBUG, "%d signatures returned\n", sigcount); CALLOC(siglist->results, sigcount, sizeof(alpm_sigresult_t), handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error); siglist->count = sigcount; for(gpgsig = verify_result->signatures, sigcount = 0; gpgsig; gpgsig = gpgsig->next, sigcount++) { alpm_list_t *summary_list, *summary; alpm_sigstatus_t status; alpm_sigvalidity_t validity; gpgme_key_t key; alpm_sigresult_t *result; _alpm_log(handle, ALPM_LOG_DEBUG, "fingerprint: %s\n", gpgsig->fpr); summary_list = list_sigsum(gpgsig->summary); for(summary = summary_list; summary; summary = summary->next) { _alpm_log(handle, ALPM_LOG_DEBUG, "summary: %s\n", (const char *)summary->data); } alpm_list_free(summary_list); _alpm_log(handle, ALPM_LOG_DEBUG, "status: %s\n", gpgme_strerror(gpgsig->status)); _alpm_log(handle, ALPM_LOG_DEBUG, "timestamp: %lu\n", gpgsig->timestamp); _alpm_log(handle, ALPM_LOG_DEBUG, "exp_timestamp: %lu\n", gpgsig->exp_timestamp); _alpm_log(handle, ALPM_LOG_DEBUG, "validity: %s; reason: %s\n", string_validity(gpgsig->validity), gpgme_strerror(gpgsig->validity_reason)); result = siglist->results + sigcount; err = gpgme_get_key(ctx, gpgsig->fpr, &key, 0); if(gpg_err_code(err) == GPG_ERR_EOF) { _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); err = GPG_ERR_NO_ERROR; /* we dupe the fpr in this case since we have no key to point at */ STRDUP(result->key.fingerprint, gpgsig->fpr, handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error); } else {
/** Applies delta files to create an upgraded package file. * * All intermediate files are deleted, leaving only the starting and * ending package files. * * @param handle the context handle * * @return 0 if all delta files were able to be applied, 1 otherwise. */ static int apply_deltas(alpm_handle_t *handle) { alpm_list_t *i; int deltas_found = 0, ret = 0; const char *cachedir = _alpm_filecache_setup(handle); alpm_trans_t *trans = handle->trans; for(i = trans->add; i; i = i->next) { alpm_pkg_t *spkg = i->data; alpm_list_t *delta_path = spkg->delta_path; alpm_list_t *dlts = NULL; if(!delta_path) { continue; } if(!deltas_found) { /* only show this if we actually have deltas to apply, and it is before * the very first one */ EVENT(handle, ALPM_EVENT_DELTA_PATCHES_START, NULL, NULL); deltas_found = 1; } for(dlts = delta_path; dlts; dlts = dlts->next) { alpm_delta_t *d = dlts->data; char *delta, *from, *to; char command[PATH_MAX]; size_t len = 0; delta = _alpm_filecache_find(handle, d->delta); /* the initial package might be in a different cachedir */ if(dlts == delta_path) { from = _alpm_filecache_find(handle, d->from); } else { /* len = cachedir len + from len + '/' + null */ len = strlen(cachedir) + strlen(d->from) + 2; MALLOC(from, len, RET_ERR(handle, ALPM_ERR_MEMORY, 1)); snprintf(from, len, "%s/%s", cachedir, d->from); } len = strlen(cachedir) + strlen(d->to) + 2; MALLOC(to, len, free(from); RET_ERR(handle, ALPM_ERR_MEMORY, 1)); snprintf(to, len, "%s/%s", cachedir, d->to); /* build the patch command */ if(endswith(to, ".gz")) { /* special handling for gzip : we disable timestamp with -n option */ snprintf(command, PATH_MAX, "xdelta3 -d -q -R -c -s %s %s | gzip -n > %s", from, delta, to); } else { snprintf(command, PATH_MAX, "xdelta3 -d -q -s %s %s %s", from, delta, to); } _alpm_log(handle, ALPM_LOG_DEBUG, "command: %s\n", command); EVENT(handle, ALPM_EVENT_DELTA_PATCH_START, d->to, d->delta); int retval = system(command); if(retval == 0) { EVENT(handle, ALPM_EVENT_DELTA_PATCH_DONE, NULL, NULL); /* delete the delta file */ unlink(delta); /* Delete the 'from' package but only if it is an intermediate * package. The starting 'from' package should be kept, just * as if deltas were not used. */ if(dlts != delta_path) { unlink(from); } } FREE(from); FREE(to); FREE(delta); if(retval != 0) { /* one delta failed for this package, cancel the remaining ones */ EVENT(handle, ALPM_EVENT_DELTA_PATCH_FAILED, NULL, NULL); handle->pm_errno = ALPM_ERR_DLT_PATCHFAILED; ret = 1; break; } } } if(deltas_found) { EVENT(handle, ALPM_EVENT_DELTA_PATCHES_DONE, NULL, NULL); } return ret; }
static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq) { FILE *fp = NULL; char line[1024]; char *pkgpath; alpm_db_t *db = info->origin_data.db; /* bitmask logic here: * infolevel: 00001111 * inforeq: 00010100 * & result: 00000100 * == to inforeq? nope, we need to load more info. */ if((info->infolevel & inforeq) == inforeq) { /* already loaded all of this info, do nothing */ return 0; } if(info->infolevel & INFRQ_ERROR) { /* We've encountered an error loading this package before. Don't attempt * repeated reloads, just give up. */ return -1; } _alpm_log(db->handle, ALPM_LOG_FUNCTION, "loading package data for %s : level=0x%x\n", info->name, inforeq); pkgpath = _alpm_local_db_pkgpath(db, info, NULL); if(!pkgpath || access(pkgpath, F_OK)) { /* directory doesn't exist or can't be opened */ _alpm_log(db->handle, ALPM_LOG_DEBUG, "cannot find '%s-%s' in db '%s'\n", info->name, info->version, db->treename); goto error; } free(pkgpath); /* clear out 'line', to be certain - and to make valgrind happy */ memset(line, 0, sizeof(line)); /* DESC */ if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) { char *path = _alpm_local_db_pkgpath(db, info, "desc"); if(!path || (fp = fopen(path, "r")) == NULL) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); free(path); goto error; } free(path); while(!feof(fp)) { if(fgets(line, sizeof(line), fp) == NULL && !feof(fp)) { goto error; } if(_alpm_strip_newline(line) == 0) { /* length of stripped line was zero */ continue; } if(strcmp(line, "%NAME%") == 0) { READ_NEXT(); if(strcmp(line, info->name) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: name " "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%VERSION%") == 0) { READ_NEXT(); if(strcmp(line, info->version) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: version " "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%DESC%") == 0) { READ_AND_STORE(info->desc); } else if(strcmp(line, "%GROUPS%") == 0) { READ_AND_STORE_ALL(info->groups); } else if(strcmp(line, "%URL%") == 0) { READ_AND_STORE(info->url); } else if(strcmp(line, "%LICENSE%") == 0) { READ_AND_STORE_ALL(info->licenses); } else if(strcmp(line, "%ARCH%") == 0) { READ_AND_STORE(info->arch); } else if(strcmp(line, "%BUILDDATE%") == 0) { READ_NEXT(); info->builddate = _alpm_parsedate(line); } else if(strcmp(line, "%INSTALLDATE%") == 0) { READ_NEXT(); info->installdate = _alpm_parsedate(line); } else if(strcmp(line, "%PACKAGER%") == 0) { READ_AND_STORE(info->packager); } else if(strcmp(line, "%REASON%") == 0) { READ_NEXT(); info->reason = (alpm_pkgreason_t)atoi(line); } else if(strcmp(line, "%SIZE%") == 0) { READ_NEXT(); info->isize = _alpm_strtoofft(line); } else if(strcmp(line, "%REPLACES%") == 0) { READ_AND_SPLITDEP(info->replaces); } else if(strcmp(line, "%DEPENDS%") == 0) { READ_AND_SPLITDEP(info->depends); } else if(strcmp(line, "%OPTDEPENDS%") == 0) { READ_AND_STORE_ALL(info->optdepends); } else if(strcmp(line, "%CONFLICTS%") == 0) { READ_AND_SPLITDEP(info->conflicts); } else if(strcmp(line, "%PROVIDES%") == 0) { READ_AND_SPLITDEP(info->provides); } } fclose(fp); fp = NULL; info->infolevel |= INFRQ_DESC; } /* FILES */ if(inforeq & INFRQ_FILES && !(info->infolevel & INFRQ_FILES)) { char *path = _alpm_local_db_pkgpath(db, info, "files"); if(!path || (fp = fopen(path, "r")) == NULL) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); free(path); goto error; } free(path); while(fgets(line, sizeof(line), fp)) { _alpm_strip_newline(line); if(strcmp(line, "%FILES%") == 0) { size_t files_count = 0, files_size = 0; alpm_file_t *files = NULL; while(fgets(line, sizeof(line), fp) && _alpm_strip_newline(line)) { if(files_count >= files_size) { size_t old_size = files_size; if(files_size == 0) { files_size = 8; } else { files_size *= 2; } files = realloc(files, sizeof(alpm_file_t) * files_size); if(!files) { ALLOC_FAIL(sizeof(alpm_file_t) * files_size); goto error; } /* ensure all new memory is zeroed out, in both the initial * allocation and later reallocs */ memset(files + old_size, 0, sizeof(alpm_file_t) * (files_size - old_size)); } STRDUP(files[files_count].name, line, goto error); /* TODO: lstat file, get mode/size */ files_count++; } /* attempt to hand back any memory we don't need */ files = realloc(files, sizeof(alpm_file_t) * files_count); info->files.count = files_count; info->files.files = files; } else if(strcmp(line, "%BACKUP%") == 0) {
/** * @brief Remove a package's files, optionally skipping its replacement's * files. * * @param handle the context handle * @param oldpkg package to remove * @param newpkg package to replace \a oldpkg (optional) * @param targ_count current index within the transaction (1-based) * @param pkg_count the number of packages affected by the transaction * * @return 0 on success, -1 if alpm lacks permission to delete some of the * files, >0 the number of files alpm was unable to delete */ static int remove_package_files(alpm_handle_t *handle, alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg, size_t targ_count, size_t pkg_count) { alpm_list_t *skip_remove; alpm_filelist_t *filelist; size_t i; int err = 0; int nosave = handle->trans->flags & ALPM_TRANS_FLAG_NOSAVE; if(newpkg) { alpm_filelist_t *newfiles; alpm_list_t *b; skip_remove = alpm_list_join( alpm_list_strdup(handle->trans->skip_remove), alpm_list_strdup(handle->noupgrade)); /* Add files in the NEW backup array to the skip_remove array * so this removal operation doesn't kill them */ /* old package backup list */ newfiles = alpm_pkg_get_files(newpkg); for(b = alpm_pkg_get_backup(newpkg); b; b = b->next) { const alpm_backup_t *backup = b->data; /* safety check (fix the upgrade026 pactest) */ if(!alpm_filelist_contains(newfiles, backup->name)) { continue; } _alpm_log(handle, ALPM_LOG_DEBUG, "adding %s to the skip_remove array\n", backup->name); skip_remove = alpm_list_add(skip_remove, strdup(backup->name)); } } else { skip_remove = alpm_list_strdup(handle->trans->skip_remove); } filelist = alpm_pkg_get_files(oldpkg); for(i = 0; i < filelist->count; i++) { alpm_file_t *file = filelist->files + i; if(!can_remove_file(handle, file, skip_remove)) { _alpm_log(handle, ALPM_LOG_DEBUG, "not removing package '%s', can't remove all files\n", oldpkg->name); FREELIST(skip_remove); RET_ERR(handle, ALPM_ERR_PKG_CANT_REMOVE, -1); } } _alpm_log(handle, ALPM_LOG_DEBUG, "removing %zd files\n", filelist->count); if(!newpkg) { /* init progress bar, but only on true remove transactions */ PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, 0, pkg_count, targ_count); } /* iterate through the list backwards, unlinking files */ for(i = filelist->count; i > 0; i--) { alpm_file_t *file = filelist->files + i - 1; if(unlink_file(handle, oldpkg, newpkg, file, skip_remove, nosave) < 0) { err++; } if(!newpkg) { /* update progress bar after each file */ int percent = ((filelist->count - i) * 100) / filelist->count; PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, percent, pkg_count, targ_count); } } FREELIST(skip_remove); if(!newpkg) { /* set progress to 100% after we finish unlinking files */ PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, 100, pkg_count, targ_count); } return err; }
/** Compute the size of the files that will be downloaded to install a * package. * @param newpkg the new package to upgrade to */ static int compute_download_size(alpm_pkg_t *newpkg) { const char *fname; char *fpath, *fnamepart = NULL; off_t size = 0; alpm_handle_t *handle = newpkg->handle; int ret = 0; if(newpkg->origin != ALPM_PKG_FROM_SYNCDB) { newpkg->infolevel |= INFRQ_DSIZE; newpkg->download_size = 0; return 0; } ASSERT(newpkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1)); fname = newpkg->filename; fpath = _alpm_filecache_find(handle, fname); /* downloaded file exists, so there's nothing to grab */ if(fpath) { size = 0; goto finish; } CALLOC(fnamepart, strlen(fname) + 6, sizeof(char), return -1); sprintf(fnamepart, "%s.part", fname); fpath = _alpm_filecache_find(handle, fnamepart); if(fpath) { struct stat st; if(stat(fpath, &st) == 0) { /* subtract the size of the .part file */ _alpm_log(handle, ALPM_LOG_DEBUG, "using (package - .part) size\n"); size = newpkg->size - st.st_size; size = size < 0 ? 0 : size; } /* tell the caller that we have a partial */ ret = 1; } else if(handle->deltaratio > 0.0) { off_t dltsize; dltsize = _alpm_shortest_delta_path(handle, newpkg->deltas, newpkg->filename, &newpkg->delta_path); if(newpkg->delta_path && (dltsize < newpkg->size * handle->deltaratio)) { _alpm_log(handle, ALPM_LOG_DEBUG, "using delta size\n"); size = dltsize; } else { _alpm_log(handle, ALPM_LOG_DEBUG, "using package size\n"); size = newpkg->size; alpm_list_free(newpkg->delta_path); newpkg->delta_path = NULL; } } else { size = newpkg->size; } finish: _alpm_log(handle, ALPM_LOG_DEBUG, "setting download size %jd for pkg %s\n", (intmax_t)size, newpkg->name); newpkg->infolevel |= INFRQ_DSIZE; newpkg->download_size = size; FREE(fpath); FREE(fnamepart); return ret; }
/** Fetch a remote pkg. */ char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url) { char *filepath; const char *cachedir; char *final_file = NULL, *final_pkg_url = NULL; struct dload_payload payload; int ret = 0; CHECK_HANDLE(handle, return NULL); ASSERT(url, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL)); /* find a valid cache dir to download to */ cachedir = _alpm_filecache_setup(handle); memset(&payload, 0, sizeof(struct dload_payload)); /* attempt to find the file in our pkgcache */ filepath = filecache_find_url(handle, url); if(filepath == NULL) { STRDUP(payload.fileurl, url, RET_ERR(handle, ALPM_ERR_MEMORY, NULL)); payload.allow_resume = 1; payload.handle = handle; payload.trust_remote_name = 1; /* download the file */ ret = _alpm_download(&payload, cachedir, &final_file, &final_pkg_url); _alpm_dload_payload_reset(&payload); if(ret == -1) { _alpm_log(handle, ALPM_LOG_WARNING, _("failed to download %s\n"), url); free(final_file); return NULL; } _alpm_log(handle, ALPM_LOG_DEBUG, "successfully downloaded %s\n", url); } /* attempt to download the signature */ if(ret == 0 && final_pkg_url && (handle->siglevel & ALPM_SIG_PACKAGE)) { char *sig_filepath, *sig_final_file = NULL; size_t len; len = strlen(final_pkg_url) + 5; MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, NULL)); snprintf(payload.fileurl, len, "%s.sig", final_pkg_url); sig_filepath = filecache_find_url(handle, payload.fileurl); if(sig_filepath == NULL) { payload.handle = handle; payload.trust_remote_name = 1; payload.force = 1; payload.errors_ok = (handle->siglevel & ALPM_SIG_PACKAGE_OPTIONAL); /* set hard upper limit of 16KiB */ payload.max_size = 16 * 1024; ret = _alpm_download(&payload, cachedir, &sig_final_file, NULL); if(ret == -1 && !payload.errors_ok) { _alpm_log(handle, ALPM_LOG_WARNING, _("failed to download %s\n"), payload.fileurl); /* Warn now, but don't return NULL. We will fail later during package * load time. */ } else if(ret == 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "successfully downloaded %s\n", payload.fileurl); } FREE(sig_final_file); } free(sig_filepath); _alpm_dload_payload_reset(&payload); } /* we should be able to find the file the second time around */ if(filepath == NULL) { filepath = _alpm_filecache_find(handle, final_file); } free(final_file); return filepath; }
static void shift_pacsave(alpm_handle_t *handle, const char *file) { DIR *dir = NULL; struct dirent *ent; struct stat st; regex_t reg; const char *basename; char *dirname; char oldfile[PATH_MAX]; char newfile[PATH_MAX]; char regstr[PATH_MAX]; unsigned long log_max = 0; size_t basename_len; dirname = mdirname(file); if(!dirname) { return; } basename = mbasename(file); basename_len = strlen(basename); snprintf(regstr, PATH_MAX, "^%s\\.pacsave\\.([[:digit:]]+)$", basename); if(regcomp(®, regstr, REG_EXTENDED | REG_NEWLINE) != 0) { goto cleanup; } dir = opendir(dirname); if(dir == NULL) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not open directory: %s: %s\n"), dirname, strerror(errno)); goto cleanup; } while((ent = readdir(dir)) != NULL) { if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { continue; } if(regexec(®, ent->d_name, 0, 0, 0) == 0) { unsigned long cur_log; cur_log = strtoul(ent->d_name + basename_len + strlen(".pacsave."), NULL, 10); if(cur_log > log_max) { log_max = cur_log; } } } /* Shift pacsaves */ unsigned long i; for(i = log_max + 1; i > 1; i--) { snprintf(oldfile, PATH_MAX, "%s.pacsave.%lu", file, i-1); snprintf(newfile, PATH_MAX, "%s.pacsave.%lu", file, i); rename(oldfile, newfile); } snprintf(oldfile, PATH_MAX, "%s.pacsave", file); if(stat(oldfile, &st) == 0) { snprintf(newfile, PATH_MAX, "%s.1", oldfile); rename(oldfile, newfile); } regfree(®); cleanup: free(dirname); closedir(dir); }
static int extract_single_file(alpm_handle_t *handle, struct archive *archive, struct archive_entry *entry, alpm_pkg_t *newpkg, alpm_pkg_t *oldpkg) { const char *entryname; mode_t entrymode; char filename[PATH_MAX]; /* the actual file we're extracting */ int needbackup = 0, notouch = 0; const char *hash_orig = NULL; char *entryname_orig = NULL; int errors = 0; entryname = archive_entry_pathname(entry); entrymode = archive_entry_mode(entry); if(strcmp(entryname, ".INSTALL") == 0) { /* the install script goes inside the db */ snprintf(filename, PATH_MAX, "%s%s-%s/install", _alpm_db_path(handle->db_local), newpkg->name, newpkg->version); archive_entry_set_perm(entry, 0644); } else if(strcmp(entryname, ".CHANGELOG") == 0) { /* the changelog goes inside the db */ snprintf(filename, PATH_MAX, "%s%s-%s/changelog", _alpm_db_path(handle->db_local), newpkg->name, newpkg->version); archive_entry_set_perm(entry, 0644); } else if(strcmp(entryname, ".MTREE") == 0) { /* the mtree file goes inside the db */ snprintf(filename, PATH_MAX, "%s%s-%s/mtree", _alpm_db_path(handle->db_local), newpkg->name, newpkg->version); archive_entry_set_perm(entry, 0644); } else if(*entryname == '.') { /* for now, ignore all files starting with '.' that haven't * already been handled (for future possibilities) */ _alpm_log(handle, ALPM_LOG_DEBUG, "skipping extraction of '%s'\n", entryname); archive_read_data_skip(archive); return 0; } else { /* build the new entryname relative to handle->root */ snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname); } /* if a file is in NoExtract then we never extract it */ if(_alpm_fnmatch_patterns(handle->noextract, entryname) == 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "%s is in NoExtract," " skipping extraction of %s\n", entryname, filename); alpm_logaction(handle, ALPM_CALLER_PREFIX, "note: %s is in NoExtract, skipping extraction\n", entryname); archive_read_data_skip(archive); return 0; } /* Check for file existence. This is one of the more crucial parts * to get 'right'. Here are the possibilities, with the filesystem * on the left and the package on the top: * (F=file, N=node, S=symlink, D=dir) * | F/N | D * non-existent | 1 | 2 * F/N | 3 | 4 * D | 5 | 6 * * 1,2- extract, no magic necessary. lstat (_alpm_lstat) will fail here. * 3,4- conflict checks should have caught this. either overwrite * or backup the file. * 5- file replacing directory- don't allow it. * 6- skip extraction, dir already exists. */ struct stat lsbuf; if(_alpm_lstat(filename, &lsbuf) != 0) { /* cases 1,2: file doesn't exist, skip all backup checks */ } else { if(S_ISDIR(lsbuf.st_mode)) { if(S_ISDIR(entrymode)) { uid_t entryuid = archive_entry_uid(entry); gid_t entrygid = archive_entry_gid(entry); /* case 6: existing dir, ignore it */ if(lsbuf.st_mode != entrymode) { /* if filesystem perms are different than pkg perms, warn user */ mode_t mask = 07777; _alpm_log(handle, ALPM_LOG_WARNING, _("directory permissions differ on %s\n" "filesystem: %o package: %o\n"), filename, lsbuf.st_mode & mask, entrymode & mask); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: directory permissions differ on %s\n" "filesystem: %o package: %o\n", filename, lsbuf.st_mode & mask, entrymode & mask); } if((entryuid != lsbuf.st_uid) || (entrygid != lsbuf.st_gid)) { _alpm_log(handle, ALPM_LOG_WARNING, _("directory ownership differs on %s\n" "filesystem: %u:%u package: %u:%u\n"), filename, lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: directory ownership differs on %s\n" "filesystem: %u:%u package: %u:%u\n", filename, lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid); } _alpm_log(handle, ALPM_LOG_DEBUG, "extract: skipping dir extraction of %s\n", filename); archive_read_data_skip(archive); return 0; } else { /* case 5: trying to overwrite dir with file, don't allow it */ _alpm_log(handle, ALPM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"), filename); archive_read_data_skip(archive); return 1; } } else if(S_ISDIR(entrymode)) { /* case 4: trying to overwrite file with dir */ _alpm_log(handle, ALPM_LOG_DEBUG, "extract: overwriting file with dir %s\n", filename); } else { /* case 3: */ /* if file is in NoUpgrade, don't touch it */ if(_alpm_fnmatch_patterns(handle->noupgrade, entryname) == 0) { notouch = 1; } else { alpm_backup_t *backup; /* go to the backup array and see if our conflict is there */ /* check newpkg first, so that adding backup files is retroactive */ backup = _alpm_needbackup(entryname, newpkg); if(backup) { needbackup = 1; } /* check oldpkg for a backup entry, store the hash if available */ if(oldpkg) { backup = _alpm_needbackup(entryname, oldpkg); if(backup) { hash_orig = backup->hash; needbackup = 1; } } } } } /* we need access to the original entryname later after calls to * archive_entry_set_pathname(), so we need to dupe it and free() later */ STRDUP(entryname_orig, entryname, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); if(needbackup) { char *checkfile; char *hash_local = NULL, *hash_pkg = NULL; size_t len; len = strlen(filename) + 10; MALLOC(checkfile, len, errors++; handle->pm_errno = ALPM_ERR_MEMORY; goto needbackup_cleanup); snprintf(checkfile, len, "%s.paccheck", filename); if(perform_extraction(handle, archive, entry, checkfile, entryname_orig)) { errors++; goto needbackup_cleanup; } hash_local = alpm_compute_md5sum(filename); hash_pkg = alpm_compute_md5sum(checkfile); /* update the md5 hash in newpkg's backup (it will be the new original) */ alpm_list_t *i; for(i = alpm_pkg_get_backup(newpkg); i; i = i->next) { alpm_backup_t *backup = i->data; char *newhash; if(!backup->name || strcmp(backup->name, entryname_orig) != 0) { continue; } STRDUP(newhash, hash_pkg, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); FREE(backup->hash); backup->hash = newhash; } _alpm_log(handle, ALPM_LOG_DEBUG, "checking hashes for %s\n", entryname_orig); _alpm_log(handle, ALPM_LOG_DEBUG, "current: %s\n", hash_local); _alpm_log(handle, ALPM_LOG_DEBUG, "new: %s\n", hash_pkg); _alpm_log(handle, ALPM_LOG_DEBUG, "original: %s\n", hash_orig); if(hash_local && hash_pkg && strcmp(hash_local, hash_pkg) == 0) { /* local and new files are the same, updating anyway to get * correct timestamps */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n", entryname_orig); if(try_rename(handle, checkfile, filename)) { errors++; } } else if(hash_orig && hash_pkg && strcmp(hash_orig, hash_pkg) == 0) { /* original and new files are the same, leave the local version alone, * including any user changes */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: leaving existing file in place\n"); unlink(checkfile); } else if(hash_orig && hash_local && strcmp(hash_orig, hash_local) == 0) { /* installed file has NOT been changed by user, * update to the new version */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n", entryname_orig); if(try_rename(handle, checkfile, filename)) { errors++; } } else { /* none of the three files matched another, unpack the new file alongside * the local file */ if(oldpkg) { char *newpath; size_t newlen = strlen(filename) + strlen(".pacnew") + 1; _alpm_log(handle, ALPM_LOG_DEBUG, "action: keeping current file and installing" " new one with .pacnew ending\n"); MALLOC(newpath, newlen, errors++; handle->pm_errno = ALPM_ERR_MEMORY; goto needbackup_cleanup); snprintf(newpath, newlen, "%s.pacnew", filename); if(try_rename(handle, checkfile, newpath)) { errors++; } else { _alpm_log(handle, ALPM_LOG_WARNING, _("%s installed as %s\n"), filename, newpath); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: %s installed as %s\n", filename, newpath); } free(newpath); } else { char *newpath; size_t newlen = strlen(filename) + strlen(".pacorig") + 1; _alpm_log(handle, ALPM_LOG_DEBUG, "action: saving existing file with a .pacorig ending" " and installing a new one\n"); MALLOC(newpath, newlen, errors++; handle->pm_errno = ALPM_ERR_MEMORY; goto needbackup_cleanup); snprintf(newpath, newlen, "%s.pacorig", filename); /* move the existing file to the "pacorig" */ if(try_rename(handle, filename, newpath)) { errors++; /* failed rename filename -> filename.pacorig */ errors++; /* failed rename checkfile -> filename */ } else { /* rename the file we extracted to the real name */ if(try_rename(handle, checkfile, filename)) { errors++; } else { _alpm_log(handle, ALPM_LOG_WARNING, _("%s saved as %s\n"), filename, newpath); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: %s saved as %s\n", filename, newpath); } } free(newpath); } } needbackup_cleanup: free(checkfile); free(hash_local); free(hash_pkg); } else {
static int sync_db_read(alpm_db_t *db, struct archive *archive, struct archive_entry *entry, alpm_pkg_t **likely_pkg) { const char *entryname, *filename; alpm_pkg_t *pkg; struct archive_read_buffer buf; entryname = archive_entry_pathname(entry); if(entryname == NULL) { _alpm_log(db->handle, ALPM_LOG_DEBUG, "invalid archive entry provided to _alpm_sync_db_read, skipping\n"); return -1; } _alpm_log(db->handle, ALPM_LOG_FUNCTION, "loading package data from archive entry %s\n", entryname); memset(&buf, 0, sizeof(buf)); /* 512K for a line length seems reasonable */ buf.max_line_size = 512 * 1024; pkg = load_pkg_for_entry(db, entryname, &filename, *likely_pkg); if(pkg == NULL) { _alpm_log(db->handle, ALPM_LOG_DEBUG, "entry %s could not be loaded into %s sync database", entryname, db->treename); return -1; } if(filename == NULL) { /* A file exists outside of a subdirectory. This isn't a read error, so return * success and try to continue on. */ _alpm_log(db->handle, ALPM_LOG_WARNING, _("unknown database file: %s\n"), filename); return 0; } if(strcmp(filename, "desc") == 0 || strcmp(filename, "depends") == 0 || strcmp(filename, "files") == 0 || (strcmp(filename, "deltas") == 0 && db->handle->deltaratio > 0.0) ) { int ret; while((ret = _alpm_archive_fgets(archive, &buf)) == ARCHIVE_OK) { char *line = buf.line; if(_alpm_strip_newline(line, buf.real_line_size) == 0) { /* length of stripped line was zero */ continue; } if(strcmp(line, "%NAME%") == 0) { READ_NEXT(); if(strcmp(line, pkg->name) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: name " "mismatch on package %s\n"), db->treename, pkg->name); } } else if(strcmp(line, "%VERSION%") == 0) { READ_NEXT(); if(strcmp(line, pkg->version) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: version " "mismatch on package %s\n"), db->treename, pkg->name); } } else if(strcmp(line, "%FILENAME%") == 0) { READ_AND_STORE(pkg->filename); if(_alpm_validate_filename(db, pkg->name, pkg->filename) < 0) { return -1; } } else if(strcmp(line, "%BASE%") == 0) { READ_AND_STORE(pkg->base); } else if(strcmp(line, "%DESC%") == 0) { READ_AND_STORE(pkg->desc); } else if(strcmp(line, "%GROUPS%") == 0) { READ_AND_STORE_ALL(pkg->groups); } else if(strcmp(line, "%URL%") == 0) { READ_AND_STORE(pkg->url); } else if(strcmp(line, "%LICENSE%") == 0) { READ_AND_STORE_ALL(pkg->licenses); } else if(strcmp(line, "%ARCH%") == 0) { READ_AND_STORE(pkg->arch); } else if(strcmp(line, "%BUILDDATE%") == 0) { READ_NEXT(); pkg->builddate = _alpm_parsedate(line); } else if(strcmp(line, "%PACKAGER%") == 0) { READ_AND_STORE(pkg->packager); } else if(strcmp(line, "%CSIZE%") == 0) { READ_NEXT(); pkg->size = _alpm_strtoofft(line); } else if(strcmp(line, "%ISIZE%") == 0) { READ_NEXT(); pkg->isize = _alpm_strtoofft(line); } else if(strcmp(line, "%MD5SUM%") == 0) { READ_AND_STORE(pkg->md5sum); } else if(strcmp(line, "%SHA256SUM%") == 0) { READ_AND_STORE(pkg->sha256sum); } else if(strcmp(line, "%PGPSIG%") == 0) { READ_AND_STORE(pkg->base64_sig); } else if(strcmp(line, "%REPLACES%") == 0) { READ_AND_SPLITDEP(pkg->replaces); } else if(strcmp(line, "%DEPENDS%") == 0) { READ_AND_SPLITDEP(pkg->depends); } else if(strcmp(line, "%OPTDEPENDS%") == 0) { READ_AND_SPLITDEP(pkg->optdepends); } else if(strcmp(line, "%MAKEDEPENDS%") == 0) { /* currently unused */ while(1) { READ_NEXT(); if(strlen(line) == 0) break; } } else if(strcmp(line, "%CHECKDEPENDS%") == 0) { /* currently unused */ while(1) { READ_NEXT(); if(strlen(line) == 0) break; } } else if(strcmp(line, "%CONFLICTS%") == 0) { READ_AND_SPLITDEP(pkg->conflicts); } else if(strcmp(line, "%PROVIDES%") == 0) { READ_AND_SPLITDEP(pkg->provides); } else if(strcmp(line, "%DELTAS%") == 0) { /* Different than the rest because of the _alpm_delta_parse call. */ while(1) { READ_NEXT(); if(strlen(line) == 0) break; pkg->deltas = alpm_list_add(pkg->deltas, _alpm_delta_parse(db->handle, line)); } } else if(strcmp(line, "%FILES%") == 0) { /* TODO: this could lazy load if there is future demand */ size_t files_count = 0, files_size = 0; alpm_file_t *files = NULL; while(1) { if(_alpm_archive_fgets(archive, &buf) != ARCHIVE_OK) { goto error; } line = buf.line; if(_alpm_strip_newline(line, buf.real_line_size) == 0) { break; } if(!_alpm_greedy_grow((void **)&files, &files_size, (files_count ? (files_count + 1) * sizeof(alpm_file_t) : 8 * sizeof(alpm_file_t)))) { goto error; } STRDUP(files[files_count].name, line, goto error); files_count++; } /* attempt to hand back any memory we don't need */ files = realloc(files, sizeof(alpm_file_t) * files_count); /* make sure the list is sorted */ qsort(files, files_count, sizeof(alpm_file_t), _alpm_files_cmp); pkg->files.count = files_count; pkg->files.files = files; } }
static int sync_db_populate(alpm_db_t *db) { const char *dbpath; size_t est_count; int count = 0; struct stat buf; struct archive *archive; struct archive_entry *entry; alpm_pkg_t *pkg = NULL; if(db->status & DB_STATUS_INVALID) { RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1); } if(db->status & DB_STATUS_MISSING) { RET_ERR(db->handle, ALPM_ERR_DB_NOT_FOUND, -1); } if((archive = archive_read_new()) == NULL) { RET_ERR(db->handle, ALPM_ERR_LIBARCHIVE, -1); } archive_read_support_compression_all(archive); archive_read_support_format_all(archive); dbpath = _alpm_db_path(db); if(!dbpath) { /* pm_errno set in _alpm_db_path() */ return -1; } _alpm_log(db->handle, ALPM_LOG_DEBUG, "opening database archive %s\n", dbpath); if(archive_read_open_filename(archive, dbpath, ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), dbpath, archive_error_string(archive)); archive_read_finish(archive); RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1); } if(stat(dbpath, &buf) != 0) { RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1); } est_count = estimate_package_count(&buf, archive); /* initialize hash at 66% full */ db->pkgcache = _alpm_pkghash_create(est_count * 3 / 2); if(db->pkgcache == NULL) { RET_ERR(db->handle, ALPM_ERR_MEMORY, -1); } while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { mode_t mode = archive_entry_mode(entry); if(S_ISDIR(mode)) { continue; } else { /* we have desc, depends or deltas - parse it */ if(sync_db_read(db, archive, entry, &pkg) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not parse package description file '%s' from db '%s'\n"), archive_entry_pathname(entry), db->treename); continue; } } } count = alpm_list_count(db->pkgcache->list); if(count > 0) { db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp); } archive_read_finish(archive); _alpm_log(db->handle, ALPM_LOG_DEBUG, "added %d packages to package cache for db '%s'\n", count, db->treename); return count; }
static int sync_db_validate(alpm_db_t *db) { alpm_siglevel_t level; const char *dbpath; if(db->status & DB_STATUS_VALID || db->status & DB_STATUS_MISSING) { return 0; } if(db->status & DB_STATUS_INVALID) { db->handle->pm_errno = ALPM_ERR_DB_INVALID_SIG; return -1; } dbpath = _alpm_db_path(db); if(!dbpath) { /* pm_errno set in _alpm_db_path() */ return -1; } /* we can skip any validation if the database doesn't exist */ if(access(dbpath, R_OK) != 0 && errno == ENOENT) { db->status &= ~DB_STATUS_EXISTS; db->status |= DB_STATUS_MISSING; _alpm_log(db->handle, ALPM_LOG_WARNING, "database file for '%s' does not exist\n", db->treename); goto valid; } db->status |= DB_STATUS_EXISTS; db->status &= ~DB_STATUS_MISSING; /* this takes into account the default verification level if UNKNOWN * was assigned to this db */ level = alpm_db_get_siglevel(db); if(level & ALPM_SIG_DATABASE) { int retry, ret; do { retry = 0; alpm_siglist_t *siglist; ret = _alpm_check_pgp_helper(db->handle, dbpath, NULL, level & ALPM_SIG_DATABASE_OPTIONAL, level & ALPM_SIG_DATABASE_MARGINAL_OK, level & ALPM_SIG_DATABASE_UNKNOWN_OK, &siglist); if(ret) { retry = _alpm_process_siglist(db->handle, db->treename, siglist, level & ALPM_SIG_DATABASE_OPTIONAL, level & ALPM_SIG_DATABASE_MARGINAL_OK, level & ALPM_SIG_DATABASE_UNKNOWN_OK); } alpm_siglist_cleanup(siglist); free(siglist); } while(retry); if(ret) { db->status &= ~DB_STATUS_VALID; db->status |= DB_STATUS_INVALID; db->handle->pm_errno = ALPM_ERR_DB_INVALID_SIG; return 1; } } valid: db->status |= DB_STATUS_VALID; db->status &= ~DB_STATUS_INVALID; return 0; }
int _alpm_local_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) { FILE *fp = NULL; char path[PATH_MAX]; char line[1024]; char *pkgpath = NULL; if(info == NULL || info->name == NULL || info->version == NULL) { _alpm_log(db->handle, PM_LOG_DEBUG, "invalid package entry provided to _alpm_local_db_read, skipping\n"); return -1; } if(info->origin != PKG_FROM_LOCALDB) { _alpm_log(db->handle, PM_LOG_DEBUG, "request to read info for a non-local package '%s', skipping...\n", info->name); return -1; } /* bitmask logic here: * infolevel: 00001111 * inforeq: 00010100 * & result: 00000100 * == to inforeq? nope, we need to load more info. */ if((info->infolevel & inforeq) == inforeq) { /* already loaded all of this info, do nothing */ return 0; } _alpm_log(db->handle, PM_LOG_FUNCTION, "loading package data for %s : level=0x%x\n", info->name, inforeq); /* clear out 'line', to be certain - and to make valgrind happy */ memset(line, 0, sizeof(line)); pkgpath = get_pkgpath(db, info); if(access(pkgpath, F_OK)) { /* directory doesn't exist or can't be opened */ _alpm_log(db->handle, PM_LOG_DEBUG, "cannot find '%s-%s' in db '%s'\n", info->name, info->version, db->treename); goto error; } /* DESC */ if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) { snprintf(path, PATH_MAX, "%sdesc", pkgpath); if((fp = fopen(path, "r")) == NULL) { _alpm_log(db->handle, PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); goto error; } while(!feof(fp)) { if(fgets(line, sizeof(line), fp) == NULL) { break; } _alpm_strtrim(line); if(strcmp(line, "%NAME%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } if(strcmp(_alpm_strtrim(line), info->name) != 0) { _alpm_log(db->handle, PM_LOG_ERROR, _("%s database is inconsistent: name " "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%VERSION%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } if(strcmp(_alpm_strtrim(line), info->version) != 0) { _alpm_log(db->handle, PM_LOG_ERROR, _("%s database is inconsistent: version " "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%DESC%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->desc, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%GROUPS%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, line, goto error); info->groups = alpm_list_add(info->groups, linedup); } } else if(strcmp(line, "%URL%") == 0) {
int _alpm_runscriptlet(const char *root, const char *installfn, const char *script, const char *ver, const char *oldver, pmtrans_t *trans) { char scriptfn[PATH_MAX]; char cmdline[PATH_MAX]; char tmpdir[PATH_MAX]; char cwd[PATH_MAX]; char *scriptpath; pid_t pid; int clean_tmpdir = 0; int restore_cwd = 0; int retval = 0; ALPM_LOG_FUNC; if(access(installfn, R_OK)) { /* not found */ _alpm_log(PM_LOG_DEBUG, "scriptlet '%s' not found\n", installfn); return(0); } /* NOTE: popen will use the PARENT's /bin/sh, not the chroot's */ if(access("/bin/sh", X_OK)) { /* not found */ _alpm_log(PM_LOG_ERROR, _("No /bin/sh in parent environment, aborting scriptlet\n")); return(0); } /* creates a directory in $root/tmp/ for copying/extracting the scriptlet */ snprintf(tmpdir, PATH_MAX, "%stmp/", root); if(access(tmpdir, F_OK) != 0) { _alpm_makepath_mode(tmpdir, 01777); } snprintf(tmpdir, PATH_MAX, "%stmp/alpm_XXXXXX", root); if(mkdtemp(tmpdir) == NULL) { _alpm_log(PM_LOG_ERROR, _("could not create temp directory\n")); return(1); } else { clean_tmpdir = 1; } /* either extract or copy the scriptlet */ snprintf(scriptfn, PATH_MAX, "%s/.INSTALL", tmpdir); if(!strcmp(script, "pre_upgrade") || !strcmp(script, "pre_install")) { if(_alpm_unpack(installfn, tmpdir, ".INSTALL")) { retval = 1; } } else { if(_alpm_copyfile(installfn, scriptfn)) { _alpm_log(PM_LOG_ERROR, _("could not copy tempfile to %s (%s)\n"), scriptfn, strerror(errno)); retval = 1; } } if(retval == 1) { goto cleanup; } /* chop off the root so we can find the tmpdir in the chroot */ scriptpath = scriptfn + strlen(root) - 1; if(!grep(scriptfn, script)) { /* script not found in scriptlet file */ goto cleanup; } /* save the cwd so we can restore it later */ if(getcwd(cwd, PATH_MAX) == NULL) { _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n")); } else { restore_cwd = 1; } /* just in case our cwd was removed in the upgrade operation */ if(chdir(root) != 0) { _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), root, strerror(errno)); goto cleanup; } _alpm_log(PM_LOG_DEBUG, "executing %s script...\n", script); if(oldver) { snprintf(cmdline, PATH_MAX, ". %s; %s %s %s", scriptpath, script, ver, oldver); } else { snprintf(cmdline, PATH_MAX, ". %s; %s %s", scriptpath, script, ver); } _alpm_log(PM_LOG_DEBUG, "%s\n", cmdline); /* fork- parent and child each have seperate code blocks below */ pid = fork(); if(pid == -1) { _alpm_log(PM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno)); retval = 1; goto cleanup; } if(pid == 0) { FILE *pipe; /* this code runs for the child only (the actual chroot/exec) */ _alpm_log(PM_LOG_DEBUG, "chrooting in %s\n", root); if(chroot(root) != 0) { _alpm_log(PM_LOG_ERROR, _("could not change the root directory (%s)\n"), strerror(errno)); exit(1); } if(chdir("/") != 0) { _alpm_log(PM_LOG_ERROR, _("could not change directory to / (%s)\n"), strerror(errno)); exit(1); } umask(0022); _alpm_log(PM_LOG_DEBUG, "executing \"%s\"\n", cmdline); /* execl("/bin/sh", "sh", "-c", cmdline, (char *)NULL); */ pipe = popen(cmdline, "r"); if(!pipe) { _alpm_log(PM_LOG_ERROR, _("call to popen failed (%s)"), strerror(errno)); exit(1); } while(!feof(pipe)) { char line[PATH_MAX]; if(fgets(line, PATH_MAX, pipe) == NULL) break; alpm_logaction("%s", line); EVENT(trans, PM_TRANS_EVT_SCRIPTLET_INFO, line, NULL); } retval = pclose(pipe); exit(WEXITSTATUS(retval)); } else { /* this code runs for the parent only (wait on the child) */ pid_t retpid; int status; while((retpid = waitpid(pid, &status, 0)) == -1 && errno == EINTR); if(retpid == -1) { _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno)); retval = 1; goto cleanup; } else { /* check the return status, make sure it is 0 (success) */ if(WIFEXITED(status)) { _alpm_log(PM_LOG_DEBUG, "call to waitpid succeeded\n"); if(WEXITSTATUS(status) != 0) { _alpm_log(PM_LOG_ERROR, _("scriptlet failed to execute correctly\n")); retval = 1; } } } } cleanup: if(clean_tmpdir && _alpm_rmrf(tmpdir)) { _alpm_log(PM_LOG_WARNING, _("could not remove tmpdir %s\n"), tmpdir); } if(restore_cwd) { chdir(cwd); } return(retval); }
int _alpm_runscriptlet(alpm_handle_t *handle, const char *filepath, const char *script, const char *ver, const char *oldver, int is_archive) { char arg0[64], arg1[3], cmdline[PATH_MAX]; char *argv[] = { arg0, arg1, cmdline, NULL }; char *tmpdir, *scriptfn = NULL, *scriptpath; int retval = 0; size_t len; if(_alpm_access(handle, NULL, filepath, R_OK) != 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "scriptlet '%s' not found\n", filepath); return 0; } if(!is_archive && !grep(filepath, script)) { /* script not found in scriptlet file; we can only short-circuit this early * if it is an actual scriptlet file and not an archive. */ return 0; } strcpy(arg0, SCRIPTLET_SHELL); strcpy(arg1, "-c"); /* create a directory in $root/tmp/ for copying/extracting the scriptlet */ len = strlen(handle->root) + strlen("tmp/alpm_XXXXXX") + 1; MALLOC(tmpdir, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); snprintf(tmpdir, len, "%stmp/", handle->root); if(access(tmpdir, F_OK) != 0) { _alpm_makepath_mode(tmpdir, 01777); } snprintf(tmpdir, len, "%stmp/alpm_XXXXXX", handle->root); if(mkdtemp(tmpdir) == NULL) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not create temp directory\n")); free(tmpdir); return 1; } /* either extract or copy the scriptlet */ len += strlen("/.INSTALL"); MALLOC(scriptfn, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); snprintf(scriptfn, len, "%s/.INSTALL", tmpdir); if(is_archive) { if(_alpm_unpack_single(handle, filepath, tmpdir, ".INSTALL")) { retval = 1; } } else { if(_alpm_copyfile(filepath, scriptfn)) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not copy tempfile to %s (%s)\n"), scriptfn, strerror(errno)); retval = 1; } } if(retval == 1) { goto cleanup; } if(is_archive && !grep(scriptfn, script)) { /* script not found in extracted scriptlet file */ goto cleanup; } /* chop off the root so we can find the tmpdir in the chroot */ scriptpath = scriptfn + strlen(handle->root) - 1; if(oldver) { snprintf(cmdline, PATH_MAX, ". %s; %s %s %s", scriptpath, script, ver, oldver); } else { snprintf(cmdline, PATH_MAX, ". %s; %s %s", scriptpath, script, ver); } _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\"\n", cmdline); retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv); cleanup: if(scriptfn && unlink(scriptfn)) { _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove %s\n"), scriptfn); } if(rmdir(tmpdir)) { _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove tmpdir %s\n"), tmpdir); } free(scriptfn); free(tmpdir); return retval; }
static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq) { FILE *fp = NULL; char line[1024]; alpm_db_t *db = info->origin_data.db; /* bitmask logic here: * infolevel: 00001111 * inforeq: 00010100 * & result: 00000100 * == to inforeq? nope, we need to load more info. */ if((info->infolevel & inforeq) == inforeq) { /* already loaded all of this info, do nothing */ return 0; } if(info->infolevel & INFRQ_ERROR) { /* We've encountered an error loading this package before. Don't attempt * repeated reloads, just give up. */ return -1; } _alpm_log(db->handle, ALPM_LOG_FUNCTION, "loading package data for %s : level=0x%x\n", info->name, inforeq); /* clear out 'line', to be certain - and to make valgrind happy */ memset(line, 0, sizeof(line)); /* DESC */ if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) { char *path = _alpm_local_db_pkgpath(db, info, "desc"); if(!path || (fp = fopen(path, "r")) == NULL) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); free(path); goto error; } free(path); while(!feof(fp)) { if(safe_fgets(line, sizeof(line), fp) == NULL && !feof(fp)) { goto error; } if(_alpm_strip_newline(line, 0) == 0) { /* length of stripped line was zero */ continue; } if(strcmp(line, "%NAME%") == 0) { READ_NEXT(); if(strcmp(line, info->name) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: name " "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%VERSION%") == 0) { READ_NEXT(); if(strcmp(line, info->version) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: version " "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%BASE%") == 0) { READ_AND_STORE(info->base); } else if(strcmp(line, "%DESC%") == 0) { READ_AND_STORE(info->desc); } else if(strcmp(line, "%GROUPS%") == 0) { READ_AND_STORE_ALL(info->groups); } else if(strcmp(line, "%URL%") == 0) { READ_AND_STORE(info->url); } else if(strcmp(line, "%LICENSE%") == 0) { READ_AND_STORE_ALL(info->licenses); } else if(strcmp(line, "%ARCH%") == 0) { READ_AND_STORE(info->arch); } else if(strcmp(line, "%BUILDDATE%") == 0) { READ_NEXT(); info->builddate = _alpm_parsedate(line); } else if(strcmp(line, "%INSTALLDATE%") == 0) { READ_NEXT(); info->installdate = _alpm_parsedate(line); } else if(strcmp(line, "%PACKAGER%") == 0) { READ_AND_STORE(info->packager); } else if(strcmp(line, "%REASON%") == 0) { READ_NEXT(); info->reason = (alpm_pkgreason_t)atoi(line); } else if(strcmp(line, "%VALIDATION%") == 0) { alpm_list_t *i, *v = NULL; READ_AND_STORE_ALL(v); for(i = v; i; i = alpm_list_next(i)) { if(strcmp(i->data, "none") == 0) { info->validation |= ALPM_PKG_VALIDATION_NONE; } else if(strcmp(i->data, "md5") == 0) { info->validation |= ALPM_PKG_VALIDATION_MD5SUM; } else if(strcmp(i->data, "sha256") == 0) { info->validation |= ALPM_PKG_VALIDATION_SHA256SUM; } else if(strcmp(i->data, "pgp") == 0) { info->validation |= ALPM_PKG_VALIDATION_SIGNATURE; } else { _alpm_log(db->handle, ALPM_LOG_WARNING, _("unknown validation type for package %s: %s\n"), info->name, (const char *)i->data); } } FREELIST(v); } else if(strcmp(line, "%SIZE%") == 0) { READ_NEXT(); info->isize = _alpm_strtoofft(line); } else if(strcmp(line, "%REPLACES%") == 0) { READ_AND_SPLITDEP(info->replaces); } else if(strcmp(line, "%DEPENDS%") == 0) { READ_AND_SPLITDEP(info->depends); } else if(strcmp(line, "%OPTDEPENDS%") == 0) { READ_AND_SPLITDEP(info->optdepends); } else if(strcmp(line, "%CONFLICTS%") == 0) { READ_AND_SPLITDEP(info->conflicts); } else if(strcmp(line, "%PROVIDES%") == 0) { READ_AND_SPLITDEP(info->provides); } } fclose(fp); fp = NULL; info->infolevel |= INFRQ_DESC; } /* FILES */ if(inforeq & INFRQ_FILES && !(info->infolevel & INFRQ_FILES)) { char *path = _alpm_local_db_pkgpath(db, info, "files"); if(!path || (fp = fopen(path, "r")) == NULL) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); free(path); goto error; } free(path); while(safe_fgets(line, sizeof(line), fp)) { _alpm_strip_newline(line, 0); if(strcmp(line, "%FILES%") == 0) { size_t files_count = 0, files_size = 0, len; alpm_file_t *files = NULL; while(safe_fgets(line, sizeof(line), fp) && (len = _alpm_strip_newline(line, 0))) { if(!_alpm_greedy_grow((void **)&files, &files_size, (files_count ? (files_count + 1) * sizeof(alpm_file_t) : 8 * sizeof(alpm_file_t)))) { goto error; } /* since we know the length of the file string already, * we can do malloc + memcpy rather than strdup */ len += 1; MALLOC(files[files_count].name, len, goto error); memcpy(files[files_count].name, line, len); files_count++; } /* attempt to hand back any memory we don't need */ if(files_count > 0) { files = realloc(files, sizeof(alpm_file_t) * files_count); /* make sure the list is sorted */ qsort(files, files_count, sizeof(alpm_file_t), _alpm_files_cmp); } else { FREE(files); } info->files.count = files_count; info->files.files = files; } else if(strcmp(line, "%BACKUP%") == 0) {
/* Re-order a list of target packages with respect to their dependencies. * * Example (reverse == 0): * A depends on C * B depends on A * Target order is A,B,C,D * * Should be re-ordered to C,A,B,D * * if reverse is > 0, the dependency order will be reversed. * * This function returns the new alpm_list_t* target list. * */ alpm_list_t *_alpm_sortbydeps(alpm_handle_t *handle, alpm_list_t *targets, int reverse) { alpm_list_t *newtargs = NULL; alpm_list_t *vertices = NULL; alpm_list_t *vptr; alpm_graph_t *vertex; if(targets == NULL) { return NULL; } _alpm_log(handle, ALPM_LOG_DEBUG, "started sorting dependencies\n"); vertices = dep_graph_init(targets); vptr = vertices; vertex = vertices->data; while(vptr) { /* mark that we touched the vertex */ vertex->state = -1; int found = 0; while(vertex->childptr && !found) { alpm_graph_t *nextchild = vertex->childptr->data; vertex->childptr = vertex->childptr->next; if(nextchild->state == 0) { found = 1; nextchild->parent = vertex; vertex = nextchild; } else if(nextchild->state == -1) { alpm_pkg_t *vertexpkg = vertex->data; alpm_pkg_t *childpkg = nextchild->data; _alpm_log(handle, ALPM_LOG_WARNING, _("dependency cycle detected:\n")); if(reverse) { _alpm_log(handle, ALPM_LOG_WARNING, _("%s will be removed after its %s dependency\n"), vertexpkg->name, childpkg->name); } else { _alpm_log(handle, ALPM_LOG_WARNING, _("%s will be installed before its %s dependency\n"), vertexpkg->name, childpkg->name); } } } if(!found) { newtargs = alpm_list_add(newtargs, vertex->data); /* mark that we've left this vertex */ vertex->state = 1; vertex = vertex->parent; if(!vertex) { vptr = vptr->next; while(vptr) { vertex = vptr->data; if(vertex->state == 0) break; vptr = vptr->next; } } } } _alpm_log(handle, ALPM_LOG_DEBUG, "sorting dependencies finished\n"); if(reverse) { /* reverse the order */ alpm_list_t *tmptargs = alpm_list_reverse(newtargs); /* free the old one */ alpm_list_free(newtargs); newtargs = tmptargs; } alpm_list_free_inner(vertices, _alpm_graph_free); alpm_list_free(vertices); return newtargs; }
static int curl_download_internal(struct dload_payload *payload, const char *localpath, char **final_file, char **final_url) { int ret = -1; FILE *localf = NULL; char *effective_url; char hostname[HOSTNAME_SIZE]; char error_buffer[CURL_ERROR_SIZE] = {0}; struct stat st; long timecond, remote_time = -1; double remote_size, bytes_dl; struct sigaction orig_sig_pipe, orig_sig_int; /* shortcut to our handle within the payload */ alpm_handle_t *handle = payload->handle; CURL *curl = get_libcurl_handle(handle); handle->pm_errno = 0; /* make sure these are NULL */ FREE(payload->tempfile_name); FREE(payload->destfile_name); FREE(payload->content_disp_name); payload->tempfile_openmode = "wb"; if(!payload->remote_name) { STRDUP(payload->remote_name, get_filename(payload->fileurl), RET_ERR(handle, ALPM_ERR_MEMORY, -1)); } if(curl_gethost(payload->fileurl, hostname, sizeof(hostname)) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("url '%s' is invalid\n"), payload->fileurl); RET_ERR(handle, ALPM_ERR_SERVER_BAD_URL, -1); } if(payload->remote_name && strlen(payload->remote_name) > 0 && strcmp(payload->remote_name, ".sig") != 0) { payload->destfile_name = get_fullpath(localpath, payload->remote_name, ""); payload->tempfile_name = get_fullpath(localpath, payload->remote_name, ".part"); if(!payload->destfile_name || !payload->tempfile_name) { goto cleanup; } } else { /* URL doesn't contain a filename, so make a tempfile. We can't support * resuming this kind of download; partial transfers will be destroyed */ payload->unlink_on_fail = 1; localf = create_tempfile(payload, localpath); if(localf == NULL) { goto cleanup; } } curl_set_handle_opts(payload, curl, error_buffer); if(localf == NULL) { localf = fopen(payload->tempfile_name, payload->tempfile_openmode); if(localf == NULL) { handle->pm_errno = ALPM_ERR_RETRIEVE; _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), payload->tempfile_name, strerror(errno)); goto cleanup; } } _alpm_log(handle, ALPM_LOG_DEBUG, "opened tempfile for download: %s (%s)\n", payload->tempfile_name, payload->tempfile_openmode); curl_easy_setopt(curl, CURLOPT_WRITEDATA, localf); /* Ignore any SIGPIPE signals. With libcurl, these shouldn't be happening, * but better safe than sorry. Store the old signal handler first. */ mask_signal(SIGPIPE, SIG_IGN, &orig_sig_pipe); mask_signal(SIGINT, &inthandler, &orig_sig_int); /* perform transfer */ payload->curlerr = curl_easy_perform(curl); _alpm_log(handle, ALPM_LOG_DEBUG, "curl returned error %d from transfer\n", payload->curlerr); /* disconnect relationships from the curl handle for things that might go out * of scope, but could still be touched on connection teardown. This really * only applies to FTP transfers. */ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *)NULL); /* was it a success? */ switch(payload->curlerr) { case CURLE_OK: /* get http/ftp response code */ _alpm_log(handle, ALPM_LOG_DEBUG, "response code: %ld\n", payload->respcode); if(payload->respcode >= 400) { payload->unlink_on_fail = 1; /* non-translated message is same as libcurl */ snprintf(error_buffer, sizeof(error_buffer), "The requested URL returned error: %ld", payload->respcode); _alpm_log(handle, ALPM_LOG_ERROR, _("failed retrieving file '%s' from %s : %s\n"), payload->remote_name, hostname, error_buffer); goto cleanup; } break; case CURLE_ABORTED_BY_CALLBACK: /* handle the interrupt accordingly */ if(dload_interrupted == ABORT_OVER_MAXFILESIZE) { payload->curlerr = CURLE_FILESIZE_EXCEEDED; handle->pm_errno = ALPM_ERR_LIBCURL; /* use the 'size exceeded' message from libcurl */ _alpm_log(handle, ALPM_LOG_ERROR, _("failed retrieving file '%s' from %s : %s\n"), payload->remote_name, hostname, curl_easy_strerror(CURLE_FILESIZE_EXCEEDED)); } goto cleanup; default: /* delete zero length downloads */ if(fstat(fileno(localf), &st) == 0 && st.st_size == 0) { payload->unlink_on_fail = 1; } if(!payload->errors_ok) { handle->pm_errno = ALPM_ERR_LIBCURL; _alpm_log(handle, ALPM_LOG_ERROR, _("failed retrieving file '%s' from %s : %s\n"), payload->remote_name, hostname, error_buffer); } else { _alpm_log(handle, ALPM_LOG_DEBUG, "failed retrieving file '%s' from %s : %s\n", payload->remote_name, hostname, error_buffer); } goto cleanup; } /* retrieve info about the state of the transfer */ curl_easy_getinfo(curl, CURLINFO_FILETIME, &remote_time); curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &remote_size); curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &bytes_dl); curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &timecond); curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url); if(final_url != NULL) { *final_url = effective_url; } /* time condition was met and we didn't download anything. we need to * clean up the 0 byte .part file that's left behind. */ if(timecond == 1 && DOUBLE_EQ(bytes_dl, 0)) { _alpm_log(handle, ALPM_LOG_DEBUG, "file met time condition\n"); ret = 1; unlink(payload->tempfile_name); goto cleanup; } /* remote_size isn't necessarily the full size of the file, just what the * server reported as remaining to download. compare it to what curl reported * as actually being transferred during curl_easy_perform() */ if(!DOUBLE_EQ(remote_size, -1) && !DOUBLE_EQ(bytes_dl, -1) && !DOUBLE_EQ(bytes_dl, remote_size)) { handle->pm_errno = ALPM_ERR_RETRIEVE; _alpm_log(handle, ALPM_LOG_ERROR, _("%s appears to be truncated: %jd/%jd bytes\n"), payload->remote_name, (intmax_t)bytes_dl, (intmax_t)remote_size); goto cleanup; } if(payload->trust_remote_name) { if(payload->content_disp_name) { /* content-disposition header has a better name for our file */ free(payload->destfile_name); payload->destfile_name = get_fullpath(localpath, payload->content_disp_name, ""); } else { const char *effective_filename = strrchr(effective_url, '/'); if(effective_filename && strlen(effective_filename) > 2) { effective_filename++; /* if destfile was never set, we wrote to a tempfile. even if destfile is * set, we may have followed some redirects and the effective url may * have a better suggestion as to what to name our file. in either case, * refactor destfile to this newly derived name. */ if(!payload->destfile_name || strcmp(effective_filename, strrchr(payload->destfile_name, '/') + 1) != 0) { free(payload->destfile_name); payload->destfile_name = get_fullpath(localpath, effective_filename, ""); } } } } ret = 0; cleanup: if(localf != NULL) { fclose(localf); utimes_long(payload->tempfile_name, remote_time); } if(ret == 0) { const char *realname = payload->tempfile_name; if(payload->destfile_name) { realname = payload->destfile_name; if(rename(payload->tempfile_name, payload->destfile_name)) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"), payload->tempfile_name, payload->destfile_name, strerror(errno)); ret = -1; } } if(ret != -1 && final_file) { STRDUP(*final_file, strrchr(realname, '/') + 1, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); } } if((ret == -1 || dload_interrupted) && payload->unlink_on_fail && payload->tempfile_name) { unlink(payload->tempfile_name); } /* restore the old signal handlers */ unmask_signal(SIGINT, &orig_sig_int); unmask_signal(SIGPIPE, &orig_sig_pipe); /* if we were interrupted, trip the old handler */ if(dload_interrupted) { raise(SIGINT); } return ret; }
/** Checks dependencies and returns missing ones in a list. * Dependencies can include versions with depmod operators. * @param handle the context handle * @param pkglist the list of local packages * @param remove an alpm_list_t* of packages to be removed * @param upgrade an alpm_list_t* of packages to be upgraded (remove-then-upgrade) * @param reversedeps handles the backward dependencies * @return an alpm_list_t* of alpm_depmissing_t pointers. */ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_handle_t *handle, alpm_list_t *pkglist, alpm_list_t *rem, alpm_list_t *upgrade, int reversedeps) { alpm_list_t *i, *j; alpm_list_t *dblist = NULL, *modified = NULL; alpm_list_t *baddeps = NULL; int nodepversion; CHECK_HANDLE(handle, return NULL); for(i = pkglist; i; i = i->next) { alpm_pkg_t *pkg = i->data; if(alpm_pkg_find(rem, pkg->name) || alpm_pkg_find(upgrade, pkg->name)) { modified = alpm_list_add(modified, pkg); } else { dblist = alpm_list_add(dblist, pkg); } } nodepversion = no_dep_version(handle); /* look for unsatisfied dependencies of the upgrade list */ for(i = upgrade; i; i = i->next) { alpm_pkg_t *tp = i->data; _alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: package %s-%s\n", tp->name, tp->version); for(j = alpm_pkg_get_depends(tp); j; j = j->next) { alpm_depend_t *depend = j->data; depend = filtered_depend(depend, nodepversion); /* 1. we check the upgrade list */ /* 2. we check database for untouched satisfying packages */ if(!find_dep_satisfier(upgrade, depend) && !find_dep_satisfier(dblist, depend)) { /* Unsatisfied dependency in the upgrade list */ alpm_depmissing_t *miss; char *missdepstring = alpm_dep_compute_string(depend); _alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: missing dependency '%s' for package '%s'\n", missdepstring, tp->name); free(missdepstring); miss = depmiss_new(tp->name, depend, NULL); baddeps = alpm_list_add(baddeps, miss); } release_filtered_depend(depend, nodepversion); } } if(reversedeps) { /* reversedeps handles the backwards dependencies, ie, * the packages listed in the requiredby field. */ for(i = dblist; i; i = i->next) { alpm_pkg_t *lp = i->data; for(j = alpm_pkg_get_depends(lp); j; j = j->next) { alpm_depend_t *depend = j->data; depend = filtered_depend(depend, nodepversion); alpm_pkg_t *causingpkg = find_dep_satisfier(modified, depend); /* we won't break this depend, if it is already broken, we ignore it */ /* 1. check upgrade list for satisfiers */ /* 2. check dblist for satisfiers */ if(causingpkg && !find_dep_satisfier(upgrade, depend) && !find_dep_satisfier(dblist, depend)) { alpm_depmissing_t *miss; char *missdepstring = alpm_dep_compute_string(depend); _alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: transaction would break '%s' dependency of '%s'\n", missdepstring, lp->name); free(missdepstring); miss = depmiss_new(lp->name, depend, causingpkg->name); baddeps = alpm_list_add(baddeps, miss); } release_filtered_depend(depend, nodepversion); } } } alpm_list_free(modified); alpm_list_free(dblist); return baddeps; }
/** * @brief Transaction preparation for remove actions. * * This functions takes a pointer to a alpm_list_t which will be * filled with a list of alpm_depmissing_t* objects representing * the packages blocking the transaction. * * @param handle the context handle * @param data a pointer to an alpm_list_t* to fill * * @return 0 on success, -1 on error */ int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data) { alpm_list_t *lp; alpm_trans_t *trans = handle->trans; alpm_db_t *db = handle->db_local; if((trans->flags & ALPM_TRANS_FLAG_RECURSE) && !(trans->flags & ALPM_TRANS_FLAG_CASCADE)) { _alpm_log(handle, ALPM_LOG_DEBUG, "finding removable dependencies\n"); if(_alpm_recursedeps(db, trans->remove, trans->flags & ALPM_TRANS_FLAG_RECURSEALL)) { return -1; } } if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { EVENT(handle, ALPM_EVENT_CHECKDEPS_START, NULL, NULL); _alpm_log(handle, ALPM_LOG_DEBUG, "looking for unsatisfied dependencies\n"); lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(db), trans->remove, NULL, 1); if(lp != NULL) { if(trans->flags & ALPM_TRANS_FLAG_CASCADE) { if(remove_prepare_cascade(handle, lp)) { return -1; } } else if(trans->flags & ALPM_TRANS_FLAG_UNNEEDED) { /* Remove needed packages (which would break dependencies) * from target list */ remove_prepare_keep_needed(handle, lp); } else { if(data) { *data = lp; } else { alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); alpm_list_free(lp); } RET_ERR(handle, ALPM_ERR_UNSATISFIED_DEPS, -1); } } } /* re-order w.r.t. dependencies */ _alpm_log(handle, ALPM_LOG_DEBUG, "sorting by dependencies\n"); lp = _alpm_sortbydeps(handle, trans->remove, 1); /* free the old alltargs */ alpm_list_free(trans->remove); trans->remove = lp; /* -Rcs == -Rc then -Rs */ if((trans->flags & ALPM_TRANS_FLAG_CASCADE) && (trans->flags & ALPM_TRANS_FLAG_RECURSE)) { _alpm_log(handle, ALPM_LOG_DEBUG, "finding removable dependencies\n"); if(_alpm_recursedeps(db, trans->remove, trans->flags & ALPM_TRANS_FLAG_RECURSEALL)) { return -1; } } /* Note packages being removed that are optdepends for installed packages */ remove_notify_needed_optdepends(handle, trans->remove); if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { EVENT(handle, ALPM_EVENT_CHECKDEPS_DONE, NULL, NULL); } return 0; }
/** Update a package database * * An update of the package database \a db will be attempted. Unless * \a force is true, the update will only be performed if the remote * database was modified since the last update. * * This operation requires a database lock, and will return an applicable error * if the lock could not be obtained. * * Example: * @code * alpm_list_t *syncs = alpm_get_syncdbs(); * for(i = syncs; i; i = alpm_list_next(i)) { * alpm_db_t *db = alpm_list_getdata(i); * result = alpm_db_update(0, db); * * if(result < 0) { * printf("Unable to update database: %s\n", alpm_strerrorlast()); * } else if(result == 1) { * printf("Database already up to date\n"); * } else { * printf("Database updated\n"); * } * } * @endcode * * @ingroup alpm_databases * @note After a successful update, the \link alpm_db_get_pkgcache() * package cache \endlink will be invalidated * @param force if true, then forces the update, otherwise update only in case * the database isn't up to date * @param db pointer to the package database to update * @return 0 on success, -1 on error (pm_errno is set accordingly), 1 if up to * to date */ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db) { char *syncpath; alpm_list_t *i; int ret = -1; mode_t oldmask; alpm_handle_t *handle; alpm_siglevel_t level; /* Sanity checks */ ASSERT(db != NULL, return -1); handle = db->handle; handle->pm_errno = 0; ASSERT(db != handle->db_local, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1)); ASSERT(db->servers != NULL, RET_ERR(handle, ALPM_ERR_SERVER_NONE, -1)); syncpath = get_sync_dir(handle); if(!syncpath) { return -1; } /* make sure we have a sane umask */ oldmask = umask(0022); level = alpm_db_get_siglevel(db); /* attempt to grab a lock */ if(_alpm_handle_lock(handle)) { free(syncpath); umask(oldmask); RET_ERR(handle, ALPM_ERR_HANDLE_LOCK, -1); } for(i = db->servers; i; i = i->next) { const char *server = i->data; struct dload_payload payload; size_t len; int sig_ret = 0; memset(&payload, 0, sizeof(struct dload_payload)); /* set hard upper limit of 25MiB */ payload.max_size = 25 * 1024 * 1024; /* print server + filename into a buffer */ len = strlen(server) + strlen(db->treename) + 5; /* TODO fix leak syncpath and umask unset */ MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); snprintf(payload.fileurl, len, "%s/%s.db", server, db->treename); payload.handle = handle; payload.force = force; payload.unlink_on_fail = 1; ret = _alpm_download(&payload, syncpath, NULL); _alpm_dload_payload_reset(&payload); if(ret == 0 && (level & ALPM_SIG_DATABASE)) { /* an existing sig file is no good at this point */ char *sigpath = _alpm_sigpath(handle, _alpm_db_path(db)); if(!sigpath) { ret = -1; break; } unlink(sigpath); free(sigpath); /* if we downloaded a DB, we want the .sig from the same server */ /* print server + filename into a buffer (leave space for .sig) */ len = strlen(server) + strlen(db->treename) + 9; /* TODO fix leak syncpath and umask unset */ MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); snprintf(payload.fileurl, len, "%s/%s.db.sig", server, db->treename); payload.handle = handle; payload.force = 1; payload.errors_ok = (level & ALPM_SIG_DATABASE_OPTIONAL); /* set hard upper limit of 16KiB */ payload.max_size = 16 * 1024; sig_ret = _alpm_download(&payload, syncpath, NULL); /* errors_ok suppresses error messages, but not the return code */ sig_ret = payload.errors_ok ? 0 : sig_ret; _alpm_dload_payload_reset(&payload); } if(ret != -1 && sig_ret != -1) { break; } } if(ret == 1) { /* files match, do nothing */ handle->pm_errno = 0; goto cleanup; } else if(ret == -1) { /* pm_errno was set by the download code */ _alpm_log(handle, ALPM_LOG_DEBUG, "failed to sync db: %s\n", alpm_strerror(handle->pm_errno)); goto cleanup; } /* Cache needs to be rebuilt */ _alpm_db_free_pkgcache(db); /* clear all status flags regarding validity/existence */ db->status &= ~DB_STATUS_VALID; db->status &= ~DB_STATUS_INVALID; db->status &= ~DB_STATUS_EXISTS; db->status &= ~DB_STATUS_MISSING; if(sync_db_validate(db)) { /* pm_errno should be set */ ret = -1; } cleanup: if(_alpm_handle_unlock(handle)) { _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove lock file %s\n"), handle->lockfile); } free(syncpath); umask(oldmask); return ret; }
/** * @brief Unlink a package file, backing it up if necessary. * * @param handle the context handle * @param oldpkg the package being removed * @param newpkg the package replacing \a oldpkg * @param fileobj file to remove * @param skip_remove list of files that shouldn't be removed * @param nosave whether files should be backed up * * @return 0 on success, -1 if there was an error unlinking the file, 1 if the * file was skipped or did not exist */ static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg, const alpm_file_t *fileobj, alpm_list_t *skip_remove, int nosave) { struct stat buf; char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s%s", handle->root, fileobj->name); /* check the remove skip list before removing the file. * see the big comment block in db_find_fileconflicts() for an * explanation. */ if(alpm_list_find(skip_remove, fileobj->name, _alpm_fnmatch)) { _alpm_log(handle, ALPM_LOG_DEBUG, "%s is in skip_remove, skipping removal\n", file); return 1; } if(_alpm_lstat(file, &buf)) { _alpm_log(handle, ALPM_LOG_DEBUG, "file %s does not exist\n", file); return 1; } if(S_ISDIR(buf.st_mode)) { ssize_t files = _alpm_files_in_directory(handle, file, 0); /* if we have files, no need to remove the directory */ if(files > 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s (contains files)\n", file); } else if(files < 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s (could not count files)\n", file); } else if(newpkg && alpm_filelist_contains(alpm_pkg_get_files(newpkg), fileobj->name)) { _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s (in new package)\n", file); } else if(dir_is_mountpoint(handle, file, &buf)) { _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s (mountpoint)\n", file); } else { /* one last check- does any other package own this file? */ alpm_list_t *local, *local_pkgs; int found = 0; local_pkgs = _alpm_db_get_pkgcache(handle->db_local); for(local = local_pkgs; local && !found; local = local->next) { alpm_pkg_t *local_pkg = local->data; alpm_filelist_t *filelist; /* we duplicated the package when we put it in the removal list, so we * so we can't use direct pointer comparison here. */ if(oldpkg->name_hash == local_pkg->name_hash && strcmp(oldpkg->name, local_pkg->name) == 0) { continue; } filelist = alpm_pkg_get_files(local_pkg); if(alpm_filelist_contains(filelist, fileobj->name)) { _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s (owned by %s)\n", file, local_pkg->name); found = 1; } } if(!found) { if(rmdir(file)) { _alpm_log(handle, ALPM_LOG_DEBUG, "directory removal of %s failed: %s\n", file, strerror(errno)); return -1; } else { _alpm_log(handle, ALPM_LOG_DEBUG, "removed directory %s (no remaining owners)\n", file); } } } } else { /* if the file needs backup and has been modified, back it up to .pacsave */ alpm_backup_t *backup = _alpm_needbackup(fileobj->name, oldpkg); if(backup) { if(nosave) { _alpm_log(handle, ALPM_LOG_DEBUG, "transaction is set to NOSAVE, not backing up '%s'\n", file); } else { char *filehash = alpm_compute_md5sum(file); int cmp = filehash ? strcmp(filehash, backup->hash) : 0; FREE(filehash); if(cmp != 0) { char *newpath; size_t len = strlen(file) + 8 + 1; MALLOC(newpath, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); shift_pacsave(handle, file); snprintf(newpath, len, "%s.pacsave", file); if(rename(file, newpath)) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"), file, newpath, strerror(errno)); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: could not rename %s to %s (%s)\n", file, newpath, strerror(errno)); free(newpath); return -1; } _alpm_log(handle, ALPM_LOG_WARNING, _("%s saved as %s\n"), file, newpath); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: %s saved as %s\n", file, newpath); free(newpath); return 0; } } } _alpm_log(handle, ALPM_LOG_DEBUG, "unlinking %s\n", file); if(unlink(file) == -1) { _alpm_log(handle, ALPM_LOG_ERROR, _("cannot remove %s (%s)\n"), file, strerror(errno)); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: cannot remove %s (%s)\n", file, strerror(errno)); return -1; } } return 0; }
static int sync_db_populate(alpm_db_t *db) { const char *dbpath; size_t est_count; int count, fd; struct stat buf; struct archive *archive; struct archive_entry *entry; alpm_pkg_t *pkg = NULL; if(db->status & DB_STATUS_INVALID) { RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1); } if(db->status & DB_STATUS_MISSING) { RET_ERR(db->handle, ALPM_ERR_DB_NOT_FOUND, -1); } dbpath = _alpm_db_path(db); if(!dbpath) { /* pm_errno set in _alpm_db_path() */ return -1; } fd = _alpm_open_archive(db->handle, dbpath, &buf, &archive, ALPM_ERR_DB_OPEN); if(fd < 0) { return -1; } est_count = estimate_package_count(&buf, archive); db->pkgcache = _alpm_pkghash_create(est_count); if(db->pkgcache == NULL) { db->handle->pm_errno = ALPM_ERR_MEMORY; count = -1; goto cleanup; } while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { mode_t mode = archive_entry_mode(entry); if(S_ISDIR(mode)) { continue; } else { /* we have desc, depends or deltas - parse it */ if(sync_db_read(db, archive, entry, &pkg) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not parse package description file '%s' from db '%s'\n"), archive_entry_pathname(entry), db->treename); continue; } } } count = alpm_list_count(db->pkgcache->list); if(count > 0) { db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp); } _alpm_log(db->handle, ALPM_LOG_DEBUG, "added %d packages to package cache for db '%s'\n", count, db->treename); cleanup: archive_read_finish(archive); if(fd >= 0) { CLOSE(fd); } return count; }
static alpm_list_t *check_replacers(alpm_handle_t *handle, alpm_pkg_t *lpkg, alpm_db_t *sdb) { /* 2. search for replacers in sdb */ alpm_list_t *replacers = NULL; alpm_list_t *k; _alpm_log(handle, ALPM_LOG_DEBUG, "searching for replacements for %s in %s\n", lpkg->name, sdb->treename); for(k = _alpm_db_get_pkgcache(sdb); k; k = k->next) { int found = 0; alpm_pkg_t *spkg = k->data; alpm_list_t *l; for(l = alpm_pkg_get_replaces(spkg); l; l = l->next) { alpm_depend_t *replace = l->data; /* we only want to consider literal matches at this point. */ if(_alpm_depcmp_literal(lpkg, replace)) { found = 1; break; } } if(found) { int doreplace = 0; alpm_pkg_t *tpkg; /* check IgnorePkg/IgnoreGroup */ if(_alpm_pkg_should_ignore(handle, spkg) || _alpm_pkg_should_ignore(handle, lpkg)) { _alpm_log(handle, ALPM_LOG_WARNING, _("ignoring package replacement (%s-%s => %s-%s)\n"), lpkg->name, lpkg->version, spkg->name, spkg->version); continue; } QUESTION(handle, ALPM_QUESTION_REPLACE_PKG, lpkg, spkg, sdb->treename, &doreplace); if(!doreplace) { continue; } /* If spkg is already in the target list, we append lpkg to spkg's * removes list */ tpkg = alpm_pkg_find(handle->trans->add, spkg->name); if(tpkg) { /* sanity check, multiple repos can contain spkg->name */ if(tpkg->origin_data.db != sdb) { _alpm_log(handle, ALPM_LOG_WARNING, _("cannot replace %s by %s\n"), lpkg->name, spkg->name); continue; } _alpm_log(handle, ALPM_LOG_DEBUG, "appending %s to the removes list of %s\n", lpkg->name, tpkg->name); tpkg->removes = alpm_list_add(tpkg->removes, lpkg); /* check the to-be-replaced package's reason field */ if(alpm_pkg_get_reason(lpkg) == ALPM_PKG_REASON_EXPLICIT) { tpkg->reason = ALPM_PKG_REASON_EXPLICIT; } } else { /* add spkg to the target list */ /* copy over reason */ spkg->reason = alpm_pkg_get_reason(lpkg); spkg->removes = alpm_list_add(NULL, lpkg); _alpm_log(handle, ALPM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n", spkg->name, spkg->version); replacers = alpm_list_add(replacers, spkg); } } } return replacers; }
static int sync_db_read(alpm_db_t *db, struct archive *archive, struct archive_entry *entry, alpm_pkg_t **likely_pkg) { const char *entryname, *filename; alpm_pkg_t *pkg; struct archive_read_buffer buf; entryname = archive_entry_pathname(entry); if(entryname == NULL) { _alpm_log(db->handle, ALPM_LOG_DEBUG, "invalid archive entry provided to _alpm_sync_db_read, skipping\n"); return -1; } _alpm_log(db->handle, ALPM_LOG_FUNCTION, "loading package data from archive entry %s\n", entryname); memset(&buf, 0, sizeof(buf)); /* 512K for a line length seems reasonable */ buf.max_line_size = 512 * 1024; pkg = load_pkg_for_entry(db, entryname, &filename, *likely_pkg); if(pkg == NULL) { _alpm_log(db->handle, ALPM_LOG_DEBUG, "entry %s could not be loaded into %s sync database", entryname, db->treename); return -1; } if(strcmp(filename, "desc") == 0 || strcmp(filename, "depends") == 0 || strcmp(filename, "deltas") == 0) { int ret; while((ret = _alpm_archive_fgets(archive, &buf)) == ARCHIVE_OK) { char *line = buf.line; if(_alpm_strip_newline(line, buf.real_line_size) == 0) { /* length of stripped line was zero */ continue; } if(strcmp(line, "%NAME%") == 0) { READ_NEXT(); if(strcmp(line, pkg->name) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: name " "mismatch on package %s\n"), db->treename, pkg->name); } } else if(strcmp(line, "%VERSION%") == 0) { READ_NEXT(); if(strcmp(line, pkg->version) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: version " "mismatch on package %s\n"), db->treename, pkg->name); } } else if(strcmp(line, "%FILENAME%") == 0) { READ_AND_STORE(pkg->filename); } else if(strcmp(line, "%DESC%") == 0) { READ_AND_STORE(pkg->desc); } else if(strcmp(line, "%GROUPS%") == 0) { READ_AND_STORE_ALL(pkg->groups); } else if(strcmp(line, "%URL%") == 0) { READ_AND_STORE(pkg->url); } else if(strcmp(line, "%LICENSE%") == 0) { READ_AND_STORE_ALL(pkg->licenses); } else if(strcmp(line, "%ARCH%") == 0) { READ_AND_STORE(pkg->arch); } else if(strcmp(line, "%BUILDDATE%") == 0) { READ_NEXT(); pkg->builddate = _alpm_parsedate(line); } else if(strcmp(line, "%PACKAGER%") == 0) { READ_AND_STORE(pkg->packager); } else if(strcmp(line, "%CSIZE%") == 0) { READ_NEXT(); pkg->size = _alpm_strtoofft(line); } else if(strcmp(line, "%ISIZE%") == 0) { READ_NEXT(); pkg->isize = _alpm_strtoofft(line); } else if(strcmp(line, "%MD5SUM%") == 0) { READ_AND_STORE(pkg->md5sum); } else if(strcmp(line, "%SHA256SUM%") == 0) { READ_AND_STORE(pkg->sha256sum); } else if(strcmp(line, "%PGPSIG%") == 0) { READ_AND_STORE(pkg->base64_sig); } else if(strcmp(line, "%REPLACES%") == 0) { READ_AND_SPLITDEP(pkg->replaces); } else if(strcmp(line, "%DEPENDS%") == 0) { READ_AND_SPLITDEP(pkg->depends); } else if(strcmp(line, "%OPTDEPENDS%") == 0) { READ_AND_SPLITDEP(pkg->optdepends); } else if(strcmp(line, "%MAKEDEPENDS%") == 0) { /* currently unused */ while(1) { READ_NEXT(); if(strlen(line) == 0) break; } } else if(strcmp(line, "%CHECKDEPENDS%") == 0) { /* currently unused */ while(1) { READ_NEXT(); if(strlen(line) == 0) break; } } else if(strcmp(line, "%CONFLICTS%") == 0) { READ_AND_SPLITDEP(pkg->conflicts); } else if(strcmp(line, "%PROVIDES%") == 0) { READ_AND_SPLITDEP(pkg->provides); } else if(strcmp(line, "%DELTAS%") == 0) { /* Different than the rest because of the _alpm_delta_parse call. */ while(1) { READ_NEXT(); if(strlen(line) == 0) break; pkg->deltas = alpm_list_add(pkg->deltas, _alpm_delta_parse(db->handle, line)); } } } if(ret != ARCHIVE_EOF) { goto error; } *likely_pkg = pkg; } else if(strcmp(filename, "files") == 0) { /* currently do nothing with this file */ } else { /* unknown database file */ _alpm_log(db->handle, ALPM_LOG_DEBUG, "unknown database file: %s\n", filename); } return 0; error: _alpm_log(db->handle, ALPM_LOG_DEBUG, "error parsing database file: %s\n", filename); return -1; }
int _alpm_sync_prepare(alpm_handle_t *handle, alpm_list_t **data) { alpm_list_t *i, *j; alpm_list_t *deps = NULL; alpm_list_t *unresolvable = NULL; size_t from_sync = 0; int ret = 0; alpm_trans_t *trans = handle->trans; if(data) { *data = NULL; } for(i = trans->add; i; i = i->next) { alpm_pkg_t *spkg = i->data; from_sync += (spkg->origin == ALPM_PKG_FROM_SYNCDB); } /* ensure all sync database are valid if we will be using them */ for(i = handle->dbs_sync; i; i = i->next) { const alpm_db_t *db = i->data; if(db->status & DB_STATUS_INVALID) { RET_ERR(handle, ALPM_ERR_DB_INVALID, -1); } /* missing databases are not allowed if we have sync targets */ if(from_sync && db->status & DB_STATUS_MISSING) { RET_ERR(handle, ALPM_ERR_DB_NOT_FOUND, -1); } } if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { alpm_list_t *resolved = NULL; alpm_list_t *remove = NULL; alpm_list_t *localpkgs; /* Build up list by repeatedly resolving each transaction package */ /* Resolve targets dependencies */ EVENT(handle, ALPM_EVENT_RESOLVEDEPS_START, NULL, NULL); _alpm_log(handle, ALPM_LOG_DEBUG, "resolving target's dependencies\n"); /* build remove list for resolvedeps */ for(i = trans->add; i; i = i->next) { alpm_pkg_t *spkg = i->data; for(j = spkg->removes; j; j = j->next) { remove = alpm_list_add(remove, j->data); } } /* Compute the fake local database for resolvedeps (partial fix for the * phonon/qt issue) */ localpkgs = alpm_list_diff(_alpm_db_get_pkgcache(handle->db_local), trans->add, _alpm_pkg_cmp); /* Resolve packages in the transaction one at a time, in addition building up a list of packages which could not be resolved. */ for(i = trans->add; i; i = i->next) { alpm_pkg_t *pkg = i->data; if(_alpm_resolvedeps(handle, localpkgs, pkg, trans->add, &resolved, remove, data) == -1) { unresolvable = alpm_list_add(unresolvable, pkg); } /* Else, [resolved] now additionally contains [pkg] and all of its dependencies not already on the list */ } alpm_list_free(localpkgs); alpm_list_free(remove); /* If there were unresolvable top-level packages, prompt the user to see if they'd like to ignore them rather than failing the sync */ if(unresolvable != NULL) { int remove_unresolvable = 0; alpm_errno_t saved_err = handle->pm_errno; QUESTION(handle, ALPM_QUESTION_REMOVE_PKGS, unresolvable, NULL, NULL, &remove_unresolvable); if(remove_unresolvable) { /* User wants to remove the unresolvable packages from the transaction. The packages will be removed from the actual transaction when the transaction packages are replaced with a dependency-reordered list below */ handle->pm_errno = 0; if(data) { alpm_list_free_inner(*data, (alpm_list_fn_free)_alpm_depmiss_free); alpm_list_free(*data); *data = NULL; } } else { /* pm_errno was set by resolvedeps, callback may have overwrote it */ handle->pm_errno = saved_err; alpm_list_free(resolved); ret = -1; goto cleanup; } } /* Set DEPEND reason for pulled packages */ for(i = resolved; i; i = i->next) { alpm_pkg_t *pkg = i->data; if(!alpm_pkg_find(trans->add, pkg->name)) { pkg->reason = ALPM_PKG_REASON_DEPEND; } } /* Unresolvable packages will be removed from the target list; set these * aside in the transaction as a list we won't operate on. If we free them * before the end of the transaction, we may kill pointers the frontend * holds to package objects. */ trans->unresolvable = unresolvable; /* re-order w.r.t. dependencies */ alpm_list_free(trans->add); trans->add = _alpm_sortbydeps(handle, resolved, 0); alpm_list_free(resolved); EVENT(handle, ALPM_EVENT_RESOLVEDEPS_DONE, NULL, NULL); } if(!(trans->flags & ALPM_TRANS_FLAG_NOCONFLICTS)) { /* check for inter-conflicts and whatnot */ EVENT(handle, ALPM_EVENT_INTERCONFLICTS_START, NULL, NULL); _alpm_log(handle, ALPM_LOG_DEBUG, "looking for conflicts\n"); /* 1. check for conflicts in the target list */ _alpm_log(handle, ALPM_LOG_DEBUG, "check targets vs targets\n"); deps = _alpm_innerconflicts(handle, trans->add); for(i = deps; i; i = i->next) { alpm_conflict_t *conflict = i->data; alpm_pkg_t *rsync, *sync, *sync1, *sync2; /* have we already removed one of the conflicting targets? */ sync1 = alpm_pkg_find(trans->add, conflict->package1); sync2 = alpm_pkg_find(trans->add, conflict->package2); if(!sync1 || !sync2) { continue; } _alpm_log(handle, ALPM_LOG_DEBUG, "conflicting packages in the sync list: '%s' <-> '%s'\n", conflict->package1, conflict->package2); /* if sync1 provides sync2, we remove sync2 from the targets, and vice versa */ alpm_depend_t *dep1 = _alpm_splitdep(conflict->package1); alpm_depend_t *dep2 = _alpm_splitdep(conflict->package2); if(_alpm_depcmp(sync1, dep2)) { rsync = sync2; sync = sync1; } else if(_alpm_depcmp(sync2, dep1)) { rsync = sync1; sync = sync2; } else { _alpm_log(handle, ALPM_LOG_ERROR, _("unresolvable package conflicts detected\n")); handle->pm_errno = ALPM_ERR_CONFLICTING_DEPS; ret = -1; if(data) { alpm_conflict_t *newconflict = _alpm_conflict_dup(conflict); if(newconflict) { *data = alpm_list_add(*data, newconflict); } } alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free); alpm_list_free(deps); _alpm_dep_free(dep1); _alpm_dep_free(dep2); goto cleanup; } _alpm_dep_free(dep1); _alpm_dep_free(dep2); /* Prints warning */ _alpm_log(handle, ALPM_LOG_WARNING, _("removing '%s' from target list because it conflicts with '%s'\n"), rsync->name, sync->name); trans->add = alpm_list_remove(trans->add, rsync, _alpm_pkg_cmp, NULL); /* rsync is not a transaction target anymore */ trans->unresolvable = alpm_list_add(trans->unresolvable, rsync); continue; } alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free); alpm_list_free(deps); deps = NULL; /* 2. we check for target vs db conflicts (and resolve)*/ _alpm_log(handle, ALPM_LOG_DEBUG, "check targets vs db and db vs targets\n"); deps = _alpm_outerconflicts(handle->db_local, trans->add); for(i = deps; i; i = i->next) { alpm_conflict_t *conflict = i->data; /* if conflict->package2 (the local package) is not elected for removal, we ask the user */ int found = 0; for(j = trans->add; j && !found; j = j->next) { alpm_pkg_t *spkg = j->data; if(alpm_pkg_find(spkg->removes, conflict->package2)) { found = 1; } } if(found) { continue; } _alpm_log(handle, ALPM_LOG_DEBUG, "package '%s' conflicts with '%s'\n", conflict->package1, conflict->package2); alpm_pkg_t *sync = alpm_pkg_find(trans->add, conflict->package1); alpm_pkg_t *local = _alpm_db_get_pkgfromcache(handle->db_local, conflict->package2); int doremove = 0; QUESTION(handle, ALPM_QUESTION_CONFLICT_PKG, conflict->package1, conflict->package2, conflict->reason->name, &doremove); if(doremove) { /* append to the removes list */ _alpm_log(handle, ALPM_LOG_DEBUG, "electing '%s' for removal\n", conflict->package2); sync->removes = alpm_list_add(sync->removes, local); } else { /* abort */ _alpm_log(handle, ALPM_LOG_ERROR, _("unresolvable package conflicts detected\n")); handle->pm_errno = ALPM_ERR_CONFLICTING_DEPS; ret = -1; if(data) { alpm_conflict_t *newconflict = _alpm_conflict_dup(conflict); if(newconflict) { *data = alpm_list_add(*data, newconflict); } } alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free); alpm_list_free(deps); goto cleanup; } } EVENT(handle, ALPM_EVENT_INTERCONFLICTS_DONE, NULL, NULL); alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free); alpm_list_free(deps); } /* Build trans->remove list */ for(i = trans->add; i; i = i->next) { alpm_pkg_t *spkg = i->data; for(j = spkg->removes; j; j = j->next) { alpm_pkg_t *rpkg = j->data; if(!alpm_pkg_find(trans->remove, rpkg->name)) { alpm_pkg_t *copy; _alpm_log(handle, ALPM_LOG_DEBUG, "adding '%s' to remove list\n", rpkg->name); if(_alpm_pkg_dup(rpkg, ©) == -1) { return -1; } trans->remove = alpm_list_add(trans->remove, copy); } } } if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { _alpm_log(handle, ALPM_LOG_DEBUG, "checking dependencies\n"); deps = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local), trans->remove, trans->add, 1); if(deps) { handle->pm_errno = ALPM_ERR_UNSATISFIED_DEPS; ret = -1; if(data) { *data = deps; } else { alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_depmiss_free); alpm_list_free(deps); } goto cleanup; } } for(i = trans->add; i; i = i->next) { /* update download size field */ alpm_pkg_t *spkg = i->data; if(compute_download_size(spkg) < 0) { ret = -1; goto cleanup; } } cleanup: return ret; }
/** Unpack a list of files in an archive. * @param handle the context handle * @param path the archive to unpack * @param prefix where to extract the files * @param list a list of files within the archive to unpack or NULL for all * @param breakfirst break after the first entry found * @return 0 on success, 1 on failure */ int _alpm_unpack(alpm_handle_t *handle, const char *path, const char *prefix, alpm_list_t *list, int breakfirst) { int ret = 0; mode_t oldmask; struct archive *archive; struct archive_entry *entry; struct stat buf; int fd, cwdfd; fd = _alpm_open_archive(handle, path, &buf, &archive, ALPM_ERR_PKG_OPEN); if(fd < 0) { return 1; } oldmask = umask(0022); /* save the cwd so we can restore it later */ OPEN(cwdfd, ".", O_RDONLY); if(cwdfd < 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); } /* just in case our cwd was removed in the upgrade operation */ if(chdir(prefix) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), prefix, strerror(errno)); ret = 1; goto cleanup; } while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { const char *entryname; mode_t mode; entryname = archive_entry_pathname(entry); /* If specific files were requested, skip entries that don't match. */ if(list) { char *entry_prefix = strdup(entryname); char *p = strstr(entry_prefix,"/"); if(p) { *(p+1) = '\0'; } char *found = alpm_list_find_str(list, entry_prefix); free(entry_prefix); if(!found) { if(archive_read_data_skip(archive) != ARCHIVE_OK) { ret = 1; goto cleanup; } continue; } else { _alpm_log(handle, ALPM_LOG_DEBUG, "extracting: %s\n", entryname); } } mode = archive_entry_mode(entry); if(S_ISREG(mode)) { archive_entry_set_perm(entry, 0644); } else if(S_ISDIR(mode)) { archive_entry_set_perm(entry, 0755); } /* Extract the archive entry. */ int readret = archive_read_extract(archive, entry, 0); if(readret == ARCHIVE_WARN) { /* operation succeeded but a non-critical error was encountered */ _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"), entryname, archive_error_string(archive)); } else if(readret != ARCHIVE_OK) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"), entryname, archive_error_string(archive)); ret = 1; goto cleanup; } if(breakfirst) { break; } } cleanup: umask(oldmask); archive_read_finish(archive); CLOSE(fd); if(cwdfd >= 0) { if(fchdir(cwdfd) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"), strerror(errno)); } CLOSE(cwdfd); } return ret; }
static int local_db_populate(alpm_db_t *db) { size_t est_count; int count = 0; struct stat buf; struct dirent *ent = NULL; const char *dbpath; DIR *dbdir; if(db->status & DB_STATUS_INVALID) { RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1); } /* note: DB_STATUS_MISSING is not fatal for local database */ dbpath = _alpm_db_path(db); if(dbpath == NULL) { /* pm_errno set in _alpm_db_path() */ return -1; } dbdir = opendir(dbpath); if(dbdir == NULL) { if(errno == ENOENT) { /* no database existing yet is not an error */ db->status &= ~DB_STATUS_EXISTS; db->status |= DB_STATUS_MISSING; return 0; } RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1); } if(fstat(dirfd(dbdir), &buf) != 0) { RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1); } db->status |= DB_STATUS_EXISTS; db->status &= ~DB_STATUS_MISSING; if(buf.st_nlink >= 2) { est_count = buf.st_nlink; } else { /* Some filesystems don't subscribe to the two-implicit links school of * thought, e.g. BTRFS, HFS+. See * http://kerneltrap.org/mailarchive/linux-btrfs/2010/1/23/6723483/thread */ est_count = 0; while(readdir(dbdir) != NULL) { est_count++; } rewinddir(dbdir); } if(est_count >= 2) { /* subtract the two extra pointers to get # of children */ est_count -= 2; } /* initialize hash at 50% full */ db->pkgcache = _alpm_pkghash_create(est_count * 2); if(db->pkgcache == NULL){ closedir(dbdir); RET_ERR(db->handle, ALPM_ERR_MEMORY, -1); } while((ent = readdir(dbdir)) != NULL) { const char *name = ent->d_name; alpm_pkg_t *pkg; if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } if(!is_dir(dbpath, ent)) { continue; } pkg = _alpm_pkg_new(); if(pkg == NULL) { closedir(dbdir); RET_ERR(db->handle, ALPM_ERR_MEMORY, -1); } /* split the db entry name */ if(_alpm_splitname(name, &(pkg->name), &(pkg->version), &(pkg->name_hash)) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("invalid name for database entry '%s'\n"), name); _alpm_pkg_free(pkg); continue; } /* duplicated database entries are not allowed */ if(_alpm_pkghash_find(db->pkgcache, pkg->name)) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name); _alpm_pkg_free(pkg); continue; } pkg->origin = PKG_FROM_LOCALDB; pkg->origin_data.db = db; pkg->ops = &local_pkg_ops; pkg->handle = db->handle; /* explicitly read with only 'BASE' data, accessors will handle the rest */ if(local_db_read(pkg, INFRQ_BASE) == -1) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("corrupted database entry '%s'\n"), name); _alpm_pkg_free(pkg); continue; } /* add to the collection */ _alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n", pkg->name, db->treename); db->pkgcache = _alpm_pkghash_add(db->pkgcache, pkg); count++; } closedir(dbdir); if(count > 0) { db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp); } _alpm_log(db->handle, ALPM_LOG_DEBUG, "added %d packages to package cache for db '%s'\n", count, db->treename); return count; }
/** Execute a command with arguments in a chroot. * @param handle the context handle * @param cmd command to execute * @param argv arguments to pass to cmd * @return 0 on success, 1 on error */ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) { pid_t pid; int pipefd[2], cwdfd; int retval = 0; /* save the cwd so we can restore it later */ OPEN(cwdfd, ".", O_RDONLY); if(cwdfd < 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); } /* just in case our cwd was removed in the upgrade operation */ if(chdir(handle->root) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), handle->root, strerror(errno)); goto cleanup; } _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", cmd, handle->root); /* Flush open fds before fork() to avoid cloning buffers */ fflush(NULL); if(pipe(pipefd) == -1) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); retval = 1; goto cleanup; } /* fork- parent and child each have seperate code blocks below */ pid = fork(); if(pid == -1) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno)); retval = 1; goto cleanup; } if(pid == 0) { /* this code runs for the child only (the actual chroot/exec) */ CLOSE(1); CLOSE(2); while(dup2(pipefd[1], 1) == -1 && errno == EINTR); while(dup2(pipefd[1], 2) == -1 && errno == EINTR); CLOSE(pipefd[0]); CLOSE(pipefd[1]); /* use fprintf instead of _alpm_log to send output through the parent */ if(chroot(handle->root) != 0) { fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno)); exit(1); } if(chdir("/") != 0) { fprintf(stderr, _("could not change directory to %s (%s)\n"), "/", strerror(errno)); exit(1); } umask(0022); execv(cmd, argv); /* execv only returns if there was an error */ fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno)); exit(1); } else { /* this code runs for the parent only (wait on the child) */ int status; FILE *pipe_file; CLOSE(pipefd[1]); pipe_file = fdopen(pipefd[0], "r"); if(pipe_file == NULL) { CLOSE(pipefd[0]); retval = 1; } else { while(!feof(pipe_file)) { char line[PATH_MAX]; if(fgets(line, PATH_MAX, pipe_file) == NULL) break; alpm_logaction(handle, "%s", line); EVENT(handle, ALPM_EVENT_SCRIPTLET_INFO, line, NULL); } fclose(pipe_file); } while(waitpid(pid, &status, 0) == -1) { if(errno != EINTR) { _alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno)); retval = 1; goto cleanup; } } /* report error from above after the child has exited */ if(retval != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno)); goto cleanup; } /* check the return status, make sure it is 0 (success) */ if(WIFEXITED(status)) { _alpm_log(handle, ALPM_LOG_DEBUG, "call to waitpid succeeded\n"); if(WEXITSTATUS(status) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("command failed to execute correctly\n")); retval = 1; } } } cleanup: if(cwdfd >= 0) { if(fchdir(cwdfd) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"), strerror(errno)); } CLOSE(cwdfd); } return retval; }
static int extract_single_file(alpm_handle_t *handle, struct archive *archive, struct archive_entry *entry, alpm_pkg_t *newpkg, alpm_pkg_t *oldpkg) { const char *entryname = archive_entry_pathname(entry); mode_t entrymode = archive_entry_mode(entry); alpm_backup_t *backup = _alpm_needbackup(entryname, newpkg); char filename[PATH_MAX]; /* the actual file we're extracting */ int needbackup = 0, notouch = 0; const char *hash_orig = NULL; int isnewfile = 0, errors = 0; struct stat lsbuf; size_t filename_len; if(*entryname == '.') { return extract_db_file(handle, archive, entry, newpkg, entryname); } if (!alpm_filelist_contains(&newpkg->files, entryname)) { _alpm_log(handle, ALPM_LOG_WARNING, _("file not found in file list for package %s. skipping extraction of %s\n"), newpkg->name, entryname); return 0; } /* build the new entryname relative to handle->root */ filename_len = snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname); if(filename_len >= PATH_MAX) { _alpm_log(handle, ALPM_LOG_ERROR, _("unable to extract %s%s: path too long"), handle->root, entryname); return 1; } /* if a file is in NoExtract then we never extract it */ if(_alpm_fnmatch_patterns(handle->noextract, entryname) == 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "%s is in NoExtract," " skipping extraction of %s\n", entryname, filename); archive_read_data_skip(archive); return 0; } /* Check for file existence. This is one of the more crucial parts * to get 'right'. Here are the possibilities, with the filesystem * on the left and the package on the top: * (F=file, N=node, S=symlink, D=dir) * | F/N | D * non-existent | 1 | 2 * F/N | 3 | 4 * D | 5 | 6 * * 1,2- extract, no magic necessary. lstat (llstat) will fail here. * 3,4- conflict checks should have caught this. either overwrite * or backup the file. * 5- file replacing directory- don't allow it. * 6- skip extraction, dir already exists. */ isnewfile = llstat(filename, &lsbuf) != 0; if(isnewfile) { /* cases 1,2: file doesn't exist, skip all backup checks */ } else if(S_ISDIR(lsbuf.st_mode) && S_ISDIR(entrymode)) { #if 0 uid_t entryuid = archive_entry_uid(entry); gid_t entrygid = archive_entry_gid(entry); #endif /* case 6: existing dir, ignore it */ if(lsbuf.st_mode != entrymode) { /* if filesystem perms are different than pkg perms, warn user */ mode_t mask = 07777; _alpm_log(handle, ALPM_LOG_WARNING, _("directory permissions differ on %s\n" "filesystem: %o package: %o\n"), filename, lsbuf.st_mode & mask, entrymode & mask); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: directory permissions differ on %s\n" "filesystem: %o package: %o\n", filename, lsbuf.st_mode & mask, entrymode & mask); } #ifndef __MSYS__ #if 0 /* Disable this warning until our user management in packages has improved. Currently many packages have to create users in post_install and chown the directories. These all resulted in "false-positive" warnings. */ if((entryuid != lsbuf.st_uid) || (entrygid != lsbuf.st_gid)) { _alpm_log(handle, ALPM_LOG_WARNING, _("directory ownership differs on %s\n" "filesystem: %u:%u package: %u:%u\n"), filename, lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: directory ownership differs on %s\n" "filesystem: %u:%u package: %u:%u\n", filename, lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid); } #endif #endif _alpm_log(handle, ALPM_LOG_DEBUG, "extract: skipping dir extraction of %s\n", filename); archive_read_data_skip(archive); return 0; } else if(S_ISDIR(lsbuf.st_mode)) { /* case 5: trying to overwrite dir with file, don't allow it */ _alpm_log(handle, ALPM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"), filename); archive_read_data_skip(archive); return 1; } else if(S_ISDIR(entrymode)) { /* case 4: trying to overwrite file with dir */ _alpm_log(handle, ALPM_LOG_DEBUG, "extract: overwriting file with dir %s\n", filename); } else { /* case 3: trying to overwrite file with file */ /* if file is in NoUpgrade, don't touch it */ if(_alpm_fnmatch_patterns(handle->noupgrade, entryname) == 0) { notouch = 1; } else { alpm_backup_t *oldbackup; if(oldpkg && (oldbackup = _alpm_needbackup(entryname, oldpkg))) { hash_orig = oldbackup->hash; needbackup = 1; } else if(backup) { /* allow adding backup files retroactively */ needbackup = 1; } } } if(notouch || needbackup) { if(filename_len + strlen(".pacnew") >= PATH_MAX) { _alpm_log(handle, ALPM_LOG_ERROR, _("unable to extract %s.pacnew: path too long"), filename); return 1; } strcpy(filename + filename_len, ".pacnew"); isnewfile = (llstat(filename, &lsbuf) != 0 && errno == ENOENT); } _alpm_log(handle, ALPM_LOG_DEBUG, "extracting %s\n", filename); if(perform_extraction(handle, archive, entry, filename)) { errors++; return errors; } if(backup) { FREE(backup->hash); backup->hash = alpm_compute_md5sum(filename); } if(notouch) { alpm_event_pacnew_created_t event = { .type = ALPM_EVENT_PACNEW_CREATED, .from_noupgrade = 1, .oldpkg = oldpkg, .newpkg = newpkg, .file = filename }; /* "remove" the .pacnew suffix */ filename[filename_len] = '\0'; EVENT(handle, &event); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: %s installed as %s.pacnew\n", filename, filename); } else if(needbackup) { char *hash_local = NULL, *hash_pkg = NULL; char origfile[PATH_MAX] = ""; strncat(origfile, filename, filename_len); hash_local = alpm_compute_md5sum(origfile); hash_pkg = backup ? backup->hash : alpm_compute_md5sum(filename); _alpm_log(handle, ALPM_LOG_DEBUG, "checking hashes for %s\n", origfile); _alpm_log(handle, ALPM_LOG_DEBUG, "current: %s\n", hash_local); _alpm_log(handle, ALPM_LOG_DEBUG, "new: %s\n", hash_pkg); _alpm_log(handle, ALPM_LOG_DEBUG, "original: %s\n", hash_orig); if(hash_local && hash_pkg && strcmp(hash_local, hash_pkg) == 0) { /* local and new files are the same, updating anyway to get * correct timestamps */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n", origfile); if(try_rename(handle, filename, origfile)) { errors++; } } else if(hash_orig && hash_pkg && strcmp(hash_orig, hash_pkg) == 0) { /* original and new files are the same, leave the local version alone, * including any user changes */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: leaving existing file in place\n"); if(isnewfile) { unlink(filename); } } else if(hash_orig && hash_local && strcmp(hash_orig, hash_local) == 0) { /* installed file has NOT been changed by user, * update to the new version */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n", origfile); if(try_rename(handle, filename, origfile)) { errors++; } } else { /* none of the three files matched another, leave the unpacked * file alongside the local file */ alpm_event_pacnew_created_t event = { .type = ALPM_EVENT_PACNEW_CREATED, .from_noupgrade = 0, .oldpkg = oldpkg, .newpkg = newpkg, .file = origfile }; _alpm_log(handle, ALPM_LOG_DEBUG, "action: keeping current file and installing" " new one with .pacnew ending\n"); EVENT(handle, &event); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: %s installed as %s\n", origfile, filename); } free(hash_local); if(!backup) { free(hash_pkg); } } return errors; } static int commit_single_pkg(alpm_handle_t *handle, alpm_pkg_t *newpkg, size_t pkg_current, size_t pkg_count) { int i, ret = 0, errors = 0; int is_upgrade = 0; alpm_pkg_t *oldpkg = NULL; alpm_db_t *db = handle->db_local; alpm_trans_t *trans = handle->trans; alpm_progress_t progress = ALPM_PROGRESS_ADD_START; alpm_event_package_operation_t event; const char *log_msg = "adding"; const char *pkgfile; ASSERT(trans != NULL, return -1); /* see if this is an upgrade. if so, remove the old package first */ if((oldpkg = newpkg->oldpkg)) { int cmp = _alpm_pkg_compare_versions(newpkg, oldpkg); if(cmp < 0) { log_msg = "downgrading"; progress = ALPM_PROGRESS_DOWNGRADE_START; event.operation = ALPM_PACKAGE_DOWNGRADE; } else if(cmp == 0) { log_msg = "reinstalling"; progress = ALPM_PROGRESS_REINSTALL_START; event.operation = ALPM_PACKAGE_REINSTALL; } else { log_msg = "upgrading"; progress = ALPM_PROGRESS_UPGRADE_START; event.operation = ALPM_PACKAGE_UPGRADE; } is_upgrade = 1; /* copy over the install reason */ newpkg->reason = alpm_pkg_get_reason(oldpkg); } else { event.operation = ALPM_PACKAGE_INSTALL; } event.type = ALPM_EVENT_PACKAGE_OPERATION_START; event.oldpkg = oldpkg; event.newpkg = newpkg; EVENT(handle, &event); pkgfile = newpkg->origin_data.file; _alpm_log(handle, ALPM_LOG_DEBUG, "%s package %s-%s\n", log_msg, newpkg->name, newpkg->version); /* pre_install/pre_upgrade scriptlet */ if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) { const char *scriptlet_name = is_upgrade ? "pre_upgrade" : "pre_install"; _alpm_runscriptlet(handle, pkgfile, scriptlet_name, newpkg->version, oldpkg ? oldpkg->version : NULL, 1); } /* we override any pre-set reason if we have alldeps or allexplicit set */ if(trans->flags & ALPM_TRANS_FLAG_ALLDEPS) { newpkg->reason = ALPM_PKG_REASON_DEPEND; } else if(trans->flags & ALPM_TRANS_FLAG_ALLEXPLICIT) { newpkg->reason = ALPM_PKG_REASON_EXPLICIT; } if(oldpkg) { /* set up fake remove transaction */ if(_alpm_remove_single_package(handle, oldpkg, newpkg, 0, 0) == -1) { handle->pm_errno = ALPM_ERR_TRANS_ABORT; ret = -1; goto cleanup; } } /* prepare directory for database entries so permission are correct after changelog/install script installation */ if(_alpm_local_db_prepare(db, newpkg)) { alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: could not create database entry %s-%s\n", newpkg->name, newpkg->version); handle->pm_errno = ALPM_ERR_DB_WRITE; ret = -1; goto cleanup; } if(!(trans->flags & ALPM_TRANS_FLAG_DBONLY)) { struct archive *archive; struct archive_entry *entry; struct stat buf; int fd, cwdfd; _alpm_log(handle, ALPM_LOG_DEBUG, "extracting files\n"); fd = _alpm_open_archive(db->handle, pkgfile, &buf, &archive, ALPM_ERR_PKG_OPEN); if(fd < 0) { ret = -1; goto cleanup; } /* save the cwd so we can restore it later */ OPEN(cwdfd, ".", O_RDONLY | O_CLOEXEC); if(cwdfd < 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); } /* libarchive requires this for extracting hard links */ if(chdir(handle->root) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), handle->root, strerror(errno)); _alpm_archive_read_free(archive); close(fd); ret = -1; goto cleanup; } /* call PROGRESS once with 0 percent, as we sort-of skip that here */ PROGRESS(handle, progress, newpkg->name, 0, pkg_count, pkg_current); for(i = 0; archive_read_next_header(archive, &entry) == ARCHIVE_OK; i++) { int percent; if(newpkg->size != 0) { /* Using compressed size for calculations here, as newpkg->isize is not * exact when it comes to comparing to the ACTUAL uncompressed size * (missing metadata sizes) */ int64_t pos = _alpm_archive_compressed_ftell(archive); percent = (pos * 100) / newpkg->size; if(percent >= 100) { percent = 100; } } else { percent = 0; } PROGRESS(handle, progress, newpkg->name, percent, pkg_count, pkg_current); /* extract the next file from the archive */ errors += extract_single_file(handle, archive, entry, newpkg, oldpkg); } _alpm_archive_read_free(archive); close(fd); /* restore the old cwd if we have it */ if(cwdfd >= 0) { if(fchdir(cwdfd) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"), strerror(errno)); } close(cwdfd); } if(errors) { ret = -1; if(is_upgrade) { _alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while upgrading %s\n"), newpkg->name); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: problem occurred while upgrading %s\n", newpkg->name); } else { _alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while installing %s\n"), newpkg->name); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: problem occurred while installing %s\n", newpkg->name); } } } /* make an install date (in UTC) */ newpkg->installdate = time(NULL); _alpm_log(handle, ALPM_LOG_DEBUG, "updating database\n"); _alpm_log(handle, ALPM_LOG_DEBUG, "adding database entry '%s'\n", newpkg->name); if(_alpm_local_db_write(db, newpkg, INFRQ_ALL)) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not update database entry %s-%s\n"), newpkg->name, newpkg->version); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: could not update database entry %s-%s\n", newpkg->name, newpkg->version); handle->pm_errno = ALPM_ERR_DB_WRITE; ret = -1; goto cleanup; } if(_alpm_db_add_pkgincache(db, newpkg) == -1) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not add entry '%s' in cache\n"), newpkg->name); } PROGRESS(handle, progress, newpkg->name, 100, pkg_count, pkg_current); switch(event.operation) { case ALPM_PACKAGE_INSTALL: alpm_logaction(handle, ALPM_CALLER_PREFIX, "installed %s (%s)\n", newpkg->name, newpkg->version); break; case ALPM_PACKAGE_DOWNGRADE: alpm_logaction(handle, ALPM_CALLER_PREFIX, "downgraded %s (%s -> %s)\n", newpkg->name, oldpkg->version, newpkg->version); break; case ALPM_PACKAGE_REINSTALL: alpm_logaction(handle, ALPM_CALLER_PREFIX, "reinstalled %s (%s)\n", newpkg->name, newpkg->version); break; case ALPM_PACKAGE_UPGRADE: alpm_logaction(handle, ALPM_CALLER_PREFIX, "upgraded %s (%s -> %s)\n", newpkg->name, oldpkg->version, newpkg->version); break; default: /* we should never reach here */ break; } /* run the post-install script if it exists */ if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) { char *scriptlet = _alpm_local_db_pkgpath(db, newpkg, "install"); const char *scriptlet_name = is_upgrade ? "post_upgrade" : "post_install"; _alpm_runscriptlet(handle, scriptlet, scriptlet_name, newpkg->version, oldpkg ? oldpkg->version : NULL, 0); free(scriptlet); } event.type = ALPM_EVENT_PACKAGE_OPERATION_DONE; EVENT(handle, &event); cleanup: return ret; }
/** * Search for a GPG key in a remote location. * This requires GPGME to call the gpg binary and have a keyserver previously * defined in a gpg.conf configuration file. * @param handle the context handle * @param fpr the fingerprint key ID to look up * @param pgpkey storage location for the given key if found * @return 1 on success, 0 on key not found, -1 on error */ static int key_search(alpm_handle_t *handle, const char *fpr, alpm_pgpkey_t *pgpkey) { gpgme_error_t err; gpgme_ctx_t ctx; gpgme_keylist_mode_t mode; gpgme_key_t key; int ret = -1; size_t fpr_len; char *full_fpr; /* gpg2 goes full retard here. For key searches ONLY, we need to prefix the * key fingerprint with 0x, or the lookup will fail. */ fpr_len = strlen(fpr); MALLOC(full_fpr, fpr_len + 3, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); sprintf(full_fpr, "0x%s", fpr); memset(&ctx, 0, sizeof(ctx)); err = gpgme_new(&ctx); CHECK_ERR(); mode = gpgme_get_keylist_mode(ctx); /* using LOCAL and EXTERN together doesn't work for GPG 1.X. Ugh. */ mode &= ~GPGME_KEYLIST_MODE_LOCAL; mode |= GPGME_KEYLIST_MODE_EXTERN; err = gpgme_set_keylist_mode(ctx, mode); CHECK_ERR(); _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s remotely\n", fpr); err = gpgme_get_key(ctx, full_fpr, &key, 0); if(gpg_err_code(err) == GPG_ERR_EOF) { _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); /* Try an alternate lookup using the 8 character fingerprint value, since * busted-ass keyservers can't support lookups using subkeys with the full * value as of now. This is why 2012 is not the year of PGP encryption. */ if(fpr_len > 8) { const char *short_fpr = memcpy(&full_fpr[fpr_len - 8], "0x", 2); _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s remotely\n", short_fpr); err = gpgme_get_key(ctx, short_fpr, &key, 0); if(gpg_err_code(err) == GPG_ERR_EOF) { _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); ret = 0; } } else { ret = 0; } } if(gpg_err_code(err) != GPG_ERR_NO_ERROR) { goto error; } /* should only get here if key actually exists */ pgpkey->data = key; if(key->subkeys->fpr) { pgpkey->fingerprint = key->subkeys->fpr; } else if(key->subkeys->keyid) { pgpkey->fingerprint = key->subkeys->keyid; } pgpkey->uid = key->uids->uid; pgpkey->name = key->uids->name; pgpkey->email = key->uids->email; pgpkey->created = key->subkeys->timestamp; pgpkey->expires = key->subkeys->expires; pgpkey->length = key->subkeys->length; pgpkey->revoked = key->subkeys->revoked; switch (key->subkeys->pubkey_algo) { case GPGME_PK_RSA: case GPGME_PK_RSA_E: case GPGME_PK_RSA_S: pgpkey->pubkey_algo = 'R'; break; case GPGME_PK_DSA: pgpkey->pubkey_algo = 'D'; break; case GPGME_PK_ELG_E: case GPGME_PK_ELG: case GPGME_PK_ECDSA: case GPGME_PK_ECDH: pgpkey->pubkey_algo = 'E'; break; } ret = 1; error: if(ret != 1) { _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err)); } free(full_fpr); gpgme_release(ctx); return ret; }