00001
00002
00003
00004
00005
00006
00007
00008
00012 #include "librpm.h"
00013
00014 #include <iostream>
00015
00016 #include "zypp/base/Logger.h"
00017
00018 #include "zypp/target/rpm/librpmDb.h"
00019 #include "zypp/target/rpm/RpmCallbacks.h"
00020 #include "zypp/ZYppCallbacks.h"
00021
00022 extern "C"
00023 {
00024 #include <string.h>
00025
00026 #define FA_MAGIC 0x02050920
00027
00028 struct faFileHeader
00029 {
00030 unsigned int magic;
00031 unsigned int firstFree;
00032 };
00033
00034 struct faHeader
00035 {
00036 unsigned int size;
00037 unsigned int freeNext;
00038 unsigned int freePrev;
00039 unsigned int isFree;
00040
00041
00042 };
00043 }
00044
00045 namespace zypp
00046 {
00047 namespace target
00048 {
00049 namespace rpm
00050 {
00051
00052 static int fadFileSize;
00053
00054 static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset)
00055 {
00056 if (Fseek(fd, offset, SEEK_SET) < 0)
00057 return -1;
00058 return Fread(buf, sizeof(char), count, fd);
00059 }
00060
00061 static FD_t fadOpen(const char * path)
00062 {
00063 struct faFileHeader newHdr;
00064 FD_t fd;
00065 struct stat stb;
00066
00067 fd = Fopen(path, "r.fdio");
00068 if (!fd || Ferror(fd))
00069 return NULL;
00070
00071 if (fstat(Fileno(fd), &stb))
00072 {
00073 Fclose(fd);
00074 return NULL;
00075 }
00076 fadFileSize = stb.st_size;
00077
00078
00079 if (fadFileSize == 0)
00080 {
00081 Fclose(fd);
00082 return NULL;
00083 }
00084 if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr))
00085 {
00086 Fclose(fd);
00087 return NULL;
00088 }
00089 if (newHdr.magic != FA_MAGIC)
00090 {
00091 Fclose(fd);
00092 return NULL;
00093 }
00094 return fd ;
00095 }
00096
00097 static int fadNextOffset(FD_t fd, unsigned int lastOffset)
00098 {
00099 struct faHeader header;
00100 int offset;
00101
00102 offset = (lastOffset)
00103 ? (lastOffset - sizeof(header))
00104 : sizeof(struct faFileHeader);
00105
00106 if (offset >= fadFileSize)
00107 return 0;
00108
00109 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
00110 return 0;
00111
00112 if (!lastOffset && !header.isFree)
00113 return (offset + sizeof(header));
00114
00115 do
00116 {
00117 offset += header.size;
00118
00119 if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
00120 return 0;
00121
00122 if (!header.isFree) break;
00123 }
00124 while (offset < fadFileSize && header.isFree);
00125
00126 if (offset < fadFileSize)
00127 {
00128
00129 offset += sizeof(header);
00130
00131 if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
00132
00133 return offset;
00134 }
00135 else
00136 return 0;
00137 }
00138
00139 static int fadFirstOffset(FD_t fd)
00140 {
00141 return fadNextOffset(fd, 0);
00142 }
00143
00144
00145 static int dncmp(const void * a, const void * b)
00146
00147 {
00148 const char *const * first = (const char *const *)a;
00149 const char *const * second = (const char *const *)b;
00150 return strcmp(*first, *second);
00151 }
00152
00153
00154
00155 static void compressFilelist(Header h)
00156
00157 {
00158 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00159 HAE_t hae = (HAE_t)headerAddEntry;
00160 HRE_t hre = (HRE_t)headerRemoveEntry;
00161 HFD_t hfd = headerFreeData;
00162 char ** fileNames;
00163 const char ** dirNames;
00164 const char ** baseNames;
00165 int_32 * dirIndexes;
00166 rpmTagType fnt;
00167 int count;
00168 int i, xx;
00169 int dirIndex = -1;
00170
00171
00172
00173
00174
00175
00176
00177 if (headerIsEntry(h, RPMTAG_DIRNAMES))
00178 {
00179 xx = hre(h, RPMTAG_OLDFILENAMES);
00180 return;
00181 }
00182
00183 void *hgePtr = NULL;
00184 if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, &hgePtr, &count))
00185 return;
00186 fileNames = (char **)hgePtr;
00187 if (fileNames == NULL || count <= 0)
00188 return;
00189
00190 dirNames = (const char **)alloca(sizeof(*dirNames) * count);
00191 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
00192 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
00193
00194 if (fileNames[0][0] != '/')
00195 {
00196
00197 dirIndex = 0;
00198 dirNames[dirIndex] = "";
00199 for (i = 0; i < count; i++)
00200 {
00201 dirIndexes[i] = dirIndex;
00202 baseNames[i] = fileNames[i];
00203 }
00204 goto exit;
00205 }
00206
00207
00208 for (i = 0; i < count; i++)
00209 {
00210 const char ** needle;
00211 char savechar;
00212 char * baseName;
00213 int len;
00214
00215 if (fileNames[i] == NULL)
00216 continue;
00217 baseName = strrchr(fileNames[i], '/') + 1;
00218 len = baseName - fileNames[i];
00219 needle = dirNames;
00220 savechar = *baseName;
00221 *baseName = '\0';
00222
00223 if (dirIndex < 0 ||
00224 (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL)
00225 {
00226 char *s = (char *)alloca(len + 1);
00227 memcpy(s, fileNames[i], len + 1);
00228 s[len] = '\0';
00229 dirIndexes[i] = ++dirIndex;
00230 dirNames[dirIndex] = s;
00231 }
00232 else
00233 dirIndexes[i] = needle - dirNames;
00234
00235
00236 *baseName = savechar;
00237 baseNames[i] = baseName;
00238 }
00239
00240
00241 exit:
00242 if (count > 0)
00243 {
00244 xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
00245 xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
00246 baseNames, count);
00247 xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
00248 dirNames, dirIndex + 1);
00249 }
00250
00251 fileNames = (char**)hfd(fileNames, fnt);
00252
00253 xx = hre(h, RPMTAG_OLDFILENAMES);
00254 }
00255
00256
00257
00258
00259
00260
00261 void providePackageNVR(Header h)
00262 {
00263 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00264 HFD_t hfd = headerFreeData;
00265 const char *name, *version, *release;
00266 void *hgePtr = NULL;
00267 int_32 * epoch;
00268 const char *pEVR;
00269 char *p;
00270 int_32 pFlags = RPMSENSE_EQUAL;
00271 const char ** provides = NULL;
00272 const char ** providesEVR = NULL;
00273 rpmTagType pnt, pvt;
00274 int_32 * provideFlags = NULL;
00275 int providesCount;
00276 int i, xx;
00277 int bingo = 1;
00278
00279
00280 xx = headerNVR(h, &name, &version, &release);
00281 if (!(name && version && release))
00282 return;
00283 pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
00284 *p = '\0';
00285 if (hge(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL))
00286 {
00287 epoch = (int_32 *)hgePtr;
00288 sprintf(p, "%d:", *epoch);
00289 while (*p != '\0')
00290 p++;
00291 }
00292 (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
00293
00294
00295
00296
00297
00298 if (!hge(h, RPMTAG_PROVIDENAME, &pnt, &hgePtr, &providesCount))
00299 goto exit;
00300 provides = (const char **)hgePtr;
00301
00302
00303
00304
00305 if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, &hgePtr, NULL))
00306 {
00307 providesEVR = (const char **)hgePtr;
00308 for (i = 0; i < providesCount; i++)
00309 {
00310 const char * vdummy = "";
00311 int_32 fdummy = RPMSENSE_ANY;
00312 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
00313 &vdummy, 1);
00314 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
00315 &fdummy, 1);
00316 }
00317 goto exit;
00318 }
00319
00320 xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
00321 provideFlags = (int_32 *)hgePtr;
00322
00323
00324 if (provides && providesEVR && provideFlags)
00325 for (i = 0; i < providesCount; i++)
00326 {
00327 if (!(provides[i] && providesEVR[i]))
00328 continue;
00329 if (!(provideFlags[i] == RPMSENSE_EQUAL &&
00330 !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
00331 continue;
00332 bingo = 0;
00333 break;
00334 }
00335
00336
00337 exit:
00338 provides = (const char **)hfd(provides, pnt);
00339 providesEVR = (const char **)hfd(providesEVR, pvt);
00340
00341 if (bingo)
00342 {
00343 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
00344 &name, 1);
00345 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
00346 &pFlags, 1);
00347 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
00348 &pEVR, 1);
00349 }
00350 }
00351
00355
00356 using namespace std;
00357
00358 #undef Y2LOG
00359 #define Y2LOG "librpmDb"
00360
00361
00362
00363
00364
00365
00366
00367 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
00368 callback::SendReport<ConvertDBReport> & report )
00369 {
00370
00371 MIL << "Convert rpm3 database to rpm4" << endl;
00372
00373
00374 FD_t fd = fadOpen( v3db_r.asString().c_str() );
00375 if ( fd == 0 )
00376 {
00377 Fclose( fd );
00378 ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
00379 }
00380
00381 if ( ! v4db_r )
00382 {
00383 Fclose( fd );
00384 INT << "NULL rpmV4 database passed as argument!" << endl;
00385 ZYPP_THROW(RpmNullDatabaseException());
00386 }
00387
00388 shared_ptr<RpmException> err = v4db_r->error();
00389 if ( err )
00390 {
00391 Fclose( fd );
00392 INT << "Can't access rpmV4 database " << v4db_r << endl;
00393 ZYPP_THROW(*err);
00394 }
00395
00396
00397
00398 rpmdb db = 0;
00399 string rootstr( v4db_r->root().asString() );
00400 const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
00401
00402 int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
00403 if ( res || ! db )
00404 {
00405 if ( db )
00406 {
00407 ::rpmdbClose( db );
00408 }
00409 Fclose( fd );
00410 ZYPP_THROW(RpmDbOpenException(root, Pathname(string(db->db_root))));
00411 }
00412
00413
00414 int max = 0;
00415 for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) )
00416 {
00417 ++max;
00418 }
00419 MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
00420
00421 unsigned failed = 0;
00422 unsigned ignored = 0;
00423 unsigned alreadyInV4 = 0;
00424 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r );
00425
00426 if ( !max )
00427 {
00428 Fclose( fd );
00429 ::rpmdbClose( db );
00430 return;
00431 }
00432
00433
00434 #warning Add CBSuggest handling if needed, also on lines below
00435
00436 bool proceed = true;
00437 for ( int offset = fadFirstOffset(fd); offset && proceed ;
00438 offset = fadNextOffset(fd, offset),
00439 report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r ) )
00440 {
00441
00442
00443
00444 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 )
00445 {
00446 ostream * reportAs = &(ERR);
00447
00448
00449
00450
00451
00452
00453
00454 ++failed;
00455
00456 (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
00457 continue;
00458 }
00459 Header h = headerRead(fd, HEADER_MAGIC_NO);
00460 if ( ! h )
00461 {
00462 ostream * reportAs = &(ERR);
00463
00464
00465
00466
00467
00468
00469
00470 ++failed;
00471
00472 (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
00473 continue;
00474 }
00475 compressFilelist(h);
00476 providePackageNVR(h);
00477 const char *name = 0;
00478 const char *version = 0;
00479 const char *release = 0;
00480 headerNVR(h, &name, &version, &release);
00481 string nrv( string(name) + "-" + version + "-" + release );
00482 rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
00483 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
00484 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
00485 if (rpmdbNextIterator(mi))
00486 {
00487
00488 WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
00489 rpmdbFreeIterator(mi);
00490 headerFree(h);
00491 ++alreadyInV4;
00492 continue;
00493 }
00494 rpmdbFreeIterator(mi);
00495 if (rpmdbAdd(db, -1, h, 0, 0))
00496 {
00497
00498 proceed = false;
00499 ++failed;
00500 ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
00501 headerFree(h);
00502 continue;
00503 }
00504 headerFree(h);
00505 }
00506
00507 Fclose(fd);
00508 ::rpmdbClose(db);
00509
00510 if ( failed )
00511 {
00512 ERR << "Convert rpm3 database to rpm4: Aborted after "
00513 << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
00514 << endl;
00515 ZYPP_THROW(RpmDbConvertException());
00516 }
00517 else
00518 {
00519 MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
00520 if ( alreadyInV4 )
00521 {
00522 MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
00523 }
00524 if ( ignored )
00525 {
00526 MIL << "; IGNORED: " << ignored << " unconverted due to error";
00527 }
00528 MIL << endl;
00529 }
00530 }
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
00541 {
00542
00543 callback::SendReport<ConvertDBReport> report;
00544 report->start(v3db_r);
00545 try
00546 {
00547 internal_convertV3toV4( v3db_r, v4db_r, report );
00548 }
00549 catch (RpmException & excpt_r)
00550 {
00551 report->finish(v3db_r, ConvertDBReport::FAILED,excpt_r.asUserString());
00552 ZYPP_RETHROW(excpt_r);
00553 }
00554 report->finish(v3db_r, ConvertDBReport::NO_ERROR, "");
00555 }
00556
00557 }
00558 }
00559 }