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"
24 #define FA_MAGIC 0x02050920
28 unsigned int firstFree;
33 unsigned int freeNext; /* offset of the next free block, 0 if none */
34 unsigned int freePrev;
37 /* note that the u16's appear last for alignment/space reasons */
45 static int fadFileSize;
47 static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset) {
48 if (Fseek(fd, offset, SEEK_SET) < 0)
50 return Fread(buf, sizeof(char), count, fd);
53 static FD_t fadOpen(const char * path)
55 struct faFileHeader newHdr;
59 fd = Fopen(path, "r.fdio");
60 if (!fd || Ferror(fd))
63 if (fstat(Fileno(fd), &stb)) {
67 fadFileSize = stb.st_size;
69 /* is this file brand new? */
70 if (fadFileSize == 0) {
74 if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr)) {
78 if (newHdr.magic != FA_MAGIC) {
82 /*@-refcounttrans@*/ return fd /*@=refcounttrans@*/ ;
85 static int fadNextOffset(FD_t fd, unsigned int lastOffset)
87 struct faHeader header;
91 ? (lastOffset - sizeof(header))
92 : sizeof(struct faFileHeader);
94 if (offset >= fadFileSize)
97 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
100 if (!lastOffset && !header.isFree)
101 return (offset + sizeof(header));
104 offset += header.size;
106 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
109 if (!header.isFree) break;
110 } while (offset < fadFileSize && header.isFree);
112 if (offset < fadFileSize) {
113 /* Sanity check this to make sure we're not going in loops */
114 offset += sizeof(header);
116 if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
123 static int fadFirstOffset(FD_t fd)
125 return fadNextOffset(fd, 0);
129 static int dncmp(const void * a, const void * b)
132 const char *const * first = (const char *const *)a;
133 const char *const * second = (const char *const *)b;
134 return strcmp(*first, *second);
139 static void compressFilelist(Header h)
142 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
143 HAE_t hae = (HAE_t)headerAddEntry;
144 HRE_t hre = (HRE_t)headerRemoveEntry;
145 HFD_t hfd = headerFreeData;
147 const char ** dirNames;
148 const char ** baseNames;
156 * This assumes the file list is already sorted, and begins with a
157 * single '/'. That assumption isn't critical, but it makes things go
161 if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
162 xx = hre(h, RPMTAG_OLDFILENAMES);
163 return; /* Already converted. */
167 if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, &hgePtr, &count))
168 return; /* no file list */
169 fileNames = (char **)hgePtr;
170 if (fileNames == NULL || count <= 0)
173 dirNames = (const char **)alloca(sizeof(*dirNames) * count); /* worst case */
174 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
175 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
177 if (fileNames[0][0] != '/') {
178 /* HACK. Source RPM, so just do things differently */
180 dirNames[dirIndex] = "";
181 for (i = 0; i < count; i++) {
182 dirIndexes[i] = dirIndex;
183 baseNames[i] = fileNames[i];
189 for (i = 0; i < count; i++) {
190 const char ** needle;
195 if (fileNames[i] == NULL) /* XXX can't happen */
197 baseName = strrchr(fileNames[i], '/') + 1;
198 len = baseName - fileNames[i];
200 savechar = *baseName;
204 (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
205 char *s = (char *)alloca(len + 1);
206 memcpy(s, fileNames[i], len + 1);
208 dirIndexes[i] = ++dirIndex;
209 dirNames[dirIndex] = s;
211 dirIndexes[i] = needle - dirNames;
214 *baseName = savechar;
215 baseNames[i] = baseName;
221 xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
222 xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
224 xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
225 dirNames, dirIndex + 1);
228 fileNames = (char**)hfd(fileNames, fnt);
230 xx = hre(h, RPMTAG_OLDFILENAMES);
235 * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
236 * Retrofit an explicit "Provides: name = epoch:version-release".
238 void providePackageNVR(Header h)
240 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
241 HFD_t hfd = headerFreeData;
242 const char *name, *version, *release;
247 int_32 pFlags = RPMSENSE_EQUAL;
248 const char ** provides = NULL;
249 const char ** providesEVR = NULL;
251 int_32 * provideFlags = NULL;
256 /* Generate provides for this package name-version-release. */
257 xx = headerNVR(h, &name, &version, &release);
258 if (!(name && version && release))
260 pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
262 if (hge(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL)) {
263 epoch = (int_32 *)hgePtr;
264 sprintf(p, "%d:", *epoch);
268 (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
271 * Rpm prior to 3.0.3 does not have versioned provides.
272 * If no provides at all are available, we can just add.
274 if (!hge(h, RPMTAG_PROVIDENAME, &pnt, &hgePtr, &providesCount))
276 provides = (const char **)hgePtr;
279 * Otherwise, fill in entries on legacy packages.
281 if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, &hgePtr, NULL)) {
282 providesEVR = (const char **)hgePtr;
283 for (i = 0; i < providesCount; i++) {
285 int_32 fdummy = RPMSENSE_ANY;
286 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
288 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
294 xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
295 provideFlags = (int_32 *)hgePtr;
297 /*@-nullderef@*/ /* LCL: providesEVR is not NULL */
298 if (provides && providesEVR && provideFlags)
299 for (i = 0; i < providesCount; i++) {
300 if (!(provides[i] && providesEVR[i]))
302 if (!(provideFlags[i] == RPMSENSE_EQUAL &&
303 !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
311 provides = (const char **)hfd(provides, pnt);
312 providesEVR = (const char **)hfd(providesEVR, pvt);
315 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
317 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
319 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
324 ///////////////////////////////////////////////////////////////////
325 ///////////////////////////////////////////////////////////////////
326 ///////////////////////////////////////////////////////////////////
331 #define Y2LOG "librpmDb"
333 /******************************************************************
336 ** FUNCTION NAME : internal_convertV3toV4
337 ** FUNCTION TYPE : int
339 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
340 ConvertDbReport & report )
342 // Timecount _t( "convert V3 to V4" );
343 MIL << "Convert rpm3 database to rpm4" << endl;
346 FD_t fd = fadOpen( v3db_r.asString().c_str() );
349 ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
354 INT << "NULL rpmV4 database passed as argument!" << endl;
355 ZYPP_THROW(RpmNullDatabaseException());
358 shared_ptr<RpmException> err = v4db_r->error();
361 INT << "Can't access rpmV4 database " << v4db_r << endl;
365 // open rpmV4 database for writing. v4db_r is ok so librpm should
366 // be properly initialized.
368 string rootstr( v4db_r->root().asString() );
369 const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
371 int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
377 ZYPP_THROW(RpmDbOpenException(root, Pathname(string(db->db_root))));
380 // Check ammount of packages to process.
382 for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) ) {
385 MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
388 unsigned ignored = 0;
389 unsigned alreadyInV4 = 0;
390 report.progress( (100 * (failed + ignored + alreadyInV4) / max) );
399 #warning Add CBSuggest handling if needed, also on lines below
400 // CBSuggest proceed;
402 for ( int offset = fadFirstOffset(fd); offset && proceed /*!= CBSuggest::CANCEL*/;
403 offset = fadNextOffset(fd, offset),
404 report.progress( (100 * (failed + ignored + alreadyInV4) / max) ) )
407 // have to use lseek instead of Fseek because headerRead
409 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 ) {
410 ostream * reportAs = &(ERR);
411 /* proceed = report->dbReadError( offset );
412 if ( proceed == CBSuggest::SKIP ) {
415 reportAs = &(WAR << "IGNORED: ");
417 // PROCEED will fail after conversion; CANCEL immediately stop loop
420 (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
423 Header h = headerRead(fd, HEADER_MAGIC_NO);
425 ostream * reportAs = &(ERR);
426 /* proceed = report->dbReadError( offset );
427 if ( proceed == CBSuggest::SKIP ) {
430 reportAs = &(WAR << "IGNORED: ");
432 // PROCEED will fail after conversion; CANCEL immediately stop loop
435 (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
439 providePackageNVR(h);
440 const char *name = 0;
441 const char *version = 0;
442 const char *release = 0;
443 headerNVR(h, &name, &version, &release);
444 string nrv( string(name) + "-" + version + "-" + release );
445 rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
446 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
447 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
448 if (rpmdbNextIterator(mi)) {
449 // report.dbInV4( nrv );
450 WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
451 rpmdbFreeIterator(mi);
456 rpmdbFreeIterator(mi);
457 if (rpmdbAdd(db, -1, h, 0, 0)) {
458 // report.dbWriteError( nrv );
459 proceed = false;//CBSuggest::CANCEL; // immediately stop loop
461 ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
472 ERR << "Convert rpm3 database to rpm4: Aborted after "
473 << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
475 ZYPP_THROW(RpmDbConvertException());
477 MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
479 MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
482 MIL << "; IGNORED: " << ignored << " unconverted due to error";
488 /******************************************************************
491 * FUNCTION NAME : convertV3toV4
493 * \throws RpmException
496 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
499 ConvertDbReport report;
502 internal_convertV3toV4( v3db_r, v4db_r, report );
504 catch (RpmException & excpt_r)
507 ZYPP_RETHROW(excpt_r);
513 } // namespace target