/*@-type@*/ /* FIX: shrug */ /** \ingroup db1 * \file rpmdb/db1.c */ #include "system.h" #ifdef DYING /*@unused@*/ static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */ #endif #define _mymemset(_a, _b, _c) #include "rpmio_internal.h" #include "rpmlib.h" #include "rpmmacro.h" /* XXX rpmGenPath */ #include "rpmurl.h" /* XXX urlGetPath */ #include "falloc.h" #include "misc.h" #include "rpmdb.h" /* XXX must follow rpmdb.h */ #define DB_VERSION_MAJOR 1 #define DB_VERSION_MINOR 85 #define DB_VERSION_PATCH 0 struct _DBT1 { void * data; /* data */ size_t size; /* data length */ }; #undef DBT #define DBT struct _DBT1 #include "debug.h" /*@access Header@*/ /* XXX compared with NULL */ /*@access rpmdb@*/ /*@access dbiIndex@*/ /*@access dbiIndexSet@*/ /*@-onlytrans@*/ #ifdef DYING /* XXX remap DB3 types back into DB1 types */ static inline DBTYPE db3_to_dbtype(int dbitype) { switch(dbitype) { case 1: return DB_BTREE; case 2: return DB_HASH; case 3: return DB_RECNO; case 4: return DB_HASH; /* XXX W2DO? */ case 5: return DB_HASH; /* XXX W2DO? */ } /*@notreached@*/ return DB_HASH; } /*@-shadow@*/ static /*@observer@*/ char * db_strerror(int error) /*@=shadow@*/ { if (error == 0) return ("Successful return: 0"); if (error > 0) return (strerror(error)); switch (error) { default: { /* * !!! * Room for a 64-bit number + slop. This buffer is only used * if we're given an unknown error, which should never happen. * Note, however, we're no longer thread-safe if it does. */ static char ebuf[40]; char * t = ebuf; *t = '\0'; t = stpcpy(t, "Unknown error: "); sprintf(t, "%d", error); return(ebuf); } } /*@notreached@*/ } static int cvtdberr(dbiIndex dbi, const char * msg, int error, int printit) /*@*/ { int rc = 0; if (error == 0) rc = 0; else if (error < 0) rc = errno; else if (error > 0) rc = -1; if (printit && rc) { if (msg) rpmError(RPMERR_DBERR, _("db%d error(%d) from %s: %s\n"), dbi->dbi_api, rc, msg, db_strerror(error)); else rpmError(RPMERR_DBERR, _("db%d error(%d): %s\n"), dbi->dbi_api, rc, db_strerror(error)); } return rc; } #endif /* DYING */ static int db1sync(dbiIndex dbi, /*@unused@*/ unsigned int flags) /*@globals fileSystem @*/ /*@modifies fileSystem @*/ { int rc = 0; if (dbi->dbi_db) { if (dbi->dbi_rpmtag == RPMDBI_PACKAGES) { FD_t pkgs = dbi->dbi_db; int fdno = Fileno(pkgs); if (fdno >= 0 && (rc = fsync(fdno)) != 0) rc = errno; } #ifdef DYING else { DB * db = dbi->dbi_db; rc = db->sync(db, flags); rc = cvtdberr(dbi, "db->sync", rc, _debug); } #endif } return rc; } /*@null@*/ static void * doGetRecord(dbiIndex dbi, unsigned int offset) /*@globals fileSystem @*/ /*@modifies dbi, fileSystem @*/ { FD_t pkgs = dbi->dbi_db; void * uh = NULL; Header h = NULL; const char ** fileNames; int fileCount = 0; int lasto = 0; int i; retry: if (offset >= fadGetFileSize(pkgs)) goto exit; (void)Fseek(pkgs, offset, SEEK_SET); h = headerRead(pkgs, HEADER_MAGIC_NO); /* let's sanity check this record a bit, otherwise just skip it */ if (h != NULL && !( headerIsEntry(h, RPMTAG_NAME) && headerIsEntry(h, RPMTAG_VERSION) && headerIsEntry(h, RPMTAG_RELEASE) && headerIsEntry(h, RPMTAG_BUILDTIME))) { h = headerFree(h); } if (h == NULL) { /* XXX HACK: try to reconnect broken chain. */ if (lasto == 0) { rpmMessage(RPMMESS_WARNING, _("Broken package chain at offset %d(0x%08x), attempting to reconnect ...\n"), (int) offset, offset); lasto = (offset ? offset : -1); offset = fadNextOffset(pkgs, offset); if (offset > 0) goto retry; } goto exit; } if (lasto) { rpmMessage(RPMMESS_WARNING, _("Reconnecting broken chain at offset %d(0x%08x).\n"), (int) offset, offset); dbi->dbi_lastoffset = offset; } /* Retrofit "Provide: name = EVR" for binary packages. */ providePackageNVR(h); /* * The RPM used to build much of RH 5.1 could produce packages whose * file lists did not have leading /'s. Now is a good time to fix that. */ /* * If this tag isn't present, either no files are in the package or * we're dealing with a package that has just the compressed file name * list. */ if (!headerGetEntryMinMemory(h, RPMTAG_OLDFILENAMES, NULL, (const void **) &fileNames, &fileCount)) goto exit; for (i = 0; i < fileCount; i++) if (*fileNames[i] != '/') break; if (i == fileCount) { free(fileNames); } else { /* bad header -- let's clean it up */ const char ** newFileNames = alloca(sizeof(*newFileNames) * fileCount); for (i = 0; i < fileCount; i++) { char * newFileName = alloca(strlen(fileNames[i]) + 2); if (*fileNames[i] != '/') { newFileName[0] = '/'; newFileName[1] = '\0'; } else newFileName[0] = '\0'; strcat(newFileName, fileNames[i]); newFileNames[i] = newFileName; } free(fileNames); (void) headerModifyEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE, newFileNames, fileCount); } /* * The file list was moved to a more compressed format which not * only saves memory (nice), but gives fingerprinting a nice, fat * speed boost (very nice). Go ahead and convert old headers to * the new style (this is a noop for new headers). */ compressFilelist(h); exit: /*@-branchstate@*/ if (h != NULL) { uh = headerUnload(h); h = headerFree(h); } /*@=branchstate@*/ return uh; } static int db1copen(/*@unused@*/ dbiIndex dbi, /*@unused@*/ DBC ** dbcp, unsigned int flags) /*@modifies *dbcp @*/ { /* XXX per-iterator cursors need to be set to non-NULL. */ if (flags) *dbcp = (DBC *)-1; return 0; } static int db1cclose(dbiIndex dbi, /*@unused@*/ DBC * dbcursor, /*@unused@*/ unsigned int flags) /*@modifies dbi @*/ { dbi->dbi_lastoffset = 0; return 0; } /*@-compmempass@*/ static int db1cget(dbiIndex dbi, /*@unused@*/ DBC * dbcursor, /*@null@*/ void ** keyp, /*@null@*/ size_t * keylen, /*@null@*/ void ** datap, /*@null@*/ size_t * datalen, /*@unused@*/ unsigned int flags) /*@globals fileSystem @*/ /*@modifies dbi, *keyp, *keylen, *datap, *datalen, fileSystem @*/ { DBT key, data; int rc = 0; if (dbi == NULL) return EFAULT; memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); /*@-unqualifiedtrans@*/ if (keyp) key.data = *keyp; if (keylen) key.size = *keylen; if (datap) data.data = *datap; if (datalen) data.size = *datalen; /*@=unqualifiedtrans@*/ if (dbi->dbi_rpmtag == RPMDBI_PACKAGES) { FD_t pkgs = dbi->dbi_db; unsigned int offset; unsigned int newSize; if (key.data == NULL) { /* XXX simulated DB_NEXT */ if (dbi->dbi_lastoffset == 0) { dbi->dbi_lastoffset = fadFirstOffset(pkgs); } else { dbi->dbi_lastoffset = fadNextOffset(pkgs, dbi->dbi_lastoffset); } /*@-immediatetrans@*/ key.data = &dbi->dbi_lastoffset; /*@=immediatetrans@*/ key.size = sizeof(dbi->dbi_lastoffset); /* Catch end-of-chain conditions. */ if (dbi->dbi_lastoffset == 0) goto bail; } memcpy(&offset, key.data, sizeof(offset)); /* XXX hack to pass sizeof header to fadAlloc */ newSize = data.size; if (offset == 0) { /* XXX simulated offset 0 record */ offset = fadAlloc(pkgs, newSize); if (offset == 0) return ENOMEM; offset--; /* XXX hack: caller will increment */ /* XXX hack: return offset as data, free in db1cput */ data.data = xmalloc(sizeof(offset)); memcpy(data.data, &offset, sizeof(offset)); data.size = sizeof(offset); } else { /* XXX simulated retrieval */ data.data = doGetRecord(dbi, offset); data.size = 0; /* XXX WRONG */ } } #ifdef DYING else { DB * db; int _printit; if ((db = dbi->dbi_db) == NULL) return EFAULT; if (key.data == NULL) { rc = db->seq(db, &key, &data, (dbi->dbi_lastoffset++ ? R_NEXT : R_FIRST)); _printit = (rc == 1 ? 0 : _debug); rc = cvtdberr(dbi, "db->seq", rc, _printit); } else { rc = db->get(db, &key, &data, 0); _printit = (rc == 1 ? 0 : _debug); rc = cvtdberr(dbi, "db1cget", rc, _printit); } } #else else rc = EINVAL; #endif bail: if (rc == 0) { if (keyp) *keyp = key.data; if (keylen) *keylen = key.size; if (datap) *datap = data.data; if (datalen) *datalen = data.size; } /*@-nullstate@*/ return rc; /*@=nullstate@*/ } /*@=compmempass@*/ static int db1cdel(dbiIndex dbi, /*@unused@*/ DBC * dbcursor, const void * keyp, size_t keylen, /*@unused@*/ unsigned int flags) /*@globals fileSystem @*/ /*@modifies dbi, fileSystem @*/ { DBT key; int rc = 0; memset(&key, 0, sizeof(key)); key.data = (void *)keyp; key.size = keylen; if (dbi->dbi_rpmtag == RPMDBI_PACKAGES) { FD_t pkgs = dbi->dbi_db; unsigned int offset; memcpy(&offset, keyp, sizeof(offset)); fadFree(pkgs, offset); } #ifdef DYING else { DB * db = dbi->dbi_db; if (db) rc = db->del(db, &key, 0); rc = cvtdberr(dbi, "db->del", rc, _debug); } #else else rc = EINVAL; #endif return rc; } static int db1cput(dbiIndex dbi, /*@unused@*/ DBC * dbcursor, const void * keyp, size_t keylen, const void * datap, size_t datalen, /*@unused@*/ unsigned int flags) /*@globals fileSystem @*/ /*@modifies dbi, datap, fileSystem @*/ { DBT key, data; int rc = 0; memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); key.data = (void *)keyp; key.size = keylen; data.data = (void *)datap; data.size = datalen; if (dbi->dbi_rpmtag == RPMDBI_PACKAGES) { FD_t pkgs = dbi->dbi_db; unsigned int offset; memcpy(&offset, key.data, sizeof(offset)); if (offset == 0) { /* XXX simulated offset 0 record */ /* XXX hack: return offset as data, free in db1cput */ if (data.size == sizeof(offset)) /*@-unqualifiedtrans@*/ free(data.data); /*@=unqualifiedtrans@*/ } else { /* XXX simulated DB_KEYLAST */ Header h = headerLoad(data.data); int newSize = headerSizeof(h, HEADER_MAGIC_NO); (void)Fseek(pkgs, offset, SEEK_SET); fdSetContentLength(pkgs, newSize); rc = headerWrite(pkgs, h, HEADER_MAGIC_NO); fdSetContentLength(pkgs, -1); if (rc) rc = EIO; h = headerFree(h); } } #ifdef DYING else { DB * db = dbi->dbi_db; if (db) rc = db->put(db, &key, &data, 0); rc = cvtdberr(dbi, "db->put", rc, _debug); } #else else rc = EINVAL; #endif return rc; } static int db1ccount(/*@unused@*/ dbiIndex dbi, /*@unused@*/ DBC * dbcursor, /*@unused@*/ /*@out@*/ unsigned int * countp, /*@unused@*/ unsigned int flags) /*@*/ { return EINVAL; } static int db1byteswapped(/*@unused@*/dbiIndex dbi) /*@*/ { return 0; } static int db1stat(/*@unused@*/ dbiIndex dbi, /*@unused@*/ unsigned int flags) /*@*/ { return EINVAL; } static int db1close(/*@only@*/ dbiIndex dbi, /*@unused@*/ unsigned int flags) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies dbi, rpmGlobalMacroContext, fileSystem @*/ { rpmdb rpmdb = dbi->dbi_rpmdb; const char * base = db1basename(dbi->dbi_rpmtag); const char * urlfn = rpmGenPath(rpmdb->db_root, rpmdb->db_home, base); const char * fn; int rc = 0; (void) urlPath(urlfn, &fn); /*@-branchstate@*/ if (dbi->dbi_db) { if (dbi->dbi_rpmtag == RPMDBI_PACKAGES) { FD_t pkgs = dbi->dbi_db; rc = Fclose(pkgs); } #ifdef DYING else { DB * db = dbi->dbi_db; rc = db->close(db); rc = cvtdberr(dbi, "db->close", rc, _debug); } #else else rc = EINVAL; #endif dbi->dbi_db = NULL; } /*@=branchstate@*/ rpmMessage(RPMMESS_DEBUG, _("closed db file %s\n"), urlfn); /* Remove temporary databases */ if (dbi->dbi_temporary) { rpmMessage(RPMMESS_DEBUG, _("removed db file %s\n"), urlfn); (void) unlink(fn); } dbi = db3Free(dbi); base = _free(base); urlfn = _free(urlfn); return rc; } static int db1open(/*@keep@*/ rpmdb rpmdb, int rpmtag, /*@out@*/ dbiIndex * dbip) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies *dbip, rpmGlobalMacroContext, fileSystem @*/ { /*@-nestedextern@*/ extern struct _dbiVec db1vec; /*@=nestedextern@*/ const char * base = NULL; const char * urlfn = NULL; const char * fn = NULL; dbiIndex dbi = NULL; int rc = 0; if (dbip) *dbip = NULL; if ((dbi = db3New(rpmdb, rpmtag)) == NULL) return EFAULT; dbi->dbi_api = DB_VERSION_MAJOR; base = db1basename(rpmtag); urlfn = rpmGenPath(rpmdb->db_root, rpmdb->db_home, base); (void) urlPath(urlfn, &fn); if (!(fn && *fn != '\0')) { rpmError(RPMERR_DBOPEN, _("bad db file %s\n"), urlfn); rc = EFAULT; goto exit; } rpmMessage(RPMMESS_DEBUG, _("opening db file %s mode 0x%x\n"), urlfn, dbi->dbi_mode); if (dbi->dbi_rpmtag == RPMDBI_PACKAGES) { FD_t pkgs; pkgs = fadOpen(fn, dbi->dbi_mode, dbi->dbi_perms); if (Ferror(pkgs)) { rc = errno; /* XXX check errno validity */ goto exit; } /* XXX HACK: fcntl lock if db3 (DB_INIT_CDB | DB_INIT_LOCK) specified */ if (dbi->dbi_lockdbfd || (dbi->dbi_eflags & 0x30)) { struct flock l; l.l_whence = 0; l.l_start = 0; l.l_len = 0; l.l_type = (dbi->dbi_mode & O_RDWR) ? F_WRLCK : F_RDLCK; if (Fcntl(pkgs, F_SETLK, (void *) &l)) { rc = errno; /* XXX check errno validity */ rpmError(RPMERR_FLOCK, _("cannot get %s lock on database\n"), ((dbi->dbi_mode & O_RDWR) ? _("exclusive") : _("shared"))); goto exit; } } dbi->dbi_db = pkgs; } #ifdef DYING else { void * dbopeninfo = NULL; int dbimode = dbi->dbi_mode; if (dbi->dbi_temporary) dbimode |= (O_CREAT | O_RDWR); dbi->dbi_db = dbopen(fn, dbimode, dbi->dbi_perms, db3_to_dbtype(dbi->dbi_type), dbopeninfo); if (dbi->dbi_db == NULL) rc = errno; } #else else rc = EINVAL; #endif exit: if (rc == 0 && dbi->dbi_db != NULL && dbip) { dbi->dbi_vec = &db1vec; if (dbip) *dbip = dbi; } else (void) db1close(dbi, 0); base = _free(base); urlfn = _free(urlfn); return rc; } /*@=onlytrans@*/ /** \ingroup db1 */ /*@-exportheadervar@*/ /*@observer@*/ /*@unchecked@*/ struct _dbiVec db1vec = { DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH, db1open, db1close, db1sync, db1copen, db1cclose, db1cdel, db1cget, db1cput, db1ccount, db1byteswapped, db1stat }; /*@=exportheadervar@*/ /*@=type@*/