GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/upload_local.cc
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 88 148 59.5%
Branches: 82 255 32.2%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "upload_local.h"
6
7 #include <errno.h>
8
9 #include <string>
10
11 #include "compression/compression.h"
12 #include "util/logging.h"
13 #include "util/posix.h"
14
15 namespace upload {
16
17 1635 LocalUploader::LocalUploader(const SpoolerDefinition &spooler_definition)
18 : AbstractUploader(spooler_definition)
19 3270 , backend_file_mode_(default_backend_file_mode_ ^ GetUmask())
20
1/2
✓ Branch 1 taken 1635 times.
✗ Branch 2 not taken.
1635 , backend_dir_mode_(default_backend_dir_mode_ ^ GetUmask())
21
1/2
✓ Branch 1 taken 1635 times.
✗ Branch 2 not taken.
1635 , upstream_path_(spooler_definition.spooler_configuration)
22
2/4
✓ Branch 2 taken 1635 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1635 times.
✗ Branch 6 not taken.
3270 , temporary_path_(spooler_definition.temporary_path) {
23
2/4
✓ Branch 1 taken 1635 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1635 times.
✗ Branch 4 not taken.
1635 assert(spooler_definition.IsValid()
24 && spooler_definition.driver_type == SpoolerDefinition::Local);
25
26 1635 atomic_init32(&copy_errors_);
27 1635 }
28
29 1860 bool LocalUploader::WillHandle(const SpoolerDefinition &spooler_definition) {
30 1860 return spooler_definition.driver_type == SpoolerDefinition::Local;
31 }
32
33 2833 unsigned int LocalUploader::GetNumberOfErrors() const {
34 2833 return atomic_read32(&copy_errors_);
35 }
36
37 bool LocalUploader::Create() {
38 return MakeCacheDirectories(upstream_path_ + "/data", backend_dir_mode_)
39 && MkdirDeep(upstream_path_ + "/stats", backend_dir_mode_, false);
40 }
41
42 25297 void LocalUploader::DoUpload(const std::string &remote_path,
43 IngestionSource *source,
44 const CallbackTN *callback) {
45
1/2
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
25297 LogCvmfs(kLogSpooler, kLogVerboseMsg, "FileUpload call started.");
46
47 // create destination in backend storage temporary directory
48 25297 std::string tmp_path;
49
2/4
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25297 times.
✗ Branch 5 not taken.
25297 FILE *ftmp = CreateTempFile(temporary_path_ + "/upload", 0666, "w",
50 &tmp_path);
51
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25297 times.
25297 if (ftmp == NULL) {
52 LogCvmfs(kLogSpooler, kLogVerboseMsg,
53 "failed to create temp path for "
54 "upload of file '%s' (errno: %d)",
55 source->GetPath().c_str(), errno);
56 atomic_inc32(&copy_errors_);
57 Respond(callback, UploaderResults(1, source->GetPath()));
58 return;
59 }
60
61 // copy file into controlled temporary directory location
62
1/2
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
25297 const bool rvb = source->Open();
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25297 times.
25297 if (!rvb) {
64 fclose(ftmp);
65 unlink(tmp_path.c_str());
66 atomic_inc32(&copy_errors_);
67 Respond(callback, UploaderResults(100, source->GetPath()));
68 return;
69 }
70 unsigned char buffer[kPageSize];
71 ssize_t rbytes;
72 do {
73
1/2
✓ Branch 1 taken 9609553 times.
✗ Branch 2 not taken.
9609553 rbytes = source->Read(buffer, kPageSize);
74 9609553 size_t wbytes = 0;
75
2/2
✓ Branch 0 taken 9593377 times.
✓ Branch 1 taken 16176 times.
9609553 if (rbytes > 0) {
76
1/2
✓ Branch 1 taken 9593377 times.
✗ Branch 2 not taken.
9593377 wbytes = fwrite(buffer, 1, rbytes, ftmp);
77 }
78
2/4
✓ Branch 0 taken 9609553 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9609553 times.
9609553 if ((rbytes < 0) || (static_cast<size_t>(rbytes) != wbytes)) {
79 source->Close();
80 fclose(ftmp);
81 unlink(tmp_path.c_str());
82 atomic_inc32(&copy_errors_);
83 Respond(callback, UploaderResults(100, source->GetPath()));
84 return;
85 }
86
2/2
✓ Branch 0 taken 9584256 times.
✓ Branch 1 taken 25297 times.
9609553 } while (rbytes == kPageSize);
87
1/2
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
25297 source->Close();
88
1/2
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
25297 fclose(ftmp);
89
90 // move the file in place (atomic operation)
91
1/2
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
25297 const int rvi = Move(tmp_path, remote_path);
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25297 times.
25297 if (rvi != 0) {
93 LogCvmfs(kLogSpooler, kLogVerboseMsg,
94 "failed to move file '%s' from the "
95 "staging area to the final location: "
96 "'%s'",
97 tmp_path.c_str(), remote_path.c_str());
98 unlink(tmp_path.c_str());
99 atomic_inc32(&copy_errors_);
100 Respond(callback, UploaderResults(rvi, source->GetPath()));
101 return;
102 }
103
104
3/6
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25297 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25297 times.
✗ Branch 8 not taken.
25297 Respond(callback, UploaderResults(rvi, source->GetPath()));
105
1/2
✓ Branch 1 taken 25297 times.
✗ Branch 2 not taken.
25297 }
106
107 6650 UploadStreamHandle *LocalUploader::InitStreamedUpload(
108 const CallbackTN *callback) {
109 6650 std::string tmp_path;
110
1/2
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
6650 const int tmp_fd = CreateAndOpenTemporaryChunkFile(&tmp_path);
111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6650 times.
6650 if (tmp_fd < 0) {
112 atomic_inc32(&copy_errors_);
113 return NULL;
114 }
115
116
2/4
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6650 times.
✗ Branch 5 not taken.
6650 return new LocalStreamHandle(callback, tmp_fd, tmp_path);
117 6650 }
118
119 40298 void LocalUploader::StreamedUpload(UploadStreamHandle *handle,
120 UploadBuffer buffer,
121 const CallbackTN *callback) {
122 40298 LocalStreamHandle *local_handle = static_cast<LocalStreamHandle *>(handle);
123
124 40298 const size_t bytes_written = write(local_handle->file_descriptor, buffer.data,
125 40298 buffer.size);
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40298 times.
40298 if (bytes_written != buffer.size) {
127 const int cpy_errno = errno;
128 LogCvmfs(kLogSpooler, kLogVerboseMsg,
129 "failed to write %lu bytes to '%s' "
130 "(errno: %d)",
131 buffer.size, local_handle->temporary_path.c_str(), cpy_errno);
132 atomic_inc32(&copy_errors_);
133 Respond(callback,
134 UploaderResults(UploaderResults::kBufferUpload, cpy_errno));
135 return;
136 }
137
138
1/2
✓ Branch 2 taken 40298 times.
✗ Branch 3 not taken.
40298 Respond(callback, UploaderResults(UploaderResults::kBufferUpload, 0));
139 }
140
141 6650 void LocalUploader::FinalizeStreamedUpload(UploadStreamHandle *handle,
142 const shash::Any &content_hash) {
143 6650 int retval = 0;
144 6650 LocalStreamHandle *local_handle = static_cast<LocalStreamHandle *>(handle);
145
146
1/2
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
6650 retval = close(local_handle->file_descriptor);
147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6650 times.
6650 if (retval != 0) {
148 const int cpy_errno = errno;
149 LogCvmfs(kLogSpooler, kLogVerboseMsg,
150 "failed to close temp file '%s' "
151 "(errno: %d)",
152 local_handle->temporary_path.c_str(), cpy_errno);
153 atomic_inc32(&copy_errors_);
154 Respond(handle->commit_callback,
155 UploaderResults(UploaderResults::kChunkCommit, cpy_errno));
156 return;
157 }
158
159 6650 std::string final_path;
160
2/4
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6650 times.
6650 if (local_handle->remote_path != "") {
161 final_path = local_handle->remote_path;
162 } else {
163
2/4
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6650 times.
✗ Branch 5 not taken.
6650 final_path = "data/" + content_hash.MakePath();
164 }
165
2/4
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6650 times.
✗ Branch 4 not taken.
6650 if (!Peek(final_path)) {
166
1/2
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
6650 retval = Move(local_handle->temporary_path, final_path);
167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6650 times.
6650 if (retval != 0) {
168 const int cpy_errno = errno;
169 LogCvmfs(kLogSpooler, kLogVerboseMsg,
170 "failed to move temp file '%s' to "
171 "final location '%s' (errno: %d)",
172 local_handle->temporary_path.c_str(), final_path.c_str(),
173 cpy_errno);
174 atomic_inc32(&copy_errors_);
175 Respond(handle->commit_callback,
176 UploaderResults(UploaderResults::kChunkCommit, cpy_errno));
177 return;
178 }
179 6650 if (!content_hash.HasSuffix()
180
5/6
✓ Branch 0 taken 1850 times.
✓ Branch 1 taken 4800 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1850 times.
✓ Branch 4 taken 4800 times.
✓ Branch 5 taken 1850 times.
6650 || content_hash.suffix == shash::kSuffixPartial) {
181
1/2
✓ Branch 1 taken 4800 times.
✗ Branch 2 not taken.
4800 CountUploadedChunks();
182
4/8
✓ Branch 1 taken 4800 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4800 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4800 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4800 times.
✗ Branch 11 not taken.
4800 CountUploadedBytes(GetFileSize(upstream_path_ + "/" + final_path));
183
2/2
✓ Branch 0 taken 1802 times.
✓ Branch 1 taken 48 times.
1850 } else if (content_hash.suffix == shash::kSuffixCatalog) {
184
1/2
✓ Branch 1 taken 1802 times.
✗ Branch 2 not taken.
1802 CountUploadedCatalogs();
185
4/8
✓ Branch 1 taken 1802 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1802 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1802 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1802 times.
✗ Branch 11 not taken.
1802 CountUploadedCatalogBytes(GetFileSize(upstream_path_ + "/" + final_path));
186 }
187 } else {
188 const int retval = unlink(local_handle->temporary_path.c_str());
189 if (retval != 0) {
190 LogCvmfs(kLogSpooler, kLogVerboseMsg,
191 "failed to remove temporary file '%s' (errno: %d)",
192 local_handle->temporary_path.c_str(), errno);
193 }
194 CountDuplicates();
195 }
196
197 6650 const CallbackTN *callback = handle->commit_callback;
198
1/2
✓ Branch 0 taken 6650 times.
✗ Branch 1 not taken.
6650 delete local_handle;
199
200
2/4
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6650 times.
✗ Branch 5 not taken.
6650 Respond(callback, UploaderResults(UploaderResults::kChunkCommit, 0));
201
1/2
✓ Branch 1 taken 6650 times.
✗ Branch 2 not taken.
6650 }
202
203 /**
204 * TODO(jblomer): investigate if parallelism increases the GC speed on local
205 * disks.
206 */
207 48 void LocalUploader::DoRemoveAsync(const std::string &file_to_delete) {
208
1/2
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
48 const int retval = unlink((upstream_path_ + "/" + file_to_delete).c_str());
209
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
48 if ((retval != 0) && (errno != ENOENT))
210 atomic_inc32(&copy_errors_);
211
1/2
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
48 Respond(NULL, UploaderResults());
212 48 }
213
214 6890 bool LocalUploader::Peek(const std::string &path) {
215
2/4
✓ Branch 2 taken 6890 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6890 times.
✗ Branch 6 not taken.
6890 const bool retval = FileExists(upstream_path_ + "/" + path);
216 6890 return retval;
217 }
218
219 bool LocalUploader::Mkdir(const std::string &path) {
220 return MkdirDeep(upstream_path_ + "/" + path, backend_dir_mode_, false);
221 }
222
223 48 bool LocalUploader::PlaceBootstrappingShortcut(const shash::Any &object) {
224
2/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
48 const std::string src = "data/" + object.MakePath();
225
3/6
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
96 const std::string dest = upstream_path_ + "/" + object.MakeAlternativePath();
226
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
96 return SymlinkForced(src, dest);
227 48 }
228
229 31947 int LocalUploader::Move(const std::string &local_path,
230 const std::string &remote_path) const {
231
2/4
✓ Branch 1 taken 31947 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 31947 times.
✗ Branch 5 not taken.
31947 const std::string destination_path = upstream_path_ + "/" + remote_path;
232
233 // make sure the file has the right permissions
234 31947 int retval = chmod(local_path.c_str(), backend_file_mode_);
235
1/2
✓ Branch 0 taken 31947 times.
✗ Branch 1 not taken.
31947 int retcode = (retval == 0) ? 0 : 101;
236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31947 times.
31947 if (retcode != 0) {
237 LogCvmfs(kLogSpooler, kLogVerboseMsg,
238 "failed to set file permission '%s' "
239 "errno: %d",
240 local_path.c_str(), errno);
241 return retcode;
242 }
243
244 // move the file in place
245 31947 retval = rename(local_path.c_str(), destination_path.c_str());
246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31947 times.
31947 retcode = (retval == 0) ? 0 : errno;
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31947 times.
31947 if (retcode != 0) {
248 LogCvmfs(kLogSpooler, kLogVerboseMsg,
249 "failed to move file '%s' to '%s' "
250 "errno: %d",
251 local_path.c_str(), remote_path.c_str(), errno);
252 }
253
254 31947 return retcode;
255 31947 }
256
257 int64_t LocalUploader::DoGetObjectSize(const std::string &file_name) {
258 return GetFileSize(upstream_path_ + "/" + file_name);
259 }
260
261 } // namespace upload
262