GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/posix.cc
Date: 2026-05-10 02:36:07
Exec Total Coverage
Lines: 903 1160 77.8%
Branches: 671 1265 53.0%

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