copy: Add COPY_GRACEFUL_WARN
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 30 Nov 2022 16:01:09 +0000 (17:01 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 1 Dec 2022 09:03:31 +0000 (10:03 +0100)
When copying between filesystems, sometimes the target filesystem
might not support symlinks/fifos/sockets/... and we want to log and
ignore  any failures to copy such files when copying. Let's introduce
a new flag to enable this behavior.

src/shared/copy.c
src/shared/copy.h

index 2b492c38a5af63a2fba1453a2b86b84dd260f7fa..e6265e2c9663f6ce7502483dc1d321629e89c21c 100644 (file)
@@ -486,11 +486,17 @@ static int fd_copy_symlink(
                 if (r < 0)
                         return r;
         }
-        r = symlinkat(target, dt, to);
+        r = RET_NERRNO(symlinkat(target, dt, to));
         if (copy_flags & COPY_MAC_CREATE)
                 mac_selinux_create_file_clear();
-        if (r < 0)
-                return -errno;
+        if (r < 0) {
+                if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
+                        log_notice_errno(r, "Failed to copy symlink '%s', ignoring: %m", from);
+                        return 0;
+                }
+
+                return r;
+        }
 
         if (fchownat(dt, to,
                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
@@ -798,11 +804,17 @@ static int fd_copy_fifo(
                 if (r < 0)
                         return r;
         }
-        r = mkfifoat(dt, to, st->st_mode & 07777);
+        r = RET_NERRNO(mkfifoat(dt, to, st->st_mode & 07777));
         if (copy_flags & COPY_MAC_CREATE)
                 mac_selinux_create_file_clear();
-        if (r < 0)
-                return -errno;
+        if (r < 0) {
+                if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
+                        log_notice_errno(r, "Failed to copy fifo '%s', ignoring: %m", from);
+                        return 0;
+                }
+
+                return r;
+        }
 
         if (fchownat(dt, to,
                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
@@ -846,11 +858,17 @@ static int fd_copy_node(
                 if (r < 0)
                         return r;
         }
-        r = mknodat(dt, to, st->st_mode, st->st_rdev);
+        r = RET_NERRNO(mknodat(dt, to, st->st_mode, st->st_rdev));
         if (copy_flags & COPY_MAC_CREATE)
                 mac_selinux_create_file_clear();
-        if (r < 0)
-                return -errno;
+        if (r < 0) {
+                if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
+                        log_notice_errno(r, "Failed to copy node '%s', ignoring: %m", from);
+                        return 0;
+                }
+
+                return r;
+        }
 
         if (fchownat(dt, to,
                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
index d19361c9a29352ae5cc4517cc7ed9d38ce06324b..1eb6d1ce0532e041cc66f617d5d6468f82e180f2 100644 (file)
 #include "set.h"
 
 typedef enum CopyFlags {
-        COPY_REFLINK     = 1 << 0,  /* Try to reflink */
-        COPY_MERGE       = 1 << 1,  /* Merge existing trees with our new one to copy */
-        COPY_REPLACE     = 1 << 2,  /* Replace an existing file if there's one */
-        COPY_SAME_MOUNT  = 1 << 3,  /* Don't descend recursively into other file systems, across mount point boundaries */
-        COPY_MERGE_EMPTY = 1 << 4,  /* Merge an existing, empty directory with our new tree to copy */
-        COPY_CRTIME      = 1 << 5,  /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
-        COPY_SIGINT      = 1 << 6,  /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
-        COPY_SIGTERM     = 1 << 7,  /* ditto, but for SIGTERM */
-        COPY_MAC_CREATE  = 1 << 8,  /* Create files with the correct MAC label (currently SELinux only) */
-        COPY_HARDLINKS   = 1 << 9,  /* Try to reproduce hard links */
-        COPY_FSYNC       = 1 << 10, /* fsync() after we are done */
-        COPY_FSYNC_FULL  = 1 << 11, /* fsync_full() after we are done */
-        COPY_SYNCFS      = 1 << 12, /* syncfs() the *top-level* dir after we are done */
-        COPY_ALL_XATTRS  = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
-        COPY_HOLES       = 1 << 14, /* Copy holes */
+        COPY_REFLINK       = 1 << 0,  /* Try to reflink */
+        COPY_MERGE         = 1 << 1,  /* Merge existing trees with our new one to copy */
+        COPY_REPLACE       = 1 << 2,  /* Replace an existing file if there's one */
+        COPY_SAME_MOUNT    = 1 << 3,  /* Don't descend recursively into other file systems, across mount point boundaries */
+        COPY_MERGE_EMPTY   = 1 << 4,  /* Merge an existing, empty directory with our new tree to copy */
+        COPY_CRTIME        = 1 << 5,  /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
+        COPY_SIGINT        = 1 << 6,  /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
+        COPY_SIGTERM       = 1 << 7,  /* ditto, but for SIGTERM */
+        COPY_MAC_CREATE    = 1 << 8,  /* Create files with the correct MAC label (currently SELinux only) */
+        COPY_HARDLINKS     = 1 << 9,  /* Try to reproduce hard links */
+        COPY_FSYNC         = 1 << 10, /* fsync() after we are done */
+        COPY_FSYNC_FULL    = 1 << 11, /* fsync_full() after we are done */
+        COPY_SYNCFS        = 1 << 12, /* syncfs() the *top-level* dir after we are done */
+        COPY_ALL_XATTRS    = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
+        COPY_HOLES         = 1 << 14, /* Copy holes */
+        COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
 } CopyFlags;
 
 typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);