static int pkg_repo_binary_init_update(struct pkg_repo *repo, const char *name) { sqlite3 *sqlite; const char update_check_sql[] = "" "INSERT INTO repo_update VALUES(1);"; const char update_start_sql[] = "" "CREATE TABLE IF NOT EXISTS repo_update (n INT);"; /* [Re]create repo */ unlink(name); if (repo->ops->create(repo) != EPKG_OK) { pkg_emit_notice("Unable to create repository %s", repo->name); return (EPKG_FATAL); } if (repo->ops->open(repo, R_OK|W_OK) != EPKG_OK) { pkg_emit_notice("Unable to open created repository %s", repo->name); return (EPKG_FATAL); } repo->ops->init(repo); sqlite = PRIV_GET(repo); if(sqlite3_exec(sqlite, update_check_sql, NULL, NULL, NULL) == SQLITE_OK) { pkg_emit_notice("Previous update has not been finished, restart it"); return (EPKG_END); } else { sql_exec(sqlite, update_start_sql); } return (EPKG_OK); }
/* ARGSUSED */ static int add_shlibs_to_pkg(struct pkg *pkg, const char *fpath, const char *name, bool is_shlib) { struct pkg_file *file = NULL; const char *filepath; switch(filter_system_shlibs(name, NULL, 0)) { case EPKG_OK: /* A non-system library */ pkg_addshlib_required(pkg, name); return (EPKG_OK); case EPKG_END: /* A system library */ return (EPKG_OK); default: /* Ignore link resolution errors if we're analysing a shared library. */ if (is_shlib) return (EPKG_OK); while (pkg_files(pkg, &file) == EPKG_OK) { filepath = file->path; if (strcmp(&filepath[strlen(filepath) - strlen(name)], name) == 0) { pkg_addshlib_required(pkg, name); return (EPKG_OK); } } pkg_emit_notice("(%s-%s) %s - required shared library %s not " "found", pkg->name, pkg->version, fpath, name); return (EPKG_FATAL); } }
static int pkg_repo_binary_update_incremental(const char *name, struct pkg_repo *repo, time_t *mtime, bool force) { FILE *fmanifest = NULL, *fdigests = NULL /*, *fconflicts = NULL*/; struct pkg *pkg = NULL; int rc = EPKG_FATAL; sqlite3 *sqlite = NULL; sqlite3_stmt *stmt; const char *origin, *digest, *offset, *length, *checksum; char *linebuf = NULL, *p; int updated = 0, removed = 0, added = 0, processed = 0, pushed = 0; long num_offset, num_length; time_t local_t; time_t digest_t; time_t packagesite_t; struct pkg_increment_task_item *ldel = NULL, *ladd = NULL, *item, *tmp_item; struct pkg_manifest_key *keys = NULL; size_t linecap = 0; ssize_t linelen; char *map = MAP_FAILED; size_t len = 0; int hash_it = 0; bool in_trans = false, legacy_repo = false; /* Required for making iterator */ struct pkgdb_it *it = NULL; struct pkgdb fakedb; pkg_debug(1, "Pkgrepo, begin incremental update of '%s'", name); /* In forced mode, ignore mtime */ if (force) *mtime = 0; /* Fetch meta */ local_t = *mtime; if (pkg_repo_fetch_meta(repo, &local_t) == EPKG_FATAL) pkg_emit_notice("repository %s has no meta file, using " "default settings", repo->name); /* Fetch digests */ local_t = *mtime; fdigests = pkg_repo_fetch_remote_extract_tmp(repo, repo->meta->digests, &local_t, &rc); if (fdigests == NULL) goto cleanup; /* Fetch packagesite */ digest_t = local_t; local_t = *mtime; fmanifest = pkg_repo_fetch_remote_extract_tmp(repo, repo->meta->manifests, &local_t, &rc); if (fmanifest == NULL) goto cleanup; packagesite_t = local_t; *mtime = digest_t; /*fconflicts = repo_fetch_remote_extract_tmp(repo, repo_conflicts_archive, "txz", &local_t, &rc, repo_conflicts_file);*/ fseek(fmanifest, 0, SEEK_END); len = ftell(fmanifest); /* Detect whether we have legacy repo */ if ((linelen = getline(&linebuf, &linecap, fdigests)) > 0) { p = linebuf; origin = strsep(&p, ":"); digest = strsep(&p, ":"); if (digest == NULL) { pkg_emit_error("invalid digest file format"); rc = EPKG_FATAL; goto cleanup; } if (!pkg_checksum_is_valid(digest, strlen(digest))) { legacy_repo = true; pkg_debug(1, "repository '%s' has a legacy digests format", repo->name); } } fseek(fdigests, 0, SEEK_SET); /* Load local repository data */ rc = pkg_repo_binary_init_update(repo, name, force); if (rc == EPKG_END) { /* Need to perform forced update */ repo->ops->close(repo, false); return (pkg_repo_binary_update_incremental(name, repo, mtime, true)); } if (rc != EPKG_OK) { rc = EPKG_FATAL; goto cleanup; } /* Here sqlite is initialized */ sqlite = PRIV_GET(repo); stmt = pkg_repo_binary_get_origins(sqlite); if (stmt == NULL) { rc = EPKG_FATAL; goto cleanup; } fakedb.sqlite = sqlite; it = pkgdb_it_new_sqlite(&fakedb, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE); if (it != NULL) { while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) { pkg_get(pkg, PKG_ORIGIN, &origin, legacy_repo ? PKG_OLD_DIGEST : PKG_DIGEST, &digest, PKG_CKSUM, &checksum); pkg_repo_binary_update_item_new(&ldel, origin, digest, 0, 0, checksum); } pkgdb_it_free(it); } else { sqlite3_finalize(stmt); } pkg_debug(1, "Pkgrepo, reading new packagesite.yaml for '%s'", name); /* load the while digests */ while ((linelen = getline(&linebuf, &linecap, fdigests)) > 0) { p = linebuf; origin = strsep(&p, ":"); digest = strsep(&p, ":"); offset = strsep(&p, ":"); /* files offset */ strsep(&p, ":"); length = p ? strsep(&p, ":\n") : NULL; checksum = p ? strsep(&p, ":\n") : NULL; if (origin == NULL || digest == NULL || offset == NULL) { pkg_emit_error("invalid digest file format"); rc = EPKG_FATAL; goto cleanup; } errno = 0; num_offset = (long)strtoul(offset, NULL, 10); if (errno != 0) { pkg_emit_errno("strtoul", "digest format error"); rc = EPKG_FATAL; goto cleanup; } if (length != NULL) { errno = 0; num_length = (long)strtoul(length, NULL, 10); if (errno != 0) { pkg_emit_errno("strtoul", "digest format error"); rc = EPKG_FATAL; goto cleanup; } } else { num_length = 0; } processed++; HASH_FIND_STR(ldel, origin, item); if (item == NULL) { added++; pkg_repo_binary_update_item_new(&ladd, origin, digest, num_offset, num_length, checksum); } else { HASH_DEL(ldel, item); if (checksum == NULL || item->checksum == NULL) { pkg_repo_binary_update_item_new(&ladd, origin, digest, num_offset, num_length, checksum); updated++; } else if (strcmp(checksum, item->checksum) != 0) { /* Allow checksum to be used as unique mark */ pkg_repo_binary_update_item_new(&ladd, origin, digest, num_offset, num_length, checksum); updated++; } pkg_repo_binary_update_item_free(item); } } rc = EPKG_OK; pkg_debug(1, "Pkgrepo, removing old entries for '%s'", name); rc = pkgdb_transaction_begin(sqlite, "REPO"); if (rc != EPKG_OK) goto cleanup; in_trans = true; removed = HASH_COUNT(ldel); hash_it = 0; if (removed > 0) pkg_emit_progress_start("Removing expired repository entries"); HASH_ITER(hh, ldel, item, tmp_item) { pkg_emit_progress_tick(++hash_it, removed); if (rc == EPKG_OK) { rc = pkgdb_repo_remove_package(item->origin); } else { pkg_emit_progress_tick(removed, removed); } HASH_DEL(ldel, item); pkg_repo_binary_update_item_free(item); }
static int apply_repo_change(struct pkgdb *db, const char *database, const struct repo_changes *repo_changes, const char *updown, int version, int *next_version) { const struct repo_changes *change; bool found = false, in_trans = false; int ret = EPKG_OK; char sql[8192]; char *errmsg; for (change = repo_changes; change->version != -1; change++) { if (change->version == version) { found = true; break; } } if (!found) { pkg_emit_error("Failed to %s \"%s\" repo schema " " version %d (target version %d) " "-- change not found", updown, database, version, REPO_SCHEMA_VERSION); return (EPKG_FATAL); } /* substitute the repo database name */ ret = substitute_into_sql(sql, sizeof(sql), change->sql, database); /* begin transaction */ if (ret == EPKG_OK) { in_trans = true; ret = pkgdb_transaction_begin(db->sqlite, "SCHEMA"); } /* apply change */ if (ret == EPKG_OK) { pkg_debug(4, "Pkgdb: running '%s'", sql); ret = sqlite3_exec(db->sqlite, sql, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { pkg_emit_error("sqlite: %s", errmsg); sqlite3_free(errmsg); ret = EPKG_FATAL; } } /* update repo user_version */ if (ret == EPKG_OK) { *next_version = change->next_version; ret = set_repo_user_version(db->sqlite, database, *next_version); } /* commit or rollback */ if (in_trans) { if (ret != EPKG_OK) pkgdb_transaction_rollback(db->sqlite, "SCHEMA"); if (pkgdb_transaction_commit(db->sqlite, "SCHEMA") != EPKG_OK) ret = EPKG_FATAL; } if (ret == EPKG_OK) { pkg_emit_notice("Repo \"%s\" %s schema %d to %d: %s", database, updown, version, change->next_version, change->message); } return (ret); }
static int pkg_add_check_pkg_archive(struct pkgdb *db, struct pkg *pkg, const char *path, int flags, struct pkg_manifest_key *keys, const char *location) { const char *arch; int ret, retcode; struct pkg_dep *dep = NULL; char bd[MAXPATHLEN], *basedir; char dpath[MAXPATHLEN], *ppath; const char *ext; struct pkg *pkg_inst = NULL; arch = pkg->abi != NULL ? pkg->abi : pkg->arch; if (!is_valid_abi(arch, true)) { if ((flags & PKG_ADD_FORCE) == 0) { return (EPKG_FATAL); } } /* XX check */ ret = pkg_try_installed(db, pkg->origin, &pkg_inst, PKG_LOAD_BASIC); if (ret == EPKG_OK) { if ((flags & PKG_ADD_FORCE) == 0) { pkg_emit_already_installed(pkg_inst); pkg_free(pkg_inst); pkg_inst = NULL; return (EPKG_INSTALLED); } else if (pkg_inst->locked) { pkg_emit_locked(pkg_inst); pkg_free(pkg_inst); pkg_inst = NULL; return (EPKG_LOCKED); } else { pkg_emit_notice("package %s is already installed, forced install", pkg->name); pkg_free(pkg_inst); pkg_inst = NULL; } } else if (ret != EPKG_END) { return (ret); } /* * Check for dependencies by searching the same directory as * the package archive we're reading. Of course, if we're * reading from a file descriptor or a unix domain socket or * whatever, there's no valid directory to search. */ strlcpy(bd, path, sizeof(bd)); if (strncmp(path, "-", 2) != 0) { basedir = dirname(bd); if ((ext = strrchr(path, '.')) == NULL) { pkg_emit_error("%s has no extension", path); return (EPKG_FATAL); } } else { ext = NULL; basedir = NULL; } retcode = EPKG_FATAL; pkg_emit_add_deps_begin(pkg); while (pkg_deps(pkg, &dep) == EPKG_OK) { if (pkg_is_installed(db, dep->name) == EPKG_OK) continue; if (basedir == NULL) { pkg_emit_missing_dep(pkg, dep); if ((flags & PKG_ADD_FORCE_MISSING) == 0) goto cleanup; continue; } if (dep->version != NULL && dep->version[0] != '\0') { snprintf(dpath, sizeof(dpath), "%s/%s-%s%s", basedir, dep->name, dep->version, ext); if ((flags & PKG_ADD_UPGRADE) == 0 && access(dpath, F_OK) == 0) { ret = pkg_add(db, dpath, PKG_ADD_AUTOMATIC, keys, location); if (ret != EPKG_OK) goto cleanup; } else { pkg_emit_missing_dep(pkg, dep); if ((flags & PKG_ADD_FORCE_MISSING) == 0) goto cleanup; } } else { snprintf(dpath, sizeof(dpath), "%s/%s-*%s", basedir, dep->name, ext); ppath = pkg_globmatch(dpath, dep->name); if (ppath == NULL) { pkg_emit_missing_dep(pkg, dep); if ((flags & PKG_ADD_FORCE_MISSING) == 0) goto cleanup; continue; } if ((flags & PKG_ADD_UPGRADE) == 0 && access(ppath, F_OK) == 0) { ret = pkg_add(db, ppath, PKG_ADD_AUTOMATIC, keys, location); free(ppath); if (ret != EPKG_OK) goto cleanup; } else { free(ppath); pkg_emit_missing_dep(pkg, dep); if ((flags & PKG_ADD_FORCE_MISSING) == 0) goto cleanup; continue; } } } retcode = EPKG_OK; cleanup: pkg_emit_add_deps_finished(pkg); return (retcode); }
static int do_extract(struct archive *a, struct archive_entry *ae, const char *location, int nfiles, struct pkg *pkg, struct pkg *local) { int retcode = EPKG_OK; int ret = 0, cur_file = 0; char path[MAXPATHLEN], pathname[MAXPATHLEN], rpath[MAXPATHLEN]; struct stat st; const struct stat *aest; bool renamed = false; const struct pkg_file *rf; struct pkg_config_file *rcf; struct sbuf *newconf; bool automerge = pkg_object_bool(pkg_config_get("AUTOMERGE")); unsigned long set, clear; #ifndef HAVE_ARC4RANDOM srand(time(NULL)); #endif if (nfiles == 0) return (EPKG_OK); pkg_emit_extract_begin(pkg); pkg_emit_progress_start(NULL); newconf = sbuf_new_auto(); do { ret = ARCHIVE_OK; sbuf_clear(newconf); rf = NULL; rcf = NULL; pkg_absolutepath(archive_entry_pathname(ae), path, sizeof(path)); snprintf(pathname, sizeof(pathname), "%s%s%s", location ? location : "", *path == '/' ? "" : "/", path ); strlcpy(rpath, pathname, sizeof(rpath)); aest = archive_entry_stat(ae); archive_entry_fflags(ae, &set, &clear); if (lstat(rpath, &st) != -1) { /* * We have an existing file on the path, so handle it */ if (!S_ISDIR(aest->st_mode)) { pkg_debug(2, "Old version found, renaming"); pkg_add_file_random_suffix(rpath, sizeof(rpath), 12); renamed = true; } if (!S_ISDIR(st.st_mode) && S_ISDIR(aest->st_mode)) { if (S_ISLNK(st.st_mode)) { if (stat(rpath, &st) == -1) { pkg_emit_error("Dead symlink %s", rpath); } else { pkg_debug(2, "Directory is a symlink, use it"); pkg_emit_progress_tick(cur_file++, nfiles); continue; } } } } archive_entry_set_pathname(ae, rpath); /* load in memory the content of config files */ if (pkg_is_config_file(pkg, path, &rf, &rcf)) { pkg_debug(1, "Populating config_file %s", pathname); size_t len = archive_entry_size(ae); rcf->content = malloc(len); archive_read_data(a, rcf->content, len); if (renamed && (!automerge || local == NULL)) strlcat(pathname, ".pkgnew", sizeof(pathname)); } /* * check if the file is already provided by previous package */ if (!automerge) attempt_to_merge(renamed, rcf, local, pathname, path, newconf); if (sbuf_len(newconf) == 0 && (rcf == NULL || rcf->content == NULL)) { pkg_debug(1, "Extracting: %s", archive_entry_pathname(ae)); int install_as_user = (getenv("INSTALL_AS_USER") != NULL); int extract_flags = EXTRACT_ARCHIVE_FLAGS; if (install_as_user) { /* when installing as user don't try to set file ownership */ extract_flags &= ~ARCHIVE_EXTRACT_OWNER; } ret = archive_read_extract(a, ae, extract_flags); } else { if (sbuf_len(newconf) == 0) { sbuf_cat(newconf, rcf->content); sbuf_finish(newconf); } pkg_debug(2, "Writing conf in %s", pathname); unlink(rpath); FILE *f = fopen(rpath, "w+"); fprintf(f, "%s", sbuf_data(newconf)); fclose(f); } if (ret != ARCHIVE_OK) { /* * show error except when the failure is during * extracting a directory and that the directory already * exists. * this allow to install packages linux_base from * package for example */ if (archive_entry_filetype(ae) != AE_IFDIR || !is_dir(pathname)) { pkg_emit_error("archive_read_extract(): %s", archive_error_string(a)); retcode = EPKG_FATAL; goto cleanup; } } /* Reapply modes to the directories to work around a problem on FreeBSD 9 */ if (archive_entry_filetype(ae) == AE_IFDIR) chmod(pathname, aest->st_mode); pkg_emit_progress_tick(cur_file++, nfiles); /* Rename old file */ if (renamed) { pkg_debug(1, "Renaming %s -> %s", rpath, pathname); #ifdef HAVE_CHFLAGS bool old = false; if (set & NOCHANGESFLAGS) chflags(rpath, 0); if (lstat(pathname, &st) != -1) { old = true; if (st.st_flags & NOCHANGESFLAGS) chflags(pathname, 0); } #endif if (rename(rpath, pathname) == -1) { #ifdef HAVE_CHFLAGS /* restore flags */ if (old) chflags(pathname, st.st_flags); #endif pkg_emit_error("cannot rename %s to %s: %s", rpath, pathname, strerror(errno)); retcode = EPKG_FATAL; goto cleanup; } #ifdef HAVE_CHFLAGS /* Restore flags */ chflags(pathname, set); #endif } if (string_end_with(pathname, ".pkgnew")) pkg_emit_notice("New configuration file: %s", pathname); renamed = false; } while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK); if (ret != ARCHIVE_EOF) { pkg_emit_error("archive_read_next_header(): %s", archive_error_string(a)); retcode = EPKG_FATAL; } cleanup: pkg_emit_progress_tick(nfiles, nfiles); pkg_emit_extract_finished(pkg); if (renamed && retcode == EPKG_FATAL) { #ifdef HAVE_CHFLAGS if (set & NOCHANGESFLAGS) chflags(rpath, set & ~NOCHANGESFLAGS); #endif unlink(rpath); } return (retcode); }
static int pkg_repo_update_incremental(const char *name, struct pkg_repo *repo, time_t *mtime) { FILE *fmanifest = NULL, *fdigests = NULL /*, *fconflicts = NULL*/; sqlite3 *sqlite = NULL; struct pkg *pkg = NULL; int rc = EPKG_FATAL; const char *origin, *digest, *offset, *length; struct pkgdb_it *it = NULL; char *linebuf = NULL, *p; int updated = 0, removed = 0, added = 0, processed = 0; long num_offset, num_length; time_t local_t = *mtime; time_t digest_t; time_t packagesite_t; struct pkg_increment_task_item *ldel = NULL, *ladd = NULL, *item, *tmp_item; struct pkg_manifest_key *keys = NULL; size_t linecap = 0; ssize_t linelen; char *map = MAP_FAILED; size_t len = 0; int hash_it = 0; time_t now, last; pkg_debug(1, "Pkgrepo, begin incremental update of '%s'", name); if ((rc = pkgdb_repo_open(name, false, &sqlite)) != EPKG_OK) { return (EPKG_FATAL); } if ((rc = pkgdb_repo_init(sqlite)) != EPKG_OK) { goto cleanup; } if ((rc = pkg_repo_register(repo, sqlite)) != EPKG_OK) goto cleanup; it = pkgdb_repo_origins(sqlite); if (it == NULL) { rc = EPKG_FATAL; goto cleanup; } while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) { pkg_get(pkg, PKG_ORIGIN, &origin, PKG_DIGEST, &digest); pkg_repo_update_increment_item_new(&ldel, origin, digest, 4, 0); } if (pkg_repo_fetch_meta(repo, NULL) == EPKG_FATAL) pkg_emit_notice("repository %s has no meta file, use default settings", repo->name); fdigests = pkg_repo_fetch_remote_extract_tmp(repo, repo->meta->digests, &local_t, &rc); if (fdigests == NULL) goto cleanup; digest_t = local_t; local_t = *mtime; fmanifest = pkg_repo_fetch_remote_extract_tmp(repo, repo->meta->manifests, &local_t, &rc); if (fmanifest == NULL) goto cleanup; packagesite_t = digest_t; *mtime = packagesite_t > digest_t ? packagesite_t : digest_t; /*fconflicts = repo_fetch_remote_extract_tmp(repo, repo_conflicts_archive, "txz", &local_t, &rc, repo_conflicts_file);*/ fseek(fmanifest, 0, SEEK_END); len = ftell(fmanifest); pkg_debug(1, "Pkgrepo, reading new packagesite.yaml for '%s'", name); /* load the while digests */ while ((linelen = getline(&linebuf, &linecap, fdigests)) > 0) { p = linebuf; origin = strsep(&p, ":"); digest = strsep(&p, ":"); offset = strsep(&p, ":"); /* files offset */ strsep(&p, ":"); length = strsep(&p, ":"); if (origin == NULL || digest == NULL || offset == NULL) { pkg_emit_error("invalid digest file format"); assert(0); rc = EPKG_FATAL; goto cleanup; } errno = 0; num_offset = (long)strtoul(offset, NULL, 10); if (errno != 0) { pkg_emit_errno("strtoul", "digest format error"); rc = EPKG_FATAL; goto cleanup; } if (length != NULL) { errno = 0; num_length = (long)strtoul(length, NULL, 10); if (errno != 0) { pkg_emit_errno("strtoul", "digest format error"); rc = EPKG_FATAL; goto cleanup; } } else { num_length = 0; } processed++; HASH_FIND_STR(ldel, origin, item); if (item == NULL) { added++; pkg_repo_update_increment_item_new(&ladd, origin, digest, num_offset, num_length); } else { if (strcmp(digest, item->digest) == 0) { free(item->origin); free(item->digest); HASH_DEL(ldel, item); free(item); item = NULL; } else { free(item->origin); free(item->digest); HASH_DEL(ldel, item); free(item); item = NULL; pkg_repo_update_increment_item_new(&ladd, origin, digest, num_offset, num_length); updated++; } } } rc = EPKG_OK; pkg_debug(1, "Pkgrepo, removing old entries for '%s'", name); removed = HASH_COUNT(ldel); hash_it = 0; last = 0; HASH_ITER(hh, ldel, item, tmp_item) { now = time(NULL); if (++hash_it == removed || now > last) { pkg_emit_update_remove(removed, hash_it); last = now; } if (rc == EPKG_OK) { rc = pkgdb_repo_remove_package(item->origin); } free(item->origin); free(item->digest); HASH_DEL(ldel, item); free(item); }
int pkg_update(struct pkg_repo *repo, bool force) { char repofile[MAXPATHLEN]; const char *dbdir = NULL; struct stat st; time_t t = 0; sqlite3 *sqlite = NULL; char *req = NULL; int64_t res; sqlite3_initialize(); if (pkg_config_string(PKG_CONFIG_DBDIR, &dbdir) != EPKG_OK) { pkg_emit_error("Cant get dbdir config entry"); return (EPKG_FATAL); } snprintf(repofile, sizeof(repofile), "%s/%s.sqlite", dbdir, pkg_repo_name(repo)); if (stat(repofile, &st) != -1) t = force ? 0 : st.st_mtime; if (t != 0) { if (sqlite3_open(repofile, &sqlite) != SQLITE_OK) { pkg_emit_error("Unable to open local database"); return (EPKG_FATAL); } if (get_pragma(sqlite, "SELECT count(name) FROM sqlite_master " "WHERE type='table' AND name='repodata';", &res) != EPKG_OK) { pkg_emit_error("Unable to query repository"); sqlite3_close(sqlite); return (EPKG_FATAL); } if (res != 1) { t = 0; if (sqlite != NULL) { sqlite3_close(sqlite); sqlite = NULL; } } } if (t != 0) { req = sqlite3_mprintf("select count(key) from repodata " "WHERE key = \"packagesite\" and value = '%q'", pkg_repo_url(repo)); if (get_pragma(sqlite, req, &res) != EPKG_OK) { sqlite3_free(req); pkg_emit_error("Unable to query repository"); sqlite3_close(sqlite); return (EPKG_FATAL); } sqlite3_free(req); if (res != 1) { t = 0; if (sqlite != NULL) { sqlite3_close(sqlite); sqlite = NULL; } unlink(repofile); } } res = pkg_update_incremental(repofile, repo, &t); if (res != EPKG_OK && res != EPKG_UPTODATE) { pkg_emit_notice("No digest falling back on legacy catalog format"); /* Still try to do full upgrade */ if ((res = pkg_update_full(repofile, repo, &t)) != EPKG_OK) goto cleanup; } res = EPKG_OK; cleanup: /* Set mtime from http request if possible */ if (t != 0) { struct timeval ftimes[2] = { { .tv_sec = t, .tv_usec = 0 }, { .tv_sec = t, .tv_usec = 0 } };
int pkg_add(struct pkgdb *db, const char *path, unsigned flags, struct pkg_manifest_key *keys) { const char *arch; const char *origin; const char *name; struct archive *a; struct archive_entry *ae; struct pkg *pkg = NULL; struct pkg_dep *dep = NULL; struct pkg *pkg_inst = NULL; bool extract = true; bool handle_rc = false; bool disable_mtree; char dpath[MAXPATHLEN]; const char *basedir; const char *ext; char *mtree; char *prefix; int retcode = EPKG_OK; int ret; assert(path != NULL); /* * Open the package archive file, read all the meta files and set the * current archive_entry to the first non-meta file. * If there is no non-meta files, EPKG_END is returned. */ ret = pkg_open2(&pkg, &a, &ae, path, keys, 0); if (ret == EPKG_END) extract = false; else if (ret != EPKG_OK) { retcode = ret; goto cleanup; } if ((flags & PKG_ADD_UPGRADE) == 0) pkg_emit_install_begin(pkg); if (pkg_is_valid(pkg) != EPKG_OK) { pkg_emit_error("the package is not valid"); return (EPKG_FATAL); } if (flags & PKG_ADD_AUTOMATIC) pkg_set(pkg, PKG_AUTOMATIC, (int64_t)true); /* * Check the architecture */ pkg_get(pkg, PKG_ARCH, &arch, PKG_ORIGIN, &origin, PKG_NAME, &name); if (!is_valid_abi(arch, true)) { if ((flags & PKG_ADD_FORCE) == 0) { retcode = EPKG_FATAL; goto cleanup; } } /* * Check if the package is already installed */ ret = pkg_try_installed(db, origin, &pkg_inst, PKG_LOAD_BASIC); if (ret == EPKG_OK) { if ((flags & PKG_FLAG_FORCE) == 0) { pkg_emit_already_installed(pkg_inst); retcode = EPKG_INSTALLED; pkg_free(pkg_inst); pkg_inst = NULL; goto cleanup; } else { pkg_emit_notice("package %s is already installed, forced install", name); pkg_free(pkg_inst); pkg_inst = NULL; } } else if (ret != EPKG_END) { retcode = ret; goto cleanup; } /* * Check for dependencies by searching the same directory as * the package archive we're reading. Of course, if we're * reading from a file descriptor or a unix domain socket or * somesuch, there's no valid directory to search. */ if (pkg_type(pkg) == PKG_FILE) { basedir = dirname(path); if ((ext = strrchr(path, '.')) == NULL) { pkg_emit_error("%s has no extension", path); retcode = EPKG_FATAL; goto cleanup; } } else { basedir = NULL; ext = NULL; } while (pkg_deps(pkg, &dep) == EPKG_OK) { if (pkg_is_installed(db, pkg_dep_origin(dep)) == EPKG_OK) continue; if (basedir != NULL) { const char *dep_name = pkg_dep_name(dep); const char *dep_ver = pkg_dep_version(dep); snprintf(dpath, sizeof(dpath), "%s/%s-%s%s", basedir, dep_name, dep_ver, ext); if ((flags & PKG_ADD_UPGRADE) == 0 && access(dpath, F_OK) == 0) { ret = pkg_add(db, dpath, PKG_ADD_AUTOMATIC, keys); if (ret != EPKG_OK) { retcode = EPKG_FATAL; goto cleanup; } } else { pkg_emit_error("Missing dependency matching '%s'", pkg_dep_get(dep, PKG_DEP_ORIGIN)); retcode = EPKG_FATAL; goto cleanup; } } else { retcode = EPKG_FATAL; pkg_emit_missing_dep(pkg, dep); goto cleanup; } } /* register the package before installing it in case there are * problems that could be caught here. */ retcode = pkgdb_register_pkg(db, pkg, flags & PKG_ADD_UPGRADE, flags & PKG_FLAG_FORCE); if (retcode != EPKG_OK) goto cleanup; /* MTREE replicates much of the standard functionality * inplicit in the way pkg works. It has to remain available * in the ports for compatibility with the old pkg_tools, but * ultimately, MTREE should be made redundant. Use this for * experimantal purposes and to develop MTREE-free versions of * packages. */ pkg_config_bool(PKG_CONFIG_DISABLE_MTREE, &disable_mtree); if (!disable_mtree) { pkg_get(pkg, PKG_PREFIX, &prefix, PKG_MTREE, &mtree); if ((retcode = do_extract_mtree(mtree, prefix)) != EPKG_OK) goto cleanup_reg; } /* * Execute pre-install scripts */ if ((flags & (PKG_ADD_NOSCRIPT | PKG_ADD_USE_UPGRADE_SCRIPTS)) == 0) pkg_script_run(pkg, PKG_SCRIPT_PRE_INSTALL); /* add the user and group if necessary */ /* pkg_add_user_group(pkg); */ /* * Extract the files on disk. */ if (extract && (retcode = do_extract(a, ae)) != EPKG_OK) { /* If the add failed, clean up (silently) */ pkg_delete_files(pkg, 2); pkg_delete_dirs(db, pkg, 1); goto cleanup_reg; } /* * Execute post install scripts */ if ((flags & PKG_ADD_NOSCRIPT) == 0) { if ((flags & PKG_ADD_USE_UPGRADE_SCRIPTS) == PKG_ADD_USE_UPGRADE_SCRIPTS) pkg_script_run(pkg, PKG_SCRIPT_POST_UPGRADE); else pkg_script_run(pkg, PKG_SCRIPT_POST_INSTALL); } /* * start the different related services if the users do want that * and that the service is running */ pkg_config_bool(PKG_CONFIG_HANDLE_RC_SCRIPTS, &handle_rc); if (handle_rc) pkg_start_stop_rc_scripts(pkg, PKG_RC_START); cleanup_reg: if ((flags & PKG_ADD_UPGRADE) == 0) pkgdb_register_finale(db, retcode); if (retcode == EPKG_OK && (flags & PKG_ADD_UPGRADE) == 0) pkg_emit_install_finished(pkg); cleanup: if (a != NULL) { archive_read_close(a); archive_read_free(a); } pkg_free(pkg); if (pkg_inst != NULL) pkg_free(pkg_inst); return (retcode); }
int pkg_repo_binary_open(struct pkg_repo *repo, unsigned mode) { char filepath[MAXPATHLEN]; struct statfs stfs; const char *dbdir = NULL; sqlite3 *sqlite = NULL; int flags; int64_t res; struct pkg_repo_it *it; struct pkg *pkg = NULL; const char *digest; sqlite3_initialize(); dbdir = pkg_object_string(pkg_config_get("PKG_DBDIR")); /* * Fall back on unix-dotfile locking strategy if on a network filesystem */ if (statfs(dbdir, &stfs) == 0) { if ((stfs.f_flags & MNT_LOCAL) != MNT_LOCAL) sqlite3_vfs_register(sqlite3_vfs_find("unix-dotfile"), 1); } snprintf(filepath, sizeof(filepath), "%s/%s.meta", dbdir, pkg_repo_name(repo)); /* Open metafile */ if (access(filepath, R_OK) != -1) { if (pkg_repo_meta_load(filepath, &repo->meta) != EPKG_OK) return (EPKG_FATAL); } snprintf(filepath, sizeof(filepath), "%s/%s", dbdir, pkg_repo_binary_get_filename(pkg_repo_name(repo))); /* Always want read mode here */ if (access(filepath, R_OK | mode) != 0) return (EPKG_ENOACCESS); flags = (mode & W_OK) != 0 ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; if (sqlite3_open_v2(filepath, &sqlite, flags, NULL) != SQLITE_OK) return (EPKG_FATAL); /* Sanitise sqlite database */ if (get_pragma(sqlite, "SELECT count(name) FROM sqlite_master " "WHERE type='table' AND name='repodata';", &res, false) != EPKG_OK) { pkg_emit_error("Unable to query repository"); sqlite3_close(sqlite); return (EPKG_FATAL); } if (res != 1) { pkg_emit_notice("Repository %s contains no repodata table, " "need to re-create database", repo->name); sqlite3_close(sqlite); return (EPKG_FATAL); } /* Check package site */ char *req = sqlite3_mprintf("select count(key) from repodata " "WHERE key = \"packagesite\" and value = '%q'", pkg_repo_url(repo)); res = 0; get_pragma(sqlite, req, &res, true); sqlite3_free(req); if (res != 1) { pkg_emit_notice("Repository %s has a wrong packagesite, need to " "re-create database", repo->name); sqlite3_close(sqlite); return (EPKG_FATAL); } /* Check version */ if (pkg_repo_binary_check_version(repo, sqlite) != EPKG_OK) { pkg_emit_error("need to re-create repo %s to upgrade schema version", repo->name); sqlite3_close(sqlite); if (mode & W_OK) unlink(filepath); return (EPKG_REPOSCHEMA); } repo->priv = sqlite; /* Check digests format */ if ((it = pkg_repo_binary_query(repo, NULL, MATCH_ALL)) == NULL) return (EPKG_OK); if (it->ops->next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) { it->ops->free(it); return (EPKG_OK); } it->ops->free(it); pkg_get(pkg, PKG_DIGEST, &digest); if (digest == NULL || !pkg_checksum_is_valid(digest, strlen(digest))) { pkg_emit_notice("Repository %s has incompatible checksum format, need to " "re-create database", repo->name); pkg_free(pkg); sqlite3_close(sqlite); repo->priv = NULL; return (EPKG_FATAL); } pkg_free(pkg); return (EPKG_OK); }
static int pkg_repo_update_incremental(const char *name, struct pkg_repo *repo, time_t *mtime) { FILE *fmanifest = NULL, *fdigests = NULL /*, *fconflicts = NULL*/; sqlite3 *sqlite = NULL; struct pkg *pkg = NULL; int rc = EPKG_FATAL; const char *origin, *digest, *offset, *length; struct pkgdb_it *it = NULL; char *linebuf = NULL, *p; int updated = 0, removed = 0, added = 0, processed = 0, pushed = 0; long num_offset, num_length; time_t local_t = *mtime; time_t digest_t; time_t packagesite_t; struct pkg_increment_task_item *ldel = NULL, *ladd = NULL, *item, *tmp_item; struct pkg_manifest_key *keys = NULL; size_t linecap = 0; ssize_t linelen; char *map = MAP_FAILED; size_t len = 0; int hash_it = 0; bool in_trans = false, new_repo = true, legacy_repo = false, reuse_repo; if (access(name, R_OK) != -1) new_repo = false; pkg_debug(1, "Pkgrepo, begin incremental update of '%s'", name); if ((rc = pkgdb_repo_open(name, false, &sqlite, &reuse_repo)) != EPKG_OK) { return (EPKG_FATAL); } if (!reuse_repo) { pkg_debug(1, "Pkgrepo, need to re-create database '%s'", name); local_t = 0; *mtime = 0; } if ((rc = pkgdb_repo_init(sqlite)) != EPKG_OK) { goto cleanup; } if ((rc = pkg_repo_register(repo, sqlite)) != EPKG_OK) goto cleanup; it = pkgdb_repo_origins(sqlite); if (it == NULL) { rc = EPKG_FATAL; goto cleanup; } if (pkg_repo_fetch_meta(repo, NULL) == EPKG_FATAL) pkg_emit_notice("repository %s has no meta file, using " "default settings", repo->name); fdigests = pkg_repo_fetch_remote_extract_tmp(repo, repo->meta->digests, &local_t, &rc); if (fdigests == NULL) { if (rc == EPKG_FATAL) /* Destroy repo completely */ if (new_repo) unlink(name); goto cleanup; } digest_t = local_t; local_t = *mtime; fmanifest = pkg_repo_fetch_remote_extract_tmp(repo, repo->meta->manifests, &local_t, &rc); if (fmanifest == NULL) { if (rc == EPKG_FATAL) /* Destroy repo completely */ if (new_repo) unlink(name); goto cleanup; } packagesite_t = digest_t; *mtime = packagesite_t > digest_t ? packagesite_t : digest_t; /*fconflicts = repo_fetch_remote_extract_tmp(repo, repo_conflicts_archive, "txz", &local_t, &rc, repo_conflicts_file);*/ fseek(fmanifest, 0, SEEK_END); len = ftell(fmanifest); /* Detect whether we have legacy repo */ if ((linelen = getline(&linebuf, &linecap, fdigests)) > 0) { p = linebuf; origin = strsep(&p, ":"); digest = strsep(&p, ":"); if (digest == NULL) { pkg_emit_error("invalid digest file format"); rc = EPKG_FATAL; goto cleanup; } if (!pkg_checksum_is_valid(digest, strlen(digest))) { legacy_repo = true; pkg_debug(1, "repository '%s' has a legacy digests format", repo->name); } } fseek(fdigests, 0, SEEK_SET); /* Load local repo data */ while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) { pkg_get(pkg, PKG_ORIGIN, &origin, legacy_repo ? PKG_OLD_DIGEST : PKG_DIGEST, &digest); pkg_repo_update_increment_item_new(&ldel, origin, digest, 4, 0); } pkg_debug(1, "Pkgrepo, reading new packagesite.yaml for '%s'", name); /* load the while digests */ while ((linelen = getline(&linebuf, &linecap, fdigests)) > 0) { p = linebuf; origin = strsep(&p, ":"); digest = strsep(&p, ":"); offset = strsep(&p, ":"); /* files offset */ strsep(&p, ":"); length = strsep(&p, ":"); if (origin == NULL || digest == NULL || offset == NULL) { pkg_emit_error("invalid digest file format"); rc = EPKG_FATAL; goto cleanup; } errno = 0; num_offset = (long)strtoul(offset, NULL, 10); if (errno != 0) { pkg_emit_errno("strtoul", "digest format error"); rc = EPKG_FATAL; goto cleanup; } if (length != NULL) { errno = 0; num_length = (long)strtoul(length, NULL, 10); if (errno != 0) { pkg_emit_errno("strtoul", "digest format error"); rc = EPKG_FATAL; goto cleanup; } } else { num_length = 0; } processed++; HASH_FIND_STR(ldel, origin, item); if (item == NULL) { added++; pkg_repo_update_increment_item_new(&ladd, origin, digest, num_offset, num_length); } else { if (strcmp(digest, item->digest) == 0) { free(item->origin); free(item->digest); HASH_DEL(ldel, item); free(item); item = NULL; } else { free(item->origin); free(item->digest); HASH_DEL(ldel, item); free(item); item = NULL; pkg_repo_update_increment_item_new(&ladd, origin, digest, num_offset, num_length); updated++; } } } rc = EPKG_OK; pkg_debug(1, "Pkgrepo, removing old entries for '%s'", name); sql_exec(sqlite, "CREATE TABLE IF NOT EXISTS repo_update (x INTEGER);"); in_trans = true; rc = pkgdb_transaction_begin(sqlite, "REPO"); if (rc != EPKG_OK) goto cleanup; removed = HASH_COUNT(ldel); hash_it = 0; pkg_emit_progress_start("Removing expired entries"); HASH_ITER(hh, ldel, item, tmp_item) { pkg_emit_progress_tick(++hash_it, removed); if (rc == EPKG_OK) { rc = pkgdb_repo_remove_package(item->origin); } free(item->origin); free(item->digest); HASH_DEL(ldel, item); free(item); }
static int pkg_repo_binary_apply_change(struct pkg_repo *repo, sqlite3 *sqlite, const struct repo_changes *repo_changes, const char *updown, int version, int *next_version) { const struct repo_changes *change; bool found = false, in_trans = false; int ret = EPKG_OK; char *errmsg; for (change = repo_changes; change->version != -1; change++) { if (change->version == version) { found = true; break; } } if (!found) { pkg_emit_error("Failed to %s \"%s\" repo schema " " version %d (target version %d) " "-- change not found", updown, repo->name, version, REPO_SCHEMA_VERSION); return (EPKG_FATAL); } /* begin transaction */ if ((ret = pkgdb_transaction_begin_sqlite(sqlite, "SCHEMA")) == EPKG_OK) in_trans = true; /* apply change */ if (ret == EPKG_OK) { pkg_debug(4, "Pkgdb: running '%s'", change->sql); ret = sqlite3_exec(sqlite, change->sql, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { pkg_emit_error("sqlite: %s", errmsg); sqlite3_free(errmsg); ret = EPKG_FATAL; } } /* update repo user_version */ if (ret == EPKG_OK) { *next_version = change->next_version; ret = pkg_repo_binary_set_version(sqlite, *next_version); } /* commit or rollback */ if (in_trans) { if (ret != EPKG_OK) pkgdb_transaction_rollback_sqlite(sqlite, "SCHEMA"); if (pkgdb_transaction_commit_sqlite(sqlite, "SCHEMA") != EPKG_OK) ret = EPKG_FATAL; } if (ret == EPKG_OK) { pkg_emit_notice("Repo \"%s\" %s schema %d to %d: %s", repo->name, updown, version, change->next_version, change->message); } return (ret); }