- read-only, not exclusive rpmdb lock for the initial dependency processing
[apt.git] / apt-pkg / rpm / rpmhandler.cc
1
2 /*
3  ######################################################################
4
5  RPM database and hdlist related handling
6
7  ######################################################################
8  */
9
10 #include <config.h>
11
12 #ifdef HAVE_RPM
13
14 #include <fcntl.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <utime.h>
18 #include <unistd.h>
19 #include <assert.h>
20
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/configuration.h>
23 #include <apt-pkg/md5.h>
24
25 #include <apt-pkg/rpmhandler.h>
26 #include <apt-pkg/rpmpackagedata.h>
27
28 #include <apti18n.h>
29
30 #if RPM_VERSION >= 0x040100
31 #include <rpm/rpmts.h>
32 #include <rpm/rpmdb.h>
33 #define rpmxxInitIterator(a,b,c,d) rpmtsInitIterator(a,(rpmTag)b,c,d)
34 #else
35 #define rpmxxInitIterator(a,b,c,d) rpmdbInitIterator(a,b,c,d)
36 #endif
37
38 RPMFileHandler::RPMFileHandler(string File)
39 {
40    ID = File;
41    FD = Fopen(File.c_str(), "r");
42    if (FD == NULL)
43    {
44       /*
45       _error->Error(_("could not open RPM package list file %s: %s"),
46                     File.c_str(), rpmErrorString());
47       */
48       return;
49    }
50    iSize = fdSize(FD);
51 }
52
53 RPMFileHandler::RPMFileHandler(FileFd *File)
54 {
55    FD = fdDup(File->Fd());
56    if (FD == NULL)
57    {
58       /*
59       _error->Error(_("could not create RPM file descriptor: %s"),
60                     rpmErrorString());
61       */
62       return;
63    }
64    iSize = fdSize(FD);
65 }
66
67 RPMFileHandler::~RPMFileHandler()
68 {
69    if (HeaderP != NULL)
70       headerFree(HeaderP);
71    if (FD != NULL)
72       Fclose(FD);
73 }
74
75 bool RPMFileHandler::Skip()
76 {
77    if (FD == NULL)
78       return false;
79    iOffset = lseek(Fileno(FD),0,SEEK_CUR);
80    if (HeaderP != NULL)
81        headerFree(HeaderP);
82    HeaderP = headerRead(FD, HEADER_MAGIC_YES);
83    return (HeaderP != NULL);
84 }
85
86 bool RPMFileHandler::Jump(unsigned Offset)
87 {
88    if (FD == NULL)
89       return false;
90    if (lseek(Fileno(FD),Offset,SEEK_SET) != Offset)
91       return false;
92    return Skip();
93 }
94
95 void RPMFileHandler::Rewind()
96 {
97    if (FD == NULL)
98       return;
99    iOffset = lseek(Fileno(FD),0,SEEK_SET);
100    if (iOffset != 0)
101       _error->Error(_("could not rewind RPMFileHandler"));
102 }
103
104 string RPMFileHandler::FileName()
105 {
106    char *str;
107    int_32 count, type;
108    assert(HeaderP != NULL);
109    int rc = headerGetEntry(HeaderP, CRPMTAG_FILENAME,
110                            &type, (void**)&str, &count);
111    assert(rc != 0);
112    return str;
113 }
114
115 string RPMFileHandler::Directory()
116 {
117    char *str;
118    int_32 count, type;
119    assert(HeaderP != NULL);
120    int rc = headerGetEntry(HeaderP, CRPMTAG_DIRECTORY,
121                            &type, (void**)&str, &count);
122    return (rc?str:"");
123 }
124
125 unsigned long RPMFileHandler::FileSize()
126 {
127    int_32 count, type;
128    int_32 *num;
129    int rc = headerGetEntry(HeaderP, CRPMTAG_FILESIZE,
130                            &type, (void**)&num, &count);
131    assert(rc != 0);
132    return (unsigned long)num[0];
133 }
134
135 string RPMFileHandler::MD5Sum()
136 {
137    char *str;
138    int_32 count, type;
139    assert(HeaderP != NULL);
140    int rc = headerGetEntry(HeaderP, CRPMTAG_MD5,
141                            &type, (void**)&str, &count);
142    assert(rc != 0);
143    return str;
144 }
145
146 bool RPMSingleFileHandler::Skip()
147 {
148    if (FD == NULL)
149       return false;
150    if (HeaderP != NULL) {
151       headerFree(HeaderP);
152       HeaderP = NULL;
153       return false;
154    }
155 #if RPM_VERSION >= 0x040100
156    rpmts TS = rpmtsCreate();
157    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
158    int rc = rpmReadPackageFile(TS, FD, sFilePath.c_str(), &HeaderP);
159    if (rc != RPMRC_OK && rc != RPMRC_NOTTRUSTED && rc != RPMRC_NOKEY) {
160       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
161       HeaderP = NULL;
162    }
163    rpmtsFree(TS);
164 #else
165    int rc = rpmReadPackageHeader(FD, &HeaderP, 0, NULL, NULL);
166    if (rc) {
167       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
168       HeaderP = NULL;
169    }
170 #endif
171    return (HeaderP != NULL);
172 }
173
174 bool RPMSingleFileHandler::Jump(unsigned Offset)
175 {
176    assert(Offset == 0);
177    Rewind();
178    return RPMFileHandler::Jump(Offset);
179 }
180
181 void RPMSingleFileHandler::Rewind()
182 {
183    if (FD == NULL)
184       return;
185    if (HeaderP != NULL) {
186       HeaderP = NULL;
187       headerFree(HeaderP);
188    }
189    lseek(Fileno(FD),0,SEEK_SET);
190 }
191
192 unsigned long RPMSingleFileHandler::FileSize()
193 {
194    struct stat S;
195    if (stat(sFilePath.c_str(),&S) != 0)
196       return 0;
197    return S.st_size;
198 }
199
200 string RPMSingleFileHandler::MD5Sum()
201 {
202    MD5Summation MD5;
203    FileFd File(sFilePath, FileFd::ReadOnly);
204    MD5.AddFD(File.Fd(), File.Size());
205    File.Close();
206    return MD5.Result().Value();
207 }
208
209 RPMDirHandler::RPMDirHandler(string DirName)
210    : sDirName(DirName)
211 {
212    ID = DirName;
213 #if RPM_VERSION >= 0x040100
214    TS = NULL;
215 #endif
216    Dir = opendir(sDirName.c_str());
217    if (Dir == NULL)
218       return;
219    iSize = 0;
220    while (nextFileName() != NULL)
221       iSize += 1;
222    rewinddir(Dir);
223 #if RPM_VERSION >= 0x040100
224    TS = rpmtsCreate();
225    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
226 #endif
227 }
228
229 const char *RPMDirHandler::nextFileName()
230 {
231    for (struct dirent *Ent = readdir(Dir); Ent != 0; Ent = readdir(Dir))
232    {
233       const char *name = Ent->d_name;
234
235       if (name[0] == '.')
236          continue;
237
238       if (flExtension(name) != "rpm")
239          continue;
240
241       // Make sure it is a file and not something else
242       sFilePath = flCombine(sDirName,name);
243       struct stat St;
244       if (stat(sFilePath.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
245          continue;
246
247       sFileName = name;
248       
249       return name;
250    } 
251    return NULL;
252 }
253
254 RPMDirHandler::~RPMDirHandler()
255 {
256    if (HeaderP != NULL)
257       headerFree(HeaderP);
258 #if RPM_VERSION >= 0x040100
259    if (TS != NULL)
260       rpmtsFree(TS);
261 #endif
262    if (Dir != NULL)
263       closedir(Dir);
264 }
265
266 bool RPMDirHandler::Skip()
267 {
268    if (Dir == NULL)
269       return false;
270    if (HeaderP != NULL) {
271       headerFree(HeaderP);
272       HeaderP = NULL;
273    }
274    const char *fname = nextFileName();
275    bool Res = false;
276    for (; fname != NULL; fname = nextFileName()) {
277       iOffset++;
278       if (fname == NULL)
279          break;
280       FD_t FD = Fopen(sFilePath.c_str(), "r");
281       if (FD == NULL)
282          continue;
283 #if RPM_VERSION >= 0x040100
284       int rc = rpmReadPackageFile(TS, FD, fname, &HeaderP);
285       Fclose(FD);
286       if (rc != RPMRC_OK
287           && rc != RPMRC_NOTTRUSTED
288           && rc != RPMRC_NOKEY)
289          continue;
290 #else
291       int isSource;
292       int rc = rpmReadPackageHeader(FD, &HeaderP, &isSource, NULL, NULL);
293       Fclose(FD);
294       if (rc != 0)
295          continue;
296 #endif
297       Res = true;
298       break;
299    }
300    return Res;
301 }
302
303 bool RPMDirHandler::Jump(unsigned Offset)
304 {
305    if (Dir == NULL)
306       return false;
307    rewinddir(Dir);
308    iOffset = 0;
309    while (1) {
310       if (iOffset+1 == Offset)
311          return Skip();
312       if (nextFileName() == NULL)
313          break;
314       iOffset++;
315    }
316    return false;
317 }
318
319 void RPMDirHandler::Rewind()
320 {
321    rewinddir(Dir);
322    iOffset = 0;
323 }
324
325 unsigned long RPMDirHandler::FileSize()
326 {
327    if (Dir == NULL)
328       return 0;
329    struct stat St;
330    if (stat(sFilePath.c_str(),&St) != 0) {
331       _error->Errno("stat",_("Unable to determine the file size"));
332       return 0;
333    }
334    return St.st_size;
335 }
336
337 string RPMDirHandler::MD5Sum()
338 {
339    if (Dir == NULL)
340       return "";
341    MD5Summation MD5;
342    FileFd File(sFilePath, FileFd::ReadOnly);
343    MD5.AddFD(File.Fd(), File.Size());
344    File.Close();
345    return MD5.Result().Value();
346 }
347
348
349 RPMDBHandler::RPMDBHandler(bool WriteLock)
350    : WriteLock(WriteLock), Handler(0)
351 {
352 #if RPM_VERSION >= 0x040000
353    RpmIter = NULL;
354 #endif
355    string Dir = _config->Find("RPM::RootDir");
356    
357    rpmReadConfigFiles(NULL, NULL);
358    ID = DataPath(false);
359
360    RPMPackageData::Singleton()->InitMinArchScore();
361
362    // Everytime we open a database for writing, it has its
363    // mtime changed, and kills our cache validity. As we never
364    // change any information in the database directly, we will
365    // restore the mtime and save our cache.
366    struct stat St;
367    stat(DataPath(false).c_str(), &St);
368    DbFileMtime = St.st_mtime;
369
370 #if RPM_VERSION >= 0x040100
371    Handler = rpmtsCreate();
372    rpmtsSetVSFlags(Handler, (rpmVSFlags_e)-1);
373    rpmtsSetRootDir(Handler, Dir.c_str());
374    if (rpmtsOpenDB(Handler, O_RDONLY) != 0)
375    {
376       _error->Error(_("could not open RPM database"));
377       return;
378    }
379 #else
380    const char *RootDir = NULL;
381    if (!Dir.empty())
382       RootDir = Dir.c_str();
383    if (rpmdbOpen(RootDir, &Handler, O_RDONLY, 0644) != 0)
384    {
385       _error->Error(_("could not open RPM database"));
386       return;
387    }
388 #endif
389 #if RPM_VERSION >= 0x040000
390    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
391    if (RpmIter == NULL) {
392       _error->Error(_("could not create RPM database iterator"));
393       return;
394    }
395    // iSize = rpmdbGetIteratorCount(RpmIter);
396    // This doesn't seem to work right now. Code in rpm (4.0.4, at least)
397    // returns a 0 from rpmdbGetIteratorCount() if rpmxxInitIterator() is
398    // called with RPMDBI_PACKAGES or with keyp == NULL. The algorithm
399    // below will be used until there's support for it.
400    iSize = 0;
401    rpmdbMatchIterator countIt;
402    countIt = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
403    while (rpmdbNextIterator(countIt) != NULL)
404       iSize++;
405    rpmdbFreeIterator(countIt);
406 #else
407    iSize = St.st_size;
408 #endif
409
410    // Restore just after opening the database, and just after closing.
411    if (WriteLock) {
412       struct utimbuf Ut;
413       Ut.actime = DbFileMtime;
414       Ut.modtime = DbFileMtime;
415       utime(DataPath(false).c_str(), &Ut);
416    }
417 }
418
419 RPMDBHandler::~RPMDBHandler()
420 {
421 #if RPM_VERSION >= 0x040000
422    if (RpmIter != NULL)
423       rpmdbFreeIterator(RpmIter);
424 #else
425    if (HeaderP != NULL)
426        headerFree(HeaderP);
427 #endif
428
429    if (Handler != NULL) {
430 #if RPM_VERSION >= 0x040100
431       rpmtsFree(Handler);
432 #else
433       rpmdbClose(Handler);
434 #endif
435    }
436
437    // Restore just after opening the database, and just after closing.
438    if (WriteLock) {
439       struct utimbuf Ut;
440       Ut.actime = DbFileMtime;
441       Ut.modtime = DbFileMtime;
442       utime(DataPath(false).c_str(), &Ut);
443    }
444 }
445
446 string RPMDBHandler::DataPath(bool DirectoryOnly)
447 {
448    string File = "packages.rpm";
449    char *tmp = (char *) rpmExpand("%{_dbpath}", NULL);
450    string DBPath(_config->Find("RPM::RootDir")+tmp);
451    free(tmp);
452
453 #if RPM_VERSION >= 0x040000
454    if (rpmExpandNumeric("%{_dbapi}") >= 3)
455       File = "Packages";       
456 #endif
457    if (DirectoryOnly == true)
458        return DBPath;
459    else
460        return DBPath+"/"+File;
461 }
462
463 bool RPMDBHandler::Skip()
464 {
465 #if RPM_VERSION >= 0x040000
466    if (RpmIter == NULL)
467        return false;
468    HeaderP = rpmdbNextIterator(RpmIter);
469    iOffset = rpmdbGetIteratorOffset(RpmIter);
470    if (HeaderP == NULL)
471       return false;
472 #else
473    if (iOffset == 0)
474       iOffset = rpmdbFirstRecNum(Handler);
475    else
476       iOffset = rpmdbNextRecNum(Handler, iOffset);
477    if (HeaderP != NULL)
478    {
479       headerFree(HeaderP);
480       HeaderP = NULL;
481    }
482    if (iOffset == 0)
483        return false;
484    HeaderP = rpmdbGetRecord(Handler, iOffset);
485 #endif
486    return true;
487 }
488
489 bool RPMDBHandler::Jump(unsigned int Offset)
490 {
491    iOffset = Offset;
492 #if RPM_VERSION >= 0x040000
493    if (RpmIter == NULL)
494       return false;
495    rpmdbFreeIterator(RpmIter);
496    if (iOffset == 0)
497       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
498    else
499       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES,
500                                   &iOffset, sizeof(iOffset));
501    HeaderP = rpmdbNextIterator(RpmIter);
502 #else
503    HeaderP = rpmdbGetRecord(Handler, iOffset);
504 #endif
505    return true;
506 }
507
508 void RPMDBHandler::Rewind()
509 {
510 #if RPM_VERSION >= 0x040000
511    if (RpmIter == NULL)
512       return;
513    rpmdbFreeIterator(RpmIter);   
514    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
515 #else
516    if (HeaderP != NULL)
517    {
518       headerFree(HeaderP);
519       HeaderP = NULL;
520    }
521 #endif
522    iOffset = 0;
523 }
524 #endif
525
526 // vim:sts=3:sw=3