int pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *data) { FTS *fts = NULL; FTSENT *ent = NULL; struct stat st; struct pkg *pkg = NULL; struct pkg_dep *dep = NULL; struct pkg_category *category = NULL; struct pkg_license *license = NULL; struct pkg_option *option = NULL; struct sbuf *manifest = sbuf_new_auto(); char *ext = NULL; sqlite3 *sqlite = NULL; sqlite3_stmt *stmt_deps = NULL; sqlite3_stmt *stmt_pkg = NULL; sqlite3_stmt *stmt_lic1 = NULL; sqlite3_stmt *stmt_lic2 = NULL; sqlite3_stmt *stmt_cat1 = NULL; sqlite3_stmt *stmt_cat2 = NULL; sqlite3_stmt *stmt_opts = NULL; int64_t package_id; char *errmsg = NULL; int retcode = EPKG_OK; char *pkg_path; char cksum[SHA256_DIGEST_LENGTH * 2 +1]; char *repopath[2]; char repodb[MAXPATHLEN + 1]; const char initsql[] = "" "CREATE TABLE packages (" "id INTEGER PRIMARY KEY," "origin TEXT UNIQUE," "name TEXT NOT NULL," "version TEXT NOT NULL," "comment TEXT NOT NULL," "desc TEXT NOT NULL," "arch TEXT NOT NULL," "osversion TEXT NOT NULL," "maintainer TEXT NOT NULL," "www TEXT," "prefix TEXT NOT NULL," "pkgsize INTEGER NOT NULL," "flatsize INTEGER NOT NULL," "licenselogic INTEGER NOT NULL," "cksum TEXT NOT NULL," "path TEXT NOT NULL," /* relative path to the package in the repository */ "pkg_format_version INTEGER" ");" "CREATE TABLE deps (" "origin TEXT," "name TEXT," "version TEXT," "package_id INTEGER REFERENCES packages(id)," "UNIQUE(package_id, origin)" ");" "CREATE TABLE categories (" "id INTEGER PRIMARY KEY, " "name TEXT NOT NULL UNIQUE " ");" "CREATE TABLE pkg_categories (" "package_id INTEGER REFERENCES packages(id), " "category_id INTEGER REFERENCES categories(id), " "UNIQUE(package_id, category_id)" ");" "CREATE TABLE licenses (" "id INTEGER PRIMARY KEY," "name TEXT NOT NULL UNIQUE" ");" "CREATE TABLE pkg_licenses (" "package_id INTEGER REFERENCES packages(id), " "license_id INTEGER REFERENCES licenses(id), " "UNIQUE(package_id, license_id)" ");" "CREATE TABLE options (" "package_id INTEGER REFERENCES packages(id), " "option TEXT," "value TEXT," "UNIQUE (package_id, option)" ");" "PRAGMA user_version=2;" ; const char pkgsql[] = "" "INSERT INTO packages (" "origin, name, version, comment, desc, arch, osversion, " "maintainer, www, prefix, pkgsize, flatsize, licenselogic, cksum, path" ")" "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15);"; const char depssql[] = "" "INSERT INTO deps (origin, name, version, package_id) " "VALUES (?1, ?2, ?3, ?4);"; const char licsql[] = "INSERT OR IGNORE INTO licenses(name) VALUES(?1);"; const char addlicsql[] = "INSERT OR ROLLBACK INTO pkg_licenses(package_id, license_id) " "VALUES (?1, (SELECT id FROM licenses WHERE name = ?2));"; const char catsql[] = "INSERT OR IGNORE INTO categories(name) VALUES(?1);"; const char addcatsql[] = "INSERT OR ROLLBACK INTO pkg_categories(package_id, category_id) " "VALUES (?1, (SELECT id FROM categories WHERE name = ?2));"; const char addoption[] = "INSERT OR ROLLBACK INTO options (option, value, package_id) " "VALUES (?1, ?2, ?3);"; if (!is_dir(path)) { pkg_emit_error("%s is not a directory", path); return EPKG_FATAL; } repopath[0] = path; repopath[1] = NULL; snprintf(repodb, sizeof(repodb), "%s/repo.sqlite", path); if (stat(repodb, &st) != -1) if (unlink(repodb) != 0) { pkg_emit_errno("unlink", path); return EPKG_FATAL; } sqlite3_initialize(); if (sqlite3_open(repodb, &sqlite) != SQLITE_OK) { sqlite3_shutdown(); return (EPKG_FATAL); } if ((retcode = sql_exec(sqlite, initsql)) != EPKG_OK) goto cleanup; if ((retcode = sql_exec(sqlite, "BEGIN TRANSACTION;")) != EPKG_OK) goto cleanup; if (sqlite3_prepare_v2(sqlite, pkgsql, -1, &stmt_pkg, NULL) != SQLITE_OK) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_prepare_v2(sqlite, depssql, -1, &stmt_deps, NULL) != SQLITE_OK) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_prepare_v2(sqlite, licsql, -1, &stmt_lic1, NULL) != SQLITE_OK) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_prepare_v2(sqlite, addlicsql, -1, &stmt_lic2, NULL) != SQLITE_OK) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_prepare_v2(sqlite, addoption, -1, &stmt_opts, NULL) != SQLITE_OK) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if ((fts = fts_open(repopath, FTS_PHYSICAL, NULL)) == NULL) { pkg_emit_errno("fts_open", path); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_prepare_v2(sqlite, catsql, -1, &stmt_cat1, NULL) != SQLITE_OK) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_prepare_v2(sqlite, addcatsql, -1, &stmt_cat2, NULL) != SQLITE_OK) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } while ((ent = fts_read(fts)) != NULL) { cksum[0] = '\0'; /* skip everything that is not a file */ if (ent->fts_info != FTS_F) continue; ext = strrchr(ent->fts_name, '.'); if (ext == NULL) continue; if (strcmp(ext, ".tgz") != 0 && strcmp(ext, ".tbz") != 0 && strcmp(ext, ".txz") != 0 && strcmp(ext, ".tar") != 0) continue; pkg_path = ent->fts_path; pkg_path += strlen(path); while (pkg_path[0] == '/' ) pkg_path++; if (pkg_open(&pkg, ent->fts_accpath, manifest) != EPKG_OK) { retcode = EPKG_WARN; continue; } if (progress != NULL) progress(pkg, data); sqlite3_bind_text(stmt_pkg, 1, pkg_get(pkg, PKG_ORIGIN), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 2, pkg_get(pkg, PKG_NAME), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 3, pkg_get(pkg, PKG_VERSION), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 4, pkg_get(pkg, PKG_COMMENT), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 5, pkg_get(pkg, PKG_DESC), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 6, pkg_get(pkg, PKG_ARCH), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 7, pkg_get(pkg, PKG_OSVERSION), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 8, pkg_get(pkg, PKG_MAINTAINER), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 9, pkg_get(pkg, PKG_WWW), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 10, pkg_get(pkg, PKG_PREFIX), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_pkg, 11, ent->fts_statp->st_size); sqlite3_bind_int64(stmt_pkg, 12, pkg_flatsize(pkg)); sha256_file(ent->fts_accpath, cksum); sqlite3_bind_int64(stmt_pkg, 13, pkg_licenselogic(pkg)); sqlite3_bind_text(stmt_pkg, 14, cksum, -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 15, pkg_path, -1, SQLITE_STATIC); if (sqlite3_step(stmt_pkg) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } sqlite3_reset(stmt_pkg); package_id = sqlite3_last_insert_rowid(sqlite); dep = NULL; while (pkg_deps(pkg, &dep) == EPKG_OK) { sqlite3_bind_text(stmt_deps, 1, pkg_dep_origin(dep), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_deps, 2, pkg_dep_name(dep), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_deps, 3, pkg_dep_version(dep), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_deps, 4, package_id); if (sqlite3_step(stmt_deps) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } sqlite3_reset(stmt_deps); } category = NULL; while (pkg_categories(pkg, &category) == EPKG_OK) { sqlite3_bind_text(stmt_cat1, 1, pkg_category_name(category), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_cat2, 1, package_id); sqlite3_bind_text(stmt_cat2, 2, pkg_category_name(category), -1, SQLITE_STATIC); if (sqlite3_step(stmt_cat1) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_step(stmt_cat2) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } sqlite3_reset(stmt_cat1); sqlite3_reset(stmt_cat2); } license = NULL; while (pkg_licenses(pkg, &license) == EPKG_OK) { sqlite3_bind_text(stmt_lic1, 1, pkg_license_name(license), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_lic2, 1, package_id); sqlite3_bind_text(stmt_lic2, 2, pkg_license_name(license), -1, SQLITE_STATIC); if (sqlite3_step(stmt_lic1) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } if (sqlite3_step(stmt_lic2) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } sqlite3_reset(stmt_lic1); sqlite3_reset(stmt_lic2); } option = NULL; while (pkg_options(pkg, &option) == EPKG_OK) { sqlite3_bind_text(stmt_opts, 1, pkg_option_opt(option), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_opts, 2, pkg_option_value(option), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_opts, 3, package_id); if (sqlite3_step(stmt_opts) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } sqlite3_reset(stmt_opts); } } if (sqlite3_exec(sqlite, "COMMIT;", NULL, NULL, &errmsg) != SQLITE_OK) { pkg_emit_error("sqlite: %s", errmsg); retcode = EPKG_FATAL; } cleanup: if (fts != NULL) fts_close(fts); if (pkg != NULL) pkg_free(pkg); if (stmt_pkg != NULL) sqlite3_finalize(stmt_pkg); if (stmt_deps != NULL) sqlite3_finalize(stmt_deps); if (stmt_cat1 != NULL) sqlite3_finalize(stmt_cat1); if (stmt_cat2 != NULL) sqlite3_finalize(stmt_cat2); if (stmt_lic1 != NULL) sqlite3_finalize(stmt_lic1); if (stmt_lic2 != NULL) sqlite3_finalize(stmt_lic2); if (stmt_opts != NULL) sqlite3_finalize(stmt_opts); if (sqlite != NULL) sqlite3_close(sqlite); if (errmsg != NULL) sqlite3_free(errmsg); sbuf_delete(manifest); sqlite3_shutdown(); return (retcode); }
int pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg) { struct pkg **deps; struct pkg_file **files; struct pkg_conflict **conflicts; struct pkg_script **scripts; struct pkg_option **options; sqlite3 *s; sqlite3_stmt *stmt_pkg = NULL; sqlite3_stmt *stmt_sel_pkg = NULL; sqlite3_stmt *stmt_dep = NULL; sqlite3_stmt *stmt_conflict = NULL; sqlite3_stmt *stmt_file = NULL; sqlite3_stmt *stmt_script = NULL; sqlite3_stmt *stmt_option = NULL; sqlite3_stmt *stmt_dirs = NULL; int i; int ret; int retcode = EPKG_OK; const char *path; int64_t package_id; char *errmsg; const char sql_begin[] = "BEGIN TRANSACTION;"; const char sql_pkg[] = "" "INSERT INTO pkg_mtree( " "origin, name, version, comment, desc, mtree, message, arch, " "osversion, maintainer, www, prefix, flatsize) " "VALUES( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13);"; const char sql_sel_pkg[] = "" "SELECT id FROM packages " "WHERE origin = ?1;"; const char sql_dep[] = "" "INSERT INTO deps (origin, name, version, package_id) " "VALUES (?1, ?2, ?3, ?4);"; const char sql_conflict[] = "" "INSERT INTO conflicts (name, package_id) " "VALUES (?1, ?2);"; const char sql_file[] = "" "INSERT INTO files (path, sha256, package_id) " "VALUES (?1, ?2, ?3);"; const char sql_script[] = "" "INSERT INTO scripts (script, type, package_id) " "VALUES (?1, ?2, ?3);"; const char sql_option[] = "" "INSERT INTO options (option, value, package_id) " "VALUES (?1, ?2, ?3);"; const char sql_dir[] = "" "INSERT INTO pkg_dirs(origin, path) " "VALUES (?1, ?2);"; if (pkgdb_has_flag(db, PKGDB_FLAG_IN_FLIGHT)) { pkg_error_set(EPKG_FATAL, "tried to register a package with an in-flight SQL command"); return (EPKG_FATAL); } s = db->sqlite; if (sqlite3_exec(s, sql_begin, NULL, NULL, &errmsg) != SQLITE_OK) { pkg_error_set(EPKG_FATAL, "sqlite: %s", errmsg); sqlite3_free(errmsg); return (EPKG_FATAL); } PKGDB_SET_FLAG(db, PKGDB_FLAG_IN_FLIGHT); /* * Insert package record */ if (sqlite3_prepare_v2(s, sql_pkg, -1, &stmt_pkg, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_bind_text(stmt_pkg, 1, pkg_get(pkg, PKG_ORIGIN), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 2, pkg_get(pkg, PKG_NAME), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 3, pkg_get(pkg, PKG_VERSION), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 4, pkg_get(pkg, PKG_COMMENT), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 5, pkg_get(pkg, PKG_DESC), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 6, pkg_get(pkg, PKG_MTREE), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 7, pkg_get(pkg, PKG_MESSAGE), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 8, pkg_get(pkg, PKG_ARCH), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 9, pkg_get(pkg, PKG_OSVERSION), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 10, pkg_get(pkg, PKG_MAINTAINER), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 11, pkg_get(pkg, PKG_WWW), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_pkg, 12, pkg_get(pkg, PKG_PREFIX), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_pkg, 13, pkg_flatsize(pkg)); if ((ret = sqlite3_step(stmt_pkg)) != SQLITE_DONE) { if ( ret == SQLITE_CONSTRAINT) retcode = pkg_error_set(EPKG_FATAL, "constraint violation on " "pkg with %s", pkg_get(pkg, PKG_ORIGIN)); else retcode = ERROR_SQLITE(s); goto cleanup; } /* * Get the generated package_id */ if (sqlite3_prepare_v2(s, sql_sel_pkg, -1, &stmt_sel_pkg, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_bind_text(stmt_sel_pkg, 1, pkg_get(pkg, PKG_ORIGIN), -1, SQLITE_STATIC); ret = sqlite3_step(stmt_sel_pkg); if (ret == SQLITE_ROW) { package_id = sqlite3_column_int64(stmt_sel_pkg, 0); ret = SQLITE_DONE; } else { retcode = ERROR_SQLITE(s); goto cleanup; } /* * Insert dependencies list */ if (sqlite3_prepare_v2(s, sql_dep, -1, &stmt_dep, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } deps = pkg_deps(pkg); for (i = 0; deps[i] != NULL; i++) { sqlite3_bind_text(stmt_dep, 1, pkg_get(deps[i], PKG_ORIGIN), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_dep, 2, pkg_get(deps[i], PKG_NAME), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_dep, 3, pkg_get(deps[i], PKG_VERSION), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_dep, 4, package_id); if ((ret = sqlite3_step(stmt_dep)) != SQLITE_DONE) { if ( ret == SQLITE_CONSTRAINT) retcode = pkg_error_set(EPKG_FATAL, "constraint violation on " "deps with %s", pkg_get(deps[i], PKG_ORIGIN)); else retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_reset(stmt_dep); } /* * Insert conflicts list */ if (sqlite3_prepare_v2(s, sql_conflict, -1, &stmt_conflict, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } conflicts = pkg_conflicts(pkg); for (i = 0; conflicts[i] != NULL; i++) { sqlite3_bind_text(stmt_conflict, 1, pkg_conflict_glob(conflicts[i]), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_conflict, 2, package_id); if ((ret = sqlite3_step(stmt_conflict)) != SQLITE_DONE) { if ( ret == SQLITE_CONSTRAINT) retcode = pkg_error_set(EPKG_FATAL, "constraint violation on " "conflicts with %s", pkg_conflict_glob(conflicts[i])); else retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_reset(stmt_conflict); } /* * Insert file * and dirs */ if (sqlite3_prepare_v2(s, sql_file, -1, &stmt_file, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } if (sqlite3_prepare_v2(s, sql_dir, -1, &stmt_dirs, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } files = pkg_files(pkg); for (i = 0; files[i] != NULL; i++) { path = pkg_file_path(files[i]); if (path[strlen(path) - 1 ] != '/') { sqlite3_bind_text(stmt_file, 1, pkg_file_path(files[i]), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_file, 2, pkg_file_sha256(files[i]), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_file, 3, package_id); if ((ret = sqlite3_step(stmt_file)) != SQLITE_DONE) { if ( ret == SQLITE_CONSTRAINT) retcode = pkg_error_set(EPKG_FATAL, "constraint violation on " "path with %s", pkg_file_path(files[i])); else retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_reset(stmt_file); } else { sqlite3_bind_text(stmt_dirs, 1, pkg_get(pkg, PKG_ORIGIN), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_dirs, 2, pkg_file_path(files[i]), -1, SQLITE_STATIC); if ((ret = sqlite3_step(stmt_dirs)) != SQLITE_DONE) { if ( ret == SQLITE_CONSTRAINT) retcode = pkg_error_set(EPKG_FATAL, "constraint violation on " "dirs with %s", pkg_file_path(files[i])); else retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_reset(stmt_dirs); } } /* * Insert scripts */ if (sqlite3_prepare_v2(s, sql_script, -1, &stmt_script, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } scripts = pkg_scripts(pkg); for (i = 0; scripts[i] != NULL; i++) { sqlite3_bind_text(stmt_script, 1, pkg_script_data(scripts[i]), -1, SQLITE_STATIC); sqlite3_bind_int(stmt_script, 2, pkg_script_type(scripts[i])); sqlite3_bind_int64(stmt_script, 3, package_id); if (sqlite3_step(stmt_script) != SQLITE_DONE) { if ( ret == SQLITE_CONSTRAINT) retcode = pkg_error_set(EPKG_FATAL, "constraint violation on " "scripts with %s", pkg_script_data(scripts[i])); else retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_reset(stmt_script); } /* * Insert options */ options = pkg_options(pkg); if (sqlite3_prepare_v2(s, sql_option, -1, &stmt_option, NULL) != SQLITE_OK) { retcode = ERROR_SQLITE(s); goto cleanup; } for (i = 0; options[i] != NULL; i++) { sqlite3_bind_text(stmt_option, 1, pkg_option_opt(options[i]), -1, SQLITE_STATIC); sqlite3_bind_text(stmt_option, 2, pkg_option_value(options[i]), -1, SQLITE_STATIC); sqlite3_bind_int64(stmt_option, 3, package_id); if (sqlite3_step(stmt_option) != SQLITE_DONE) { retcode = ERROR_SQLITE(s); goto cleanup; } sqlite3_reset(stmt_option); } cleanup: if (stmt_pkg != NULL) sqlite3_finalize(stmt_pkg); if (stmt_sel_pkg != NULL) sqlite3_finalize(stmt_sel_pkg); if (stmt_dep != NULL) sqlite3_finalize(stmt_dep); if (stmt_conflict != NULL) sqlite3_finalize(stmt_conflict); if (stmt_file != NULL) sqlite3_finalize(stmt_file); if (stmt_script != NULL) sqlite3_finalize(stmt_script); if (stmt_option != NULL) sqlite3_finalize(stmt_option); if (stmt_dirs != NULL) sqlite3_finalize(stmt_dirs); return (retcode); }