GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/network/jobinfo.cc
Date: 2026-04-05 02:35:23
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 4133 JobInfo::JobInfo(const std::string *u, const bool c, const bool ph,
21
1/2
✓ Branch 2 taken 4133 times.
✗ Branch 3 not taken.
4133 const shash::Any *h, cvmfs::Sink *s) {
22
1/2
✓ Branch 1 taken 4133 times.
✗ Branch 2 not taken.
4133 Init();
23
24 4133 url_ = u;
25 4133 compressed_ = c;
26 4133 probe_hosts_ = ph;
27 4133 expected_hash_ = h;
28 4133 sink_ = s;
29 4133 }
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 4884 void JobInfo::Init() {
48 4884 id_ = atomic_xadd64(&next_uuid, 1);
49 4884 pipe_job_results = NULL;
50 4884 url_ = NULL;
51 4884 compressed_ = false;
52 4884 probe_hosts_ = false;
53 4884 head_request_ = false;
54 4884 follow_redirects_ = false;
55 4884 force_nocache_ = false;
56 4884 pid_ = -1;
57 4884 uid_ = -1;
58 4884 gid_ = -1;
59 4884 cred_data_ = NULL;
60 4884 interrupt_cue_ = NULL;
61 4884 sink_ = NULL;
62 4884 expected_hash_ = NULL;
63 4884 path_info_ = NULL;
64 //
65 4884 range_offset_ = -1;
66 4884 range_size_ = -1;
67 //
68 4884 curl_handle_ = NULL;
69 4884 headers_ = NULL;
70 4884 info_header_ = NULL;
71 4884 tracing_header_pid_ = NULL;
72 4884 tracing_header_gid_ = NULL;
73 4884 tracing_header_uid_ = NULL;
74 4884 nocache_ = false;
75 4884 error_code_ = kFailOther;
76 4884 http_code_ = -1;
77 4884 link_ = "";
78 4884 num_used_proxies_ = 0;
79 4884 num_used_metalinks_ = 0;
80 4884 num_used_hosts_ = 0;
81 4884 num_retries_ = 0;
82 4884 backoff_ms_ = 0;
83 4884 current_metalink_chain_index_ = -1;
84 4884 current_host_chain_index_ = -1;
85
86 4884 allow_failure_ = false;
87
88 4884 memset(&zstream_, 0, sizeof(zstream_));
89 4884 }
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 385939 bool JobInfo::EscapeUrlChar(unsigned char input, char output[3]) {
98
8/8
✓ Branch 0 taken 342293 times.
✓ Branch 1 taken 43646 times.
✓ Branch 2 taken 267546 times.
✓ Branch 3 taken 74747 times.
✓ Branch 4 taken 261843 times.
✓ Branch 5 taken 49349 times.
✓ Branch 6 taken 247886 times.
✓ Branch 7 taken 13957 times.
385939 if (((input >= '0') && (input <= '9')) || ((input >= 'A') && (input <= 'Z'))
99
8/8
✓ Branch 0 taken 234171 times.
✓ Branch 1 taken 63064 times.
✓ Branch 2 taken 441 times.
✓ Branch 3 taken 233730 times.
✓ Branch 4 taken 29318 times.
✓ Branch 5 taken 34187 times.
✓ Branch 6 taken 23615 times.
✓ Branch 7 taken 5703 times.
297235 || ((input >= 'a') && (input <= 'z')) || (input == '/') || (input == ':')
100
6/8
✓ Branch 0 taken 14323 times.
✓ Branch 1 taken 9292 times.
✓ Branch 2 taken 14323 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14323 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14156 times.
✓ Branch 7 taken 167 times.
23615 || (input == '.') || (input == '@') || (input == '+') || (input == '-')
101
5/8
✓ Branch 0 taken 441 times.
✓ Branch 1 taken 13715 times.
✓ Branch 2 taken 441 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 441 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 441 times.
✗ Branch 7 not taken.
14156 || (input == '_') || (input == '~') || (input == '[') || (input == ']')
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 441 times.
441 || (input == ',')) {
103 385498 output[0] = static_cast<char>(input);
104 385498 return false;
105 }
106
107 441 output[0] = '%';
108 882 output[1] = static_cast<char>((input / 16)
109
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 343 times.
441 + ((input / 16 <= 9) ? '0' : 'A' - 10));
110 882 output[2] = static_cast<char>((input % 16)
111
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 245 times.
441 + ((input % 16 <= 9) ? '0' : 'A' - 10));
112 441 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