GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/posix.cc
Date: 2026-10-11 02:40:02
Exec Total Coverage
Lines: 904 1160 77.9%
Branches: 671 1265 53.0%

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