]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/create_inode_libarchive.c
mke2fs: use the correct shared library version for libarchive on FreeBSD
[thirdparty/e2fsprogs.git] / misc / create_inode_libarchive.c
CommitLineData
7e3a4f0a
JSMR
1/*
2 * create_inode_libarchive.c --- create an inode from libarchive input
3 *
4 * Copyright (C) 2023 Johannes Schauer Marin Rodrigues <josch@debian.org>
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU library
8 * General Public License, version 2.
9 * %End-Header%
10 */
11
12#define _FILE_OFFSET_BITS 64
13#define _LARGEFILE64_SOURCE 1
14#define _GNU_SOURCE 1
15
16#include "config.h"
17#include <ext2fs/ext2_types.h>
18#include "create_inode.h"
19#include "support/nls-enable.h"
20
21#ifdef HAVE_ARCHIVE_H
22
23/* 64KiB is the minimum blksize to best minimize system call overhead. */
24//#define COPY_FILE_BUFLEN 65536
25//#define COPY_FILE_BUFLEN 1048576
26#define COPY_FILE_BUFLEN 16777216
27
28#include <archive.h>
29#include <archive_entry.h>
30#include <libgen.h>
31#include <locale.h>
32
33static const char *(*dl_archive_entry_hardlink)(struct archive_entry *);
34static const char *(*dl_archive_entry_pathname)(struct archive_entry *);
35static const struct stat *(*dl_archive_entry_stat)(struct archive_entry *);
36static const char *(*dl_archive_entry_symlink)(struct archive_entry *);
37static int (*dl_archive_entry_xattr_count)(struct archive_entry *);
38static int (*dl_archive_entry_xattr_next)(struct archive_entry *, const char **,
39 const void **, size_t *);
40static int (*dl_archive_entry_xattr_reset)(struct archive_entry *);
41static const char *(*dl_archive_error_string)(struct archive *);
42static int (*dl_archive_read_close)(struct archive *);
43static la_ssize_t (*dl_archive_read_data)(struct archive *, void *, size_t);
44static int (*dl_archive_read_free)(struct archive *);
45static struct archive *(*dl_archive_read_new)(void);
46static int (*dl_archive_read_next_header)(struct archive *,
47 struct archive_entry **);
48static int (*dl_archive_read_open_filename)(struct archive *,
49 const char *filename, size_t);
50static int (*dl_archive_read_support_filter_all)(struct archive *);
51static int (*dl_archive_read_support_format_all)(struct archive *);
52
53#ifdef HAVE_DLOPEN
54#include <dlfcn.h>
55
56static void *libarchive_handle;
57
8c012b7b
TT
58#if defined(__FreeBSD__)
59#define LIBARCHIVE_SO "libarchive.so.7"
60#else
61#define LIBARCHIVE_SO "libarchive.so.13"
62#endif
63
7e3a4f0a
JSMR
64static int libarchive_available(void)
65{
66 if (!libarchive_handle) {
8c012b7b 67 libarchive_handle = dlopen(LIBARCHIVE_SO, RTLD_NOW);
7e3a4f0a
JSMR
68 if (!libarchive_handle)
69 return 0;
70
71 dl_archive_entry_hardlink =
72 (const char *(*)(struct archive_entry *))dlsym(
73 libarchive_handle, "archive_entry_hardlink");
74 if (!dl_archive_entry_hardlink)
75 return 0;
76 dl_archive_entry_pathname =
77 (const char *(*)(struct archive_entry *))dlsym(
78 libarchive_handle, "archive_entry_pathname");
79 if (!dl_archive_entry_pathname)
80 return 0;
81 dl_archive_entry_stat =
82 (const struct stat *(*)(struct archive_entry *))dlsym(
83 libarchive_handle, "archive_entry_stat");
84 if (!dl_archive_entry_stat)
85 return 0;
86 dl_archive_entry_symlink =
87 (const char *(*)(struct archive_entry *))dlsym(
88 libarchive_handle, "archive_entry_symlink");
89 if (!dl_archive_entry_symlink)
90 return 0;
91 dl_archive_entry_xattr_count =
92 (int (*)(struct archive_entry *))dlsym(
93 libarchive_handle, "archive_entry_xattr_count");
94 if (!dl_archive_entry_xattr_count)
95 return 0;
96 dl_archive_entry_xattr_next = (int (*)(
97 struct archive_entry *, const char **, const void **,
98 size_t *))dlsym(libarchive_handle,
99 "archive_entry_xattr_next");
100 if (!dl_archive_entry_xattr_next)
101 return 0;
102 dl_archive_entry_xattr_reset =
103 (int (*)(struct archive_entry *))dlsym(
104 libarchive_handle, "archive_entry_xattr_reset");
105 if (!dl_archive_entry_xattr_reset)
106 return 0;
107 dl_archive_error_string =
108 (const char *(*)(struct archive *))dlsym(
109 libarchive_handle, "archive_error_string");
110 if (!dl_archive_error_string)
111 return 0;
112 dl_archive_read_close = (int (*)(struct archive *))dlsym(
113 libarchive_handle, "archive_read_close");
114 if (!dl_archive_read_close)
115 return 0;
116 dl_archive_read_data =
117 (la_ssize_t(*)(struct archive *, void *, size_t))dlsym(
118 libarchive_handle, "archive_read_data");
119 if (!dl_archive_read_data)
120 return 0;
121 dl_archive_read_free = (int (*)(struct archive *))dlsym(
122 libarchive_handle, "archive_read_free");
123 if (!dl_archive_read_free)
124 return 0;
125 dl_archive_read_new = (struct archive * (*)(void))
126 dlsym(libarchive_handle, "archive_read_new");
127 if (!dl_archive_read_new)
128 return 0;
129 dl_archive_read_next_header = (int (*)(struct archive *,
130 struct archive_entry **))
131 dlsym(libarchive_handle, "archive_read_next_header");
132 if (!dl_archive_read_next_header)
133 return 0;
134 dl_archive_read_open_filename =
135 (int (*)(struct archive *, const char *filename,
136 size_t))dlsym(libarchive_handle,
137 "archive_read_open_filename");
138 if (!dl_archive_read_open_filename)
139 return 0;
140 dl_archive_read_support_filter_all =
141 (int (*)(struct archive *))dlsym(
142 libarchive_handle,
143 "archive_read_support_filter_all");
144 if (!dl_archive_read_support_filter_all)
145 return 0;
146 dl_archive_read_support_format_all =
147 (int (*)(struct archive *))dlsym(
148 libarchive_handle,
149 "archive_read_support_format_all");
150 if (!dl_archive_read_support_format_all)
151 return 0;
152 }
153
154 return 1;
155}
156#else
157static int libarchive_available(void)
158{
159 dl_archive_entry_hardlink = archive_entry_hardlink;
160 dl_archive_entry_pathname = archive_entry_pathname;
161 dl_archive_entry_stat = archive_entry_stat;
162 dl_archive_entry_symlink = archive_entry_symlink;
163 dl_archive_entry_xattr_count = archive_entry_xattr_count;
164 dl_archive_entry_xattr_next = archive_entry_xattr_next;
165 dl_archive_entry_xattr_reset = archive_entry_xattr_reset;
166 dl_archive_error_string = archive_error_string;
167 dl_archive_read_close = archive_read_close;
168 dl_archive_read_data = archive_read_data;
169 dl_archive_read_free = archive_read_free;
170 dl_archive_read_new = archive_read_new;
171 dl_archive_read_next_header = archive_read_next_header;
172 dl_archive_read_open_filename = archive_read_open_filename;
173 dl_archive_read_support_filter_all = archive_read_support_filter_all;
174 dl_archive_read_support_format_all = archive_read_support_format_all;
175
176 return 1;
177}
178#endif
179
180static errcode_t __find_path(ext2_filsys fs, ext2_ino_t root, const char *name,
181 ext2_ino_t *inode)
182{
183 errcode_t retval;
184 ext2_ino_t tmpino;
185 char *p, *n, *n2 = strdup(name);
186
187 if (n2 == NULL) {
188 retval = errno;
189 goto out;
190 }
191 n = n2;
192 *inode = root;
193 /* any number of leading slashes */
194 while (*n == '/')
195 n++;
196 while (*n) {
197 /* replace the next slash by a NULL, if any */
198 if ((p = strchr(n, '/')))
199 (*p) = 0;
200 /* find the inode of the next component */
201 retval = ext2fs_lookup(fs, *inode, n, strlen(n), 0, &tmpino);
202 if (retval)
203 goto out;
204 *inode = tmpino;
205 /* continue the search at the character after the slash */
206 if (p)
207 n = p + 1;
208 else
209 break;
210 }
211
212out:
213 free(n2);
214 return retval;
215}
216
217/* Rounds quantity up to a multiple of size. size should be a power of 2 */
218static inline unsigned int __round_up(unsigned int quantity, unsigned int size)
219{
220 return (quantity + (size - 1)) & ~(size - 1);
221}
222
223static int remove_inode(ext2_filsys fs, ext2_ino_t ino)
224{
225 errcode_t ret = 0;
226 struct ext2_inode_large inode;
227
228 memset(&inode, 0, sizeof(inode));
229 ret = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
230 sizeof(inode));
231 if (ret)
232 goto out;
233
234 switch (inode.i_links_count) {
235 case 0:
236 return 0; /* XXX: already done? */
237 case 1:
238 inode.i_links_count--;
239 inode.i_dtime = fs->now ? fs->now : time(0);
240 break;
241 default:
242 inode.i_links_count--;
243 }
244
245 if (inode.i_links_count)
246 goto write_out;
247
248 /* Nobody holds this file; free its blocks! */
249 ret = ext2fs_free_ext_attr(fs, ino, &inode);
250 if (ret)
251 goto write_out;
252
253 if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *)&inode)) {
254 ret = ext2fs_punch(fs, ino, (struct ext2_inode *)&inode, NULL,
255 0, ~0ULL);
256 if (ret)
257 goto write_out;
258 }
259
260 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
261
262write_out:
263 ret = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
264 sizeof(inode));
265 if (ret)
266 goto out;
267out:
268 return ret;
269}
270
271static errcode_t copy_file_chunk_tar(ext2_filsys fs, struct archive *archive,
272 ext2_file_t e2_file, off_t start,
273 off_t end, char *buf, char *zerobuf)
274{
275 off_t off, bpos;
276 ssize_t got, blen;
277 unsigned int written;
278 char *ptr;
279 errcode_t err = 0;
280
281 for (off = start; off < end; off += COPY_FILE_BUFLEN) {
282 got = dl_archive_read_data(archive, buf, COPY_FILE_BUFLEN);
283 if (got < 0) {
284 err = errno;
285 goto fail;
286 }
287 for (bpos = 0, ptr = buf; bpos < got; bpos += fs->blocksize) {
288 blen = fs->blocksize;
289 if (blen > got - bpos)
290 blen = got - bpos;
291 if (memcmp(ptr, zerobuf, blen) == 0) {
292 ptr += blen;
293 continue;
294 }
295 err = ext2fs_file_llseek(e2_file, off + bpos,
296 EXT2_SEEK_SET, NULL);
297 if (err)
298 goto fail;
299 while (blen > 0) {
300 err = ext2fs_file_write(e2_file, ptr, blen,
301 &written);
302 if (err)
303 goto fail;
304 if (written == 0) {
305 err = EIO;
306 goto fail;
307 }
308 blen -= written;
309 ptr += written;
310 }
311 }
312 }
313fail:
314 return err;
315}
316static errcode_t copy_file_tar(ext2_filsys fs, struct archive *archive,
317 const struct stat *statbuf, ext2_ino_t ino)
318{
319 ext2_file_t e2_file;
320 char *buf = NULL, *zerobuf = NULL;
321 errcode_t err, close_err;
322
323 err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
324 if (err)
325 return err;
326
327 err = ext2fs_get_mem(COPY_FILE_BUFLEN, &buf);
328 if (err)
329 goto out;
330
331 err = ext2fs_get_memzero(fs->blocksize, &zerobuf);
332 if (err)
333 goto out;
334
335 err = copy_file_chunk_tar(fs, archive, e2_file, 0, statbuf->st_size,
336 buf, zerobuf);
337out:
338 ext2fs_free_mem(&zerobuf);
339 ext2fs_free_mem(&buf);
340 close_err = ext2fs_file_close(e2_file);
341 if (err == 0)
342 err = close_err;
343 return err;
344}
345
346static errcode_t do_write_internal_tar(ext2_filsys fs, ext2_ino_t cwd,
347 struct archive *archive,
348 const char *dest,
349 const struct stat *statbuf)
350{
351 ext2_ino_t newfile;
352 errcode_t retval;
353 struct ext2_inode inode;
354 char *cp;
355
356 retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
357 if (retval)
358 goto out;
359#ifdef DEBUGFS
360 printf("Allocated inode: %u\n", newfile);
361#endif
362 retval = ext2fs_link(fs, cwd, dest, newfile, EXT2_FT_REG_FILE);
363 if (retval == EXT2_ET_DIR_NO_SPACE) {
364 retval = ext2fs_expand_dir(fs, cwd);
365 if (retval)
366 goto out;
367 retval = ext2fs_link(fs, cwd, dest, newfile, EXT2_FT_REG_FILE);
368 }
369 if (retval)
370 goto out;
371 if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
372 com_err(__func__, 0, "Warning: inode already set");
373 ext2fs_inode_alloc_stats2(fs, newfile, 1, 0);
374 memset(&inode, 0, sizeof(inode));
375 inode.i_mode = (statbuf->st_mode & ~S_IFMT) | LINUX_S_IFREG;
376 inode.i_atime = inode.i_ctime = inode.i_mtime = fs->now ? fs->now :
377 time(0);
378 inode.i_links_count = 1;
379 retval = ext2fs_inode_size_set(fs, &inode, statbuf->st_size);
380 if (retval)
381 goto out;
382 if (ext2fs_has_feature_inline_data(fs->super)) {
383 inode.i_flags |= EXT4_INLINE_DATA_FL;
384 } else if (ext2fs_has_feature_extents(fs->super)) {
385 ext2_extent_handle_t handle;
386
387 inode.i_flags &= ~EXT4_EXTENTS_FL;
388 retval = ext2fs_extent_open2(fs, newfile, &inode, &handle);
389 if (retval)
390 goto out;
391 ext2fs_extent_free(handle);
392 }
393
394 retval = ext2fs_write_new_inode(fs, newfile, &inode);
395 if (retval)
396 goto out;
397 if (inode.i_flags & EXT4_INLINE_DATA_FL) {
398 retval = ext2fs_inline_data_init(fs, newfile);
399 if (retval)
400 goto out;
401 }
402 if (LINUX_S_ISREG(inode.i_mode)) {
403 retval = copy_file_tar(fs, archive, statbuf, newfile);
404 if (retval)
405 goto out;
406 }
407out:
408 return retval;
409}
410
411static errcode_t set_inode_xattr_tar(ext2_filsys fs, ext2_ino_t ino,
412 struct archive_entry *entry)
413{
414 errcode_t retval, close_retval;
415 struct ext2_xattr_handle *handle;
416 ssize_t size;
417 const char *name;
418 const void *value;
419 size_t value_size;
420
421 if (no_copy_xattrs)
422 return 0;
423
424 size = dl_archive_entry_xattr_count(entry);
425 if (size == 0)
426 return 0;
427
428 retval = ext2fs_xattrs_open(fs, ino, &handle);
429 if (retval) {
430 if (retval == EXT2_ET_MISSING_EA_FEATURE)
431 return 0;
432 com_err(__func__, retval, _("while opening inode %u"), ino);
433 return retval;
434 }
435
436 retval = ext2fs_xattrs_read(handle);
437 if (retval) {
438 com_err(__func__, retval,
439 _("while reading xattrs for inode %u"), ino);
440 goto out;
441 }
442
443 dl_archive_entry_xattr_reset(entry);
444 while (dl_archive_entry_xattr_next(entry, &name, &value, &value_size) ==
445 ARCHIVE_OK) {
446 if (strcmp(name, "security.capability") != 0)
447 continue;
448
449 retval = ext2fs_xattr_set(handle, name, value, value_size);
450 if (retval) {
451 com_err(__func__, retval,
452 _("while writing attribute \"%s\" to inode %u"),
453 name, ino);
454 break;
455 }
456 }
457out:
458 close_retval = ext2fs_xattrs_close(&handle);
459 if (close_retval) {
460 com_err(__func__, retval, _("while closing inode %u"), ino);
461 retval = retval ? retval : close_retval;
462 }
463 return retval;
464}
465
466static errcode_t handle_entry(ext2_filsys fs, ext2_ino_t root_ino,
467 ext2_ino_t root, ext2_ino_t dirinode, char *name,
468 struct archive *a, struct archive_entry *entry,
469 const struct stat *st)
470{
471 errcode_t retval = 0;
472 char *ln_target;
473 ext2_ino_t tmpino;
474
475 switch (st->st_mode & S_IFMT) {
476 case S_IFCHR:
477 case S_IFBLK:
478 case S_IFIFO:
479 case S_IFSOCK:
480 retval = do_mknod_internal(fs, dirinode, name, st->st_mode,
481 st->st_rdev);
482 if (retval) {
483 com_err(__func__, retval,
484 _("while creating special file "
485 "\"%s\""),
486 name);
487 return 1;
488 }
489 break;
490 case S_IFLNK:
491 ln_target = calloc(
492 1, __round_up(strlen(dl_archive_entry_symlink(entry)),
493 1024));
494 strcpy(ln_target, dl_archive_entry_symlink(entry));
495 retval = do_symlink_internal(fs, dirinode, name, ln_target,
496 root);
497 free(ln_target);
498 if (retval) {
499 com_err(__func__, retval,
500 _("while writing symlink\"%s\""), name);
501 return 1;
502 }
503 break;
504 case S_IFREG:
505 retval = do_write_internal_tar(fs, dirinode, a, name, st);
506 if (retval) {
507 com_err(__func__, retval,
508 _("while writing file \"%s\""), name);
509 return 1;
510 }
511 break;
512 case S_IFDIR:
513 retval = do_mkdir_internal(fs, dirinode, name, root);
514 if (retval) {
515 com_err(__func__, retval, _("while making dir \"%s\""),
516 name);
517 return 1;
518 }
519 break;
520 default:
521 if (dl_archive_entry_hardlink(entry) != NULL) {
522 if (retval = __find_path(
523 fs, root_ino,
524 dl_archive_entry_hardlink(entry),
525 &tmpino)) {
526 com_err(__func__, retval,
527 _("cannot find hardlink destination \"%s\" "
528 "to create \"%s\""),
529 dl_archive_entry_hardlink(entry), name);
530 return 1;
531 }
532 retval = add_link(fs, dirinode, tmpino, name);
533 if (retval) {
534 com_err(__func__, retval, "while linking %s",
535 name);
536 return 1;
537 }
538 } else {
539 com_err(__func__, 0, _("ignoring entry \"%s\""),
540 dl_archive_entry_pathname(entry));
541 }
542 }
543 return 0;
544}
545#endif
546
547errcode_t __populate_fs_from_tar(ext2_filsys fs, ext2_ino_t root_ino,
548 const char *source_tar, ext2_ino_t root,
549 struct hdlinks_s *hdlinks,
550 struct file_info *target,
551 struct fs_ops_callbacks *fs_callbacks)
552{
553#ifndef HAVE_ARCHIVE_H
554 com_err(__func__, 0,
555 _("you need to compile e2fsprogs with libarchive to "
556 "be able to process tarballs"));
557 return 1;
558#else
559 char *path2, *path3, *dir, *name;
560 unsigned int uid, gid, mode, dir_exists;
561 unsigned long ctime, mtime;
562 struct archive *a;
563 struct archive_entry *entry;
564 errcode_t retval = 0;
565 locale_t archive_locale;
566 locale_t old_locale;
567 ext2_ino_t dirinode, tmpino;
568 const struct stat *st;
569
570 if (!libarchive_available()) {
571 com_err(__func__, 0,
572 _("you need libarchive to be able to process tarballs"));
573 return 1;
574 }
575
576 archive_locale = newlocale(LC_CTYPE_MASK, "", (locale_t)0);
577 old_locale = uselocale(archive_locale);
578 a = dl_archive_read_new();
579 if (a == NULL) {
580 retval = 1;
581 com_err(__func__, retval, _("while creating archive reader"));
582 goto out;
583 }
584 if (dl_archive_read_support_filter_all(a) != ARCHIVE_OK) {
585 retval = 1;
586 com_err(__func__, retval, _("while enabling decompression"));
587 goto out;
588 }
589 if (dl_archive_read_support_format_all(a) != ARCHIVE_OK) {
590 retval = 1;
591 com_err(__func__, retval, _("while enabling reader formats"));
592 goto out;
593 }
594
595 if ((retval = dl_archive_read_open_filename(a, source_tar, 4096))) {
596 com_err(__func__, retval, _("while opening \"%s\""),
597 dl_archive_error_string(a));
598 goto out;
599 }
600
601 for (;;) {
602 retval = dl_archive_read_next_header(a, &entry);
603 if (retval == ARCHIVE_EOF) {
604 retval = 0;
605 break;
606 }
607 if (retval != ARCHIVE_OK) {
608 com_err(__func__, retval,
609 _("cannot read archive header: \"%s\""),
610 dl_archive_error_string(a));
611 goto out;
612 }
613 path2 = strdup(dl_archive_entry_pathname(entry));
614 path3 = strdup(dl_archive_entry_pathname(entry));
615 name = basename(path2);
616 dir = dirname(path3);
617 if (retval = __find_path(fs, root_ino, dir, &dirinode)) {
618 com_err(__func__, retval,
619 _("cannot find directory \"%s\" to create \"%s\""),
620 dir, name);
621 goto out;
622 }
623
624 /*
625 * Did we already create this file as the result of a repeated entry
626 * in the tarball? Delete the existing one (except if it is a
627 * directory) so that it can be re-created by handle_entry().
628 */
629 dir_exists = 0;
630 st = dl_archive_entry_stat(entry);
631 retval = ext2fs_namei(fs, root, dirinode, name, &tmpino);
632 if (!retval) {
633 if ((st->st_mode & S_IFMT) == S_IFDIR) {
634 dir_exists = 1;
635 } else {
636 retval = ext2fs_unlink(fs, dirinode, name,
637 tmpino, 0);
638 if (retval) {
639 com_err(__func__, retval,
640 _("failed to unlink \"%s/%s\""),
641 dir, name);
642 goto out;
643 }
644 retval = remove_inode(fs, tmpino);
645 if (retval) {
646 com_err(__func__, retval,
647 _("failed to remove inode of \"%s/%s\""),
648 dir, name);
649 goto out;
650 }
651 }
652 }
653
654 /*
655 * Create files, directories, symlinks etc referenced by this archive
656 * entry unless this is an already existing directory
657 */
658 if (!dir_exists) {
659 retval = handle_entry(fs, root_ino, root, dirinode,
660 name, a, entry, st);
661 if (retval)
662 goto out;
663 retval =
664 ext2fs_namei(fs, root, dirinode, name, &tmpino);
665 if (retval) {
666 com_err(__func__, retval,
667 _("while looking up \"%s\""), name);
668 goto out;
669 }
670 }
671
672 /* set uid, gid, mode and time for the new (or re-created) inode */
673 retval = set_inode_extra(fs, tmpino, st);
674 if (retval) {
675 com_err(__func__, retval,
676 _("while setting inode for \"%s\""), name);
677 goto out;
678 }
679
680 retval = set_inode_xattr_tar(fs, tmpino, entry);
681 if (retval) {
682 com_err(__func__, retval,
683 _("while setting xattrs for \"%s\""), name);
684 goto out;
685 }
686
687 if (fs_callbacks && fs_callbacks->end_create_new_inode) {
688 retval = fs_callbacks->end_create_new_inode(
689 fs, target->path, name, dirinode, root,
690 st->st_mode & S_IFMT);
691 if (retval)
692 goto out;
693 }
694
695 free(path2);
696 free(path3);
697 }
698
699out:
700 dl_archive_read_close(a);
701 dl_archive_read_free(a);
702 uselocale(old_locale);
703 freelocale(archive_locale);
704 return retval;
705#endif
706}