1*14675a02SAndroid Build Coastguard Worker /*
2*14675a02SAndroid Build Coastguard Worker * Copyright 2022 Google LLC
3*14675a02SAndroid Build Coastguard Worker *
4*14675a02SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*14675a02SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*14675a02SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*14675a02SAndroid Build Coastguard Worker *
8*14675a02SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*14675a02SAndroid Build Coastguard Worker *
10*14675a02SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*14675a02SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*14675a02SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*14675a02SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*14675a02SAndroid Build Coastguard Worker * limitations under the License.
15*14675a02SAndroid Build Coastguard Worker */
16*14675a02SAndroid Build Coastguard Worker
17*14675a02SAndroid Build Coastguard Worker #include "fcp/client/cache/temp_files.h"
18*14675a02SAndroid Build Coastguard Worker
19*14675a02SAndroid Build Coastguard Worker #include <sys/file.h>
20*14675a02SAndroid Build Coastguard Worker #include <unistd.h>
21*14675a02SAndroid Build Coastguard Worker
22*14675a02SAndroid Build Coastguard Worker #include <cstdlib>
23*14675a02SAndroid Build Coastguard Worker #include <filesystem>
24*14675a02SAndroid Build Coastguard Worker #include <fstream>
25*14675a02SAndroid Build Coastguard Worker #include <memory>
26*14675a02SAndroid Build Coastguard Worker #include <string>
27*14675a02SAndroid Build Coastguard Worker #include <system_error> // NOLINT
28*14675a02SAndroid Build Coastguard Worker
29*14675a02SAndroid Build Coastguard Worker #include "fcp/base/monitoring.h"
30*14675a02SAndroid Build Coastguard Worker #include "fcp/client/diag_codes.pb.h"
31*14675a02SAndroid Build Coastguard Worker
32*14675a02SAndroid Build Coastguard Worker namespace fcp {
33*14675a02SAndroid Build Coastguard Worker namespace client {
34*14675a02SAndroid Build Coastguard Worker namespace cache {
35*14675a02SAndroid Build Coastguard Worker namespace {
36*14675a02SAndroid Build Coastguard Worker
DeleteFilesInDirectory(const std::filesystem::path & directory)37*14675a02SAndroid Build Coastguard Worker absl::Status DeleteFilesInDirectory(const std::filesystem::path& directory) {
38*14675a02SAndroid Build Coastguard Worker if (!std::filesystem::exists(directory)) {
39*14675a02SAndroid Build Coastguard Worker return absl::InvalidArgumentError(
40*14675a02SAndroid Build Coastguard Worker absl::StrCat("Directory does not exist: ", directory.string()));
41*14675a02SAndroid Build Coastguard Worker }
42*14675a02SAndroid Build Coastguard Worker absl::Status status = absl::OkStatus();
43*14675a02SAndroid Build Coastguard Worker // Note this only iterates through the top level directory and will not
44*14675a02SAndroid Build Coastguard Worker // traverse subdirectories.
45*14675a02SAndroid Build Coastguard Worker for (auto& de : std::filesystem::directory_iterator(directory)) {
46*14675a02SAndroid Build Coastguard Worker std::error_code error;
47*14675a02SAndroid Build Coastguard Worker // Save the first error, but attempt to delete the other files.
48*14675a02SAndroid Build Coastguard Worker if (!std::filesystem::remove(de.path(), error)) {
49*14675a02SAndroid Build Coastguard Worker if (status.ok()) {
50*14675a02SAndroid Build Coastguard Worker status = absl::InternalError(absl::StrCat(
51*14675a02SAndroid Build Coastguard Worker "Failed to delete file with error code: ", error.value()));
52*14675a02SAndroid Build Coastguard Worker }
53*14675a02SAndroid Build Coastguard Worker }
54*14675a02SAndroid Build Coastguard Worker }
55*14675a02SAndroid Build Coastguard Worker return status;
56*14675a02SAndroid Build Coastguard Worker }
57*14675a02SAndroid Build Coastguard Worker
58*14675a02SAndroid Build Coastguard Worker } // namespace
59*14675a02SAndroid Build Coastguard Worker
Create(const std::string & cache_dir,LogManager * log_manager)60*14675a02SAndroid Build Coastguard Worker absl::StatusOr<std::unique_ptr<TempFiles>> TempFiles::Create(
61*14675a02SAndroid Build Coastguard Worker const std::string& cache_dir, LogManager* log_manager) {
62*14675a02SAndroid Build Coastguard Worker std::filesystem::path root_path(cache_dir);
63*14675a02SAndroid Build Coastguard Worker if (!root_path.is_absolute()) {
64*14675a02SAndroid Build Coastguard Worker return absl::InvalidArgumentError(
65*14675a02SAndroid Build Coastguard Worker absl::StrCat("The provided path: ", cache_dir,
66*14675a02SAndroid Build Coastguard Worker "is invalid. The path must start with \"/\""));
67*14675a02SAndroid Build Coastguard Worker }
68*14675a02SAndroid Build Coastguard Worker
69*14675a02SAndroid Build Coastguard Worker // Create fcp parent dir in the passed root dir.
70*14675a02SAndroid Build Coastguard Worker std::filesystem::path fcp_base_dir = root_path / kParentDir;
71*14675a02SAndroid Build Coastguard Worker std::error_code error;
72*14675a02SAndroid Build Coastguard Worker std::filesystem::create_directories(fcp_base_dir, error);
73*14675a02SAndroid Build Coastguard Worker if (error.value() != 0) {
74*14675a02SAndroid Build Coastguard Worker return absl::InternalError(absl::StrCat(
75*14675a02SAndroid Build Coastguard Worker "Failed to create TempFiles base directory ",
76*14675a02SAndroid Build Coastguard Worker fcp_base_dir.generic_string(), " with error code ", error.value()));
77*14675a02SAndroid Build Coastguard Worker }
78*14675a02SAndroid Build Coastguard Worker
79*14675a02SAndroid Build Coastguard Worker // Create directory in parent dir for temporary files.
80*14675a02SAndroid Build Coastguard Worker std::filesystem::path temp_files_dir = fcp_base_dir / kTempFilesDir;
81*14675a02SAndroid Build Coastguard Worker std::filesystem::create_directories(temp_files_dir, error);
82*14675a02SAndroid Build Coastguard Worker if (error.value() != 0) {
83*14675a02SAndroid Build Coastguard Worker return absl::InternalError(
84*14675a02SAndroid Build Coastguard Worker absl::StrCat("Failed to create TempFiles temp file directory ",
85*14675a02SAndroid Build Coastguard Worker temp_files_dir.generic_string()));
86*14675a02SAndroid Build Coastguard Worker }
87*14675a02SAndroid Build Coastguard Worker
88*14675a02SAndroid Build Coastguard Worker // We clean up the temp files dir on creation in case we failed to clean it up
89*14675a02SAndroid Build Coastguard Worker // during a previous run (i.e. due to the training process getting killed
90*14675a02SAndroid Build Coastguard Worker // etc.) and to make sure we don't end up in the pathological case where we
91*14675a02SAndroid Build Coastguard Worker // are always crashing partway through training and stranding temp files
92*14675a02SAndroid Build Coastguard Worker // because the TempFiles dtor never runs.
93*14675a02SAndroid Build Coastguard Worker auto cleanup_status = DeleteFilesInDirectory(temp_files_dir);
94*14675a02SAndroid Build Coastguard Worker if (!cleanup_status.ok()) {
95*14675a02SAndroid Build Coastguard Worker log_manager->LogDiag(ProdDiagCode::TEMP_FILES_NATIVE_FAILED_TO_DELETE);
96*14675a02SAndroid Build Coastguard Worker return cleanup_status;
97*14675a02SAndroid Build Coastguard Worker }
98*14675a02SAndroid Build Coastguard Worker return absl::WrapUnique(new TempFiles(temp_files_dir, log_manager));
99*14675a02SAndroid Build Coastguard Worker }
100*14675a02SAndroid Build Coastguard Worker
CreateTempFile(const std::string & prefix,const std::string & suffix)101*14675a02SAndroid Build Coastguard Worker absl::StatusOr<std::string> TempFiles::CreateTempFile(
102*14675a02SAndroid Build Coastguard Worker const std::string& prefix, const std::string& suffix) {
103*14675a02SAndroid Build Coastguard Worker std::filesystem::path candidate_path;
104*14675a02SAndroid Build Coastguard Worker int fd;
105*14675a02SAndroid Build Coastguard Worker do {
106*14675a02SAndroid Build Coastguard Worker candidate_path = temp_files_dir_ /
107*14675a02SAndroid Build Coastguard Worker absl::StrCat(prefix, std::to_string(std::rand()), suffix);
108*14675a02SAndroid Build Coastguard Worker } while ((fd = open(candidate_path.c_str(), O_CREAT | O_EXCL | O_RDWR,
109*14675a02SAndroid Build Coastguard Worker S_IRWXU)) == -1 &&
110*14675a02SAndroid Build Coastguard Worker errno == EEXIST);
111*14675a02SAndroid Build Coastguard Worker close(fd);
112*14675a02SAndroid Build Coastguard Worker std::ofstream tmp_file(candidate_path);
113*14675a02SAndroid Build Coastguard Worker if (!tmp_file) {
114*14675a02SAndroid Build Coastguard Worker return absl::InvalidArgumentError(
115*14675a02SAndroid Build Coastguard Worker absl::StrCat("could not create file ", candidate_path.string()));
116*14675a02SAndroid Build Coastguard Worker }
117*14675a02SAndroid Build Coastguard Worker
118*14675a02SAndroid Build Coastguard Worker return candidate_path.string();
119*14675a02SAndroid Build Coastguard Worker }
120*14675a02SAndroid Build Coastguard Worker
~TempFiles()121*14675a02SAndroid Build Coastguard Worker TempFiles::~TempFiles() {
122*14675a02SAndroid Build Coastguard Worker if (!DeleteFilesInDirectory(temp_files_dir_).ok()) {
123*14675a02SAndroid Build Coastguard Worker log_manager_.LogDiag(ProdDiagCode::TEMP_FILES_NATIVE_FAILED_TO_DELETE);
124*14675a02SAndroid Build Coastguard Worker }
125*14675a02SAndroid Build Coastguard Worker }
126*14675a02SAndroid Build Coastguard Worker
127*14675a02SAndroid Build Coastguard Worker } // namespace cache
128*14675a02SAndroid Build Coastguard Worker } // namespace client
129*14675a02SAndroid Build Coastguard Worker } // namespace fcp
130