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

systemd / systemd / 19020191358

02 Nov 2025 05:04PM UTC coverage: 72.222% (-0.02%) from 72.241%
19020191358

push

github

web-flow
Enhance docs for ukify and direct kernel boots (#39516)

305246 of 422650 relevant lines covered (72.22%)

1085243.28 hits per line

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

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

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

14
#include "alloc-util.h"
15
#include "btrfs.h"
16
#include "chattr-util.h"
17
#include "copy.h"
18
#include "dirent-util.h"
19
#include "errno-util.h"
20
#include "fd-util.h"
21
#include "fileio.h"
22
#include "fs-util.h"
23
#include "hashmap.h"
24
#include "log.h"
25
#include "mountpoint-util.h"
26
#include "nulstr-util.h"
27
#include "path-util.h"
28
#include "rm-rf.h"
29
#include "selinux-util.h"
30
#include "set.h"
31
#include "signal-util.h"
32
#include "stat-util.h"
33
#include "stdio-util.h"
34
#include "string-util.h"
35
#include "sync-util.h"
36
#include "time-util.h"
37
#include "tmpfile-util.h"
38
#include "umask-util.h"
39
#include "user-util.h"
40
#include "xattr-util.h"
41

42
/* If we copy via a userspace buffer, size it to 64K */
43
#define COPY_BUFFER_SIZE (64U*U64_KB)
44

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

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

54
static ssize_t try_copy_file_range(
583,609✔
55
                int fd_in, loff_t *off_in,
56
                int fd_out, loff_t *off_out,
57
                size_t len,
58
                unsigned flags) {
59

60
        static int have = -1;
583,609✔
61
        ssize_t r;
583,609✔
62

63
        if (have == 0)
583,609✔
64
                return -ENOSYS;
65

66
        r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
583,609✔
67
        if (have < 0)
583,609✔
68
                have = r >= 0 || errno != ENOSYS;
1,030✔
69
        if (r < 0)
583,609✔
70
                return -errno;
208,253✔
71

72
        return r;
73
}
74

75
enum {
76
        FD_IS_NO_PIPE,
77
        FD_IS_BLOCKING_PIPE,
78
        FD_IS_NONBLOCKING_PIPE,
79
};
80

81
static int fd_is_nonblock_pipe(int fd) {
334✔
82
        struct stat st;
334✔
83
        int flags;
334✔
84

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

87
        if (fstat(fd, &st) < 0)
334✔
88
                return -errno;
×
89

90
        if (!S_ISFIFO(st.st_mode))
334✔
91
                return FD_IS_NO_PIPE;
92

93
        flags = fcntl(fd, F_GETFL);
24✔
94
        if (flags < 0)
24✔
95
                return -errno;
×
96

97
        return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE;
24✔
98
}
99

100
static int look_for_signals(CopyFlags copy_flags) {
1,422,713✔
101
        int r;
1,422,713✔
102

103
        if ((copy_flags & (COPY_SIGINT|COPY_SIGTERM)) == 0)
1,422,713✔
104
                return 0;
105

106
        r = pop_pending_signal(copy_flags & COPY_SIGINT ? SIGINT : 0,
1,141,150✔
107
                               copy_flags & COPY_SIGTERM ? SIGTERM : 0);
108
        if (r < 0)
576,517✔
109
                return r;
110
        if (r != 0)
576,517✔
111
                return log_debug_errno(SYNTHETIC_ERRNO(EINTR),
×
112
                                       "Got %s, cancelling copy operation.", signal_to_string(r));
113

114
        return 0;
115
}
116

117
static int create_hole(int fd, off_t size) {
149✔
118
        off_t offset;
149✔
119
        off_t end;
149✔
120

121
        offset = lseek(fd, 0, SEEK_CUR);
149✔
122
        if (offset < 0)
149✔
123
                return -errno;
×
124

125
        end = lseek(fd, 0, SEEK_END);
149✔
126
        if (end < 0)
149✔
127
                return -errno;
×
128

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

131
        if (offset < end &&
194✔
132
            fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, MIN(size, end - offset)) < 0 &&
45✔
133
            !ERRNO_IS_NOT_SUPPORTED(errno))
×
134
                return -errno;
×
135

136
        if (end - offset >= size) {
149✔
137
                /* If we've created the full hole, set the file pointer to the end of the hole we created and exit. */
138
                if (lseek(fd, offset + size, SEEK_SET) < 0)
45✔
139
                        return -errno;
×
140

141
                return 0;
142
        }
143

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

147
        size -= end - offset;
104✔
148

149
        if (ftruncate(fd, end + size) < 0)
104✔
150
                return -errno;
×
151

152
        if (lseek(fd, 0, SEEK_END) < 0)
104✔
153
                return -errno;
×
154

155
        return 0;
156
}
157

158
int copy_bytes_full(
399,646✔
159
                int fdf, int fdt,
160
                uint64_t max_bytes,
161
                CopyFlags copy_flags,
162
                void **ret_remains,
163
                size_t *ret_remains_size,
164
                copy_progress_bytes_t progress,
165
                void *userdata) {
166

167
        _cleanup_close_ int fdf_opened = -EBADF, fdt_opened = -EBADF;
399,646✔
168
        bool try_cfr = true, try_sendfile = true, try_splice = true;
399,646✔
169
        uint64_t copied_total = 0;
399,646✔
170
        int r, nonblock_pipe = -1;
399,646✔
171

172
        assert(fdf >= 0);
399,646✔
173
        assert(fdt >= 0);
399,646✔
174
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
399,646✔
175

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

185
        if (ret_remains)
399,646✔
186
                *ret_remains = NULL;
×
187
        if (ret_remains_size)
399,646✔
188
                *ret_remains_size = 0;
×
189

190
        fdf = fd_reopen_condition(fdf, O_CLOEXEC | O_NOCTTY | O_RDONLY, O_PATH, &fdf_opened);
399,646✔
191
        if (fdf < 0)
399,646✔
192
                return fdf;
193
        fdt = fd_reopen_condition(fdt, O_CLOEXEC | O_NOCTTY | O_RDWR, O_PATH, &fdt_opened);
399,646✔
194
        if (fdt < 0)
399,646✔
195
                return fdt;
196

197
        /* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of
198
         * source and destination first. */
199
        if ((copy_flags & COPY_REFLINK)) {
399,646✔
200
                off_t foffset;
397,722✔
201

202
                foffset = lseek(fdf, 0, SEEK_CUR);
397,722✔
203
                if (foffset >= 0) {
397,722✔
204
                        off_t toffset;
397,719✔
205

206
                        toffset = lseek(fdt, 0, SEEK_CUR);
397,719✔
207
                        if (toffset >= 0) {
397,719✔
208

209
                                if (foffset == 0 && toffset == 0 && max_bytes == UINT64_MAX)
397,717✔
210
                                        r = reflink(fdf, fdt); /* full file reflink */
397,542✔
211
                                else
212
                                        r = reflink_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
194✔
213
                                if (r >= 0) {
397,717✔
214
                                        off_t t;
×
215
                                        int ret;
×
216

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

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

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

234
                                                if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
×
235
                                                        r = fd_verify_linked(fdf);
×
236
                                                        if (r < 0)
×
237
                                                                return r;
238
                                                }
239

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

247
                                                t = lseek(fdt, toffset + max_bytes, SEEK_SET);
×
248
                                                if (t < 0)
×
249
                                                        return -errno;
×
250

251
                                                /* We copied only some number of bytes, which worked, but
252
                                                 * this means we didn't hit EOF, return 1. */
253
                                                ret = 1;
254
                                        }
255

256
                                        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
×
257
                                                r = fd_verify_linked(fdf);
×
258
                                                if (r < 0)
×
259
                                                        return r;
260
                                        }
261

262
                                        return ret;
×
263
                                }
264
                        }
265
                }
266
        }
267

268
        usec_t start_timestamp = USEC_INFINITY;
399,646✔
269
        if (progress)
399,646✔
270
                start_timestamp = now(CLOCK_MONOTONIC);
399,646✔
271

272
        for (;;) {
834,192✔
273
                ssize_t n;
834,192✔
274
                size_t m;
834,192✔
275

276
                if (max_bytes <= 0)
834,192✔
277
                        break;
278

279
                r = look_for_signals(copy_flags);
834,123✔
280
                if (r < 0)
834,123✔
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) {
834,123✔
286
                        assert(copied_total < SSIZE_MAX);
800,038✔
287
                        m = (uint64_t) SSIZE_MAX - copied_total;
800,038✔
288
                } else
289
                        m = SSIZE_MAX;
290

291
                if (max_bytes != UINT64_MAX && m > max_bytes)
