GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/posix.cc
Date: 2026-03-22 02:40:38
Exec Total Coverage
Lines: 903 1160 77.8%
Branches: 673 1267 53.1%

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