• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

0 of 1 new or added line in 1 file covered. (0.0%)

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.45
/src/shared/copy.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <linux/btrfs.h>
6
#include <stddef.h>
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <sys/file.h>
10
#include <sys/ioctl.h>
11
#include <sys/sendfile.h>
12
#include <sys/xattr.h>
13
#include <unistd.h>
14

15
#include "alloc-util.h"
16
#include "btrfs-util.h"
17
#include "chattr-util.h"
18
#include "copy.h"
19
#include "dirent-util.h"
20
#include "errno-util.h"
21
#include "fd-util.h"
22
#include "fileio.h"
23
#include "fs-util.h"
24
#include "io-util.h"
25
#include "log.h"
26
#include "macro.h"
27
#include "missing_fs.h"
28
#include "missing_syscall.h"
29
#include "mkdir-label.h"
30
#include "mountpoint-util.h"
31
#include "nulstr-util.h"
32
#include "rm-rf.h"
33
#include "selinux-util.h"
34
#include "signal-util.h"
35
#include "stat-util.h"
36
#include "stdio-util.h"
37
#include "string-util.h"
38
#include "strv.h"
39
#include "sync-util.h"
40
#include "time-util.h"
41
#include "tmpfile-util.h"
42
#include "umask-util.h"
43
#include "user-util.h"
44
#include "xattr-util.h"
45

46
/* If we copy via a userspace buffer, size it to 64K */
47
#define COPY_BUFFER_SIZE (64U*U64_KB)
48

49
/* If a byte progress function is specified during copying, never try to copy more than 1M, so that we can
50
 * reasonably call the progress function still */
51
#define PROGRESS_STEP_SIZE (1U*U64_MB)
52

53
/* A safety net for descending recursively into file system trees to copy. On Linux PATH_MAX is 4096, which means the
54
 * deepest valid path one can build is around 2048, which we hence use as a safety net here, to not spin endlessly in
55
 * case of bind mount cycles and suchlike. */
56
#define COPY_DEPTH_MAX 2048U
57

58
static ssize_t try_copy_file_range(
582,601✔
59
                int fd_in, loff_t *off_in,
60
                int fd_out, loff_t *off_out,
61
                size_t len,
62
                unsigned flags) {
63

64
        static int have = -1;
582,601✔
65
        ssize_t r;
582,601✔
66

67
        if (have == 0)
582,601✔
68
                return -ENOSYS;
69

70
        r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
582,601✔
71
        if (have < 0)
582,601✔
72
                have = r >= 0 || errno != ENOSYS;
950✔
73
        if (r < 0)
582,601✔
74
                return -errno;
207,891✔
75

76
        return r;
77
}
78

79
enum {
80
        FD_IS_NO_PIPE,
81
        FD_IS_BLOCKING_PIPE,
82
        FD_IS_NONBLOCKING_PIPE,
83
};
84

85
static int fd_is_nonblock_pipe(int fd) {
274✔
86
        struct stat st;
274✔
87
        int flags;
274✔
88

89
        /* Checks whether the specified file descriptor refers to a pipe, and if so if O_NONBLOCK is set. */
90

91
        if (fstat(fd, &st) < 0)
274✔
UNCOV
92
                return -errno;
×
93

94
        if (!S_ISFIFO(st.st_mode))
274✔
95
                return FD_IS_NO_PIPE;
96

97
        flags = fcntl(fd, F_GETFL);
24✔
98
        if (flags < 0)
24✔
UNCOV
99
                return -errno;
×
100

101
        return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE;
24✔
102
}
103

104
static int look_for_signals(CopyFlags copy_flags) {
1,415,132✔
105
        int r;
1,415,132✔
106

107
        if ((copy_flags & (COPY_SIGINT|COPY_SIGTERM)) == 0)
1,415,132✔
108
                return 0;
109

110
        r = pop_pending_signal(copy_flags & COPY_SIGINT ? SIGINT : 0,
1,136,810✔
111
                               copy_flags & COPY_SIGTERM ? SIGTERM : 0);
112
        if (r < 0)
574,319✔
113
                return r;
114
        if (r != 0)
574,319✔
UNCOV
115
                return log_debug_errno(SYNTHETIC_ERRNO(EINTR),
×
116
                                       "Got %s, cancelling copy operation.", signal_to_string(r));
117

118
        return 0;
119
}
120

121
static int create_hole(int fd, off_t size) {
132✔
122
        off_t offset;
132✔
123
        off_t end;
132✔
124

125
        offset = lseek(fd, 0, SEEK_CUR);
132✔
126
        if (offset < 0)
132✔
UNCOV
127
                return -errno;
×
128

129
        end = lseek(fd, 0, SEEK_END);
132✔
130
        if (end < 0)
132✔
UNCOV
131
                return -errno;
×
132

133
        /* If we're not at the end of the target file, try to punch a hole in the existing space using fallocate(). */
134

135
        if (offset < end &&
170✔
136
            fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, MIN(size, end - offset)) < 0 &&
38✔
137
            !ERRNO_IS_NOT_SUPPORTED(errno))
×
UNCOV
138
                return -errno;
×
139

140
        if (end - offset >= size) {
132✔
141
                /* If we've created the full hole, set the file pointer to the end of the hole we created and exit. */
142
                if (lseek(fd, offset + size, SEEK_SET) < 0)
38✔
UNCOV
143
                        return -errno;
×
144

145
                return 0;
146
        }
147

148
        /* If we haven't created the full hole, use ftruncate() to grow the file (and the hole) to the
149
         * required size and move the file pointer to the end of the file. */
150

151
        size -= end - offset;
94✔
152

153
        if (ftruncate(fd, end + size) < 0)
94✔
UNCOV
154
                return -errno;
×
155

156
        if (lseek(fd, 0, SEEK_END) < 0)
94✔
UNCOV
157
                return -errno;
×
158

159
        return 0;
160
}
161

162
int copy_bytes_full(
398,977✔
163
                int fdf, int fdt,
164
                uint64_t max_bytes,
165
                CopyFlags copy_flags,
166
                void **ret_remains,
167
                size_t *ret_remains_size,
168
                copy_progress_bytes_t progress,
169
                void *userdata) {
170

171
        _cleanup_close_ int fdf_opened = -EBADF, fdt_opened = -EBADF;
398,977✔
172
        bool try_cfr = true, try_sendfile = true, try_splice = true;
398,977✔
173
        uint64_t copied_total = 0;
398,977✔
174
        int r, nonblock_pipe = -1;
398,977✔
175

176
        assert(fdf >= 0);
398,977✔
177
        assert(fdt >= 0);
398,977✔
178
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
398,977✔
179

180
        /* Tries to copy bytes from the file descriptor 'fdf' to 'fdt' in the smartest possible way. Copies a
181
         * maximum of 'max_bytes', which may be specified as UINT64_MAX, in which no maximum is applied.
182
         * Returns negative on error, zero if EOF is hit before the bytes limit is hit and positive
183
         * otherwise. If the copy fails for some reason but we read but didn't yet write some data and
184
         * ret_remains/ret_remains_size is not NULL, then it will be initialized with an allocated buffer
185
         * containing this "remaining" data. Note that these two parameters are initialized with a valid
186
         * buffer only on failure and only if there's actually data already read. Otherwise these parameters
187
         * if non-NULL are set to NULL. */
188

189
        if (ret_remains)
398,977✔
UNCOV
190
                *ret_remains = NULL;
×
191
        if (ret_remains_size)
398,977✔
UNCOV
192
                *ret_remains_size = 0;
×
193

194
        fdf = fd_reopen_condition(fdf, O_CLOEXEC | O_NOCTTY | O_RDONLY, O_PATH, &fdf_opened);
398,977✔
195
        if (fdf < 0)
398,977✔
196
                return fdf;
197
        fdt = fd_reopen_condition(fdt, O_CLOEXEC | O_NOCTTY | O_RDWR, O_PATH, &fdt_opened);
398,977✔
198
        if (fdt < 0)
398,977✔
199
                return fdt;
200

201
        /* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of
202
         * source and destination first. */
203
        if ((copy_flags & COPY_REFLINK)) {
398,977✔
204
                off_t foffset;
397,101✔
205

206
                foffset = lseek(fdf, 0, SEEK_CUR);
397,101✔
207
                if (foffset >= 0) {
397,101✔
208
                        off_t toffset;
397,098✔
209

210
                        toffset = lseek(fdt, 0, SEEK_CUR);
397,098✔
211
                        if (toffset >= 0) {
397,098✔
212

213
                                if (foffset == 0 && toffset == 0 && max_bytes == UINT64_MAX)
397,096✔
214
                                        r = reflink(fdf, fdt); /* full file reflink */
396,922✔
215
                                else
216
                                        r = reflink_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
192✔
217
                                if (r >= 0) {
397,096✔
218
                                        off_t t;
×
UNCOV
219
                                        int ret;
×
220

221
                                        /* This worked, yay! Now — to be fully correct — let's adjust the file pointers */
UNCOV
222
                                        if (max_bytes == UINT64_MAX) {
×
223

224
                                                /* We cloned to the end of the source file, let's position the read
225
                                                 * pointer there, and query it at the same time. */
226
                                                t = lseek(fdf, 0, SEEK_END);
×
227
                                                if (t < 0)
×
228
                                                        return -errno;
×
UNCOV
229
                                                if (t < foffset)
×
230
                                                        return -ESPIPE;
231

232
                                                /* Let's adjust the destination file write pointer by the same number
233
                                                 * of bytes. */
234
                                                t = lseek(fdt, toffset + (t - foffset), SEEK_SET);
×
235
                                                if (t < 0)
×
UNCOV
236
                                                        return -errno;
×
237

238
                                                if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
×
239
                                                        r = fd_verify_linked(fdf);
×
UNCOV
240
                                                        if (r < 0)
×
241
                                                                return r;
242
                                                }
243

244
                                                /* We copied the whole thing, hence hit EOF, return 0. */
245
                                                ret = 0;
246
                                        } else {
247
                                                t = lseek(fdf, foffset + max_bytes, SEEK_SET);
×
248
                                                if (t < 0)
×
UNCOV
249
                                                        return -errno;
×
250

251
                                                t = lseek(fdt, toffset + max_bytes, SEEK_SET);
×
252
                                                if (t < 0)
×
UNCOV
253
                                                        return -errno;
×
254

255
                                                /* We copied only some number of bytes, which worked, but
256
                                                 * this means we didn't hit EOF, return 1. */
257
                                                ret = 1;
258
                                        }
259

260
                                        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
×
261
                                                r = fd_verify_linked(fdf);
×
UNCOV
262
                                                if (r < 0)
×
263
                                                        return r;
264
                                        }
265

UNCOV
266
                                        return ret;
×
267
                                }
268
                        }
269
                }
