GCC Code Coverage Report


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