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