static int tar_import_fork_tar(TarImport *i) { int r; assert(i); assert(!i->final_path); assert(!i->temp_path); assert(i->tar_fd < 0); i->final_path = strjoin(i->image_root, "/", i->local, NULL); if (!i->final_path) return log_oom(); r = tempfn_random(i->final_path, &i->temp_path); if (r < 0) return log_oom(); (void) mkdir_parents_label(i->temp_path, 0700); r = btrfs_subvol_make(i->temp_path); if (r == -ENOTTY) { if (mkdir(i->temp_path, 0755) < 0) return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); } else if (r < 0) return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path); i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); if (i->tar_fd < 0) return i->tar_fd; return 0; }
static int raw_import_open_disk(RawImport *i) { int r; assert(i); assert(!i->final_path); assert(!i->temp_path); assert(i->output_fd < 0); i->final_path = strjoin(i->image_root, "/", i->local, ".raw"); if (!i->final_path) return log_oom(); r = tempfn_random(i->final_path, NULL, &i->temp_path); if (r < 0) return log_oom(); (void) mkdir_parents_label(i->temp_path, 0700); i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); if (i->output_fd < 0) return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path); r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL); if (r < 0) log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); return 0; }
static int raw_pull_job_on_open_disk_settings(PullJob *j) { RawPull *i; int r; assert(j); assert(j->userdata); i = j->userdata; assert(i->settings_job == j); assert(!i->settings_path); assert(!i->settings_temp_path); r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); if (r < 0) return log_oom(); r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); if (r < 0) return log_oom(); mkdir_parents_label(i->settings_temp_path, 0700); j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); if (j->disk_fd < 0) return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); 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_pull_job_on_open_disk_raw(PullJob *j) { RawPull *i; int r; assert(j); assert(j->userdata); i = j->userdata; assert(i->raw_job == j); assert(!i->final_path); assert(!i->temp_path); r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path); if (r < 0) return log_oom(); r = tempfn_random(i->final_path, NULL, &i->temp_path); if (r < 0) return log_oom(); (void) mkdir_parents_label(i->temp_path, 0700); j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); if (j->disk_fd < 0) return log_error_errno(errno, "Failed to create %s: %m", i->temp_path); r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL); if (r < 0) log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path); return 0; }
int main(int argc, char** argv) { _cleanup_free_ char *cmd = NULL, *cmd2 = NULL, *ans = NULL, *ans2 = NULL, *d = NULL, *tmp = NULL, *line = NULL; _cleanup_close_ int fd = -1, fd2 = -1; const char *p = argv[1] ?: "/tmp"; char *pattern; log_set_max_level(LOG_DEBUG); log_parse_environment(); pattern = strjoina(p, "/systemd-test-XXXXXX"); fd = open_tmpfile_unlinkable(p, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid_cached(), fd) > 0); (void) system(cmd); assert_se(readlink_malloc(cmd + 6, &ans) >= 0); log_debug("link1: %s", ans); assert_se(endswith(ans, " (deleted)")); fd2 = mkostemp_safe(pattern); assert_se(fd >= 0); assert_se(unlink(pattern) == 0); assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid_cached(), fd2) > 0); (void) system(cmd2); assert_se(readlink_malloc(cmd2 + 6, &ans2) >= 0); log_debug("link2: %s", ans2); assert_se(endswith(ans2, " (deleted)")); pattern = strjoina(p, "/tmpfiles-test"); assert_se(tempfn_random(pattern, NULL, &d) >= 0); fd = open_tmpfile_linkable(d, O_RDWR|O_CLOEXEC, &tmp); assert_se(fd >= 0); assert_se(write(fd, "foobar\n", 7) == 7); assert_se(touch(d) >= 0); assert_se(link_tmpfile(fd, tmp, d) == -EEXIST); assert_se(unlink(d) >= 0); assert_se(link_tmpfile(fd, tmp, d) >= 0); assert_se(read_one_line_file(d, &line) >= 0); assert_se(streq(line, "foobar")); assert_se(unlink(d) >= 0); return 0; }
static int raw_pull_maybe_convert_qcow2(RawPull *i) { _cleanup_close_ int converted_fd = -1; _cleanup_free_ char *t = NULL; int r; assert(i); assert(i->raw_job); r = qcow2_detect(i->raw_job->disk_fd); if (r < 0) return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m"); if (r == 0) return 0; /* This is a QCOW2 image, let's convert it */ r = tempfn_random(i->final_path, NULL, &t); if (r < 0) return log_oom(); converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); if (converted_fd < 0) return log_error_errno(errno, "Failed to create %s: %m", t); r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL); if (r < 0) log_warning_errno(errno, "Failed to set file attributes on %s: %m", t); log_info("Unpacking QCOW2 file."); r = qcow2_convert(i->raw_job->disk_fd, converted_fd); if (r < 0) { unlink(t); return log_error_errno(r, "Failed to convert qcow2 image: %m"); } (void) unlink(i->temp_path); free(i->temp_path); i->temp_path = t; t = NULL; safe_close(i->raw_job->disk_fd); i->raw_job->disk_fd = converted_fd; converted_fd = -1; return 1; }
static int tar_pull_job_on_open_disk_tar(PullJob *j) { TarPull *i; int r; assert(j); assert(j->userdata); i = j->userdata; assert(i->tar_job == j); assert(!i->final_path); assert(!i->temp_path); assert(i->tar_pid <= 0); r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path); if (r < 0) return log_oom(); r = tempfn_random(i->final_path, NULL, &i->temp_path); if (r < 0) return log_oom(); mkdir_parents_label(i->temp_path, 0700); r = btrfs_subvol_make(i->temp_path); if (r == -ENOTTY) { if (mkdir(i->temp_path, 0755) < 0) return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); } else if (r < 0) return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path); else (void) import_assign_pool_quota_and_warn(i->temp_path); j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); if (j->disk_fd < 0) return j->disk_fd; return 0; }
static int dkr_pull_job_on_open_disk(PullJob *j) { const char *base; DkrPull *i; int r; assert(j); assert(j->userdata); i = j->userdata; assert(i->layer_job == j); assert(i->final_path); assert(!i->temp_path); assert(i->tar_pid <= 0); r = tempfn_random(i->final_path, &i->temp_path); if (r < 0) return log_oom(); mkdir_parents_label(i->temp_path, 0700); base = dkr_pull_current_base_layer(i); if (base) { const char *base_path; base_path = strjoina(i->image_root, "/.dkr-", base); r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY); } else r = btrfs_subvol_make(i->temp_path); if (r < 0) return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path); j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); if (j->disk_fd < 0) return j->disk_fd; return 0; }
static int raw_pull_make_local_copy(RawPull *i) { _cleanup_free_ char *tp = NULL; _cleanup_close_ int dfd = -1; const char *p; int r; assert(i); assert(i->raw_job); if (!i->local) return 0; if (i->raw_job->etag_exists) { /* We have downloaded this one previously, reopen it */ assert(i->raw_job->disk_fd < 0); i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC); if (i->raw_job->disk_fd < 0) return log_error_errno(errno, "Failed to open vendor image: %m"); } else { /* We freshly downloaded the image, use it */ assert(i->raw_job->disk_fd >= 0); if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1) return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m"); } p = strjoina(i->image_root, "/", i->local, ".raw"); if (i->force_local) (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); r = tempfn_random(p, NULL, &tp); if (r < 0) return log_oom(); dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); if (dfd < 0) return log_error_errno(errno, "Failed to create writable copy of image: %m"); /* Turn off COW writing. This should greatly improve * performance on COW file systems like btrfs, since it * reduces fragmentation caused by not allowing in-place * writes. */ r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL); if (r < 0) log_warning_errno(r, "Failed to set file attributes on %s: %m", tp); r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, COPY_REFLINK); if (r < 0) { unlink(tp); return log_error_errno(r, "Failed to make writable copy of image: %m"); } (void) copy_times(i->raw_job->disk_fd, dfd); (void) copy_xattr(i->raw_job->disk_fd, dfd); dfd = safe_close(dfd); r = rename(tp, p); if (r < 0) { r = log_error_errno(errno, "Failed to move writable image into place: %m"); unlink(tp); return r; } log_info("Created new local image '%s'.", i->local); if (i->roothash) { r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path); if (r < 0) return r; } if (i->settings) { r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path); if (r < 0) return r; } return 0; }
if (fd_to < 0) { if (errno != -ENOENT) return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to); } else { r = version_check(fd_from, from, fd_to, to); if (r < 0) return r; if (lseek(fd_from, 0, SEEK_SET) == (off_t) -1) return log_error_errno(errno, "Failed to seek in \%s\": %m", from); fd_to = safe_close(fd_to); } } r = tempfn_random(to, NULL, &t); if (r < 0) return log_oom(); RUN_WITH_UMASK(0000) { fd_to = open(t, O_WRONLY|O_CREAT|O_CLOEXEC|O_EXCL|O_NOFOLLOW, 0644); if (fd_to < 0) return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t); } r = copy_bytes(fd_from, fd_to, (uint64_t) -1, COPY_REFLINK); if (r < 0) { (void) unlink(t); return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", from, t); }
static int save_external_coredump( const char *info[_INFO_LEN], uid_t uid, char **ret_filename, int *ret_fd, uint64_t *ret_size) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; struct stat st; int r; assert(info); assert(ret_filename); assert(ret_fd); assert(ret_size); r = make_filename(info, &fn); if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); r = tempfn_random(fn, NULL, &tmp); if (r < 0) return log_error_errno(r, "Failed to determine temporary file name: %m"); mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); if (fd < 0) return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max, false); if (r == -EFBIG) { log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]); goto fail; } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]); goto fail; } else if (r < 0) { log_error_errno(r, "Failed to dump coredump to file: %m"); goto fail; } if (fstat(fd, &st) < 0) { log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { log_error_errno(errno, "Failed to seek on %s: %m", tmp); goto fail; } #if defined(HAVE_XZ) || defined(HAVE_LZ4) /* If we will remove the coredump anyway, do not compress. */ if (maybe_remove_external_coredump(NULL, st.st_size) == 0 && arg_compress) { _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; _cleanup_close_ int fd_compressed = -1; fn_compressed = strappend(fn, COMPRESSED_EXT); if (!fn_compressed) { log_oom(); goto uncompressed; } r = tempfn_random(fn_compressed, NULL, &tmp_compressed); if (r < 0) { log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed); goto uncompressed; } fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); if (fd_compressed < 0) { log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { log_error_errno(r, "Failed to compress %s: %m", tmp_compressed); goto fail_compressed; } r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid); if (r < 0) goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_fd = fd; /* uncompressed */ *ret_size = (uint64_t) st.st_size; /* uncompressed */ fn_compressed = NULL; fd = -1; return 0; fail_compressed: unlink_noerrno(tmp_compressed); } uncompressed: #endif r = fix_permissions(fd, tmp, fn, info, uid); if (r < 0) goto fail; *ret_filename = fn; *ret_fd = fd; *ret_size = (uint64_t) st.st_size; fn = NULL; fd = -1; return 0; fail: unlink_noerrno(tmp); return r; }
int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) { _cleanup_close_ int sfd = -1; int r; assert(e); assert(path); assert(fd >= 0); assert(compress < _IMPORT_COMPRESS_TYPE_MAX); assert(compress != IMPORT_COMPRESS_UNKNOWN); if (e->output_fd >= 0) return -EBUSY; sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC); if (sfd < 0) return -errno; if (fstat(sfd, &e->st) < 0) return -errno; r = fd_nonblock(fd, true); if (r < 0) return r; r = free_and_strdup(&e->path, path); if (r < 0) return r; e->quota_referenced = (uint64_t) -1; if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */ BtrfsQuotaInfo q; r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q); if (r >= 0) e->quota_referenced = q.referenced; e->temp_path = mfree(e->temp_path); r = tempfn_random(path, NULL, &e->temp_path); if (r < 0) return r; /* Let's try to make a snapshot, if we can, so that the export is atomic */ r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE); if (r < 0) { log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path); e->temp_path = mfree(e->temp_path); } } r = import_compress_init(&e->compress, compress); if (r < 0) return r; r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e); if (r == -EPERM) { r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e); if (r < 0) return r; r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); } if (r < 0) return r; e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid); if (e->tar_fd < 0) { e->output_event_source = sd_event_source_unref(e->output_event_source); return e->tar_fd; } e->output_fd = fd; return r; }