#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;
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;
&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),
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),
#include "alloc-util.h"
#include "chase.h"
+#include "chattr-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "hexdecoct.h"
#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;
struct timespec mtime;
uid_t uid;
gid_t gid;
+ unsigned fflags;
XAttr *xattr;
size_t n_xattr;
} OpenInode;
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) {
struct archive_entry *entry,
int parent_fd,
const char *filename,
- const char *path) {
+ const char *path,
+ unsigned fflags) {
int r;
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(
struct archive_entry *entry,
int parent_fd,
const char *filename,
- const char *path) {
+ const char *path,
+ unsigned fflags) {
+
+ int r;
assert(a);
assert(entry);
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);
}
struct timespec *mtime,
uid_t *uid,
gid_t *gid,
+ unsigned *fflags,
XAttr **xa,
size_t *n_xa,
TarFlags flags) {
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;
&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);
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);
&mtime,
&uid,
&gid,
+ &fflags,
&xa,
&n_xa,
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:
.mtime = mtime,
.uid = uid,
.gid = gid,
+ .fflags = fflags,
.xattr = TAKE_PTR(xa),
.n_xattr = n_xa,
};
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));