270
        }
271

272
        for (;;) {
830,507✔
273
                ssize_t n;
830,507✔
274
                size_t m;
830,507✔
275

276
                if (max_bytes <= 0)
830,507✔
277
                        break;
278

279
                r = look_for_signals(copy_flags);
830,438✔
280
                if (r < 0)
830,438✔
281
                        return r;
282

283
                /* sendfile() accepts at most SSIZE_MAX-offset bytes to copy, hence let's subtract how much
284
                 * copied so far from SSIZE_MAX as maximum of what we want to copy. */
285
                if (try_sendfile) {
830,438✔
286
                        assert(copied_total < SSIZE_MAX);
798,180✔
287
                        m = (uint64_t) SSIZE_MAX - copied_total;
798,180✔
288
                } else
289
                        m = SSIZE_MAX;
290

291
                if (max_bytes != UINT64_MAX && m > max_bytes)
830,438✔
292
                        m = max_bytes;
24,400✔
293

294
                if (progress && m > PROGRESS_STEP_SIZE)
830,438✔
295
                        m = PROGRESS_STEP_SIZE;
8,054✔
296

297
                if (copy_flags & COPY_HOLES) {
830,438✔
298
                        off_t c, e;
22,099✔
299

300
                        c = lseek(fdf, 0, SEEK_CUR);
22,099✔
301
                        if (c < 0)
22,099✔
UNCOV
302
                                return -errno;
×
303

304
                        /* To see if we're in a hole, we search for the next data offset. */
305
                        e = lseek(fdf, c, SEEK_DATA);
22,099✔
306
                        if (e < 0 && errno == ENXIO)
22,099✔
307
                                /* If errno == ENXIO, that means we've reached the final hole of the file and
308
                                * that hole isn't followed by more data. */
309
                                e = lseek(fdf, 0, SEEK_END);
11,017✔
310
                        if (e < 0)
11,017✔
UNCOV
311
                                return -errno;
×
312

313
                        /* If we're in a hole (current offset is not a data offset), create a hole of the
314
                         * same size in the target file. */
315
                        if (e > c) {
22,099✔
316
                                /* Make sure our new hole doesn't go over the maximum size we're allowed to copy. */
317
                                n = MIN(max_bytes, (uint64_t) e - c);
132✔
318
                                r = create_hole(fdt, n);
132✔
319
                                if (r < 0)
132✔
320
                                        return r;
321

322
                                /* Make sure holes are taken into account in the maximum size we're supposed to copy. */
323
                                if (max_bytes != UINT64_MAX) {
132✔
324
                                        max_bytes -= n;
5✔
325
                                        if (max_bytes <= 0)
5✔
326
                                                break;
327
                                }
328

329
                                /* Update the size we're supposed to copy in this iteration if needed. */
330
                                if (m > max_bytes)
3✔
331
                                        m = max_bytes;
3✔
332
                        }
333

334
                        c = e; /* Set c to the start of the data segment. */
22,097✔
335

336
                        /* After copying a potential hole, find the end of the data segment by looking for
337
                         * the next hole. If we get ENXIO, we're at EOF. */
338
                        e = lseek(fdf, c, SEEK_HOLE);
22,097✔
339
                        if (e < 0) {
22,097✔
340
                                if (errno == ENXIO)
11,017✔
341
                                        break;
UNCOV
342
                                return -errno;
×
343
                        }
344

345
                        /* SEEK_HOLE modifies the file offset so we need to move back to the initial offset. */
346
                        if (lseek(fdf, c, SEEK_SET) < 0)
11,080✔
UNCOV
347
                                return -errno;
×
348

349
                        /* Make sure we're not copying more than the current data segment. */
350
                        m = MIN(m, (size_t) e - c);
11,080✔
351
                        if (m <= 0)
11,080✔
UNCOV
352
                                continue;
×
353
                }
354

355
                /* First try copy_file_range(), unless we already tried */
356
                if (try_cfr) {
819,419✔
357
                        n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
582,601✔
358
                        if (n < 0) {
582,601✔
359
                                if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF, -EOPNOTSUPP))
207,891✔
UNCOV
360
                                        return n;
×
361

362
                                try_cfr = false;
363
                                /* use fallback below */
364
                        } else if (n == 0) { /* likely EOF */
374,710✔
365

366
                                if (copied_total > 0)
183,302✔
367
                                        break;
368

369
                                /* So, we hit EOF immediately, without having copied a single byte. This
370
                                 * could indicate two things: the file is actually empty, or we are on some
371
                                 * virtual file system such as procfs/sysfs where the syscall actually
372
                                 * doesn't work but doesn't return an error. Try to handle that, by falling
373
                                 * back to simple read()s in case we encounter empty files.
374
                                 *
375
                                 * See: https://lwn.net/Articles/846403/ */
376
                                try_cfr = try_sendfile = try_splice = false;
377
                        } else
378
                                /* Success! */
379
                                goto next;
191,408✔
380
                }
381

382
                /* First try sendfile(), unless we already tried */
383
                if (try_sendfile) {
444,709✔
384
                        n = sendfile(fdt, fdf, NULL, m);
412,451✔
385
                        if (n < 0) {
412,451✔
386
                                if (!IN_SET(errno, EINVAL, ENOSYS))
148✔
387
                                        return -errno;
11✔
388

389
                                try_sendfile = false;
390
                                /* use fallback below */
391
                        } else if (n == 0) { /* likely EOF */
412,303✔
392

393
                                if (copied_total > 0)
204,441✔
394
                                        break;
395

396
                                try_sendfile = try_splice = false; /* same logic as above for copy_file_range() */
397
                        } else
398
                                /* Success! */
399
                                goto next;
207,862✔
400
                }
401

402
                /* Then try splice, unless we already tried. */
403
                if (try_splice) {
32,784✔
404

405
                        /* splice()'s asynchronous I/O support is a bit weird. When it encounters a pipe file
406
                         * descriptor, then it will ignore its O_NONBLOCK flag and instead only honour the
407
                         * SPLICE_F_NONBLOCK flag specified in its flag parameter. Let's hide this behaviour
408
                         * here, and check if either of the specified fds are a pipe, and if so, let's pass
409
                         * the flag automatically, depending on O_NONBLOCK being set.
410
                         *
411
                         * Here's a twist though: when we use it to move data between two pipes of which one
412
                         * has O_NONBLOCK set and the other has not, then we have no individual control over
413
                         * O_NONBLOCK behaviour. Hence in that case we can't use splice() and still guarantee
414
                         * systematic O_NONBLOCK behaviour, hence don't. */
415

416
                        if (nonblock_pipe < 0) {
3,435✔
417
                                int a, b;
137✔
418

419
                                /* Check if either of these fds is a pipe, and if so non-blocking or not */
420
                                a = fd_is_nonblock_pipe(fdf);
137✔
421
                                if (a < 0)
137✔
422
                                        return a;
423

424
                                b = fd_is_nonblock_pipe(fdt);
137✔
425
                                if (b < 0)
137✔
426
                                        return b;
427

428
                                if ((a == FD_IS_NO_PIPE && b == FD_IS_NO_PIPE) ||
137✔
429
                                    (a == FD_IS_BLOCKING_PIPE && b == FD_IS_NONBLOCKING_PIPE) ||
24✔
430
                                    (a == FD_IS_NONBLOCKING_PIPE && b == FD_IS_BLOCKING_PIPE))
24✔
431

432
                                        /* splice() only works if one of the fds is a pipe. If neither is,
433
                                         * let's skip this step right-away. As mentioned above, if one of the
434
                                         * two fds refers to a blocking pipe and the other to a non-blocking
435
                                         * pipe, we can't use splice() either, hence don't try either. This
436
                                         * hence means we can only use splice() if either only one of the two
437
                                         * fds is a pipe, or if both are pipes with the same nonblocking flag
438
                                         * setting. */
439

440
                                        try_splice = false;
441
                                else
442
                                        nonblock_pipe = a == FD_IS_NONBLOCKING_PIPE || b == FD_IS_NONBLOCKING_PIPE;
24✔
443
                        }
444
                }
