/* * be_files.c * * Copyright (c) 2006-2010 Pacman Development Team * Copyright (c) 2002-2006 by Judd Vinet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include #include #include /* intmax_t */ #include #include #include #include #include /* PATH_MAX */ #include /* setlocale */ /* libarchive */ #include #include /* libalpm */ #include "db.h" #include "alpm_list.h" #include "log.h" #include "util.h" #include "alpm.h" #include "handle.h" #include "package.h" #include "group.h" #include "delta.h" #include "deps.h" #include "dload.h" #define LAZY_LOAD(info, errret) \ do { \ ALPM_LOG_FUNC; \ ASSERT(handle != NULL, return(errret)); \ ASSERT(pkg != NULL, return(errret)); \ if(pkg->origin != PKG_FROM_FILE && !(pkg->infolevel & info)) { \ _alpm_db_read(pkg->origin_data.db, pkg, info); \ } \ } while(0) /* Cache-specific accessor functions. These implementations allow for lazy * loading by the files backend when a data member is actually needed * rather than loading all pieces of information when the package is first * initialized. */ const char *_cache_get_filename(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->filename; } const char *_cache_get_name(pmpkg_t *pkg) { ASSERT(pkg != NULL, return(NULL)); return pkg->name; } static const char *_cache_get_version(pmpkg_t *pkg) { ASSERT(pkg != NULL, return(NULL)); return pkg->version; } static const char *_cache_get_desc(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->desc; } const char *_cache_get_url(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->url; } time_t _cache_get_builddate(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, 0); return pkg->builddate; } time_t _cache_get_installdate(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, 0); return pkg->installdate; } const char *_cache_get_packager(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->packager; } const char *_cache_get_md5sum(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->md5sum; } const char *_cache_get_arch(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->arch; } off_t _cache_get_size(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, -1); return pkg->size; } off_t _cache_get_isize(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, -1); return pkg->isize; } pmpkgreason_t _cache_get_reason(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, -1); return pkg->reason; } alpm_list_t *_cache_get_licenses(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->licenses; } alpm_list_t *_cache_get_groups(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->groups; } int _cache_has_force(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, -1); return pkg->force; } alpm_list_t *_cache_get_depends(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DEPENDS, NULL); return pkg->depends; } alpm_list_t *_cache_get_optdepends(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DEPENDS, NULL); return pkg->optdepends; } alpm_list_t *_cache_get_conflicts(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DEPENDS, NULL); return pkg->conflicts; } alpm_list_t *_cache_get_provides(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DEPENDS, NULL); return pkg->provides; } alpm_list_t *_cache_get_replaces(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DESC, NULL); return pkg->replaces; } alpm_list_t *_cache_get_deltas(pmpkg_t *pkg) { LAZY_LOAD(INFRQ_DELTAS, NULL); return pkg->deltas; } alpm_list_t *_cache_get_files(pmpkg_t *pkg) { ALPM_LOG_FUNC; /* Sanity checks */ ASSERT(handle != NULL, return(NULL)); ASSERT(pkg != NULL, return(NULL)); if(pkg->origin == PKG_FROM_LOCALDB && !(pkg->infolevel & INFRQ_FILES)) { _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); } return pkg->files; } alpm_list_t *_cache_get_backup(pmpkg_t *pkg) { ALPM_LOG_FUNC; /* Sanity checks */ ASSERT(handle != NULL, return(NULL)); ASSERT(pkg != NULL, return(NULL)); if(pkg->origin == PKG_FROM_LOCALDB && !(pkg->infolevel & INFRQ_FILES)) { _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); } return pkg->backup; } /** * Open a package changelog for reading. Similar to fopen in functionality, * except that the returned 'file stream' is from the database. * @param pkg the package (from db) to read the changelog * @return a 'file stream' to the package changelog */ void *_cache_changelog_open(pmpkg_t *pkg) { ALPM_LOG_FUNC; /* Sanity checks */ ASSERT(handle != NULL, return(NULL)); ASSERT(pkg != NULL, return(NULL)); char clfile[PATH_MAX]; snprintf(clfile, PATH_MAX, "%s/%s/%s-%s/changelog", alpm_option_get_dbpath(), alpm_db_get_name(alpm_pkg_get_db(pkg)), alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); return fopen(clfile, "r"); } /** * Read data from an open changelog 'file stream'. Similar to fread in * functionality, this function takes a buffer and amount of data to read. * @param ptr a buffer to fill with raw changelog data * @param size the size of the buffer * @param pkg the package that the changelog is being read from * @param fp a 'file stream' to the package changelog * @return the number of characters read, or 0 if there is no more data */ size_t _cache_changelog_read(void *ptr, size_t size, const pmpkg_t *pkg, const void *fp) { return ( fread(ptr, 1, size, (FILE*)fp) ); } /* int _cache_changelog_feof(const pmpkg_t *pkg, void *fp) { return( feof((FILE*)fp) ); } */ /** * Close a package changelog for reading. Similar to fclose in functionality, * except that the 'file stream' is from the database. * @param pkg the package that the changelog was read from * @param fp a 'file stream' to the package changelog * @return whether closing the package changelog stream was successful */ int _cache_changelog_close(const pmpkg_t *pkg, void *fp) { return( fclose((FILE*)fp) ); } /** The sync database operations struct. Get package fields through * lazy accessor methods that handle any backend loading and caching * logic. */ static struct pkg_operations sync_pkg_ops = { .get_filename = _cache_get_filename, .get_name = _cache_get_name, .get_version = _cache_get_version, .get_desc = _cache_get_desc, .get_url = _cache_get_url, .get_builddate = _cache_get_builddate, .get_installdate = _cache_get_installdate, .get_packager = _cache_get_packager, .get_md5sum = _cache_get_md5sum, .get_arch = _cache_get_arch, .get_size = _cache_get_size, .get_isize = _cache_get_isize, .get_reason = _cache_get_reason, .has_force = _cache_has_force, .get_licenses = _cache_get_licenses, .get_groups = _cache_get_groups, .get_depends = _cache_get_depends, .get_optdepends = _cache_get_optdepends, .get_conflicts = _cache_get_conflicts, .get_provides = _cache_get_provides, .get_replaces = _cache_get_replaces, .get_deltas = _cache_get_deltas, .get_files = _cache_get_files, .get_backup = _cache_get_backup, }; /** The local database operations struct. Get package fields through * lazy accessor methods that handle any backend loading and caching * logic. */ static struct pkg_operations local_pkg_ops = { .get_filename = _cache_get_filename, .get_name = _cache_get_name, .get_version = _cache_get_version, .get_desc = _cache_get_desc, .get_url = _cache_get_url, .get_builddate = _cache_get_builddate, .get_installdate = _cache_get_installdate, .get_packager = _cache_get_packager, .get_md5sum = _cache_get_md5sum, .get_arch = _cache_get_arch, .get_size = _cache_get_size, .get_isize = _cache_get_isize, .get_reason = _cache_get_reason, .has_force = _cache_has_force, .get_licenses = _cache_get_licenses, .get_groups = _cache_get_groups, .get_depends = _cache_get_depends, .get_optdepends = _cache_get_optdepends, .get_conflicts = _cache_get_conflicts, .get_provides = _cache_get_provides, .get_replaces = _cache_get_replaces, .get_deltas = _cache_get_deltas, .get_files = _cache_get_files, .get_backup = _cache_get_backup, .changelog_open = _cache_changelog_open, .changelog_read = _cache_changelog_read, .changelog_close = _cache_changelog_close, }; /* Returns a new package cache from db. * It frees the cache if it already exists. */ int _alpm_db_load_pkgcache(pmdb_t *db) { ALPM_LOG_FUNC; if(db == NULL) { return(-1); } _alpm_db_free_pkgcache(db); _alpm_log(PM_LOG_DEBUG, "loading package cache for repository '%s'\n", db->treename); if(_alpm_db_populate(db) == -1) { _alpm_log(PM_LOG_DEBUG, "failed to load package cache for repository '%s'\n", db->treename); return(-1); } db->pkgcache_loaded = 1; return(0); } void _alpm_db_free_pkgcache(pmdb_t *db) { ALPM_LOG_FUNC; if(db == NULL || !db->pkgcache_loaded) { return; } _alpm_log(PM_LOG_DEBUG, "freeing package cache for repository '%s'\n", db->treename); alpm_list_free_inner(db->pkgcache, (alpm_list_fn_free)_alpm_pkg_free); alpm_list_free(db->pkgcache); db->pkgcache_loaded = 0; _alpm_db_free_grpcache(db); } alpm_list_t *_alpm_db_get_pkgcache(pmdb_t *db) { ALPM_LOG_FUNC; if(db == NULL) { return(NULL); } if(!db->pkgcache_loaded) { _alpm_db_load_pkgcache(db); } /* hmmm, still NULL ?*/ if(!db->pkgcache) { _alpm_log(PM_LOG_DEBUG, "warning: pkgcache is NULL for db '%s'\n", db->treename); } return(db->pkgcache); } /* "duplicate" pkg then add it to pkgcache */ int _alpm_db_add_pkgincache(pmdb_t *db, pmpkg_t *pkg) { pmpkg_t *newpkg; ALPM_LOG_FUNC; if(db == NULL || !db->pkgcache_loaded || pkg == NULL) { return(-1); } newpkg = _alpm_pkg_dup(pkg); if(newpkg == NULL) { return(-1); } _alpm_log(PM_LOG_DEBUG, "adding entry '%s' in '%s' cache\n", alpm_pkg_get_name(newpkg), db->treename); db->pkgcache = alpm_list_add_sorted(db->pkgcache, newpkg, _alpm_pkg_cmp); _alpm_db_free_grpcache(db); return(0); } int _alpm_db_remove_pkgfromcache(pmdb_t *db, pmpkg_t *pkg) { void *vdata; pmpkg_t *data; ALPM_LOG_FUNC; if(db == NULL || !db->pkgcache_loaded || pkg == NULL) { return(-1); } _alpm_log(PM_LOG_DEBUG, "removing entry '%s' from '%s' cache\n", alpm_pkg_get_name(pkg), db->treename); db->pkgcache = alpm_list_remove(db->pkgcache, pkg, _alpm_pkg_cmp, &vdata); data = vdata; if(data == NULL) { /* package not found */ _alpm_log(PM_LOG_DEBUG, "cannot remove entry '%s' from '%s' cache: not found\n", alpm_pkg_get_name(pkg), db->treename); return(-1); } _alpm_pkg_free(data); _alpm_db_free_grpcache(db); return(0); } pmpkg_t *_alpm_db_get_pkgfromcache(pmdb_t *db, const char *target) { ALPM_LOG_FUNC; if(db == NULL) { return(NULL); } alpm_list_t *pkgcache = _alpm_db_get_pkgcache(db); if(!pkgcache) { _alpm_log(PM_LOG_DEBUG, "warning: failed to get '%s' from NULL pkgcache\n", target); return(NULL); } return(_alpm_pkg_find(pkgcache, target)); } /* Returns a new group cache from db. */ int _alpm_db_load_grpcache(pmdb_t *db) { alpm_list_t *lp; ALPM_LOG_FUNC; if(db == NULL) { return(-1); } _alpm_log(PM_LOG_DEBUG, "loading group cache for repository '%s'\n", db->treename); for(lp = _alpm_db_get_pkgcache(db); lp; lp = lp->next) { const alpm_list_t *i; pmpkg_t *pkg = lp->data; for(i = alpm_pkg_get_groups(pkg); i; i = i->next) { const char *grpname = i->data; alpm_list_t *j; pmgrp_t *grp = NULL; int found = 0; /* first look through the group cache for a group with this name */ for(j = db->grpcache; j; j = j->next) { grp = j->data; if(strcmp(grp->name, grpname) == 0 && !alpm_list_find_ptr(grp->packages, pkg)) { grp->packages = alpm_list_add(grp->packages, pkg); found = 1; break; } } if(found) { continue; } /* we didn't find the group, so create a new one with this name */ grp = _alpm_grp_new(grpname); grp->packages = alpm_list_add(grp->packages, pkg); db->grpcache = alpm_list_add(db->grpcache, grp); } } db->grpcache_loaded = 1; return(0); } void _alpm_db_free_grpcache(pmdb_t *db) { alpm_list_t *lg; ALPM_LOG_FUNC; if(db == NULL || !db->grpcache_loaded) { return; } _alpm_log(PM_LOG_DEBUG, "freeing group cache for repository '%s'\n", db->treename); for(lg = db->grpcache; lg; lg = lg->next) { _alpm_grp_free(lg->data); lg->data = NULL; } FREELIST(db->grpcache); db->grpcache_loaded = 0; } alpm_list_t *_alpm_db_get_grpcache(pmdb_t *db) { ALPM_LOG_FUNC; if(db == NULL) { return(NULL); } if(!db->grpcache_loaded) { _alpm_db_load_grpcache(db); } return(db->grpcache); } pmgrp_t *_alpm_db_get_grpfromcache(pmdb_t *db, const char *target) { alpm_list_t *i; ALPM_LOG_FUNC; if(db == NULL || target == NULL || strlen(target) == 0) { return(NULL); } for(i = _alpm_db_get_grpcache(db); i; i = i->next) { pmgrp_t *info = i->data; if(strcmp(info->name, target) == 0) { return(info); } } return(NULL); } static int checkdbdir(pmdb_t *db) { struct stat buf; const char *path = _alpm_db_path(db); if(stat(path, &buf) != 0) { _alpm_log(PM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n", path); if(_alpm_makepath(path) != 0) { RET_ERR(PM_ERR_SYSTEM, -1); } } else if(!S_ISDIR(buf.st_mode)) { _alpm_log(PM_LOG_WARNING, _("removing invalid database: %s\n"), path); if(unlink(path) != 0 || _alpm_makepath(path) != 0) { RET_ERR(PM_ERR_SYSTEM, -1); } } return(0); } /* create list of directories in db */ static int dirlist_from_tar(const char *archive, alpm_list_t **dirlist) { struct archive *_archive; struct archive_entry *entry; if((_archive = archive_read_new()) == NULL) RET_ERR(PM_ERR_LIBARCHIVE, -1); archive_read_support_compression_all(_archive); archive_read_support_format_all(_archive); if(archive_read_open_filename(_archive, archive, ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { _alpm_log(PM_LOG_ERROR, _("could not open %s: %s\n"), archive, archive_error_string(_archive)); RET_ERR(PM_ERR_PKG_OPEN, -1); } while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) { const struct stat *st; const char *entryname; /* the name of the file in the archive */ st = archive_entry_stat(entry); entryname = archive_entry_pathname(entry); if(S_ISDIR(st->st_mode)) { char *name = strdup(entryname); *dirlist = alpm_list_add(*dirlist, name); } } archive_read_finish(_archive); *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp); return(0); } static int is_dir(const char *path, struct dirent *entry) { #ifdef DT_DIR return(entry->d_type == DT_DIR); #else char buffer[PATH_MAX]; snprintf(buffer, PATH_MAX, "%s/%s", path, entry->d_name); struct stat sbuf; if (!stat(buffer, &sbuf)) { return(S_ISDIR(sbuf.st_mode)); } return(0); #endif } /* create list of directories in db */ static int dirlist_from_fs(const char *syncdbpath, alpm_list_t **dirlist) { DIR *dbdir; struct dirent *ent = NULL; dbdir = opendir(syncdbpath); if (dbdir != NULL) { while((ent = readdir(dbdir)) != NULL) { char *name = ent->d_name; size_t len; char *entry; if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } if(!is_dir(syncdbpath, ent)) { continue; } len = strlen(name); MALLOC(entry, len + 2, RET_ERR(PM_ERR_MEMORY, -1)); strcpy(entry, name); entry[len] = '/'; entry[len+1] = '\0'; *dirlist = alpm_list_add(*dirlist, entry); } closedir(dbdir); } *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp); return(0); } /* remove old directories from dbdir */ static int remove_olddir(const char *syncdbpath, alpm_list_t *dirlist) { alpm_list_t *i; for (i = dirlist; i; i = i->next) { const char *name = i->data; char *dbdir; size_t len = strlen(syncdbpath) + strlen(name) + 2; MALLOC(dbdir, len, RET_ERR(PM_ERR_MEMORY, -1)); snprintf(dbdir, len, "%s%s", syncdbpath, name); _alpm_log(PM_LOG_DEBUG, "removing: %s\n", dbdir); if(_alpm_rmrf(dbdir) != 0) { _alpm_log(PM_LOG_ERROR, _("could not remove database directory %s\n"), dbdir); free(dbdir); RET_ERR(PM_ERR_DB_REMOVE, -1); } free(dbdir); } 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. * * A transaction is necessary for this operation, in order to obtain a * database lock. During this transaction the front-end will be informed * of the download progress of the database via the download callback. * * Example: * @code * pmdb_t *db; * int result; * db = alpm_list_getdata(alpm_option_get_syncdbs()); * if(alpm_trans_init(0, NULL, NULL, NULL) == 0) { * result = alpm_db_update(0, db); * alpm_trans_release(); * * if(result > 0) { * printf("Unable to update database: %s\n", alpm_strerrorlast()); * } else if(result < 0) { * 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, > 0 on error (pm_errno is set accordingly), < 0 if up * to date */ int SYMEXPORT alpm_db_update(int force, pmdb_t *db) { char *dbfile, *dbfilepath, *syncpath; const char *dbpath, *syncdbpath; alpm_list_t *newdirlist = NULL, *olddirlist = NULL; alpm_list_t *onlynew = NULL, *onlyold = NULL; size_t len; int ret; ALPM_LOG_FUNC; /* Sanity checks */ ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); ASSERT(db != NULL && db != handle->db_local, RET_ERR(PM_ERR_WRONG_ARGS, -1)); /* Verify we are in a transaction. This is done _mainly_ because we need a DB * lock - if we update without a db lock, we may kludge some other pacman * process that _has_ a lock. */ ASSERT(handle->trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); ASSERT(handle->trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); if(!alpm_list_find_ptr(handle->dbs_sync, db)) { RET_ERR(PM_ERR_DB_NOT_FOUND, -1); } len = strlen(db->treename) + 4; MALLOC(dbfile, len, RET_ERR(PM_ERR_MEMORY, -1)); sprintf(dbfile, "%s.db", db->treename); dbpath = alpm_option_get_dbpath(); len = strlen(dbpath) + 6; MALLOC(syncpath, len, RET_ERR(PM_ERR_MEMORY, -1)); sprintf(syncpath, "%s%s", dbpath, "sync/"); ret = _alpm_download_single_file(dbfile, db->servers, syncpath, force); free(dbfile); free(syncpath); if(ret == 1) { /* files match, do nothing */ pm_errno = 0; return(1); } else if(ret == -1) { /* pm_errno was set by the download code */ _alpm_log(PM_LOG_DEBUG, "failed to sync db: %s\n", alpm_strerrorlast()); return(-1); } syncdbpath = _alpm_db_path(db); /* form the path to the db location */ len = strlen(dbpath) + strlen(db->treename) + 9; MALLOC(dbfilepath, len, RET_ERR(PM_ERR_MEMORY, -1)); sprintf(dbfilepath, "%ssync/%s.db", dbpath, db->treename); if(force) { /* if forcing update, remove the old dir and extract the db */ if(_alpm_rmrf(syncdbpath) != 0) { _alpm_log(PM_LOG_ERROR, _("could not remove database %s\n"), db->treename); RET_ERR(PM_ERR_DB_REMOVE, -1); } else { _alpm_log(PM_LOG_DEBUG, "database dir %s removed\n", _alpm_db_path(db)); } } else { /* if not forcing, only remove and extract what is necessary */ ret = dirlist_from_tar(dbfilepath, &newdirlist); if(ret) { goto cleanup; } ret = dirlist_from_fs(syncdbpath, &olddirlist); if(ret) { goto cleanup; } alpm_list_diff_sorted(olddirlist, newdirlist, _alpm_str_cmp, &onlyold, &onlynew); ret = remove_olddir(syncdbpath, onlyold); if(ret) { goto cleanup; } } /* Cache needs to be rebuilt */ _alpm_db_free_pkgcache(db); checkdbdir(db); ret = _alpm_unpack(dbfilepath, syncdbpath, onlynew, 0); cleanup: FREELIST(newdirlist); FREELIST(olddirlist); alpm_list_free(onlynew); alpm_list_free(onlyold); free(dbfilepath); if(ret) { RET_ERR(PM_ERR_SYSTEM, -1); } return(0); } static int splitname(const char *target, pmpkg_t *pkg) { /* the format of a db entry is as follows: * package-version-rel/ * package name can contain hyphens, so parse from the back- go back * two hyphens and we have split the version from the name. */ char *tmp, *p, *q; if(target == NULL || pkg == NULL) { return(-1); } STRDUP(tmp, target, RET_ERR(PM_ERR_MEMORY, -1)); p = tmp + strlen(tmp); /* do the magic parsing- find the beginning of the version string * by doing two iterations of same loop to lop off two hyphens */ for(q = --p; *q && *q != '-'; q--); for(p = --q; *p && *p != '-'; p--); if(*p != '-' || p == tmp) { return(-1); } /* copy into fields and return */ if(pkg->version) { FREE(pkg->version); } STRDUP(pkg->version, p+1, RET_ERR(PM_ERR_MEMORY, -1)); /* insert a terminator at the end of the name (on hyphen)- then copy it */ *p = '\0'; if(pkg->name) { FREE(pkg->name); } STRDUP(pkg->name, tmp, RET_ERR(PM_ERR_MEMORY, -1)); free(tmp); return(0); } int _alpm_db_populate(pmdb_t *db) { int count = 0; struct dirent *ent = NULL; const char *dbpath; DIR *dbdir; ALPM_LOG_FUNC; ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); dbpath = _alpm_db_path(db); dbdir = opendir(dbpath); if(dbdir == NULL) { return(0); } while((ent = readdir(dbdir)) != NULL) { const char *name = ent->d_name; pmpkg_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); return(-1); } /* split the db entry name */ if(splitname(name, pkg) != 0) { _alpm_log(PM_LOG_ERROR, _("invalid name for database entry '%s'\n"), name); _alpm_pkg_free(pkg); continue; } /* duplicated database entries are not allowed */ if(_alpm_pkg_find(db->pkgcache, pkg->name)) { _alpm_log(PM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name); _alpm_pkg_free(pkg); continue; } /* explicitly read with only 'BASE' data, accessors will handle the rest */ if(_alpm_db_read(db, pkg, INFRQ_BASE) == -1) { _alpm_log(PM_LOG_ERROR, _("corrupted database entry '%s'\n"), name); _alpm_pkg_free(pkg); continue; } if(db == handle->db_local) { pkg->origin = PKG_FROM_LOCALDB; pkg->ops = &local_pkg_ops; } else { pkg->origin = PKG_FROM_SYNCDB; pkg->ops = &sync_pkg_ops; } pkg->origin_data.db = db; /* add to the collection */ _alpm_log(PM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n", pkg->name, db->treename); db->pkgcache = alpm_list_add(db->pkgcache, pkg); count++; } closedir(dbdir); db->pkgcache = alpm_list_msort(db->pkgcache, count, _alpm_pkg_cmp); return(count); } /* Note: the return value must be freed by the caller */ static char *get_pkgpath(pmdb_t *db, pmpkg_t *info) { size_t len; char *pkgpath; const char *dbpath; dbpath = _alpm_db_path(db); len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3; MALLOC(pkgpath, len, RET_ERR(PM_ERR_MEMORY, NULL)); sprintf(pkgpath, "%s%s-%s/", dbpath, info->name, info->version); return(pkgpath); } int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) { FILE *fp = NULL; char path[PATH_MAX]; char line[1024]; char *pkgpath = NULL; ALPM_LOG_FUNC; if(db == NULL) { RET_ERR(PM_ERR_DB_NULL, -1); } if(info == NULL || info->name == NULL || info->version == NULL) { _alpm_log(PM_LOG_DEBUG, "invalid package entry provided to _alpm_db_read, skipping\n"); return(-1); } if(info->origin == PKG_FROM_FILE) { _alpm_log(PM_LOG_DEBUG, "request to read database info for a file-based 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 this info, do nothing */ return(0); } _alpm_log(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(PM_LOG_DEBUG, "cannot find '%s-%s' in db '%s'\n", info->name, info->version, db->treename); goto error; } /* DESC */ if(inforeq & INFRQ_DESC) { snprintf(path, PATH_MAX, "%sdesc", pkgpath); if((fp = fopen(path, "r")) == NULL) { _alpm_log(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(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(PM_LOG_ERROR, _("%s database is inconsistent: version " "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%FILENAME%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->filename, _alpm_strtrim(line), goto error); } 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, _alpm_strtrim(line), goto error); info->groups = alpm_list_add(info->groups, linedup); } } else if(strcmp(line, "%URL%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->url, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%LICENSE%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->licenses = alpm_list_add(info->licenses, linedup); } } else if(strcmp(line, "%ARCH%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->arch, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%BUILDDATE%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } _alpm_strtrim(line); char first = tolower((unsigned char)line[0]); if(first > 'a' && first < 'z') { struct tm tmp_tm = {0}; /* initialize to null in case of failure */ setlocale(LC_TIME, "C"); strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm); info->builddate = mktime(&tmp_tm); setlocale(LC_TIME, ""); } else { info->builddate = atol(line); } } else if(strcmp(line, "%INSTALLDATE%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } _alpm_strtrim(line); char first = tolower((unsigned char)line[0]); if(first > 'a' && first < 'z') { struct tm tmp_tm = {0}; /* initialize to null in case of failure */ setlocale(LC_TIME, "C"); strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm); info->installdate = mktime(&tmp_tm); setlocale(LC_TIME, ""); } else { info->installdate = atol(line); } } else if(strcmp(line, "%PACKAGER%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->packager, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%REASON%") == 0) { if(fgets(line, sizeof(line), fp) == NULL) { goto error; } info->reason = (pmpkgreason_t)atol(_alpm_strtrim(line)); } else if(strcmp(line, "%SIZE%") == 0 || strcmp(line, "%CSIZE%") == 0) { /* NOTE: the CSIZE and SIZE fields both share the "size" field * in the pkginfo_t struct. This can be done b/c CSIZE * is currently only used in sync databases, and SIZE is * only used in local databases. */ if(fgets(line, sizeof(line), fp) == NULL) { goto error; } info->size = atol(_alpm_strtrim(line)); /* also store this value to isize if isize is unset */ if(info->isize == 0) { info->isize = info->size; } } else if(strcmp(line, "%ISIZE%") == 0) { /* ISIZE (installed size) tag only appears in sync repositories, * not the local one. */ if(fgets(line, sizeof(line), fp) == NULL) { goto error; } info->isize = atol(_alpm_strtrim(line)); } else if(strcmp(line, "%MD5SUM%") == 0) { /* MD5SUM tag only appears in sync repositories, * not the local one. */ if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->md5sum, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%REPLACES%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->replaces = alpm_list_add(info->replaces, linedup); } } else if(strcmp(line, "%FORCE%") == 0) { info->force = 1; } } fclose(fp); fp = NULL; } /* FILES */ if(inforeq & INFRQ_FILES) { snprintf(path, PATH_MAX, "%sfiles", pkgpath); if((fp = fopen(path, "r")) == NULL) { _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); goto error; } while(fgets(line, sizeof(line), fp)) { _alpm_strtrim(line); if(strcmp(line, "%FILES%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->files = alpm_list_add(info->files, linedup); } } else if(strcmp(line, "%BACKUP%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->backup = alpm_list_add(info->backup, linedup); } } } fclose(fp); fp = NULL; } /* DEPENDS */ if(inforeq & INFRQ_DEPENDS) { snprintf(path, PATH_MAX, "%sdepends", pkgpath); if((fp = fopen(path, "r")) == NULL) { _alpm_log(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, "%DEPENDS%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { pmdepend_t *dep = _alpm_splitdep(_alpm_strtrim(line)); info->depends = alpm_list_add(info->depends, dep); } } else if(strcmp(line, "%OPTDEPENDS%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->optdepends = alpm_list_add(info->optdepends, linedup); } } else if(strcmp(line, "%CONFLICTS%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->conflicts = alpm_list_add(info->conflicts, linedup); } } else if(strcmp(line, "%PROVIDES%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->provides = alpm_list_add(info->provides, linedup); } } } fclose(fp); fp = NULL; } /* DELTAS */ if(inforeq & INFRQ_DELTAS) { snprintf(path, PATH_MAX, "%sdeltas", pkgpath); if((fp = fopen(path, "r"))) { while(!feof(fp)) { if(fgets(line, sizeof(line), fp) == NULL) { break; } _alpm_strtrim(line); if(strcmp(line, "%DELTAS%") == 0) { while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { pmdelta_t *delta = _alpm_delta_parse(line); if(delta) { info->deltas = alpm_list_add(info->deltas, delta); } } } } fclose(fp); fp = NULL; } } /* INSTALL */ if(inforeq & INFRQ_SCRIPTLET) { snprintf(path, PATH_MAX, "%sinstall", pkgpath); if(access(path, F_OK) == 0) { info->scriptlet = 1; } } /* internal */ info->infolevel |= inforeq; free(pkgpath); return(0); error: free(pkgpath); if(fp) { fclose(fp); } return(-1); } int _alpm_db_prepare(pmdb_t *db, pmpkg_t *info) { mode_t oldmask; int retval = 0; char *pkgpath = NULL; if(checkdbdir(db) != 0) { return(-1); } oldmask = umask(0000); pkgpath = get_pkgpath(db, info); if((retval = mkdir(pkgpath, 0755)) != 0) { _alpm_log(PM_LOG_ERROR, _("could not create directory %s: %s\n"), pkgpath, strerror(errno)); } free(pkgpath); umask(oldmask); return(retval); } int _alpm_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) { FILE *fp = NULL; char path[PATH_MAX]; mode_t oldmask; alpm_list_t *lp = NULL; int retval = 0; int local = 0; char *pkgpath = NULL; ALPM_LOG_FUNC; if(db == NULL || info == NULL) { return(-1); } pkgpath = get_pkgpath(db, info); /* make sure we have a sane umask */ oldmask = umask(0022); if(strcmp(db->treename, "local") == 0) { local = 1; } /* DESC */ if(inforeq & INFRQ_DESC) { _alpm_log(PM_LOG_DEBUG, "writing %s-%s DESC information back to db\n", info->name, info->version); snprintf(path, PATH_MAX, "%sdesc", pkgpath); if((fp = fopen(path, "w")) == NULL) { _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); retval = -1; goto cleanup; } fprintf(fp, "%%NAME%%\n%s\n\n" "%%VERSION%%\n%s\n\n", info->name, info->version); if(info->desc) { fprintf(fp, "%%DESC%%\n" "%s\n\n", info->desc); } if(info->groups) { fputs("%GROUPS%\n", fp); for(lp = info->groups; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } if(info->replaces) { fputs("%REPLACES%\n", fp); for(lp = info->replaces; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } if(info->force) { fprintf(fp, "%%FORCE%%\n\n"); } if(local) { if(info->url) { fprintf(fp, "%%URL%%\n" "%s\n\n", info->url); } if(info->licenses) { fputs("%LICENSE%\n", fp); for(lp = info->licenses; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } if(info->arch) { fprintf(fp, "%%ARCH%%\n" "%s\n\n", info->arch); } if(info->builddate) { fprintf(fp, "%%BUILDDATE%%\n" "%ld\n\n", info->builddate); } if(info->installdate) { fprintf(fp, "%%INSTALLDATE%%\n" "%ld\n\n", info->installdate); } if(info->packager) { fprintf(fp, "%%PACKAGER%%\n" "%s\n\n", info->packager); } if(info->isize) { /* only write installed size, csize is irrelevant once installed */ fprintf(fp, "%%SIZE%%\n" "%jd\n\n", (intmax_t)info->isize); } if(info->reason) { fprintf(fp, "%%REASON%%\n" "%u\n\n", info->reason); } } else { if(info->size) { fprintf(fp, "%%CSIZE%%\n" "%jd\n\n", (intmax_t)info->size); } if(info->isize) { fprintf(fp, "%%ISIZE%%\n" "%jd\n\n", (intmax_t)info->isize); } if(info->md5sum) { fprintf(fp, "%%MD5SUM%%\n" "%s\n\n", info->md5sum); } } fclose(fp); fp = NULL; } /* FILES */ if(local && (inforeq & INFRQ_FILES)) { _alpm_log(PM_LOG_DEBUG, "writing %s-%s FILES information back to db\n", info->name, info->version); snprintf(path, PATH_MAX, "%sfiles", pkgpath); if((fp = fopen(path, "w")) == NULL) { _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); retval = -1; goto cleanup; } if(info->files) { fprintf(fp, "%%FILES%%\n"); for(lp = info->files; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } if(info->backup) { fprintf(fp, "%%BACKUP%%\n"); for(lp = info->backup; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } fclose(fp); fp = NULL; } /* DEPENDS */ if(inforeq & INFRQ_DEPENDS) { _alpm_log(PM_LOG_DEBUG, "writing %s-%s DEPENDS information back to db\n", info->name, info->version); snprintf(path, PATH_MAX, "%sdepends", pkgpath); if((fp = fopen(path, "w")) == NULL) { _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); retval = -1; goto cleanup; } if(info->depends) { fputs("%DEPENDS%\n", fp); for(lp = info->depends; lp; lp = lp->next) { char *depstring = alpm_dep_compute_string(lp->data); fprintf(fp, "%s\n", depstring); free(depstring); } fprintf(fp, "\n"); } if(info->optdepends) { fputs("%OPTDEPENDS%\n", fp); for(lp = info->optdepends; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } if(info->conflicts) { fputs("%CONFLICTS%\n", fp); for(lp = info->conflicts; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } if(info->provides) { fputs("%PROVIDES%\n", fp); for(lp = info->provides; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } fclose(fp); fp = NULL; } /* INSTALL */ /* nothing needed here (script is automatically extracted) */ cleanup: umask(oldmask); free(pkgpath); if(fp) { fclose(fp); } return(retval); } int _alpm_db_remove(pmdb_t *db, pmpkg_t *info) { int ret = 0; char *pkgpath = NULL; ALPM_LOG_FUNC; if(db == NULL || info == NULL) { RET_ERR(PM_ERR_DB_NULL, -1); } pkgpath = get_pkgpath(db, info); ret = _alpm_rmrf(pkgpath); free(pkgpath); if(ret != 0) { ret = -1; } return(ret); } /* vim: set ts=2 sw=2 noet: */