GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/network/jobinfo.cc
Date: 2026-10-04 02:40:33
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 <errno.h>
6 #include <fcntl.h>
7 #include <inttypes.h>
8 #include <stdint.h>
9 #include <sys/stat.h>
10
11 #include "jobinfo.h"
12 #include "util/capabilities.h"
13 #include "util/logging.h"
14 #include "util/string.h"
15
16 namespace download {
17
18 atomic_int64 JobInfo::next_uuid = 0;
19
20 2537 JobInfo::JobInfo(const std::string *u, const bool c, const bool ph,
21 2537 const shash::Any *h, cvmfs::Sink *s) {
22
1/2
✓ Branch 1 taken 2537 times.
✗ Branch 2 not taken.
2537 Init();
23
24 2537 url_ = u;
25 2537 compressed_ = c;
26 2537 probe_hosts_ = ph;
27 2537 expected_hash_ = h;
28 2537 sink_ = s;
29 2537 }
30
31 ✗ JobInfo::JobInfo(const std::string *u, const bool ph) {
32 ✗ Init();
33
34 ✗ url_ = u;
35 ✗ probe_hosts_ = ph;
36 ✗ head_request_ = true;
37 }
38
39
40 ✗ bool JobInfo::IsFileNotFound() {
41 ✗ if (HasPrefix(*url_, "file://", true /* ignore_case */))
42 ✗ return error_code_ == kFailHostConnection;
43
44 ✗ return http_code_ == 404;
45 }
46
47 3336 void JobInfo::Init() {
48 3336 id_ = atomic_xadd64(&next_uuid, 1);
49 3336 pipe_job_results = NULL;
50 3336 url_ = NULL;
51 3336 compressed_ = false;
52 3336 probe_hosts_ = false;
53 3336 head_request_ = false;
54 3336 follow_redirects_ = false;
55 3336 force_nocache_ = false;
56 3336 pid_ = -1;
57 3336 uid_ = -1;
58 3336 gid_ = -1;
59 3336 cred_data_ = NULL;
60 3336 interrupt_cue_ = NULL;
61 3336 sink_ = NULL;
62 3336 expected_hash_ = NULL;
63 3336 path_info_ = NULL;
64 //
65 3336 range_offset_ = -1;
66 3336 range_size_ = -1;
67 //
68 3336 curl_handle_ = NULL;
69 3336 headers_ = NULL;
70 3336 info_header_ = NULL;
71 3336 tracing_header_pid_ = NULL;
72 3336 tracing_header_gid_ = NULL;
73 3336 tracing_header_uid_ = NULL;
74 3336 nocache_ = false;
75 3336 error_code_ = kFailOther;
76 3336 http_code_ = -1;
77 3336 link_ = "";
78 3336 num_used_proxies_ = 0;
79 3336 num_used_metalinks_ = 0;
80 3336 num_used_hosts_ = 0;
81 3336 num_retries_ = 0;
82 3336 backoff_ms_ = 0;
83 3336 current_metalink_chain_index_ = -1;
84 3336 current_host_chain_index_ = -1;
85
86 3336 allow_failure_ = false;
87
88 3336 memset(&zstream_, 0, sizeof(zstream_));
89 3336 }
90
91
92 /*
93 * Return true if input character is escaped to 3 output characters,
94 * otherwise return false and leave the input character in the first
95 * output character.
96 */
97 295143 bool JobInfo::EscapeUrlChar(unsigned char input, char output[3]) {
98
8/8
✓ Branch 0 taken 262647 times.
✓ Branch 1 taken 32496 times.
✓ Branch 2 taken 191324 times.
✓ Branch 3 taken 71323 times.
✓ Branch 4 taken 186976 times.
✓ Branch 5 taken 36844 times.
✓ Branch 6 taken 177422 times.
✓ Branch 7 taken 9554 times.
295143 if (((input >= '0') && (input <= '9')) || ((input >= 'A') && (input <= 'Z'))
99
8/8
✓ Branch 0 taken 167346 times.
✓ Branch 1 taken 46920 times.
✓ Branch 2 taken 414 times.
✓ Branch 3 taken 166932 times.
✓ Branch 4 taken 21835 times.
✓ Branch 5 taken 25499 times.
✓ Branch 6 taken 17487 times.
✓ Branch 7 taken 4348 times.
214266 || ((input >= 'a') && (input <= 'z')) || (input == '/') || (input == ':')
100
6/8
✓ Branch 0 taken 10544 times.
✓ Branch 1 taken 6943 times.
✓ Branch 2 taken 10544 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10544 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10490 times.
✓ Branch 7 taken 54 times.
17487 || (input == '.') || (input == '@') || (input == '+') || (input == '-')
101
5/8
✓ Branch 0 taken 414 times.
✓ Branch 1 taken 10076 times.
✓ Branch 2 taken 414 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 414 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 414 times.
✗ Branch 7 not taken.
10490 || (input == '_') || (input == '~') || (input == '[') || (input == ']')
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
414 || (input == ',')) {
103 294729 output[0] = static_cast<char>(input);
104 294729 return false;
105 }
106
107 414 output[0] = '%';
108 828 output[1] = static_cast<char>((input / 16)
109
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 322 times.
414 + ((input / 16 <= 9) ? '0' : 'A' - 10));
110 828 output[2] = static_cast<char>((input % 16)
111
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 230 times.
414 + ((input % 16 <= 9) ? '0' : 'A' - 10));
112 414 return true;
113 }
114
115
116 namespace {
117
118 ✗ std::string EscapeHeader(const std::string &header) {
119 ✗ std::string escaped = "";
120 char escaped_char[3];
121 ✗ for (std::string::const_iterator i = header.begin(); i != header.end(); i++) {
122 ✗ if (JobInfo::EscapeUrlChar(*i, escaped_char)) {
123 ✗ for (unsigned j = 0; j < 3; ++j) {
124 ✗ escaped += escaped_char[j];
125 }
126 } else {
127 ✗ escaped += escaped_char[0];
128 }
129 }
130
131 ✗ return escaped;
132 }
133
134 } // namespace
135
136 /*
137 * Return the filled-in template of CVMFS_INFO_HEADER
138 */
139 ✗ std::string JobInfo::GetInfoHeaderContents(const std::string &templ) {
140 enum ParseMode {
141 kParseModeDefault, // reading literal characters
142 kParseModeAfterPercent, // just saw '%'
143 kParseModeInKey, // inside '%{...}'
144 };
145
146 enum MatchMode {
147 kMatchModeSkipping, // skipping to next '\0' separator
148 kMatchModeMatching, // comparing chars against variable name
149 kMatchModeCollecting, // name matched, collecting value
150 };
151
152 ✗ std::string answer = "";
153 ✗ std::string key;
154 ✗ ParseMode parsemode = kParseModeDefault;
155 ✗ std::vector<char> envbuf;
156 ✗ bool env_attempted = false;
157
158 ✗ for (std::string::const_iterator i = templ.begin(); i != templ.end(); i++) {
159 ✗ const char c = *i;
160 ✗ switch (parsemode) {
161 ✗ case kParseModeDefault:
162 ✗ if (c == '%') {
163 ✗ parsemode = kParseModeAfterPercent;
164 ✗ key = "";
165 } else {
166 ✗ answer += c;
167 }
168 ✗ break;
169 ✗ case kParseModeAfterPercent:
170 ✗ if (c == '{') {
171 ✗ parsemode = kParseModeInKey;
172 } else {
173 ✗ parsemode = kParseModeDefault;
174 ✗ answer += '%';
175 ✗ answer += c;
176 }
177 ✗ break;
178 ✗ case kParseModeInKey:
179 ✗ if (c != '}') {
180 ✗ key += c;
181 } else {
182 ✗ parsemode = kParseModeDefault;
183 ✗ if (key == "path") {
184 ✗ if (path_info_ != NULL) {
185 ✗ answer += EscapeHeader(*path_info_);
186 } else {
187 ✗ answer += "-";
188 }
189 ✗ } else if (key == "pid") {
190 ✗ if (pid_ != static_cast<pid_t>(-1)) {
191 ✗ answer += StringifyInt(pid_);
192 } else {
193 ✗ answer += "-";
194 }
195 ✗ } else if (key == "uid") {
196 ✗ if (uid_ != static_cast<uid_t>(-1)) {
197 ✗ answer += StringifyInt(uid_);
198 } else {
199 ✗ answer += "-";
200 }
201 ✗ } else if (key == "gid") {
202 ✗ if (gid_ != static_cast<gid_t>(-1)) {
203 ✗ answer += StringifyInt(gid_);
204 } else {
205 ✗ answer += "-";
206 }
207 ✗ } else if (key.substr(0, 4) == "env:") {
208 ✗ if (!env_attempted) {
209 ✗ env_attempted = true;
210 #ifndef __APPLE__
211 ✗ if (pid_ != static_cast<pid_t>(-1)) {
212 ✗ ObtainDacReadSearchCapability();
213 ✗ ObtainSysPtraceCapability();
214 ✗ const std::string fname = "/proc/" +
215 ✗ 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",
243 ✗ id_, 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() + 4; // everything after "env:"
252 ✗ const char *varp = var;
253 ✗ std::string val = "";
254 ✗ MatchMode matchmode = kMatchModeMatching;
255 ✗ const char * const endp = envbuf.data() + envbuf.size();
256 ✗ for (const char *p = envbuf.data(); p < endp; p++) {
257 ✗ switch (matchmode) {
258 ✗ case kMatchModeSkipping:
259 // skipping to next null character
260 ✗ if (*p == '\0') {
261 ✗ varp = var;
262 ✗ matchmode = kMatchModeMatching;
263 }
264 ✗ break;
265 ✗ case kMatchModeMatching:
266 // matching variable name
267 ✗ if (*p == '\0') {
268 // premature end without an '='
269 ✗ varp = var;
270 ✗ } else if (*varp == *p) {
271 // so far so good
272 ✗ ++varp;
273 ✗ } else if ((*varp == '\0') && (*p == '=')) {
274 // matched
275 ✗ matchmode = kMatchModeCollecting;
276 } else {
277 // didn't match
278 ✗ matchmode = kMatchModeSkipping;
279 }
280 ✗ break;
281 ✗ case kMatchModeCollecting:
282 // matched, collecting value
283 ✗ if (*p == '\0') {
284 // all done
285 ✗ p = endp;
286 ✗ break;
287 }
288 ✗ val += *p;
289 ✗ break;
290 }
291 }
292 ✗ if (val != "") {
293 ✗ answer += ' ';
294 ✗ answer += EscapeHeader(val);
295 }
296 }
297 }
298 }
299 ✗ break;
300 }
301 }
302
303 ✗ return answer;
304 }
305
306 } // namespace download
307