GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/posix.cc
Date: 2025-11-09 02:35:23
Exec Total Coverage
Lines: 902 1154 78.2%
Branches: 680 1265 53.8%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * Some common functions.
5 */
6
7 #ifndef __STDC_FORMAT_MACROS
8 // NOLINTNEXTLINE
9 #define __STDC_FORMAT_MACROS
10 #endif
11
12 #include <cctype>
13 #include <stdlib.h>
14 #include <cstdlib>
15 #include <dirent.h>
16 #include <sys/file.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <sched.h>
20 #include <sys/select.h>
21 #include <sys/uio.h>
22
23 #include "posix.h"
24
25 #include <arpa/inet.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <grp.h>
29 #include <inttypes.h>
30 #include <netinet/in.h>
31 #include <pthread.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <stdint.h>
35 #include <sys/resource.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #ifdef __APPLE__
39 #include <sys/mount.h> // for statfs()
40 #else
41 #include <sys/statfs.h>
42 #endif
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <sys/un.h>
46 #include <sys/utsname.h>
47 #include <sys/wait.h>
48 #include <unistd.h>
49 // If valgrind headers are present on the build system, then we can detect
50 // valgrind at runtime.
51 #ifdef HAS_VALGRIND_HEADERS
52 #include <valgrind/valgrind.h>
53 #endif
54
55 #include <algorithm>
56 #include <cassert>
57 #include <cstdio>
58 #include <cstring>
59 #include <map>
60 #include <set>
61 #include <string>
62 #include <vector>
63
64 #include "util/algorithm.h"
65 #include "util/exception.h"
66 #include "util/fs_traversal.h"
67 #include "util/logging.h"
68 #include "util/pipe.h"
69 #include "util/string.h"
70 #include "util/smalloc.h"
71 #include "util/smalloc.h"
72 #include "util/smalloc.h"
73 #include "util/mutex.h"
74 #include "util/mutex.h"
75 #include "util/mutex.h"
76
77 // using namespace std; // NOLINT
78
79 #ifndef ST_RDONLY
80 // On Linux, this is in sys/statvfs.h
81 // On macOS, this flag is called MNT_RDONLY /usr/include/sys/mount.h
82 #define ST_RDONLY 1
83 #endif
84
85 // Older Linux glibc versions do not provide the f_flags member in struct statfs
86 #define CVMFS_HAS_STATFS_F_FLAGS
87 #ifndef __APPLE__
88 #ifdef __GLIBC_MINOR__
89 #if __GLIBC_MINOR__ < 12
90 #undef CVMFS_HAS_STATFS_F_FLAGS
91 #endif
92 #endif
93 #endif
94
95 // Work around missing clearenv()
96 #ifdef __APPLE__
97 extern "C" {
98 extern char **environ;
99 }
100 #endif
101
102 #ifdef CVMFS_NAMESPACE_GUARD
103 namespace CVMFS_NAMESPACE_GUARD {
104 #endif
105
106 static pthread_mutex_t getumask_mutex = PTHREAD_MUTEX_INITIALIZER;
107
108
109 /**
110 * Removes a trailing "/" from a path.
111 */
112 6255 std::string MakeCanonicalPath(const std::string &path) {
113
2/2
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 6214 times.
6255 if (path.length() == 0)
114 41 return path;
115
116
2/2
✓ Branch 2 taken 131 times.
✓ Branch 3 taken 6083 times.
6214 if (path[path.length() - 1] == '/') {
117 131 return path.substr(0, path.length() - 1);
118 } else {
119 6083 return path;
120 }
121 }
122
123 /**
124 * Return both the file and directory name for a given path.
125 *
126 * NOTE: If only a filename is given, the directory is returned as "."
127 */
128 702 void SplitPath(const std::string &path,
129 std::string *dirname,
130 std::string *filename) {
131 702 const size_t dir_sep = path.rfind('/');
132
2/2
✓ Branch 0 taken 558 times.
✓ Branch 1 taken 144 times.
702 if (dir_sep != std::string::npos) {
133 558 *dirname = path.substr(0, dir_sep);
134 558 *filename = path.substr(dir_sep + 1);
135 } else {
136 144 *dirname = ".";
137 144 *filename = path;
138 }
139 702 }
140
141
142 /**
143 * Gets the directory part of a path.
144 */
145 13711 std::string GetParentPath(const std::string &path) {
146 13711 const std::string::size_type idx = path.find_last_of('/');
147
2/2
✓ Branch 0 taken 13530 times.
✓ Branch 1 taken 181 times.
13711 if (idx != std::string::npos) {
148 13530 return path.substr(0, idx);
149 } else {
150
1/2
✓ Branch 2 taken 181 times.
✗ Branch 3 not taken.
181 return "";
151 }
152 }
153
154
155 /**
156 * Gets the file name part of a path.
157 */
158 228572 std::string GetFileName(const std::string &path) {
159 228572 const std::string::size_type idx = path.find_last_of('/');
160
2/2
✓ Branch 0 taken 227787 times.
✓ Branch 1 taken 785 times.
228572 if (idx != std::string::npos) {
161 227787 return path.substr(idx + 1);
162 } else {
163 785 return path;
164 }
165 }
166
167
168 495 bool IsAbsolutePath(const std::string &path) {
169
4/4
✓ Branch 1 taken 450 times.
✓ Branch 2 taken 45 times.
✓ Branch 4 taken 87 times.
✓ Branch 5 taken 363 times.
495 return (!path.empty() && path[0] == '/');
170 }
171
172
173 360 std::string GetAbsolutePath(const std::string &path) {
174
2/2
✓ Branch 1 taken 42 times.
✓ Branch 2 taken 318 times.
360 if (IsAbsolutePath(path))
175 42 return path;
176
177
2/4
✓ Branch 2 taken 318 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 318 times.
✗ Branch 6 not taken.
636 return GetCurrentWorkingDirectory() + "/" + path;
178 }
179
180
181 1798 bool IsHttpUrl(const std::string &path) {
182
2/2
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 1718 times.
1798 if (path.length() < 7) {
183 80 return false;
184 }
185
186
1/2
✓ Branch 1 taken 1718 times.
✗ Branch 2 not taken.
1718 std::string prefix = path.substr(0, 8);
187 1718 std::transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
188
189
6/12
✓ Branch 1 taken 1718 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1518 times.
✓ Branch 5 taken 200 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 200 times.
✓ Branch 8 taken 1318 times.
✓ Branch 9 taken 1718 times.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
1718 return prefix.substr(0, 7) == "http://" || prefix == "https://";
190 1718 }
191
192
193 2592 FileSystemInfo GetFileSystemInfo(const std::string &path) {
194 2592 FileSystemInfo result;
195
196 struct statfs info;
197 2592 const int retval = statfs(path.c_str(), &info);
198
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 2508 times.
2592 if (retval != 0)
199 84 return result;
200
201
3/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✓ Branch 5 taken 2446 times.
2508 switch (info.f_type) {
202 case kFsTypeAutofs:
203 result.type = kFsTypeAutofs;
204 break;
205 case kFsTypeNFS:
206 result.type = kFsTypeNFS;
207 break;
208 42 case kFsTypeProc:
209 42 result.type = kFsTypeProc;
210 42 break;
211 case kFsTypeBeeGFS:
212 result.type = kFsTypeBeeGFS;
213 break;
214 20 case kFsTypeTmpfs:
215 20 result.type = kFsTypeTmpfs;
216 20 break;
217 2446 default:
218 2446 result.type = kFsTypeUnknown;
219 }
220
221 #ifdef CVMFS_HAS_STATFS_F_FLAGS
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2508 times.
2508 if (info.f_flags & ST_RDONLY)
223 result.is_rdonly = true;
224 #else
225 // On old Linux systems, fall back to access()
226 retval = access(path.c_str(), W_OK);
227 result.is_rdonly = (retval != 0);
228 #endif
229
230
231 2508 return result;
232 }
233
234
235 1566 std::string ReadSymlink(const std::string &path) {
236 // TODO(jblomer): avoid PATH_MAX
237 char buf[PATH_MAX + 1];
238 1566 const ssize_t nchars = readlink(path.c_str(), buf, PATH_MAX);
239
2/2
✓ Branch 0 taken 1485 times.
✓ Branch 1 taken 81 times.
1566 if (nchars >= 0) {
240 1485 buf[nchars] = '\0';
241
1/2
✓ Branch 2 taken 1485 times.
✗ Branch 3 not taken.
1485 return std::string(buf);
242 }
243
1/2
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
81 return "";
244 }
245
246
247 /**
248 * Follow all symlinks if possible. Equivalent to
249 * `readlink --canonicalize-missing`
250 */
251 924 std::string ResolvePath(const std::string &path) {
252
6/6
✓ Branch 1 taken 686 times.
✓ Branch 2 taken 238 times.
✓ Branch 4 taken 77 times.
✓ Branch 5 taken 609 times.
✓ Branch 6 taken 315 times.
✓ Branch 7 taken 609 times.
924 if (path.empty() || (path == "/"))
253
1/2
✓ Branch 2 taken 315 times.
✗ Branch 3 not taken.
315 return "/";
254
1/2
✓ Branch 1 taken 609 times.
✗ Branch 2 not taken.
609 const std::string name = GetFileName(path);
255
1/2
✓ Branch 1 taken 609 times.
✗ Branch 2 not taken.
609 std::string result = name;
256
2/2
✓ Branch 1 taken 525 times.
✓ Branch 2 taken 84 times.
609 if (name != path) {
257 // There is a parent path of 'path'
258
2/4
✓ Branch 1 taken 525 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 525 times.
✗ Branch 5 not taken.
525 const std::string parent = ResolvePath(GetParentPath(path));
259
4/6
✓ Branch 1 taken 329 times.
✓ Branch 2 taken 196 times.
✓ Branch 4 taken 525 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 525 times.
✗ Branch 8 not taken.
525 result = parent + (parent == "/" ? "" : "/") + name;
260 525 }
261 609 char *real_result = realpath(result.c_str(), NULL);
262
2/2
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 273 times.
609 if (real_result) {
263
1/2
✓ Branch 1 taken 336 times.
✗ Branch 2 not taken.
336 result = real_result;
264 336 free(real_result);
265 }
266
3/4
✓ Branch 1 taken 609 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 567 times.
609 if (SymlinkExists(result)) {
267 char buf[PATH_MAX + 1];
268 42 const ssize_t nchars = readlink(result.c_str(), buf, PATH_MAX);
269
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 if (nchars >= 0) {
270 42 buf[nchars] = '\0';
271
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 result = buf;
272 }
273 }
274 609 return result;
275 609 }
276
277
278 105 bool IsMountPoint(const std::string &path) {
279
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 std::vector<std::string> mount_list = platform_mountlist();
280
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 const std::string resolved_path = ResolvePath(path);
281
2/2
✓ Branch 1 taken 8610 times.
✓ Branch 2 taken 35 times.
8645 for (unsigned i = 0; i < mount_list.size(); ++i) {
282
2/2
✓ Branch 2 taken 70 times.
✓ Branch 3 taken 8540 times.
8610 if (mount_list[i] == resolved_path)
283 70 return true;
284 }
285 35 return false;
286 105 }
287
288
289 /**
290 * By default PANIC(NULL) on failure
291 */
292 1383 void CreateFile(const std::string &path,
293 const int mode,
294 const bool ignore_failure) {
295 1383 const int fd = open(path.c_str(), O_CREAT, mode);
296
2/2
✓ Branch 0 taken 1338 times.
✓ Branch 1 taken 45 times.
1383 if (fd >= 0) {
297 1338 close(fd);
298 1338 return;
299 }
300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 if (ignore_failure)
301 return;
302 45 PANIC(NULL);
303 }
304
305
306 /**
307 * Symlinks /tmp/cvmfs.XYZ/l --> ParentPath(path) to make it shorter
308 */
309 201 static std::string MakeShortSocketLink(const std::string &path) {
310 struct sockaddr_un sock_addr;
311 201 const unsigned max_length = sizeof(sock_addr.sun_path);
312
313 201 std::string result;
314
2/4
✓ Branch 2 taken 201 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 201 times.
✗ Branch 6 not taken.
402 const std::string tmp_path = CreateTempDir("/tmp/cvmfs");
315
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 201 times.
201 if (tmp_path.empty())
316 return "";
317
1/2
✓ Branch 1 taken 201 times.
✗ Branch 2 not taken.
201 const std::string link = tmp_path + "/l";
318
3/6
✓ Branch 1 taken 201 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 201 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 201 times.
✗ Branch 8 not taken.
201 result = link + "/" + GetFileName(path);
319
2/2
✓ Branch 1 taken 81 times.
✓ Branch 2 taken 120 times.
201 if (result.length() >= max_length) {
320 81 rmdir(tmp_path.c_str());
321
1/2
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
81 return "";
322 }
323
1/2
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
120 const int retval = symlink(GetParentPath(path).c_str(), link.c_str());
324
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if (retval != 0) {
325 rmdir(tmp_path.c_str());
326 return "";
327 }
328 120 return result;
329 201 }
330
331 120 static void RemoveShortSocketLink(const std::string &short_path) {
332
1/2
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
120 const std::string link = GetParentPath(short_path);
333 120 unlink(link.c_str());
334
1/2
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
120 rmdir(GetParentPath(link).c_str());
335 120 }
336
337
338 /**
339 * Creates and binds to a named socket.
340 */
341 2755 int MakeSocket(const std::string &path, const int mode) {
342
1/2
✓ Branch 1 taken 2755 times.
✗ Branch 2 not taken.
2755 std::string short_path(path);
343 struct sockaddr_un sock_addr;
344
2/2
✓ Branch 1 taken 123 times.
✓ Branch 2 taken 2632 times.
2755 if (path.length() >= sizeof(sock_addr.sun_path)) {
345 // Socket paths are limited to 108 bytes (on some systems to 92 bytes),
346 // try working around
347
1/2
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
123 short_path = MakeShortSocketLink(path);
348
2/2
✓ Branch 1 taken 42 times.
✓ Branch 2 taken 81 times.
123 if (short_path.empty())
349 42 return -1;
350 }
351 2713 sock_addr.sun_family = AF_UNIX;
352 2713 strncpy(sock_addr.sun_path, short_path.c_str(), sizeof(sock_addr.sun_path));
353
354 2713 const int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2713 times.
2713 assert(socket_fd != -1);
356
357 #ifndef __APPLE__
358 // fchmod on a socket is not allowed under Mac OS X
359 // using default 0770 here
360
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2713 times.
2713 if (fchmod(socket_fd, mode) != 0)
361 goto make_socket_failure;
362 #endif
363
364 2713 if (bind(socket_fd, reinterpret_cast<struct sockaddr *>(&sock_addr),
365 sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path))
366
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 2671 times.
2713 < 0) {
367
3/6
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 42 times.
✗ Branch 7 not taken.
42 if ((errno == EADDRINUSE) && (unlink(path.c_str()) == 0)) {
368 // Second try, perhaps the file was left over
369 42 if (bind(socket_fd, reinterpret_cast<struct sockaddr *>(&sock_addr),
370 sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path))
371
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 < 0) {
372 LogCvmfs(kLogCvmfs, kLogDebug, "binding socket failed (%d)", errno);
373 goto make_socket_failure;
374 }
375 } else {
376 LogCvmfs(kLogCvmfs, kLogDebug, "binding socket failed (%d)", errno);
377 goto make_socket_failure;
378 }
379 }
380
381
2/2
✓ Branch 1 taken 81 times.
✓ Branch 2 taken 2632 times.
2713 if (short_path != path)
382
1/2
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
81 RemoveShortSocketLink(short_path);
383
384 2713 return socket_fd;
385
386 make_socket_failure:
387 close(socket_fd);
388 if (short_path != path)
389 RemoveShortSocketLink(short_path);
390 return -1;
391 2755 }
392
393
394 /**
395 * Creates and binds a TCP/IPv4 socket. An empty address binds to the "any"
396 * address.
397 */
398 1180 int MakeTcpEndpoint(const std::string &ipv4_address, int portno) {
399 1180 const int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1180 times.
1180 assert(socket_fd != -1);
401 1180 const int on = 1;
402 1180 int retval = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
403
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1180 times.
1180 assert(retval == 0);
404
405 struct sockaddr_in endpoint_addr;
406 1180 memset(&endpoint_addr, 0, sizeof(endpoint_addr));
407 1180 endpoint_addr.sin_family = AF_INET;
408
2/2
✓ Branch 1 taken 1045 times.
✓ Branch 2 taken 135 times.
1180 if (ipv4_address.empty()) {
409 1045 endpoint_addr.sin_addr.s_addr = INADDR_ANY;
410 } else {
411 135 retval = inet_aton(ipv4_address.c_str(), &(endpoint_addr.sin_addr));
412
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 90 times.
135 if (retval == 0) {
413
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 LogCvmfs(kLogCvmfs, kLogDebug, "invalid IPv4 address");
414
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 close(socket_fd);
415 45 return -1;
416 }
417 }
418 1135 endpoint_addr.sin_port = htons(portno);
419
420 1135 retval = bind(socket_fd, reinterpret_cast<struct sockaddr *>(&endpoint_addr),
421 sizeof(endpoint_addr));
422
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 1090 times.
1135 if (retval < 0) {
423
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 LogCvmfs(kLogCvmfs, kLogDebug, "binding TCP endpoint failed (%d)", errno);
424
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 close(socket_fd);
425 45 return -1;
426 }
427 1090 return socket_fd;
428 }
429
430
431 /**
432 * Connects to a named socket.
433 *
434 * \return socket file descriptor on success, -1 else
435 */
436 629 int ConnectSocket(const std::string &path) {
437
1/2
✓ Branch 1 taken 629 times.
✗ Branch 2 not taken.
629 std::string short_path(path);
438 struct sockaddr_un sock_addr;
439
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 551 times.
629 if (path.length() >= sizeof(sock_addr.sun_path)) {
440 // Socket paths are limited to 108 bytes (on some systems to 92 bytes),
441 // try working around
442
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 short_path = MakeShortSocketLink(path);
443
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 39 times.
78 if (short_path.empty())
444 39 return -1;
445 }
446 590 sock_addr.sun_family = AF_UNIX;
447 590 strncpy(sock_addr.sun_path, short_path.c_str(), sizeof(sock_addr.sun_path));
448
449 590 const int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
450
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 590 times.
590 assert(socket_fd != -1);
451
452
1/2
✓ Branch 1 taken 590 times.
✗ Branch 2 not taken.
590 const int retval = connect(
453 socket_fd, reinterpret_cast<struct sockaddr *>(&sock_addr),
454 sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path));
455
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 551 times.
590 if (short_path != path)
456
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 RemoveShortSocketLink(short_path);
457
458
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 551 times.
590 if (retval < 0) {
459
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 close(socket_fd);
460 39 return -1;
461 }
462
463 551 return socket_fd;
464 629 }
465
466
467 /**
468 * Connects to a (remote) TCP server
469 */
470 161 int ConnectTcpEndpoint(const std::string &ipv4_address, int portno) {
471 161 const int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 161 times.
161 assert(socket_fd != -1);
473
474 struct sockaddr_in endpoint_addr;
475 161 memset(&endpoint_addr, 0, sizeof(endpoint_addr));
476 161 endpoint_addr.sin_family = AF_INET;
477 161 int retval = inet_aton(ipv4_address.c_str(), &(endpoint_addr.sin_addr));
478
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 116 times.
161 if (retval == 0) {
479
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 LogCvmfs(kLogCvmfs, kLogDebug, "invalid IPv4 address");
480
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 close(socket_fd);
481 45 return -1;
482 }
483 116 endpoint_addr.sin_port = htons(portno);
484
485
1/2
✓ Branch 1 taken 116 times.
✗ Branch 2 not taken.
116 retval = connect(socket_fd,
486 reinterpret_cast<struct sockaddr *>(&endpoint_addr),
487 sizeof(endpoint_addr));
488
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 71 times.
116 if (retval != 0) {
489 45 LogCvmfs(kLogCvmfs, kLogDebug, "failed to connect to TCP endpoint (%d)",
490
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 errno);
491
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 close(socket_fd);
492 45 return -1;
493 }
494 71 return socket_fd;
495 }
496
497
498 /**
499 * Creating a pipe should always succeed.
500 */
501 19305 void MakePipe(int pipe_fd[2]) {
502 19305 const int retval = pipe(pipe_fd);
503
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19305 times.
19305 assert(retval == 0);
504 19305 }
505
506
507 /**
508 * Writes to a pipe should always succeed.
509 */
510 3580611 void WritePipe(int fd, const void *buf, size_t nbyte) {
511 ssize_t num_bytes;
512 do {
513 3580611 num_bytes = write(fd, buf, nbyte);
514
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3580611 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3580611 } while ((num_bytes < 0) && (errno == EINTR));
515
2/4
✓ Branch 0 taken 3580611 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3580611 times.
✗ Branch 3 not taken.
3580611 assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
516 3580611 }
517
518
519 /**
520 * Reads from a pipe should always succeed.
521 */
522 153057456 void ReadPipe(int fd, void *buf, size_t nbyte) {
523 ssize_t num_bytes;
524 do {
525 153057456 num_bytes = read(fd, buf, nbyte);
526
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 153057456 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
153057456 } while ((num_bytes < 0) && (errno == EINTR));
527
2/4
✓ Branch 0 taken 153057456 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 153057456 times.
✗ Branch 3 not taken.
153057456 assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
528 153057456 }
529
530
531 /**
532 * Reads from a pipe where writer's end is not yet necessarily connected
533 */
534 1001436 bool ReadHalfPipe(int fd, void *buf, size_t nbyte, unsigned timeout_ms) {
535 ssize_t num_bytes;
536 1001436 unsigned i = 0;
537 1001436 unsigned backoff_ms = 1;
538 1001436 uint64_t duration_ms = 0;
539 1001436 uint64_t timestamp = 0;
540
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 1001391 times.
1001436 if (timeout_ms != 0)
541 45 timestamp = platform_monotonic_time_ns();
542
543 1001436 const unsigned max_backoff_ms = 256;
544 do {
545 // When the writer is not connected, this takes ~200-300ns per call as per
546 // micro benchmarks
547 1001436 num_bytes = read(fd, buf, nbyte);
548
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1001436 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1001436 if ((num_bytes < 0) && (errno == EINTR))
549 continue;
550 1001436 i++;
551 // Start backing off when the busy loop reaches the ballpark of 1ms
552
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1001436 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1001436 if ((i > 3000) && (num_bytes == 0)) {
553 // The BackoffThrottle would pull in too many dependencies
554 SafeSleepMs(backoff_ms);
555 if (backoff_ms < max_backoff_ms)
556 backoff_ms *= 2;
557 }
558
3/4
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 1001391 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 45 times.
1001436 if ((timeout_ms != 0) && (num_bytes == 0)) {
559 duration_ms = (platform_monotonic_time_ns() - timestamp)
560 / (1000UL * 1000UL);
561 if (duration_ms > timeout_ms)
562 return false;
563 }
564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1001436 times.
1001436 } while (num_bytes == 0);
565
2/4
✓ Branch 0 taken 1001436 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1001436 times.
✗ Branch 3 not taken.
1001436 assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
566 1001436 return true;
567 }
568
569
570 /**
571 * Closes both ends of a pipe
572 */
573 15775 void ClosePipe(int pipe_fd[2]) {
574 15775 close(pipe_fd[0]);
575 15775 close(pipe_fd[1]);
576 15775 }
577
578
579 /**
580 * Compares two directory trees on the meta-data level. Returns true iff the
581 * trees have identical content.
582 */
583 105171 bool DiffTree(const std::string &path_a, const std::string &path_b) {
584 int retval;
585 105171 std::vector<std::string> ls_a;
586 105171 std::vector<std::string> ls_b;
587 105171 std::vector<std::string> subdirs;
588
589
1/2
✓ Branch 2 taken 105171 times.
✗ Branch 3 not taken.
105171 DIR *dirp_a = opendir(path_a.c_str());
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105171 times.
105171 if (dirp_a == NULL)
591 return false;
592
1/2
✓ Branch 2 taken 105171 times.
✗ Branch 3 not taken.
105171 DIR *dirp_b = opendir(path_b.c_str());
593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105171 times.
105171 if (dirp_b == NULL) {
594 closedir(dirp_a);
595 return false;
596 }
597
598 platform_dirent64 *dirent;
599
3/4
✓ Branch 1 taken 429396 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 324225 times.
✓ Branch 4 taken 105171 times.
429396 while ((dirent = platform_readdir(dirp_a))) {
600
1/2
✓ Branch 2 taken 324225 times.
✗ Branch 3 not taken.
324225 const std::string name(dirent->d_name);
601
6/6
✓ Branch 1 taken 219054 times.
✓ Branch 2 taken 105171 times.
✓ Branch 4 taken 105171 times.
✓ Branch 5 taken 113883 times.
✓ Branch 6 taken 210342 times.
✓ Branch 7 taken 113883 times.
324225 if ((name == ".") || (name == ".."))
602 210342 continue;
603
2/4
✓ Branch 1 taken 113883 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 113883 times.
✗ Branch 5 not taken.
113883 const std::string path = path_a + "/" + name;
604
1/2
✓ Branch 1 taken 113883 times.
✗ Branch 2 not taken.
113883 ls_a.push_back(path);
605
606 platform_stat64 info;
607 113883 retval = platform_lstat(path.c_str(), &info);
608
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 113883 times.
113883 if (retval != 0) {
609 closedir(dirp_a);
610 closedir(dirp_b);
611 return false;
612 }
613
2/2
✓ Branch 0 taken 105864 times.
✓ Branch 1 taken 8019 times.
113883 if (S_ISDIR(info.st_mode))
614
1/2
✓ Branch 1 taken 105864 times.
✗ Branch 2 not taken.
105864 subdirs.push_back(name);
615
3/5
✓ Branch 1 taken 113883 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 113883 times.
✓ Branch 5 taken 210342 times.
✗ Branch 6 not taken.
324225 }
616
3/4
✓ Branch 1 taken 428736 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 323565 times.
✓ Branch 4 taken 105171 times.
428736 while ((dirent = platform_readdir(dirp_b))) {
617
1/2
✓ Branch 2 taken 323565 times.
✗ Branch 3 not taken.
323565 const std::string name(dirent->d_name);
618
6/6
✓ Branch 1 taken 218394 times.
✓ Branch 2 taken 105171 times.
✓ Branch 4 taken 105171 times.
✓ Branch 5 taken 113223 times.
✓ Branch 6 taken 210342 times.
✓ Branch 7 taken 113223 times.
323565 if ((name == ".") || (name == ".."))
619 210342 continue;
620
2/4
✓ Branch 1 taken 113223 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 113223 times.
✗ Branch 5 not taken.
113223 const std::string path = path_b + "/" + name;
621
1/2
✓ Branch 1 taken 113223 times.
✗ Branch 2 not taken.
113223 ls_b.push_back(path);
622
2/2
✓ Branch 2 taken 113223 times.
✓ Branch 3 taken 210342 times.
323565 }
623
1/2
✓ Branch 1 taken 105171 times.
✗ Branch 2 not taken.
105171 closedir(dirp_a);
624
1/2
✓ Branch 1 taken 105171 times.
✗ Branch 2 not taken.
105171 closedir(dirp_b);
625
626
1/2
✓ Branch 3 taken 105171 times.
✗ Branch 4 not taken.
105171 sort(ls_a.begin(), ls_a.end());
627
1/2
✓ Branch 3 taken 105171 times.
✗ Branch 4 not taken.
105171 sort(ls_b.begin(), ls_b.end());
628
2/2
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 105138 times.
105171 if (ls_a.size() != ls_b.size())
629 33 return false;
630
2/2
✓ Branch 1 taken 112530 times.
✓ Branch 2 taken 105138 times.
217668 for (unsigned i = 0; i < ls_a.size(); ++i) {
631
3/7
✓ Branch 2 taken 112530 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112530 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 112530 times.
112530 if (GetFileName(ls_a[i]) != GetFileName(ls_b[i]))
632 return false;
633 platform_stat64 info_a;
634 platform_stat64 info_b;
635 112530 retval = platform_lstat(ls_a[i].c_str(), &info_a);
636
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112530 times.
112530 if (retval != 0)
637 return false;
638 112530 retval = platform_lstat(ls_b[i].c_str(), &info_b);
639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112530 times.
112530 if (retval != 0)
640 return false;
641
2/4
✓ Branch 0 taken 112530 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 112530 times.
✗ Branch 3 not taken.
112530 if ((info_a.st_mode != info_b.st_mode) || (info_a.st_uid != info_b.st_uid)
642
1/2
✓ Branch 0 taken 112530 times.
✗ Branch 1 not taken.
112530 || (info_a.st_gid != info_b.st_gid)
643
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 112530 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
112530 || ((info_a.st_size != info_b.st_size) && !S_ISDIR(info_a.st_mode))) {
644 return false;
645 }
646 }
647
648
2/2
✓ Branch 1 taken 105072 times.
✓ Branch 2 taken 105138 times.
210210 for (unsigned i = 0; i < subdirs.size(); ++i) {
649
3/6
✓ Branch 2 taken 105072 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 105072 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 105072 times.
✗ Branch 9 not taken.
105072 const bool retval_subtree = DiffTree(path_a + "/" + subdirs[i],
650
2/4
✓ Branch 2 taken 105072 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 105072 times.
✗ Branch 6 not taken.
210144 path_b + "/" + subdirs[i]);
651
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105072 times.
105072 if (!retval_subtree)
652 return false;
653 }
654
655 105138 return true;
656 105171 }
657
658
659 /**
660 * Changes a non-blocking file descriptor to a blocking one.
661 */
662 81 void Nonblock2Block(int filedes) {
663 81 const int flags = fcntl(filedes, F_GETFL);
664
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
81 assert(flags != -1);
665 81 const int retval = fcntl(filedes, F_SETFL, flags & ~O_NONBLOCK);
666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
81 assert(retval != -1);
667 81 }
668
669
670 /**
671 * Changes a blocking file descriptor to a non-blocking one.
672 */
673 80 void Block2Nonblock(int filedes) {
674 80 const int flags = fcntl(filedes, F_GETFL);
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 assert(flags != -1);
676 80 const int retval = fcntl(filedes, F_SETFL, flags | O_NONBLOCK);
677
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 assert(retval != -1);
678 80 }
679
680
681 /**
682 * Drops the characters of string to a socket. It doesn't matter
683 * if the other side has hung up.
684 */
685 166 void SendMsg2Socket(const int fd, const std::string &msg) {
686 166 (void)send(fd, &msg[0], msg.length(), MSG_NOSIGNAL);
687 166 }
688
689 /**
690 * Sends the file descriptor passing_fd to the socket socket_fd. Can be used
691 * to transfer an open file descriptor from one process to another. Use
692 * ConnectSocket() to get the socket_fd.
693 */
694 42 bool SendFd2Socket(int socket_fd, int passing_fd) {
695 union {
696 // Make sure that ctrl_msg is properly aligned.
697 struct cmsghdr align;
698 // Buffer large enough to store the file descriptor (ancillary data)
699 unsigned char buf[CMSG_SPACE(sizeof(int))];
700 } ctrl_msg;
701
702 42 memset(ctrl_msg.buf, 0, sizeof(ctrl_msg.buf));
703
704 struct msghdr msgh;
705 42 msgh.msg_name = NULL;
706 42 msgh.msg_namelen = 0;
707
708 42 unsigned char dummy = 0;
709 struct iovec iov;
710 42 iov.iov_base = &dummy;
711 42 iov.iov_len = 1;
712 42 msgh.msg_iov = &iov;
713 42 msgh.msg_iovlen = 1;
714
715 42 msgh.msg_control = ctrl_msg.buf;
716 42 msgh.msg_controllen = sizeof(ctrl_msg.buf);
717
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh);
718 42 cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
719 42 cmsgp->cmsg_level = SOL_SOCKET;
720 42 cmsgp->cmsg_type = SCM_RIGHTS;
721 42 memcpy(CMSG_DATA(cmsgp), &passing_fd, sizeof(int));
722
723
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 const ssize_t retval = sendmsg(socket_fd, &msgh, 0);
724 42 return (retval != -1);
725 }
726
727
728 /**
729 * Returns the file descriptor that has been sent with SendFd2Socket. The
730 * msg_fd file descriptor needs to come from a call to accept() on the socket
731 * where the passing file descriptor has been sent to.
732 * Returns -errno on error.
733 */
734 int RecvFdFromSocket(int msg_fd) {
735 union {
736 // Make sure that ctrl_msg is properly aligned.
737 struct cmsghdr align;
738 // Buffer large enough to store the file descriptor (ancillary data)
739 unsigned char buf[CMSG_SPACE(sizeof(int))];
740 } ctrl_msg;
741
742 memset(ctrl_msg.buf, 0, sizeof(ctrl_msg.buf));
743
744 struct msghdr msgh;
745 msgh.msg_name = NULL;
746 msgh.msg_namelen = 0;
747
748 unsigned char dummy;
749 struct iovec iov;
750 iov.iov_base = &dummy;
751 iov.iov_len = 1;
752 msgh.msg_iov = &iov;
753 msgh.msg_iovlen = 1;
754
755 msgh.msg_control = ctrl_msg.buf;
756 msgh.msg_controllen = sizeof(ctrl_msg.buf);
757
758 const ssize_t retval = recvmsg(msg_fd, &msgh, 0);
759 if (retval == -1)
760 return -errno;
761
762 struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh);
763 assert(cmsgp != NULL);
764 if (cmsgp->cmsg_len != CMSG_LEN(sizeof(int)))
765 return -ERANGE;
766 assert(cmsgp->cmsg_level == SOL_SOCKET);
767 assert(cmsgp->cmsg_type == SCM_RIGHTS);
768
769 int passing_fd;
770 memcpy(&passing_fd, CMSG_DATA(cmsgp), sizeof(int));
771 assert(passing_fd >= 0);
772 return passing_fd;
773 }
774
775
776 33 std::string GetHostname() {
777 char name[HOST_NAME_MAX + 1];
778 33 const int retval = gethostname(name, HOST_NAME_MAX);
779
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 assert(retval == 0);
780
1/2
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 return name;
781 }
782
783
784 /**
785 * set(e){g/u}id wrapper.
786 *
787 * avoid_mutexes: if true, means that we are a forked child and must not use
788 * mutexes.
789 */
790 61 bool SwitchCredentials(const uid_t uid, const gid_t gid, const bool temporarily,
791 const bool avoid_mutexes) {
792
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 28 times.
61 if (!avoid_mutexes) {
793 33 LogCvmfs(kLogCvmfs, kLogDebug,
794 "current credentials uid %d gid %d "
795 "euid %d egid %d, switching to %d %d (temp: %d)",
796 getuid(), getgid(), geteuid(), getegid(), uid, gid, temporarily);
797 }
798 61 int retval = 0;
799
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (temporarily) {
800 if (gid != getegid())
801 retval = setegid(gid);
802 if ((retval == 0) && (uid != geteuid()))
803 retval = seteuid(uid);
804 } else {
805 // If effective uid is not root, we must first gain root access back
806
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 61 times.
61 if ((getuid() == 0) && (getuid() != geteuid())) {
807 retval = SwitchCredentials(0, getgid(), true);
808 if (!retval)
809 return false;
810 }
811
3/4
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 33 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 28 times.
61 retval = setgid(gid) || setuid(uid);
812 }
813
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 28 times.
61 if (!avoid_mutexes) {
814 33 LogCvmfs(kLogCvmfs, kLogDebug, "switch credentials result %d (%d)", retval,
815 33 errno);
816 }
817 61 return retval == 0;
818 }
819
820
821 /**
822 * Checks if the regular file path exists.
823 */
824 74207 bool FileExists(const std::string &path) {
825 platform_stat64 info;
826
4/4
✓ Branch 2 taken 49624 times.
✓ Branch 3 taken 24583 times.
✓ Branch 4 taken 48678 times.
✓ Branch 5 taken 946 times.
74207 return ((platform_lstat(path.c_str(), &info) == 0) && S_ISREG(info.st_mode));
827 }
828
829
830 /**
831 * Returns -1 on failure.
832 */
833 5639121 int64_t GetFileSize(const std::string &path) {
834 platform_stat64 info;
835 5639121 const int retval = platform_stat(path.c_str(), &info);
836
2/2
✓ Branch 0 taken 5565278 times.
✓ Branch 1 taken 89173 times.
5654451 if (retval != 0)
837 5565278 return -1;
838 89173 return info.st_size;
839 }
840
841
842 /**
843 * Checks if the directory (not symlink) path exists.
844 */
845 11487 bool DirectoryExists(const std::string &path) {
846 platform_stat64 info;
847
3/4
✓ Branch 2 taken 11330 times.
✓ Branch 3 taken 157 times.
✓ Branch 4 taken 11330 times.
✗ Branch 5 not taken.
11487 return ((platform_lstat(path.c_str(), &info) == 0) && S_ISDIR(info.st_mode));
848 }
849
850
851 /**
852 * Checks if the symlink file path exists.
853 */
854 1779 bool SymlinkExists(const std::string &path) {
855 platform_stat64 info;
856
4/4
✓ Branch 2 taken 1515 times.
✓ Branch 3 taken 264 times.
✓ Branch 4 taken 1179 times.
✓ Branch 5 taken 336 times.
1779 return ((platform_lstat(path.c_str(), &info) == 0) && S_ISLNK(info.st_mode));
857 }
858
859
860 /**
861 * Equivalent of `ln -sf $src $dest`
862 */
863 262 bool SymlinkForced(const std::string &src, const std::string &dest) {
864 262 int retval = unlink(dest.c_str());
865
3/4
✓ Branch 0 taken 217 times.
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 217 times.
262 if ((retval != 0) && (errno != ENOENT))
866 return false;
867 262 retval = symlink(src.c_str(), dest.c_str());
868 262 return retval == 0;
869 }
870
871
872 /**
873 * The mkdir -p command. Additionally checks if the directory is writable
874 * if it exists.
875 */
876 1130811 bool MkdirDeep(const std::string &path,
877 const mode_t mode,
878 bool verify_writable) {
879
2/2
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 1130753 times.
1130811 if (path == "")
880 58 return false;
881
882 1130753 int retval = mkdir(path.c_str(), mode);
883
2/2
✓ Branch 0 taken 1126779 times.
✓ Branch 1 taken 3974 times.
1130753 if (retval == 0)
884 1126779 return true;
885
886 7948 if ((errno == ENOENT)
887
9/15
✓ Branch 0 taken 2913 times.
✓ Branch 1 taken 1061 times.
✓ Branch 3 taken 2913 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2913 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2913 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2913 times.
✓ Branch 11 taken 1061 times.
✓ Branch 13 taken 2913 times.
✓ Branch 14 taken 1061 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
3974 && (MkdirDeep(GetParentPath(path), mode, verify_writable))) {
888 2913 return MkdirDeep(path, mode, verify_writable);
889 }
890
891
2/2
✓ Branch 0 taken 1041 times.
✓ Branch 1 taken 20 times.
1061 if (errno == EEXIST) {
892 platform_stat64 info;
893
5/6
✓ Branch 2 taken 1041 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1001 times.
✓ Branch 5 taken 40 times.
✓ Branch 6 taken 1001 times.
✓ Branch 7 taken 40 times.
1041 if ((platform_stat(path.c_str(), &info) == 0) && S_ISDIR(info.st_mode)) {
894
2/2
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 793 times.
1001 if (verify_writable) {
895 208 retval = utimes(path.c_str(), NULL);
896
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 if (retval == 0)
897 1001 return true;
898 } else {
899 793 return true;
900 }
901 }
902 }
903
904 60 return false;
905 }
906
907
908 /**
909 * Creates the "hash cache" directory structure in path.
910 */
911 3905 bool MakeCacheDirectories(const std::string &path, const mode_t mode) {
912
1/2
✓ Branch 1 taken 3905 times.
✗ Branch 2 not taken.
3905 const std::string canonical_path = MakeCanonicalPath(path);
913
914
1/2
✓ Branch 1 taken 3905 times.
✗ Branch 2 not taken.
3905 std::string this_path = canonical_path + "/quarantaine";
915
3/4
✓ Branch 1 taken 3905 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 3885 times.
3905 if (!MkdirDeep(this_path, mode, false))
916 20 return false;
917
918
1/2
✓ Branch 1 taken 3885 times.
✗ Branch 2 not taken.
3885 this_path = canonical_path + "/ff";
919
920 platform_stat64 stat_info;
921
2/2
✓ Branch 2 taken 3449 times.
✓ Branch 3 taken 436 times.
3885 if (platform_stat(this_path.c_str(), &stat_info) != 0) {
922
1/2
✓ Branch 1 taken 3449 times.
✗ Branch 2 not taken.
3449 this_path = canonical_path + "/txn";
923
2/4
✓ Branch 1 taken 3449 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3449 times.
3449 if (!MkdirDeep(this_path, mode, false))
924 return false;
925
2/2
✓ Branch 0 taken 882944 times.
✓ Branch 1 taken 3449 times.
886393 for (int i = 0; i <= 0xff; i++) {
926 char hex[4];
927 882944 snprintf(hex, sizeof(hex), "%02x", i);
928
3/6
✓ Branch 2 taken 882944 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 882944 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 882944 times.
✗ Branch 9 not taken.
882944 this_path = canonical_path + "/" + std::string(hex);
929
2/4
✓ Branch 1 taken 882944 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 882944 times.
882944 if (!MkdirDeep(this_path, mode, false))
930 return false;
931 }
932 }
933 3885 return true;
934 3905 }
935
936
937 /**
938 * Tries to locks file path, return an error if file is already locked.
939 * Creates path if required.
940 *
941 * \return file descriptor, -1 on error, -2 if it would block
942 */
943 1946 int TryLockFile(const std::string &path) {
944 1946 const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600);
945
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 1901 times.
1946 if (fd_lockfile < 0)
946 45 return -1;
947
948
2/2
✓ Branch 1 taken 91 times.
✓ Branch 2 taken 1810 times.
1901 if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) {
949 91 close(fd_lockfile);
950
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 91 times.
91 if (errno != EWOULDBLOCK)
951 return -1;
952 91 return -2;
953 }
954
955 1810 return fd_lockfile;
956 }
957
958
959 /**
960 * Tries to write the process id in a /var/run/progname.pid like file. Returns
961 * the same as TryLockFile.
962 *
963 * \return file descriptor, -1 on error, -2 if it would block
964 */
965 116 int WritePidFile(const std::string &path) {
966
1/2
✓ Branch 2 taken 116 times.
✗ Branch 3 not taken.
116 const int fd = open(path.c_str(), O_CREAT | O_RDWR, 0600);
967
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 87 times.
116 if (fd < 0)
968 29 return -1;
969
2/2
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 58 times.
87 if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
970
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 close(fd);
971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (errno != EWOULDBLOCK)
972 return -1;
973 29 return -2;
974 }
975
976 // Don't leak the file descriptor to exec'd children
977
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
58 int flags = fcntl(fd, F_GETFD);
978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 assert(flags != -1);
979 58 flags |= FD_CLOEXEC;
980
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
58 flags = fcntl(fd, F_SETFD, flags);
981
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 assert(flags != -1);
982
983 char buf[64];
984
985 58 snprintf(buf, sizeof(buf), "%" PRId64 "\n", static_cast<uint64_t>(getpid()));
986 58 const bool retval = (ftruncate(fd, 0) == 0)
987
3/6
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 58 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 58 times.
✗ Branch 6 not taken.
58 && SafeWrite(fd, buf, strlen(buf));
988
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if (!retval) {
989 UnlockFile(fd);
990 return -1;
991 }
992 58 return fd;
993 }
994
995
996 /**
997 * Locks file path, blocks if file is already locked. Creates path if required.
998 *
999 * \return file descriptor, -1 on error
1000 */
1001 1432 int LockFile(const std::string &path) {
1002 1432 const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600);
1003
2/2
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 1329 times.
1432 if (fd_lockfile < 0)
1004 103 return -1;
1005
1006
1007
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 1291 times.
1329 if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) {
1008
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (errno != EWOULDBLOCK) {
1009 close(fd_lockfile);
1010 return -1;
1011 }
1012 38 LogCvmfs(kLogCvmfs, kLogSyslog, "another process holds %s, waiting.",
1013 path.c_str());
1014
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 if (flock(fd_lockfile, LOCK_EX) != 0) {
1015 close(fd_lockfile);
1016 return -1;
1017 }
1018 38 LogCvmfs(kLogCvmfs, kLogSyslog, "lock %s acquired", path.c_str());
1019 }
1020
1021 1329 return fd_lockfile;
1022 }
1023
1024
1025 3027 void UnlockFile(const int filedes) {
1026 3027 const int retval = flock(filedes, LOCK_UN);
1027
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3027 times.
3027 assert(retval == 0);
1028 3027 close(filedes);
1029 3027 }
1030
1031
1032 /**
1033 * Wrapper around mkstemp.
1034 */
1035 60549 FILE *CreateTempFile(const std::string &path_prefix, const int mode,
1036 const char *open_flags, std::string *final_path) {
1037 60549 *final_path = path_prefix + ".XXXXXX";
1038 60549 char *tmp_file = strdupa(final_path->c_str());
1039 60549 const int tmp_fd = mkstemp(tmp_file);
1040
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 60457 times.
60549 if (tmp_fd < 0) {
1041 92 return NULL;
1042 }
1043
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 60457 times.
60457 if (fchmod(tmp_fd, mode) != 0) {
1044 close(tmp_fd);
1045 return NULL;
1046 }
1047
1048 60457 *final_path = tmp_file;
1049 60457 FILE *tmp_fp = fdopen(tmp_fd, open_flags);
1050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60457 times.
60457 if (!tmp_fp) {
1051 close(tmp_fd);
1052 unlink(tmp_file);
1053 return NULL;
1054 }
1055
1056 60457 return tmp_fp;
1057 }
1058
1059
1060 /**
1061 * Create the file but don't open. Use only in non-public tmp directories.
1062 */
1063 18673 std::string CreateTempPath(const std::string &path_prefix, const int mode) {
1064 18673 std::string result;
1065
1/2
✓ Branch 1 taken 18673 times.
✗ Branch 2 not taken.
18673 FILE *f = CreateTempFile(path_prefix, mode, "w", &result);
1066
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 18628 times.
18673 if (!f)
1067
1/2
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
45 return "";
1068
1/2
✓ Branch 1 taken 18628 times.
✗ Branch 2 not taken.
18628 fclose(f);
1069 18628 return result;
1070 18673 }
1071
1072
1073 /**
1074 * Create a directory with a unique name.
1075 */
1076 6887 std::string CreateTempDir(const std::string &path_prefix) {
1077
1/2
✓ Branch 1 taken 6887 times.
✗ Branch 2 not taken.
6887 const std::string dir = path_prefix + ".XXXXXX";
1078 6887 char *tmp_dir = strdupa(dir.c_str());
1079 6887 tmp_dir = mkdtemp(tmp_dir);
1080
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 6842 times.
6887 if (tmp_dir == NULL)
1081
1/2
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
45 return "";
1082
1/2
✓ Branch 2 taken 6842 times.
✗ Branch 3 not taken.
6842 return std::string(tmp_dir);
1083 6887 }
1084
1085
1086 /**
1087 * Get the current working directory of the running process
1088 */
1089 7812 std::string GetCurrentWorkingDirectory() {
1090 char cwd[PATH_MAX];
1091
3/9
✓ Branch 1 taken 7812 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 7812 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 7812 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
7812 return (getcwd(cwd, sizeof(cwd)) != NULL) ? std::string(cwd) : std::string();
1092 }
1093
1094
1095 /**
1096 * Helper class that provides callback functions for the file system traversal.
1097 */
1098 class RemoveTreeHelper {
1099 public:
1100 bool success;
1101 15068 RemoveTreeHelper() { success = true; }
1102 76741 void RemoveFile(const std::string &parent_path, const std::string &name) {
1103
1/2
✓ Branch 2 taken 76741 times.
✗ Branch 3 not taken.
76741 const int retval = unlink((parent_path + "/" + name).c_str());
1104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76741 times.
76741 if (retval != 0)
1105 success = false;
1106 76741 }
1107 36443 void RemoveDir(const std::string &parent_path, const std::string &name) {
1108
1/2
✓ Branch 2 taken 36443 times.
✗ Branch 3 not taken.
36443 const int retval = rmdir((parent_path + "/" + name).c_str());
1109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36443 times.
36443 if (retval != 0)
1110 success = false;
1111 36443 }
1112 978956 bool TryRemoveDir(const std::string &parent_path, const std::string &name) {
1113
1/2
✓ Branch 2 taken 978956 times.
✗ Branch 3 not taken.
978956 const int retval = rmdir((parent_path + "/" + name).c_str());
1114 978956 return (retval != 0);
1115 }
1116 };
1117
1118
1119 /**
1120 * Does rm -rf on path.
1121 */
1122 15713 bool RemoveTree(const std::string &path) {
1123 platform_stat64 info;
1124 15713 const int retval = platform_lstat(path.c_str(), &info);
1125
2/2
✓ Branch 0 taken 645 times.
✓ Branch 1 taken 15068 times.
15713 if (retval != 0)
1126 645 return errno == ENOENT;
1127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15068 times.
15068 if (!S_ISDIR(info.st_mode))
1128 return false;
1129
1130
1/2
✓ Branch 1 taken 15068 times.
✗ Branch 2 not taken.
15068 RemoveTreeHelper *remove_tree_helper = new RemoveTreeHelper();
1131
2/4
✓ Branch 2 taken 15068 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 15068 times.
✗ Branch 6 not taken.
30136 FileSystemTraversal<RemoveTreeHelper> traversal(remove_tree_helper, "", true);
1132 15068 traversal.fn_new_file = &RemoveTreeHelper::RemoveFile;
1133 15068 traversal.fn_new_character_dev = &RemoveTreeHelper::RemoveFile;
1134 15068 traversal.fn_new_symlink = &RemoveTreeHelper::RemoveFile;
1135 15068 traversal.fn_new_socket = &RemoveTreeHelper::RemoveFile;
1136 15068 traversal.fn_new_fifo = &RemoveTreeHelper::RemoveFile;
1137 15068 traversal.fn_leave_dir = &RemoveTreeHelper::RemoveDir;
1138 15068 traversal.fn_new_dir_prefix = &RemoveTreeHelper::TryRemoveDir;
1139
1/2
✓ Branch 1 taken 15068 times.
✗ Branch 2 not taken.
15068 traversal.Recurse(path);
1140 15068 const bool result = remove_tree_helper->success;
1141
1/2
✓ Branch 0 taken 15068 times.
✗ Branch 1 not taken.
15068 delete remove_tree_helper;
1142
1143 15068 return result;
1144 15068 }
1145
1146
1147 /**
1148 * Returns ls $dir/GLOB$suffix
1149 */
1150 430 std::vector<std::string> FindFilesBySuffix(const std::string &dir,
1151 const std::string &suffix) {
1152 430 std::vector<std::string> result;
1153
1/2
✓ Branch 2 taken 430 times.
✗ Branch 3 not taken.
430 DIR *dirp = opendir(dir.c_str());
1154
2/2
✓ Branch 0 taken 252 times.
✓ Branch 1 taken 178 times.
430 if (!dirp)
1155 252 return result;
1156
1157 platform_dirent64 *dirent;
1158
3/4
✓ Branch 1 taken 1206 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1028 times.
✓ Branch 4 taken 178 times.
1206 while ((dirent = platform_readdir(dirp))) {
1159
1/2
✓ Branch 2 taken 1028 times.
✗ Branch 3 not taken.
1028 const std::string name(dirent->d_name);
1160 1028 if ((name.length() >= suffix.length())
1161
9/14
✓ Branch 1 taken 776 times.
✓ Branch 2 taken 252 times.
✓ Branch 6 taken 776 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 398 times.
✓ Branch 10 taken 378 times.
✓ Branch 11 taken 776 times.
✓ Branch 12 taken 252 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 398 times.
✓ Branch 15 taken 630 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2056 && (name.substr(name.length() - suffix.length()) == suffix)) {
1162
3/6
✓ Branch 1 taken 398 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 398 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 398 times.
✗ Branch 8 not taken.
398 result.push_back(dir + "/" + name);
1163 }
1164 1028 }
1165
1/2
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
178 closedir(dirp);
1166
1/2
✓ Branch 3 taken 178 times.
✗ Branch 4 not taken.
178 std::sort(result.begin(), result.end());
1167 178 return result;
1168 }
1169
1170
1171 /**
1172 * Returns ls $dir/$prefixGLOB
1173 */
1174 200 std::vector<std::string> FindFilesByPrefix(const std::string &dir,
1175 const std::string &prefix) {
1176 200 std::vector<std::string> result;
1177
1/2
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
200 DIR *dirp = opendir(dir.c_str());
1178
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 160 times.
200 if (!dirp)
1179 40 return result;
1180
1181 platform_dirent64 *dirent;
1182
3/4
✓ Branch 1 taken 1120 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 960 times.
✓ Branch 4 taken 160 times.
1120 while ((dirent = platform_readdir(dirp))) {
1183
1/2
✓ Branch 2 taken 960 times.
✗ Branch 3 not taken.
960 const std::string name(dirent->d_name);
1184 960 if ((name.length() >= prefix.length())
1185
9/14
✓ Branch 1 taken 800 times.
✓ Branch 2 taken 160 times.
✓ Branch 5 taken 800 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 440 times.
✓ Branch 9 taken 360 times.
✓ Branch 10 taken 800 times.
✓ Branch 11 taken 160 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 440 times.
✓ Branch 14 taken 520 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
1920 && (name.substr(0, prefix.length()) == prefix)) {
1186
3/6
✓ Branch 1 taken 440 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 440 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 440 times.
✗ Branch 8 not taken.
440 result.push_back(dir + "/" + name);
1187 }
1188 960 }
1189
1/2
✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
160 closedir(dirp);
1190
1/2
✓ Branch 3 taken 160 times.
✗ Branch 4 not taken.
160 std::sort(result.begin(), result.end());
1191 160 return result;
1192 }
1193
1194
1195 /**
1196 * Finds all direct subdirectories under parent_dir (except ., ..). Used,
1197 * for instance, to parse /etc/cvmfs/repositories.d/<reponoame>
1198 */
1199 180 std::vector<std::string> FindDirectories(const std::string &parent_dir) {
1200 180 std::vector<std::string> result;
1201
1/2
✓ Branch 2 taken 180 times.
✗ Branch 3 not taken.
180 DIR *dirp = opendir(parent_dir.c_str());
1202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 180 times.
180 if (!dirp)
1203 return result;
1204
1205 platform_dirent64 *dirent;
1206
3/4
✓ Branch 1 taken 945 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 765 times.
✓ Branch 4 taken 180 times.
945 while ((dirent = platform_readdir(dirp))) {
1207
1/2
✓ Branch 2 taken 765 times.
✗ Branch 3 not taken.
765 const std::string name(dirent->d_name);
1208
6/6
✓ Branch 1 taken 585 times.
✓ Branch 2 taken 180 times.
✓ Branch 4 taken 180 times.
✓ Branch 5 taken 405 times.
✓ Branch 6 taken 360 times.
✓ Branch 7 taken 405 times.
765 if ((name == ".") || (name == ".."))
1209 360 continue;
1210
2/4
✓ Branch 1 taken 405 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 405 times.
✗ Branch 5 not taken.
405 const std::string path = parent_dir + "/" + name;
1211
1212 platform_stat64 info;
1213 405 const int retval = platform_stat(path.c_str(), &info);
1214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 405 times.
405 if (retval != 0)
1215 continue;
1216
2/2
✓ Branch 0 taken 315 times.
✓ Branch 1 taken 90 times.
405 if (S_ISDIR(info.st_mode))
1217
1/2
✓ Branch 1 taken 315 times.
✗ Branch 2 not taken.
315 result.push_back(path);
1218
3/4
✓ Branch 1 taken 405 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 405 times.
✓ Branch 5 taken 360 times.
765 }
1219
1/2
✓ Branch 1 taken 180 times.
✗ Branch 2 not taken.
180 closedir(dirp);
1220
1/2
✓ Branch 3 taken 180 times.
✗ Branch 4 not taken.
180 sort(result.begin(), result.end());
1221 180 return result;
1222 }
1223
1224
1225 /**
1226 * Finds all files and direct subdirectories under directory (except ., ..).
1227 */
1228 333 bool ListDirectory(const std::string &directory,
1229 std::vector<std::string> *names,
1230 std::vector<mode_t> *modes) {
1231 333 DIR *dirp = opendir(directory.c_str());
1232
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 300 times.
333 if (!dirp)
1233 33 return false;
1234
1235 platform_dirent64 *dirent;
1236
2/2
✓ Branch 1 taken 4320 times.
✓ Branch 2 taken 261 times.
4581 while ((dirent = platform_readdir(dirp))) {
1237
1/2
✓ Branch 2 taken 4320 times.
✗ Branch 3 not taken.
4320 const std::string name(dirent->d_name);
1238
6/6
✓ Branch 1 taken 4020 times.
✓ Branch 2 taken 300 times.
✓ Branch 4 taken 300 times.
✓ Branch 5 taken 3720 times.
✓ Branch 6 taken 600 times.
✓ Branch 7 taken 3720 times.
4320 if ((name == ".") || (name == ".."))
1239 600 continue;
1240
2/4
✓ Branch 1 taken 3720 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3720 times.
✗ Branch 5 not taken.
3720 const std::string path = directory + "/" + name;
1241
1242 platform_stat64 info;
1243 3720 const int retval = platform_lstat(path.c_str(), &info);
1244
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 3681 times.
3720 if (retval != 0) {
1245
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 closedir(dirp);
1246 39 return false;
1247 }
1248
1249
1/2
✓ Branch 1 taken 3681 times.
✗ Branch 2 not taken.
3681 names->push_back(name);
1250
1/2
✓ Branch 1 taken 3681 times.
✗ Branch 2 not taken.
3681 modes->push_back(info.st_mode);
1251
5/5
✓ Branch 1 taken 3681 times.
✓ Branch 2 taken 39 times.
✓ Branch 4 taken 3681 times.
✓ Branch 5 taken 600 times.
✓ Branch 6 taken 39 times.
4359 }
1252 261 closedir(dirp);
1253
1254 261 SortTeam(names, modes);
1255 261 return true;
1256 }
1257
1258
1259 /**
1260 * Looks whether exe is an executable file. If exe is not an absolute path,
1261 * searches the PATH environment.
1262 */
1263 99 std::string FindExecutable(const std::string &exe) {
1264
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
99 if (exe.empty())
1265 return "";
1266
1267 99 std::vector<std::string> search_paths;
1268
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 66 times.
99 if (exe[0] == '/') {
1269
2/4
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 33 times.
✗ Branch 5 not taken.
33 search_paths.push_back(GetParentPath(exe));
1270 } else {
1271 66 char *path_env = getenv("PATH");
1272
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 if (path_env) {
1273
2/4
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 66 times.
✗ Branch 6 not taken.
66 search_paths = SplitString(path_env, ':');
1274 }
1275 }
1276
1277
2/2
✓ Branch 1 taken 363 times.
✓ Branch 2 taken 33 times.
396 for (unsigned i = 0; i < search_paths.size(); ++i) {
1278
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 363 times.
363 if (search_paths[i].empty())
1279 297 continue;
1280
2/4
✓ Branch 2 taken 363 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 363 times.
363 if (search_paths[i][0] != '/')
1281 continue;
1282
1283
3/6
✓ Branch 1 taken 363 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 363 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 363 times.
✗ Branch 9 not taken.
726 std::string path = search_paths[i] + "/" + GetFileName(exe);
1284 platform_stat64 info;
1285 363 int retval = platform_stat(path.c_str(), &info);
1286
2/2
✓ Branch 0 taken 297 times.
✓ Branch 1 taken 66 times.
363 if (retval != 0)
1287 297 continue;
1288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if (!S_ISREG(info.st_mode))
1289 continue;
1290 66 retval = access(path.c_str(), X_OK);
1291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if (retval != 0)
1292 continue;
1293
1294 66 return path;
1295
2/2
✓ Branch 1 taken 297 times.
✓ Branch 2 taken 66 times.
363 }
1296
1297
1/2
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 return "";
1298 99 }
1299
1300
1301 45 std::string GetUserName() {
1302 struct passwd pwd;
1303 45 struct passwd *result = NULL;
1304 45 int bufsize = 16 * 1024;
1305 45 char *buf = static_cast<char *>(smalloc(bufsize));
1306
2/4
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 45 times.
45 while (getpwuid_r(geteuid(), &pwd, buf, bufsize, &result) == ERANGE) {
1307 bufsize *= 2;
1308 buf = static_cast<char *>(srealloc(buf, bufsize));
1309 }
1310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 if (result == NULL) {
1311 free(buf);
1312 return "";
1313 }
1314
1/2
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
45 std::string user_name = pwd.pw_name;
1315 45 free(buf);
1316 45 return user_name;
1317 45 }
1318
1319 std::string GetShell() {
1320 struct passwd pwd;
1321 struct passwd *result = NULL;
1322 int bufsize = 16 * 1024;
1323 char *buf = static_cast<char *>(smalloc(bufsize));
1324 while (getpwuid_r(geteuid(), &pwd, buf, bufsize, &result) == ERANGE) {
1325 bufsize *= 2;
1326 buf = static_cast<char *>(srealloc(buf, bufsize));
1327 }
1328 if (result == NULL) {
1329 free(buf);
1330 return "";
1331 }
1332 std::string shell = pwd.pw_shell;
1333 free(buf);
1334 return shell;
1335 }
1336
1337 /**
1338 * UID -> Name from passwd database
1339 */
1340 33 bool GetUserNameOf(uid_t uid, std::string *username) {
1341 struct passwd pwd;
1342 33 struct passwd *result = NULL;
1343 33 int bufsize = 16 * 1024;
1344 33 char *buf = static_cast<char *>(smalloc(bufsize));
1345
2/4
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 33 times.
33 while (getpwuid_r(uid, &pwd, buf, bufsize, &result) == ERANGE) {
1346 bufsize *= 2;
1347 buf = static_cast<char *>(srealloc(buf, bufsize));
1348 }
1349
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if (result == NULL) {
1350 free(buf);
1351 return false;
1352 }
1353
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 if (username)
1354
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 *username = result->pw_name;
1355 33 free(buf);
1356 33 return true;
1357 }
1358
1359
1360 /**
1361 * Name -> UID from passwd database
1362 */
1363 175 bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid) {
1364 struct passwd pwd;
1365 175 struct passwd *result = NULL;
1366 175 int bufsize = 16 * 1024;
1367 175 char *buf = static_cast<char *>(smalloc(bufsize));
1368
2/4
✓ Branch 2 taken 175 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 175 times.
175 while (getpwnam_r(username.c_str(), &pwd, buf, bufsize, &result) == ERANGE) {
1369 bufsize *= 2;
1370 buf = static_cast<char *>(srealloc(buf, bufsize));
1371 }
1372
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 130 times.
175 if (result == NULL) {
1373 45 free(buf);
1374 45 return false;
1375 }
1376 130 *uid = result->pw_uid;
1377 130 *main_gid = result->pw_gid;
1378 130 free(buf);
1379 130 return true;
1380 }
1381
1382
1383 /**
1384 * Name -> GID from groups database
1385 */
1386 100 bool GetGidOf(const std::string &groupname, gid_t *gid) {
1387 struct group grp;
1388 100 struct group *result = NULL;
1389 100 int bufsize = 16 * 1024;
1390 100 char *buf = static_cast<char *>(smalloc(bufsize));
1391
2/4
✓ Branch 2 taken 100 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 100 times.
100 while (getgrnam_r(groupname.c_str(), &grp, buf, bufsize, &result) == ERANGE) {
1392 bufsize *= 2;
1393 buf = static_cast<char *>(srealloc(buf, bufsize));
1394 }
1395
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 67 times.
100 if (result == NULL) {
1396 33 free(buf);
1397 33 return false;
1398 }
1399 67 *gid = result->gr_gid;
1400 67 free(buf);
1401 67 return true;
1402 }
1403
1404 /**
1405 * read the current umask of this process
1406 * Note: umask query is guarded by a global mutex. Hence, always use
1407 * this function and beware of scalability bottlenecks
1408 */
1409 2574 mode_t GetUmask() {
1410 2574 const MutexLockGuard m(&getumask_mutex);
1411 2574 const mode_t my_umask = umask(0);
1412 2574 umask(my_umask);
1413 5148 return my_umask;
1414 2574 }
1415
1416
1417 /**
1418 * Adds gid to the list of supplementary groups
1419 */
1420 bool AddGroup2Persona(const gid_t gid) {
1421 const int ngroups = getgroups(0, NULL);
1422 if (ngroups < 0)
1423 return false;
1424 gid_t *groups = static_cast<gid_t *>(smalloc((ngroups + 1) * sizeof(gid_t)));
1425 int retval = getgroups(ngroups, groups);
1426 if (retval < 0) {
1427 free(groups);
1428 return false;
1429 }
1430 for (int i = 0; i < ngroups; ++i) {
1431 if (groups[i] == gid) {
1432 free(groups);
1433 return true;
1434 }
1435 }
1436 groups[ngroups] = gid;
1437 retval = setgroups(ngroups + 1, groups);
1438 free(groups);
1439 return retval == 0;
1440 }
1441
1442
1443 78 std::string GetHomeDirectory() {
1444 78 const uid_t uid = getuid();
1445 struct passwd pwd;
1446 78 struct passwd *result = NULL;
1447 78 int bufsize = 16 * 1024;
1448 78 char *buf = static_cast<char *>(smalloc(bufsize));
1449
2/4
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 78 times.
78 while (getpwuid_r(uid, &pwd, buf, bufsize, &result) == ERANGE) {
1450 bufsize *= 2;
1451 buf = static_cast<char *>(srealloc(buf, bufsize));
1452 }
1453
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (result == NULL) {
1454 free(buf);
1455 return "";
1456 }
1457
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 std::string home_dir = result->pw_dir;
1458 78 free(buf);
1459 78 return home_dir;
1460 78 }
1461
1462 /**
1463 * Returns the output of `uname -m`
1464 */
1465 45 std::string GetArch() {
1466 struct utsname info;
1467 45 const int retval = uname(&info);
1468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 assert(retval == 0);
1469
1/2
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
45 return info.machine;
1470 }
1471
1472
1473 /**
1474 * Sets soft and hard limit for maximum number of open file descriptors.
1475 * Returns 0 on success, -1 on failure, -2 if running under valgrind.
1476 */
1477 206 int SetLimitNoFile(unsigned limit_nofile) {
1478 struct rlimit rpl;
1479 206 memset(&rpl, 0, sizeof(rpl));
1480 206 getrlimit(RLIMIT_NOFILE, &rpl);
1481
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 77 times.
206 if (rpl.rlim_max < limit_nofile)
1482 129 rpl.rlim_max = limit_nofile;
1483 206 rpl.rlim_cur = limit_nofile;
1484 206 const int retval = setrlimit(RLIMIT_NOFILE, &rpl);
1485
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 129 times.
206 if (retval == 0)
1486 77 return 0;
1487
1488 #ifdef HAS_VALGRIND_HEADERS
1489
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 129 times.
129 return RUNNING_ON_VALGRIND ? -2 : -1;
1490 #else
1491 return -1;
1492 #endif
1493 }
1494
1495
1496 /**
1497 * Get the file descriptor limits
1498 */
1499 460 void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit) {
1500 460 *soft_limit = 0;
1501 460 *hard_limit = 0;
1502
1503 struct rlimit rpl;
1504 460 memset(&rpl, 0, sizeof(rpl));
1505 460 getrlimit(RLIMIT_NOFILE, &rpl);
1506 460 *soft_limit = rpl.rlim_cur;
1507
1508 #ifdef __APPLE__
1509 int value = sysconf(_SC_OPEN_MAX);
1510 assert(value > 0);
1511 *hard_limit = value;
1512 #else
1513 460 *hard_limit = rpl.rlim_max;
1514 #endif
1515 460 }
1516
1517
1518 39 std::vector<LsofEntry> Lsof(const std::string &path) {
1519 39 std::vector<LsofEntry> result;
1520
1521 39 std::vector<std::string> proc_names;
1522 39 std::vector<mode_t> proc_modes;
1523
2/4
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 39 times.
✗ Branch 6 not taken.
39 ListDirectory("/proc", &proc_names, &proc_modes);
1524
1525
2/2
✓ Branch 1 taken 2496 times.
✓ Branch 2 taken 39 times.
2535 for (unsigned i = 0; i < proc_names.size(); ++i) {
1526
2/2
✓ Branch 1 taken 1911 times.
✓ Branch 2 taken 585 times.
2496 if (!S_ISDIR(proc_modes[i]))
1527 2301 continue;
1528
2/2
✓ Branch 2 taken 390 times.
✓ Branch 3 taken 195 times.
585 if (proc_names[i].find_first_not_of("1234567890") != std::string::npos)
1529 390 continue;
1530
1531 195 std::vector<std::string> fd_names;
1532 195 std::vector<mode_t> fd_modes;
1533
1/2
✓ Branch 2 taken 195 times.
✗ Branch 3 not taken.
195 const std::string proc_dir = "/proc/" + proc_names[i];
1534
1/2
✓ Branch 1 taken 195 times.
✗ Branch 2 not taken.
195 const std::string fd_dir = proc_dir + "/fd";
1535
1/2
✓ Branch 1 taken 195 times.
✗ Branch 2 not taken.
195 const bool rvb = ListDirectory(fd_dir, &fd_names, &fd_modes);
1536 195 uid_t proc_uid = 0;
1537
1538 // The working directory of the process requires special handling
1539
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 39 times.
195 if (rvb) {
1540 platform_stat64 info;
1541 156 platform_stat(proc_dir.c_str(), &info);
1542 156 proc_uid = info.st_uid;
1543
1544
2/4
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 156 times.
✗ Branch 5 not taken.
156 const std::string cwd = ReadSymlink(proc_dir + "/cwd");
1545
5/9
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 156 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 156 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 39 times.
✓ Branch 12 taken 117 times.
156 if (HasPrefix(cwd + "/", path + "/", false /* ignore_case */)) {
1546 39 LsofEntry entry;
1547
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 entry.pid = static_cast<pid_t>(String2Uint64(proc_names[i]));
1548 39 entry.owner = proc_uid;
1549 39 entry.read_only = true; // A bit sloppy but good enough for the moment
1550
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 entry.executable = ReadSymlink(proc_dir + "/exe");
1551
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 entry.path = cwd;
1552
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 result.push_back(entry);
1553 39 }
1554 156 }
1555
1556
2/2
✓ Branch 1 taken 1053 times.
✓ Branch 2 taken 195 times.
1248 for (unsigned j = 0; j < fd_names.size(); ++j) {
1557
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1053 times.
1053 if (!S_ISLNK(fd_modes[j]))
1558 897 continue;
1559
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1053 times.
1053 if (fd_names[j].find_first_not_of("1234567890") != std::string::npos)
1560 continue;
1561
1562
3/6
✓ Branch 2 taken 1053 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1053 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1053 times.
✗ Branch 9 not taken.
2106 const std::string target = ReadSymlink(fd_dir + "/" + fd_names[j]);
1563
5/9
✓ Branch 1 taken 1053 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1053 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1053 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 897 times.
✓ Branch 12 taken 156 times.
1053 if (!HasPrefix(target + "/", path + "/", false /* ignore_case */))
1564 897 continue;
1565
1566 156 LsofEntry entry;
1567
1/2
✓ Branch 2 taken 156 times.
✗ Branch 3 not taken.
156 entry.pid = static_cast<pid_t>(String2Uint64(proc_names[i]));
1568 156 entry.owner = proc_uid;
1569 156 entry.read_only = !((fd_modes[j] & S_IWUSR) == S_IWUSR);
1570
2/4
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 156 times.
✗ Branch 5 not taken.
156 entry.executable = ReadSymlink(proc_dir + "/exe");
1571
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 entry.path = target;
1572
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 result.push_back(entry);
1573
2/2
✓ Branch 2 taken 156 times.
✓ Branch 3 taken 897 times.
1053 }
1574 195 }
1575
1576 78 return result;
1577 39 }
1578
1579
1580 138 bool ProcessExists(pid_t pid) {
1581
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
138 assert(pid > 0);
1582 138 const int retval = kill(pid, 0);
1583
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 46 times.
138 if (retval == 0)
1584 92 return true;
1585 46 return (errno != ESRCH);
1586 }
1587
1588
1589 /**
1590 * Blocks a signal for the calling thread.
1591 */
1592 79 void BlockSignal(int signum) {
1593 sigset_t sigset;
1594 79 int retval = sigemptyset(&sigset);
1595
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 assert(retval == 0);
1596 79 retval = sigaddset(&sigset, signum);
1597
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 assert(retval == 0);
1598 79 retval = pthread_sigmask(SIG_BLOCK, &sigset, NULL);
1599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 assert(retval == 0);
1600 79 }
1601
1602
1603 /**
1604 * Waits for a signal. The signal should be blocked before for all threads.
1605 * Threads inherit their parent's signal mask.
1606 */
1607 35 void WaitForSignal(int signum) {
1608 int retval;
1609 do {
1610 35 retval = platform_sigwait(signum);
1611
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
35 } while ((retval != signum) && (errno == EINTR));
1612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 assert(retval == signum);
1613 35 }
1614
1615
1616 /**
1617 * Returns -1 if the child crashed or the exit code otherwise.
1618 * @param pid Process identifier.
1619 * @param sig_ok List of signals that are still considered a successful
1620 * termination.
1621 */
1622 230 int WaitForChild(pid_t pid, const std::vector<int> &sig_ok) {
1623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 230 times.
230 assert(pid > 0);
1624 int statloc;
1625 while (true) {
1626
1/2
✓ Branch 1 taken 230 times.
✗ Branch 2 not taken.
230 const pid_t retval = waitpid(pid, &statloc, 0);
1627
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 196 times.
230 if (retval == -1) {
1628
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (errno == EINTR)
1629 continue;
1630 34 PANIC(kLogSyslogErr | kLogDebug, "waitpid failed with errno %d", errno);
1631 }
1632
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 196 times.
196 assert(retval == pid);
1633 196 break;
1634 }
1635
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 34 times.
196 if (WIFEXITED(statloc))
1636 162 return WEXITSTATUS(statloc);
1637 68 if (WIFSIGNALED(statloc)
1638
3/6
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 5 taken 34 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 34 times.
102 && (std::find(sig_ok.begin(), sig_ok.end(), WTERMSIG(statloc))
1639
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
102 != sig_ok.end()))
1640 return 0;
1641 34 return -1;
1642 }
1643
1644 /**
1645 * Exec a command as a daemon.
1646 */
1647
1648 bool ExecAsDaemon(const std::vector<std::string> &command_line,
1649 pid_t *child_pid) {
1650 assert(command_line.size() >= 1);
1651
1652 Pipe<kPipeDetachedChild> pipe_fork;
1653 const pid_t pid = fork();
1654 assert(pid >= 0);
1655 if (pid == 0) {
1656 pid_t pid_grand_child;
1657
1658 const char *argv[command_line.size() + 1];
1659 for (unsigned i = 0; i < command_line.size(); ++i)
1660 argv[i] = command_line[i].c_str();
1661 argv[command_line.size()] = NULL;
1662 int retval = setsid();
1663 assert(retval != -1);
1664
1665 pid_grand_child = fork();
1666 assert(pid_grand_child >= 0);
1667
1668 if (pid_grand_child != 0) {
1669 pipe_fork.Write<pid_t>(pid_grand_child);
1670 _exit(0);
1671 } else {
1672 const int null_read = open("/dev/null", O_RDONLY);
1673 const int null_write = open("/dev/null", O_WRONLY);
1674 assert((null_read >= 0) && (null_write >= 0));
1675 retval = dup2(null_read, 0);
1676 assert(retval == 0);
1677 retval = dup2(null_write, 1);
1678 assert(retval == 1);
1679 retval = dup2(null_write, 2);
1680 assert(retval == 2);
1681 close(null_read);
1682 close(null_write);
1683
1684 execvp(command_line[0].c_str(), const_cast<char **>(argv));
1685
1686 pipe_fork.CloseWriteFd();
1687 }
1688 }
1689 int statloc;
1690 waitpid(pid, &statloc, 0);
1691 pid_t buf_child_pid = 0;
1692 pipe_fork.Read(&buf_child_pid);
1693 if (child_pid != NULL)
1694 *child_pid = buf_child_pid;
1695 pipe_fork.CloseReadFd();
1696
1697 LogCvmfs(kLogCvmfs, kLogDebug, "exec'd as daemon %s (PID: %d)",
1698 command_line[0].c_str(), static_cast<int>(*child_pid));
1699 return true;
1700 }
1701
1702
1703 /**
1704 * Makes a daemon. The daemon() call is deprecated on OS X
1705 */
1706 void Daemonize() {
1707 pid_t pid;
1708 int statloc;
1709 if ((pid = fork()) == 0) {
1710 int retval = setsid();
1711 assert(retval != -1);
1712 if ((pid = fork()) == 0) {
1713
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 const int null_read = open("/dev/null", O_RDONLY);
1714
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 const int null_write = open("/dev/null", O_WRONLY);
1715
2/4
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
49 assert((null_read >= 0) && (null_write >= 0));
1716 49 retval = dup2(null_read, 0);
1717
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 assert(retval == 0);
1718 49 retval = dup2(null_write, 1);
1719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 assert(retval == 1);
1720 49 retval = dup2(null_write, 2);
1721
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 assert(retval == 2);
1722
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 close(null_read);
1723
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 close(null_write);
1724
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 LogCvmfs(kLogCvmfs, kLogDebug, "daemonized");
1725 } else {
1726 assert(pid > 0);
1727 _exit(0);
1728 }
1729 } else {
1730 assert(pid > 0);
1731 waitpid(pid, &statloc, 0);
1732 _exit(0);
1733 }
1734 49 }
1735
1736
1737 670 bool ExecuteBinary(int *fd_stdin,
1738 int *fd_stdout,
1739 int *fd_stderr,
1740 const std::string &binary_path,
1741 const std::vector<std::string> &argv,
1742 const bool double_fork,
1743 pid_t *child_pid) {
1744 int pipe_stdin[2];
1745 int pipe_stdout[2];
1746 int pipe_stderr[2];
1747
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 MakePipe(pipe_stdin);
1748
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 MakePipe(pipe_stdout);
1749
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 MakePipe(pipe_stderr);
1750
1751 670 std::set<int> preserve_fildes;
1752
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 preserve_fildes.insert(0);
1753
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 preserve_fildes.insert(1);
1754
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 preserve_fildes.insert(2);
1755 670 std::map<int, int> map_fildes;
1756
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 map_fildes[pipe_stdin[0]] = 0; // Reading end of pipe_stdin
1757
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 map_fildes[pipe_stdout[1]] = 1; // Writing end of pipe_stdout
1758
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 map_fildes[pipe_stderr[1]] = 2; // Writing end of pipe_stderr
1759 670 std::vector<std::string> cmd_line;
1760
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 cmd_line.push_back(binary_path);
1761
1/2
✓ Branch 5 taken 670 times.
✗ Branch 6 not taken.
670 cmd_line.insert(cmd_line.end(), argv.begin(), argv.end());
1762
1763
2/4
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 670 times.
670 if (!ManagedExec(cmd_line,
1764 preserve_fildes,
1765 map_fildes,
1766 true /* drop_credentials */,
1767 false /* clear_env */,
1768 double_fork,
1769 child_pid)) {
1770 ClosePipe(pipe_stdin);
1771 ClosePipe(pipe_stdout);
1772 ClosePipe(pipe_stderr);
1773 return false;
1774 }
1775
1776
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 close(pipe_stdin[0]);
1777
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 close(pipe_stdout[1]);
1778
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 close(pipe_stderr[1]);
1779 670 *fd_stdin = pipe_stdin[1];
1780 670 *fd_stdout = pipe_stdout[0];
1781 670 *fd_stderr = pipe_stderr[0];
1782 670 return true;
1783 670 }
1784
1785
1786 /**
1787 * Opens /bin/sh and provides file descriptors to write into stdin and
1788 * read from stdout. Quit shell simply by closing stderr, stdout, and stdin.
1789 */
1790 563 bool Shell(int *fd_stdin, int *fd_stdout, int *fd_stderr) {
1791 563 const bool double_fork = true;
1792
2/4
✓ Branch 2 taken 563 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 563 times.
✗ Branch 6 not taken.
1126 return ExecuteBinary(fd_stdin, fd_stdout, fd_stderr, "/bin/sh",
1793 1689 std::vector<std::string>(), double_fork);
1794 }
1795
1796 struct ForkFailures { // TODO(rmeusel): C++11 (type safe enum)
1797 enum Names {
1798 kSendPid,
1799 kUnknown,
1800 kFailDupFd,
1801 kFailCloseFds,
1802 kFailGetFdFlags,
1803 kFailSetFdFlags,
1804 kFailDropCredentials,
1805 kFailExec,
1806 };
1807
1808 static std::string ToString(const Names name) {
1809 switch (name) {
1810 case kSendPid:
1811 return "Sending PID";
1812
1813 default:
1814 case kUnknown:
1815 return "Unknown Status";
1816 case kFailDupFd:
1817 return "Duplicate File Descriptor";
1818 case kFailCloseFds:
1819 return "Close File Descriptors";
1820 case kFailGetFdFlags:
1821 return "Read File Descriptor Flags";
1822 case kFailSetFdFlags:
1823 return "Set File Descriptor Flags";
1824 case kFailDropCredentials:
1825 return "Lower User Permissions";
1826 case kFailExec:
1827 return "Invoking execvp()";
1828 }
1829 }
1830 };
1831
1832 /**
1833 * Loop through all possible FDs and close them.
1834 */
1835 17 static bool CloseAllFildesUntilMaxFD(const std::set<int> &preserve_fildes,
1836 int max_fd) {
1837
2/2
✓ Branch 0 taken 8500 times.
✓ Branch 1 taken 17 times.
8517 for (int fd = 0; fd < max_fd; fd++) {
1838
3/4
✓ Branch 1 taken 8500 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8415 times.
✓ Branch 4 taken 85 times.
8500 if (preserve_fildes.count(fd) == 0) {
1839
1/2
✓ Branch 1 taken 8415 times.
✗ Branch 2 not taken.
8415 close(fd);
1840 }
1841 }
1842
1843 17 return true;
1844 }
1845
1846 /**
1847 * Loop through /proc/self/fd and close the listed FDs.
1848 * Not used on macOS.
1849 */
1850 #ifndef __APPLE__
1851 34 static bool CloseAllFildesInProcSelfFd(const std::set<int> &preserve_fildes) {
1852 34 DIR *dirp = opendir("/proc/self/fd");
1853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (!dirp)
1854 return false;
1855
1856 platform_dirent64 *dirent;
1857
1858
2/2
✓ Branch 1 taken 793 times.
✓ Branch 2 taken 34 times.
827 while ((dirent = platform_readdir(dirp))) {
1859
1/2
✓ Branch 2 taken 793 times.
✗ Branch 3 not taken.
793 const std::string name(dirent->d_name);
1860 uint64_t name_uint64;
1861
1862 // Make sure the dir name is digits only (skips ".", ".." and similar).
1863
3/4
✓ Branch 1 taken 793 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 68 times.
✓ Branch 4 taken 725 times.
793 if (!String2Uint64Parse(name, &name_uint64)) {
1864 68 continue;
1865 }
1866
1867 725 const int fd = static_cast<int>(name_uint64);
1868
3/4
✓ Branch 1 taken 725 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 168 times.
✓ Branch 4 taken 557 times.
725 if (preserve_fildes.count(fd)) {
1869 168 continue;
1870 }
1871
1872
1/2
✓ Branch 1 taken 557 times.
✗ Branch 2 not taken.
557 close(fd);
1873
2/2
✓ Branch 1 taken 557 times.
✓ Branch 2 taken 236 times.
793 }
1874
1875 34 closedir(dirp);
1876
1877 34 return true;
1878 }
1879 #endif
1880
1881 /**
1882 * Closes all file descriptors except the ones in preserve_fildes.
1883 * To be used after fork but before exec.
1884 */
1885 51 bool CloseAllFildes(const std::set<int> &preserve_fildes) {
1886 51 const int max_fd = static_cast<int>(sysconf(_SC_OPEN_MAX));
1887
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
51 if (max_fd < 0) {
1888 return false;
1889 }
1890
1891 #ifdef __APPLE__
1892 return CloseAllFildesUntilMaxFD(preserve_fildes, max_fd);
1893 #else // ifdef __APPLE__
1894
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 17 times.
51 if (max_fd > 100000) {
1895 // CloseAllFildesUntilMaxFD is inefficient with very large max_fd.
1896 // Looping through /proc/self/fd performs better.
1897 34 return CloseAllFildesInProcSelfFd(preserve_fildes);
1898 }
1899
1900 17 return CloseAllFildesUntilMaxFD(preserve_fildes, max_fd);
1901 #endif // #ifdef __APPLE__
1902 }
1903
1904 /**
1905 * Execve to the given command line, preserving the given file descriptors.
1906 * If stdin, stdout, stderr should be preserved, add 0, 1, 2.
1907 * File descriptors from the parent process can also be mapped to the new
1908 * process (dup2) using map_fildes. Can be useful for
1909 * stdout/in/err redirection.
1910 * NOTE: The destination fildes have to be preserved!
1911 * NOTE: For forked child, held mutexes have invalid state, avoid operations.
1912 * Does a double fork to detach child.
1913 * The command_line parameter contains the binary at index 0 and the arguments
1914 * in the rest of the vector.
1915 * Using the optional parameter *pid it is possible to retrieve the process ID
1916 * of the spawned process.
1917 */
1918 746 bool ManagedExec(const std::vector<std::string> &command_line,
1919 const std::set<int> &preserve_fildes,
1920 const std::map<int, int> &map_fildes,
1921 const bool drop_credentials,
1922 const bool clear_env,
1923 const bool double_fork,
1924 pid_t *child_pid) {
1925
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 746 times.
746 assert(command_line.size() >= 1);
1926
1927
1/2
✓ Branch 1 taken 746 times.
✗ Branch 2 not taken.
746 Pipe<kPipeDetachedChild> pipe_fork;
1928 746 const pid_t pid = fork();
1929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 748 times.
748 assert(pid >= 0);
1930
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 746 times.
748 if (pid == 0) {
1931 pid_t pid_grand_child;
1932 int fd_flags;
1933 2 ForkFailures::Names failed = ForkFailures::kUnknown;
1934
1935 2 std::set<int> skip_fds = preserve_fildes;
1936
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 skip_fds.insert(pipe_fork.GetWriteFd());
1937
1938
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (clear_env) {
1939 #ifdef __APPLE__
1940 environ = NULL;
1941 #else
1942 const int retval = clearenv();
1943 assert(retval == 0);
1944 #endif
1945 }
1946
1947 2 const char *argv[command_line.size() + 1];
1948
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
5 for (unsigned i = 0; i < command_line.size(); ++i)
1949 3 argv[i] = command_line[i].c_str();
1950 2 argv[command_line.size()] = NULL;
1951
1952 // Child, map file descriptors
1953 4 for (std::map<int, int>::const_iterator i = map_fildes.begin(),
1954 2 iEnd = map_fildes.end();
1955
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
8 i != iEnd;
1956 6 ++i) {
1957 6 const int retval = dup2(i->first, i->second);
1958
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (retval == -1) {
1959 failed = ForkFailures::kFailDupFd;
1960 goto fork_failure;
1961 }
1962 }
1963
1964 // Child, close file descriptors
1965
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (!CloseAllFildes(skip_fds)) {
1966 failed = ForkFailures::kFailCloseFds;
1967 goto fork_failure;
1968 }
1969
1970 // Double fork to disconnect from parent
1971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (double_fork) {
1972 pid_grand_child = fork();
1973
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 assert(pid_grand_child >= 0);
1974
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 if (pid_grand_child != 0)
1975 _exit(0);
1976 }
1977
1978
1/2
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 fd_flags = fcntl(pipe_fork.GetWriteFd(), F_GETFD);
1979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (fd_flags < 0) {
1980 failed = ForkFailures::kFailGetFdFlags;
1981 goto fork_failure;
1982 }
1983 29 fd_flags |= FD_CLOEXEC;
1984
2/4
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 29 times.
29 if (fcntl(pipe_fork.GetWriteFd(), F_SETFD, fd_flags) < 0) {
1985 failed = ForkFailures::kFailSetFdFlags;
1986 goto fork_failure;
1987 }
1988
1989 #ifdef DEBUGMSG
1990
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
29 assert(setenv("__CVMFS_DEBUG_MODE__", "yes", 1) == 0);
1991 #endif
1992
5/8
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1 times.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 29 times.
29 if (drop_credentials && !SwitchCredentials(geteuid(), getegid(), false, true)) {
1993 failed = ForkFailures::kFailDropCredentials;
1994 goto fork_failure;
1995 }
1996
1997 // retrieve the PID of the new (grand) child process and send it to the
1998 // grand father
1999 29 pid_grand_child = getpid();
2000 29 failed = ForkFailures::kSendPid;
2001
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 pipe_fork.Write<ForkFailures::Names>(failed);
2002
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 pipe_fork.Write<pid_t>(pid_grand_child);
2003
2004 29 execvp(command_line[0].c_str(), const_cast<char **>(argv));
2005
2006 failed = ForkFailures::kFailExec;
2007
2008 fork_failure:
2009 pipe_fork.Write<ForkFailures::Names>(failed);
2010 _exit(1);
2011
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 }
2012
2/2
✓ Branch 0 taken 684 times.
✓ Branch 1 taken 62 times.
746 if (double_fork) {
2013 int statloc;
2014
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 waitpid(pid, &statloc, 0);
2015 }
2016
2017
1/2
✓ Branch 1 taken 746 times.
✗ Branch 2 not taken.
746 pipe_fork.CloseWriteFd();
2018
2019 // Either the PID or a return value is sent
2020 ForkFailures::Names status_code;
2021
1/2
✓ Branch 1 taken 746 times.
✗ Branch 2 not taken.
746 pipe_fork.Read<ForkFailures::Names>(&status_code);
2022
2023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 746 times.
746 if (status_code != ForkFailures::kSendPid) {
2024 pipe_fork.CloseReadFd();
2025 LogCvmfs(kLogCvmfs, kLogDebug, "managed execve failed (%s)",
2026 ForkFailures::ToString(status_code).c_str());
2027 return false;
2028 }
2029
2030 // read the PID of the spawned process if requested
2031 // (the actual read needs to be done in any case!)
2032 746 pid_t buf_child_pid = 0;
2033
1/2
✓ Branch 1 taken 746 times.
✗ Branch 2 not taken.
746 pipe_fork.Read(&buf_child_pid);
2034
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 563 times.
746 if (child_pid != NULL)
2035 183 *child_pid = buf_child_pid;
2036
1/2
✓ Branch 1 taken 746 times.
✗ Branch 2 not taken.
746 pipe_fork.CloseReadFd();
2037
1/2
✓ Branch 2 taken 746 times.
✗ Branch 3 not taken.
746 LogCvmfs(kLogCvmfs, kLogDebug, "execve'd %s (PID: %d)",
2038 746 command_line[0].c_str(), static_cast<int>(buf_child_pid));
2039 746 return true;
2040 746 }
2041
2042
2043 /**
2044 * Sleeps using select. This is without signals and doesn't interfere with
2045 * other uses of the ALRM signal.
2046 */
2047 924 void SafeSleepMs(const unsigned ms) {
2048 struct timeval wait_for;
2049 924 wait_for.tv_sec = ms / 1000;
2050 924 wait_for.tv_usec = (ms % 1000) * 1000;
2051
2/2
✓ Branch 1 taken 914 times.
✓ Branch 2 taken 10 times.
924 select(0, NULL, NULL, NULL, &wait_for);
2052 914 }
2053
2054
2055 /**
2056 * Deal with EINTR and partial writes.
2057 */
2058 6470 bool SafeWrite(int fd, const void *buf, size_t nbyte) {
2059
2/2
✓ Branch 0 taken 6470 times.
✓ Branch 1 taken 6294 times.
12764 while (nbyte) {
2060 6470 const ssize_t retval = write(fd, buf, nbyte);
2061
2/2
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 6294 times.
6470 if (retval < 0) {
2062
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 176 times.
176 if (errno == EINTR)
2063 continue;
2064 176 return false;
2065 }
2066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6294 times.
6294 assert(static_cast<size_t>(retval) <= nbyte);
2067 6294 buf = reinterpret_cast<const char *>(buf) + retval;
2068 6294 nbyte -= retval;
2069 }
2070 6294 return true;
2071 }
2072
2073 /**
2074 * The contents of the iov vector might be modified by the function.
2075 */
2076 157554 bool SafeWriteV(int fd, struct iovec *iov, unsigned iovcnt) {
2077 157554 unsigned nbytes = 0;
2078
2/2
✓ Branch 0 taken 443913 times.
✓ Branch 1 taken 157554 times.
601467 for (unsigned i = 0; i < iovcnt; ++i)
2079 443913 nbytes += iov[i].iov_len;
2080 157554 unsigned iov_idx = 0;
2081
2082
1/2
✓ Branch 0 taken 157554 times.
✗ Branch 1 not taken.
157554 while (nbytes) {
2083 315086 const ssize_t retval = writev(fd, &iov[iov_idx],
2084 157554 static_cast<int>(iovcnt - iov_idx));
2085
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 157532 times.
157532 if (retval < 0) {
2086 if (errno == EINTR)
2087 continue;
2088 return false;
2089 }
2090
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 157532 times.
157532 assert(static_cast<size_t>(retval) <= nbytes);
2091 157532 nbytes -= retval;
2092
2093 157532 unsigned sum_written_blocks = 0;
2094 157532 while ((sum_written_blocks + iov[iov_idx].iov_len)
2095
1/2
✓ Branch 0 taken 443649 times.
✗ Branch 1 not taken.
443649 <= static_cast<size_t>(retval)) {
2096 443649 sum_written_blocks += iov[iov_idx].iov_len;
2097 443649 iov_idx++;
2098
2/2
✓ Branch 0 taken 157532 times.
✓ Branch 1 taken 286117 times.
443649 if (iov_idx == iovcnt) {
2099
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 157532 times.
157532 assert(sum_written_blocks == static_cast<size_t>(retval));
2100 157532 return true;
2101 }
2102 }
2103 const unsigned offset = retval - sum_written_blocks;
2104 iov[iov_idx].iov_len -= offset;
2105 iov[iov_idx].iov_base = reinterpret_cast<char *>(iov[iov_idx].iov_base)
2106 + offset;
2107 }
2108
2109 return true;
2110 }
2111
2112
2113 /**
2114 * Deal with EINTR and partial reads.
2115 */
2116 16131533 ssize_t SafeRead(int fd, void *buf, size_t nbyte) {
2117 16131533 ssize_t total_bytes = 0;
2118
2/2
✓ Branch 0 taken 16350841 times.
✓ Branch 1 taken 16096877 times.
32447718 while (nbyte) {
2119 16350841 const ssize_t retval = read(fd, buf, nbyte);
2120
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 16357751 times.
16357837 if (retval < 0) {
2121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 86 times.
86 if (errno == EINTR)
2122 continue;
2123 86 return -1;
2124
2/2
✓ Branch 0 taken 41566 times.
✓ Branch 1 taken 16316185 times.
16357751 } else if (retval == 0) {
2125 41566 return total_bytes;
2126 }
2127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16316185 times.
16316185 assert(static_cast<size_t>(retval) <= nbyte);
2128 16316185 buf = reinterpret_cast<char *>(buf) + retval;
2129 16316185 nbyte -= retval;
2130 16316185 total_bytes += retval;
2131 }
2132 16096877 return total_bytes;
2133 }
2134
2135
2136 /**
2137 * Pull file contents into a string
2138 */
2139 1587 bool SafeReadToString(int fd, std::string *final_result) {
2140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1587 times.
1587 if (!final_result) {
2141 return false;
2142 }
2143
2144 1587 std::string tmp_result;
2145 static const int buf_size = 4096;
2146 char buf[4096];
2147 1587 ssize_t total_bytes = -1;
2148 do {
2149
1/2
✓ Branch 1 taken 17895 times.
✗ Branch 2 not taken.
17895 total_bytes = SafeRead(fd, buf, buf_size);
2150
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 17852 times.
17895 if (total_bytes < 0) {
2151 43 return false;
2152 }
2153
1/2
✓ Branch 1 taken 17852 times.
✗ Branch 2 not taken.
17852 tmp_result.append(buf, total_bytes);
2154
2/2
✓ Branch 0 taken 16308 times.
✓ Branch 1 taken 1544 times.
17852 } while (total_bytes == buf_size);
2155 1544 final_result->swap(tmp_result);
2156 1544 return true;
2157 1587 }
2158
2159 4529 bool SafeWriteToFile(const std::string &content,
2160 const std::string &path,
2161 int mode) {
2162 4529 const int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
2163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4529 times.
4529 if (fd < 0)
2164 return false;
2165 4529 const bool retval = SafeWrite(fd, content.data(), content.size());
2166 4529 close(fd);
2167 4529 return retval;
2168 }
2169
2170
2171 #ifdef CVMFS_NAMESPACE_GUARD
2172 } // namespace CVMFS_NAMESPACE_GUARD
2173 #endif
2174