]>
Commit | Line | Data |
---|---|---|
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> |
76011205 | 30 | #include <openssl/err.h> |
5e8dfbeb | 31 | #include <openssl/evp.h> |
9802aaf6 | 32 | #include <openssl/sha.h> |
9a6e3e2d | 33 | |
221cc3ce MT |
34 | #include <pakfire/constants.h> |
35 | #include <pakfire/file.h> | |
3fca5032 | 36 | #include <pakfire/logging.h> |
883b3be9 | 37 | #include <pakfire/pakfire.h> |
9f953e68 | 38 | #include <pakfire/private.h> |
d973a13d | 39 | #include <pakfire/string.h> |
221cc3ce MT |
40 | #include <pakfire/util.h> |
41 | ||
c0b051bb MT |
42 | enum pakfire_file_verification_status { |
43 | PAKFIRE_FILE_NOENT = (1 << 0), | |
9e09e361 MT |
44 | PAKFIRE_FILE_TYPE_CHANGED = (1 << 1), |
45 | PAKFIRE_FILE_PERMISSIONS_CHANGED = (1 << 2), | |
46 | PAKFIRE_FILE_DEV_CHANGED = (1 << 3), | |
47 | PAKFIRE_FILE_SIZE_CHANGED = (1 << 4), | |
48 | PAKFIRE_FILE_OWNER_CHANGED = (1 << 5), | |
49 | PAKFIRE_FILE_GROUP_CHANGED = (1 << 6), | |
0eeac4a5 MT |
50 | PAKFIRE_FILE_CTIME_CHANGED = (1 << 7), |
51 | PAKFIRE_FILE_MTIME_CHANGED = (1 << 8), | |
76011205 | 52 | PAKFIRE_FILE_PAYLOAD_CHANGED = (1 << 9), |
c0b051bb MT |
53 | }; |
54 | ||
5803b5f6 | 55 | struct pakfire_file { |
ac4c607b | 56 | struct pakfire* pakfire; |
5e9463ec | 57 | int nrefs; |
221cc3ce | 58 | |
e8d3b985 | 59 | // The relative path |
3b9e3970 | 60 | char path[PATH_MAX]; |
e8d3b985 MT |
61 | |
62 | // The absolute path in the file system | |
0027da6f | 63 | char abspath[PATH_MAX]; |
221cc3ce | 64 | |
e8d3b985 MT |
65 | // File Ownership |
66 | char user[LOGIN_NAME_MAX]; | |
67 | char group[LOGIN_NAME_MAX]; | |
68 | ||
69 | // File Size | |
70 | ssize_t size; | |
221cc3ce | 71 | |
e8d3b985 | 72 | // File Mode |
5e9463ec | 73 | mode_t mode; |
e8d3b985 MT |
74 | |
75 | // Dev Minor/Major | |
487d6485 | 76 | dev_t dev; |
ef4e8460 | 77 | |
e8d3b985 | 78 | // Creation/Modification Time |
ef4e8460 MT |
79 | time_t ctime; |
80 | time_t mtime; | |
d03fa9a3 | 81 | |
59d8c727 MT |
82 | // Link destinations |
83 | char hardlink[PATH_MAX]; | |
84 | char symlink[PATH_MAX]; | |
85 | ||
65131b30 | 86 | // Digests |
9802aaf6 MT |
87 | struct pakfire_file_digests { |
88 | // SHA-512 | |
89 | unsigned char sha512[SHA512_DIGEST_LENGTH]; | |
90 | ||
91 | // SHA-256 | |
92 | unsigned char sha256[SHA256_DIGEST_LENGTH]; | |
93 | } digests; | |
5e9463ec | 94 | |
c0b051bb MT |
95 | // Verification Status |
96 | int verify_status; | |
97 | ||
5e9463ec MT |
98 | #warning TODO capabilities, config, data |
99 | // capabilities | |
100 | //int is_configfile; | |
101 | //int is_datafile; | |
102 | }; | |
103 | ||
ac4c607b | 104 | PAKFIRE_EXPORT int pakfire_file_create(struct pakfire_file** file, struct pakfire* pakfire) { |
5803b5f6 | 105 | struct pakfire_file* f = calloc(1, sizeof(*f)); |
5e9463ec | 106 | if (!f) |
7403779a | 107 | return 1; |
5e9463ec | 108 | |
e8d3b985 | 109 | // Store reference to Pakfire |
883b3be9 | 110 | f->pakfire = pakfire_ref(pakfire); |
e8d3b985 MT |
111 | |
112 | // Initialize reference counter | |
5e9463ec MT |
113 | f->nrefs = 1; |
114 | ||
115 | *file = f; | |
116 | return 0; | |
d03fa9a3 MT |
117 | } |
118 | ||
ac4c607b | 119 | int pakfire_file_create_from_archive_entry(struct pakfire_file** file, struct pakfire* pakfire, |
d0985e31 MT |
120 | struct archive_entry* entry) { |
121 | int r = pakfire_file_create(file, pakfire); | |
122 | if (r) | |
123 | return r; | |
124 | ||
125 | // Copy archive entry | |
126 | r = pakfire_file_copy_archive_entry(*file, entry); | |
127 | if (r) | |
128 | goto ERROR; | |
129 | ||
130 | return 0; | |
131 | ||
132 | ERROR: | |
133 | pakfire_file_unref(*file); | |
134 | *file = NULL; | |
135 | ||
136 | return r; | |
137 | } | |
138 | ||
aa8ea50c MT |
139 | static const struct pakfire_libarchive_digest { |
140 | enum pakfire_digests pakfire; | |
141 | int libarchive; | |
142 | } pakfire_libarchive_digests[] = { | |
143 | { PAKFIRE_DIGEST_SHA512, ARCHIVE_ENTRY_DIGEST_SHA512 }, | |
144 | { PAKFIRE_DIGEST_SHA256, ARCHIVE_ENTRY_DIGEST_SHA256 }, | |
9802aaf6 | 145 | { 0, 0 }, |
aa8ea50c MT |
146 | }; |
147 | ||
5803b5f6 | 148 | int pakfire_file_copy_archive_entry(struct pakfire_file* file, struct archive_entry* entry) { |
aa8ea50c MT |
149 | int r = 0; |
150 | ||
3b9e3970 MT |
151 | // Set abspath |
152 | pakfire_file_set_abspath(file, archive_entry_sourcepath(entry)); | |
153 | ||
32485f6c | 154 | // Set path |
b9f703e5 MT |
155 | const char* path = archive_entry_pathname(entry); |
156 | if (path) { | |
157 | // Strip any leading dots from paths | |
158 | if (pakfire_string_startswith(path, "./")) | |
159 | path++; | |
160 | ||
161 | pakfire_file_set_path(file, path); | |
162 | } | |
9a6e3e2d | 163 | |
59d8c727 MT |
164 | // Set links |
165 | pakfire_file_set_hardlink(file, archive_entry_hardlink(entry)); | |
166 | pakfire_file_set_symlink(file, archive_entry_symlink(entry)); | |
167 | ||
9a6e3e2d MT |
168 | // Set size |
169 | pakfire_file_set_size(file, archive_entry_size(entry)); | |
170 | ||
171 | // Set mode | |
172 | pakfire_file_set_mode(file, archive_entry_mode(entry)); | |
173 | ||
487d6485 MT |
174 | // Set dev type |
175 | if (archive_entry_dev_is_set(entry)) | |
176 | pakfire_file_set_dev(file, archive_entry_dev(entry)); | |
177 | ||
9a6e3e2d MT |
178 | // Set user |
179 | pakfire_file_set_user(file, archive_entry_uname(entry)); | |
180 | ||
181 | // Set group | |
182 | pakfire_file_set_group(file, archive_entry_gname(entry)); | |
183 | ||
ef4e8460 MT |
184 | // Set times |
185 | pakfire_file_set_ctime(file, archive_entry_ctime(entry)); | |
186 | pakfire_file_set_mtime(file, archive_entry_mtime(entry)); | |
9a6e3e2d | 187 | |
aa8ea50c MT |
188 | // Copy digest |
189 | for (const struct pakfire_libarchive_digest* type = pakfire_libarchive_digests; | |
190 | type->pakfire; type++) { | |
191 | const unsigned char* digest = archive_entry_digest(entry, type->libarchive); | |
192 | if (digest) { | |
9802aaf6 | 193 | r = pakfire_file_set_digest(file, type->pakfire, digest); |
aa8ea50c MT |
194 | if (r) |
195 | return r; | |
aa8ea50c MT |
196 | } |
197 | } | |
198 | ||
199 | return r; | |
9a6e3e2d MT |
200 | } |
201 | ||
5acd852e MT |
202 | struct archive_entry* pakfire_file_archive_entry(struct pakfire_file* file) { |
203 | struct archive_entry* entry = archive_entry_new(); | |
204 | if (!entry) { | |
205 | ERROR(file->pakfire, "Could not allocate archive entry: %m\n"); | |
206 | return NULL; | |
207 | } | |
208 | ||
209 | // Set path | |
210 | archive_entry_copy_pathname(entry, pakfire_file_get_path(file)); | |
211 | ||
212 | // Set source path | |
213 | archive_entry_copy_sourcepath(entry, file->abspath); | |
214 | ||
215 | // Set links | |
216 | if (*file->hardlink) | |
217 | archive_entry_set_hardlink(entry, file->hardlink); | |
218 | if (*file->symlink) | |
219 | archive_entry_set_symlink(entry, file->symlink); | |
220 | ||
221 | // Set size | |
222 | archive_entry_set_size(entry, pakfire_file_get_size(file)); | |
223 | ||
224 | // Set mode | |
225 | archive_entry_set_mode(entry, pakfire_file_get_mode(file)); | |
226 | ||
227 | // Set user | |
228 | archive_entry_set_uname(entry, pakfire_file_get_user(file)); | |
229 | ||
230 | // Set group | |
231 | archive_entry_set_gname(entry, pakfire_file_get_group(file)); | |
232 | ||
233 | // Set times | |
234 | archive_entry_set_ctime(entry, pakfire_file_get_ctime(file), 0); | |
235 | archive_entry_set_mtime(entry, pakfire_file_get_mtime(file), 0); | |
236 | ||
237 | // XXX copy digest | |
238 | ||
239 | return entry; | |
240 | } | |
241 | ||
5803b5f6 | 242 | static void pakfire_file_free(struct pakfire_file* file) { |
5e8dfbeb | 243 | pakfire_unref(file->pakfire); |
f0d6233d | 244 | free(file); |
221cc3ce MT |
245 | } |
246 | ||
5803b5f6 | 247 | PAKFIRE_EXPORT struct pakfire_file* pakfire_file_ref(struct pakfire_file* file) { |
5e9463ec | 248 | file->nrefs++; |
221cc3ce | 249 | |
5e9463ec MT |
250 | return file; |
251 | } | |
221cc3ce | 252 | |
5803b5f6 | 253 | PAKFIRE_EXPORT struct pakfire_file* pakfire_file_unref(struct pakfire_file* file) { |
5e9463ec MT |
254 | if (--file->nrefs > 0) |
255 | return file; | |
256 | ||
257 | pakfire_file_free(file); | |
258 | return NULL; | |
221cc3ce MT |
259 | } |
260 | ||
5803b5f6 | 261 | PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) { |
32485f6c MT |
262 | const char* path1 = pakfire_file_get_path(file1); |
263 | const char* path2 = pakfire_file_get_path(file2); | |
221cc3ce | 264 | |
32485f6c | 265 | return strcmp(path1, path2); |
221cc3ce MT |
266 | } |
267 | ||
5803b5f6 | 268 | const char* pakfire_file_get_abspath(struct pakfire_file* file) { |
e4c2f7a9 MT |
269 | return file->abspath; |
270 | } | |
271 | ||
5803b5f6 | 272 | int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) { |
d13bd5b7 MT |
273 | // Check if path is set and absolute |
274 | if (!path || *path != '/') { | |
275 | errno = EINVAL; | |
276 | return 1; | |
277 | } | |
278 | ||
e7917fb3 | 279 | return pakfire_string_set(file->abspath, path); |
3b9e3970 MT |
280 | } |
281 | ||
5803b5f6 | 282 | PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) { |
32485f6c | 283 | return file->path; |
221cc3ce MT |
284 | } |
285 | ||
5803b5f6 | 286 | PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) { |
d13bd5b7 MT |
287 | // Check if path is set and absolute |
288 | if (!path || *path != '/') { | |
289 | errno = EINVAL; | |
290 | return 1; | |
291 | } | |
292 | ||
e7917fb3 | 293 | return pakfire_string_set(file->path, path); |
221cc3ce MT |
294 | } |
295 | ||
59d8c727 MT |
296 | PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) { |
297 | if (!*file->hardlink) | |
298 | return NULL; | |
299 | ||
300 | return file->hardlink; | |
301 | } | |
302 | ||
303 | PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) { | |
304 | if (!link || !*link) | |
305 | *file->hardlink = '\0'; | |
306 | else | |
307 | pakfire_string_set(file->hardlink, link); | |
308 | } | |
309 | ||
310 | PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) { | |
311 | if (!*file->symlink) | |
312 | return NULL; | |
313 | ||
314 | return file->symlink; | |
315 | } | |
316 | ||
317 | PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) { | |
318 | if (!link || !*link) | |
319 | *file->hardlink = '\0'; | |
320 | else | |
321 | pakfire_string_set(file->symlink, link); | |
322 | } | |
323 | ||
5803b5f6 | 324 | PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) { |
f9770f19 | 325 | return file->mode & S_IFMT; |
221cc3ce MT |
326 | } |
327 | ||
5803b5f6 | 328 | PAKFIRE_EXPORT ssize_t pakfire_file_get_size(struct pakfire_file* file) { |
221cc3ce MT |
329 | return file->size; |
330 | } | |
331 | ||
5803b5f6 | 332 | PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, ssize_t size) { |
221cc3ce MT |
333 | file->size = size; |
334 | } | |
335 | ||
5803b5f6 | 336 | PAKFIRE_EXPORT const char* pakfire_file_get_user(struct pakfire_file* file) { |
221cc3ce MT |
337 | return file->user; |
338 | } | |
339 | ||
5803b5f6 | 340 | PAKFIRE_EXPORT void pakfire_file_set_user(struct pakfire_file* file, const char* user) { |
e7917fb3 | 341 | pakfire_string_set(file->user, user); |
221cc3ce MT |
342 | } |
343 | ||
5803b5f6 | 344 | PAKFIRE_EXPORT const char* pakfire_file_get_group(struct pakfire_file* file) { |
221cc3ce MT |
345 | return file->group; |
346 | } | |
347 | ||
5803b5f6 | 348 | PAKFIRE_EXPORT void pakfire_file_set_group(struct pakfire_file* file, const char* group) { |
e7917fb3 | 349 | pakfire_string_set(file->group, group); |
221cc3ce MT |
350 | } |
351 | ||
5803b5f6 | 352 | PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) { |
221cc3ce MT |
353 | return file->mode; |
354 | } | |
355 | ||
5803b5f6 | 356 | PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) { |
221cc3ce MT |
357 | file->mode = mode; |
358 | } | |
359 | ||
487d6485 MT |
360 | PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) { |
361 | return file->dev; | |
362 | } | |
363 | ||
364 | PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, dev_t dev) { | |
365 | file->dev = dev; | |
366 | } | |
367 | ||
5803b5f6 | 368 | PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) { |
ef4e8460 MT |
369 | return file->ctime; |
370 | } | |
371 | ||
5803b5f6 | 372 | PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) { |
ef4e8460 MT |
373 | file->ctime = time; |
374 | } | |
375 | ||
5803b5f6 | 376 | PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) { |
ef4e8460 | 377 | return file->mtime; |
221cc3ce MT |
378 | } |
379 | ||
5803b5f6 | 380 | PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) { |
ef4e8460 | 381 | file->mtime = time; |
221cc3ce MT |
382 | } |
383 | ||
9802aaf6 MT |
384 | /* |
385 | Returns one if the digest is not all zeros. | |
386 | */ | |
76011205 MT |
387 | #define pakfire_file_has_digest(digest) __pakfire_file_has_digest(digest, sizeof(digest)) |
388 | ||
389 | static int __pakfire_file_has_digest(const unsigned char* digest, const size_t length) { | |
9802aaf6 MT |
390 | for (unsigned int i = 0; i < length; i++) { |
391 | if (digest[i]) | |
392 | return 1; | |
65131b30 | 393 | } |
5e8dfbeb | 394 | |
9802aaf6 | 395 | return 0; |
5e8dfbeb MT |
396 | } |
397 | ||
65131b30 | 398 | PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest( |
9802aaf6 | 399 | struct pakfire_file* file, const enum pakfire_digests type, size_t* length) { |
65131b30 | 400 | |
9802aaf6 MT |
401 | switch (type) { |
402 | case PAKFIRE_DIGEST_SHA512: | |
76011205 | 403 | if (!pakfire_file_has_digest(file->digests.sha512)) |
9802aaf6 | 404 | return NULL; |
65131b30 | 405 | |
9802aaf6 MT |
406 | if (length) |
407 | *length = sizeof(file->digests.sha512); | |
65131b30 | 408 | |
9802aaf6 MT |
409 | return file->digests.sha512; |
410 | ||
411 | case PAKFIRE_DIGEST_SHA256: | |
76011205 | 412 | if (!pakfire_file_has_digest(file->digests.sha256)) |
9802aaf6 | 413 | return NULL; |
5e8dfbeb | 414 | |
9802aaf6 MT |
415 | if (length) |
416 | *length = sizeof(file->digests.sha256); | |
5e8dfbeb | 417 | |
9802aaf6 | 418 | return file->digests.sha256; |
5e8dfbeb MT |
419 | } |
420 | ||
9802aaf6 | 421 | return NULL; |
5e8dfbeb MT |
422 | } |
423 | ||
424 | PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file, | |
9802aaf6 MT |
425 | const enum pakfire_digests type, const unsigned char* digest) { |
426 | if (!digest) { | |
5e8dfbeb MT |
427 | errno = EINVAL; |
428 | return 1; | |
429 | } | |
430 | ||
9802aaf6 MT |
431 | switch (type) { |
432 | case PAKFIRE_DIGEST_SHA512: | |
433 | memcpy(file->digests.sha512, digest, sizeof(file->digests.sha512)); | |
434 | break; | |
65131b30 | 435 | |
9802aaf6 MT |
436 | case PAKFIRE_DIGEST_SHA256: |
437 | memcpy(file->digests.sha256, digest, sizeof(file->digests.sha256)); | |
438 | break; | |
5e8dfbeb MT |
439 | } |
440 | ||
5e8dfbeb | 441 | return 0; |
221cc3ce MT |
442 | } |
443 | ||
9802aaf6 MT |
444 | PAKFIRE_EXPORT char* pakfire_file_get_hexdigest( |
445 | struct pakfire_file* file, const enum pakfire_digests type) { | |
446 | const unsigned char* digest = NULL; | |
447 | size_t length = 0; | |
ffbef232 | 448 | |
9802aaf6 MT |
449 | // Fetch the digest |
450 | digest = pakfire_file_get_digest(file, type, &length); | |
5e8dfbeb | 451 | if (!digest) |
9802aaf6 | 452 | return NULL; |
5e8dfbeb | 453 | |
9802aaf6 | 454 | return __pakfire_hexlify(digest, length); |
221cc3ce | 455 | } |
3fca5032 | 456 | |
5803b5f6 | 457 | static int pakfire_file_levels(struct pakfire_file* file) { |
3fca5032 MT |
458 | if (!*file->path) |
459 | return 0; | |
460 | ||
461 | int levels = 0; | |
462 | ||
463 | for (char* p = file->path; *p; p++) { | |
464 | if (*p == '/') | |
465 | levels++; | |
466 | } | |
467 | ||
468 | return levels; | |
469 | } | |
470 | ||
d5a13ade MT |
471 | FILE* pakfire_file_open(struct pakfire_file* file) { |
472 | FILE* f = fopen(file->abspath, "r"); | |
473 | if (!f) | |
474 | ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath); | |
475 | ||
476 | return f; | |
477 | } | |
478 | ||
ac71886a | 479 | int pakfire_file_remove(struct pakfire_file* file) { |
3fca5032 MT |
480 | if (!*file->abspath) { |
481 | errno = EINVAL; | |
482 | return 1; | |
483 | } | |
484 | ||
485 | DEBUG(file->pakfire, "Removing %s...\n", file->path); | |
486 | ||
487 | int r = remove(file->abspath); | |
488 | if (r) { | |
da89e02f MT |
489 | switch (errno) { |
490 | // Ignore when we could not remove directories | |
491 | case ENOTEMPTY: | |
492 | return 0; | |
493 | ||
494 | // Ignore if the file didn't exist | |
495 | case ENOENT: | |
496 | return 0; | |
497 | ||
498 | default: | |
499 | break; | |
500 | } | |
3fca5032 | 501 | |
b1772bfb | 502 | ERROR(file->pakfire, "Could not remove %s (%s): %m\n", file->path, file->abspath); |
3fca5032 MT |
503 | } |
504 | ||
ac71886a MT |
505 | return r; |
506 | } | |
507 | ||
508 | /* | |
509 | This function tries to remove the file after it has been packaged. | |
510 | ||
511 | It will try to delete any parent directories as well and ignore if directories | |
512 | cannot be deleted because they might contain other files | |
513 | */ | |
514 | int pakfire_file_cleanup(struct pakfire_file* file) { | |
515 | char path[PATH_MAX]; | |
516 | ||
517 | // Try removing the file | |
518 | int r = pakfire_file_remove(file); | |
519 | if (r) | |
520 | return r; | |
521 | ||
3fca5032 MT |
522 | // Create a working copy of abspath |
523 | r = pakfire_string_set(path, file->abspath); | |
a60955af | 524 | if (r) |
3fca5032 MT |
525 | return r; |
526 | ||
527 | // See how many levels this file has | |
528 | int levels = pakfire_file_levels(file); | |
529 | ||
530 | // Walk all the way up and remove all parent directories if possible | |
531 | while (--levels) { | |
532 | dirname(path); | |
533 | ||
534 | // Break if path is suddenly empty | |
535 | if (!*path) | |
536 | break; | |
537 | ||
538 | r = rmdir(path); | |
539 | if (r) { | |
540 | if (errno == ENOTEMPTY) | |
541 | return 0; | |
542 | ||
543 | return r; | |
544 | } | |
545 | } | |
546 | ||
a942166c | 547 | return 0; |
3fca5032 | 548 | } |
cf9bd6f4 | 549 | |
9e09e361 MT |
550 | static int pakfire_file_verify_mode(struct pakfire_file* file, const struct stat* st) { |
551 | // Did the type change? | |
552 | if ((file->mode & S_IFMT) != (st->st_mode & S_IFMT)) { | |
553 | file->verify_status |= PAKFIRE_FILE_TYPE_CHANGED; | |
554 | ||
555 | DEBUG(file->pakfire, "%s: File Type changed\n", file->path); | |
556 | } | |
557 | ||
558 | // Check permissions | |
559 | if ((file->mode & 0777) != (st->st_mode & 0777)) { | |
560 | file->verify_status |= PAKFIRE_FILE_PERMISSIONS_CHANGED; | |
561 | ||
562 | DEBUG(file->pakfire, "%s: Permissions changed\n", file->path); | |
563 | } | |
564 | ||
565 | // Check if device node changed | |
566 | if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { | |
567 | if (file->dev != st->st_dev) { | |
568 | file->verify_status |= PAKFIRE_FILE_DEV_CHANGED; | |
569 | ||
570 | DEBUG(file->pakfire, "%s: Device Node changed\n", file->path); | |
571 | } | |
572 | } | |
573 | ||
574 | return 0; | |
575 | } | |
576 | ||
c0b051bb MT |
577 | static int pakfire_file_verify_size(struct pakfire_file* file, const struct stat* st) { |
578 | // Nothing to do if size matches | |
579 | if (file->size == st->st_size) | |
580 | return 0; | |
581 | ||
582 | // Size differs | |
d2b0e219 | 583 | file->verify_status |= PAKFIRE_FILE_SIZE_CHANGED; |
c0b051bb MT |
584 | |
585 | DEBUG(file->pakfire, "%s: Filesize differs (expected %zu, got %zu byte(s))\n", | |
586 | file->path, file->size, st->st_size); | |
587 | ||
588 | return 0; | |
589 | } | |
590 | ||
591 | static int pakfire_file_verify_ownership(struct pakfire_file* file, const struct stat* st) { | |
592 | // Fetch UID/GID | |
593 | #if 0 | |
594 | const uid_t uid = pakfire_unmap_id(file->pakfire, st->st_uid); | |
595 | const gid_t gid = pakfire_unmap_id(file->pakfire, st->st_gid); | |
596 | #else | |
597 | const uid_t uid = st->st_uid; | |
598 | const gid_t gid = st->st_gid; | |
599 | #endif | |
600 | ||
601 | // Fetch owner & group | |
602 | struct passwd* owner = pakfire_getpwnam(file->pakfire, file->user); | |
603 | struct group* group = pakfire_getgrnam(file->pakfire, file->group); | |
604 | ||
605 | // Check if owner matches | |
606 | if (!owner || owner->pw_uid != uid) { | |
d2b0e219 | 607 | file->verify_status |= PAKFIRE_FILE_OWNER_CHANGED; |
c0b051bb MT |
608 | |
609 | DEBUG(file->pakfire, "%s: Owner differs\n", file->path); | |
610 | } | |
611 | ||
612 | // Check if group matches | |
613 | if (!group || group->gr_gid != gid) { | |
d2b0e219 | 614 | file->verify_status |= PAKFIRE_FILE_GROUP_CHANGED; |
c0b051bb MT |
615 | |
616 | DEBUG(file->pakfire, "%s: Group differs\n", file->path); | |
617 | } | |
618 | ||
619 | return 0; | |
620 | } | |
621 | ||
0eeac4a5 MT |
622 | static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struct stat* st) { |
623 | // Check creation time | |
624 | if (file->ctime != st->st_ctime) { | |
625 | file->verify_status |= PAKFIRE_FILE_CTIME_CHANGED; | |
626 | ||
627 | DEBUG(file->pakfire, "%s: Creation time changed\n", file->path); | |
628 | } | |
629 | ||
630 | // Check modification time | |
631 | if (file->mtime != st->st_mtime) { | |
632 | file->verify_status |= PAKFIRE_FILE_MTIME_CHANGED; | |
633 | ||
634 | DEBUG(file->pakfire, "%s: Modification time changed\n", file->path); | |
635 | } | |
636 | ||
637 | return 0; | |
638 | } | |
639 | ||
76011205 MT |
640 | static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) { |
641 | char buffer[PAKFIRE_BUFFER_SIZE]; | |
642 | FILE* f = NULL; | |
643 | int r; | |
644 | ||
645 | EVP_MD_CTX* sha512_ctx = NULL; | |
646 | EVP_MD_CTX* sha256_ctx = NULL; | |
647 | ||
648 | struct pakfire_file_digests computed_digests; | |
649 | ||
650 | // Nothing to do for anything that isn't a regular file | |
651 | if (!S_ISREG(st->st_mode)) | |
652 | return 0; | |
653 | ||
654 | // Fast-path if size changed. The payload will have changed, too | |
655 | if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) { | |
656 | file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED; | |
657 | return 0; | |
658 | } | |
659 | ||
660 | // Check if this file has any digests at all | |
661 | if (!pakfire_file_has_digest(file->digests.sha512) && | |
662 | !pakfire_file_has_digest(file->digests.sha256)) { | |
663 | ERROR(file->pakfire, "%s: No digests available\n", file->path); | |
664 | return 0; | |
665 | } | |
666 | ||
667 | // Initialize context for SHA-512 | |
668 | sha512_ctx = EVP_MD_CTX_new(); | |
669 | if (!sha512_ctx) { | |
670 | ERROR(file->pakfire, "Could not initialize OpenSSL context: %s\n", | |
671 | ERR_error_string(ERR_get_error(), NULL)); | |
672 | r = 1; | |
673 | goto ERROR; | |
674 | } | |
675 | ||
676 | // Setup SHA-512 digest | |
677 | r = EVP_DigestInit_ex(sha512_ctx, EVP_sha512(), NULL); | |
678 | if (r != 1) { | |
679 | ERROR(file->pakfire, "Could not setup SHA-512 digest: %s\n", | |
680 | ERR_error_string(ERR_get_error(), NULL)); | |
681 | r = 1; | |
682 | goto ERROR; | |
683 | } | |
684 | ||
685 | // Initialize context for SHA-256 | |
686 | sha256_ctx = EVP_MD_CTX_new(); | |
687 | if (!sha256_ctx) { | |
688 | ERROR(file->pakfire, "Could not initialize OpenSSL context: %s\n", | |
689 | ERR_error_string(ERR_get_error(), NULL)); | |
690 | r = 1; | |
691 | goto ERROR; | |
692 | } | |
693 | ||
694 | // Setup SHA-256 digest | |
695 | r = EVP_DigestInit_ex(sha256_ctx, EVP_sha256(), NULL); | |
696 | if (r != 1) { | |
697 | ERROR(file->pakfire, "Could not setup SHA-256 digest: %s\n", | |
698 | ERR_error_string(ERR_get_error(), NULL)); | |
699 | r = 1; | |
700 | goto ERROR; | |
701 | } | |
702 | ||
703 | // Open the file | |
704 | f = pakfire_file_open(file); | |
705 | if (!f) { | |
706 | ERROR(file->pakfire, "Could not open %s: %m\n", file->path); | |
707 | goto ERROR; | |
708 | } | |
709 | ||
710 | // Read the file into the hash functions | |
711 | while (!feof(f)) { | |
712 | size_t bytes_read = fread(buffer, 1, sizeof(buffer), f); | |
713 | ||
714 | // Raise any reading errors | |
715 | if (ferror(f)) { | |
716 | r = 1; | |
717 | goto ERROR; | |
718 | } | |
719 | ||
720 | // SHA-512 | |
721 | r = EVP_DigestUpdate(sha512_ctx, buffer, bytes_read); | |
722 | if (r != 1) { | |
723 | ERROR(file->pakfire, "EVP_Digest_Update() failed: %s\n", | |
724 | ERR_error_string(ERR_get_error(), NULL)); | |
725 | r = 1; | |
726 | goto ERROR; | |
727 | } | |
728 | ||
729 | // SHA-256 | |
730 | r = EVP_DigestUpdate(sha256_ctx, buffer, bytes_read); | |
731 | if (r != 1) { | |
732 | ERROR(file->pakfire, "EVP_Digest_Update() failed: %s\n", | |
733 | ERR_error_string(ERR_get_error(), NULL)); | |
734 | r = 1; | |
735 | goto ERROR; | |
736 | } | |
737 | } | |
738 | ||
739 | // Finalize SHA-512 | |
740 | r = EVP_DigestFinal_ex(sha512_ctx, computed_digests.sha512, NULL); | |
741 | if (r != 1) { | |
742 | ERROR(file->pakfire, "EVP_DigestFinal_ex() failed: %s\n", | |
743 | ERR_error_string(ERR_get_error(), NULL)); | |
744 | r = 1; | |
745 | goto ERROR; | |
746 | } | |
747 | ||
748 | // Finalize SHA-256 | |
749 | r = EVP_DigestFinal_ex(sha256_ctx, computed_digests.sha256, NULL); | |
750 | if (r != 1) { | |
751 | ERROR(file->pakfire, "EVP_DigestFinal_ex() failed: %s\n", | |
752 | ERR_error_string(ERR_get_error(), NULL)); | |
753 | r = 1; | |
754 | goto ERROR; | |
755 | } | |
756 | ||
757 | // Check SHA-512 | |
758 | r = CRYPTO_memcmp(computed_digests.sha512, | |
759 | file->digests.sha512, sizeof(file->digests.sha512)); | |
760 | if (r) { | |
761 | file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED; | |
762 | ||
763 | DEBUG(file->pakfire, "%s: SHA-512 digest does not match\n", file->path); | |
764 | } | |
765 | ||
766 | // Check SHA-256 | |
767 | r = CRYPTO_memcmp(computed_digests.sha256, | |
768 | file->digests.sha256, sizeof(file->digests.sha256)); | |
769 | if (r) { | |
770 | file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED; | |
771 | ||
772 | DEBUG(file->pakfire, "%s: SHA-256 digest does not match\n", file->path); | |
773 | } | |
774 | ||
775 | // Success | |
776 | r = 0; | |
777 | ||
778 | ERROR: | |
779 | if (sha512_ctx) | |
780 | EVP_MD_CTX_free(sha512_ctx); | |
781 | if (sha256_ctx) | |
782 | EVP_MD_CTX_free(sha256_ctx); | |
783 | if (f) | |
784 | fclose(f); | |
785 | ||
786 | return r; | |
787 | } | |
788 | ||
cf9bd6f4 MT |
789 | /* |
790 | Verify the file - i.e. does the metadata match what is on disk? | |
791 | */ | |
c0b051bb MT |
792 | int pakfire_file_verify(struct pakfire_file* file, int* status) { |
793 | struct stat st; | |
794 | int r; | |
795 | ||
cf9bd6f4 MT |
796 | DEBUG(file->pakfire, "Verifying %s...\n", file->path); |
797 | ||
c0b051bb MT |
798 | // stat() the file |
799 | r = lstat(file->abspath, &st); | |
800 | if (r) { | |
801 | // File does not exist | |
802 | if (errno == ENOENT) { | |
803 | file->verify_status |= PAKFIRE_FILE_NOENT; | |
804 | return 1; | |
805 | } | |
806 | ||
807 | // Raise any other errors from stat() | |
808 | return r; | |
809 | } | |
810 | ||
9e09e361 MT |
811 | // Verify mode |
812 | r = pakfire_file_verify_mode(file, &st); | |
813 | if (r) | |
814 | return r; | |
815 | ||
c0b051bb MT |
816 | // Verify size |
817 | r = pakfire_file_verify_size(file, &st); | |
818 | if (r) | |
819 | return r; | |
820 | ||
821 | // Verify ownership | |
822 | r = pakfire_file_verify_ownership(file, &st); | |
823 | if (r) | |
824 | return r; | |
825 | ||
0eeac4a5 MT |
826 | // Verify timestamps |
827 | r = pakfire_file_verify_timestamps(file, &st); | |
828 | if (r) | |
829 | return r; | |
830 | ||
76011205 MT |
831 | // Verify payload |
832 | r = pakfire_file_verify_payload(file, &st); | |
833 | if (r) | |
834 | return r; | |
835 | ||
cf9bd6f4 MT |
836 | return 0; |
837 | } |