/* This function will break if the same HASH.tar full file is downloaded * multiple times in parallel. */ int untar_full_download(void *data) { struct file *file = data; char *tarfile; char *tar_dotfile; char *targetfile; struct stat stat; int err; char *tarcommand; string_or_die(&tar_dotfile, "%s/download/.%s.tar", state_dir, file->hash); string_or_die(&tarfile, "%s/download/%s.tar", state_dir, file->hash); string_or_die(&targetfile, "%s/staged/%s", state_dir, file->hash); /* If valid target file already exists, we're done. * NOTE: this should NEVER happen given the checking that happens * ahead of queueing a download. But... */ if (lstat(targetfile, &stat) == 0) { if (verify_file(file, targetfile)) { unlink(tar_dotfile); unlink(tarfile); free(tar_dotfile); free(tarfile); free(targetfile); return 0; } else { unlink(tarfile); unlink(targetfile); } } else if (lstat(tarfile, &stat) == 0) { /* remove tar file from possible past failure */ unlink(tarfile); } err = rename(tar_dotfile, tarfile); if (err) { free(tar_dotfile); goto exit; } free(tar_dotfile); err = check_tarfile_content(file, tarfile); if (err) { goto exit; } /* modern tar will automatically determine the compression type used */ string_or_die(&tarcommand, TAR_COMMAND " -C %s/staged/ " TAR_PERM_ATTR_ARGS " -xf %s 2> /dev/null", state_dir, tarfile); err = system(tarcommand); if (WIFEXITED(err)) { err = WEXITSTATUS(err); } free(tarcommand); if (err) { printf("ignoring tar extract failure for fullfile %s.tar (ret %d)\n", file->hash, err); goto exit; /* FIXME: can we respond meaningfully to tar error codes? * symlink untars may have perm/xattr complaints and non-zero * tar return, but symlink (probably?) untarred ok. * * Also getting complaints on some new regular files? * * Either way we verify the hash later, so on error there, * something could try to recover? */ } else { /* Only unlink when tar succeeded, so we can examine the tar file * in the failure case. */ unlink(tarfile); } err = lstat(targetfile, &stat); if (!err && !verify_file(file, targetfile)) { /* Download was successful but the hash was bad. This is fatal*/ printf("Error: File content hash mismatch for %s (bad server data?)\n", targetfile); exit(EXIT_FAILURE); } exit: free(tarfile); free(targetfile); if (err) { unlink_all_staged_content(file); } return err; }
/* This function will break if the same HASH.tar full file is downloaded * multiple times in parallel. */ int untar_full_download(void *data) { struct file *file = data; char *tarfile; char *tar_dotfile; char *targetfile; struct stat stat; int err; string_or_die(&tar_dotfile, "%s/download/.%s.tar", state_dir, file->hash); string_or_die(&tarfile, "%s/download/%s.tar", state_dir, file->hash); string_or_die(&targetfile, "%s/staged/%s", state_dir, file->hash); /* If valid target file already exists, we're done. * NOTE: this should NEVER happen given the checking that happens * ahead of queueing a download. But... */ if (lstat(targetfile, &stat) == 0) { if (verify_file(file, targetfile)) { unlink(tar_dotfile); unlink(tarfile); free_string(&tar_dotfile); free_string(&tarfile); free_string(&targetfile); return 0; } else { unlink(tarfile); unlink(targetfile); } } else if (lstat(tarfile, &stat) == 0) { /* remove tar file from possible past failure */ unlink(tarfile); } err = rename(tar_dotfile, tarfile); if (err) { free_string(&tar_dotfile); goto exit; } free_string(&tar_dotfile); err = check_tarfile_content(file, tarfile); if (err) { goto exit; } /* modern tar will automatically determine the compression type used */ char *outputdir; string_or_die(&outputdir, "%s/staged", state_dir); err = extract_to(tarfile, outputdir); free_string(&outputdir); if (err) { fprintf(stderr, "ignoring tar extract failure for fullfile %s.tar (ret %d)\n", file->hash, err); goto exit; /* TODO: respond to ARCHIVE_RETRY error codes * libarchive returns ARCHIVE_RETRY when tar extraction fails but the * operation is retry-able. We need to determine if it is worth our time * to retry in these situations. */ } else { /* Only unlink when tar succeeded, so we can examine the tar file * in the failure case. */ unlink(tarfile); } err = lstat(targetfile, &stat); if (!err && !verify_file(file, targetfile)) { /* Download was successful but the hash was bad. This is fatal*/ fprintf(stderr, "Error: File content hash mismatch for %s (bad server data?)\n", targetfile); exit(EXIT_FAILURE); } exit: free_string(&tarfile); free_string(&targetfile); if (err) { unlink_all_staged_content(file); } return err; }