1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/librpmDb.cv3.cc
17 #include "zypp/base/Logger.h"
19 #include "zypp/target/rpm/librpmDb.h"
20 #include "zypp/target/rpm/RpmCallbacks.h"
21 #include "zypp/ZYppCallbacks.h"
27 #define FA_MAGIC 0x02050920
32 unsigned int firstFree;
38 unsigned int freeNext; /* offset of the next free block, 0 if none */
39 unsigned int freePrev;
42 /* note that the u16's appear last for alignment/space reasons */
53 static int fadFileSize;
55 static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset)
57 if (Fseek(fd, offset, SEEK_SET) < 0)
59 return Fread(buf, sizeof(char), count, fd);
62 static FD_t fadOpen(const char * path)
64 struct faFileHeader newHdr;
68 fd = Fopen(path, "r.fdio");
69 if (!fd || Ferror(fd))
72 if (fstat(Fileno(fd), &stb))
77 fadFileSize = stb.st_size;
79 /* is this file brand new? */
85 if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr))
90 if (newHdr.magic != FA_MAGIC)
95 /*@-refcounttrans@*/ return fd /*@=refcounttrans@*/ ;
98 static int fadNextOffset(FD_t fd, unsigned int lastOffset)
100 struct faHeader header;
103 offset = (lastOffset)
104 ? (lastOffset - sizeof(header))
105 : sizeof(struct faFileHeader);
107 if (offset >= fadFileSize)
110 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
113 if (!lastOffset && !header.isFree)
114 return (offset + sizeof(header));
118 offset += header.size;
120 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
123 if (!header.isFree) break;
125 while (offset < fadFileSize && header.isFree);
127 if (offset < fadFileSize)
129 /* Sanity check this to make sure we're not going in loops */
130 offset += sizeof(header);
132 if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
140 static int fadFirstOffset(FD_t fd)
142 return fadNextOffset(fd, 0);
146 static int dncmp(const void * a, const void * b)
149 const char *const * first = (const char *const *)a;
150 const char *const * second = (const char *const *)b;
151 return strcmp(*first, *second);
156 static void compressFilelist(Header h)
159 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
160 HAE_t hae = (HAE_t)headerAddEntry;
161 HRE_t hre = (HRE_t)headerRemoveEntry;
162 HFD_t hfd = headerFreeData;
164 const char ** dirNames;
165 const char ** baseNames;
173 * This assumes the file list is already sorted, and begins with a
174 * single '/'. That assumption isn't critical, but it makes things go
178 if (headerIsEntry(h, RPMTAG_DIRNAMES))
180 xx = hre(h, RPMTAG_OLDFILENAMES);
181 return; /* Already converted. */
185 if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, &hgePtr, &count))
186 return; /* no file list */
187 fileNames = (char **)hgePtr;
188 if (fileNames == NULL || count <= 0)
191 dirNames = (const char **)alloca(sizeof(*dirNames) * count); /* worst case */
192 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
193 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
195 if (fileNames[0][0] != '/')
197 /* HACK. Source RPM, so just do things differently */
199 dirNames[dirIndex] = "";
200 for (i = 0; i < count; i++)
202 dirIndexes[i] = dirIndex;
203 baseNames[i] = fileNames[i];
209 for (i = 0; i < count; i++)
211 const char ** needle;
216 if (fileNames[i] == NULL) /* XXX can't happen */
218 baseName = strrchr(fileNames[i], '/') + 1;
219 len = baseName - fileNames[i];
221 savechar = *baseName;
225 (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL)
227 char *s = (char *)alloca(len + 1);
228 memcpy(s, fileNames[i], len + 1);
230 dirIndexes[i] = ++dirIndex;
231 dirNames[dirIndex] = s;
234 dirIndexes[i] = needle - dirNames;
237 *baseName = savechar;
238 baseNames[i] = baseName;
245 xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
246 xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
248 xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
249 dirNames, dirIndex + 1);
252 fileNames = (char**)hfd(fileNames, fnt);
254 xx = hre(h, RPMTAG_OLDFILENAMES);
259 * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
260 * Retrofit an explicit "Provides: name = epoch:version-release".
262 void providePackageNVR(Header h)
264 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
265 HFD_t hfd = headerFreeData;
266 const char *name, *version, *release;
271 int_32 pFlags = RPMSENSE_EQUAL;
272 const char ** provides = NULL;
273 const char ** providesEVR = NULL;
275 int_32 * provideFlags = NULL;
280 /* Generate provides for this package name-version-release. */
281 xx = headerNVR(h, &name, &version, &release);
282 if (!(name && version && release))
284 pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
286 if (hge(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL))
288 epoch = (int_32 *)hgePtr;
289 sprintf(p, "%d:", *epoch);
293 (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
296 * Rpm prior to 3.0.3 does not have versioned provides.
297 * If no provides at all are available, we can just add.
299 if (!hge(h, RPMTAG_PROVIDENAME, &pnt, &hgePtr, &providesCount))
301 provides = (const char **)hgePtr;
304 * Otherwise, fill in entries on legacy packages.
306 if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, &hgePtr, NULL))
308 providesEVR = (const char **)hgePtr;
309 for (i = 0; i < providesCount; i++)
311 const char * vdummy = "";
312 int_32 fdummy = RPMSENSE_ANY;
313 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
315 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
321 xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
322 provideFlags = (int_32 *)hgePtr;
324 /*@-nullderef@*/ /* LCL: providesEVR is not NULL */
325 if (provides && providesEVR && provideFlags)
326 for (i = 0; i < providesCount; i++)
328 if (!(provides[i] && providesEVR[i]))
330 if (!(provideFlags[i] == RPMSENSE_EQUAL &&
331 !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
339 provides = (const char **)hfd(provides, pnt);
340 providesEVR = (const char **)hfd(providesEVR, pvt);
344 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
346 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
348 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
353 ///////////////////////////////////////////////////////////////////
354 ///////////////////////////////////////////////////////////////////
355 ///////////////////////////////////////////////////////////////////
360 #define Y2LOG "librpmDb"
362 /******************************************************************
365 ** FUNCTION NAME : internal_convertV3toV4
366 ** FUNCTION TYPE : int
368 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
369 callback::SendReport<ConvertDBReport> & report )
371 // Timecount _t( "convert V3 to V4" );
372 MIL << "Convert rpm3 database to rpm4" << endl;
375 FD_t fd = fadOpen( v3db_r.asString().c_str() );
379 ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
385 INT << "NULL rpmV4 database passed as argument!" << endl;
386 ZYPP_THROW(RpmNullDatabaseException());
389 shared_ptr<RpmException> err = v4db_r->error();
393 INT << "Can't access rpmV4 database " << v4db_r << endl;
397 // open rpmV4 database for writing. v4db_r is ok so librpm should
398 // be properly initialized.
400 string rootstr( v4db_r->root().asString() );
401 const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
403 int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
411 ZYPP_THROW(RpmDbOpenException(root, Pathname(string(db->db_root))));
414 // Check ammount of packages to process.
416 for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) )
420 MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
423 unsigned ignored = 0;
424 unsigned alreadyInV4 = 0;
425 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r );
435 #warning Add CBSuggest handling if needed, also on lines below
436 // CBSuggest proceed;
438 for ( int offset = fadFirstOffset(fd); offset && proceed /*!= CBSuggest::CANCEL*/;
439 offset = fadNextOffset(fd, offset),
440 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r ) )
443 // have to use lseek instead of Fseek because headerRead
445 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 )
447 ostream * reportAs = &(ERR);
448 /* proceed = report->dbReadError( offset );
449 if ( proceed == CBSuggest::SKIP ) {
452 reportAs = &(WAR << "IGNORED: ");
454 // PROCEED will fail after conversion; CANCEL immediately stop loop
457 (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
460 Header h = headerRead(fd, HEADER_MAGIC_NO);
463 ostream * reportAs = &(ERR);
464 /* proceed = report->dbReadError( offset );
465 if ( proceed == CBSuggest::SKIP ) {
468 reportAs = &(WAR << "IGNORED: ");
470 // PROCEED will fail after conversion; CANCEL immediately stop loop
473 (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
477 providePackageNVR(h);
478 const char *name = 0;
479 const char *version = 0;
480 const char *release = 0;
481 headerNVR(h, &name, &version, &release);
482 string nrv( string(name) + "-" + version + "-" + release );
483 rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
484 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
485 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
486 if (rpmdbNextIterator(mi))
488 // report.dbInV4( nrv );
489 WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
490 rpmdbFreeIterator(mi);
495 rpmdbFreeIterator(mi);
496 if (rpmdbAdd(db, -1, h, 0, 0))
498 // report.dbWriteError( nrv );
499 proceed = false;//CBSuggest::CANCEL; // immediately stop loop
501 ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
513 ERR << "Convert rpm3 database to rpm4: Aborted after "
514 << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
516 ZYPP_THROW(RpmDbConvertException());
520 MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
523 MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
527 MIL << "; IGNORED: " << ignored << " unconverted due to error";
533 /******************************************************************
536 * FUNCTION NAME : convertV3toV4
538 * \throws RpmException
541 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
544 callback::SendReport<ConvertDBReport> report;
545 report->start(v3db_r);
548 internal_convertV3toV4( v3db_r, v4db_r, report );
550 catch (RpmException & excpt_r)
552 report->finish(v3db_r, ConvertDBReport::FAILED,excpt_r.asUserString());
553 ZYPP_RETHROW(excpt_r);
555 report->finish(v3db_r, ConvertDBReport::NO_ERROR, "");
559 } // namespace target