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