GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/posix.cc
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 900 1149 78.3%
Branches: 673 1261 53.4%

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