834,123✔
292
                        m = max_bytes;
26,296✔
293

294
                if (progress && m > PROGRESS_STEP_SIZE)
834,123✔
295
                        m = PROGRESS_STEP_SIZE;
8,593✔
296

297
                if (copy_flags & COPY_HOLES) {
834,123✔
298
                        off_t c, e;
22,136✔
299

300
                        c = lseek(fdf, 0, SEEK_CUR);
22,136✔
301
                        if (c < 0)
22,136✔
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,136✔
306
                        if (e < 0 && errno == ENXIO)
22,136✔
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,025✔
310
                        if (e < 0)
11,025✔
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,136✔
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);
149✔
318
                                r = create_hole(fdt, n);
149✔
319
                                if (r < 0)
149✔
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) {
149✔
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,134✔
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,134✔
339
                        if (e < 0) {
22,134✔
340
                                if (errno == ENXIO)
11,025✔
341
                                        break;
342
                                return -errno;
×
343
                        }
344
                        if (e == c)
11,109✔
345
                                /* Empty data segment? Maybe concurrent hole punching taking place? Or EOF?
346
                                 * Let's figure this out by copying the smallest amount possible */
347
                                m = 1;
348
                        else {
349
                                assert(e > c);
11,109✔
350

351
                                /* SEEK_HOLE modifies the file offset so we need to move back to the initial offset. */
352
                                if (lseek(fdf, c, SEEK_SET) < 0)
11,109✔
353
                                        return -errno;
×
354

355
                                /* Make sure we're not copying more than the current data segment. */
356
                                m = MIN(m, (size_t) e - c);
11,109✔
357
                                assert(m > 0);
11,109✔
358
                        }
359
                }
360

361
                /* First try copy_file_range(), unless we already tried */
362
                if (try_cfr) {
823,096✔
363
                        n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
583,609✔
364
                        if (n < 0) {
583,609✔
365
                                if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF, -EOPNOTSUPP))
208,253✔
366
                                        return n;
×
367

368
                                try_cfr = false;
369
                                /* use fallback below */
370
                        } else if (n == 0) { /* likely EOF */
375,356✔
371

372
                                if (copied_total > 0)
183,606✔
373
                                        break;
374

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

388
                /* First try sendfile(), unless we already tried */
389
                if (try_sendfile) {
447,740✔
390
                        n = sendfile(fdt, fdf, NULL, m);
413,655✔
391
                        if (n < 0) {
413,655✔
392
                                if (!IN_SET(errno, EINVAL, ENOSYS))
178✔
393
                                        return -errno;
11✔
394

395
                                try_sendfile = false;
396
                                /* use fallback below */
397
                        } else if (n == 0) { /* likely EOF */
413,477✔
398

399
                                if (copied_total > 0)
204,768✔
400
                                        break;
401

402
                                try_sendfile = try_splice = false; /* same logic as above for copy_file_range() */
403
                        } else
404
                                /* Success! */
405
                                goto next;
208,709✔
406
                }
407

408
                /* Then try splice, unless we already tried. */
409
                if (try_splice) {
34,641✔
410

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

422
                        if (nonblock_pipe < 0) {
3,237✔
423
                                int a, b;
167✔
424

425
                                /* Check if either of these fds is a pipe, and if so non-blocking or not */
426
                                a = fd_is_nonblock_pipe(fdf);
167✔
427
                                if (a < 0)
167✔
428
                                        return a;
429

430
                                b = fd_is_nonblock_pipe(fdt);
167✔
431
                                if (b < 0)
167✔
432
                                        return b;
433

434
                                if ((a == FD_IS_NO_PIPE && b == FD_IS_NO_PIPE) ||
167✔
435
                                    (a == FD_IS_BLOCKING_PIPE && b == FD_IS_NONBLOCKING_PIPE) ||
24✔
436
                                    (a == FD_IS_NONBLOCKING_PIPE && b == FD_IS_BLOCKING_PIPE))
24✔
437

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

446
                                        try_splice = false;
447
                                else
448
                                        nonblock_pipe = a == FD_IS_NONBLOCKING_PIPE || b == FD_IS_NONBLOCKING_PIPE;
24✔
449
                        }
450
                }
451

452
                if (try_splice) {
3,094✔
453
                        n = splice(fdf, NULL, fdt, NULL, m, nonblock_pipe ? SPLICE_F_NONBLOCK : 0);
6,188✔
454
                        if (n < 0) {
3,094✔
455
                                if (!IN_SET(errno, EINVAL, ENOSYS))
×
456
                                        return -errno;
×
457

458
                                try_splice = false;
459
                                /* use fallback below */
460
                        } else if (n == 0) { /* likely EOF */
3,094✔
461

462
                                if (copied_total > 0)
24✔
463
                                        break;
464

465
                                try_splice = false; /* same logic as above for copy_file_range() + sendfile() */
466
                        } else
467
                                /* Success! */
468
                                goto next;
3,070✔
469
                }
470

471
                /* As a fallback just copy bits by hand */
472
                {
31,957✔
473
                        uint8_t buf[MIN(m, COPY_BUFFER_SIZE)], *p = buf;
31,957✔
474
                        ssize_t z;
31,957✔
475

476
                        n = read(fdf, buf, sizeof buf);
31,957✔
477
                        if (n < 0)
31,957✔
478
                                return -errno;
140✔
479
                        if (n == 0) /* EOF */
31,817✔
480
                                break;
481

482
                        z = (size_t) n;
483
                        do {
31,017✔
484
                                ssize_t k;
31,017✔
485

486
                                k = write(fdt, p, z);
31,017✔
487
                                if (k < 0) {
31,017✔
488
                                        r = -errno;
×
489

490
                                        if (ret_remains) {
×
491
                                                void *copy;
×
492

493
                                                copy = memdup(p, z);
×
494
                                                if (!copy)
×
495
                                                        return -ENOMEM;
496

497
                                                *ret_remains = copy;
×
498
                                        }
499

500
                                        if (ret_remains_size)
×
501
                                                *ret_remains_size = z;
×
502

503
                                        return r;
×
504
                                }
505

506
                                assert(k <= z);
31,017✔
507
                                z -= k;
31,017✔
508
                                p += k;
31,017✔
509
                        } while (z > 0);
31,017✔
510
                }
511

512
        next:
434,546✔
513
                copied_total += n;
434,546✔
514

515
                /* Disable sendfile() in case we are getting too close to it's SSIZE_MAX-offset limit */
516
                if (copied_total > SSIZE_MAX - COPY_BUFFER_SIZE)
434,546✔
517
                        try_sendfile = false;
×
518

519
                if (progress) {
434,546✔
520
                        usec_t t = now(CLOCK_MONOTONIC);
5,205✔
521
                        usec_t d = usec_sub_unsigned(t, start_timestamp);
5,205✔
522
                        uint64_t bps = UINT64_MAX;
5,205✔
523
                        if (d > USEC_PER_SEC * 3U)
5,205✔
524
                                bps = (uint64_t) (copied_total / ((double) d / USEC_PER_SEC));
×
525

526
                        r = progress(n, bps, userdata);
5,205✔
527
                        if (r < 0)
5,205✔
528
                                return r;
529
                }
530

531
                if (max_bytes != UINT64_MAX) {
434,546✔
532
                        assert(max_bytes >= (uint64_t) n);
26,145✔
533
                        max_bytes -= n;
26,145✔
534
                }
535
        }
536

537
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
399,495✔
538
                r = fd_verify_linked(fdf);
1✔
539
                if (r < 0)
1✔
540
                        return r;
541
        }
542

543
        if (copy_flags & COPY_TRUNCATE) {
399,494✔
544
                off_t off = lseek(fdt, 0, SEEK_CUR);
11,004✔
545
                if (off < 0)
11,004✔
546
                        return -errno;
×
547

548
                if (ftruncate(fdt, off) < 0)
11,004✔
549
                        return -errno;
×
550
        }
551

552
        return max_bytes <= 0; /* return 0 if we hit EOF earlier than the size limit */
399,494✔
553
}
554

