1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/librpmDb.cv3.cc
16 #include "zypp/base/Logger.h"
18 #include "zypp/target/rpm/librpmDb.h"
19 #include "zypp/target/rpm/RpmCallbacks.h"
20 #include "zypp/ZYppCallbacks.h"
25 #define FA_MAGIC 0x02050920
29 unsigned int firstFree;
34 unsigned int freeNext; /* offset of the next free block, 0 if none */
35 unsigned int freePrev;
38 /* note that the u16's appear last for alignment/space reasons */
46 static int fadFileSize;
48 static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset) {
49 if (Fseek(fd, offset, SEEK_SET) < 0)
51 return Fread(buf, sizeof(char), count, fd);
54 static FD_t fadOpen(const char * path)
56 struct faFileHeader newHdr;
60 fd = Fopen(path, "r.fdio");
61 if (!fd || Ferror(fd))
64 if (fstat(Fileno(fd), &stb)) {
68 fadFileSize = stb.st_size;
70 /* is this file brand new? */
71 if (fadFileSize == 0) {
75 if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr)) {
79 if (newHdr.magic != FA_MAGIC) {
83 /*@-refcounttrans@*/ return fd /*@=refcounttrans@*/ ;
86 static int fadNextOffset(FD_t fd, unsigned int lastOffset)
88 struct faHeader header;
92 ? (lastOffset - sizeof(header))
93 : sizeof(struct faFileHeader);
95 if (offset >= fadFileSize)
98 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
101 if (!lastOffset && !header.isFree)
102 return (offset + sizeof(header));
105 offset += header.size;
107 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
110 if (!header.isFree) break;
111 } while (offset < fadFileSize && header.isFree);
113 if (offset < fadFileSize) {
114 /* Sanity check this to make sure we're not going in loops */
115 offset += sizeof(header);
117 if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
124 static int fadFirstOffset(FD_t fd)
126 return fadNextOffset(fd, 0);
130 static int dncmp(const void * a, const void * b)
133 const char *const * first = (const char *const *)a;
134 const char *const * second = (const char *const *)b;
135 return strcmp(*first, *second);
140 static void compressFilelist(Header h)
143 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
144 HAE_t hae = (HAE_t)headerAddEntry;
145 HRE_t hre = (HRE_t)headerRemoveEntry;
146 HFD_t hfd = headerFreeData;
148 const char ** dirNames;
149 const char ** baseNames;
157 * This assumes the file list is already sorted, and begins with a
158 * single '/'. That assumption isn't critical, but it makes things go
162 if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
163 xx = hre(h, RPMTAG_OLDFILENAMES);
164 return; /* Already converted. */
168 if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, &hgePtr, &count))
169 return; /* no file list */
170 fileNames = (char **)hgePtr;
171 if (fileNames == NULL || count <= 0)
174 dirNames = (const char **)alloca(sizeof(*dirNames) * count); /* worst case */
175 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
176 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
178 if (fileNames[0][0] != '/') {
179 /* HACK. Source RPM, so just do things differently */
181 dirNames[dirIndex] = "";
182 for (i = 0; i < count; i++) {
183 dirIndexes[i] = dirIndex;
184 baseNames[i] = fileNames[i];
190 for (i = 0; i < count; i++) {
191 const char ** needle;
196 if (fileNames[i] == NULL) /* XXX can't happen */
198 baseName = strrchr(fileNames[i], '/') + 1;
199 len = baseName - fileNames[i];
201 savechar = *baseName;
205 (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
206 char *s = (char *)alloca(len + 1);
207 memcpy(s, fileNames[i], len + 1);
209 dirIndexes[i] = ++dirIndex;
210 dirNames[dirIndex] = s;
212 dirIndexes[i] = needle - dirNames;
215 *baseName = savechar;
216 baseNames[i] = baseName;
222 xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
223 xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
225 xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
226 dirNames, dirIndex + 1);
229 fileNames = (char**)hfd(fileNames, fnt);
231 xx = hre(h, RPMTAG_OLDFILENAMES);
236 * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
237 * Retrofit an explicit "Provides: name = epoch:version-release".
239 void providePackageNVR(Header h)
241 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
242 HFD_t hfd = headerFreeData;
243 const char *name, *version, *release;
248 int_32 pFlags = RPMSENSE_EQUAL;
249 const char ** provides = NULL;
250 const char ** providesEVR = NULL;
252 int_32 * provideFlags = NULL;
257 /* Generate provides for this package name-version-release. */
258 xx = headerNVR(h, &name, &version, &release);
259 if (!(name && version && release))
261 pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
263 if (hge(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL)) {
264 epoch = (int_32 *)hgePtr;
265 sprintf(p, "%d:", *epoch);
269 (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
272 * Rpm prior to 3.0.3 does not have versioned provides.
273 * If no provides at all are available, we can just add.
275 if (!hge(h, RPMTAG_PROVIDENAME, &pnt, &hgePtr, &providesCount))
277 provides = (const char **)hgePtr;
280 * Otherwise, fill in entries on legacy packages.
282 if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, &hgePtr, NULL)) {
283 providesEVR = (const char **)hgePtr;
284 for (i = 0; i < providesCount; i++) {
286 int_32 fdummy = RPMSENSE_ANY;
287 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
289 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
295 xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
296 provideFlags = (int_32 *)hgePtr;
298 /*@-nullderef@*/ /* LCL: providesEVR is not NULL */
299 if (provides && providesEVR && provideFlags)
300 for (i = 0; i < providesCount; i++) {
301 if (!(provides[i] && providesEVR[i]))
303 if (!(provideFlags[i] == RPMSENSE_EQUAL &&
304 !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
312 provides = (const char **)hfd(provides, pnt);
313 providesEVR = (const char **)hfd(providesEVR, pvt);
316 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
318 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
320 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
325 ///////////////////////////////////////////////////////////////////
326 ///////////////////////////////////////////////////////////////////
327 ///////////////////////////////////////////////////////////////////
332 #define Y2LOG "librpmDb"
334 /******************************************************************
337 ** FUNCTION NAME : internal_convertV3toV4
338 ** FUNCTION TYPE : int
340 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
341 callback::SendReport<ConvertDBReport> & report )
343 // Timecount _t( "convert V3 to V4" );
344 MIL << "Convert rpm3 database to rpm4" << endl;
347 FD_t fd = fadOpen( v3db_r.asString().c_str() );
350 ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
355 INT << "NULL rpmV4 database passed as argument!" << endl;
356 ZYPP_THROW(RpmNullDatabaseException());
359 shared_ptr<RpmException> err = v4db_r->error();
362 INT << "Can't access rpmV4 database " << v4db_r << endl;
366 // open rpmV4 database for writing. v4db_r is ok so librpm should
367 // be properly initialized.
369 string rootstr( v4db_r->root().asString() );
370 const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
372 int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
378 ZYPP_THROW(RpmDbOpenException(root, Pathname(string(db->db_root))));
381 // Check ammount of packages to process.
383 for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) ) {
386 MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
389 unsigned ignored = 0;
390 unsigned alreadyInV4 = 0;
391 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r );
400 #warning Add CBSuggest handling if needed, also on lines below
401 // CBSuggest proceed;
403 for ( int offset = fadFirstOffset(fd); offset && proceed /*!= CBSuggest::CANCEL*/;
404 offset = fadNextOffset(fd, offset),
405 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r ) )
408 // have to use lseek instead of Fseek because headerRead
410 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 ) {
411 ostream * reportAs = &(ERR);
412 /* proceed = report->dbReadError( offset );
413 if ( proceed == CBSuggest::SKIP ) {
416 reportAs = &(WAR << "IGNORED: ");
418 // PROCEED will fail after conversion; CANCEL immediately stop loop
421 (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
424 Header h = headerRead(fd, HEADER_MAGIC_NO);
426 ostream * reportAs = &(ERR);
427 /* proceed = report->dbReadError( offset );
428 if ( proceed == CBSuggest::SKIP ) {
431 reportAs = &(WAR << "IGNORED: ");
433 // PROCEED will fail after conversion; CANCEL immediately stop loop
436 (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
440 providePackageNVR(h);
441 const char *name = 0;
442 const char *version = 0;
443 const char *release = 0;
444 headerNVR(h, &name, &version, &release);
445 string nrv( string(name) + "-" + version + "-" + release );
446 rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
447 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
448 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
449 if (rpmdbNextIterator(mi)) {
450 // report.dbInV4( nrv );
451 WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
452 rpmdbFreeIterator(mi);
457 rpmdbFreeIterator(mi);
458 if (rpmdbAdd(db, -1, h, 0, 0)) {
459 // report.dbWriteError( nrv );
460 proceed = false;//CBSuggest::CANCEL; // immediately stop loop
462 ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
473 ERR << "Convert rpm3 database to rpm4: Aborted after "
474 << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
476 ZYPP_THROW(RpmDbConvertException());
478 MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
480 MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
483 MIL << "; IGNORED: " << ignored << " unconverted due to error";
489 /******************************************************************
492 * FUNCTION NAME : convertV3toV4
494 * \throws RpmException
497 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
500 callback::SendReport<ConvertDBReport> report;
501 report->start(v3db_r);
503 internal_convertV3toV4( v3db_r, v4db_r, report );
505 catch (RpmException & excpt_r)
507 report->finish(v3db_r, ConvertDBReport::FAILED,excpt_r.msg());
508 ZYPP_RETHROW(excpt_r);
510 report->finish(v3db_r, ConvertDBReport::NO_ERROR, "");
514 } // namespace target