tar-util: add support for file flags

This commit is contained in:
Lennart Poettering
2025-11-04 14:12:39 +01:00
parent a54f4520f3
commit e1e170feca
3 changed files with 92 additions and 4 deletions
+4
View File
@@ -7,6 +7,7 @@
#if HAVE_LIBARCHIVE
static void *libarchive_dl = NULL;
DLSYM_PROTOTYPE(archive_entry_fflags) = NULL;
DLSYM_PROTOTYPE(archive_entry_filetype) = NULL;
DLSYM_PROTOTYPE(archive_entry_free) = NULL;
DLSYM_PROTOTYPE(archive_entry_gid) = NULL;
@@ -26,6 +27,7 @@ DLSYM_PROTOTYPE(archive_entry_pathname) = NULL;
DLSYM_PROTOTYPE(archive_entry_rdevmajor) = NULL;
DLSYM_PROTOTYPE(archive_entry_rdevminor) = NULL;
DLSYM_PROTOTYPE(archive_entry_set_ctime) = NULL;
DLSYM_PROTOTYPE(archive_entry_set_fflags) = NULL;
DLSYM_PROTOTYPE(archive_entry_set_filetype) = NULL;
DLSYM_PROTOTYPE(archive_entry_set_gid) = NULL;
DLSYM_PROTOTYPE(archive_entry_set_hardlink) = NULL;
@@ -74,6 +76,7 @@ int dlopen_libarchive(void) {
&libarchive_dl,
"libarchive.so.13",
LOG_DEBUG,
DLSYM_ARG(archive_entry_fflags),
DLSYM_ARG(archive_entry_filetype),
DLSYM_ARG(archive_entry_free),
DLSYM_ARG(archive_entry_gid),
@@ -93,6 +96,7 @@ int dlopen_libarchive(void) {
DLSYM_ARG(archive_entry_rdevmajor),
DLSYM_ARG(archive_entry_rdevminor),
DLSYM_ARG(archive_entry_set_ctime),
DLSYM_ARG(archive_entry_set_fflags),
DLSYM_ARG(archive_entry_set_filetype),
DLSYM_ARG(archive_entry_set_gid),
DLSYM_ARG(archive_entry_set_hardlink),
+2
View File
@@ -9,6 +9,7 @@
#include "dlfcn-util.h"
extern DLSYM_PROTOTYPE(archive_entry_fflags);
extern DLSYM_PROTOTYPE(archive_entry_filetype);
extern DLSYM_PROTOTYPE(archive_entry_free);
extern DLSYM_PROTOTYPE(archive_entry_gid);
@@ -22,6 +23,7 @@ extern DLSYM_PROTOTYPE(archive_entry_pathname);
extern DLSYM_PROTOTYPE(archive_entry_rdevmajor);
extern DLSYM_PROTOTYPE(archive_entry_rdevminor);
extern DLSYM_PROTOTYPE(archive_entry_set_ctime);
extern DLSYM_PROTOTYPE(archive_entry_set_fflags);
extern DLSYM_PROTOTYPE(archive_entry_set_filetype);
extern DLSYM_PROTOTYPE(archive_entry_set_gid);
extern DLSYM_PROTOTYPE(archive_entry_set_hardlink);
+86 -4
View File
@@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "chase.h"
#include "chattr-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "hexdecoct.h"
@@ -29,6 +30,15 @@
#define DEPTH_MAX 128U
/* We are a bit conservative with the flags we save/restore in tar files */
#define CHATTR_TAR_FL \
(FS_NOATIME_FL | \
FS_NOCOW_FL | \
FS_PROJINHERIT_FL | \
FS_NODUMP_FL | \
FS_SYNC_FL | \
FS_DIRSYNC_FL)
typedef struct XAttr {
char *name;
struct iovec data;
@@ -44,6 +54,7 @@ typedef struct OpenInode {
struct timespec mtime;
uid_t uid;
gid_t gid;
unsigned fflags;
XAttr *xattr;
size_t n_xattr;
} OpenInode;
@@ -105,6 +116,20 @@ static int open_inode_finalize(OpenInode *of) {
RET_GATHER(r, log_error_errno(k, "Failed to adjust ownership/mode of '%s': %m", of->path));
}
if ((of->fflags & ~CHATTR_EARLY_FL) != 0 && inode_type_can_chattr(of->filetype)) {
k = chattr_full(of->fd,
/* path= */ NULL,
/* value= */ of->fflags,
/* mask= */ of->fflags & ~CHATTR_EARLY_FL,
/* ret_previous= */ NULL,
/* ret_final= */ NULL,
CHATTR_FALLBACK_BITWISE);
if (ERRNO_IS_NEG_NOT_SUPPORTED(k))
log_warning_errno(k, "Failed to apply chattr of '%s', ignoring: %m", of->path);
else if (k < 0)
RET_GATHER(r, log_error_errno(k, "Failed to adjust chattr of '%s': %m", of->path));
}
/* We also adjust the mtime only after leaving a dir, since it might otherwise change again
* because we make modifications inside it */
if (of->mtime.tv_nsec != UTIME_OMIT) {
@@ -155,7 +180,8 @@ static int archive_unpack_regular(
struct archive_entry *entry,
int parent_fd,
const char *filename,
const char *path) {
const char *path,
unsigned fflags) {
int r;
@@ -170,6 +196,22 @@ static int archive_unpack_regular(
if (fd < 0)
return log_error_errno(fd, "Failed to create regular file '%s': %m", path);
if ((fflags & CHATTR_EARLY_FL) != 0) {
r = chattr_full(fd,
/* path= */ NULL,
/* value= */ fflags,
/* mask= */ fflags & CHATTR_EARLY_FL,
/* ret_previous= */ NULL,
/* ret_final= */ NULL,
CHATTR_FALLBACK_BITWISE);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
log_warning_errno(r, "Failed to apply chattr of '%s', ignoring: %m", path);
else if (r < 0) {
log_error_errno(r, "Failed to adjust chattr of '%s': %m", path);
goto fail;
}
}
r = sym_archive_read_data_into_fd(a, fd);
if (r != ARCHIVE_OK) {
r = log_error_errno(
@@ -210,7 +252,10 @@ static int archive_unpack_directory(
struct archive_entry *entry,
int parent_fd,
const char *filename,
const char *path) {
const char *path,
unsigned fflags) {
int r;
assert(a);
assert(entry);
@@ -226,6 +271,20 @@ static int archive_unpack_directory(
if (fd < 0)
return log_error_errno(fd, "Failed to create directory '%s': %m", path);
if ((fflags & CHATTR_EARLY_FL) != 0) {
r = chattr_full(fd,
/* path= */ NULL,
/* value= */ fflags,
/* mask= */ fflags & CHATTR_EARLY_FL,
/* ret_previous= */ NULL,
/* ret_final= */ NULL,
CHATTR_FALLBACK_BITWISE);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
log_warning_errno(r, "Failed to apply chattr of '%s', ignoring: %m", path);
else if (r < 0)
return log_error_errno(r, "Failed to adjust chattr of '%s': %m", path);
}
return TAKE_FD(fd);
}
@@ -334,6 +393,7 @@ static int archive_entry_read_stat(
struct timespec *mtime,
uid_t *uid,
gid_t *gid,
unsigned *fflags,
XAttr **xa,
size_t *n_xa,
TarFlags flags) {
@@ -361,6 +421,12 @@ static int archive_entry_read_stat(
if (gid && sym_archive_entry_gid_is_set(entry))
*gid = sym_archive_entry_gid(entry);
if (fflags) {
unsigned long fs = 0, fc = 0;
sym_archive_entry_fflags(entry, &fs, &fc);
*fflags = (fs & ~fc) & CHATTR_TAR_FL;
}
(void) sym_archive_entry_xattr_reset(entry);
for (;;) {
const char *name = NULL;
@@ -476,6 +542,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
&open_inodes[0].mtime,
&open_inodes[0].uid,
&open_inodes[0].gid,
&open_inodes[0].fflags,
&open_inodes[0].xattr,
&open_inodes[0].n_xattr,
flags);
@@ -546,6 +613,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
struct timespec mtime = { .tv_nsec = UTIME_OMIT };
unsigned fflags = 0;
XAttr *xa = NULL;
size_t n_xa = 0;
CLEANUP_ARRAY(xa, n_xa, xattr_done_many);
@@ -620,6 +688,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
&mtime,
&uid,
&gid,
&fflags,
&xa,
&n_xa,
flags);
@@ -629,11 +698,11 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
switch (filetype) {
case S_IFREG:
fd = archive_unpack_regular(a, entry, parent_fd, e, j);
fd = archive_unpack_regular(a, entry, parent_fd, e, j, fflags);
break;
case S_IFDIR:
fd = archive_unpack_directory(a, entry, parent_fd, e, j);
fd = archive_unpack_directory(a, entry, parent_fd, e, j, fflags);
break;
case S_IFLNK:
@@ -678,6 +747,7 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
.mtime = mtime,
.uid = uid,
.gid = gid,
.fflags = fflags,
.xattr = TAKE_PTR(xa),
.n_xattr = n_xa,
};
@@ -975,6 +1045,18 @@ static int archive_item(
return r;
}
if (inode_type_can_chattr(sx->stx_mode)) {
unsigned f = 0;
r = read_attr_fd(data_fd >= 0 ? data_fd : inode_fd, &f);
if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
return log_error_errno(r, "Failed to read file flags of '%s': %m", path);
f &= CHATTR_TAR_FL;
if (f != 0)
sym_archive_entry_set_fflags(entry, f, /* clear= */ 0);
}
if (sym_archive_write_header(d->archive, entry) != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(d->archive));