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"
26 #define FA_MAGIC 0x02050920
31 unsigned int firstFree;
37 unsigned int freeNext; /* offset of the next free block, 0 if none */
38 unsigned int freePrev;
41 /* note that the u16's appear last for alignment/space reasons */
52 static int fadFileSize;
54 static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset)
56 if (Fseek(fd, offset, SEEK_SET) < 0)
58 return Fread(buf, sizeof(char), count, fd);
61 static FD_t fadOpen(const char * path)
63 struct faFileHeader newHdr;
67 fd = Fopen(path, "r.fdio");
68 if (!fd || Ferror(fd))
71 if (fstat(Fileno(fd), &stb))
76 fadFileSize = stb.st_size;
78 /* is this file brand new? */
84 if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr))
89 if (newHdr.magic != FA_MAGIC)
94 /*@-refcounttrans@*/ return fd /*@=refcounttrans@*/ ;
97 static int fadNextOffset(FD_t fd, unsigned int lastOffset)
99 struct faHeader header;
102 offset = (lastOffset)
103 ? (lastOffset - sizeof(header))
104 : sizeof(struct faFileHeader);
106 if (offset >= fadFileSize)
109 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
112 if (!lastOffset && !header.isFree)
113 return (offset + sizeof(header));
117 offset += header.size;
119 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
122 if (!header.isFree) break;
124 while (offset < fadFileSize && header.isFree);
126 if (offset < fadFileSize)
128 /* Sanity check this to make sure we're not going in loops */
129 offset += sizeof(header);
131 if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
139 static int fadFirstOffset(FD_t fd)
141 return fadNextOffset(fd, 0);
145 static int dncmp(const void * a, const void * b)
148 const char *const * first = (const char *const *)a;
149 const char *const * second = (const char *const *)b;
150 return strcmp(*first, *second);
155 static void compressFilelist(Header h)
158 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
159 HAE_t hae = (HAE_t)headerAddEntry;
160 HRE_t hre = (HRE_t)headerRemoveEntry;
161 HFD_t hfd = headerFreeData;
163 const char ** dirNames;
164 const char ** baseNames;
172 * This assumes the file list is already sorted, and begins with a
173 * single '/'. That assumption isn't critical, but it makes things go
177 if (headerIsEntry(h, RPMTAG_DIRNAMES))
179 xx = hre(h, RPMTAG_OLDFILENAMES);
180 return; /* Already converted. */
184 if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, &hgePtr, &count))
185 return; /* no file list */
186 fileNames = (char **)hgePtr;
187 if (fileNames == NULL || count <= 0)
190 dirNames = (const char **)alloca(sizeof(*dirNames) * count); /* worst case */
191 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
192 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
194 if (fileNames[0][0] != '/')
196 /* HACK. Source RPM, so just do things differently */
198 dirNames[dirIndex] = "";
199 for (i = 0; i < count; i++)
201 dirIndexes[i] = dirIndex;
202 baseNames[i] = fileNames[i];
208 for (i = 0; i < count; i++)
210 const char ** needle;
215 if (fileNames[i] == NULL) /* XXX can't happen */
217 baseName = strrchr(fileNames[i], '/') + 1;
218 len = baseName - fileNames[i];
220 savechar = *baseName;
224 (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL)
226 char *s = (char *)alloca(len + 1);
227 memcpy(s, fileNames[i], len + 1);
229 dirIndexes[i] = ++dirIndex;
230 dirNames[dirIndex] = s;
233 dirIndexes[i] = needle - dirNames;
236 *baseName = savechar;
237 baseNames[i] = baseName;
244 xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
245 xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
247 xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
248 dirNames, dirIndex + 1);
251 fileNames = (char**)hfd(fileNames, fnt);
253 xx = hre(h, RPMTAG_OLDFILENAMES);
258 * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
259 * Retrofit an explicit "Provides: name = epoch:version-release".
261 void providePackageNVR(Header h)
263 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
264 HFD_t hfd = headerFreeData;
265 const char *name, *version, *release;
270 int_32 pFlags = RPMSENSE_EQUAL;
271 const char ** provides = NULL;
272 const char ** providesEVR = NULL;
274 int_32 * provideFlags = NULL;
279 /* Generate provides for this package name-version-release. */
280 xx = headerNVR(h, &name, &version, &release);
281 if (!(name && version && release))
283 pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
285 if (hge(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL))
287 epoch = (int_32 *)hgePtr;
288 sprintf(p, "%d:", *epoch);
292 (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
295 * Rpm prior to 3.0.3 does not have versioned provides.
296 * If no provides at all are available, we can just add.
298 if (!hge(h, RPMTAG_PROVIDENAME, &pnt, &hgePtr, &providesCount))
300 provides = (const char **)hgePtr;
303 * Otherwise, fill in entries on legacy packages.
305 if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, &hgePtr, NULL))
307 providesEVR = (const char **)hgePtr;
308 for (i = 0; i < providesCount; i++)
311 int_32 fdummy = RPMSENSE_ANY;
312 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
314 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
320 xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
321 provideFlags = (int_32 *)hgePtr;
323 /*@-nullderef@*/ /* LCL: providesEVR is not NULL */
324 if (provides && providesEVR && provideFlags)
325 for (i = 0; i < providesCount; i++)
327 if (!(provides[i] && providesEVR[i]))
329 if (!(provideFlags[i] == RPMSENSE_EQUAL &&
330 !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
338 provides = (const char **)hfd(provides, pnt);
339 providesEVR = (const char **)hfd(providesEVR, pvt);
343 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
345 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
347 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
352 ///////////////////////////////////////////////////////////////////
353 ///////////////////////////////////////////////////////////////////
354 ///////////////////////////////////////////////////////////////////
359 #define Y2LOG "librpmDb"
361 /******************************************************************
364 ** FUNCTION NAME : internal_convertV3toV4
365 ** FUNCTION TYPE : int
367 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
368 callback::SendReport<ConvertDBReport> & report )
370 // Timecount _t( "convert V3 to V4" );
371 MIL << "Convert rpm3 database to rpm4" << endl;
374 FD_t fd = fadOpen( v3db_r.asString().c_str() );
378 ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
384 INT << "NULL rpmV4 database passed as argument!" << endl;
385 ZYPP_THROW(RpmNullDatabaseException());
388 shared_ptr<RpmException> err = v4db_r->error();
392 INT << "Can't access rpmV4 database " << v4db_r << endl;
396 // open rpmV4 database for writing. v4db_r is ok so librpm should
397 // be properly initialized.
399 string rootstr( v4db_r->root().asString() );
400 const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
402 int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
410 ZYPP_THROW(RpmDbOpenException(root, Pathname(string(db->db_root))));
413 // Check ammount of packages to process.
415 for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) )
419 MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
422 unsigned ignored = 0;
423 unsigned alreadyInV4 = 0;
424 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r );
434 #warning Add CBSuggest handling if needed, also on lines below
435 // CBSuggest proceed;
437 for ( int offset = fadFirstOffset(fd); offset && proceed /*!= CBSuggest::CANCEL*/;
438 offset = fadNextOffset(fd, offset),
439 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r ) )
442 // have to use lseek instead of Fseek because headerRead
444 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 )
446 ostream * reportAs = &(ERR);
447 /* proceed = report->dbReadError( offset );
448 if ( proceed == CBSuggest::SKIP ) {
451 reportAs = &(WAR << "IGNORED: ");
453 // PROCEED will fail after conversion; CANCEL immediately stop loop
456 (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
459 Header h = headerRead(fd, HEADER_MAGIC_NO);
462 ostream * reportAs = &(ERR);
463 /* proceed = report->dbReadError( offset );
464 if ( proceed == CBSuggest::SKIP ) {
467 reportAs = &(WAR << "IGNORED: ");
469 // PROCEED will fail after conversion; CANCEL immediately stop loop
472 (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
476 providePackageNVR(h);
477 const char *name = 0;
478 const char *version = 0;
479 const char *release = 0;
480 headerNVR(h, &name, &version, &release);
481 string nrv( string(name) + "-" + version + "-" + release );
482 rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
483 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
484 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
485 if (rpmdbNextIterator(mi))
487 // report.dbInV4( nrv );
488 WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
489 rpmdbFreeIterator(mi);
494 rpmdbFreeIterator(mi);
495 if (rpmdbAdd(db, -1, h, 0, 0))
497 // report.dbWriteError( nrv );
498 proceed = false;//CBSuggest::CANCEL; // immediately stop loop
500 ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
512 ERR << "Convert rpm3 database to rpm4: Aborted after "
513 << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
515 ZYPP_THROW(RpmDbConvertException());
519 MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
522 MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
526 MIL << "; IGNORED: " << ignored << " unconverted due to error";
532 /******************************************************************
535 * FUNCTION NAME : convertV3toV4
537 * \throws RpmException
540 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
543 callback::SendReport<ConvertDBReport> report;
544 report->start(v3db_r);
547 internal_convertV3toV4( v3db_r, v4db_r, report );
549 catch (RpmException & excpt_r)
551 report->finish(v3db_r, ConvertDBReport::FAILED,excpt_r.asUserString());
552 ZYPP_RETHROW(excpt_r);
554 report->finish(v3db_r, ConvertDBReport::NO_ERROR, "");
558 } // namespace target