445

446
                if (try_splice) {
3,322✔
447
                        n = splice(fdf, NULL, fdt, NULL, m, nonblock_pipe ? SPLICE_F_NONBLOCK : 0);
6,644✔
448
                        if (n < 0) {
3,322✔
449
                                if (!IN_SET(errno, EINVAL, ENOSYS))
×
UNCOV
450
                                        return -errno;
×
451

452
                                try_splice = false;
453
                                /* use fallback below */
454
                        } else if (n == 0) { /* likely EOF */
3,322✔
455

456
                                if (copied_total > 0)
24✔
457
                                        break;
458

459
                                try_splice = false; /* same logic as above for copy_file_range() + sendfile() */
460
                        } else
461
                                /* Success! */
462
                                goto next;
3,298✔
463
                }
464

465
                /* As a fallback just copy bits by hand */
466
                {
29,872✔
467
                        uint8_t buf[MIN(m, COPY_BUFFER_SIZE)], *p = buf;
29,872✔
468
                        ssize_t z;
29,872✔
469

470
                        n = read(fdf, buf, sizeof buf);
29,872✔
471
                        if (n < 0)
29,872✔
472
                                return -errno;
110✔
473
                        if (n == 0) /* EOF */
29,762✔
474
                                break;
475

476
                        z = (size_t) n;
477
                        do {
28,962✔
478
                                ssize_t k;
28,962✔
479

480
                                k = write(fdt, p, z);
28,962✔
481
                                if (k < 0) {
28,962✔
UNCOV
482
                                        r = -errno;
×
483

484
                                        if (ret_remains) {
×
UNCOV
485
                                                void *copy;
×
486

487
                                                copy = memdup(p, z);
×
UNCOV
488
                                                if (!copy)
×
489
                                                        return -ENOMEM;
490

UNCOV
491
                                                *ret_remains = copy;
×
492
                                        }
493

494
                                        if (ret_remains_size)
×
UNCOV
495
                                                *ret_remains_size = z;
×
496

UNCOV
497
                                        return r;
×
498
                                }
499

500
                                assert(k <= z);
28,962✔
501
                                z -= k;
28,962✔
502
                                p += k;
28,962✔
503
                        } while (z > 0);
28,962✔
504
                }
505

506
        next:
431,530✔
507
                copied_total += n;
431,530✔
508

509
                /* Disable sendfile() in case we are getting too close to it's SSIZE_MAX-offset limit */
510
                if (copied_total > SSIZE_MAX - COPY_BUFFER_SIZE)
431,530✔
UNCOV
511
                        try_sendfile = false;
×
512

513
                if (progress) {
431,530✔
514
                        r = progress(n, userdata);
4,671✔
515
                        if (r < 0)
4,671✔
516
                                return r;
517
                }
518

519
                if (max_bytes != UINT64_MAX) {
431,530✔
520
                        assert(max_bytes >= (uint64_t) n);
24,249✔
521
                        max_bytes -= n;
24,249✔
522
                }
523
        }
524

525
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
398,856✔
526
                r = fd_verify_linked(fdf);
1✔
527
                if (r < 0)
1✔
528
                        return r;
529
        }
530

531
        if (copy_flags & COPY_TRUNCATE) {
398,855✔
532
                off_t off = lseek(fdt, 0, SEEK_CUR);
10,997✔
533
                if (off < 0)
10,997✔
UNCOV
534
                        return -errno;
×
535

536
                if (ftruncate(fdt, off) < 0)
10,997✔
UNCOV
537
                        return -errno;
×
538
        }
539

540
        return max_bytes <= 0; /* return 0 if we hit EOF earlier than the size limit */
398,855✔
541
}
542

543
static int fd_copy_symlink(
138,360✔
544
                int df,
545
                const char *from,
546
                const struct stat *st,
547
                int dt,
548
                const char *to,
549
                uid_t override_uid,
550
                gid_t override_gid,
551
                CopyFlags copy_flags) {
552

553
        _cleanup_free_ char *target = NULL;
138,360✔
554
        int r;
138,360✔
555

556
        assert(st);
138,360✔
557
        assert(to);
138,360✔
558

559
        r = readlinkat_malloc(df, from, &target);
138,360✔
560
        if (r < 0)
138,360✔
561
                return r;
562

563
        if (copy_flags & COPY_MAC_CREATE) {
138,360✔
564
                r = mac_selinux_create_file_prepare_at(dt, to, S_IFLNK);
×
UNCOV
565
                if (r < 0)
×
566
                        return r;
567
        }
568
        r = RET_NERRNO(symlinkat(target, dt, to));
138,360✔
569
        if (copy_flags & COPY_MAC_CREATE)
138,360✔
UNCOV
570
                mac_selinux_create_file_clear();
×
571
        if (r < 0) {
138,360✔
572
                if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
×
UNCOV
573
                        log_notice_errno(r, "Failed to copy symlink%s%s%s, ignoring: %m",
×
574
                                         isempty(from) ? "" : " '",
575
                                         strempty(from),
576
                                         isempty(from) ? "" : "'");
UNCOV
577
                        return 0;
×
578
                }
579

580
                return r;
581
        }
582

583
        if (fchownat(dt, to,
415,080✔
584
                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
138,360✔
585
                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
138,360✔
586
                     AT_SYMLINK_NOFOLLOW) < 0)
UNCOV
587
                r = -errno;
×
588

589
        (void) copy_xattr(df, from, dt, to, copy_flags);
138,360✔
590
        (void) utimensat(dt, to, (struct timespec[]) { st->st_atim, st->st_mtim }, AT_SYMLINK_NOFOLLOW);
138,360✔
591
        return r;
138,360✔
592
}
593

594
/* Encapsulates the database we store potential hardlink targets in */
595
typedef struct HardlinkContext {
596
        int dir_fd;    /* An fd to the directory we use as lookup table. Never AT_FDCWD. Lazily created, when
597
                        * we add the first entry. */
598

599
        /* These two fields are used to create the hardlink repository directory above — via
600
         * mkdirat(parent_fd, subdir) — and are kept so that we can automatically remove the directory again
601
         * when we are done. */
602
        int parent_fd; /* Possibly AT_FDCWD */
603
        char *subdir;
604
} HardlinkContext;
605

606
static int hardlink_context_setup(
16,799✔
607
                HardlinkContext *c,
608
                int dt,
609
                const char *to,
610
                CopyFlags copy_flags) {
611

612
        _cleanup_close_ int dt_copy = -EBADF;
16,799✔
613
        int r;
16,799✔
614

615
        assert(c);
16,799✔
616
        assert(c->dir_fd < 0 && c->dir_fd != AT_FDCWD);
16,799✔
617
        assert(c->parent_fd < 0);
16,799✔
618
        assert(!c->subdir);
16,799✔
619

620
        /* If hardlink recreation is requested we have to maintain a database of inodes that are potential
621
         * hardlink sources. Given that generally disk sizes have to be assumed to be larger than what fits
622
         * into physical RAM we cannot maintain that database in dynamic memory alone. Here we opt to
623
         * maintain it on disk, to simplify things: inside the destination directory we'll maintain a
624
         * temporary directory consisting of hardlinks of every inode we copied that might be subject of
625
         * hardlinks. We can then use that as hardlink source later on. Yes, this means additional disk IO
626
         * but thankfully Linux is optimized for this kind of thing. If this ever becomes a performance
627
         * bottleneck we can certainly place an in-memory hash table in front of this, but for the beginning,
628
         * let's keep things simple, and just use the disk as lookup table for inodes.
629
         *
630
         * Note that this should have zero performance impact as long as .n_link of all files copied remains
631
         * <= 0, because in that case we will not actually allocate the hardlink inode lookup table directory
632
         * on disk (we do so lazily, when the first candidate with .n_link > 1 is seen). This means, in the
633
         * common case where hardlinks are not used at all or only for few files the fact that we store the
634
         * table on disk shouldn't matter perfomance-wise. */
635

636
        if (!FLAGS_SET(copy_flags, COPY_HARDLINKS))
16,799✔
637
                return 0;
638

639
        if (dt == AT_FDCWD)
502✔
640
                dt_copy = AT_FDCWD;
641
        else if (dt < 0)
491✔
642
                return -EBADF;
643
        else {
644
                dt_copy = fcntl(dt, F_DUPFD_CLOEXEC, 3);
491✔
645
                if (dt_copy < 0)
491✔
UNCOV
646
                        return -errno;
×
647
        }
648

649
        r = tempfn_random_child(to, "hardlink", &c->subdir);
502✔
650
        if (r < 0)
502✔
651
                return r;
652

653
        c->parent_fd = TAKE_FD(dt_copy);
502✔
654

655
        /* We don't actually create the directory we keep the table in here, that's done on-demand when the
656
         * first entry is added, using hardlink_context_realize() below. */
657
        return 1;
502✔
658
}
659

