GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/posix.cc
Date: 2025-02-09 02:34:19
Exec Total Coverage
Lines: 893 1138 78.5%
Branches: 672 1263 53.2%

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