GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/network/jobinfo.cc
Date: 2026-10-11 02:40:02
Exec Total Coverage
Lines: 62 179 34.6%
Branches: 33 206 16.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "jobinfo.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <inttypes.h>
10 #include <stdint.h>
11 #include <sys/stat.h>
12
13 #include "util/capabilities.h"
14 #include "util/logging.h"
15 #include "util/string.h"
16
17 namespace download {
18
19 std::atomic<int64_t> JobInfo::next_uuid(0);
20
21 5072 JobInfo::JobInfo(const std::string *u, const bool c, const bool ph,
22 5072 const shash::Any *h, cvmfs::Sink *s) {
23
1/2
✓ Branch 1 taken 5072 times.
✗ Branch 2 not taken.
5072 Init();
24
25 5072 url_ = u;
26 5072 compressed_ = c;
27 5072 probe_hosts_ = ph;
28 5072 expected_hash_ = h;
29 5072 sink_ = s;
30 5072 }
31
32 ✗ JobInfo::JobInfo(const std::string *u, const bool ph) {
33 ✗ Init();
34
35 ✗ url_ = u;
36 ✗ probe_hosts_ = ph;
37 ✗ head_request_ = true;
38 }
39
40
41 ✗ bool JobInfo::IsFileNotFound() {
42 ✗ if (HasPrefix(*url_, "file://", true /* ignore_case */))
43 ✗ return error_code_ == kFailHostConnection;
44
45 ✗ return http_code_ == 404;
46 }
47
48 5709 void JobInfo::Init() {
49 5709 id_ = next_uuid.fetch_add(1);
50 5709 pipe_job_results = NULL;
51 5709 url_ = NULL;
52 5709 compressed_ = false;
53 5709 probe_hosts_ = false;
54 5709 head_request_ = false;
55 5709 follow_redirects_ = false;
56 5709 force_nocache_ = false;
57 5709 pid_ = -1;
58 5709 uid_ = -1;
59 5709 gid_ = -1;
60 5709 cred_data_ = NULL;
61 5709 interrupt_cue_ = NULL;
62 5709 sink_ = NULL;
63 5709 expected_hash_ = NULL;
64 5709 path_info_ = NULL;
65 //
66 5709 range_offset_ = -1;
67 5709 range_size_ = -1;
68 //
69 5709 curl_handle_ = NULL;
70 5709 headers_ = NULL;
71 5709 info_header_ = NULL;
72 5709 tracing_header_pid_ = NULL;
73 5709 tracing_header_gid_ = NULL;
74 5709 tracing_header_uid_ = NULL;
75 5709 nocache_ = false;
76 5709 error_code_ = kFailOther;
77 5709 http_code_ = -1;
78 5709 link_ = "";
79 5709 num_used_proxies_ = 0;
80 5709 num_used_metalinks_ = 0;
81 5709 num_used_hosts_ = 0;
82 5709 num_retries_ = 0;
83 5709 backoff_ms_ = 0;
84 5709 current_metalink_chain_index_ = -1;
85 5709 current_host_chain_index_ = -1;
86
87 5709 allow_failure_ = false;
88
89 5709 memset(&zstream_, 0, sizeof(zstream_));
90 5709 }
91
92
93 /*
94 * Return true if input character is escaped to 3 output characters,
95 * otherwise return false and leave the input character in the first
96 * output character.
97 */
98 492442 bool JobInfo::EscapeUrlChar(unsigned char input, char output[3]) {
99
8/8
✓ Branch 0 taken 441040 times.
✓ Branch 1 taken 51402 times.
✓ Branch 2 taken 342786 times.
✓ Branch 3 taken 98254 times.
✓ Branch 4 taken 336457 times.
✓ Branch 5 taken 57731 times.
✓ Branch 6 taken 309369 times.
✓ Branch 7 taken 27088 times.
492442 if (((input >= '0') && (input <= '9')) || ((input >= 'A') && (input <= 'Z'))
100
8/8
✓ Branch 0 taken 290180 times.
✓ Branch 1 taken 76920 times.
✓ Branch 2 taken 324 times.
✓ Branch 3 taken 289856 times.
✓ Branch 4 taken 35483 times.
✓ Branch 5 taken 41761 times.
✓ Branch 6 taken 29154 times.
✓ Branch 7 taken 6329 times.
367100 || ((input >= 'a') && (input <= 'z')) || (input == '/') || (input == ':')
101
6/8
✓ Branch 0 taken 19670 times.
✓ Branch 1 taken 9484 times.
✓ Branch 2 taken 19670 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19670 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19513 times.
✓ Branch 7 taken 157 times.
29154 || (input == '.') || (input == '@') || (input == '+') || (input == '-')
102
5/8
✓ Branch 0 taken 324 times.
✓ Branch 1 taken 19189 times.
✓ Branch 2 taken 324 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 324 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 324 times.
✗ Branch 7 not taken.
19513 || (input == '_') || (input == '~') || (input == '[') || (input == ']')
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
324 || (input == ',')) {
104 492118 output[0] = static_cast<char>(input);
105 492118 return false;
106 }
107
108 324 output[0] = '%';
109 648 output[1] = static_cast<char>((input / 16)
110
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 252 times.
324 + ((input / 16 <= 9) ? '0' : 'A' - 10));
111 648 output[2] = static_cast<char>((input % 16)
112
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 180 times.
324 + ((input % 16 <= 9) ? '0' : 'A' - 10));
113 324 return true;
114 }
115
116
117 namespace {
118
119 ✗ std::string EscapeHeader(const std::string &header) {
120 ✗ std::string escaped = "";
121 char escaped_char[3];
122 ✗ for (std::string::const_iterator i = header.begin(); i != header.end(); i++) {
123 ✗ if (JobInfo::EscapeUrlChar(*i, escaped_char)) {
124 ✗ for (unsigned j = 0; j < 3; ++j) {
125 ✗ escaped += escaped_char[j];
126 }
127 } else {
128 ✗ escaped += escaped_char[0];
129 }
130 }
131
132 ✗ return escaped;
133 }
134
135 } // namespace
136
137 /*
138 * Return the filled-in template of CVMFS_INFO_HEADER
139 */
140 ✗ std::string JobInfo::GetInfoHeaderContents(const std::string &templ) {
141 enum ParseMode {
142 kParseModeDefault, // reading literal characters
143 kParseModeAfterPercent, // just saw '%'
144 kParseModeInKey, // inside '%{...}'
145 };
146
147 enum MatchMode {
148 kMatchModeSkipping, // skipping to next '\0' separator
149 kMatchModeMatching, // comparing chars against variable name
150 kMatchModeCollecting, // name matched, collecting value
151 };
152
153 ✗ std::string answer = "";
154 ✗ std::string key;
155 ✗ ParseMode parsemode = kParseModeDefault;
156 ✗ std::vector<char> envbuf;
157 ✗ bool env_attempted = false;
158
159 ✗ for (std::string::const_iterator i = templ.begin(); i != templ.end(); i++) {
160 ✗ const char c = *i;
161 ✗ switch (parsemode) {
162 ✗ case kParseModeDefault:
163 ✗ if (c == '%') {
164 ✗ parsemode = kParseModeAfterPercent;
165 ✗ key = "";
166 } else {
167 ✗ answer += c;
168 }
169 ✗ break;
170 ✗ case kParseModeAfterPercent:
171 ✗ if (c == '{') {
172 ✗ parsemode = kParseModeInKey;
173 } else {
174 ✗ parsemode = kParseModeDefault;
175 ✗ answer += '%';
176 ✗ answer += c;
177 }
178 ✗ break;
179 ✗ case kParseModeInKey:
180 ✗ if (c != '}') {
181 ✗ key += c;
182 } else {
183 ✗ parsemode = kParseModeDefault;
184 ✗ if (key == "path") {
185 ✗ if (path_info_ != NULL) {
186 ✗ answer += EscapeHeader(*path_info_);
187 } else {
188 ✗ answer += "-";
189 }
190 ✗ } else if (key == "pid") {
191 ✗ if (pid_ != static_cast<pid_t>(-1)) {
192 ✗ answer += StringifyInt(pid_);
193 } else {
194 ✗ answer += "-";
195 }
196 ✗ } else if (key == "uid") {
197 ✗ if (uid_ != static_cast<uid_t>(-1)) {
198 ✗ answer += StringifyInt(uid_);
199 } else {
200 ✗ answer += "-";
201 }
202 ✗ } else if (key == "gid") {
203 ✗ if (gid_ != static_cast<gid_t>(-1)) {
204 ✗ answer += StringifyInt(gid_);
205 } else {
206 ✗ answer += "-";
207 }
208 ✗ } else if (key.substr(0, 4) == "env:") {
209 ✗ if (!env_attempted) {
210 ✗ env_attempted = true;
211 #ifndef __APPLE__
212 ✗ if (pid_ != static_cast<pid_t>(-1)) {
213 ✗ ObtainDacReadSearchCapability();
214 ✗ ObtainSysPtraceCapability();
215 ✗ const std::string fname = "/proc/" + StringifyInt(pid_)
216 ✗ + "/environ";
217 ✗ const int fd = open(fname.c_str(), O_RDONLY);
218 ✗ if (fd != -1) {
219 // Unfortunately fstat does not show the size so need to
220 // read it to find out the size
221 ssize_t n;
222 ✗ int size = 0;
223 char buf[BUFSIZ];
224 ✗ while ((n = read(fd, buf, BUFSIZ)) > 0) {
225 ✗ size += static_cast<int>(n);
226 }
227 ✗ if ((n >= 0) && (size > 0)) {
228 ✗ if (lseek(fd, 0, SEEK_SET) >= 0) {
229 ✗ envbuf.resize(size);
230 ✗ if (read(fd, envbuf.data(), size) > 0) {
231 ✗ LogCvmfs(kLogDownload, kLogDebug,
232 "(job id %" PRId64 ") read %d bytes from %s",
233 id_, size, fname.c_str());
234 } else {
235 ✗ envbuf.clear();
236 }
237 }
238 }
239 ✗ close(fd);
240 } else {
241 ✗ LogCvmfs(kLogDownload, kLogDebug,
242 "(job id %" PRId64 ") unable to open %s: %s", id_,
243 ✗ fname.c_str(), strerror(errno));
244 }
245 ✗ DropSysPtraceCapability();
246 ✗ DropDacReadSearchCapability();
247 }
248 #endif
249 }
250 ✗ if (!envbuf.empty()) {
251 ✗ const char * const var = key.c_str()
252 ✗ + 4; // everything after "env:"
253 ✗ const char *varp = var;
254 ✗ std::string val = "";
255 ✗ MatchMode matchmode = kMatchModeMatching;
256 ✗ const char * const endp = envbuf.data() + envbuf.size();
257 ✗ for (const char *p = envbuf.data(); p < endp; p++) {
258 ✗ switch (matchmode) {
259 ✗ case kMatchModeSkipping:
260 // skipping to next null character
261 ✗ if (*p == '\0') {
262 ✗ varp = var;
263 ✗ matchmode = kMatchModeMatching;
264 }
265 ✗ break;
266 ✗ case kMatchModeMatching:
267 // matching variable name
268 ✗ if (*p == '\0') {
269 // premature end without an '='
270 ✗ varp = var;
271 ✗ } else if (*varp == *p) {
272 // so far so good
273 ✗ ++varp;
274 ✗ } else if ((*varp == '\0') && (*p == '=')) {
275 // matched
276 ✗ matchmode = kMatchModeCollecting;
277 } else {
278 // didn't match
279 ✗ matchmode = kMatchModeSkipping;
280 }
281 ✗ break;
282 ✗ case kMatchModeCollecting:
283 // matched, collecting value
284 ✗ if (*p == '\0') {
285 // all done
286 ✗ p = endp;
287 ✗ break;
288 }
289 ✗ val += *p;
290 ✗ break;
291 }
292 }
293 ✗ if (val != "") {
294 ✗ answer += ' ';
295 ✗ answer += EscapeHeader(val);
296 }
297 }
298 }
299 }
300 ✗ break;
301 }
302 }
303
304 ✗ return answer;
305 }
306
307 } // namespace download
308
309