660
static int hardlink_context_realize(HardlinkContext *c) {
7,310✔
661
        if (!c)
7,310✔
662
                return 0;
663

664
        if (c->dir_fd >= 0) /* Already realized */
7,310✔
665
                return 1;
666

667
        if (c->parent_fd < 0 && c->parent_fd != AT_FDCWD) /* Not configured */
64✔
668
                return 0;
669

670
        assert(c->subdir);
64✔
671

672
        c->dir_fd = open_mkdir_at(c->parent_fd, c->subdir, O_EXCL|O_CLOEXEC, 0700);
64✔
673
        if (c->dir_fd < 0)
64✔
UNCOV
674
                return c->dir_fd;
×
675

676
        return 1;
677
}
678

679
static void hardlink_context_destroy(HardlinkContext *c) {
68,585✔
680
        int r;
68,585✔
681

682
        assert(c);
68,585✔
683

684
        /* Automatically remove the hardlink lookup table directory again after we are done. This is used via
685
         * _cleanup_() so that we really delete this, even on failure. */
686

687
        if (c->dir_fd >= 0) {
68,585✔
688
                /* <dir_fd> might be have already been used for reading, so we need to rewind it. */
689
                if (lseek(c->dir_fd, 0, SEEK_SET) < 0)
64✔
UNCOV
690
                        log_debug_errno(errno, "Failed to lseek on file descriptor, ignoring: %m");
×
691

692
                r = rm_rf_children(TAKE_FD(c->dir_fd), REMOVE_PHYSICAL, NULL); /* consumes dir_fd in all cases, even on failure */
64✔
693
                if (r < 0)
64✔
UNCOV
694
                        log_debug_errno(r, "Failed to remove hardlink store (%s) contents, ignoring: %m", c->subdir);
×
695

696
                assert(c->parent_fd >= 0 || c->parent_fd == AT_FDCWD);
64✔
697
                assert(c->subdir);
64✔
698

699
                if (unlinkat(c->parent_fd, c->subdir, AT_REMOVEDIR) < 0)
64✔
UNCOV
700
                        log_debug_errno(errno, "Failed to remove hardlink store (%s) directory, ignoring: %m", c->subdir);
×
701
        }
702

703
        assert_cc(AT_FDCWD < 0);
68,585✔
704
        c->parent_fd = safe_close(c->parent_fd);
68,585✔
705

706
        c->subdir = mfree(c->subdir);
68,585✔
707
}
68,585✔
708

709
static int try_hardlink(
416,040✔
710
                HardlinkContext *c,
711
                const struct stat *st,
712
                int dt,
713
                const char *to) {
714

715
        char dev_ino[DECIMAL_STR_MAX(dev_t)*2 + DECIMAL_STR_MAX(uint64_t) + 4];
416,040✔
716

717
        assert(st);
416,040✔
718
        assert(dt >= 0 || dt == AT_FDCWD);
416,040✔
719
        assert(to);
416,040✔
720

721
        if (!c) /* No temporary hardlink directory, don't bother */
416,040✔
722
                return 0;
416,040✔
723

724
        if (st->st_nlink <= 1) /* Source not hardlinked, don't bother */
220,253✔
725
                return 0;
726

727
        if (c->dir_fd < 0) /* not yet realized, hence empty */
24,196✔
728
                return 0;
729

730
        xsprintf(dev_ino, "%u:%u:%" PRIu64, major(st->st_dev), minor(st->st_dev), (uint64_t) st->st_ino);
24,132✔
731
        if (linkat(c->dir_fd, dev_ino, dt, to, 0) < 0)  {
24,132✔
732
                if (errno != ENOENT) /* doesn't exist in store yet */
7,246✔
UNCOV
733
                        log_debug_errno(errno, "Failed to hardlink %s to %s, ignoring: %m", dev_ino, to);
×
734
                return 0;
7,246✔
735
        }
736

737
        return 1;
738
}
739

740
static int memorize_hardlink(
395,696✔
741
                HardlinkContext *c,
742
                const struct stat *st,
743
                int dt,
744
                const char *to) {
745

746
        char dev_ino[DECIMAL_STR_MAX(dev_t)*2 + DECIMAL_STR_MAX(uint64_t) + 4];
395,696✔
747
        int r;
395,696✔
748

749
        assert(st);
395,696✔
750
        assert(dt >= 0 || dt == AT_FDCWD);
395,696✔
751
        assert(to);
395,696✔
752

753
        if (!c) /* No temporary hardlink directory, don't bother */
395,696✔
754
                return 0;
395,696✔
755

756
        if (st->st_nlink <= 1) /* Source not hardlinked, don't bother */
203,366✔
757
                return 0;
758

759
        r = hardlink_context_realize(c); /* Create the hardlink store lazily */
7,310✔
760
        if (r < 0)
7,310✔
761
                return r;
762

763
        xsprintf(dev_ino, "%u:%u:%" PRIu64, major(st->st_dev), minor(st->st_dev), (uint64_t) st->st_ino);
7,310✔
764
        if (linkat(dt, to, c->dir_fd, dev_ino, 0) < 0) {
7,310✔
765
                log_debug_errno(errno, "Failed to hardlink %s to %s, ignoring: %m", to, dev_ino);
×
UNCOV
766
                return 0;
×
767
        }
768

769
        return 1;
770
}
771

772
static int prepare_nocow(int fdf, const char *from, int fdt, unsigned *chattr_mask, unsigned *chattr_flags) {
397,392✔
773
        unsigned attrs = 0;
397,392✔
774
        int r;
397,392✔
775

776
        assert(fdf >= 0 || fdf == AT_FDCWD);
397,392✔
777
        assert(fdt >= 0);
397,392✔
778
        assert(!!chattr_mask == !!chattr_flags);
397,392✔
779

780
        /* If caller explicitly requested NOCOW to be set or unset, let's not interfere. */
781
        if (chattr_mask && FLAGS_SET(*chattr_mask, FS_NOCOW_FL))
397,392✔
782
                return 0;
397,392✔
783

784
        r = read_attr_at(fdf, from, &attrs);
397,392✔
785
        if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r) && r != -ELOOP) /* If the source is a symlink we get ELOOP */
397,392✔
786
                return r;
787

788
        if (FLAGS_SET(attrs, FS_NOCOW_FL)) {
397,358✔
789
                if (chattr_mask && chattr_flags) {
×
790
                        *chattr_mask |= FS_NOCOW_FL;
×
UNCOV
791
                        *chattr_flags |= FS_NOCOW_FL;
×
792
                } else
793
                        /* If the NOCOW flag is set on the source, make the copy NOCOW as well. If the source
794
                         * is not NOCOW, don't do anything in particular with the copy. */
UNCOV
795
                        (void) chattr_fd(fdt, FS_NOCOW_FL, FS_NOCOW_FL);
×
796
        }
797

798
        return 0;
799
}
800

801
static int fd_copy_tree_generic(
802
                int df,
803
                const char *from,
804
                const struct stat *st,
805
                int dt,
806
                const char *to,
807
                dev_t original_device,
808
                unsigned depth_left,
809
                uid_t override_uid,
810
                gid_t override_gid,
811
                CopyFlags copy_flags,
812
                Hashmap *denylist,
813
                Set *subvolumes,
814
                HardlinkContext *hardlink_context,
815
                const char *display_path,
816
                copy_progress_path_t progress_path,
817
                copy_progress_bytes_t progress_bytes,
818
                void *userdata);
819

820
static int fd_copy_regular(
416,039✔
821
                int df,
822
                const char *from,
823
                const struct stat *st,
824
                int dt,
825
                const char *to,
826
                uid_t override_uid,
827
                gid_t override_gid,
828
                CopyFlags copy_flags,
829
                HardlinkContext *hardlink_context,
830
                copy_progress_bytes_t progress,
831
                void *userdata) {
832

833
        _cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
416,039✔
834
        int r, q;
416,039✔
835

836
        assert(st);
416,039✔
837
        assert(to);
416,039✔
838

839
        r = try_hardlink(hardlink_context, st, dt, to);
416,039✔
840
        if (r < 0)
416,039✔
841
                return r;
842
        if (r > 0) /* worked! */
416,039✔
843
                return 0;
844

845
        fdf = xopenat_full(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, XO_REGULAR, 0);
399,153✔
846
        if (fdf < 0)
399,153✔
847
                return fdf;
848

849
        if (copy_flags & COPY_MAC_CREATE) {
399,153✔
850
                r = mac_selinux_create_file_prepare_at(dt, to, S_IFREG);
3,476✔
851
                if (r < 0)
3,476✔
852
                        return r;
853
        }
854
        fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
399,153✔
855
        if (copy_flags & COPY_MAC_CREATE)
399,153✔
856
                mac_selinux_create_file_clear();
3,476✔
857
        if (fdt < 0)
399,153✔
858
                return -errno;
3,458✔
859

860
        r = prepare_nocow(fdf, /*from=*/ NULL, fdt, /*chattr_mask=*/ NULL, /*chattr_flags=*/ NULL);
395,695✔
861
        if (r < 0)
395,695✔
862
                return r;
863

864
        r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata);