555
static int fd_copy_symlink(
141,652✔
556
                int df,
557
                const char *from,
558
                const struct stat *st,
559
                int dt,
560
                const char *to,
561
                uid_t override_uid,
562
                gid_t override_gid,
563
                CopyFlags copy_flags) {
564

565
        _cleanup_free_ char *target = NULL;
141,652✔
566
        int r;
141,652✔
567

568
        assert(st);
141,652✔
569
        assert(to);
141,652✔
570

571
        r = readlinkat_malloc(df, from, &target);
141,652✔
572
        if (r < 0)
141,652✔
573
                return r;
574

575
        if (copy_flags & COPY_MAC_CREATE) {
141,652✔
576
                r = mac_selinux_create_file_prepare_at(dt, to, S_IFLNK);
×
577
                if (r < 0)
×
578
                        return r;
579
        }
580
        r = RET_NERRNO(symlinkat(target, dt, to));
141,652✔
581
        if (copy_flags & COPY_MAC_CREATE)
141,652✔
582
                mac_selinux_create_file_clear();
×
583
        if (r < 0) {
141,652✔
584
                if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
×
585
                        log_notice_errno(r, "Failed to copy symlink%s%s%s, ignoring: %m",
×
586
                                         isempty(from) ? "" : " '",
587
                                         strempty(from),
588
                                         isempty(from) ? "" : "'");
589
                        return 0;
×
590
                }
591

592
                return r;
593
        }
594

595
        if (fchownat(dt, to,
424,956✔
596
                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
141,652✔
597
                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
141,652✔
598
                     AT_SYMLINK_NOFOLLOW) < 0)
599
                r = -errno;
×
600

601
        (void) copy_xattr(df, from, dt, to, copy_flags);
141,652✔
602
        (void) utimensat(dt, to, (struct timespec[]) { st->st_atim, st->st_mtim }, AT_SYMLINK_NOFOLLOW);
141,652✔
603
        return r;
141,652✔
604
}
605

606
/* Encapsulates the database we store potential hardlink targets in */
607
typedef struct HardlinkContext {
608
        int dir_fd;    /* An fd to the directory we use as lookup table. Never AT_FDCWD. Lazily created, when
609
                        * we add the first entry. */
610

611
        /* These two fields are used to create the hardlink repository directory above — via
612
         * mkdirat(parent_fd, subdir) — and are kept so that we can automatically remove the directory again
613
         * when we are done. */
614
        int parent_fd; /* Possibly AT_FDCWD */
615
        char *subdir;
616
} HardlinkContext;
617

618
static int hardlink_context_setup(
16,552✔
619
                HardlinkContext *c,
620
                int dt,
621
                const char *to,
622
                CopyFlags copy_flags) {
623

624
        _cleanup_close_ int dt_copy = -EBADF;
16,552✔
625
        int r;
16,552✔
626

627
        assert(c);
16,552✔
628
        assert(c->dir_fd < 0 && c->dir_fd != AT_FDCWD);
16,552✔
629
        assert(c->parent_fd < 0);
16,552✔
630
        assert(!c->subdir);
16,552✔
631

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

648
        if (!FLAGS_SET(copy_flags, COPY_HARDLINKS))
16,552✔
649
                return 0;
650

651
        if (dt == AT_FDCWD)
251✔
652
                dt_copy = AT_FDCWD;
653
        else if (dt < 0)
240✔
654
                return -EBADF;
655
        else {
656
                dt_copy = fcntl(dt, F_DUPFD_CLOEXEC, 3);
240✔
657
                if (dt_copy < 0)
240✔
658
                        return -errno;
×
659
        }
660

661
        if (to)
251✔
662
                r = tempfn_random_child(to, "hardlink", &c->subdir);
251✔
663
        else
664
                r = tempfn_random("hardlink", /* extra= */ NULL, &c->subdir);
×
665
        if (r < 0)
251✔
666
                return r;
667

668
        c->parent_fd = TAKE_FD(dt_copy);
251✔
669

670
        /* We don't actually create the directory we keep the table in here, that's done on-demand when the
671
         * first entry is added, using hardlink_context_realize() below. */
672
        return 1;
251✔
673
}
674

675
static int hardlink_context_realize(HardlinkContext *c) {
7,310✔
676
        if (!c)
7,310✔
677
                return 0;
678

679
        if (c->dir_fd >= 0) /* Already realized */
7,310✔
680
                return 1;
681

682
        if (c->parent_fd < 0 && c->parent_fd != AT_FDCWD) /* Not configured */
64✔
683
                return 0;
684

685
        assert(c->subdir);
64✔
686

687
        c->dir_fd = open_mkdir_at(c->parent_fd, c->subdir, O_EXCL|O_CLOEXEC, 0700);
64✔
688
        if (c->dir_fd < 0)
64✔
689
                return c->dir_fd;
×
690

691
        return 1;
692
}
693

694
static void hardlink_context_destroy(HardlinkContext *c) {
68,338✔
695
        int r;
68,338✔
696

697
        assert(c);
68,338✔
698

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

702
        if (c->dir_fd >= 0) {
68,338✔
703
                /* <dir_fd> might be have already been used for reading, so we need to rewind it. */
704
                if (lseek(c->dir_fd, 0, SEEK_SET) < 0)
64✔
705
                        log_debug_errno(errno, "Failed to lseek on file descriptor, ignoring: %m");
×
706

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

711
                assert(c->parent_fd >= 0 || c->parent_fd == AT_FDCWD);
64✔
712
                assert(c->subdir);
64✔
713

714
                if (unlinkat(c->parent_fd, c->subdir, AT_REMOVEDIR) < 0)
64✔
715
                        log_debug_errno(errno, "Failed to remove hardlink store (%s) directory, ignoring: %m", c->subdir);
×
716
        }
717

718
        assert_cc(AT_FDCWD < 0);
68,338✔
719
        c->parent_fd = safe_close(c->parent_fd);
68,338✔
720

721
        c->subdir = mfree(c->subdir);
68,338✔
722
}
68,338✔
723

724
static int try_hardlink(
416,878✔
725
                HardlinkContext *c,
726
                const struct stat *st,
727
                int dt,
728
                const char *to) {
729

730
        char dev_ino[DECIMAL_STR_MAX(dev_t)*2 + DECIMAL_STR_MAX(uint64_t) + 4];
416,878✔
731

732
        assert(st);
416,878✔
733
        assert(dt >= 0 || dt == AT_FDCWD);
416,878✔
734
        assert(to);
416,878✔
735

736
        if (!c) /* No temporary hardlink directory, don't bother */
416,878✔
737
                return 0;
416,878✔
738

739
        if (st->st_nlink <= 1) /* Source not hardlinked, don't bother */
220,565✔
740
                return 0;
741

742
        if (c->dir_fd < 0) /* not yet realized, hence empty */
24,196✔
743
                return 0;
744

745
        xsprintf(dev_ino, "%u:%u:%" PRIu64, major(st->st_dev), minor(st->st_dev), (uint64_t) st->st_ino);
24,132✔
746
        if (linkat(c->dir_fd, dev_ino, dt, to, 0) < 0)  {
24,132✔
747
                if (errno != ENOENT) /* doesn't exist in store yet */
7,246✔
748
                        log_debug_errno(errno, "Failed to hardlink %s to %s, ignoring: %m", dev_ino, to);
×
749
                return 0;
7,246✔
750
        }
751

752
        return 1;
753
}
754

755
static int memorize_hardlink(
396,284✔
756
                HardlinkContext *c,
757
                const struct stat *st,
758
                int dt,
759
                const char *to) {
760

761
        char dev_ino[DECIMAL_STR_MAX(dev_t)*2 + DECIMAL_STR_MAX(uint64_t) + 4];
396,284✔
762
        int r;
396,284✔
763

764
        assert(st);
396,284✔
765
        assert(dt >= 0 || dt == AT_FDCWD);
396,284✔
766
        assert(to);
396,284✔
767

768
        if (!c) /* No temporary hardlink directory, don't bother */
396,284✔
769
                return 0;
396,284✔
770

771
        if (st->st_nlink <= 1) /* Source not hardlinked, don't bother */
203,678✔
772
                return 0;
773

774
        r = hardlink_context_realize(c); /* Create the hardlink store lazily */
7,310✔
775
        if (r < 0)
7,310✔
776
                return r;
777

778
        xsprintf(dev_ino, "%u:%u:%" PRIu64, major(st->st_dev), minor(st->st_dev), (uint64_t) st->st_ino);
7,310✔
779
        if (linkat(dt, to, c->dir_fd, dev_ino, 0) < 0) {
7,310✔
780
                log_debug_errno(errno, "Failed to hardlink %s to %s, ignoring: %m", to, dev_ino);
×
781
                return 0;
×
782
        }
783

784
        return 1;
785
}
786

