libalpm
Arch Linux Package Manager Library
be_local.c
Go to the documentation of this file.
00001 /*
00002  *  be_local.c : backend for the local database
00003  *
00004  *  Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
00005  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00021 #include <unistd.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <errno.h>
00025 #include <string.h>
00026 #include <stdint.h> /* intmax_t */
00027 #include <sys/stat.h>
00028 #include <dirent.h>
00029 #include <limits.h> /* PATH_MAX */
00030 
00031 /* libalpm */
00032 #include "db.h"
00033 #include "alpm_list.h"
00034 #include "log.h"
00035 #include "util.h"
00036 #include "alpm.h"
00037 #include "handle.h"
00038 #include "package.h"
00039 #include "deps.h"
00040 
00041 static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq);
00042 
00043 #define LAZY_LOAD(info, errret) \
00044     do { \
00045         if(!(pkg->infolevel & info)) { \
00046             local_db_read(pkg, info); \
00047         } \
00048     } while(0)
00049 
00050 
00051 /* Cache-specific accessor functions. These implementations allow for lazy
00052  * loading by the files backend when a data member is actually needed
00053  * rather than loading all pieces of information when the package is first
00054  * initialized.
00055  */
00056 
00057 static const char *_cache_get_desc(alpm_pkg_t *pkg)
00058 {
00059     LAZY_LOAD(INFRQ_DESC, NULL);
00060     return pkg->desc;
00061 }
00062 
00063 static const char *_cache_get_url(alpm_pkg_t *pkg)
00064 {
00065     LAZY_LOAD(INFRQ_DESC, NULL);
00066     return pkg->url;
00067 }
00068 
00069 static alpm_time_t _cache_get_builddate(alpm_pkg_t *pkg)
00070 {
00071     LAZY_LOAD(INFRQ_DESC, 0);
00072     return pkg->builddate;
00073 }
00074 
00075 static alpm_time_t _cache_get_installdate(alpm_pkg_t *pkg)
00076 {
00077     LAZY_LOAD(INFRQ_DESC, 0);
00078     return pkg->installdate;
00079 }
00080 
00081 static const char *_cache_get_packager(alpm_pkg_t *pkg)
00082 {
00083     LAZY_LOAD(INFRQ_DESC, NULL);
00084     return pkg->packager;
00085 }
00086 
00087 static const char *_cache_get_arch(alpm_pkg_t *pkg)
00088 {
00089     LAZY_LOAD(INFRQ_DESC, NULL);
00090     return pkg->arch;
00091 }
00092 
00093 static off_t _cache_get_isize(alpm_pkg_t *pkg)
00094 {
00095     LAZY_LOAD(INFRQ_DESC, -1);
00096     return pkg->isize;
00097 }
00098 
00099 static alpm_pkgreason_t _cache_get_reason(alpm_pkg_t *pkg)
00100 {
00101     LAZY_LOAD(INFRQ_DESC, -1);
00102     return pkg->reason;
00103 }
00104 
00105 static alpm_list_t *_cache_get_licenses(alpm_pkg_t *pkg)
00106 {
00107     LAZY_LOAD(INFRQ_DESC, NULL);
00108     return pkg->licenses;
00109 }
00110 
00111 static alpm_list_t *_cache_get_groups(alpm_pkg_t *pkg)
00112 {
00113     LAZY_LOAD(INFRQ_DESC, NULL);
00114     return pkg->groups;
00115 }
00116 
00117 static int _cache_has_scriptlet(alpm_pkg_t *pkg)
00118 {
00119     LAZY_LOAD(INFRQ_SCRIPTLET, NULL);
00120     return pkg->scriptlet;
00121 }
00122 
00123 static alpm_list_t *_cache_get_depends(alpm_pkg_t *pkg)
00124 {
00125     LAZY_LOAD(INFRQ_DESC, NULL);
00126     return pkg->depends;
00127 }
00128 
00129 static alpm_list_t *_cache_get_optdepends(alpm_pkg_t *pkg)
00130 {
00131     LAZY_LOAD(INFRQ_DESC, NULL);
00132     return pkg->optdepends;
00133 }
00134 
00135 static alpm_list_t *_cache_get_conflicts(alpm_pkg_t *pkg)
00136 {
00137     LAZY_LOAD(INFRQ_DESC, NULL);
00138     return pkg->conflicts;
00139 }
00140 
00141 static alpm_list_t *_cache_get_provides(alpm_pkg_t *pkg)
00142 {
00143     LAZY_LOAD(INFRQ_DESC, NULL);
00144     return pkg->provides;
00145 }
00146 
00147 static alpm_list_t *_cache_get_replaces(alpm_pkg_t *pkg)
00148 {
00149     LAZY_LOAD(INFRQ_DESC, NULL);
00150     return pkg->replaces;
00151 }
00152 
00153 static alpm_filelist_t *_cache_get_files(alpm_pkg_t *pkg)
00154 {
00155     LAZY_LOAD(INFRQ_FILES, NULL);
00156     return &(pkg->files);
00157 }
00158 
00159 static alpm_list_t *_cache_get_backup(alpm_pkg_t *pkg)
00160 {
00161     LAZY_LOAD(INFRQ_FILES, NULL);
00162     return pkg->backup;
00163 }
00164 
00165 /**
00166  * Open a package changelog for reading. Similar to fopen in functionality,
00167  * except that the returned 'file stream' is from the database.
00168  * @param pkg the package (from db) to read the changelog
00169  * @return a 'file stream' to the package changelog
00170  */
00171 static void *_cache_changelog_open(alpm_pkg_t *pkg)
00172 {
00173     alpm_db_t *db = alpm_pkg_get_db(pkg);
00174     char *clfile = _alpm_local_db_pkgpath(db, pkg, "changelog");
00175     FILE *f = fopen(clfile, "r");
00176     free(clfile);
00177     return f;
00178 }
00179 
00180 /**
00181  * Read data from an open changelog 'file stream'. Similar to fread in
00182  * functionality, this function takes a buffer and amount of data to read.
00183  * @param ptr a buffer to fill with raw changelog data
00184  * @param size the size of the buffer
00185  * @param pkg the package that the changelog is being read from
00186  * @param fp a 'file stream' to the package changelog
00187  * @return the number of characters read, or 0 if there is no more data
00188  */
00189 static size_t _cache_changelog_read(void *ptr, size_t size,
00190         const alpm_pkg_t UNUSED *pkg, void *fp)
00191 {
00192     return fread(ptr, 1, size, (FILE *)fp);
00193 }
00194 
00195 /**
00196  * Close a package changelog for reading. Similar to fclose in functionality,
00197  * except that the 'file stream' is from the database.
00198  * @param pkg the package that the changelog was read from
00199  * @param fp a 'file stream' to the package changelog
00200  * @return whether closing the package changelog stream was successful
00201  */
00202 static int _cache_changelog_close(const alpm_pkg_t UNUSED *pkg, void *fp)
00203 {
00204     return fclose((FILE *)fp);
00205 }
00206 
00207 static int _cache_force_load(alpm_pkg_t *pkg)
00208 {
00209     return local_db_read(pkg, INFRQ_ALL);
00210 }
00211 
00212 
00213 /** The local database operations struct. Get package fields through
00214  * lazy accessor methods that handle any backend loading and caching
00215  * logic.
00216  */
00217 static struct pkg_operations local_pkg_ops = {
00218     .get_desc        = _cache_get_desc,
00219     .get_url         = _cache_get_url,
00220     .get_builddate   = _cache_get_builddate,
00221     .get_installdate = _cache_get_installdate,
00222     .get_packager    = _cache_get_packager,
00223     .get_arch        = _cache_get_arch,
00224     .get_isize       = _cache_get_isize,
00225     .get_reason      = _cache_get_reason,
00226     .has_scriptlet   = _cache_has_scriptlet,
00227     .get_licenses    = _cache_get_licenses,
00228     .get_groups      = _cache_get_groups,
00229     .get_depends     = _cache_get_depends,
00230     .get_optdepends  = _cache_get_optdepends,
00231     .get_conflicts   = _cache_get_conflicts,
00232     .get_provides    = _cache_get_provides,
00233     .get_replaces    = _cache_get_replaces,
00234     .get_files       = _cache_get_files,
00235     .get_backup      = _cache_get_backup,
00236 
00237     .changelog_open  = _cache_changelog_open,
00238     .changelog_read  = _cache_changelog_read,
00239     .changelog_close = _cache_changelog_close,
00240 
00241     .force_load      = _cache_force_load,
00242 };
00243 
00244 static int checkdbdir(alpm_db_t *db)
00245 {
00246     struct stat buf;
00247     const char *path = _alpm_db_path(db);
00248 
00249     if(stat(path, &buf) != 0) {
00250         _alpm_log(db->handle, ALPM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n",
00251                 path);
00252         if(_alpm_makepath(path) != 0) {
00253             RET_ERR(db->handle, ALPM_ERR_SYSTEM, -1);
00254         }
00255     } else if(!S_ISDIR(buf.st_mode)) {
00256         _alpm_log(db->handle, ALPM_LOG_WARNING, _("removing invalid database: %s\n"), path);
00257         if(unlink(path) != 0 || _alpm_makepath(path) != 0) {
00258             RET_ERR(db->handle, ALPM_ERR_SYSTEM, -1);
00259         }
00260     }
00261     return 0;
00262 }
00263 
00264 static int is_dir(const char *path, struct dirent *entry)
00265 {
00266 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
00267     if(entry->d_type != DT_UNKNOWN) {
00268         return (entry->d_type == DT_DIR);
00269     }
00270 #endif
00271     {
00272         char buffer[PATH_MAX];
00273         struct stat sbuf;
00274 
00275         snprintf(buffer, PATH_MAX, "%s/%s", path, entry->d_name);
00276 
00277         if(!stat(buffer, &sbuf)) {
00278             return S_ISDIR(sbuf.st_mode);
00279         }
00280     }
00281 
00282     return 0;
00283 }
00284 
00285 static int local_db_validate(alpm_db_t *db)
00286 {
00287     struct dirent *ent = NULL;
00288     const char *dbpath;
00289     DIR *dbdir;
00290     int ret = -1;
00291 
00292     if(db->status & DB_STATUS_VALID) {
00293         return 0;
00294     }
00295     if(db->status & DB_STATUS_INVALID) {
00296         return -1;
00297     }
00298 
00299     dbpath = _alpm_db_path(db);
00300     if(dbpath == NULL) {
00301         RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
00302     }
00303     dbdir = opendir(dbpath);
00304     if(dbdir == NULL) {
00305         if(errno == ENOENT) {
00306             /* database dir doesn't exist yet */
00307             db->status |= DB_STATUS_VALID;
00308             db->status &= ~DB_STATUS_INVALID;
00309             db->status &= ~DB_STATUS_EXISTS;
00310             db->status |= DB_STATUS_MISSING;
00311             return 0;
00312         } else {
00313             RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
00314         }
00315     }
00316     db->status |= DB_STATUS_EXISTS;
00317     db->status &= ~DB_STATUS_MISSING;
00318 
00319     while((ent = readdir(dbdir)) != NULL) {
00320         const char *name = ent->d_name;
00321         char path[PATH_MAX];
00322 
00323         if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
00324             continue;
00325         }
00326         if(!is_dir(dbpath, ent)) {
00327             continue;
00328         }
00329 
00330         snprintf(path, PATH_MAX, "%s%s/depends", dbpath, name);
00331         if(access(path, F_OK) == 0) {
00332             /* we found a depends file- bail */
00333             db->status &= ~DB_STATUS_VALID;
00334             db->status |= DB_STATUS_INVALID;
00335             db->handle->pm_errno = ALPM_ERR_DB_VERSION;
00336             goto done;
00337         }
00338     }
00339     /* we found no depends file after full scan */
00340     db->status |= DB_STATUS_VALID;
00341     db->status &= ~DB_STATUS_INVALID;
00342     ret = 0;
00343 
00344 done:
00345     if(dbdir) {
00346         closedir(dbdir);
00347     }
00348 
00349     return ret;
00350 }
00351 
00352 static int local_db_populate(alpm_db_t *db)
00353 {
00354     size_t est_count;
00355     int count = 0;
00356     struct stat buf;
00357     struct dirent *ent = NULL;
00358     const char *dbpath;
00359     DIR *dbdir;
00360 
00361     if(db->status & DB_STATUS_INVALID) {
00362         RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1);
00363     }
00364     /* note: DB_STATUS_MISSING is not fatal for local database */
00365 
00366     dbpath = _alpm_db_path(db);
00367     if(dbpath == NULL) {
00368         /* pm_errno set in _alpm_db_path() */
00369         return -1;
00370     }
00371 
00372     dbdir = opendir(dbpath);
00373     if(dbdir == NULL) {
00374         if(errno == ENOENT) {
00375             /* no database existing yet is not an error */
00376             db->status &= ~DB_STATUS_EXISTS;
00377             db->status |= DB_STATUS_MISSING;
00378             return 0;
00379         }
00380         RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
00381     }
00382     if(fstat(dirfd(dbdir), &buf) != 0) {
00383         RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
00384     }
00385     db->status |= DB_STATUS_EXISTS;
00386     db->status &= ~DB_STATUS_MISSING;
00387     if(buf.st_nlink >= 2) {
00388         est_count = buf.st_nlink;
00389     } else {
00390         /* Some filesystems don't subscribe to the two-implicit links school of
00391          * thought, e.g. BTRFS, HFS+. See
00392          * http://kerneltrap.org/mailarchive/linux-btrfs/2010/1/23/6723483/thread
00393          */
00394         est_count = 0;
00395         while(readdir(dbdir) != NULL) {
00396             est_count++;
00397         }
00398         rewinddir(dbdir);
00399     }
00400     if(est_count >= 2) {
00401         /* subtract the two extra pointers to get # of children */
00402         est_count -= 2;
00403     }
00404 
00405     db->pkgcache = _alpm_pkghash_create(est_count);
00406     if(db->pkgcache == NULL){
00407         closedir(dbdir);
00408         RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
00409     }
00410 
00411     while((ent = readdir(dbdir)) != NULL) {
00412         const char *name = ent->d_name;
00413 
00414         alpm_pkg_t *pkg;
00415 
00416         if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
00417             continue;
00418         }
00419         if(!is_dir(dbpath, ent)) {
00420             continue;
00421         }
00422 
00423         pkg = _alpm_pkg_new();
00424         if(pkg == NULL) {
00425             closedir(dbdir);
00426             RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
00427         }
00428         /* split the db entry name */
00429         if(_alpm_splitname(name, &(pkg->name), &(pkg->version),
00430                     &(pkg->name_hash)) != 0) {
00431             _alpm_log(db->handle, ALPM_LOG_ERROR, _("invalid name for database entry '%s'\n"),
00432                     name);
00433             _alpm_pkg_free(pkg);
00434             continue;
00435         }
00436 
00437         /* duplicated database entries are not allowed */
00438         if(_alpm_pkghash_find(db->pkgcache, pkg->name)) {
00439             _alpm_log(db->handle, ALPM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name);
00440             _alpm_pkg_free(pkg);
00441             continue;
00442         }
00443 
00444         pkg->origin = PKG_FROM_LOCALDB;
00445         pkg->origin_data.db = db;
00446         pkg->ops = &local_pkg_ops;
00447         pkg->handle = db->handle;
00448 
00449         /* explicitly read with only 'BASE' data, accessors will handle the rest */
00450         if(local_db_read(pkg, INFRQ_BASE) == -1) {
00451             _alpm_log(db->handle, ALPM_LOG_ERROR, _("corrupted database entry '%s'\n"), name);
00452             _alpm_pkg_free(pkg);
00453             continue;
00454         }
00455 
00456         /* add to the collection */
00457         _alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
00458                 pkg->name, db->treename);
00459         db->pkgcache = _alpm_pkghash_add(db->pkgcache, pkg);
00460         count++;
00461     }
00462 
00463     closedir(dbdir);
00464     if(count > 0) {
00465         db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp);
00466     }
00467     _alpm_log(db->handle, ALPM_LOG_DEBUG, "added %d packages to package cache for db '%s'\n",
00468             count, db->treename);
00469 
00470     return count;
00471 }
00472 
00473 /* Note: the return value must be freed by the caller */
00474 char *_alpm_local_db_pkgpath(alpm_db_t *db, alpm_pkg_t *info,
00475         const char *filename)
00476 {
00477     size_t len;
00478     char *pkgpath;
00479     const char *dbpath;
00480 
00481     dbpath = _alpm_db_path(db);
00482     len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3;
00483     len += filename ? strlen(filename) : 0;
00484     MALLOC(pkgpath, len, RET_ERR(db->handle, ALPM_ERR_MEMORY, NULL));
00485     sprintf(pkgpath, "%s%s-%s/%s", dbpath, info->name, info->version,
00486             filename ? filename : "");
00487     return pkgpath;
00488 }
00489 
00490 #define READ_NEXT() do { \
00491     if(fgets(line, sizeof(line), fp) == NULL && !feof(fp)) goto error; \
00492     _alpm_strip_newline(line); \
00493 } while(0)
00494 
00495 #define READ_AND_STORE(f) do { \
00496     READ_NEXT(); \
00497     STRDUP(f, line, goto error); \
00498 } while(0)
00499 
00500 #define READ_AND_STORE_ALL(f) do { \
00501     char *linedup; \
00502     if(fgets(line, sizeof(line), fp) == NULL) {\
00503         if(!feof(fp)) goto error; else break; \
00504     } \
00505     if(_alpm_strip_newline(line) == 0) break; \
00506     STRDUP(linedup, line, goto error); \
00507     f = alpm_list_add(f, linedup); \
00508 } while(1) /* note the while(1) and not (0) */
00509 
00510 #define READ_AND_SPLITDEP(f) do { \
00511     if(fgets(line, sizeof(line), fp) == NULL) {\
00512         if(!feof(fp)) goto error; else break; \
00513     } \
00514     if(_alpm_strip_newline(line) == 0) break; \
00515     f = alpm_list_add(f, _alpm_splitdep(line)); \
00516 } while(1) /* note the while(1) and not (0) */
00517 
00518 static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq)
00519 {
00520     FILE *fp = NULL;
00521     char line[1024];
00522     char *pkgpath;
00523     alpm_db_t *db = info->origin_data.db;
00524 
00525     /* bitmask logic here:
00526      * infolevel: 00001111
00527      * inforeq:   00010100
00528      * & result:  00000100
00529      * == to inforeq? nope, we need to load more info. */
00530     if((info->infolevel & inforeq) == inforeq) {
00531         /* already loaded all of this info, do nothing */
00532         return 0;
00533     }
00534 
00535     if(info->infolevel & INFRQ_ERROR) {
00536         /* We've encountered an error loading this package before. Don't attempt
00537          * repeated reloads, just give up. */
00538         return -1;
00539     }
00540 
00541     _alpm_log(db->handle, ALPM_LOG_FUNCTION, "loading package data for %s : level=0x%x\n",
00542             info->name, inforeq);
00543 
00544     pkgpath = _alpm_local_db_pkgpath(db, info, NULL);
00545     if(!pkgpath || access(pkgpath, F_OK)) {
00546         /* directory doesn't exist or can't be opened */
00547         _alpm_log(db->handle, ALPM_LOG_DEBUG, "cannot find '%s-%s' in db '%s'\n",
00548                 info->name, info->version, db->treename);
00549         goto error;
00550     }
00551     free(pkgpath);
00552 
00553     /* clear out 'line', to be certain - and to make valgrind happy */
00554     memset(line, 0, sizeof(line));
00555 
00556     /* DESC */
00557     if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) {
00558         char *path = _alpm_local_db_pkgpath(db, info, "desc");
00559         if(!path || (fp = fopen(path, "r")) == NULL) {
00560             _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
00561             free(path);
00562             goto error;
00563         }
00564         free(path);
00565         while(!feof(fp)) {
00566             if(fgets(line, sizeof(line), fp) == NULL && !feof(fp)) {
00567                 goto error;
00568             }
00569             if(_alpm_strip_newline(line) == 0) {
00570                 /* length of stripped line was zero */
00571                 continue;
00572             }
00573             if(strcmp(line, "%NAME%") == 0) {
00574                 READ_NEXT();
00575                 if(strcmp(line, info->name) != 0) {
00576                     _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: name "
00577                                 "mismatch on package %s\n"), db->treename, info->name);
00578                 }
00579             } else if(strcmp(line, "%VERSION%") == 0) {
00580                 READ_NEXT();
00581                 if(strcmp(line, info->version) != 0) {
00582                     _alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: version "
00583                                 "mismatch on package %s\n"), db->treename, info->name);
00584                 }
00585             } else if(strcmp(line, "%DESC%") == 0) {
00586                 READ_AND_STORE(info->desc);
00587             } else if(strcmp(line, "%GROUPS%") == 0) {
00588                 READ_AND_STORE_ALL(info->groups);
00589             } else if(strcmp(line, "%URL%") == 0) {
00590                 READ_AND_STORE(info->url);
00591             } else if(strcmp(line, "%LICENSE%") == 0) {
00592                 READ_AND_STORE_ALL(info->licenses);
00593             } else if(strcmp(line, "%ARCH%") == 0) {
00594                 READ_AND_STORE(info->arch);
00595             } else if(strcmp(line, "%BUILDDATE%") == 0) {
00596                 READ_NEXT();
00597                 info->builddate = _alpm_parsedate(line);
00598             } else if(strcmp(line, "%INSTALLDATE%") == 0) {
00599                 READ_NEXT();
00600                 info->installdate = _alpm_parsedate(line);
00601             } else if(strcmp(line, "%PACKAGER%") == 0) {
00602                 READ_AND_STORE(info->packager);
00603             } else if(strcmp(line, "%REASON%") == 0) {
00604                 READ_NEXT();
00605                 info->reason = (alpm_pkgreason_t)atoi(line);
00606             } else if(strcmp(line, "%SIZE%") == 0) {
00607                 READ_NEXT();
00608                 info->isize = _alpm_strtoofft(line);
00609             } else if(strcmp(line, "%REPLACES%") == 0) {
00610                 READ_AND_SPLITDEP(info->replaces);
00611             } else if(strcmp(line, "%DEPENDS%") == 0) {
00612                 READ_AND_SPLITDEP(info->depends);
00613             } else if(strcmp(line, "%OPTDEPENDS%") == 0) {
00614                 READ_AND_STORE_ALL(info->optdepends);
00615             } else if(strcmp(line, "%CONFLICTS%") == 0) {
00616                 READ_AND_SPLITDEP(info->conflicts);
00617             } else if(strcmp(line, "%PROVIDES%") == 0) {
00618                 READ_AND_SPLITDEP(info->provides);
00619             }
00620         }
00621         fclose(fp);
00622         fp = NULL;
00623         info->infolevel |= INFRQ_DESC;
00624     }
00625 
00626     /* FILES */
00627     if(inforeq & INFRQ_FILES && !(info->infolevel & INFRQ_FILES)) {
00628         char *path = _alpm_local_db_pkgpath(db, info, "files");
00629         if(!path || (fp = fopen(path, "r")) == NULL) {
00630             _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
00631             free(path);
00632             goto error;
00633         }
00634         free(path);
00635         while(fgets(line, sizeof(line), fp)) {
00636             _alpm_strip_newline(line);
00637             if(strcmp(line, "%FILES%") == 0) {
00638                 size_t files_count = 0, files_size = 0, len;
00639                 alpm_file_t *files = NULL;
00640 
00641                 while(fgets(line, sizeof(line), fp) &&
00642                         (len = _alpm_strip_newline(line))) {
00643                     if(files_count >= files_size) {
00644                         size_t old_size = files_size;
00645                         if(files_size == 0) {
00646                             files_size = 8;
00647                         } else {
00648                             files_size *= 2;
00649                         }
00650                         files = realloc(files, sizeof(alpm_file_t) * files_size);
00651                         if(!files) {
00652                             ALLOC_FAIL(sizeof(alpm_file_t) * files_size);
00653                             goto error;
00654                         }
00655                         /* ensure all new memory is zeroed out, in both the initial
00656                          * allocation and later reallocs */
00657                         memset(files + old_size, 0,
00658                                 sizeof(alpm_file_t) * (files_size - old_size));
00659                     }
00660                     /* since we know the length of the file string already,
00661                      * we can do malloc + memcpy rather than strdup */
00662                     files[files_count].name = malloc(len + 1);
00663                     if(files[files_count].name == NULL) {
00664                         ALLOC_FAIL(len);
00665                         goto error;
00666                     }
00667                     memcpy(files[files_count].name, line, len + 1);
00668                     files_count++;
00669                 }
00670                 /* attempt to hand back any memory we don't need */
00671                 files = realloc(files, sizeof(alpm_file_t) * files_count);
00672                 info->files.count = files_count;
00673                 info->files.files = files;
00674             } else if(strcmp(line, "%BACKUP%") == 0) {
00675                 while(fgets(line, sizeof(line), fp) && _alpm_strip_newline(line)) {
00676                     alpm_backup_t *backup;
00677                     CALLOC(backup, 1, sizeof(alpm_backup_t), goto error);
00678                     if(_alpm_split_backup(line, &backup)) {
00679                         goto error;
00680                     }
00681                     info->backup = alpm_list_add(info->backup, backup);
00682                 }
00683             }
00684         }
00685         fclose(fp);
00686         fp = NULL;
00687         info->infolevel |= INFRQ_FILES;
00688     }
00689 
00690     /* INSTALL */
00691     if(inforeq & INFRQ_SCRIPTLET && !(info->infolevel & INFRQ_SCRIPTLET)) {
00692         char *path = _alpm_local_db_pkgpath(db, info, "install");
00693         if(access(path, F_OK) == 0) {
00694             info->scriptlet = 1;
00695         }
00696         free(path);
00697         info->infolevel |= INFRQ_SCRIPTLET;
00698     }
00699 
00700     return 0;
00701 
00702 error:
00703     info->infolevel |= INFRQ_ERROR;
00704     if(fp) {
00705         fclose(fp);
00706     }
00707     return -1;
00708 }
00709 
00710 int _alpm_local_db_prepare(alpm_db_t *db, alpm_pkg_t *info)
00711 {
00712     mode_t oldmask;
00713     int retval = 0;
00714     char *pkgpath;
00715 
00716     if(checkdbdir(db) != 0) {
00717         return -1;
00718     }
00719 
00720     oldmask = umask(0000);
00721     pkgpath = _alpm_local_db_pkgpath(db, info, NULL);
00722 
00723     if((retval = mkdir(pkgpath, 0755)) != 0) {
00724         _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not create directory %s: %s\n"),
00725                 pkgpath, strerror(errno));
00726     }
00727 
00728     free(pkgpath);
00729     umask(oldmask);
00730 
00731     return retval;
00732 }
00733 
00734 int _alpm_local_db_write(alpm_db_t *db, alpm_pkg_t *info, alpm_dbinfrq_t inforeq)
00735 {
00736     FILE *fp = NULL;
00737     mode_t oldmask;
00738     alpm_list_t *lp;
00739     int retval = 0;
00740 
00741     if(db == NULL || info == NULL || !(db->status & DB_STATUS_LOCAL)) {
00742         return -1;
00743     }
00744 
00745     /* make sure we have a sane umask */
00746     oldmask = umask(0022);
00747 
00748     /* DESC */
00749     if(inforeq & INFRQ_DESC) {
00750         char *path;
00751         _alpm_log(db->handle, ALPM_LOG_DEBUG, "writing %s-%s DESC information back to db\n",
00752                 info->name, info->version);
00753         path = _alpm_local_db_pkgpath(db, info, "desc");
00754         if(!path || (fp = fopen(path, "w")) == NULL) {
00755             _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
00756                     path, strerror(errno));
00757             retval = -1;
00758             free(path);
00759             goto cleanup;
00760         }
00761         free(path);
00762         fprintf(fp, "%%NAME%%\n%s\n\n"
00763                         "%%VERSION%%\n%s\n\n", info->name, info->version);
00764         if(info->desc) {
00765             fprintf(fp, "%%DESC%%\n"
00766                             "%s\n\n", info->desc);
00767         }
00768         if(info->groups) {
00769             fputs("%GROUPS%\n", fp);
00770             for(lp = info->groups; lp; lp = lp->next) {
00771                 fprintf(fp, "%s\n", (char *)lp->data);
00772             }
00773             fprintf(fp, "\n");
00774         }
00775         if(info->replaces) {
00776             fputs("%REPLACES%\n", fp);
00777             for(lp = info->replaces; lp; lp = lp->next) {
00778                 char *depstring = alpm_dep_compute_string(lp->data);
00779                 fprintf(fp, "%s\n", depstring);
00780                 free(depstring);
00781             }
00782             fprintf(fp, "\n");
00783         }
00784         if(info->url) {
00785             fprintf(fp, "%%URL%%\n"
00786                             "%s\n\n", info->url);
00787         }
00788         if(info->licenses) {
00789             fputs("%LICENSE%\n", fp);
00790             for(lp = info->licenses; lp; lp = lp->next) {
00791                 fprintf(fp, "%s\n", (char *)lp->data);
00792             }
00793             fprintf(fp, "\n");
00794         }
00795         if(info->arch) {
00796             fprintf(fp, "%%ARCH%%\n"
00797                             "%s\n\n", info->arch);
00798         }
00799         if(info->builddate) {
00800             fprintf(fp, "%%BUILDDATE%%\n"
00801                             "%jd\n\n", (intmax_t)info->builddate);
00802         }
00803         if(info->installdate) {
00804             fprintf(fp, "%%INSTALLDATE%%\n"
00805                             "%jd\n\n", (intmax_t)info->installdate);
00806         }
00807         if(info->packager) {
00808             fprintf(fp, "%%PACKAGER%%\n"
00809                             "%s\n\n", info->packager);
00810         }
00811         if(info->isize) {
00812             /* only write installed size, csize is irrelevant once installed */
00813             fprintf(fp, "%%SIZE%%\n"
00814                             "%jd\n\n", (intmax_t)info->isize);
00815         }
00816         if(info->reason) {
00817             fprintf(fp, "%%REASON%%\n"
00818                             "%u\n\n", info->reason);
00819         }
00820         if(info->depends) {
00821             fputs("%DEPENDS%\n", fp);
00822             for(lp = info->depends; lp; lp = lp->next) {
00823                 char *depstring = alpm_dep_compute_string(lp->data);
00824                 fprintf(fp, "%s\n", depstring);
00825                 free(depstring);
00826             }
00827             fprintf(fp, "\n");
00828         }
00829         if(info->optdepends) {
00830             fputs("%OPTDEPENDS%\n", fp);
00831             for(lp = info->optdepends; lp; lp = lp->next) {
00832                 fprintf(fp, "%s\n", (char *)lp->data);
00833             }
00834             fprintf(fp, "\n");
00835         }
00836         if(info->conflicts) {
00837             fputs("%CONFLICTS%\n", fp);
00838             for(lp = info->conflicts; lp; lp = lp->next) {
00839                 char *depstring = alpm_dep_compute_string(lp->data);
00840                 fprintf(fp, "%s\n", depstring);
00841                 free(depstring);
00842             }
00843             fprintf(fp, "\n");
00844         }
00845         if(info->provides) {
00846             fputs("%PROVIDES%\n", fp);
00847             for(lp = info->provides; lp; lp = lp->next) {
00848                 char *depstring = alpm_dep_compute_string(lp->data);
00849                 fprintf(fp, "%s\n", depstring);
00850                 free(depstring);
00851             }
00852             fprintf(fp, "\n");
00853         }
00854 
00855         fclose(fp);
00856         fp = NULL;
00857     }
00858 
00859     /* FILES */
00860     if(inforeq & INFRQ_FILES) {
00861         char *path;
00862         _alpm_log(db->handle, ALPM_LOG_DEBUG, "writing %s-%s FILES information back to db\n",
00863                 info->name, info->version);
00864         path = _alpm_local_db_pkgpath(db, info, "files");
00865         if(!path || (fp = fopen(path, "w")) == NULL) {
00866             _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
00867                     path, strerror(errno));
00868             retval = -1;
00869             free(path);
00870             goto cleanup;
00871         }
00872         free(path);
00873         if(info->files.count) {
00874             size_t i;
00875             fprintf(fp, "%%FILES%%\n");
00876             for(i = 0; i < info->files.count; i++) {
00877                 const alpm_file_t *file = info->files.files + i;
00878                 fprintf(fp, "%s\n", file->name);
00879             }
00880             fprintf(fp, "\n");
00881         }
00882         if(info->backup) {
00883             fprintf(fp, "%%BACKUP%%\n");
00884             for(lp = info->backup; lp; lp = lp->next) {
00885                 const alpm_backup_t *backup = lp->data;
00886                 fprintf(fp, "%s\t%s\n", backup->name, backup->hash);
00887             }
00888             fprintf(fp, "\n");
00889         }
00890         fclose(fp);
00891         fp = NULL;
00892     }
00893 
00894     /* INSTALL */
00895     /* nothing needed here (script is automatically extracted) */
00896 
00897 cleanup:
00898     umask(oldmask);
00899 
00900     if(fp) {
00901         fclose(fp);
00902     }
00903 
00904     return retval;
00905 }
00906 
00907 int _alpm_local_db_remove(alpm_db_t *db, alpm_pkg_t *info)
00908 {
00909     int ret = 0;
00910     char *pkgpath = _alpm_local_db_pkgpath(db, info, NULL);
00911 
00912     /* TODO explicit file removes and then an rmdir? */
00913     ret = _alpm_rmrf(pkgpath);
00914     free(pkgpath);
00915     if(ret != 0) {
00916         ret = -1;
00917     }
00918     return ret;
00919 }
00920 
00921 struct db_operations local_db_ops = {
00922     .validate         = local_db_validate,
00923     .populate         = local_db_populate,
00924     .unregister       = _alpm_db_unregister,
00925 };
00926 
00927 alpm_db_t *_alpm_db_register_local(alpm_handle_t *handle)
00928 {
00929     alpm_db_t *db;
00930 
00931     _alpm_log(handle, ALPM_LOG_DEBUG, "registering local database\n");
00932 
00933     db = _alpm_db_new("local", 1);
00934     if(db == NULL) {
00935         handle->pm_errno = ALPM_ERR_DB_CREATE;
00936         return NULL;
00937     }
00938     db->ops = &local_db_ops;
00939     db->handle = handle;
00940 
00941     if(local_db_validate(db)) {
00942         /* pm_errno set in local_db_validate() */
00943         _alpm_db_free(db);
00944         return NULL;
00945     }
00946 
00947     handle->db_local = db;
00948     return db;
00949 }
00950 
00951 /* vim: set ts=2 sw=2 noet: */