395,695✔
865
        if (r < 0)
395,695✔
UNCOV
866
                goto fail;
×
867

868
        if (fchown(fdt,
1,186,733✔
869
                   uid_is_valid(override_uid) ? override_uid : st->st_uid,
395,695✔
870
                   gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
395,695✔
UNCOV
871
                r = -errno;
×
872

873
        if (fchmod(fdt, st->st_mode & 07777) < 0)
395,695✔
UNCOV
874
                r = -errno;
×
875

876
        (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
395,695✔
877
        (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
395,695✔
878

879
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
395,695✔
880
                r = fd_verify_linked(fdf);
×
UNCOV
881
                if (r < 0)
×
882
                        return r;
883
        }
884

885
        if (copy_flags & COPY_FSYNC) {
395,695✔
886
                if (fsync(fdt) < 0) {
191,978✔
887
                        r = -errno;
×
UNCOV
888
                        goto fail;
×
889
                }
890
        }
891

892
        q = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
395,695✔
893
        if (q < 0) {
395,695✔
894
                r = q;
×
UNCOV
895
                goto fail;
×
896
        }
897

898
        (void) memorize_hardlink(hardlink_context, st, dt, to);
395,695✔
899
        return r;
900

901
fail:
×
902
        (void) unlinkat(dt, to, 0);
×
UNCOV
903
        return r;
×
904
}
905

UNCOV
906
static int fd_copy_fifo(
×
907
                int df,
908
                const char *from,
909
                const struct stat *st,
910
                int dt,
911
                const char *to,
912
                uid_t override_uid,
913
                gid_t override_gid,
914
                CopyFlags copy_flags,
915
                HardlinkContext *hardlink_context) {
UNCOV
916
        int r;
×
917

918
        assert(st);
×
UNCOV
919
        assert(to);
×
920

921
        r = try_hardlink(hardlink_context, st, dt, to);
×
922
        if (r < 0)
×
923
                return r;
×
UNCOV
924
        if (r > 0) /* worked! */
×
925
                return 0;
926

927
        if (copy_flags & COPY_MAC_CREATE) {
×
928
                r = mac_selinux_create_file_prepare_at(dt, to, S_IFIFO);
×
UNCOV
929
                if (r < 0)
×
930
                        return r;
931
        }
932
        r = RET_NERRNO(mkfifoat(dt, to, st->st_mode & 07777));
×
933
        if (copy_flags & COPY_MAC_CREATE)
×
934
                mac_selinux_create_file_clear();
×
935
        if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) {
×
UNCOV
936
                log_notice_errno(r, "Failed to copy fifo%s%s%s, ignoring: %m",
×
937
                                 isempty(from) ? "" : " '",
938
                                 strempty(from),
939
                                 isempty(from) ? "" : "'");
940
                return 0;
×
UNCOV
941
        } else if (r < 0)
×
942
                return r;
943

944
        if (fchownat(dt, to,
×
945
                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
×
UNCOV
946
                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
×
947
                     AT_SYMLINK_NOFOLLOW) < 0)
UNCOV
948
                r = -errno;
×
949

950
        if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
×
UNCOV
951
                r = -errno;
×
952

UNCOV
953
        (void) utimensat(dt, to, (struct timespec[]) { st->st_atim, st->st_mtim }, AT_SYMLINK_NOFOLLOW);
×
954

UNCOV
955
        (void) memorize_hardlink(hardlink_context, st, dt, to);
×
956
        return r;
957
}
958

959
static int fd_copy_node(
1✔
960
                int df,
961
                const char *from,
962
                const struct stat *st,
963
                int dt,
964
                const char *to,
965
                uid_t override_uid,
966
                gid_t override_gid,
967
                CopyFlags copy_flags,
968
                HardlinkContext *hardlink_context) {
969
        int r;
1✔
970

971
        assert(st);
1✔
972
        assert(to);
1✔
973

974
        r = try_hardlink(hardlink_context, st, dt, to);
1✔
975
        if (r < 0)
1✔
976
                return r;
1✔
977
        if (r > 0) /* worked! */
1✔
978
                return 0;
979

980
        if (copy_flags & COPY_MAC_CREATE) {
1✔
981
                r = mac_selinux_create_file_prepare_at(dt, to, st->st_mode & S_IFMT);
×
UNCOV
982
                if (r < 0)
×
983
                        return r;
984
        }
985
        r = RET_NERRNO(mknodat(dt, to, st->st_mode, st->st_rdev));
1✔
986
        if (copy_flags & COPY_MAC_CREATE)
1✔
UNCOV
987
                mac_selinux_create_file_clear();
×
988
        if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) {
1✔
UNCOV
989
                log_notice_errno(r, "Failed to copy node%s%s%s, ignoring: %m",
×
990
                                 isempty(from) ? "" : " '",
991
                                 strempty(from),
992
                                 isempty(from) ? "" : "'");
UNCOV
993
                return 0;
×
994
        } else if (r < 0)
1✔
995
                return r;
996

997
        if (fchownat(dt, to,
3✔
998
                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
1✔
999
                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
1✔
1000
                     AT_SYMLINK_NOFOLLOW) < 0)
UNCOV
1001
                r = -errno;
×
1002

1003
        if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
1✔
UNCOV
1004
                r = -errno;
×
1005

1006
        (void) utimensat(dt, to, (struct timespec[]) { st->st_atim, st->st_mtim }, AT_SYMLINK_NOFOLLOW);
1✔
1007

1008
        (void) memorize_hardlink(hardlink_context, st, dt, to);
1✔
1009
        return r;
1010
}
1011

1012
static int fd_copy_directory(
34,567✔
1013
                int df,
1014
                const char *from,
1015
                const struct stat *st,
1016
                int dt,
1017
                const char *to,
1018
                dev_t original_device,
1019
                unsigned depth_left,
1020
                uid_t override_uid,
1021
                gid_t override_gid,
1022
                CopyFlags copy_flags,
1023
                Hashmap *denylist,
1024
                Set *subvolumes,
1025
                HardlinkContext *hardlink_context,
1026
                const char *display_path,
1027
                copy_progress_path_t progress_path,
1028
                copy_progress_bytes_t progress_bytes,
1029
                void *userdata) {
1030

UNCOV
1031
        _cleanup_(hardlink_context_destroy) HardlinkContext our_hardlink_context = {
×
1032
                .dir_fd = -EBADF,
1033
                .parent_fd = -EBADF,
1034
        };
1035

1036
        _cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
69,134✔
1037
        _cleanup_closedir_ DIR *d = NULL;
34,567✔
1038
        struct stat dt_st;
34,567✔
1039
        bool exists;
34,567✔
1040
        int r;
34,567✔
1041

1042
        assert(st);
34,567✔
1043
        assert(to);
34,567✔
1044

1045
        if (depth_left == 0)
34,567✔
1046
                return -ENAMETOOLONG;
1047

1048
        fdf = xopenat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
34,567✔
1049
        if (fdf < 0)
34,567✔
1050
                return fdf;
1051

1052
        if (!hardlink_context) {
34,567✔
1053
                /* If recreating hardlinks is requested let's set up a context for that now. */
1054
                r = hardlink_context_setup(&our_hardlink_context, dt, to, copy_flags);
16,799✔
1055
                if (r < 0)
16,799✔
1056
                        return r;
1057
                if (r > 0) /* It's enabled and allocated, let's now use the same context for all recursive
16,799✔
1058
                            * invocations from here down */
1059
                        hardlink_context = &our_hardlink_context;
502✔
1060
        }
1061

1062
        d = take_fdopendir(&fdf);
34,567✔
1063
        if (!d)
34,567✔
UNCOV
1064
                return -errno;
×
1065

1066
        r = dir_is_empty_at(dt, to, /* ignore_hidden_or_backup= */ false);
34,567✔
1067
        if (r < 0 && r != -ENOENT)
34,567✔
1068
                return r;
1069
        if ((r > 0 && !(copy_flags & (COPY_MERGE|COPY_MERGE_EMPTY))) || (r == 0 && !FLAGS_SET(copy_flags, COPY_MERGE)))
34,567✔
1070
                return -EEXIST;
1071

1072
        exists = r >= 0;
34,319✔
1073

1074
        fdt = xopenat_lock_full(dt, to,
68,321✔
1075
                                O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|(exists ? 0 : O_CREAT|O_EXCL),
1076
                                (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0)|(set_contains(subvolumes, st) ? XO_SUBVOLUME : 0),
34,319✔
1077
                                st->st_mode & 07777,
34,319✔
1078
                                copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE,
34,319✔
1079
                                LOCK_EX);
1080
        if (fdt < 0)
34,319✔
1081
                return fdt;
1082

1083
        if (exists && FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS) && fstat(fdt, &dt_st) < 0)
34,319✔
UNCOV
1084
                return -errno;
×
1085

1086
        int ret = 0;
34,319✔
1087

1088
        if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) {
34,319✔
1089
                log_debug("%s is in the denylist, not recursing", from ?: "file to copy");
32✔
1090
                goto finish;
32✔
1091
        }
1092

