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