787
static int prepare_nocow(int fdf, const char *from, int fdt, unsigned *chattr_mask, unsigned *chattr_flags) {
398,027✔
788
        unsigned attrs = 0;
398,027✔
789
        int r;
398,027✔
790

791
        assert(fdf >= 0 || fdf == AT_FDCWD);
398,027✔
792
        assert(fdt >= 0);
398,027✔
793
        assert(!!chattr_mask == !!chattr_flags);
398,027✔
794

795
        /* If caller explicitly requested NOCOW to be set or unset, let's not interfere. */
796
        if (chattr_mask && FLAGS_SET(*chattr_mask, FS_NOCOW_FL))
398,027✔
797
                return 0;
398,027✔
798

799
        r = read_attr_at(fdf, from, &attrs);
398,027✔
800
        if (r < 0 && !ERRNO_IS_IOCTL_NOT_SUPPORTED(r) && r != -ELOOP) /* If the source is a symlink we get ELOOP */
398,027✔
801
                return r;
802

803
        if (FLAGS_SET(attrs, FS_NOCOW_FL)) {
397,993✔
804
                if (chattr_mask && chattr_flags) {
×
805
                        *chattr_mask |= FS_NOCOW_FL;
×
806
                        *chattr_flags |= FS_NOCOW_FL;
×
807
                } else
808
                        /* If the NOCOW flag is set on the source, make the copy NOCOW as well. If the source
809
                         * is not NOCOW, don't do anything in particular with the copy. */
810
                        (void) chattr_fd(fdt, FS_NOCOW_FL, FS_NOCOW_FL);
×
811
        }
812

813
        return 0;
814
}
815

816
/* Copies fs-verity status.  May re-open fdt to do its job. */
817
static int copy_fs_verity(int fdf, int *fdt) {
×
818
        int r;
×
819

820
        assert(fdf >= 0);
×
821
        assert(fdt);
×
822
        assert(*fdt >= 0);
×
823

824
        r = fd_verify_regular(fdf);
×
825
        if (r < 0)
×
826
                return r;
×
827

828
        struct fsverity_descriptor desc = {};
×
829
        struct fsverity_read_metadata_arg read_arg = {
×
830
                .metadata_type = FS_VERITY_METADATA_TYPE_DESCRIPTOR,
831
                .buf_ptr = (uintptr_t) &desc,
×
832
                .length = sizeof(desc),
833
        };
834

835
        r = ioctl(fdf, FS_IOC_READ_VERITY_METADATA, &read_arg);
×
836
        if (r < 0) {
×
837
                /* ENODATA means that the file doesn't have fs-verity,
838
                 * so the correct thing to do is to do nothing at all. */
839
                if (errno == ENODATA)
×
840
                        return 0;
841
                log_error_errno(errno, "Failed to read fs-verity metadata from source file: %m");
×
842
                /* For cases where fs-verity is unsupported we return a special error code */
843
                if (ERRNO_IS_NOT_SUPPORTED(errno))
×
844
                        return -ESOCKTNOSUPPORT;
845
                return -errno;
×
846
        }
847

848
        /* Make sure that the descriptor is completely initialized */
849
        assert(r == (int) sizeof desc);
×
850

851
        r = fd_verify_regular(*fdt);
×
852
        if (r < 0)
×
853
                return r;
854

855
        /* Okay. We're doing this now. We need to re-open fdt as read-only because
856
         * we can't enable fs-verity while writable file descriptors are outstanding. */
857
        _cleanup_close_ int reopened_fd = -EBADF;
×
858
        r = fd_reopen_condition(*fdt, O_RDONLY|O_CLOEXEC|O_NOCTTY, O_ACCMODE_STRICT|O_PATH, &reopened_fd);
×
859
        if (r < 0)
×
860
                return r;
861
        if (reopened_fd >= 0)
×
862
                close_and_replace(*fdt, reopened_fd);
×
863

864
        struct fsverity_enable_arg enable_arg = {
×
865
                .version = desc.version,
×
866
                .hash_algorithm = desc.hash_algorithm,
×
867
                .block_size = UINT32_C(1) << desc.log_blocksize,
×
868
                .salt_size = desc.salt_size,
×
869
                .salt_ptr = (uintptr_t) &desc.salt,
×
870
        };
871

872
        if (ioctl(*fdt, FS_IOC_ENABLE_VERITY, &enable_arg) < 0) {
×
873
                log_error_errno(errno, "Failed to set fs-verity metadata: %m");
×
874
                /* For cases where fs-verity is unsupported we return a special error code */
875
                if (ERRNO_IS_NOT_SUPPORTED(errno))
×
876
                        return -ESOCKTNOSUPPORT;
877
                return -errno;
×
878
        }
879

880
        return 0;
881
}
882

883
static int fd_copy_tree_generic(
884
                int df,
885
                const char *from,
886
                const struct stat *st,
887
                int dt,
888
                const char *to,
889
                dev_t original_device,
890
                unsigned depth_left,
891
                uid_t override_uid,
892
                gid_t override_gid,
893
                CopyFlags copy_flags,
894
                Hashmap *denylist,
895
                Hashmap *subvolumes,
896
                HardlinkContext *hardlink_context,
897
                const char *display_path,
898
                copy_progress_path_t progress_path,
899
                copy_progress_bytes_t progress_bytes,
900
                void *userdata);
901

902
static int fd_copy_regular(
416,877✔
903
                int df,
904
                const char *from,
905
                const struct stat *st,
906
                int dt,
907
                const char *to,
908
                uid_t override_uid,
909
                gid_t override_gid,
910
                CopyFlags copy_flags,
911
                HardlinkContext *hardlink_context,
912
                copy_progress_bytes_t progress,
913
                void *userdata) {
914

915
        _cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
416,877✔
916
        int r, q;
416,877✔
917

918
        assert(st);
416,877✔
919
        assert(to);
416,877✔
920

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

927
        fdf = xopenat_full(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, XO_REGULAR, 0);
399,991✔
928
        if (fdf < 0)
399,991✔
929
                return fdf;
930

931
        if (copy_flags & COPY_MAC_CREATE) {
399,991✔
932
                r = mac_selinux_create_file_prepare_at(dt, to, S_IFREG);
3,710✔
933
                if (r < 0)
3,710✔
934
                        return r;
935
        }
936
        fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
399,991✔
937
        if (copy_flags & COPY_MAC_CREATE)
399,991✔
938
                mac_selinux_create_file_clear();
3,710✔
939
        if (fdt < 0)
399,991✔
940
                return -errno;
3,708✔
941

942
        r = prepare_nocow(fdf, /*from=*/ NULL, fdt, /*chattr_mask=*/ NULL, /*chattr_flags=*/ NULL);
396,283✔
943
        if (r < 0)
396,283✔
944
                return r;
945

946
        r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata);
396,283✔
947
        if (r < 0)
396,283✔
948
                goto fail;
×
949