1093
        FOREACH_DIRENT_ALL(de, d, return -errno) {
687,555✔
1094
                const char *child_display_path = NULL;
653,268✔
1095
                _cleanup_free_ char *dp = NULL;
653,268✔
1096
                struct stat buf;
653,268✔
1097

1098
                if (dot_or_dot_dot(de->d_name))
653,268✔
1099
                        continue;
68,574✔
1100

1101
                r = look_for_signals(copy_flags);
584,694✔
1102
                if (r < 0)
584,694✔
1103
                        return r;
1104

1105
                if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
584,694✔
1106
                        RET_GATHER(ret, -errno);
×
UNCOV
1107
                        continue;
×
1108
                }
1109

1110
                if (progress_path) {
584,694✔
1111
                        if (display_path)
4,857✔
1112
                                child_display_path = dp = path_join(display_path, de->d_name);
4,813✔
1113
                        else
1114
                                child_display_path = de->d_name;
1115

1116
                        r = progress_path(child_display_path, &buf, userdata);
4,857✔
1117
                        if (r < 0)
4,857✔
1118
                                return r;
1119
                }
1120

1121
                if (PTR_TO_INT(hashmap_get(denylist, &buf)) == DENY_INODE) {
584,694✔
1122
                        log_debug("%s%s%s is in the denylist, ignoring",
10✔
1123
                                  strempty(from), isempty(from) ? "" : "/", de->d_name);
1124
                        continue;
5✔
1125
                }
1126

1127
                if (S_ISDIR(buf.st_mode)) {
584,689✔
1128
                        /*
1129
                         * Don't descend into directories on other file systems, if this is requested. We do a simple
1130
                         * .st_dev check here, which basically comes for free. Note that we do this check only on
1131
                         * directories, not other kind of file system objects, for two reason:
1132
                         *
1133
                         * • The kernel's overlayfs pseudo file system that overlays multiple real file systems
1134
                         *   propagates the .st_dev field of the file system a file originates from all the way up
1135
                         *   through the stack to stat(). It doesn't do that for directories however. This means that
1136
                         *   comparing .st_dev on non-directories suggests that they all are mount points. To avoid
1137
                         *   confusion we hence avoid relying on this check for regular files.
1138
                         *
1139
                         * • The main reason we do this check at all is to protect ourselves from bind mount cycles,
1140
                         *   where we really want to avoid descending down in all eternity. However the .st_dev check
1141
                         *   is usually not sufficient for this protection anyway, as bind mount cycles from the same
1142
                         *   file system onto itself can't be detected that way. (Note we also do a recursion depth
1143
                         *   check, which is probably the better protection in this regard, which is why
1144
                         *   COPY_SAME_MOUNT is optional).
1145
                         */
1146

1147
                        if (FLAGS_SET(copy_flags, COPY_SAME_MOUNT)) {
33,784✔
1148
                                if (buf.st_dev != original_device)
16,689✔
UNCOV
1149
                                        continue;
×
1150

1151
                                r = is_mount_point_at(dirfd(d), de->d_name, 0);
16,689✔
1152
                                if (r < 0)
16,689✔
1153
                                        return r;
1154
                                if (r > 0)
16,689✔
UNCOV
1155
                                        continue;
×
1156
                        }
1157
                }
1158

1159
                r = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device,
584,689✔
1160
                                         depth_left-1, override_uid, override_gid, copy_flags & ~COPY_LOCK_BSD,
584,689✔
1161
                                         denylist, subvolumes, hardlink_context, child_display_path, progress_path,
1162
                                         progress_bytes, userdata);
1163

1164
                if (IN_SET(r, -EINTR, -ENOSPC)) /* Propagate SIGINT/SIGTERM and ENOSPC up instantly */
584,689✔
1165
                        return r;
1166
                if (r == -EEXIST && (copy_flags & COPY_MERGE))
584,689✔
1167
                        r = 0;
1✔
1168
                RET_GATHER(ret, r);
584,689✔
1169
        }
1170

1171
finish:
34,319✔
1172
        if (!exists) {
34,319✔
1173
                if (fchown(fdt,
102,005✔
1174
                           uid_is_valid(override_uid) ? override_uid : st->st_uid,
34,002✔
1175
                           gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
34,002✔
UNCOV
1176
                        RET_GATHER(ret, -errno);
×
1177

1178
                if (fchmod(fdt, st->st_mode & 07777) < 0)
34,002✔
UNCOV
1179
                        RET_GATHER(ret, -errno);
×
1180

1181
                /* Run hardlink context cleanup now because it potentially changes timestamps */
1182
                hardlink_context_destroy(&our_hardlink_context);
34,002✔
1183
                (void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags);
34,002✔
1184
                (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
34,002✔
1185
        } else if (FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS)) {
317✔
1186
                /* Run hardlink context cleanup now because it potentially changes timestamps */
1187
                hardlink_context_destroy(&our_hardlink_context);
16✔
1188
                /* If the directory already exists, make sure the timestamps stay the same as before. */
1189
                (void) futimens(fdt, (struct timespec[]) { dt_st.st_atim, dt_st.st_mtim });
16✔
1190
        }
1191

1192
        if (copy_flags & COPY_FSYNC_FULL) {
34,319✔
1193
                if (fsync(fdt) < 0)
16,184✔
UNCOV
1194
                        return -errno;
×
1195
        }
1196

1197
        if (ret < 0)
34,319✔
1198
                return ret;
1199

1200
        return copy_flags & COPY_LOCK_BSD ? TAKE_FD(fdt) : 0;
34,319✔
1201
}
1202

1203
static int fd_copy_leaf(
554,400✔
1204
                int df,
1205
                const char *from,
1206
                const struct stat *st,
1207
                int dt,
1208
                const char *to,
1209
                uid_t override_uid,
1210
                gid_t override_gid,
1211
                CopyFlags copy_flags,
1212
                HardlinkContext *hardlink_context,
1213
                const char *display_path,
1214
                copy_progress_bytes_t progress_bytes,
1215
                void *userdata) {
1216
        int r;
554,400✔
1217

1218
        if (S_ISREG(st->st_mode))
554,400✔
1219
                r = fd_copy_regular(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, progress_bytes, userdata);
416,039✔
1220
        else if (S_ISLNK(st->st_mode))
138,361✔
1221
                r = fd_copy_symlink(df, from, st, dt, to, override_uid, override_gid, copy_flags);
138,360✔
1222
        else if (S_ISFIFO(st->st_mode))
1✔
UNCOV
1223
                r = fd_copy_fifo(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
×
1224
        else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) || S_ISSOCK(st->st_mode))
1✔
1225
                r = fd_copy_node(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
1✔
1226
        else
1227
                r = -EOPNOTSUPP;
1228

1229
        return r;
554,400✔
1230
}
1231

1232
static int fd_copy_tree_generic(
588,887✔
1233
                int df,
1234
                const char *from,
1235
                const struct stat *st,
1236
                int dt,
1237
                const char *to,
1238
                dev_t original_device,
1239
                unsigned depth_left,
1240
                uid_t override_uid,
1241
                gid_t override_gid,
1242
                CopyFlags copy_flags,
1243
                Hashmap *denylist,
1244
                Set *subvolumes,
1245
                HardlinkContext *hardlink_context,
1246
                const char *display_path,
1247
                copy_progress_path_t progress_path,
1248
                copy_progress_bytes_t progress_bytes,
1249
                void *userdata) {
1250

1251
        int r;
588,887✔
1252

1253
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
588,887✔
1254

1255
        if (S_ISDIR(st->st_mode))
588,887✔
1256
                return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid,
34,492✔
1257
                                         override_gid, copy_flags, denylist, subvolumes, hardlink_context,
1258
                                         display_path, progress_path, progress_bytes, userdata);
1259

1260
        DenyType t = PTR_TO_INT(hashmap_get(denylist, st));
554,395✔
1261
        if (t == DENY_INODE) {
554,395✔
1262
                log_debug("%s is in the denylist, ignoring", from ?: "file to copy");
×
UNCOV
1263
                return 0;
×
1264
        } else if (t == DENY_CONTENTS)
554,395✔
UNCOV
1265
                log_debug("%s is configured to have its contents excluded, but is not a directory", from ?: "file to copy");
×
1266

1267
        r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
554,395✔
1268
        /* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */
1269
        if (r == -EEXIST && (copy_flags & COPY_REPLACE)) {
554,395✔
1270
                /* This codepath is us trying to address an error to copy, if the unlink fails, lets just return the original error. */
1271
                if (unlinkat(dt, to, 0) < 0)
5✔
1272
                        return r;
1273

1274
                r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
5✔
1275
        }
1276

1277
        return r;
1278
}
1279

