GCC Code Coverage Report


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