1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/librpmDb.cv3.cc
15 #ifndef _RPM_4_4_COMPAT
17 typedef rpmuint32_t rpm_count_t;
19 typedef int32_t rpm_count_t;
24 #define HGEPtr_t void *
25 #define headerGetEntryMinMemory headerGetEntry
26 #define headerNVR(h,n,v,r) headerNEVRA(h,n,NULL,v,r,NULL)
28 #define HGEPtr_t const void *
34 #include "zypp/base/Logger.h"
36 #include "zypp/target/rpm/librpmDb.h"
37 #include "zypp/target/rpm/RpmCallbacks.h"
38 #include "zypp/ZYppCallbacks.h"
44 #define FA_MAGIC 0x02050920
49 unsigned int firstFree;
55 unsigned int freeNext; /* offset of the next free block, 0 if none */
56 unsigned int freePrev;
59 /* note that the u16's appear last for alignment/space reasons */
70 static int fadFileSize;
72 static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset)
74 if (Fseek(fd, offset, SEEK_SET) < 0)
76 return Fread(buf, sizeof(char), count, fd);
79 static FD_t fadOpen(const char * path)
81 struct faFileHeader newHdr;
85 fd = Fopen(path, "r.fdio");
86 if (!fd || Ferror(fd))
89 if (fstat(Fileno(fd), &stb))
94 fadFileSize = stb.st_size;
96 /* is this file brand new? */
102 if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr))
107 if (newHdr.magic != FA_MAGIC)
112 /*@-refcounttrans@*/ return fd /*@=refcounttrans@*/ ;
115 static int fadNextOffset(FD_t fd, unsigned int lastOffset)
117 struct faHeader header;
120 offset = (lastOffset)
121 ? (lastOffset - sizeof(header))
122 : sizeof(struct faFileHeader);
124 if (offset >= fadFileSize)
127 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
130 if (!lastOffset && !header.isFree)
131 return (offset + sizeof(header));
135 offset += header.size;
137 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
140 if (!header.isFree) break;
142 while (offset < fadFileSize && header.isFree);
144 if (offset < fadFileSize)
146 /* Sanity check this to make sure we're not going in loops */
147 offset += sizeof(header);
149 if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
157 static int fadFirstOffset(FD_t fd)
159 return fadNextOffset(fd, 0);
163 static int dncmp(const void * a, const void * b)
166 const char *const * first = (const char *const *)a;
167 const char *const * second = (const char *const *)b;
168 return strcmp(*first, *second);
173 static void compressFilelist(Header h)
177 const char ** dirNames;
178 const char ** baseNames;
186 * This assumes the file list is already sorted, and begins with a
187 * single '/'. That assumption isn't critical, but it makes things go
191 if (headerIsEntry(h, RPMTAG_DIRNAMES))
193 xx = headerRemoveEntry(h, RPMTAG_OLDFILENAMES);
194 return; /* Already converted. */
197 HGEPtr_t hgePtr = NULL;
198 if (!headerGetEntryMinMemory(h, RPMTAG_OLDFILENAMES, hTYP_t(&fnt), &hgePtr, &count))
199 return; /* no file list */
200 fileNames = (char **)hgePtr;
201 if (fileNames == NULL || count <= 0)
204 dirNames = (const char **)alloca(sizeof(*dirNames) * count); /* worst case */
205 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
206 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
208 if (fileNames[0][0] != '/')
210 /* HACK. Source RPM, so just do things differently */
212 dirNames[dirIndex] = "";
213 for (rpm_count_t i = 0; i < count; i++)
215 dirIndexes[i] = dirIndex;
216 baseNames[i] = fileNames[i];
222 for (rpm_count_t i = 0; i < count; i++)
224 const char ** needle;
229 if (fileNames[i] == NULL) /* XXX can't happen */
231 baseName = strrchr(fileNames[i], '/') + 1;
232 len = baseName - fileNames[i];
234 savechar = *baseName;
238 (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL)
240 char *s = (char *)alloca(len + 1);
241 memcpy(s, fileNames[i], len + 1);
243 dirIndexes[i] = ++dirIndex;
244 dirNames[dirIndex] = s;
247 dirIndexes[i] = needle - dirNames;
250 *baseName = savechar;
251 baseNames[i] = baseName;
258 xx = headerAddEntry(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
259 xx = headerAddEntry(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
261 xx = headerAddEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
262 dirNames, dirIndex + 1);
265 fileNames = (char**)headerFreeData(fileNames, fnt);
267 xx = headerRemoveEntry(h, RPMTAG_OLDFILENAMES);
272 * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
273 * Retrofit an explicit "Provides: name = epoch:version-release".
275 void providePackageNVR(Header h)
277 const char *name, *version, *release;
278 HGEPtr_t hgePtr = NULL;
282 int_32 pFlags = RPMSENSE_EQUAL;
283 const char ** provides = NULL;
284 const char ** providesEVR = NULL;
286 int_32 * provideFlags = NULL;
287 rpm_count_t providesCount;
291 /* Generate provides for this package name-version-release. */
292 xx = headerNVR(h, &name, &version, &release);
293 if (!(name && version && release))
295 pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
297 if (headerGetEntryMinMemory(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL))
299 epoch = (int_32 *)hgePtr;
300 sprintf(p, "%d:", *epoch);
304 (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
307 * Rpm prior to 3.0.3 does not have versioned provides.
308 * If no provides at all are available, we can just add.
310 if (!headerGetEntryMinMemory(h, RPMTAG_PROVIDENAME, hTYP_t(&pnt), &hgePtr, &providesCount))
312 provides = (const char **)hgePtr;
315 * Otherwise, fill in entries on legacy packages.
317 if (!headerGetEntryMinMemory(h, RPMTAG_PROVIDEVERSION, hTYP_t(&pvt), &hgePtr, NULL))
319 providesEVR = (const char **)hgePtr;
320 for (rpm_count_t i = 0; i < providesCount; i++)
322 const char * vdummy = "";
323 int_32 fdummy = RPMSENSE_ANY;
324 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
326 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
332 xx = headerGetEntryMinMemory(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
333 provideFlags = (int_32 *)hgePtr;
335 /*@-nullderef@*/ /* LCL: providesEVR is not NULL */
336 if (provides && providesEVR && provideFlags)
337 for (rpm_count_t i = 0; i < providesCount; i++)
339 if (!(provides[i] && providesEVR[i]))
341 if (!(provideFlags[i] == RPMSENSE_EQUAL &&
342 !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
350 provides = (const char **)headerFreeData(provides, pnt);
351 providesEVR = (const char **)headerFreeData(providesEVR, pvt);
355 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
357 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
359 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
364 ///////////////////////////////////////////////////////////////////
365 ///////////////////////////////////////////////////////////////////
366 ///////////////////////////////////////////////////////////////////
371 #define Y2LOG "librpmDb"
373 /******************************************************************
376 ** FUNCTION NAME : internal_convertV3toV4
377 ** FUNCTION TYPE : int
379 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
380 callback::SendReport<ConvertDBReport> & report )
382 // Timecount _t( "convert V3 to V4" );
383 MIL << "Convert rpm3 database to rpm4" << endl;
386 FD_t fd = fadOpen( v3db_r.asString().c_str() );
390 ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
396 INT << "NULL rpmV4 database passed as argument!" << endl;
397 ZYPP_THROW(RpmNullDatabaseException());
400 shared_ptr<RpmException> err = v4db_r->error();
404 INT << "Can't access rpmV4 database " << v4db_r << endl;
408 // open rpmV4 database for writing. v4db_r is ok so librpm should
409 // be properly initialized.
411 string rootstr( v4db_r->root().asString() );
412 const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
414 int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
422 ZYPP_THROW(RpmDbOpenException(root, v4db_r->dbPath()));
425 // Check ammount of packages to process.
427 for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) )
431 MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
434 unsigned ignored = 0;
435 unsigned alreadyInV4 = 0;
436 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r );
446 #warning Add CBSuggest handling if needed, also on lines below
447 // CBSuggest proceed;
449 for ( int offset = fadFirstOffset(fd); offset && proceed /*!= CBSuggest::CANCEL*/;
450 offset = fadNextOffset(fd, offset),
451 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r ) )
454 // have to use lseek instead of Fseek because headerRead
456 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 )
458 ostream * reportAs = &(ERR);
459 /* proceed = report->dbReadError( offset );
460 if ( proceed == CBSuggest::SKIP ) {
463 reportAs = &(WAR << "IGNORED: ");
465 // PROCEED will fail after conversion; CANCEL immediately stop loop
468 (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
471 Header h = headerRead(fd, HEADER_MAGIC_NO);
474 ostream * reportAs = &(ERR);
475 /* proceed = report->dbReadError( offset );
476 if ( proceed == CBSuggest::SKIP ) {
479 reportAs = &(WAR << "IGNORED: ");
481 // PROCEED will fail after conversion; CANCEL immediately stop loop
484 (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
488 providePackageNVR(h);
489 const char *name = 0;
490 const char *version = 0;
491 const char *release = 0;
492 headerNVR(h, &name, &version, &release);
493 string nrv( string(name) + "-" + version + "-" + release );
494 rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
495 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
496 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
497 if (rpmdbNextIterator(mi))
499 // report.dbInV4( nrv );
500 WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
501 rpmdbFreeIterator(mi);
506 rpmdbFreeIterator(mi);
508 if (rpmdbAdd(db, -1, h, 0))
510 if (rpmdbAdd(db, -1, h, 0, 0))
513 // report.dbWriteError( nrv );
514 proceed = false;//CBSuggest::CANCEL; // immediately stop loop
516 ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
528 ERR << "Convert rpm3 database to rpm4: Aborted after "
529 << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
531 ZYPP_THROW(RpmDbConvertException());
535 MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
538 MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
542 MIL << "; IGNORED: " << ignored << " unconverted due to error";
548 /******************************************************************
551 * FUNCTION NAME : convertV3toV4
553 * \throws RpmException
556 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
559 callback::SendReport<ConvertDBReport> report;
560 report->start(v3db_r);
563 internal_convertV3toV4( v3db_r, v4db_r, report );
565 catch (RpmException & excpt_r)
567 report->finish(v3db_r, ConvertDBReport::FAILED,excpt_r.asUserString());
568 ZYPP_RETHROW(excpt_r);
570 report->finish(v3db_r, ConvertDBReport::NO_ERROR, "");
574 } // namespace target