1280
int copy_tree_at_full(
4,199✔
1281
                int fdf,
1282
                const char *from,
1283
                int fdt,
1284
                const char *to,
1285
                uid_t override_uid,
1286
                gid_t override_gid,
1287
                CopyFlags copy_flags,
1288
                Hashmap *denylist,
1289
                Set *subvolumes,
1290
                copy_progress_path_t progress_path,
1291
                copy_progress_bytes_t progress_bytes,
1292
                void *userdata) {
1293

1294
        struct stat st;
4,199✔
1295
        int r;
4,199✔
1296

1297
        assert(to);
4,199✔
1298
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
4,199✔
1299

1300
        if (fstatat(fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW | (isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
8,398✔
1301
                return -errno;
1✔
1302

1303
        r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid,
4,198✔
1304
                                 override_gid, copy_flags, denylist, subvolumes, NULL, NULL, progress_path,
1305
                                 progress_bytes, userdata);
1306
        if (r < 0)
4,198✔
1307
                return r;
1308

1309
        if (S_ISDIR(st.st_mode) && (copy_flags & COPY_SYNCFS)) {
498✔
1310
                /* If the top-level inode is a directory run syncfs() now. */
1311
                r = syncfs_path(fdt, to);
168✔
1312
                if (r < 0)
168✔
UNCOV
1313
                        return r;
×
1314
        } else if ((copy_flags & (COPY_FSYNC_FULL|COPY_SYNCFS)) != 0) {
330✔
1315
                /* fsync() the parent dir of what we just copied if COPY_FSYNC_FULL is set. Also do this in
1316
                 * case COPY_SYNCFS is set but the top-level inode wasn't actually a directory. We do this so that
1317
                 * COPY_SYNCFS provides reasonable synchronization semantics on any kind of inode: when the
1318
                 * copy operation is done the whole inode — regardless of its type — and all its children
1319
                 * will be synchronized to disk. */
1320
                r = fsync_parent_at(fdt, to);
10✔
1321
                if (r < 0)
10✔
UNCOV
1322
                        return r;
×
1323
        }
1324

1325
        return 0;
1326
}
1327

1328
static int sync_dir_by_flags(int dir_fd, const char *path, CopyFlags copy_flags) {
75✔
1329
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
75✔
1330
        assert(path);
75✔
1331

1332
        if (copy_flags & COPY_SYNCFS)
75✔
UNCOV
1333
                return syncfs_path(dir_fd, path);
×
1334
        if (copy_flags & COPY_FSYNC_FULL)
75✔
UNCOV
1335
                return fsync_parent_at(dir_fd, path);
×
1336

1337
        return 0;
1338
}
1339

1340
int copy_directory_at_full(
75✔
1341
                int dir_fdf,
1342
                const char *from,
1343
                int dir_fdt,
1344
                const char *to,
1345
                CopyFlags copy_flags,
1346
                copy_progress_path_t progress_path,
1347
                copy_progress_bytes_t progress_bytes,
1348
                void *userdata) {
1349

1350
        _cleanup_close_ int fdt = -EBADF;
75✔
1351
        struct stat st;
75✔
1352
        int r;
75✔
1353

1354
        assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
75✔
1355
        assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
75✔
1356
        assert(to);
75✔
1357

1358
        if (fstatat(dir_fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW|(isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
150✔
UNCOV
1359
                return -errno;
×
1360

1361
        r = stat_verify_directory(&st);
75✔
1362
        if (r < 0)
75✔
1363
                return r;
1364

1365
        r = fd_copy_directory(
150✔
1366
                        dir_fdf, from,
1367
                        &st,
1368
                        dir_fdt, to,
1369
                        st.st_dev,
75✔
1370
                        COPY_DEPTH_MAX,
1371
                        UID_INVALID, GID_INVALID,
1372
                        copy_flags,
1373
                        NULL, NULL, NULL, NULL,
1374
                        progress_path,
1375
                        progress_bytes,
1376
                        userdata);
1377
        if (r < 0)
75✔
1378
                return r;
1379

1380
        if (FLAGS_SET(copy_flags, COPY_LOCK_BSD))
75✔
1381
                fdt = r;
1✔
1382

1383
        r = sync_dir_by_flags(dir_fdt, to, copy_flags);
75✔
1384
        if (r < 0)
75✔
1385
                return r;
1386

1387
        return FLAGS_SET(copy_flags, COPY_LOCK_BSD) ? TAKE_FD(fdt) : 0;
75✔
1388
}
1389

1390
int copy_file_fd_at_full(
1,543✔
1391
                int dir_fdf,
1392
                const char *from,
1393
                int fdt,
1394
                CopyFlags copy_flags,
1395
                copy_progress_bytes_t progress_bytes,
1396
                void *userdata) {
1397

1398
        _cleanup_close_ int fdf = -EBADF;
1,543✔
1399
        struct stat st;
1,543✔
1400
        int r;
1,543✔
1401

1402
        assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
1,543✔
1403
        assert(fdt >= 0);
1,543✔
1404
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
1,543✔
1405

1406
        fdf = xopenat_full(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY, XO_REGULAR, 0);
1,543✔
1407
        if (fdf < 0)
1,543✔
1408
                return fdf;
1409

1410
        if (fstat(fdt, &st) < 0)
1,542✔
UNCOV
1411
                return -errno;
×
1412

1413
        r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
1,542✔
1414
        if (r < 0)
1,542✔
1415
                return r;
1416

1417
        /* Make sure to copy file attributes only over if target is a regular
1418
         * file (so that copying a file to /dev/null won't alter the access
1419
         * mode/ownership of that device node...) */
1420
        if (S_ISREG(st.st_mode)) {
1,542✔
1421
                (void) copy_times(fdf, fdt, copy_flags);
1,542✔
1422
                (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
1,542✔
1423
        }
1424

1425
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
1,542✔
1426
                r = fd_verify_linked(fdf);
×
UNCOV
1427
                if (r < 0)
×
1428
                        return r;
1429
        }
1430

1431
        if (copy_flags & COPY_FSYNC_FULL) {
1,542✔
1432
                r = fsync_full(fdt);
×
1433
                if (r < 0)
×
UNCOV
1434
                        return r;
×
1435
        } else if (copy_flags & COPY_FSYNC) {
1,542✔
1436
                if (fsync(fdt) < 0)
×
UNCOV
1437
                        return -errno;
×
1438
        }
1439

1440
        return 0;
1441
}
1442

1443
int copy_file_at_full(
127✔
1444
                int dir_fdf,
1445
                const char *from,
1446
                int dir_fdt,
1447
                const char *to,
1448
                int flags,
1449
                mode_t mode,
1450
                unsigned chattr_flags,
1451
                unsigned chattr_mask,
1452
                CopyFlags copy_flags,
1453
                copy_progress_bytes_t progress_bytes,
1454
                void *userdata) {
1455

1456
        _cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
127✔
1457
        int r;
127✔
1458

1459
        assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
127✔
1460
        assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
127✔
1461
        assert(to);
127✔
1462

1463
        fdf = xopenat_full(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY, XO_REGULAR, 0);
127✔
1464
        if (fdf < 0)
127✔
1465
                return fdf;
1466

1467
        if (mode == MODE_INVALID) {
127✔
1468
                struct stat st;
2✔
1469

1470
                if (fstat(fdf, &st) < 0)
2✔
UNCOV
1471
                        return -errno;
×
1472

1473
                mode = st.st_mode;
2✔
1474
        }
1475

1476
        WITH_UMASK(0000) {
254✔
1477
                fdt = xopenat_lock_full(dir_fdt, to,
254✔
1478
                                        flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
1479
                                        XO_REGULAR | (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0),
127✔
1480
                                        mode,
1481
                                        copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, LOCK_EX);
127✔
1482
                if (fdt < 0)
127✔
UNCOV
1483
                        return fdt;
×
1484
        }
1485

1486
        r = prepare_nocow(fdf, /*from=*/ NULL, fdt, &chattr_mask, &chattr_flags);
127✔
1487
        if (r < 0)
127✔
1488
                return r;
1489

1490
        if ((chattr_mask & CHATTR_EARLY_FL) != 0)
127✔
UNCOV
1491
                (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL);
×
1492

1493
        r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags & ~COPY_LOCK_BSD, NULL, NULL, progress_bytes, userdata);
127✔
1494
        if (r < 0)
127✔
1495
                goto fail;
1✔
1496

1497
        (void) copy_times(fdf, fdt, copy_flags);
126✔
1498
        (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
126✔
1499

1500
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
126✔
1501
                r = fd_verify_linked(fdf);
×
1502
                if (r < 0)
×
UNCOV
1503
                        goto fail;
×
1504
        }
1505

1506
        unsigned nocow = FLAGS_SET(copy_flags, COPY_NOCOW_AFTER) ? FS_NOCOW_FL : 0;
126✔
1507
        if (((chattr_mask & ~CHATTR_EARLY_FL) | nocow) != 0)
126✔
UNCOV
1508
                (void) chattr_fd(fdt, chattr_flags | nocow, (chattr_mask & ~CHATTR_EARLY_FL) | nocow);
×
1509

1510
        if (copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL)) {
126✔
1511
                if (fsync(fdt) < 0) {
×
1512
                        r = -errno;
×
UNCOV
1513
                        goto fail;
×
1514
                }
1515
        }
1516

1517
        if (!FLAGS_SET(copy_flags, COPY_LOCK_BSD)) {
126✔
1518
                r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
125✔
1519
                if (r < 0)
125✔
UNCOV
1520
                        goto fail;
×
1521
        }
1522

1523
        if (copy_flags & COPY_FSYNC_FULL) {
126✔
1524
                r = fsync_parent_at(dir_fdt, to);
×
1525
                if (r < 0)
×
UNCOV
1526
                        goto fail;
×
1527
        }
1528

1529
        return copy_flags & COPY_LOCK_BSD ? TAKE_FD(fdt) : 0;
126✔
1530

1531
fail:
1✔
1532
        /* Only unlink if we definitely are the ones who created the file */
1533
        if (FLAGS_SET(flags, O_EXCL))
1✔
1534
                (void) unlinkat(dir_fdt, to, 0);
1✔
1535

1536
        return r;
1537
}
1538

1539
int copy_file_atomic_at_full(
1,587✔
1540
                int dir_fdf,
1541
                const char *from,
1542
                int dir_fdt,
1543
                const char *to,
1544
                mode_t mode,
1545
                unsigned chattr_flags,
1546
                unsigned chattr_mask,
1547
                CopyFlags copy_flags,
1548
                copy_progress_bytes_t progress_bytes,
1549
                void *userdata) {
1550

UNCOV
1551
        _cleanup_(unlink_and_freep) char *t = NULL;
×
1552
        _cleanup_close_ int fdt = -EBADF;
1,587✔
1553
        int r;
1,587✔
1554

1555
        assert(to);
1,587✔
1556
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
1,587✔
1557

1558
        if (copy_flags & COPY_MAC_CREATE) {
1,587✔
1559
                r = mac_selinux_create_file_prepare_at(dir_fdt, to, S_IFREG);
205✔
1560
                if (r < 0)
205✔
1561
                        return r;
1562
        }
1563
        fdt = open_tmpfile_linkable_at(dir_fdt, to, O_WRONLY|O_CLOEXEC, &t);
1,587✔
1564
        if (copy_flags & COPY_MAC_CREATE)
1,587✔
1565
                mac_selinux_create_file_clear();
205✔
1566
        if (fdt < 0)
1,587✔
1567
                return fdt;
1568

1569
        r = prepare_nocow(dir_fdf, from, fdt, &chattr_mask, &chattr_flags);
1,570✔
1570
        if (r < 0)
1,570✔
1571
                return r;
1572

1573
        if ((chattr_mask & CHATTR_EARLY_FL) != 0)
1,536✔
UNCOV
1574
                (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL);
×
1575

1576
        r = copy_file_fd_at_full(dir_fdf, from, fdt, copy_flags, progress_bytes, userdata);
1,536✔
1577
        if (r < 0)
1,536✔
1578
                return r;
1579

1580
        if (fchmod(fdt, mode) < 0)
1,536✔
UNCOV
1581
                return -errno;
×
1582

1583
        if ((copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL))) {
1,536✔
1584
                /* Sync the file */
1585
                if (fsync(fdt) < 0)
×
UNCOV
1586
                        return -errno;
×
1587
        }
1588

1589
        r = link_tmpfile_at(fdt, dir_fdt, t, to, (copy_flags & COPY_REPLACE) ? LINK_TMPFILE_REPLACE : 0);
1,536✔
1590
        if (r < 0)
1,536✔
1591
                return r;
1592

1593
        t = mfree(t);
1,535✔
1594

1595
        unsigned nocow = FLAGS_SET(copy_flags, COPY_NOCOW_AFTER) ? FS_NOCOW_FL : 0;
1,535✔
1596
        if (((chattr_mask & ~CHATTR_EARLY_FL) | nocow) != 0)
1,535✔
1597
                (void) chattr_fd(fdt, chattr_flags | nocow, (chattr_mask & ~CHATTR_EARLY_FL) | nocow);
1✔
1598

1599
        r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
1,535✔
1600
        if (r < 0)
1,535✔
UNCOV
1601
                goto fail;
×
1602

1603
        if (copy_flags & COPY_FSYNC_FULL) {
1,535✔
1604
                /* Sync the parent directory */
1605
                r = fsync_parent_at(dir_fdt, to);
×
1606
                if (r < 0)
×
UNCOV
1607
                        goto fail;
×
1608
        }
1609

1610
        return 0;
1611

1612
fail:
×
1613
        (void) unlinkat(dir_fdt, to, 0);
×
UNCOV
1614
        return r;
×
1615
}
1616

1617
int copy_times(int fdf, int fdt, CopyFlags flags) {
1,761✔
1618
        struct stat st;
1,761✔
1619

1620
        assert(fdf >= 0);
1,761✔
1621
        assert(fdt >= 0);
1,761✔
1622

1623
        if (fstat(fdf, &st) < 0)
1,761✔
UNCOV
1624
                return -errno;
×
1625

1626
        if (futimens(fdt, (struct timespec[2]) { st.st_atim, st.st_mtim }) < 0)
1,761✔
UNCOV
1627
                return -errno;
×
1628

1629
        if (FLAGS_SET(flags, COPY_CRTIME)) {
1,761✔
1630
                usec_t crtime;
45✔
1631

1632
                if (fd_getcrtime(fdf, &crtime) >= 0)
45✔
1633
                        (void) fd_setcrtime(fdt, crtime);
45✔
1634
        }
1635

1636
        return 0;
1637
}
1638

1639
int copy_access(int fdf, int fdt) {
13✔
1640
        struct stat st;
13✔
1641

1642
        assert(fdf >= 0);
13✔
1643
        assert(fdt >= 0);
13✔
1644

1645
        /* Copies just the access mode (and not the ownership) from fdf to fdt */
1646

1647
        if (fstat(fdf, &st) < 0)
13✔
UNCOV
1648
                return -errno;
×
1649

1650
        return RET_NERRNO(fchmod(fdt, st.st_mode & 07777));
13✔
1651
}
1652

1653
int copy_rights_with_fallback(int fdf, int fdt, const char *patht) {
44✔
1654
        struct stat st;
44✔
1655

1656
        assert(fdf >= 0);
44✔
1657
        assert(fdt >= 0);
44✔
1658

1659
        /* Copies both access mode and ownership from fdf to fdt */
1660

1661
        if (fstat(fdf, &st) < 0)
44✔
UNCOV
1662
                return -errno;
×
1663

1664
        return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid);
44✔
1665
}
1666

1667
int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_flags) {
569,782✔
1668
        _cleanup_free_ char *names = NULL;
569,782✔
1669
        int ret = 0, r;
569,782✔
1670

1671
        r = listxattr_at_malloc(df, from, 0, &names);
569,782✔
1672
        if (r < 0)
569,782✔
1673
                return r;
1674

1675
        NULSTR_FOREACH(p, names) {
569,916✔
1676
                if (!FLAGS_SET(copy_flags, COPY_ALL_XATTRS) && !startswith(p, "user."))
526✔
1677
                        continue;
280✔
1678

1679
                _cleanup_free_ char *value = NULL;
246✔
1680
                size_t value_size;
246✔
1681
                r = getxattr_at_malloc(df, from, p, 0, &value, &value_size);
246✔
1682
                if (r == -ENODATA)
246✔
UNCOV
1683
                        continue; /* gone by now */
×
1684
                if (r < 0)
246✔
UNCOV
1685
                        return r;
×
1686

1687
                RET_GATHER(ret, xsetxattr_full(dt, to, /* at_flags = */ 0, p, value, value_size, /* xattr_flags = */ 0));
246✔
1688
        }
1689

1690
        return ret;
1691
}
1692

