diff options
Diffstat (limited to 'src/import/pull-tar.c')
-rw-r--r-- | src/import/pull-tar.c | 310 |
1 files changed, 211 insertions, 99 deletions
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 1227c89ffb..6aca3c9979 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -14,6 +14,7 @@ #include "hostname-util.h" #include "import-common.h" #include "import-util.h" +#include "install-file.h" #include "macro.h" #include "mkdir.h" #include "path-util.h" @@ -25,6 +26,7 @@ #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" +#include "user-util.h" #include "utf8.h" #include "util.h" #include "web-util.h" @@ -61,6 +63,8 @@ struct TarPull { char *settings_path; char *settings_temp_path; + + char *checksum; }; TarPull* tar_pull_unref(TarPull *i) { @@ -87,6 +91,7 @@ TarPull* tar_pull_unref(TarPull *i) { free(i->settings_path); free(i->image_root); free(i->local); + free(i->checksum); return mfree(i); } @@ -194,7 +199,10 @@ static void tar_pull_report_progress(TarPull *i, TarProgress p) { log_debug("Combined progress %u%%", percent); } -static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) { +static int tar_pull_determine_path( + TarPull *i, + const char *suffix, + char **field /* input + output (!) */) { int r; assert(i); @@ -213,6 +221,8 @@ static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) } static int tar_pull_make_local_copy(TarPull *i) { + _cleanup_(rm_rf_subvolume_and_freep) char *t = NULL; + const char *p; int r; assert(i); @@ -221,9 +231,38 @@ static int tar_pull_make_local_copy(TarPull *i) { if (!i->local) return 0; - r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->flags); + assert(i->final_path); + + p = prefix_roota(i->image_root, i->local); + + r = tempfn_random(p, NULL, &t); if (r < 0) - return r; + return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p); + + if (i->flags & PULL_BTRFS_SUBVOL) + r = btrfs_subvol_snapshot( + i->final_path, + t, + (i->flags & PULL_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)| + BTRFS_SNAPSHOT_FALLBACK_COPY| + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY| + BTRFS_SNAPSHOT_RECURSIVE); + else + r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS); + if (r < 0) + return log_error_errno(r, "Failed to create local image: %m"); + + r = install_file(AT_FDCWD, t, + AT_FDCWD, p, + (i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) | + (i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) | + (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0)); + if (r < 0) + return log_error_errno(r, "Failed to install local image '%s': %m", p); + + t = mfree(t); + + log_info("Created new local image '%s'.", i->local); if (FLAGS_SET(i->flags, PULL_SETTINGS)) { const char *local_settings; @@ -235,7 +274,14 @@ static int tar_pull_make_local_copy(TarPull *i) { local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, 0, COPY_REFLINK | (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0)); + r = copy_file_atomic( + i->settings_path, + local_settings, + 0664, + 0, 0, + COPY_REFLINK | + (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) | + (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0)); if (r == -EEXIST) log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); else if (r == -ENOENT) @@ -274,17 +320,22 @@ static void tar_pull_job_on_finished(PullJob *j) { i = j->userdata; - if (j == i->settings_job) { - if (j->error != 0) + if (j->error != 0) { + if (j == i->tar_job) { + if (j->error == ENOMEDIUM) /* HTTP 404 */ + r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); + else + r = log_error_errno(j->error, "Failed to retrieve image file."); + goto finish; + } else if (j == i->checksum_job) { + r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); + goto finish; + } else if (j == i->signature_job) + log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url); + else if (j == i->settings_job) log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); - } else if (j->error != 0 && j != i->signature_job) { - if (j == i->checksum_job) - log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); else - log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); - - r = j->error; - goto finish; + assert("unexpected job"); } /* This is invoked if either the download completed successfully, or the download was skipped because @@ -296,6 +347,8 @@ static void tar_pull_job_on_finished(PullJob *j) { if (i->signature_job && i->signature_job->error != 0) { VerificationStyle style; + assert(i->checksum_job); + r = verification_style_from_url(i->checksum_job->url, &style); if (r < 0) { log_error_errno(r, "Failed to determine verification style from checksum URL: %m"); @@ -306,19 +359,14 @@ static void tar_pull_job_on_finished(PullJob *j) { * in per-directory verification mode, since only * then the signature is detached, and thus a file * of its own. */ - log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); - r = i->signature_job->error; + r = log_error_errno(i->signature_job->error, + "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); goto finish; } } - i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd); - if (i->settings_job) - i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); - - r = tar_pull_determine_path(i, NULL, &i->final_path); - if (r < 0) - goto finish; + pull_job_close_disk_fd(i->tar_job); + pull_job_close_disk_fd(i->settings_job); if (i->tar_pid > 0) { r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG); @@ -337,6 +385,7 @@ static void tar_pull_job_on_finished(PullJob *j) { tar_pull_report_progress(i, TAR_VERIFYING); r = pull_verify(i->verify, + i->checksum, i->tar_job, i->checksum_job, i->signature_job, @@ -346,59 +395,92 @@ static void tar_pull_job_on_finished(PullJob *j) { /* verity_job = */ NULL); if (r < 0) goto finish; + } - tar_pull_report_progress(i, TAR_FINALIZING); + if (i->flags & PULL_DIRECT) { + assert(!i->settings_job); + assert(i->local); + assert(!i->temp_path); - r = import_mangle_os_tree(i->temp_path); - if (r < 0) - goto finish; + tar_pull_report_progress(i, TAR_FINALIZING); - r = import_make_read_only(i->temp_path); + r = import_mangle_os_tree(i->local); if (r < 0) goto finish; - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); + r = install_file( + AT_FDCWD, i->local, + AT_FDCWD, NULL, + (i->flags & PULL_READ_ONLY) ? INSTALL_READ_ONLY : 0 | + (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0)); if (r < 0) { - log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path); + log_error_errno(r, "Failed to finalize '%s': %m", i->local); goto finish; } + } else { + r = tar_pull_determine_path(i, NULL, &i->final_path); + if (r < 0) + goto finish; - i->temp_path = mfree(i->temp_path); - - if (i->settings_job && - i->settings_job->error == 0) { + if (!i->tar_job->etag_exists) { + /* This is a new download, verify it, and move it into place */ - /* Also move the settings file into place, if it exists. Note that we do so only if we also - * moved the tar file in place, to keep things strictly in sync. */ - assert(i->settings_temp_path); + assert(i->temp_path); + assert(i->final_path); - /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and - * we should incorporate it in the file name if we can */ - i->settings_path = mfree(i->settings_path); + tar_pull_report_progress(i, TAR_FINALIZING); - r = tar_pull_determine_path(i, ".nspawn", &i->settings_path); + r = import_mangle_os_tree(i->temp_path); if (r < 0) goto finish; - r = import_make_read_only(i->settings_temp_path); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); + r = install_file( + AT_FDCWD, i->temp_path, + AT_FDCWD, i->final_path, + INSTALL_READ_ONLY| + (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0)); if (r < 0) { - log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path); + log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path); goto finish; } - i->settings_temp_path = mfree(i->settings_temp_path); + i->temp_path = mfree(i->temp_path); + + if (i->settings_job && + i->settings_job->error == 0) { + + /* Also move the settings file into place, if it exists. Note that we do so only if we also + * moved the tar file in place, to keep things strictly in sync. */ + assert(i->settings_temp_path); + + /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and + * we should incorporate it in the file name if we can */ + i->settings_path = mfree(i->settings_path); + + r = tar_pull_determine_path(i, ".nspawn", &i->settings_path); + if (r < 0) + goto finish; + + r = install_file( + AT_FDCWD, i->settings_temp_path, + AT_FDCWD, i->settings_path, + INSTALL_READ_ONLY| + (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0)); + if (r < 0) { + log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path); + goto finish; + } + + i->settings_temp_path = mfree(i->settings_temp_path); + } } - } - tar_pull_report_progress(i, TAR_COPYING); + tar_pull_report_progress(i, TAR_COPYING); - r = tar_pull_make_local_copy(i); - if (r < 0) - goto finish; + r = tar_pull_make_local_copy(i); + if (r < 0) + goto finish; + } r = 0; @@ -410,6 +492,7 @@ finish: } static int tar_pull_job_on_open_disk_tar(PullJob *j) { + const char *where; TarPull *i; int r; @@ -420,23 +503,39 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) { assert(i->tar_job == j); assert(i->tar_pid <= 0); - if (!i->temp_path) { - r = tempfn_random_child(i->image_root, "tar", &i->temp_path); - if (r < 0) - return log_oom(); + if (i->flags & PULL_DIRECT) + where = i->local; + else { + if (!i->temp_path) { + r = tempfn_random_child(i->image_root, "tar", &i->temp_path); + if (r < 0) + return log_oom(); + } + + where = i->temp_path; } - mkdir_parents_label(i->temp_path, 0700); + (void) mkdir_parents_label(where, 0700); + + if (FLAGS_SET(i->flags, PULL_DIRECT|PULL_FORCE)) + (void) rm_rf(where, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - r = btrfs_subvol_make_fallback(i->temp_path, 0755); + if (i->flags & PULL_BTRFS_SUBVOL) + r = btrfs_subvol_make_fallback(where, 0755); + else + r = mkdir(where, 0755) < 0 ? -errno : 0; + if (r == -EEXIST && (i->flags & PULL_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise, + * because in that case our temporary path collided */ + r = 0; if (r < 0) - return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path); - if (r > 0) { /* actually btrfs subvol */ - (void) import_assign_pool_quota_and_warn(i->image_root); - (void) import_assign_pool_quota_and_warn(i->temp_path); + return log_error_errno(r, "Failed to create directory/subvolume %s: %m", where); + if (r > 0 && (i->flags & PULL_BTRFS_QUOTA)) { /* actually btrfs subvol */ + if (!(i->flags & PULL_DIRECT)) + (void) import_assign_pool_quota_and_warn(i->image_root); + (void) import_assign_pool_quota_and_warn(where); } - j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); + j->disk_fd = import_fork_tar_x(where, &i->tar_pid); if (j->disk_fd < 0) return j->disk_fd; @@ -459,7 +558,7 @@ static int tar_pull_job_on_open_disk_settings(PullJob *j) { return log_oom(); } - mkdir_parents_label(i->settings_temp_path, 0700); + (void) 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) @@ -484,19 +583,24 @@ int tar_pull_start( const char *url, const char *local, PullFlags flags, - ImportVerify verify) { + ImportVerify verify, + const char *checksum) { + PullJob *j; int r; assert(i); - assert(verify < _IMPORT_VERIFY_MAX); - assert(verify >= 0); + assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); + assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); + assert((verify < 0) || !checksum); assert(!(flags & ~PULL_FLAGS_MASK_TAR)); + assert(!(flags & PULL_SETTINGS) || !(flags & PULL_DIRECT)); + assert(!(flags & PULL_SETTINGS) || !checksum); if (!http_url_is_valid(url)) return -EINVAL; - if (local && !hostname_is_valid(local, 0)) + if (local && !pull_validate_local(local, flags)) return -EINVAL; if (i->tar_job) @@ -506,6 +610,10 @@ int tar_pull_start( if (r < 0) return r; + r = free_and_strdup(&i->checksum, checksum); + if (r < 0) + return r; + i->flags = flags; i->verify = verify; @@ -516,52 +624,56 @@ int tar_pull_start( i->tar_job->on_finished = tar_pull_job_on_finished; i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar; - i->tar_job->on_progress = tar_pull_job_on_progress; - i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; + i->tar_job->calc_checksum = checksum || IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE); - r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); - if (r < 0) - return r; + if (!FLAGS_SET(flags, PULL_DIRECT)) { + r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); + if (r < 0) + return r; + } /* Set up download of checksum/signature files */ - r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i); + r = pull_make_verification_jobs( + &i->checksum_job, + &i->signature_job, + verify, + checksum, + url, + i->glue, + tar_pull_job_on_finished, + i); if (r < 0) return r; /* Set up download job for the settings file (.nspawn) */ if (FLAGS_SET(flags, PULL_SETTINGS)) { - r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i); + r = pull_make_auxiliary_job( + &i->settings_job, + url, + tar_strip_suffixes, + ".nspawn", + verify, + i->glue, + tar_pull_job_on_open_disk_settings, + tar_pull_job_on_finished, + i); if (r < 0) return r; - - i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings; - i->settings_job->on_progress = tar_pull_job_on_progress; - i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; } - r = pull_job_begin(i->tar_job); - if (r < 0) - return r; - - if (i->checksum_job) { - i->checksum_job->on_progress = tar_pull_job_on_progress; - i->checksum_job->on_not_found = pull_job_restart_with_sha256sum; - - r = pull_job_begin(i->checksum_job); - if (r < 0) - return r; - } + FOREACH_POINTER(j, + i->tar_job, + i->checksum_job, + i->signature_job, + i->settings_job) { - if (i->signature_job) { - i->signature_job->on_progress = tar_pull_job_on_progress; + if (!j) + continue; - r = pull_job_begin(i->signature_job); - if (r < 0) - return r; - } + j->on_progress = tar_pull_job_on_progress; + j->sync = FLAGS_SET(flags, PULL_SYNC); - if (i->settings_job) { - r = pull_job_begin(i->settings_job); + r = pull_job_begin(j); if (r < 0) return r; } |