950
        if (fchown(fdt,
1,188,501✔
951
                   uid_is_valid(override_uid) ? override_uid : st->st_uid,
396,283✔
952
                   gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
396,283✔
953
                r = -errno;
×
954

955
        if (fchmod(fdt, st->st_mode & 07777) < 0)
396,283✔
956
                r = -errno;
×
957

958
        (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
396,283✔
959
        (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
396,283✔
960

961
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
396,283✔
962
                r = fd_verify_linked(fdf);
×
963
                if (r < 0)
×
964
                        return r;
965
        }
966

967
        /* NB: fs-verity cannot be enabled when a writable file descriptor is outstanding.
968
         * copy_fs_verity() may well re-open 'fdt' as O_RDONLY. All code below this point
969
         * needs to be able to work with a read-only file descriptor. */
970
        if (FLAGS_SET(copy_flags, COPY_PRESERVE_FS_VERITY)) {
396,283✔
971
                r = copy_fs_verity(fdf, &fdt);
×
972
                if (r < 0)
×
973
                        goto fail;
×
974
        }
975

976
        if (copy_flags & COPY_FSYNC) {
396,283✔
977
                if (fsync(fdt) < 0) {
192,258✔
978
                        r = -errno;
×
979
                        goto fail;
×
980
                }
981
        }
982

983
        q = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
396,283✔
984
        if (q < 0) {
396,283✔
985
                r = q;
×
986
                goto fail;
×
987
        }
988

989
        (void) memorize_hardlink(hardlink_context, st, dt, to);
396,283✔
990
        return r;
991

992
fail:
×
993
        (void) unlinkat(dt, to, 0);
×
994
        return r;
×
995
}
996

997
static int fd_copy_fifo(
×
998
                int df,
999
                const char *from,
1000
                const struct stat *st,
1001
                int dt,
1002
                const char *to,
1003
                uid_t override_uid,
1004
                gid_t override_gid,
1005
                CopyFlags copy_flags,
1006
                HardlinkContext *hardlink_context) {
1007
        int r;
×
1008

1009
        assert(st);
×
1010
        assert(to);
×
1011

1012
        r = try_hardlink(hardlink_context, st, dt, to);
×
1013
        if (r < 0)
×
1014
                return r;
×
1015
        if (r > 0) /* worked! */
×
1016
                return 0;
1017

1018
        if (copy_flags & COPY_MAC_CREATE) {
×
1019
                r = mac_selinux_create_file_prepare_at(dt, to, S_IFIFO);
×
1020
                if (r < 0)
×
1021
                        return r;
1022
        }
1023
        r = RET_NERRNO(mkfifoat(dt, to, st->st_mode & 07777));
×
1024
        if (copy_flags & COPY_MAC_CREATE)
×
1025
                mac_selinux_create_file_clear();
×
1026
        if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) {
×
1027
                log_notice_errno(r, "Failed to copy fifo%s%s%s, ignoring: %m",
×
1028
                                 isempty(from) ? "" : " '",
1029
                                 strempty(from),
1030
                                 isempty(from) ? "" : "'");
1031
                return 0;
×
1032
        } else if (r < 0)
×
1033
                return r;
1034

1035
        if (fchownat(dt, to,
×
1036
                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
×
1037
                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
×
1038
                     AT_SYMLINK_NOFOLLOW) < 0)
1039
                r = -errno;
×
1040

1041
        if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
×
1042
                r = -errno;
×
1043

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

1046
        (void) memorize_hardlink(hardlink_context, st, dt, to);
×
1047
        return r;
1048
}
1049

1050
static int fd_copy_node(
1✔
1051
                int df,
1052
                const char *from,
1053
                const struct stat *st,
1054
                int dt,
1055
                const char *to,
1056
                uid_t override_uid,
1057
                gid_t override_gid,
1058
                CopyFlags copy_flags,
1059
                HardlinkContext *hardlink_context) {
1060
        int r;
1✔
1061

1062
        assert(st);
1✔
1063
        assert(to);
1✔
1064

1065
        r = try_hardlink(hardlink_context, st, dt, to);
1✔
1066
        if (r < 0)
1✔
1067
                return r;
1✔
1068
        if (r > 0) /* worked! */
1✔
1069
                return 0;
1070

1071
        if (copy_flags & COPY_MAC_CREATE) {
1✔
1072
                r = mac_selinux_create_file_prepare_at(dt, to, st->st_mode & S_IFMT);
×
1073
                if (r < 0)
×
1074
                        return r;
1075
        }
1076
        r = RET_NERRNO(mknodat(dt, to, st->st_mode, st->st_rdev));
1✔
1077
        if (copy_flags & COPY_MAC_CREATE)
1✔
1078
                mac_selinux_create_file_clear();
×
1079
        if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_NOT_SUPPORTED(r))) {
1✔
1080
                log_notice_errno(r, "Failed to copy node%s%s%s, ignoring: %m",
×
1081
                                 isempty(from) ? "" : " '",
1082
                                 strempty(from),
1083
                                 isempty(from) ? "" : "'");
1084
                return 0;
×
1085
        } else if (r < 0)
1✔
1086
                return r;
1087

1088
        if (fchownat(dt, to,
3✔
1089
                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
1✔
1090
                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
1✔
1091
                     AT_SYMLINK_NOFOLLOW) < 0)
1092
                r = -errno;
×
1093

1094
        if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
1✔
1095
                r = -errno;
×
1096

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

1099
        (void) memorize_hardlink(hardlink_context, st, dt, to);
1✔
1100
        return r;
1101
}
1102

1103
static int fd_copy_directory(
34,320✔
1104
                int df,
1105
                const char *from,
1106
                const struct stat *st,
1107
                int dt,
1108
                const char *to,
1109
                dev_t original_device,
1110
                unsigned depth_left,
1111
                uid_t override_uid,
1112
                gid_t override_gid,
1113
                CopyFlags copy_flags,
1114
                Hashmap *denylist,
1115
                Hashmap *subvolumes,
1116
                HardlinkContext *hardlink_context,
1117
                const char *display_path,
1118
                copy_progress_path_t progress_path,
1119
                copy_progress_bytes_t progress_bytes,
1120
                void *userdata) {
1121

1122
        _cleanup_(hardlink_context_destroy) HardlinkContext our_hardlink_context = {
×
1123
                .dir_fd = -EBADF,
1124
                .parent_fd = -EBADF,
1125
        };
1126

1127
        _cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
68,640✔
1128
        _cleanup_closedir_ DIR *d = NULL;
34,320✔
1129
        struct stat dt_st;
34,320✔
1130
        bool exists;
34,320✔
1131
        int r;
34,320✔
1132

1133
        assert(st);
34,320✔
1134

1135
        if (depth_left == 0)
34,320✔
1136
                return -ENAMETOOLONG;
1137

1138
        fdf = xopenat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
34,320✔
1139
        if (fdf < 0)
34,320✔
1140
                return fdf;
1141

1142
        if (!hardlink_context) {
34,320✔
1143
                /* If recreating hardlinks is requested let's set up a context for that now. */
1144
                r = hardlink_context_setup(&our_hardlink_context, dt, to, copy_flags);
16,552✔
1145
                if (r < 0)
16,552✔
1146
                        return r;
1147
                if (r > 0) /* It's enabled and allocated, let's now use the same context for all recursive
16,552✔
1148
                            * invocations from here down */
1149
                        hardlink_context = &our_hardlink_context;
251✔
1150
        }
1151

1152
        d = take_fdopendir(&fdf);
34,320✔
1153
        if (!d)
34,320✔
1154
                return -errno;
×
1155

1156
        r = dir_is_empty_at(dt, to, /* ignore_hidden_or_backup= */ false);
34,320✔
1157
        if (r < 0 && r != -ENOENT)
34,320✔
1158
                return r;
1159
        if ((r > 0 && !(copy_flags & (COPY_MERGE|COPY_MERGE_EMPTY))) || (r == 0 && !FLAGS_SET(copy_flags, COPY_MERGE)))
34,320✔
1160
                return -EEXIST;
1161

1162
        exists = r >= 0;
34,198✔
1163

1164
        XOpenFlags flags = copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0;
34,198✔
1165
        if (hashmap_contains(subvolumes, st)) {
34,198✔
1166
                flags |= XO_SUBVOLUME;
×
1167
                if ((PTR_TO_INT(hashmap_get(subvolumes, st)) & BTRFS_SUBVOL_NODATACOW))
×
1168
                        flags |= XO_NOCOW;
×
1169
        }
1170

1171
        fdt = xopenat_lock_full(dt, to,
34,198✔
1172
                                O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|(exists ? 0 : O_CREAT|O_EXCL),
1173
                                flags,
1174
                                st->st_mode & 07777,
34,198✔
1175
                                copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE,
34,198✔
1176
                                LOCK_EX);
1177
        if (fdt < 0)
34,198✔
1178
                return fdt;
1179

1180
        if (exists && FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS) && fstat(fdt, &dt_st) < 0)
34,198✔
1181
                return -errno;
×
1182

1183
        int ret = 0;
34,198✔
1184

1185
        if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) {
34,198✔
1186
                log_debug("%s is in the denylist, not recursing", from ?: "file to copy");
32✔
1187
                goto finish;
32✔
1188
        }
1189

1190
        FOREACH_DIRENT_ALL(de, d, return -errno) {
691,088✔
1191
                const char *child_display_path = NULL;
656,922✔
1192
                _cleanup_free_ char *dp = NULL;
656,922✔
1193
                struct stat buf;
656,922✔
1194

1195
                if (dot_or_dot_dot(de->d_name))
656,922✔
1196
                        continue;
68,332✔
1197

1198
                r = look_for_signals(copy_flags);
588,590✔
1199
                if (r < 0)
588,590✔
1200
                        return r;
1201

1202
                if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
588,590✔
1203
                        RET_GATHER(ret, -errno);
×
1204
                        continue;
×
1205
                }
1206

1207
                if (progress_path) {
588,590✔
1208
                        if (display_path)
4,889✔
1209
                                child_display_path = dp = path_join(display_path, de->d_name);
4,845✔
1210
                        else
1211
                                child_display_path = de->d_name;
1212

1213
                        r = progress_path(child_display_path, &buf, userdata);
4,889✔
1214
                        if (r < 0)
4,889✔
1215
                                return r;
1216
                }
1217

1218
                if (PTR_TO_INT(hashmap_get(denylist, &buf)) == DENY_INODE) {
588,590✔
1219
                        log_debug("%s%s%s is in the denylist, ignoring",
10✔
1220
                                  strempty(from), isempty(from) ? "" : "/", de->d_name);
1221
                        continue;
5✔
1222
                }
1223

1224
                if (S_ISDIR(buf.st_mode)) {
588,585✔
1225
                        /*
1226
                         * Don't descend into directories on other file systems, if this is requested. We do a simple
1227
                         * .st_dev check here, which basically comes for free. Note that we do this check only on
1228
                         * directories, not other kind of file system objects, for two reason:
1229
                         *
1230
                         * • The kernel's overlayfs pseudo file system that overlays multiple real file systems
1231
                         *   propagates the .st_dev field of the file system a file originates from all the way up
1232
                         *   through the stack to stat(). It doesn't do that for directories however. This means that
1233
                         *   comparing .st_dev on non-directories suggests that they all are mount points. To avoid
1234
                         *   confusion we hence avoid relying on this check for regular files.
1235
                         *
1236
                         * • The main reason we do this check at all is to protect ourselves from bind mount cycles,
1237
                         *   where we really want to avoid descending down in all eternity. However the .st_dev check
1238
                         *   is usually not sufficient for this protection anyway, as bind mount cycles from the same
1239
                         *   file system onto itself can't be detected that way. (Note we also do a recursion depth
1240
                         *   check, which is probably the better protection in this regard, which is why
1241
                         *   COPY_SAME_MOUNT is optional).
1242
                         */
1243

1244
                        if (FLAGS_SET(copy_flags, COPY_SAME_MOUNT)) {
33,784✔
1245
                                if (buf.st_dev != original_device)
16,689✔
1246
                                        continue;
×
1247

1248
                                r = is_mount_point_at(dirfd(d), de->d_name, 0);
16,689✔
1249
                                if (r < 0)
16,689✔
1250
                                        return r;
1251
                                if (r > 0)
16,689✔
1252
                                        continue;
×
1253
                        }
1254
                }
1255

1256
                r = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device,
588,585✔
1257
                                         depth_left-1, override_uid, override_gid, copy_flags & ~COPY_LOCK_BSD,
588,585✔
1258
                                         denylist, subvolumes, hardlink_context, child_display_path, progress_path,
1259
                                         progress_bytes, userdata);
1260

1261
                /* Propagate SIGINT/SIGTERM, ENOSPC, and fs-verity fails up instantly */
1262
                if (IN_SET(r, -EINTR, -ENOSPC, -ESOCKTNOSUPPORT))
588,585✔
1263
                        return r;
1264
                if (r == -EEXIST && (copy_flags & COPY_MERGE))
588,585✔
1265
                        r = 0;
1✔
1266
                RET_GATHER(ret, r);
588,585✔
1267
        }
1268

1269
finish:
34,198✔
1270
        if (FLAGS_SET(copy_flags, COPY_MERGE_APPLY_STAT) || !exists) {
68,200✔
1271
                if (fchown(fdt,
102,005✔
1272
                           uid_is_valid(override_uid) ? override_uid : st->st_uid,
34,002✔
1273
                           gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
34,002✔
1274
                        RET_GATHER(ret, -errno);
×
1275

1276
                if (fchmod(fdt, st->st_mode & 07777) < 0)
34,002✔
1277
                        RET_GATHER(ret, -errno);
×
1278

1279
                /* Run hardlink context cleanup now because it potentially changes timestamps */
1280
                hardlink_context_destroy(&our_hardlink_context);
34,002✔
1281
                (void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags);
34,002✔
1282
                (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
34,002✔
1283
        } else if (FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS)) {
196✔
1284
                /* Run hardlink context cleanup now because it potentially changes timestamps */
1285
                hardlink_context_destroy(&our_hardlink_context);
16✔
1286
                /* If the directory already exists, make sure the timestamps stay the same as before. */
1287
                (void) futimens(fdt, (struct timespec[]) { dt_st.st_atim, dt_st.st_mtim });
16✔
1288
        }
1289

1290
        if (copy_flags & COPY_FSYNC_FULL) {
34,198✔
1291
                if (fsync(fdt) < 0)
16,184✔
1292
                        return -errno;
×
1293
        }
1294

1295
        if (ret < 0)
34,198✔
1296
                return ret;
1297

1298
        return copy_flags & COPY_LOCK_BSD ? TAKE_FD(fdt) : 0;
34,198✔
1299
}
1300

1301
static int fd_copy_leaf(
558,530✔
1302
                int df,
1303
                const char *from,
1304
                const struct stat *st,
1305
                int dt,
1306
                const char *to,
1307
                uid_t override_uid,
1308
                gid_t override_gid,
1309
                CopyFlags copy_flags,
1310
                HardlinkContext *hardlink_context,
1311
                const char *display_path,
1312
                copy_progress_bytes_t progress_bytes,
1313
                void *userdata) {
1314
        int r;
558,530✔
1315

1316
        if (S_ISREG(st->st_mode))
558,530✔
1317
                r = fd_copy_regular(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, progress_bytes, userdata);
416,877✔
1318
        else if (S_ISLNK(st->st_mode))
141,653✔
1319
                r = fd_copy_symlink(df, from, st, dt, to, override_uid, override_gid, copy_flags);
141,652✔
1320
        else if (S_ISFIFO(st->st_mode))
1✔
1321
                r = fd_copy_fifo(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
×
1322
        else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) || S_ISSOCK(st->st_mode))
1✔
1323
                r = fd_copy_node(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
1✔
1324
        else
1325
                r = -EOPNOTSUPP;
1326

1327
        return r;
558,530✔
1328
}
1329

1330
static int fd_copy_tree_generic(
592,770✔
1331
                int df,
1332
                const char *from,
1333
                const struct stat *st,
1334
                int dt,
1335
                const char *to,
1336
                dev_t original_device,
1337
                unsigned depth_left,
1338
                uid_t override_uid,
1339
                gid_t override_gid,
1340
                CopyFlags copy_flags,
1341
                Hashmap *denylist,
1342
                Hashmap *subvolumes,
1343
                HardlinkContext *hardlink_context,
1344
                const char *display_path,
1345
                copy_progress_path_t progress_path,
1346
                copy_progress_bytes_t progress_bytes,
1347
                void *userdata) {
1348

1349
        int r;
592,770✔
1350

1351
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
592,770✔
1352

1353
        if (S_ISDIR(st->st_mode))
592,770✔
1354
                return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid,
34,245✔
1355
                                         override_gid, copy_flags, denylist, subvolumes, hardlink_context,
1356
                                         display_path, progress_path, progress_bytes, userdata);
1357

1358
        /* Only if we are copying a directory we are fine if the target dir is referenced by fd only */
1359
        if (!to)
558,525✔
1360
                return -ENOTDIR;
1361

1362
        DenyType t = PTR_TO_INT(hashmap_get(denylist, st));
558,525✔
1363
        if (t == DENY_INODE) {
558,525✔
1364
                log_debug("%s is in the denylist, ignoring", from ?: "file to copy");
×
1365
                return 0;
×
1366
        } else if (t == DENY_CONTENTS)
558,525✔
1367
                log_debug("%s is configured to have its contents excluded, but is not a directory", from ?: "file to copy");
×
1368

1369
        r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
558,525✔
1370
        /* 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. */
1371
        if (r == -EEXIST && (copy_flags & COPY_REPLACE)) {
558,525✔
1372
                /* This codepath is us trying to address an error to copy, if the unlink fails, lets just return the original error. */
1373
                if (unlinkat(dt, to, 0) < 0)
5✔
1374
                        return r;
1375

1376
                r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
5✔
1377
        }
1378

1379
        return r;
1380
}
1381

1382
int copy_tree_at_full(
4,186✔
1383
                int fdf,
1384
                const char *from,
1385
                int fdt,
1386
                const char *to,
1387
                uid_t override_uid,
1388
                gid_t override_gid,
1389
                CopyFlags copy_flags,
1390
                Hashmap *denylist,
1391
                Hashmap *subvolumes,
1392
                copy_progress_path_t progress_path,
1393
                copy_progress_bytes_t progress_bytes,
1394
                void *userdata) {
1395

1396
        struct stat st;
4,186✔
1397
        int r;
4,186✔
1398

1399
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
4,186✔
1400

1401
        if (fstatat(fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW | (isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
8,372✔
1402
                return -errno;
1✔
1403

1404
        r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid,
4,185✔
1405
                                 override_gid, copy_flags, denylist, subvolumes, NULL, NULL, progress_path,
1406
                                 progress_bytes, userdata);
1407
        if (r < 0)
4,185✔
1408
                return r;
1409

1410
        if (S_ISDIR(st.st_mode) && (copy_flags & COPY_SYNCFS)) {
361✔
1411
                /* If the top-level inode is a directory run syncfs() now. */
1412
                r = syncfs_path(fdt, to);
168✔
1413
                if (r < 0)
168✔
1414
                        return r;
×
1415
        } else if ((copy_flags & (COPY_FSYNC_FULL|COPY_SYNCFS)) != 0) {
193✔
1416
                /* fsync() the parent dir of what we just copied if COPY_FSYNC_FULL is set. Also do this in
1417
                 * case COPY_SYNCFS is set but the top-level inode wasn't actually a directory. We do this so that
1418
                 * COPY_SYNCFS provides reasonable synchronization semantics on any kind of inode: when the
1419
                 * copy operation is done the whole inode — regardless of its type — and all its children
1420
                 * will be synchronized to disk. */
1421
                r = fsync_parent_at(fdt, to);
10✔
1422
                if (r < 0)
10✔
1423
                        return r;
×
1424
        }
1425

1426
        return 0;
1427
}
1428

1429
static int sync_dir_by_flags(int dir_fd, const char *path, CopyFlags copy_flags) {
75✔
1430
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
75✔
1431
        assert(path);
75✔
1432

1433
        if (copy_flags & COPY_SYNCFS)
75✔
1434
                return syncfs_path(dir_fd, path);
×
1435
        if (copy_flags & COPY_FSYNC_FULL)
75✔
1436
                return fsync_parent_at(dir_fd, path);
×
1437

1438
        return 0;
1439
}
1440

1441
int copy_directory_at_full(
75✔
1442
                int dir_fdf,
1443
                const char *from,
1444
                int dir_fdt,
1445
                const char *to,
1446
                CopyFlags copy_flags,
1447
                copy_progress_path_t progress_path,
1448
                copy_progress_bytes_t progress_bytes,
1449
                void *userdata) {
1450

1451
        _cleanup_close_ int fdt = -EBADF;
75✔
1452
        struct stat st;
75✔
1453
        int r;
75✔
1454

1455
        assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
75✔
1456
        assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
75✔
1457
        assert(to);
75✔
1458

1459
        if (fstatat(dir_fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW|(isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
150✔
1460
                return -errno;
×
1461

1462
        r = stat_verify_directory(&st);
75✔
1463
        if (r < 0)
75✔
1464
                return r;
1465

1466
        r = fd_copy_directory(
150✔
1467
                        dir_fdf, from,
1468
                        &st,
1469
                        dir_fdt, to,
1470
                        st.st_dev,
75✔
1471
                        COPY_DEPTH_MAX,
1472
                        UID_INVALID, GID_INVALID,
1473
                        copy_flags,
1474
                        NULL, NULL, NULL, NULL,
1475
                        progress_path,
1476
                        progress_bytes,
1477
                        userdata);
1478
        if (r < 0)
75✔
1479
                return r;
1480

1481
        if (FLAGS_SET(copy_flags, COPY_LOCK_BSD))
75✔
1482
                fdt = r;
1✔
1483

1484
        r = sync_dir_by_flags(dir_fdt, to, copy_flags);
75✔
1485
        if (r < 0)
75✔
1486
                return r;
1487

1488
        return FLAGS_SET(copy_flags, COPY_LOCK_BSD) ? TAKE_FD(fdt) : 0;
75✔
1489
}
1490

1491
int copy_file_fd_at_full(
1,560✔
1492
                int dir_fdf,
1493
                const char *from,
1494
                int fdt,
1495
                CopyFlags copy_flags,
1496
                copy_progress_bytes_t progress_bytes,
1497
                void *userdata) {
1498

1499
        _cleanup_close_ int fdf = -EBADF;
1,560✔
1500
        struct stat st;
1,560✔
1501
        int r;
1,560✔
1502

1503
        assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
1,560✔
1504
        assert(fdt >= 0);
1,560✔
1505
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
1,560✔
1506

1507
        fdf = xopenat_full(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY, XO_REGULAR, 0);
1,560✔
1508
        if (fdf < 0)
1,560✔
1509
                return fdf;
1510

1511
        if (fstat(fdt, &st) < 0)
1,559✔
1512
                return -errno;
×
1513

1514
        r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
1,559✔
1515
        if (r < 0)
1,559✔
1516
                return r;
1517

1518
        /* Make sure to copy file attributes only over if target is a regular
1519
         * file (so that copying a file to /dev/null won't alter the access
1520
         * mode/ownership of that device node...) */
1521
        if (S_ISREG(st.st_mode)) {
1,559✔
1522
                (void) copy_times(fdf, fdt, copy_flags);
1,559✔
1523
                (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
1,559✔
1524
        }
1525

1526
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
1,559✔
1527
                r = fd_verify_linked(fdf);
×
1528
                if (r < 0)
×
1529
                        return r;
1530
        }
1531

1532
        if (copy_flags & COPY_FSYNC_FULL) {
1,559✔
1533
                r = fsync_full(fdt);
×
1534
                if (r < 0)
×
1535
                        return r;
×
1536
        } else if (copy_flags & COPY_FSYNC) {
1,559✔
1537
                if (fsync(fdt) < 0)
×
1538
                        return -errno;
×
1539
        }
1540

1541
        return 0;
1542
}
1543

1544
int copy_file_at_full(
157✔
1545
                int dir_fdf,
1546
                const char *from,
1547
                int dir_fdt,
1548
                const char *to,
1549
                int flags,
1550
                mode_t mode,
1551
                unsigned chattr_flags,
1552
                unsigned chattr_mask,
1553
                CopyFlags copy_flags,
1554
                copy_progress_bytes_t progress_bytes,
1555
                void *userdata) {
1556

1557
        _cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
157✔
1558
        int r;
157✔
1559

1560
        assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
157✔
1561
        assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
157✔
1562
        assert(to);
157✔
1563

1564
        fdf = xopenat_full(dir_fdf, from, O_RDONLY|O_CLOEXEC|O_NOCTTY, XO_REGULAR, 0);
157✔
1565
        if (fdf < 0)
157✔
1566
                return fdf;
1567

1568
        if (mode == MODE_INVALID) {
157✔
1569
                struct stat st;
2✔
1570

1571
                if (fstat(fdf, &st) < 0)
2✔
1572
                        return -errno;
×
1573

1574
                mode = st.st_mode;
2✔
1575
        }
1576

1577
        WITH_UMASK(0000) {
314✔
1578
                fdt = xopenat_lock_full(dir_fdt, to,
314✔
1579
                                        flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
1580
                                        XO_REGULAR | (copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0),
157✔
1581
                                        mode,
1582
                                        copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE, LOCK_EX);
157✔
1583
                if (fdt < 0)
157✔
1584
                        return fdt;
×
1585
        }
1586

1587
        r = prepare_nocow(fdf, /*from=*/ NULL, fdt, &chattr_mask, &chattr_flags);
157✔
1588
        if (r < 0)
157✔
1589
                return r;
1590

1591
        if ((chattr_mask & CHATTR_EARLY_FL) != 0)
157✔
1592
                (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL);
×
1593

1594
        r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags & ~COPY_LOCK_BSD, NULL, NULL, progress_bytes, userdata);
157✔
1595
        if (r < 0)
157✔
1596
                goto fail;
1✔
1597

1598
        (void) copy_times(fdf, fdt, copy_flags);
156✔
1599
        (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
156✔
1600

1601
        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
156✔
1602
                r = fd_verify_linked(fdf);
×
1603
                if (r < 0)
×
1604
                        goto fail;
×
1605
        }
1606

1607
        unsigned nocow = FLAGS_SET(copy_flags, COPY_NOCOW_AFTER) ? FS_NOCOW_FL : 0;
156✔
1608
        if (((chattr_mask & ~CHATTR_EARLY_FL) | nocow) != 0)
156✔
1609
                (void) chattr_fd(fdt, chattr_flags | nocow, (chattr_mask & ~CHATTR_EARLY_FL) | nocow);
×
1610

1611
        if (copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL)) {
156✔
1612
                if (fsync(fdt) < 0) {
×
1613
                        r = -errno;
×
1614
                        goto fail;
×
1615
                }
1616
        }
1617

1618
        if (!FLAGS_SET(copy_flags, COPY_LOCK_BSD)) {
156✔
1619
                r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
155✔
1620
                if (r < 0)
155✔
1621
                        goto fail;
×
1622
        }
1623

1624
        if (copy_flags & COPY_FSYNC_FULL) {
156✔
1625
                r = fsync_parent_at(dir_fdt, to);
×
1626
                if (r < 0)
×
1627
                        goto fail;
×
1628
        }
1629

1630
        return copy_flags & COPY_LOCK_BSD ? TAKE_FD(fdt) : 0;
156✔
1631

1632
fail:
1✔
1633
        /* Only unlink if we definitely are the ones who created the file */
1634
        if (FLAGS_SET(flags, O_EXCL))
1✔
1635
                (void) unlinkat(dir_fdt, to, 0);
1✔
1636

1637
        return r;
1638
}
1639

1640
int copy_file_atomic_at_full(
1,619✔
1641
                int dir_fdf,
1642
                const char *from,
1643
                int dir_fdt,
1644
                const char *to,
1645
                mode_t mode,
1646
                unsigned chattr_flags,
1647
                unsigned chattr_mask,
1648
                CopyFlags copy_flags,
1649
                copy_progress_bytes_t progress_bytes,
1650
                void *userdata) {
1651

1652
        _cleanup_(unlink_and_freep) char *t = NULL;
×
1653
        _cleanup_close_ int fdt = -EBADF;
1,619✔
1654
        int r;
1,619✔
1655

1656
        assert(to);
1,619✔
1657
        assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
1,619✔
1658

1659
        if (copy_flags & COPY_MAC_CREATE) {
1,619✔
1660
                r = mac_selinux_create_file_prepare_at(dir_fdt, to, S_IFREG);
220✔
1661
                if (r < 0)
220✔
1662
                        return r;
1663
        }
1664
        fdt = open_tmpfile_linkable_at(dir_fdt, to, O_WRONLY|O_CLOEXEC, &t);
1,619✔
1665
        if (copy_flags & COPY_MAC_CREATE)
1,619✔
1666
                mac_selinux_create_file_clear();
220✔
1667
        if (fdt < 0)
1,619✔
1668
                return fdt;
1669

1670
        r = prepare_nocow(dir_fdf, from, fdt, &chattr_mask, &chattr_flags);
1,587✔
1671
        if (r < 0)
1,587✔
1672
                return r;
1673

1674
        if ((chattr_mask & CHATTR_EARLY_FL) != 0)
1,553✔
1675
                (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL);
×
1676

1677
        r = copy_file_fd_at_full(dir_fdf, from, fdt, copy_flags, progress_bytes, userdata);
1,553✔
1678
        if (r < 0)
1,553✔
1679
                return r;
1680

1681
        if (fchmod(fdt, mode) < 0)
1,553✔
1682
                return -errno;
×
1683

1684
        if ((copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL))) {
1,553✔
1685
                /* Sync the file */
1686
                if (fsync(fdt) < 0)
×
1687
                        return -errno;
×
1688
        }
1689

1690
        r = link_tmpfile_at(fdt, dir_fdt, t, to, (copy_flags & COPY_REPLACE) ? LINK_TMPFILE_REPLACE : 0);
1,553✔
1691
        if (r < 0)
1,553✔
1692
                return r;
1693

1694
        t = mfree(t);
1,552✔
1695

1696
        unsigned nocow = FLAGS_SET(copy_flags, COPY_NOCOW_AFTER) ? FS_NOCOW_FL : 0;
1,552✔
1697
        if (((chattr_mask & ~CHATTR_EARLY_FL) | nocow) != 0)
1,552✔
1698
                (void) chattr_fd(fdt, chattr_flags | nocow, (chattr_mask & ~CHATTR_EARLY_FL) | nocow);
1✔
1699

1700
        r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
1,552✔
1701
        if (r < 0)
1,552✔
1702
                goto fail;
×
1703

1704
        if (copy_flags & COPY_FSYNC_FULL) {
1,552✔
1705
                /* Sync the parent directory */
1706
                r = fsync_parent_at(dir_fdt, to);
×
1707
                if (r < 0)
×
1708
                        goto fail;
×
1709
        }
1710

1711
        return 0;
1712

1713
fail:
×
1714
        (void) unlinkat(dir_fdt, to, 0);
×
1715
        return r;
×
1716
}
1717

1718
int copy_times(int fdf, int fdt, CopyFlags flags) {
1,808✔
1719
        struct stat st;
1,808✔
1720

1721
        assert(fdf >= 0);
1,808✔
1722
        assert(fdt >= 0);
1,808✔
1723

1724
        if (fstat(fdf, &st) < 0)
1,808✔
1725
                return -errno;
×
1726

1727
        if (futimens(fdt, (struct timespec[2]) { st.st_atim, st.st_mtim }) < 0)
1,808✔
1728
                return -errno;
×
1729

1730
        if (FLAGS_SET(flags, COPY_CRTIME)) {
1,808✔
1731
                usec_t crtime;
45✔
1732

1733
                if (fd_getcrtime(fdf, &crtime) >= 0)
45✔
1734
                        (void) fd_setcrtime(fdt, crtime);
45✔
1735
        }
1736

1737
        return 0;
1738
}
1739

1740
int copy_access(int fdf, int fdt) {
13✔
1741
        struct stat st;
13✔
1742

1743
        assert(fdf >= 0);
13✔
1744
        assert(fdt >= 0);
13✔
1745

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

1748
        if (fstat(fdf, &st) < 0)
13✔
1749
                return -errno;
×
1750

1751
        return RET_NERRNO(fchmod(fdt, st.st_mode & 07777));
13✔
1752
}
1753

1754
int copy_rights_with_fallback(int fdf, int fdt, const char *patht) {
44✔
1755
        struct stat st;
44✔
1756

1757
        assert(fdf >= 0);
44✔
1758
        assert(fdt >= 0);
44✔
1759

1760
        /* Copies both access mode and ownership from fdf to fdt */
1761

1762
        if (fstat(fdf, &st) < 0)
44✔
1763
                return -errno;
×
1764

1765
        return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid);
44✔
1766
}
1767

1768
int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_flags) {
573,709✔
1769
        _cleanup_free_ char *names = NULL;
573,709✔
1770
        int ret = 0, r;
573,709✔
1771

1772
        r = listxattr_at_malloc(df, from, 0, &names);
573,709✔
1773
        if (r < 0)
573,709✔
1774
                return r;
1775

1776
        NULSTR_FOREACH(p, names) {
573,843✔
1777
                if (!FLAGS_SET(copy_flags, COPY_ALL_XATTRS) && !startswith(p, "user."))
526✔
1778
                        continue;
280✔
1779

1780
                _cleanup_free_ char *value = NULL;
246✔
1781
                size_t value_size;
246✔
1782
                r = getxattr_at_malloc(df, from, p, 0, &value, &value_size);
246✔
1783
                if (r == -ENODATA)
246✔
1784
                        continue; /* gone by now */
×
1785
                if (r < 0)
246✔
1786
                        return r;
×
1787

1788
                RET_GATHER(ret, xsetxattr_full(dt, to, /* at_flags = */ 0, p, value, value_size, /* xattr_flags = */ 0));
246✔
1789
        }
1790

1791
        return ret;
1792
}
1793

