static int raw_import_try_reflink(RawImport *i) { off_t p; int r; assert(i); assert(i->input_fd >= 0); assert(i->output_fd >= 0); if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED) return 0; if (!S_ISREG(i->st.st_mode)) return 0; p = lseek(i->input_fd, 0, SEEK_CUR); if (p == (off_t) -1) return log_error_errno(errno, "Failed to read file offset of input file: %m"); /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */ if ((uint64_t) p != (uint64_t) i->buffer_size) return 0; r = btrfs_reflink(i->input_fd, i->output_fd); if (r >= 0) return 1; return 0; }
static int reflink_snapshot(int fd, const char *path) { char *p, *d; int new_fd, r; p = strdupa(path); d = dirname(p); new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600); if (new_fd < 0) { _cleanup_free_ char *t = NULL; r = tempfn_random(path, NULL, &t); if (r < 0) return r; new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600); if (new_fd < 0) return -errno; (void) unlink(t); } r = btrfs_reflink(fd, new_fd); if (r < 0) { safe_close(new_fd); return r; } return new_fd; }
static int raw_export_process(RawExport *e) { ssize_t l; int r; assert(e); if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { /* If we shall take an uncompressed snapshot we can * reflink source to destination directly. Let's see * if this works. */ r = btrfs_reflink(e->input_fd, e->output_fd); if (r >= 0) { r = 0; goto finish; } e->tried_reflink = true; } if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE); if (l < 0) { if (errno == EAGAIN) return 0; e->tried_sendfile = true; } else if (l == 0) { r = 0; goto finish; } else { e->written_uncompressed += l; e->written_compressed += l; raw_export_report_progress(e); return 0; } } while (e->buffer_size <= 0) { uint8_t input[COPY_BUFFER_SIZE]; if (e->eof) { r = 0; goto finish; } l = read(e->input_fd, input, sizeof(input)); if (l < 0) { r = log_error_errno(errno, "Failed to read raw file: %m"); goto finish; } if (l == 0) { e->eof = true; r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated); } else { e->written_uncompressed += l; r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated); } if (r < 0) { r = log_error_errno(r, "Failed to encode: %m"); goto finish; } } l = write(e->output_fd, e->buffer, e->buffer_size); if (l < 0) { if (errno == EAGAIN) return 0; r = log_error_errno(errno, "Failed to write output file: %m"); goto finish; } assert((size_t) l <= e->buffer_size); memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l); e->buffer_size -= l; e->written_compressed += l; raw_export_report_progress(e); return 0; finish: if (r >= 0) { (void) copy_times(e->input_fd, e->output_fd); (void) copy_xattr(e->input_fd, e->output_fd); } if (e->on_finished) e->on_finished(e, r, e->userdata); else sd_event_exit(e->event, r); return 0; }
int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { bool try_sendfile = true; int r; assert(fdf >= 0); assert(fdt >= 0); /* Try btrfs reflinks first. */ if (try_reflink && max_bytes == (off_t) -1) { r = btrfs_reflink(fdf, fdt); if (r >= 0) return r; } for (;;) { size_t m = COPY_BUFFER_SIZE; ssize_t n; if (max_bytes != (off_t) -1) { if (max_bytes <= 0) return -EFBIG; if ((off_t) m > max_bytes) m = (size_t) max_bytes; } /* First try sendfile(), unless we already tried */ if (try_sendfile) { n = sendfile(fdt, fdf, NULL, m); if (n < 0) { if (errno != EINVAL && errno != ENOSYS) return -errno; try_sendfile = false; /* use fallback below */ } else if (n == 0) /* EOF */ break; else if (n > 0) /* Succcess! */ goto next; } /* As a fallback just copy bits by hand */ { char buf[m]; n = read(fdf, buf, m); if (n < 0) return -errno; if (n == 0) /* EOF */ break; r = loop_write(fdt, buf, (size_t) n, false); if (r < 0) return r; } next: if (max_bytes != (off_t) -1) { assert(max_bytes >= n); max_bytes -= n; } } return 0; }