3f9798c2db3df6721da5abf0a78686b444288081
[platform/upstream/libsolv.git] / ext / repo_rpmdb_librpm.h
1 /*
2  * Copyright (c) 2018, SUSE Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_rpmdb_librpm.h
10  *
11  * Use librpm to access the rpm database
12  *
13  */
14
15 #include <rpm/rpmlib.h>
16 #include <rpm/rpmts.h>
17 #include <rpm/rpmmacro.h>
18
19 struct rpmdbstate {
20   Pool *pool;
21   char *rootdir;
22
23   RpmHead *rpmhead;     /* header storage space */
24   unsigned int rpmheadsize;
25
26   int dbenvopened;      /* database environment opened */
27   const char *dbpath;   /* path to the database */
28   int dbpath_allocated; /* do we need to free the path? */
29
30   rpmts ts;
31   rpmdbMatchIterator mi;        /* iterator over packages database */
32 };
33
34 static inline int
35 access_rootdir(struct rpmdbstate *state, const char *dir, int mode)
36 {
37   if (state->rootdir)
38     {
39       char *path = solv_dupjoin(state->rootdir, dir, 0);
40       int r = access(path, mode);
41       free(path);
42       return r;
43     }
44   return access(dir, mode);
45 }
46
47 static void
48 detect_dbpath(struct rpmdbstate *state)
49 {
50   state->dbpath = rpmExpand("%{?_dbpath}", NULL);
51   if (state->dbpath && *state->dbpath)
52     {
53       state->dbpath_allocated = 1;
54       return;
55     }
56   solv_free((char *)state->dbpath);
57   state->dbpath = access_rootdir(state, "/var/lib/rpm", W_OK) == -1
58                   && (access_rootdir(state, "/usr/share/rpm/Packages", R_OK) == 0 || access_rootdir(state, "/usr/share/rpm/rpmdb.sqlite", R_OK) == 0)
59                   ? "/usr/share/rpm" : "/var/lib/rpm";
60 }
61
62 static int
63 stat_database(struct rpmdbstate *state, struct stat *statbuf)
64 {
65   static const char *dbname[] = {
66     "/Packages",
67     "/Packages.db",
68     "/rpmdb.sqlite",
69     "/data.mdb",
70     "/Packages",                /* for error reporting */
71     0,
72   };
73   int i;
74
75 #ifdef HAVE_RPMDBFSTAT
76   if (state->dbenvopened == 1)
77     return rpmdbFStat(rpmtsGetRdb(state->ts), statbuf);
78 #endif
79   if (!state->dbpath)
80     detect_dbpath(state);
81   for (i = 0; ; i++)
82     {
83       char *dbpath = solv_dupjoin(state->rootdir, state->dbpath, dbname[i]);
84       if (!stat(dbpath, statbuf))
85         {
86           free(dbpath);
87           return 0;
88         }
89       if (errno != ENOENT || !dbname[i + 1])
90         {
91           int saved_errno = errno;
92           pool_error(state->pool, -1, "%s: %s", dbpath, strerror(errno));
93           solv_free(dbpath);
94           errno = saved_errno;
95           return -1;
96         }
97       solv_free(dbpath);
98     }
99   return 0;
100 }
101
102 /* rpm-4.16.0 cannot read the database if _db_backend is not set */
103 #ifndef HAVE_RPMDBNEXTITERATORHEADERBLOB
104 static void
105 set_db_backend()
106 {
107   static int db_backend_set;
108   char *db_backend;
109
110   if (db_backend_set)
111     return;
112   db_backend_set = 1;
113   db_backend = rpmExpand("%{?_db_backend}", NULL);
114   if (!db_backend || !*db_backend)
115     rpmReadConfigFiles(NULL, NULL);
116   solv_free(db_backend);
117 }
118 #endif
119
120 static int
121 opendbenv(struct rpmdbstate *state)
122 {
123   rpmts ts;
124   char *dbpath;
125
126   if (!state->dbpath)
127     detect_dbpath(state);
128   dbpath = solv_dupjoin("_dbpath ", state->rootdir, state->dbpath);
129   rpmDefineMacro(NULL, dbpath, 0);
130   solv_free(dbpath);
131   ts = rpmtsCreate();
132   if (!ts)
133     {
134       pool_error(state->pool, 0, "rpmtsCreate failed");
135       delMacro(NULL, "_dbpath");
136       return 0;
137     }
138 #ifndef HAVE_RPMDBNEXTITERATORHEADERBLOB
139   if (!strcmp(RPMVERSION, "4.16.0"))
140     set_db_backend();
141 #endif
142   if (rpmtsOpenDB(ts, O_RDONLY))
143     {
144       pool_error(state->pool, 0, "rpmtsOpenDB failed: %s", strerror(errno));
145       rpmtsFree(ts);
146       delMacro(NULL, "_dbpath");
147       return 0;
148     }
149   delMacro(NULL, "_dbpath");
150   rpmtsSetVSFlags(ts, _RPMVSF_NODIGESTS | _RPMVSF_NOSIGNATURES | _RPMVSF_NOHEADER);
151   state->ts = ts;
152   state->dbenvopened = 1;
153   return 1;
154 }
155
156 static void
157 closedbenv(struct rpmdbstate *state)
158 {
159   if (state->ts)
160     rpmtsFree(state->ts);
161   state->ts = 0;
162   state->dbenvopened = 0;
163 }
164
165 /* get the rpmdbids of all installed packages from the Name index database.
166  * This is much faster then querying the big Packages database */
167 static struct rpmdbentry *
168 getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap, int keep_gpg_pubkey)
169 {
170   const void * key;
171   size_t keylen, matchl = 0;
172   Id nameoff;
173
174   char *namedata = 0;
175   int namedatal = 0;
176   struct rpmdbentry *entries = 0;
177   int nentries = 0;
178
179   rpmdbIndexIterator ii;
180
181   *nentriesp = 0;
182   if (namedatap)
183     *namedatap = 0;
184
185   if (state->dbenvopened != 1 && !opendbenv(state))
186     return 0;
187
188   if (match)
189     matchl = strlen(match);
190   ii = rpmdbIndexIteratorInit(rpmtsGetRdb(state->ts), RPMDBI_NAME);
191
192   while (rpmdbIndexIteratorNext(ii, &key, &keylen) == 0)
193     {
194       unsigned int i, npkgs;
195       if (match)
196         {
197           if (keylen != matchl || memcmp(key, match, keylen) != 0)
198             continue;
199         }
200       else if (!keep_gpg_pubkey && keylen == 10 && !memcmp(key, "gpg-pubkey", 10))
201         continue;
202       nameoff = namedatal;
203       if (namedatap)
204        {
205          namedata = solv_extend(namedata, namedatal, keylen + 1, 1, NAMEDATA_BLOCK);
206          memcpy(namedata + namedatal, key, keylen);
207          namedata[namedatal + keylen] = 0;
208          namedatal += keylen + 1;
209        }
210       npkgs = rpmdbIndexIteratorNumPkgs(ii);
211       for (i = 0; i < npkgs; i++)
212        {
213          entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
214          entries[nentries].rpmdbid = rpmdbIndexIteratorPkgOffset(ii, i);
215          entries[nentries].nameoff = nameoff;
216          nentries++;
217        }
218     }
219   rpmdbIndexIteratorFree(ii);
220   /* make sure that enteries is != 0 if there was no error */
221   if (!entries)
222     entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
223   *nentriesp = nentries;
224   if (namedatap)
225     *namedatap = namedata;
226   return entries;
227 }
228
229 #if defined(HAVE_RPMDBNEXTITERATORHEADERBLOB) && !defined(ENABLE_RPMPKG_LIBRPM)
230 static int headfromhdrblob(struct rpmdbstate *state, const unsigned char *data, unsigned int size);
231 #endif
232
233 /* retrive header by rpmdbid, returns 0 if not found, -1 on error */
234 static int
235 getrpm_dbid(struct rpmdbstate *state, Id rpmdbid)
236 {
237 #if defined(HAVE_RPMDBNEXTITERATORHEADERBLOB) && !defined(ENABLE_RPMPKG_LIBRPM)
238   const unsigned char *uh;
239   unsigned int uhlen;
240 #else
241   Header h;
242 #endif
243   rpmdbMatchIterator mi;
244   unsigned int offset = rpmdbid;
245
246   if (rpmdbid <= 0)
247     return pool_error(state->pool, -1, "illegal rpmdbid %d", rpmdbid);
248   if (state->dbenvopened != 1 && !opendbenv(state))
249     return -1;
250   mi = rpmdbInitIterator(rpmtsGetRdb(state->ts), RPMDBI_PACKAGES, &offset, sizeof(offset));
251 #if defined(HAVE_RPMDBNEXTITERATORHEADERBLOB) && !defined(ENABLE_RPMPKG_LIBRPM)
252   uh = rpmdbNextIteratorHeaderBlob(mi, &uhlen);
253   if (!uh)
254     {
255       rpmdbFreeIterator(mi);
256       return 0;
257     }
258   if (!headfromhdrblob(state, uh, uhlen))
259     {
260       rpmdbFreeIterator(mi);
261       return -1;
262     }
263 #else
264   h = rpmdbNextIterator(mi);
265   if (!h)
266     {
267       rpmdbFreeIterator(mi);
268       return 0;
269     }
270   if (!rpm_byrpmh(state, h))
271     {
272       rpmdbFreeIterator(mi);
273       return -1;
274     }
275 #endif
276   mi = rpmdbFreeIterator(mi);
277   return rpmdbid;
278 }
279
280 static int
281 count_headers(struct rpmdbstate *state)
282 {
283   int count;
284   rpmdbMatchIterator mi;
285
286   if (state->dbenvopened != 1 && !opendbenv(state))
287     return 0;
288   mi = rpmdbInitIterator(rpmtsGetRdb(state->ts), RPMDBI_NAME, NULL, 0);
289   count = rpmdbGetIteratorCount(mi);
290   rpmdbFreeIterator(mi);
291   return count;
292 }
293
294 static int
295 pkgdb_cursor_open(struct rpmdbstate *state)
296 {
297   state->mi = rpmdbInitIterator(rpmtsGetRdb(state->ts), RPMDBI_PACKAGES, NULL, 0);
298   return 0;
299 }
300
301 static void
302 pkgdb_cursor_close(struct rpmdbstate *state)
303 {
304   rpmdbFreeIterator(state->mi);
305   state->mi = 0;
306 }
307
308 static Id
309 pkgdb_cursor_getrpm(struct rpmdbstate *state)
310 {
311 #if defined(HAVE_RPMDBNEXTITERATORHEADERBLOB) && !defined(ENABLE_RPMPKG_LIBRPM)
312   const unsigned char *uh;
313   unsigned int uhlen;
314   while ((uh = rpmdbNextIteratorHeaderBlob(state->mi, &uhlen)) != 0)
315     {
316       Id dbid = rpmdbGetIteratorOffset(state->mi);
317       if (!headfromhdrblob(state, uh, uhlen))
318         continue;
319       return dbid;
320     }
321 #else
322   Header h;
323   while ((h = rpmdbNextIterator(state->mi)))
324     {
325       Id dbid = rpmdbGetIteratorOffset(state->mi);
326       if (!rpm_byrpmh(state, h))
327         continue;
328       return dbid;
329     }
330 #endif
331   return 0;
332 }
333
334 static int
335 hash_name_index(struct rpmdbstate *state, Chksum *chk)
336 {
337   rpmdbIndexIterator ii;
338   const void *key;
339   size_t keylen;
340
341   if (state->dbenvopened != 1 && !opendbenv(state))
342     return -1;
343   ii = rpmdbIndexIteratorInit(rpmtsGetRdb(state->ts), RPMDBI_NAME);
344   if (!ii)
345     return -1;
346   while (rpmdbIndexIteratorNext(ii, &key, &keylen) == 0)
347     {
348       unsigned int i, npkgs = rpmdbIndexIteratorNumPkgs(ii);
349       solv_chksum_add(chk, key, (int)keylen);
350       for (i = 0; i < npkgs; i++)
351         {
352           unsigned int offset = rpmdbIndexIteratorPkgOffset(ii, i);
353           solv_chksum_add(chk, &offset, sizeof(offset));
354         }
355     }
356   rpmdbIndexIteratorFree(ii);
357   return 0;
358 }
359