1693
int reflink(int infd, int outfd) {
396,970✔
1694
        int r;
396,970✔
1695

1696
        assert(infd >= 0);
396,970✔
1697
        assert(outfd >= 0);
396,970✔
1698

1699
        /* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
1700

1701
        r = fd_verify_regular(outfd);
396,970✔
1702
        if (r < 0)
396,970✔
1703
                return r;
1704

1705
        /* FICLONE was introduced in Linux 4.5 but it uses the same number as BTRFS_IOC_CLONE introduced earlier */
1706

1707
        assert_cc(FICLONE == BTRFS_IOC_CLONE);
396,970✔
1708

1709
        return RET_NERRNO(ioctl(outfd, FICLONE, infd));
396,970✔
1710
}
1711

1712
assert_cc(sizeof(struct file_clone_range) == sizeof(struct btrfs_ioctl_clone_range_args));
1713

1714
int reflink_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
174✔
1715
        struct file_clone_range args = {
174✔
1716
                .src_fd = infd,
1717
                .src_offset = in_offset,
1718
                .src_length = sz,
1719
                .dest_offset = out_offset,
1720
        };
1721
        int r;
174✔
1722

1723
        assert(infd >= 0);
174✔
1724
        assert(outfd >= 0);
174✔
1725

1726
        /* Inside the kernel, FICLONE is identical to FICLONERANGE with offsets and size set to zero, let's
1727
         * simplify things and use the simple ioctl in that case. Also, do the same if the size is
1728
         * UINT64_MAX, which is how we usually encode "everything". */
1729
        if (in_offset == 0 && out_offset == 0 && IN_SET(sz, 0, UINT64_MAX))
174✔
UNCOV
1730
                return reflink(infd, outfd);
×
1731

1732
        r = fd_verify_regular(outfd);
174✔
1733
        if (r < 0)
174✔
1734
                return r;
1735

1736
        assert_cc(FICLONERANGE == BTRFS_IOC_CLONE_RANGE);
163✔
1737

1738
        return RET_NERRNO(ioctl(outfd, FICLONERANGE, &args));
163✔
1739
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc