GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/network/jobinfo.cc
Date: 2026-06-28 02:36:10
Exec Total Coverage
Lines: 62 179 34.6%
Branches: 34 210 16.2%

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 3581 JobInfo::JobInfo(const std::string *u, const bool c, const bool ph,
21
1/2
✓ Branch 2 taken 3581 times.
✗ Branch 3 not taken.
3581 const shash::Any *h, cvmfs::Sink *s) {
22
1/2
✓ Branch 1 taken 3581 times.
✗ Branch 2 not taken.
3581 Init();
23
24 3581 url_ = u;
25 3581 compressed_ = c;
26 3581 probe_hosts_ = ph;
27 3581 expected_hash_ = h;
28 3581 sink_ = s;
29 3581 }
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 3975 void JobInfo::Init() {
48 3975 id_ = atomic_xadd64(&next_uuid, 1);
49 3975 pipe_job_results = NULL;
50 3975 url_ = NULL;
51 3975 compressed_ = false;
52 3975 probe_hosts_ = false;
53 3975 head_request_ = false;
54 3975 follow_redirects_ = false;
55 3975 force_nocache_ = false;
56 3975 pid_ = -1;
57 3975 uid_ = -1;
58 3975 gid_ = -1;
59 3975 cred_data_ = NULL;
60 3975 interrupt_cue_ = NULL;
61 3975 sink_ = NULL;
62 3975 expected_hash_ = NULL;
63 3975 path_info_ = NULL;
64 //
65 3975 range_offset_ = -1;
66 3975 range_size_ = -1;
67 //
68 3975 curl_handle_ = NULL;
69 3975 headers_ = NULL;
70 3975 info_header_ = NULL;
71 3975 tracing_header_pid_ = NULL;
72 3975 tracing_header_gid_ = NULL;
73 3975 tracing_header_uid_ = NULL;
74 3975 nocache_ = false;
75 3975 error_code_ = kFailOther;
76 3975 http_code_ = -1;
77 3975 link_ = "";
78 3975 num_used_proxies_ = 0;
79 3975 num_used_metalinks_ = 0;
80 3975 num_used_hosts_ = 0;
81 3975 num_retries_ = 0;
82 3975 backoff_ms_ = 0;
83 3975 current_metalink_chain_index_ = -1;
84 3975 current_host_chain_index_ = -1;
85
86 3975 allow_failure_ = false;
87
88 3975 memset(&zstream_, 0, sizeof(zstream_));
89 3975 }
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 379167 bool JobInfo::EscapeUrlChar(unsigned char input, char output[3]) {
98
8/8
✓ Branch 0 taken 342078 times.
✓ Branch 1 taken 37089 times.
✓ Branch 2 taken 248597 times.
✓ Branch 3 taken 93481 times.
✓ Branch 4 taken 244345 times.
✓ Branch 5 taken 41341 times.
✓ Branch 6 taken 224611 times.
✓ Branch 7 taken 19734 times.
379167 if (((input >= '0') && (input <= '9')) || ((input >= 'A') && (input <= 'Z'))
99
8/8
✓ Branch 0 taken 211255 times.
✓ Branch 1 taken 54697 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 211183 times.
✓ Branch 4 taken 22956 times.
✓ Branch 5 taken 31813 times.
✓ Branch 6 taken 18704 times.
✓ Branch 7 taken 4252 times.
265952 || ((input >= 'a') && (input <= 'z')) || (input == '/') || (input == ':')
100
6/8
✓ Branch 0 taken 13573 times.
✓ Branch 1 taken 5131 times.
✓ Branch 2 taken 13573 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13573 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 13428 times.
✓ Branch 7 taken 145 times.
18704 || (input == '.') || (input == '@') || (input == '+') || (input == '-')
101
5/8
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 13356 times.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 72 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 72 times.
✗ Branch 7 not taken.
13428 || (input == '_') || (input == '~') || (input == '[') || (input == ']')
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 || (input == ',')) {
103 379095 output[0] = static_cast<char>(input);
104 379095 return false;
105 }
106
107 72 output[0] = '%';
108 144 output[1] = static_cast<char>((input / 16)
109
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 56 times.
72 + ((input / 16 <= 9) ? '0' : 'A' - 10));
110 144 output[2] = static_cast<char>((input % 16)
111
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 40 times.
72 + ((input % 16 <= 9) ? '0' : 'A' - 10));
112 72 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