]> git.ipfire.org Git - pakfire.git/blob - src/libpakfire/repo.c
libpakfire: Automatically determine the repository priority
[pakfire.git] / src / libpakfire / repo.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2013 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <assert.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include <solv/repo.h>
27 #include <solv/repo_solv.h>
28 #include <solv/repo_write.h>
29
30 #include <lzma.h>
31
32 #include <pakfire/constants.h>
33 #include <pakfire/errno.h>
34 #include <pakfire/logging.h>
35 #include <pakfire/package.h>
36 #include <pakfire/pakfire.h>
37 #include <pakfire/private.h>
38 #include <pakfire/repo.h>
39 #include <pakfire/types.h>
40 #include <pakfire/util.h>
41
42 const uint8_t XZ_HEADER_MAGIC[] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 };
43 const size_t XZ_HEADER_LENGTH = sizeof(XZ_HEADER_MAGIC);
44
45 struct pakfire_repo_appdata {
46 Repodata* repodata;
47
48 char* baseurl;
49 };
50
51 struct _PakfireRepo {
52 Pakfire pakfire;
53 Repo* repo;
54 struct pakfire_repo_appdata* appdata;
55 int nrefs;
56 };
57
58 static void free_repo_appdata(struct pakfire_repo_appdata* appdata) {
59 // repodata is being destroyed with the repository
60
61 if (appdata->baseurl)
62 pakfire_free(appdata->baseurl);
63
64 pakfire_free(appdata);
65 }
66
67 void pakfire_repo_free_all(Pakfire pakfire) {
68 Pool* pool = pakfire_get_solv_pool(pakfire);
69
70 Repo* repo;
71 int i;
72
73 FOR_REPOS(i, repo) {
74 free_repo_appdata(repo->appdata);
75 repo_free(repo, 0);
76 }
77 }
78
79 PAKFIRE_EXPORT PakfireRepo pakfire_repo_create(Pakfire pakfire, const char* name) {
80 PakfireRepo repo = pakfire_calloc(1, sizeof(*repo));
81 if (repo) {
82 DEBUG("Allocated Repo at %p\n", repo);
83 repo->nrefs = 1;
84
85 repo->pakfire = pakfire_ref(pakfire);
86
87 // Allocate a libsolv repository
88 Pool* pool = pakfire_get_solv_pool(pakfire);
89 repo->repo = repo_create(pool, name);
90
91 // Allocate repository appdata
92 repo->appdata = repo->repo->appdata = \
93 calloc(1, sizeof(*repo->appdata));
94
95 repo->appdata->repodata = repo_add_repodata(repo->repo,
96 REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL|REPO_NO_INTERNALIZE|REPO_NO_LOCATION);
97 }
98
99 return repo;
100 }
101
102 PakfireRepo pakfire_repo_create_from_repo(Pakfire pakfire, Repo* r) {
103 PakfireRepo repo = r->appdata = pakfire_calloc(1, sizeof(*repo));
104 if (repo) {
105 DEBUG("Allocated Repo at %p\n", repo);
106 repo->nrefs = 1;
107
108 repo->pakfire = pakfire_ref(pakfire);
109
110 // Reference repository
111 repo->repo = r;
112 repo->appdata = r->appdata;
113 }
114
115 return repo;
116 }
117
118 PAKFIRE_EXPORT PakfireRepo pakfire_repo_ref(PakfireRepo repo) {
119 repo->nrefs++;
120
121 return repo;
122 }
123
124 static void pakfire_repo_free(PakfireRepo repo) {
125 pakfire_unref(repo->pakfire);
126
127 pakfire_free(repo);
128 DEBUG("Released Repo at %p\n", repo);
129 }
130
131 PAKFIRE_EXPORT PakfireRepo pakfire_repo_unref(PakfireRepo repo) {
132 if (!repo)
133 return NULL;
134
135 if (--repo->nrefs > 0)
136 return repo;
137
138 pakfire_repo_free(repo);
139 return NULL;
140 }
141
142 PAKFIRE_EXPORT Pakfire pakfire_repo_get_pakfire(PakfireRepo repo) {
143 return pakfire_ref(repo->pakfire);
144 }
145
146 Repo* pakfire_repo_get_repo(PakfireRepo repo) {
147 return repo->repo;
148 }
149
150 Repodata* pakfire_repo_get_repodata(PakfireRepo repo) {
151 return repo->appdata->repodata;
152 }
153
154 PAKFIRE_EXPORT int pakfire_repo_identical(PakfireRepo repo1, PakfireRepo repo2) {
155 Repo* r1 = repo1->repo;
156 Repo* r2 = repo2->repo;
157
158 return strcmp(r1->name, r2->name);
159 }
160
161 PAKFIRE_EXPORT int pakfire_repo_cmp(PakfireRepo repo1, PakfireRepo repo2) {
162 Repo* r1 = repo1->repo;
163 Repo* r2 = repo2->repo;
164
165 if (r1->priority > r2->priority)
166 return 1;
167
168 else if (r1->priority < r2->priority)
169 return -1;
170
171 return strcmp(r1->name, r2->name);
172 }
173
174 PAKFIRE_EXPORT int pakfire_repo_count(PakfireRepo repo) {
175 Pool* pool = pakfire_get_solv_pool(repo->pakfire);
176 int cnt = 0;
177
178 for (int i = 2; i < pool->nsolvables; i++) {
179 Solvable* s = pool->solvables + i;
180 if (s->repo && s->repo == repo->repo)
181 cnt++;
182 }
183
184 return cnt;
185 }
186
187 PAKFIRE_EXPORT void pakfire_repo_internalize(PakfireRepo repo) {
188 repo_internalize(repo->repo);
189 }
190
191 PAKFIRE_EXPORT const char* pakfire_repo_get_name(PakfireRepo repo) {
192 return repo->repo->name;
193 }
194
195 PAKFIRE_EXPORT void pakfire_repo_set_name(PakfireRepo repo, const char* name) {
196 repo->repo->name = pakfire_strdup(name);
197 }
198
199 PAKFIRE_EXPORT int pakfire_repo_get_enabled(PakfireRepo repo) {
200 return !repo->repo->disabled;
201 }
202
203 PAKFIRE_EXPORT void pakfire_repo_set_enabled(PakfireRepo repo, int enabled) {
204 repo->repo->disabled = !enabled;
205
206 pakfire_pool_has_changed(repo->pakfire);
207 }
208
209 // Returns a default priority based on the repository configuration
210 static int pakfire_repo_auto_priority(PakfireRepo repo) {
211 // The @system repository has a priority of zero
212 if (pakfire_repo_is_installed_repo(repo) == 0)
213 return 0;
214
215 if (repo->appdata->baseurl) {
216 // HTTPS
217 if (pakfire_string_startswith(repo->appdata->baseurl, "https://"))
218 return 75;
219
220 // HTTP
221 if (pakfire_string_startswith(repo->appdata->baseurl, "http://"))
222 return 75;
223
224 // Local path
225 if (pakfire_string_startswith(repo->appdata->baseurl, "dir://"))
226 return 50;
227 }
228
229 // Default to 100
230 return 100;
231 }
232
233 PAKFIRE_EXPORT int pakfire_repo_get_priority(PakfireRepo repo) {
234 if (repo->repo->priority > 0)
235 return repo->repo->priority;
236
237 return pakfire_repo_auto_priority(repo);
238 }
239
240 PAKFIRE_EXPORT void pakfire_repo_set_priority(PakfireRepo repo, int priority) {
241 repo->repo->priority = priority;
242 }
243
244 PAKFIRE_EXPORT const char* pakfire_repo_get_baseurl(PakfireRepo repo) {
245 return repo->appdata->baseurl;
246 }
247
248 PAKFIRE_EXPORT int pakfire_repo_set_baseurl(PakfireRepo repo, const char* baseurl) {
249 if (repo->appdata->baseurl)
250 pakfire_free(repo->appdata->baseurl);
251
252 repo->appdata->baseurl = pakfire_strdup(baseurl);
253 return 0;
254 }
255
256 PAKFIRE_EXPORT int pakfire_repo_is_installed_repo(PakfireRepo repo) {
257 PakfireRepo installed_repo = pakfire_get_installed_repo(repo->pakfire);
258
259 int r = pakfire_repo_identical(repo, installed_repo);
260
261 pakfire_repo_unref(installed_repo);
262
263 return r;
264 }
265
266 PAKFIRE_EXPORT int pakfire_repo_read_solv(PakfireRepo repo, const char* filename, int flags) {
267 FILE* f = fopen(filename, "rb");
268 if (!f) {
269 return PAKFIRE_E_IO;
270 }
271
272 int ret = pakfire_repo_read_solv_fp(repo, f, flags);
273 fclose(f);
274
275 return ret;
276 }
277
278 struct xz_cookie {
279 FILE* f;
280 lzma_stream stream;
281 int done;
282
283 // XXX This should actually be larger than one byte, but fread()
284 // in _xz_read() somehow segfaults when this is larger
285 uint8_t buffer[1];
286 };
287
288 static ssize_t _xz_read(void* data, char* buffer, size_t size) {
289 struct xz_cookie* cookie = (struct xz_cookie*)data;
290 if (!cookie)
291 return -1;
292
293 // Return nothing after we are done
294 if (cookie->done)
295 return 0;
296
297 lzma_action action = LZMA_RUN;
298
299 // Set output to allocated buffer
300 cookie->stream.next_out = (uint8_t *)buffer;
301 cookie->stream.avail_out = size;
302
303 while (1) {
304 // Read something when the input buffer is empty
305 if (cookie->stream.avail_in == 0) {
306 cookie->stream.next_in = cookie->buffer;
307 cookie->stream.avail_in = fread(cookie->buffer,
308 1, sizeof(cookie->buffer), cookie->f);
309
310 // Break if the input file could not be read
311 if (ferror(cookie->f))
312 return -1;
313
314 // Finish after we have reached the end of the input file
315 if (feof(cookie->f)) {
316 action = LZMA_FINISH;
317 cookie->done = 1;
318 }
319 }
320
321 lzma_ret ret = lzma_code(&cookie->stream, action);
322
323 // If the stream has ended, we just send the
324 // remaining output and mark that we are done.
325 if (ret == LZMA_STREAM_END) {
326 cookie->done = 1;
327 return size - cookie->stream.avail_out;
328 }
329
330 // Break on all other unexpected errors
331 if (ret != LZMA_OK)
332 return -1;
333
334 // When we have read enough to fill the entire output buffer, we return
335 if (cookie->stream.avail_out == 0)
336 return size;
337
338 if (cookie->done)
339 return -1;
340 }
341 }
342
343 static int _xz_close(void* data) {
344 struct xz_cookie* cookie = (struct xz_cookie*)data;
345
346 // Free the deocder
347 lzma_end(&cookie->stream);
348
349 // Close input file
350 fclose(cookie->f);
351
352 return 0;
353 }
354
355 static FILE* decompression_proxy(FILE* f) {
356 uint8_t buffer;
357
358 // Search for XZ header
359 for (unsigned int i = 0; i < XZ_HEADER_LENGTH; i++) {
360 fread(&buffer, 1, 1, f);
361
362 if (buffer != XZ_HEADER_MAGIC[i])
363 goto UNCOMPRESSED;
364 }
365
366 // Reset to beginning
367 fseek(f, 0, SEEK_SET);
368
369 // If we get here, an XZ header was found
370 struct xz_cookie cookie = {
371 .f = f,
372 .stream = LZMA_STREAM_INIT,
373 .done = 0,
374 };
375
376 // Initialise the decoder
377 lzma_ret ret = lzma_stream_decoder(&cookie.stream, UINT64_MAX, 0);
378 if (ret != LZMA_OK)
379 return NULL;
380
381 cookie_io_functions_t functions = {
382 .read = _xz_read,
383 .write = NULL,
384 .seek = NULL,
385 .close = _xz_close,
386 };
387
388 return fopencookie(&cookie, "rb", functions);
389
390 UNCOMPRESSED:
391 fseek(f, 0, SEEK_SET);
392 return f;
393 }
394
395 PAKFIRE_EXPORT int pakfire_repo_read_solv_fp(PakfireRepo repo, FILE *f, int flags) {
396 f = decompression_proxy(f);
397
398 int ret = repo_add_solv(repo->repo, f, flags);
399
400 switch (ret) {
401 // Everything OK
402 case 0:
403 break;
404
405 // Not SOLV format
406 case 1:
407 return PAKFIRE_E_SOLV_NOT_SOLV;
408
409 // Unsupported version
410 case 2:
411 return PAKFIRE_E_SOLV_UNSUPPORTED;
412
413 // End of file
414 case 3:
415 return PAKFIRE_E_EOF;
416
417 // Corrupted
418 case 4:
419 case 5:
420 case 6:
421 default:
422 return PAKFIRE_E_SOLV_CORRUPTED;
423 }
424
425 pakfire_pool_has_changed(repo->pakfire);
426
427 return ret;
428 }
429
430 PAKFIRE_EXPORT int pakfire_repo_write_solv(PakfireRepo repo, const char* filename, int flags) {
431 FILE* f = fopen(filename, "wb");
432 if (!f) {
433 return PAKFIRE_E_IO;
434 }
435
436 int ret = pakfire_repo_write_solv_fp(repo, f, flags);
437 fclose(f);
438
439 return ret;
440 }
441
442 PAKFIRE_EXPORT int pakfire_repo_write_solv_fp(PakfireRepo repo, FILE *f, int flags) {
443 pakfire_repo_internalize(repo);
444
445 return repo_write(repo->repo, f);
446 }
447
448 PAKFIRE_EXPORT PakfirePackage pakfire_repo_add_package(PakfireRepo repo) {
449 Id id = repo_add_solvable(repo->repo);
450
451 return pakfire_package_create(repo->pakfire, id);
452 }
453
454 // Cache
455
456 static char* pakfire_repo_get_cache_prefix(PakfireRepo repo) {
457 char* prefix = pakfire_calloc(1, STRING_SIZE + 1);
458
459 snprintf(prefix, STRING_SIZE, "repodata/%s", pakfire_repo_get_name(repo));
460
461 return prefix;
462 }
463
464 static char* pakfire_repo_make_cache_path(PakfireRepo repo, const char* path) {
465 char* prefix = pakfire_repo_get_cache_prefix(repo);
466
467 // Add the prefix for the repository first
468 char* cache_path = pakfire_path_join(prefix, path);
469 pakfire_free(prefix);
470
471 return cache_path;
472 }
473
474 PAKFIRE_EXPORT int pakfire_repo_clean(PakfireRepo repo) {
475 char* cache_path = pakfire_repo_make_cache_path(repo, NULL);
476
477 if (cache_path)
478 return pakfire_cache_destroy(repo->pakfire, cache_path);
479
480 return -1;
481 }
482
483 PAKFIRE_EXPORT char* pakfire_repo_cache_get_path(PakfireRepo repo, const char* path) {
484 char* repo_cache_path = pakfire_repo_make_cache_path(repo, path);
485
486 char* cache_path = pakfire_get_cache_path(repo->pakfire, repo_cache_path);
487 pakfire_free(repo_cache_path);
488
489 return cache_path;
490 }
491
492 PAKFIRE_EXPORT FILE* pakfire_repo_cache_open(PakfireRepo repo, const char* path, const char* mode) {
493 char* cache_path = pakfire_repo_make_cache_path(repo, path);
494
495 FILE* f = pakfire_cache_open(repo->pakfire, cache_path, mode);
496 pakfire_free(cache_path);
497
498 return f;
499 }
500
501 PAKFIRE_EXPORT int pakfire_repo_cache_access(PakfireRepo repo, const char* path, int mode) {
502 char* cache_path = pakfire_repo_make_cache_path(repo, path);
503
504 int r = pakfire_cache_access(repo->pakfire, cache_path, mode);
505 pakfire_free(cache_path);
506
507 return r;
508 }
509
510 PAKFIRE_EXPORT time_t pakfire_repo_cache_age(PakfireRepo repo, const char* path) {
511 char* cache_path = pakfire_repo_make_cache_path(repo, path);
512
513 time_t t = pakfire_cache_age(repo->pakfire, cache_path);
514 pakfire_free(cache_path);
515
516 return t;
517 }