1794
int reflink(int infd, int outfd) {
397,590✔
1795
        int r;
397,590✔
1796

1797
        assert(infd >= 0);
397,590✔
1798
        assert(outfd >= 0);
397,590✔
1799

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

1802
        r = fd_verify_regular(outfd);
397,590✔
1803
        if (r < 0)
397,590✔
1804
                return r;
1805

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

1808
        assert_cc(FICLONE == BTRFS_IOC_CLONE);
397,590✔
1809

1810
        return RET_NERRNO(ioctl(outfd, FICLONE, infd));
397,590✔
1811
}
1812

1813
assert_cc(sizeof(struct file_clone_range) == sizeof(struct btrfs_ioctl_clone_range_args));
1814

1815
int reflink_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
175✔
1816
        struct file_clone_range args = {
175✔
1817
                .src_fd = infd,
1818
                .src_offset = in_offset,
1819
                .src_length = sz,
1820
                .dest_offset = out_offset,
1821
        };
1822
        int r;
175✔
1823

1824
        assert(infd >= 0);
175✔
1825
        assert(outfd >= 0);
175✔
1826

1827
        /* Inside the kernel, FICLONE is identical to FICLONERANGE with offsets and size set to zero, let's
1828
         * simplify things and use the simple ioctl in that case. Also, do the same if the size is
1829
         * UINT64_MAX, which is how we usually encode "everything". */
1830
        if (in_offset == 0 && out_offset == 0 && IN_SET(sz, 0, UINT64_MAX))
175✔
1831
                return reflink(infd, outfd);
×
1832

1833
        r = fd_verify_regular(outfd);
175✔
1834
        if (r < 0)
175✔
1835
                return r;
1836

1837
        assert_cc(FICLONERANGE == BTRFS_IOC_CLONE_RANGE);
164✔
1838

1839
        return RET_NERRNO(ioctl(outfd, FICLONERANGE, &args));
164✔
1840
}
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