]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/file.c
string: Refactor pakfire_string_format to be more robust
[people/stevee/pakfire.git] / src / libpakfire / file.c
CommitLineData
221cc3ce
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2014 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
5e9463ec 21#include <errno.h>
3fca5032 22#include <libgen.h>
c98132d1 23#include <linux/limits.h>
221cc3ce
MT
24#include <stdlib.h>
25#include <string.h>
221cc3ce 26#include <sys/types.h>
221cc3ce
MT
27#include <time.h>
28
9a6e3e2d 29#include <archive_entry.h>
5e8dfbeb 30#include <openssl/evp.h>
9a6e3e2d 31
221cc3ce
MT
32#include <pakfire/constants.h>
33#include <pakfire/file.h>
3fca5032 34#include <pakfire/logging.h>
883b3be9 35#include <pakfire/pakfire.h>
9f953e68 36#include <pakfire/private.h>
d973a13d 37#include <pakfire/string.h>
221cc3ce
MT
38#include <pakfire/util.h>
39
65131b30
MT
40#define MAX_DIGESTS 4
41
42struct pakfire_file_digest {
43 enum pakfire_digests type;
44 unsigned char digest[EVP_MAX_MD_SIZE];
45 size_t length;
46
47 // Add a buffer to store the hex representation
48 char* hexdigest;
49};
50
5803b5f6 51struct pakfire_file {
ac4c607b 52 struct pakfire* pakfire;
5e9463ec 53 int nrefs;
221cc3ce 54
3b9e3970 55 char path[PATH_MAX];
0027da6f 56 char abspath[PATH_MAX];
5e9463ec 57 ssize_t size;
221cc3ce 58
c98132d1
MT
59 char user[256];
60 char group[256];
221cc3ce 61
5e9463ec 62 mode_t mode;
487d6485 63 dev_t dev;
ef4e8460
MT
64
65 time_t ctime;
66 time_t mtime;
d03fa9a3 67
59d8c727
MT
68 // Link destinations
69 char hardlink[PATH_MAX];
70 char symlink[PATH_MAX];
71
65131b30
MT
72 // Digests
73 struct pakfire_file_digest digests[MAX_DIGESTS];
5e9463ec
MT
74
75 #warning TODO capabilities, config, data
76 // capabilities
77 //int is_configfile;
78 //int is_datafile;
79};
80
ac4c607b 81PAKFIRE_EXPORT int pakfire_file_create(struct pakfire_file** file, struct pakfire* pakfire) {
5803b5f6 82 struct pakfire_file* f = calloc(1, sizeof(*f));
5e9463ec 83 if (!f)
7403779a 84 return 1;
5e9463ec 85
883b3be9 86 f->pakfire = pakfire_ref(pakfire);
5e9463ec
MT
87 f->nrefs = 1;
88
89 *file = f;
90 return 0;
d03fa9a3
MT
91}
92
ac4c607b 93int pakfire_file_create_from_archive_entry(struct pakfire_file** file, struct pakfire* pakfire,
d0985e31
MT
94 struct archive_entry* entry) {
95 int r = pakfire_file_create(file, pakfire);
96 if (r)
97 return r;
98
99 // Copy archive entry
100 r = pakfire_file_copy_archive_entry(*file, entry);
101 if (r)
102 goto ERROR;
103
104 return 0;
105
106ERROR:
107 pakfire_file_unref(*file);
108 *file = NULL;
109
110 return r;
111}
112
aa8ea50c
MT
113static const struct pakfire_libarchive_digest {
114 enum pakfire_digests pakfire;
115 int libarchive;
116} pakfire_libarchive_digests[] = {
117 { PAKFIRE_DIGEST_SHA512, ARCHIVE_ENTRY_DIGEST_SHA512 },
118 { PAKFIRE_DIGEST_SHA256, ARCHIVE_ENTRY_DIGEST_SHA256 },
119 { PAKFIRE_DIGEST_SHA1, ARCHIVE_ENTRY_DIGEST_SHA1 },
120 { PAKFIRE_DIGEST_NONE, 0 },
121};
122
5803b5f6 123int pakfire_file_copy_archive_entry(struct pakfire_file* file, struct archive_entry* entry) {
aa8ea50c
MT
124 int r = 0;
125
3b9e3970
MT
126 // Set abspath
127 pakfire_file_set_abspath(file, archive_entry_sourcepath(entry));
128
32485f6c 129 // Set path
b9f703e5
MT
130 const char* path = archive_entry_pathname(entry);
131 if (path) {
132 // Strip any leading dots from paths
133 if (pakfire_string_startswith(path, "./"))
134 path++;
135
136 pakfire_file_set_path(file, path);
137 }
9a6e3e2d 138
59d8c727
MT
139 // Set links
140 pakfire_file_set_hardlink(file, archive_entry_hardlink(entry));
141 pakfire_file_set_symlink(file, archive_entry_symlink(entry));
142
9a6e3e2d
MT
143 // Set size
144 pakfire_file_set_size(file, archive_entry_size(entry));
145
146 // Set mode
147 pakfire_file_set_mode(file, archive_entry_mode(entry));
148
487d6485
MT
149 // Set dev type
150 if (archive_entry_dev_is_set(entry))
151 pakfire_file_set_dev(file, archive_entry_dev(entry));
152
9a6e3e2d
MT
153 // Set user
154 pakfire_file_set_user(file, archive_entry_uname(entry));
155
156 // Set group
157 pakfire_file_set_group(file, archive_entry_gname(entry));
158
ef4e8460
MT
159 // Set times
160 pakfire_file_set_ctime(file, archive_entry_ctime(entry));
161 pakfire_file_set_mtime(file, archive_entry_mtime(entry));
9a6e3e2d 162
aa8ea50c
MT
163 // Copy digest
164 for (const struct pakfire_libarchive_digest* type = pakfire_libarchive_digests;
165 type->pakfire; type++) {
166 const unsigned char* digest = archive_entry_digest(entry, type->libarchive);
167 if (digest) {
65131b30
MT
168 size_t length = pakfire_digest_length(type->pakfire);
169
170 r = pakfire_file_set_digest(file, type->pakfire, digest, length);
aa8ea50c
MT
171 if (r)
172 return r;
aa8ea50c
MT
173 }
174 }
175
176 return r;
9a6e3e2d
MT
177}
178
5acd852e
MT
179struct archive_entry* pakfire_file_archive_entry(struct pakfire_file* file) {
180 struct archive_entry* entry = archive_entry_new();
181 if (!entry) {
182 ERROR(file->pakfire, "Could not allocate archive entry: %m\n");
183 return NULL;
184 }
185
186 // Set path
187 archive_entry_copy_pathname(entry, pakfire_file_get_path(file));
188
189 // Set source path
190 archive_entry_copy_sourcepath(entry, file->abspath);
191
192 // Set links
193 if (*file->hardlink)
194 archive_entry_set_hardlink(entry, file->hardlink);
195 if (*file->symlink)
196 archive_entry_set_symlink(entry, file->symlink);
197
198 // Set size
199 archive_entry_set_size(entry, pakfire_file_get_size(file));
200
201 // Set mode
202 archive_entry_set_mode(entry, pakfire_file_get_mode(file));
203
204 // Set user
205 archive_entry_set_uname(entry, pakfire_file_get_user(file));
206
207 // Set group
208 archive_entry_set_gname(entry, pakfire_file_get_group(file));
209
210 // Set times
211 archive_entry_set_ctime(entry, pakfire_file_get_ctime(file), 0);
212 archive_entry_set_mtime(entry, pakfire_file_get_mtime(file), 0);
213
214 // XXX copy digest
215
216 return entry;
217}
218
5803b5f6 219static void pakfire_file_free(struct pakfire_file* file) {
65131b30
MT
220 struct pakfire_file_digest* digest = NULL;
221
222 // Free any generated hexdigests
223 for (unsigned int i = 0; i < MAX_DIGESTS; i++) {
224 digest = &file->digests[i];
225
baf8b0ed 226 if (digest->hexdigest) {
65131b30 227 free(digest->hexdigest);
baf8b0ed
MT
228 digest->hexdigest = NULL;
229 }
65131b30 230 }
221cc3ce 231
5e8dfbeb 232 pakfire_unref(file->pakfire);
f0d6233d 233 free(file);
221cc3ce
MT
234}
235
5803b5f6 236PAKFIRE_EXPORT struct pakfire_file* pakfire_file_ref(struct pakfire_file* file) {
5e9463ec 237 file->nrefs++;
221cc3ce 238
5e9463ec
MT
239 return file;
240}
221cc3ce 241
5803b5f6 242PAKFIRE_EXPORT struct pakfire_file* pakfire_file_unref(struct pakfire_file* file) {
5e9463ec
MT
243 if (--file->nrefs > 0)
244 return file;
245
246 pakfire_file_free(file);
247 return NULL;
221cc3ce
MT
248}
249
5803b5f6 250PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) {
32485f6c
MT
251 const char* path1 = pakfire_file_get_path(file1);
252 const char* path2 = pakfire_file_get_path(file2);
221cc3ce 253
32485f6c 254 return strcmp(path1, path2);
221cc3ce
MT
255}
256
5803b5f6 257const char* pakfire_file_get_abspath(struct pakfire_file* file) {
e4c2f7a9
MT
258 return file->abspath;
259}
260
5803b5f6 261int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) {
e7917fb3 262 return pakfire_string_set(file->abspath, path);
3b9e3970
MT
263}
264
5803b5f6 265PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) {
32485f6c 266 return file->path;
221cc3ce
MT
267}
268
5803b5f6 269PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) {
e7917fb3 270 return pakfire_string_set(file->path, path);
221cc3ce
MT
271}
272
59d8c727
MT
273PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) {
274 if (!*file->hardlink)
275 return NULL;
276
277 return file->hardlink;
278}
279
280PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) {
281 if (!link || !*link)
282 *file->hardlink = '\0';
283 else
284 pakfire_string_set(file->hardlink, link);
285}
286
287PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) {
288 if (!*file->symlink)
289 return NULL;
290
291 return file->symlink;
292}
293
294PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) {
295 if (!link || !*link)
296 *file->hardlink = '\0';
297 else
298 pakfire_string_set(file->symlink, link);
299}
300
5803b5f6 301PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) {
f9770f19 302 return file->mode & S_IFMT;
221cc3ce
MT
303}
304
5803b5f6 305PAKFIRE_EXPORT ssize_t pakfire_file_get_size(struct pakfire_file* file) {
221cc3ce
MT
306 return file->size;
307}
308
5803b5f6 309PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, ssize_t size) {
221cc3ce
MT
310 file->size = size;
311}
312
5803b5f6 313PAKFIRE_EXPORT const char* pakfire_file_get_user(struct pakfire_file* file) {
221cc3ce
MT
314 return file->user;
315}
316
5803b5f6 317PAKFIRE_EXPORT void pakfire_file_set_user(struct pakfire_file* file, const char* user) {
e7917fb3 318 pakfire_string_set(file->user, user);
221cc3ce
MT
319}
320
5803b5f6 321PAKFIRE_EXPORT const char* pakfire_file_get_group(struct pakfire_file* file) {
221cc3ce
MT
322 return file->group;
323}
324
5803b5f6 325PAKFIRE_EXPORT void pakfire_file_set_group(struct pakfire_file* file, const char* group) {
e7917fb3 326 pakfire_string_set(file->group, group);
221cc3ce
MT
327}
328
5803b5f6 329PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) {
221cc3ce
MT
330 return file->mode;
331}
332
5803b5f6 333PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) {
221cc3ce
MT
334 file->mode = mode;
335}
336
487d6485
MT
337PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) {
338 return file->dev;
339}
340
341PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, dev_t dev) {
342 file->dev = dev;
343}
344
5803b5f6 345PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) {
ef4e8460
MT
346 return file->ctime;
347}
348
5803b5f6 349PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) {
ef4e8460
MT
350 file->ctime = time;
351}
352
5803b5f6 353PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) {
ef4e8460 354 return file->mtime;
221cc3ce
MT
355}
356
5803b5f6 357PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) {
ef4e8460 358 file->mtime = time;
221cc3ce
MT
359}
360
65131b30
MT
361static struct pakfire_file_digest* pakfire_file_find_digest(
362 struct pakfire_file* file, enum pakfire_digests type) {
363 struct pakfire_file_digest* digest = NULL;
364
365 for (unsigned int i = 0; i < MAX_DIGESTS; i++) {
366 digest = &file->digests[i];
5e8dfbeb 367
65131b30
MT
368 if (digest->type == type)
369 return digest;
370 }
5e8dfbeb 371
65131b30 372 // No match
5e8dfbeb
MT
373 return NULL;
374}
375
65131b30
MT
376PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
377 struct pakfire_file* file, enum pakfire_digests type, size_t* length) {
378 const struct pakfire_file_digest* digest = pakfire_file_find_digest(file, type);
379 if (!digest)
380 return NULL;
381
382 // Export length
383 if (length)
384 *length = digest->length;
385
386 return digest->digest;
387}
388
5e8dfbeb 389PAKFIRE_EXPORT const char* pakfire_file_get_hexdigest(
65131b30
MT
390 struct pakfire_file* file, enum pakfire_digests type) {
391 struct pakfire_file_digest* digest = pakfire_file_find_digest(file, type);
392 if (!digest)
393 return NULL;
5e8dfbeb 394
65131b30
MT
395 // Generate the hexdigest if non exists
396 if (!digest->hexdigest) {
397 const size_t length = pakfire_digest_length(digest->type);
398 if (!length)
5e8dfbeb
MT
399 return NULL;
400
65131b30 401 digest->hexdigest = __pakfire_hexlify(digest->digest, length);
5e8dfbeb
MT
402 }
403
65131b30 404 return digest->hexdigest;
5e8dfbeb
MT
405}
406
407PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file,
65131b30
MT
408 enum pakfire_digests type, const unsigned char* digest, size_t length) {
409 if (!digest || !length) {
5e8dfbeb
MT
410 errno = EINVAL;
411 return 1;
412 }
413
65131b30
MT
414 // Find any existing digests of this type
415 struct pakfire_file_digest* d = pakfire_file_find_digest(file, type);
416
417 // If there is no digest, we will try finding a new one
418 if (!d)
419 d = pakfire_file_find_digest(file, PAKFIRE_DIGEST_NONE);
420
421 // If we could not find a free spot, we probably run out of space
baf8b0ed
MT
422 if (!d) {
423 errno = ENOBUFS;
424 return 1;
425 }
426
427 // Check if the digest fits into our pre-allocated buffer space
428 if (length > sizeof(d->digest)) {
65131b30 429 errno = ENOBUFS;
5e8dfbeb
MT
430 return 1;
431 }
432
65131b30
MT
433 // Store type & length
434 d->type = type;
435 d->length = length;
5e8dfbeb
MT
436
437 // Store digest
65131b30 438 memcpy(d->digest, digest, d->length);
5e8dfbeb
MT
439
440 return 0;
221cc3ce
MT
441}
442
5e8dfbeb
MT
443PAKFIRE_EXPORT int pakfire_file_set_hexdigest(struct pakfire_file* file,
444 enum pakfire_digests type, const char* hexdigest) {
445 const size_t digest_length = pakfire_digest_length(type);
446 if (!digest_length) {
447 errno = EINVAL;
448 return 1;
ffbef232
MT
449 }
450
5e8dfbeb
MT
451 // Allocate a buffer for the binary representation of the digest
452 unsigned char* digest = alloca(digest_length);
453 if (!digest)
454 return 1;
455
456 // Convert from hex to binary
457 __pakfire_unhexlify(digest, digest_length, hexdigest);
458
65131b30 459 return pakfire_file_set_digest(file, type, digest, digest_length);
221cc3ce 460}
3fca5032 461
5803b5f6 462static int pakfire_file_levels(struct pakfire_file* file) {
3fca5032
MT
463 if (!*file->path)
464 return 0;
465
466 int levels = 0;
467
468 for (char* p = file->path; *p; p++) {
469 if (*p == '/')
470 levels++;
471 }
472
473 return levels;
474}
475
d5a13ade
MT
476FILE* pakfire_file_open(struct pakfire_file* file) {
477 FILE* f = fopen(file->abspath, "r");
478 if (!f)
479 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
480
481 return f;
482}
483
ac71886a 484int pakfire_file_remove(struct pakfire_file* file) {
3fca5032
MT
485 if (!*file->abspath) {
486 errno = EINVAL;
487 return 1;
488 }
489
490 DEBUG(file->pakfire, "Removing %s...\n", file->path);
491
492 int r = remove(file->abspath);
493 if (r) {
da89e02f
MT
494 switch (errno) {
495 // Ignore when we could not remove directories
496 case ENOTEMPTY:
497 return 0;
498
499 // Ignore if the file didn't exist
500 case ENOENT:
501 return 0;
502
503 default:
504 break;
505 }
3fca5032 506
b1772bfb 507 ERROR(file->pakfire, "Could not remove %s (%s): %m\n", file->path, file->abspath);
3fca5032
MT
508 }
509
ac71886a
MT
510 return r;
511}
512
513/*
514 This function tries to remove the file after it has been packaged.
515
516 It will try to delete any parent directories as well and ignore if directories
517 cannot be deleted because they might contain other files
518*/
519int pakfire_file_cleanup(struct pakfire_file* file) {
520 char path[PATH_MAX];
521
522 // Try removing the file
523 int r = pakfire_file_remove(file);
524 if (r)
525 return r;
526
3fca5032
MT
527 // Create a working copy of abspath
528 r = pakfire_string_set(path, file->abspath);
529 if (r < 0)
530 return r;
531
532 // See how many levels this file has
533 int levels = pakfire_file_levels(file);
534
535 // Walk all the way up and remove all parent directories if possible
536 while (--levels) {
537 dirname(path);
538
539 // Break if path is suddenly empty
540 if (!*path)
541 break;
542
543 r = rmdir(path);
544 if (r) {
545 if (errno == ENOTEMPTY)
546 return 0;
547
548 return r;
549 }
550 }
551
a942166c 552 return 0;
3fca5032 553}