GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/posix.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 888 1091 81.4%
Branches: 667 1207 55.3%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * Some common functions.
5 */
6
7 #ifndef __STDC_FORMAT_MACROS
8 // NOLINTNEXTLINE
9 #define __STDC_FORMAT_MACROS
10 #endif
11
12 #include "cvmfs_config.h"
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 339 std::string MakeCanonicalPath(const std::string &path) {
99
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 338 times.
339 if (path.length() == 0) return path;
100
101
2/2
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 332 times.
338 if (path[path.length()-1] == '/') {
102 6 return path.substr(0, path.length()-1);
103 } else {
104 332 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 3426 std::string GetFileName(const std::string &path) {
146 3426 const std::string::size_type idx = path.find_last_of('/');
147
2/2
✓ Branch 0 taken 3402 times.
✓ Branch 1 taken 24 times.
3426 if (idx != std::string::npos) {
148 3402 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 171 FileSystemInfo GetFileSystemInfo(const std::string &path) {
181 171 FileSystemInfo result;
182
183 struct statfs info;
184 171 int retval = statfs(path.c_str(), &info);
185
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 165 times.
171 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 162 times.
165 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 162 default:
205 162 result.type = kFsTypeUnknown;
206 }
207
208 #ifdef CVMFS_HAS_STATFS_F_FLAGS
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 165 times.
165 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 165 return result;
220 }
221
222
223 37 std::string ReadSymlink(const std::string &path) {
224 // TODO(jblomer): avoid PATH_MAX
225 char buf[PATH_MAX + 1];
226 37 ssize_t nchars = readlink(path.c_str(), buf, PATH_MAX);
227
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 2 times.
37 if (nchars >= 0) {
228 35 buf[nchars] = '\0';
229
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 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 28 times.
✓ Branch 2 taken 1 times.
29 for (unsigned i = 0; i < mount_list.size(); ++i) {
270
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 26 times.
28 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 74 void CreateFile(
281 const std::string &path,
282 const int mode,
283 const bool ignore_failure)
284 {
285 74 int fd = open(path.c_str(), O_CREAT, mode);
286
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 1 times.
74 if (fd >= 0) {
287 73 close(fd);
288 73 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 906 void MakePipe(int pipe_fd[2]) {
493 906 int retval = pipe(pipe_fd);
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 906 times.
906 assert(retval == 0);
495 906 }
496
497
498 /**
499 * Writes to a pipe should always succeed.
500 */
501 3908 void WritePipe(int fd, const void *buf, size_t nbyte) {
502 ssize_t num_bytes;
503 do {
504 3908 num_bytes = write(fd, buf, nbyte);
505
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3908 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3908 } while ((num_bytes < 0) && (errno == EINTR));
506
2/4
✓ Branch 0 taken 3908 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3908 times.
✗ Branch 3 not taken.
3908 assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
507 3908 }
508
509
510 /**
511 * Reads from a pipe should always succeed.
512 */
513 4197233 void ReadPipe(int fd, void *buf, size_t nbyte) {
514 ssize_t num_bytes;
515 do {
516 4197233 num_bytes = read(fd, buf, nbyte);
517
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4197233 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4197233 } while ((num_bytes < 0) && (errno == EINTR));
518
2/4
✓ Branch 0 taken 4197233 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4197233 times.
✗ Branch 3 not taken.
4197233 assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
519 4197233 }
520
521
522 /**
523 * Reads from a pipe where writer's end is not yet necessarily connected
524 */
525 144 void ReadHalfPipe(int fd, void *buf, size_t nbyte) {
526 ssize_t num_bytes;
527 144 unsigned i = 0;
528 144 unsigned backoff_ms = 1;
529 144 const unsigned max_backoff_ms = 256;
530 do {
531 // When the writer is not connected, this takes ~200-300ns per call as per
532 // micro benchmarks
533 144 num_bytes = read(fd, buf, nbyte);
534
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
144 if ((num_bytes < 0) && (errno == EINTR))
535 continue;
536 144 i++;
537 // Start backing off when the busy loop reaches the ballpark of 1ms
538
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
144 if ((i > 3000) && (num_bytes == 0)) {
539 // The BackoffThrottle would pull in too many dependencies
540 SafeSleepMs(backoff_ms);
541 if (backoff_ms < max_backoff_ms) backoff_ms *= 2;
542 }
543
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 } while (num_bytes == 0);
544
2/4
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
144 assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
545 144 }
546
547
548 /**
549 * Closes both ends of a pipe
550 */
551 764 void ClosePipe(int pipe_fd[2]) {
552 764 close(pipe_fd[0]);
553 764 close(pipe_fd[1]);
554 764 }
555
556
557 /**
558 * Compares two directory trees on the meta-data level. Returns true iff the
559 * trees have identical content.
560 */
561 1621 bool DiffTree(const std::string &path_a, const std::string &path_b) {
562 int retval;
563 1621 std::vector<std::string> ls_a;
564 1621 std::vector<std::string> ls_b;
565 1621 std::vector<std::string> subdirs;
566
567
1/2
✓ Branch 2 taken 1621 times.
✗ Branch 3 not taken.
1621 DIR *dirp_a = opendir(path_a.c_str());
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1621 times.
1621 if (dirp_a == NULL) return false;
569
1/2
✓ Branch 2 taken 1621 times.
✗ Branch 3 not taken.
1621 DIR *dirp_b = opendir(path_b.c_str());
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1621 times.
1621 if (dirp_b == NULL) {
571 closedir(dirp_a);
572 return false;
573 }
574
575 platform_dirent64 *dirent;
576
3/4
✓ Branch 1 taken 6548 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4927 times.
✓ Branch 4 taken 1621 times.
6548 while ((dirent = platform_readdir(dirp_a))) {
577
1/2
✓ Branch 2 taken 4927 times.
✗ Branch 3 not taken.
4927 const std::string name(dirent->d_name);
578
6/6
✓ Branch 1 taken 3306 times.
✓ Branch 2 taken 1621 times.
✓ Branch 4 taken 1621 times.
✓ Branch 5 taken 1685 times.
✓ Branch 6 taken 3242 times.
✓ Branch 7 taken 1685 times.
4927 if ((name == ".") || (name == ".."))
579 3242 continue;
580
2/4
✓ Branch 1 taken 1685 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1685 times.
✗ Branch 5 not taken.
1685 const std::string path = path_a + "/" + name;
581
1/2
✓ Branch 1 taken 1685 times.
✗ Branch 2 not taken.
1685 ls_a.push_back(path);
582
583 platform_stat64 info;
584 1685 retval = platform_lstat(path.c_str(), &info);
585
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1685 times.
1685 if (retval != 0) {
586 closedir(dirp_a);
587 closedir(dirp_b);
588 return false;
589 }
590
3/4
✓ Branch 0 taken 1638 times.
✓ Branch 1 taken 47 times.
✓ Branch 3 taken 1638 times.
✗ Branch 4 not taken.
1685 if (S_ISDIR(info.st_mode)) subdirs.push_back(name);
591
3/5
✓ Branch 1 taken 1685 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1685 times.
✓ Branch 5 taken 3242 times.
✗ Branch 6 not taken.
4927 }
592
3/4
✓ Branch 1 taken 6542 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4921 times.
✓ Branch 4 taken 1621 times.
6542 while ((dirent = platform_readdir(dirp_b))) {
593
1/2
✓ Branch 2 taken 4921 times.
✗ Branch 3 not taken.
4921 const std::string name(dirent->d_name);
594
6/6
✓ Branch 1 taken 3300 times.
✓ Branch 2 taken 1621 times.
✓ Branch 4 taken 1621 times.
✓ Branch 5 taken 1679 times.
✓ Branch 6 taken 3242 times.
✓ Branch 7 taken 1679 times.
4921 if ((name == ".") || (name == ".."))
595 3242 continue;
596
2/4
✓ Branch 1 taken 1679 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1679 times.
✗ Branch 5 not taken.
1679 const std::string path = path_b + "/" + name;
597
1/2
✓ Branch 1 taken 1679 times.
✗ Branch 2 not taken.
1679 ls_b.push_back(path);
598
2/2
✓ Branch 2 taken 1679 times.
✓ Branch 3 taken 3242 times.
4921 }
599
1/2
✓ Branch 1 taken 1621 times.
✗ Branch 2 not taken.
1621 closedir(dirp_a);
600
1/2
✓ Branch 1 taken 1621 times.
✗ Branch 2 not taken.
1621 closedir(dirp_b);
601
602
1/2
✓ Branch 3 taken 1621 times.
✗ Branch 4 not taken.
1621 sort(ls_a.begin(), ls_a.end());
603
1/2
✓ Branch 3 taken 1621 times.
✗ Branch 4 not taken.
1621 sort(ls_b.begin(), ls_b.end());
604
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1620 times.
1621 if (ls_a.size() != ls_b.size())
605 1 return false;
606
2/2
✓ Branch 1 taken 1658 times.
✓ Branch 2 taken 1620 times.
3278 for (unsigned i = 0; i < ls_a.size(); ++i) {
607
3/7
✓ Branch 2 taken 1658 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1658 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1658 times.
1658 if (GetFileName(ls_a[i]) != GetFileName(ls_b[i])) return false;
608 platform_stat64 info_a;
609 platform_stat64 info_b;
610 1658 retval = platform_lstat(ls_a[i].c_str(), &info_a);
611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1658 times.
1658 if (retval != 0) return false;
612 1658 retval = platform_lstat(ls_b[i].c_str(), &info_b);
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1658 times.
1658 if (retval != 0) return false;
614
1/2
✓ Branch 0 taken 1658 times.
✗ Branch 1 not taken.
1658 if ((info_a.st_mode != info_b.st_mode) ||
615
1/2
✓ Branch 0 taken 1658 times.
✗ Branch 1 not taken.
1658 (info_a.st_uid != info_b.st_uid) ||
616
1/2
✓ Branch 0 taken 1658 times.
✗ Branch 1 not taken.
1658 (info_a.st_gid != info_b.st_gid) ||
617
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1658 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1658 ((info_a.st_size != info_b.st_size) && !S_ISDIR(info_a.st_mode)))
618 {
619 return false;
620 }
621 }
622
623
2/2
✓ Branch 1 taken 1618 times.
✓ Branch 2 taken 1620 times.
3238 for (unsigned i = 0; i < subdirs.size(); ++i) {
624
3/6
✓ Branch 2 taken 1618 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1618 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1618 times.
✗ Branch 9 not taken.
1618 bool retval_subtree = DiffTree(path_a + "/" + subdirs[i],
625
2/4
✓ Branch 2 taken 1618 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1618 times.
✗ Branch 6 not taken.
3236 path_b + "/" + subdirs[i]);
626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1618 times.
1618 if (!retval_subtree) return false;
627 }
628
629 1620 return true;
630 1621 }
631
632
633 /**
634 * Changes a non-blocking file descriptor to a blocking one.
635 */
636 5 void Nonblock2Block(int filedes) {
637 5 int flags = fcntl(filedes, F_GETFL);
638
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 assert(flags != -1);
639 5 int retval = fcntl(filedes, F_SETFL, flags & ~O_NONBLOCK);
640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 assert(retval != -1);
641 5 }
642
643
644 /**
645 * Changes a blocking file descriptor to a non-blocking one.
646 */
647 5 void Block2Nonblock(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 * Drops the characters of string to a socket. It doesn't matter
657 * if the other side has hung up.
658 */
659 10 void SendMsg2Socket(const int fd, const std::string &msg) {
660 10 (void)send(fd, &msg[0], msg.length(), MSG_NOSIGNAL);
661 10 }
662
663 /**
664 * Sends the file descriptor passing_fd to the socket socket_fd. Can be used
665 * to transfer an open file descriptor from one process to another. Use
666 * ConnectSocket() to get the socket_fd.
667 */
668 1 bool SendFd2Socket(int socket_fd, int passing_fd) {
669 union {
670 // Make sure that ctrl_msg is properly aligned.
671 struct cmsghdr align;
672 // Buffer large enough to store the file descriptor (ancillary data)
673 unsigned char buf[CMSG_SPACE(sizeof(int))];
674 } ctrl_msg;
675
676 1 memset(ctrl_msg.buf, 0, sizeof(ctrl_msg.buf));
677
678 struct msghdr msgh;
679 1 msgh.msg_name = NULL;
680 1 msgh.msg_namelen = 0;
681
682 1 unsigned char dummy = 0;
683 struct iovec iov;
684 1 iov.iov_base = &dummy;
685 1 iov.iov_len = 1;
686 1 msgh.msg_iov = &iov;
687 1 msgh.msg_iovlen = 1;
688
689 1 msgh.msg_control = ctrl_msg.buf;
690 1 msgh.msg_controllen = sizeof(ctrl_msg.buf);
691
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh);
692 1 cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
693 1 cmsgp->cmsg_level = SOL_SOCKET;
694 1 cmsgp->cmsg_type = SCM_RIGHTS;
695 1 memcpy(CMSG_DATA(cmsgp), &passing_fd, sizeof(int));
696
697
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ssize_t retval = sendmsg(socket_fd, &msgh, 0);
698 1 return (retval != -1);
699 }
700
701
702 /**
703 * Returns the file descriptor that has been sent with SendFd2Socket. The
704 * msg_fd file descriptor needs to come from a call to accept() on the socket
705 * where the passing file descriptor has been sent to.
706 * Returns -errno on error.
707 */
708 int RecvFdFromSocket(int msg_fd) {
709 union {
710 // Make sure that ctrl_msg is properly aligned.
711 struct cmsghdr align;
712 // Buffer large enough to store the file descriptor (ancillary data)
713 unsigned char buf[CMSG_SPACE(sizeof(int))];
714 } ctrl_msg;
715
716 memset(ctrl_msg.buf, 0, sizeof(ctrl_msg.buf));
717
718 struct msghdr msgh;
719 msgh.msg_name = NULL;
720 msgh.msg_namelen = 0;
721
722 unsigned char dummy;
723 struct iovec iov;
724 iov.iov_base = &dummy;
725 iov.iov_len = 1;
726 msgh.msg_iov = &iov;
727 msgh.msg_iovlen = 1;
728
729 msgh.msg_control = ctrl_msg.buf;
730 msgh.msg_controllen = sizeof(ctrl_msg.buf);
731
732 ssize_t retval = recvmsg(msg_fd, &msgh, 0);
733 if (retval == -1)
734 return -errno;
735
736 struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh);
737 assert(cmsgp != NULL);
738 if (cmsgp->cmsg_len != CMSG_LEN(sizeof(int)))
739 return -ERANGE;
740 assert(cmsgp->cmsg_level == SOL_SOCKET);
741 assert(cmsgp->cmsg_type == SCM_RIGHTS);
742
743 int passing_fd;
744 memcpy(&passing_fd, CMSG_DATA(cmsgp), sizeof(int));
745 assert(passing_fd >= 0);
746 return passing_fd;
747 }
748
749
750 1 std::string GetHostname() {
751 char name[HOST_NAME_MAX + 1];
752 1 int retval = gethostname(name, HOST_NAME_MAX);
753
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 assert(retval == 0);
754
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return name;
755 }
756
757
758 /**
759 * set(e){g/u}id wrapper.
760 */
761 29 bool SwitchCredentials(const uid_t uid, const gid_t gid,
762 const bool temporarily)
763 {
764 29 LogCvmfs(kLogCvmfs, kLogDebug, "current credentials uid %d gid %d "
765 "euid %d egid %d, switching to %d %d (temp: %d)",
766 getuid(), getgid(), geteuid(), getegid(), uid, gid, temporarily);
767 29 int retval = 0;
768
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (temporarily) {
769 if (gid != getegid())
770 retval = setegid(gid);
771 if ((retval == 0) && (uid != geteuid()))
772 retval = seteuid(uid);
773 } else {
774 // If effective uid is not root, we must first gain root access back
775
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())) {
776 retval = SwitchCredentials(0, getgid(), true);
777 if (!retval)
778 return false;
779 }
780
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);
781 }
782 29 LogCvmfs(kLogCvmfs, kLogDebug, "switch credentials result %d (%d)",
783 29 retval, errno);
784 29 return retval == 0;
785 }
786
787
788 /**
789 * Checks if the regular file path exists.
790 */
791 2394 bool FileExists(const std::string &path) {
792 platform_stat64 info;
793
2/2
✓ Branch 2 taken 1398 times.
✓ Branch 3 taken 996 times.
3792 return ((platform_lstat(path.c_str(), &info) == 0) &&
794
2/2
✓ Branch 0 taken 1362 times.
✓ Branch 1 taken 36 times.
3792 S_ISREG(info.st_mode));
795 }
796
797
798 /**
799 * Returns -1 on failure.
800 */
801 371746 int64_t GetFileSize(const std::string &path) {
802 platform_stat64 info;
803 371746 int retval = platform_stat(path.c_str(), &info);
804
2/2
✓ Branch 0 taken 370068 times.
✓ Branch 1 taken 2476 times.
372544 if (retval != 0)
805 370068 return -1;
806 2476 return info.st_size;
807 }
808
809
810 /**
811 * Checks if the directory (not symlink) path exists.
812 */
813 270 bool DirectoryExists(const std::string &path) {
814 platform_stat64 info;
815
2/2
✓ Branch 2 taken 266 times.
✓ Branch 3 taken 4 times.
536 return ((platform_lstat(path.c_str(), &info) == 0) &&
816
1/2
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
536 S_ISDIR(info.st_mode));
817 }
818
819
820 /**
821 * Checks if the symlink file path exists.
822 */
823 59 bool SymlinkExists(const std::string &path) {
824 platform_stat64 info;
825
2/2
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 7 times.
111 return ((platform_lstat(path.c_str(), &info) == 0) &&
826
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 8 times.
111 S_ISLNK(info.st_mode));
827 }
828
829
830 /**
831 * Equivalent of `ln -sf $src $dest`
832 */
833 6 bool SymlinkForced(const std::string &src, const std::string &dest) {
834 6 int retval = unlink(dest.c_str());
835
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))
836 return false;
837 6 retval = symlink(src.c_str(), dest.c_str());
838 6 return retval == 0;
839 }
840
841
842 /**
843 * The mkdir -p command. Additionally checks if the directory is writable
844 * if it exists.
845 */
846 52314 bool MkdirDeep(
847 const std::string &path,
848 const mode_t mode,
849 bool verify_writable)
850 {
851
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 52312 times.
52314 if (path == "") return false;
852
853 52312 int retval = mkdir(path.c_str(), mode);
854
2/2
✓ Branch 0 taken 52138 times.
✓ Branch 1 taken 174 times.
52312 if (retval == 0) return true;
855
856
2/4
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 71 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
277 if ((errno == ENOENT) &&
857
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 71 times.
✓ Branch 11 taken 103 times.
✓ Branch 12 taken 71 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
277 (MkdirDeep(GetParentPath(path), mode, verify_writable)))
858 {
859 103 return MkdirDeep(path, mode, verify_writable);
860 }
861
862
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 2 times.
71 if (errno == EEXIST) {
863 platform_stat64 info;
864
5/6
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 68 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 68 times.
✓ Branch 7 taken 1 times.
69 if ((platform_stat(path.c_str(), &info) == 0) && S_ISDIR(info.st_mode)) {
865
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 58 times.
68 if (verify_writable) {
866 10 retval = utimes(path.c_str(), NULL);
867
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (retval == 0)
868 68 return true;
869 } else {
870 58 return true;
871 }
872 }
873 }
874
875 3 return false;
876 }
877
878
879 /**
880 * Creates the "hash cache" directory structure in path.
881 */
882 213 bool MakeCacheDirectories(const std::string &path, const mode_t mode) {
883
1/2
✓ Branch 1 taken 213 times.
✗ Branch 2 not taken.
213 const std::string canonical_path = MakeCanonicalPath(path);
884
885
1/2
✓ Branch 1 taken 213 times.
✗ Branch 2 not taken.
213 std::string this_path = canonical_path + "/quarantaine";
886
3/4
✓ Branch 1 taken 213 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 211 times.
213 if (!MkdirDeep(this_path, mode, false)) return false;
887
888
1/2
✓ Branch 1 taken 211 times.
✗ Branch 2 not taken.
211 this_path = canonical_path + "/ff";
889
890 platform_stat64 stat_info;
891
2/2
✓ Branch 2 taken 176 times.
✓ Branch 3 taken 35 times.
211 if (platform_stat(this_path.c_str(), &stat_info) != 0) {
892
1/2
✓ Branch 1 taken 176 times.
✗ Branch 2 not taken.
176 this_path = canonical_path + "/txn";
893
2/4
✓ Branch 1 taken 176 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 176 times.
176 if (!MkdirDeep(this_path, mode, false))
894 return false;
895
2/2
✓ Branch 0 taken 45056 times.
✓ Branch 1 taken 176 times.
45232 for (int i = 0; i <= 0xff; i++) {
896 char hex[4];
897 45056 snprintf(hex, sizeof(hex), "%02x", i);
898
3/6
✓ Branch 2 taken 45056 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 45056 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 45056 times.
✗ Branch 9 not taken.
45056 this_path = canonical_path + "/" + std::string(hex);
899
2/4
✓ Branch 1 taken 45056 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 45056 times.
45056 if (!MkdirDeep(this_path, mode, false))
900 return false;
901 }
902 }
903 211 return true;
904 213 }
905
906
907 /**
908 * Tries to locks file path, return an error if file is already locked.
909 * Creates path if required.
910 *
911 * \return file descriptor, -1 on error, -2 if it would block
912 */
913 98 int TryLockFile(const std::string &path) {
914 98 const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600);
915
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 97 times.
98 if (fd_lockfile < 0)
916 1 return -1;
917
918
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 94 times.
97 if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) {
919 3 close(fd_lockfile);
920
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (errno != EWOULDBLOCK)
921 return -1;
922 3 return -2;
923 }
924
925 94 return fd_lockfile;
926 }
927
928
929 /**
930 * Tries to write the process id in a /var/run/progname.pid like file. Returns
931 * the same as TryLockFile.
932 *
933 * \return file descriptor, -1 on error, -2 if it would block
934 */
935 4 int WritePidFile(const std::string &path) {
936
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 const int fd = open(path.c_str(), O_CREAT | O_RDWR, 0600);
937
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (fd < 0)
938 1 return -1;
939
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
940
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 close(fd);
941
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (errno != EWOULDBLOCK)
942 return -1;
943 1 return -2;
944 }
945
946 // Don't leak the file descriptor to exec'd children
947
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 int flags = fcntl(fd, F_GETFD);
948
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(flags != -1);
949 2 flags |= FD_CLOEXEC;
950
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 flags = fcntl(fd, F_SETFD, flags);
951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(flags != -1);
952
953 char buf[64];
954
955 2 snprintf(buf, sizeof(buf), "%" PRId64 "\n", static_cast<uint64_t>(getpid()));
956 bool retval =
957
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));
958
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!retval) {
959 UnlockFile(fd);
960 return -1;
961 }
962 2 return fd;
963 }
964
965
966 /**
967 * Locks file path, blocks if file is already locked. Creates path if required.
968 *
969 * \return file descriptor, -1 on error
970 */
971 99 int LockFile(const std::string &path) {
972 99 const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600);
973
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 95 times.
99 if (fd_lockfile < 0)
974 4 return -1;
975
976
977
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 94 times.
95 if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) {
978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (errno != EWOULDBLOCK) {
979 close(fd_lockfile);
980 return -1;
981 }
982 1 LogCvmfs(kLogCvmfs, kLogSyslog, "another process holds %s, waiting.",
983 path.c_str());
984
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (flock(fd_lockfile, LOCK_EX) != 0) {
985 close(fd_lockfile);
986 return -1;
987 }
988 1 LogCvmfs(kLogCvmfs, kLogSyslog, "lock %s acquired", path.c_str());
989 }
990
991 95 return fd_lockfile;
992 }
993
994
995 184 void UnlockFile(const int filedes) {
996 184 int retval = flock(filedes, LOCK_UN);
997
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 184 times.
184 assert(retval == 0);
998 184 close(filedes);
999 184 }
1000
1001
1002 /**
1003 * Wrapper around mkstemp.
1004 */
1005 1628 FILE *CreateTempFile(const std::string &path_prefix, const int mode,
1006 const char *open_flags, std::string *final_path)
1007 {
1008 1628 *final_path = path_prefix + ".XXXXXX";
1009 1628 char *tmp_file = strdupa(final_path->c_str());
1010 1628 int tmp_fd = mkstemp(tmp_file);
1011
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1625 times.
1628 if (tmp_fd < 0) {
1012 3 return NULL;
1013 }
1014
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1625 times.
1625 if (fchmod(tmp_fd, mode) != 0) {
1015 close(tmp_fd);
1016 return NULL;
1017 }
1018
1019 1625 *final_path = tmp_file;
1020 1625 FILE *tmp_fp = fdopen(tmp_fd, open_flags);
1021
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1625 times.
1625 if (!tmp_fp) {
1022 close(tmp_fd);
1023 unlink(tmp_file);
1024 return NULL;
1025 }
1026
1027 1625 return tmp_fp;
1028 }
1029
1030
1031 /**
1032 * Create the file but don't open. Use only in non-public tmp directories.
1033 */
1034 497 std::string CreateTempPath(const std::string &path_prefix, const int mode) {
1035 497 std::string result;
1036
1/2
✓ Branch 1 taken 497 times.
✗ Branch 2 not taken.
497 FILE *f = CreateTempFile(path_prefix, mode, "w", &result);
1037
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 496 times.
497 if (!f)
1038
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return "";
1039
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 fclose(f);
1040 496 return result;
1041 497 }
1042
1043
1044 /**
1045 * Create a directory with a unique name.
1046 */
1047 243 std::string CreateTempDir(const std::string &path_prefix) {
1048
1/2
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
243 std::string dir = path_prefix + ".XXXXXX";
1049 243 char *tmp_dir = strdupa(dir.c_str());
1050 243 tmp_dir = mkdtemp(tmp_dir);
1051
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 242 times.
243 if (tmp_dir == NULL)
1052
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return "";
1053
1/2
✓ Branch 2 taken 242 times.
✗ Branch 3 not taken.
242 return std::string(tmp_dir);
1054 243 }
1055
1056
1057 /**
1058 * Get the current working directory of the running process
1059 */
1060 220 std::string GetCurrentWorkingDirectory() {
1061 char cwd[PATH_MAX];
1062
3/9
✓ Branch 1 taken 220 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 220 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 220 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
220 return (getcwd(cwd, sizeof(cwd)) != NULL) ? std::string(cwd) : std::string();
1063 }
1064
1065
1066 /**
1067 * Helper class that provides callback functions for the file system traversal.
1068 */
1069 class RemoveTreeHelper {
1070 public:
1071 bool success;
1072 472 RemoveTreeHelper() {
1073 472 success = true;
1074 472 }
1075 2650 void RemoveFile(const std::string &parent_path, const std::string &name) {
1076
1/2
✓ Branch 2 taken 2650 times.
✗ Branch 3 not taken.
2650 int retval = unlink((parent_path + "/" + name).c_str());
1077
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2650 times.
2650 if (retval != 0)
1078 success = false;
1079 2650 }
1080 1357 void RemoveDir(const std::string &parent_path, const std::string &name) {
1081
1/2
✓ Branch 2 taken 1357 times.
✗ Branch 3 not taken.
1357 int retval = rmdir((parent_path + "/" + name).c_str());
1082
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1357 times.
1357 if (retval != 0)
1083 success = false;
1084 1357 }
1085 52831 bool TryRemoveDir(const std::string &parent_path, const std::string &name) {
1086
1/2
✓ Branch 2 taken 52831 times.
✗ Branch 3 not taken.
52831 int retval = rmdir((parent_path + "/" + name).c_str());
1087 52831 return (retval != 0);
1088 }
1089 };
1090
1091
1092 /**
1093 * Does rm -rf on path.
1094 */
1095 506 bool RemoveTree(const std::string &path) {
1096 platform_stat64 info;
1097 506 int retval = platform_lstat(path.c_str(), &info);
1098
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 472 times.
506 if (retval != 0)
1099 34 return errno == ENOENT;
1100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 472 times.
472 if (!S_ISDIR(info.st_mode))
1101 return false;
1102
1103
1/2
✓ Branch 1 taken 472 times.
✗ Branch 2 not taken.
472 RemoveTreeHelper *remove_tree_helper = new RemoveTreeHelper();
1104 FileSystemTraversal<RemoveTreeHelper> traversal(remove_tree_helper, "",
1105
2/4
✓ Branch 2 taken 472 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 472 times.
✗ Branch 6 not taken.
944 true);
1106 472 traversal.fn_new_file = &RemoveTreeHelper::RemoveFile;
1107 472 traversal.fn_new_character_dev = &RemoveTreeHelper::RemoveFile;
1108 472 traversal.fn_new_symlink = &RemoveTreeHelper::RemoveFile;
1109 472 traversal.fn_new_socket = &RemoveTreeHelper::RemoveFile;
1110 472 traversal.fn_new_fifo = &RemoveTreeHelper::RemoveFile;
1111 472 traversal.fn_leave_dir = &RemoveTreeHelper::RemoveDir;
1112 472 traversal.fn_new_dir_prefix = &RemoveTreeHelper::TryRemoveDir;
1113
1/2
✓ Branch 1 taken 472 times.
✗ Branch 2 not taken.
472 traversal.Recurse(path);
1114 472 bool result = remove_tree_helper->success;
1115
1/2
✓ Branch 0 taken 472 times.
✗ Branch 1 not taken.
472 delete remove_tree_helper;
1116
1117 472 return result;
1118 472 }
1119
1120
1121 /**
1122 * Returns ls $dir/GLOB$suffix
1123 */
1124 14 std::vector<std::string> FindFilesBySuffix(
1125 const std::string &dir,
1126 const std::string &suffix)
1127 {
1128 14 std::vector<std::string> result;
1129
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 DIR *dirp = opendir(dir.c_str());
1130
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
14 if (!dirp)
1131 9 return result;
1132
1133 platform_dirent64 *dirent;
1134
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))) {
1135
1/2
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
26 const std::string name(dirent->d_name);
1136
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()) &&
1137
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))
1138 {
1139
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);
1140 }
1141 26 }
1142
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 closedir(dirp);
1143
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 std::sort(result.begin(), result.end());
1144 5 return result;
1145 }
1146
1147
1148 /**
1149 * Returns ls $dir/$prefixGLOB
1150 */
1151 5 std::vector<std::string> FindFilesByPrefix(
1152 const std::string &dir,
1153 const std::string &prefix)
1154 {
1155 5 std::vector<std::string> result;
1156
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 DIR *dirp = opendir(dir.c_str());
1157
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (!dirp)
1158 1 return result;
1159
1160 platform_dirent64 *dirent;
1161
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))) {
1162
1/2
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 const std::string name(dirent->d_name);
1163
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()) &&
1164
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))
1165 {
1166
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);
1167 }
1168 24 }
1169
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 closedir(dirp);
1170
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 std::sort(result.begin(), result.end());
1171 4 return result;
1172 }
1173
1174
1175 /**
1176 * Finds all direct subdirectories under parent_dir (except ., ..). Used,
1177 * for instance, to parse /etc/cvmfs/repositories.d/<reponoame>
1178 */
1179 4 std::vector<std::string> FindDirectories(const std::string &parent_dir) {
1180 4 std::vector<std::string> result;
1181
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 DIR *dirp = opendir(parent_dir.c_str());
1182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!dirp)
1183 return result;
1184
1185 platform_dirent64 *dirent;
1186
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))) {
1187
1/2
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 const std::string name(dirent->d_name);
1188
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 == ".."))
1189 8 continue;
1190
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;
1191
1192 platform_stat64 info;
1193 9 int retval = platform_stat(path.c_str(), &info);
1194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (retval != 0)
1195 continue;
1196
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
9 if (S_ISDIR(info.st_mode))
1197
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 result.push_back(path);
1198
3/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 8 times.
17 }
1199
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 closedir(dirp);
1200
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 sort(result.begin(), result.end());
1201 4 return result;
1202 }
1203
1204
1205 /**
1206 * Finds all files and direct subdirectories under directory (except ., ..).
1207 */
1208 9 bool ListDirectory(const std::string &directory,
1209 std::vector<std::string> *names,
1210 std::vector<mode_t> *modes)
1211 {
1212 9 DIR *dirp = opendir(directory.c_str());
1213
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 if (!dirp)
1214 2 return false;
1215
1216 platform_dirent64 *dirent;
1217
2/2
✓ Branch 1 taken 107 times.
✓ Branch 2 taken 7 times.
114 while ((dirent = platform_readdir(dirp))) {
1218
1/2
✓ Branch 2 taken 107 times.
✗ Branch 3 not taken.
107 const std::string name(dirent->d_name);
1219
6/6
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 7 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 93 times.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 93 times.
107 if ((name == ".") || (name == ".."))
1220 14 continue;
1221
2/4
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 93 times.
✗ Branch 5 not taken.
93 const std::string path = directory + "/" + name;
1222
1223 platform_stat64 info;
1224 93 int retval = platform_lstat(path.c_str(), &info);
1225
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (retval != 0) {
1226 closedir(dirp);
1227 return false;
1228 }
1229
1230
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 names->push_back(name);
1231
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 modes->push_back(info.st_mode);
1232
3/5
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 93 times.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
107 }
1233 7 closedir(dirp);
1234
1235 7 SortTeam(names, modes);
1236 7 return true;
1237 }
1238
1239
1240 /**
1241 * Looks whether exe is an executable file. If exe is not an absolute path,
1242 * searches the PATH environment.
1243 */
1244 3 std::string FindExecutable(const std::string &exe) {
1245
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (exe.empty())
1246 return "";
1247
1248 3 std::vector<std::string> search_paths;
1249
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if (exe[0] == '/') {
1250
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));
1251 } else {
1252 2 char *path_env = getenv("PATH");
1253
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (path_env) {
1254
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, ':');
1255 }
1256 }
1257
1258
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
12 for (unsigned i = 0; i < search_paths.size(); ++i) {
1259
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 if (search_paths[i].empty())
1260 9 continue;
1261
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] != '/')
1262 continue;
1263
1264
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);
1265 platform_stat64 info;
1266 11 int retval = platform_stat(path.c_str(), &info);
1267
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 if (retval != 0)
1268 9 continue;
1269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!S_ISREG(info.st_mode))
1270 continue;
1271 2 retval = access(path.c_str(), X_OK);
1272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (retval != 0)
1273 continue;
1274
1275 2 return path;
1276
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
11 }
1277
1278
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return "";
1279 3 }
1280
1281
1282 1 std::string GetUserName() {
1283 struct passwd pwd;
1284 1 struct passwd *result = NULL;
1285 1 int bufsize = 16 * 1024;
1286 1 char *buf = static_cast<char *>(smalloc(bufsize));
1287
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) {
1288 bufsize *= 2;
1289 buf = static_cast<char *>(srealloc(buf, bufsize));
1290 }
1291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (result == NULL) {
1292 free(buf);
1293 return "";
1294 }
1295
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::string user_name = pwd.pw_name;
1296 1 free(buf);
1297 1 return user_name;
1298 1 }
1299
1300 std::string GetShell() {
1301 struct passwd pwd;
1302 struct passwd *result = NULL;
1303 int bufsize = 16 * 1024;
1304 char *buf = static_cast<char *>(smalloc(bufsize));
1305 while (getpwuid_r(geteuid(), &pwd, buf, bufsize, &result) == ERANGE) {
1306 bufsize *= 2;
1307 buf = static_cast<char *>(srealloc(buf, bufsize));
1308 }
1309 if (result == NULL) {
1310 free(buf);
1311 return "";
1312 }
1313 std::string shell = pwd.pw_shell;
1314 free(buf);
1315 return shell;
1316 }
1317
1318 /**
1319 * UID -> Name from passwd database
1320 */
1321 1 bool GetUserNameOf(uid_t uid, std::string *username) {
1322 struct passwd pwd;
1323 1 struct passwd *result = NULL;
1324 1 int bufsize = 16 * 1024;
1325 1 char *buf = static_cast<char *>(smalloc(bufsize));
1326
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) {
1327 bufsize *= 2;
1328 buf = static_cast<char *>(srealloc(buf, bufsize));
1329 }
1330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (result == NULL) {
1331 free(buf);
1332 return false;
1333 }
1334
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (username)
1335
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 *username = result->pw_name;
1336 1 free(buf);
1337 1 return true;
1338 }
1339
1340
1341 /**
1342 * Name -> UID from passwd database
1343 */
1344 2 bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid) {
1345 struct passwd pwd;
1346 2 struct passwd *result = NULL;
1347 2 int bufsize = 16 * 1024;
1348 2 char *buf = static_cast<char *>(smalloc(bufsize));
1349
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) {
1350 bufsize *= 2;
1351 buf = static_cast<char *>(srealloc(buf, bufsize));
1352 }
1353
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (result == NULL) {
1354 1 free(buf);
1355 1 return false;
1356 }
1357 1 *uid = result->pw_uid;
1358 1 *main_gid = result->pw_gid;
1359 1 free(buf);
1360 1 return true;
1361 }
1362
1363
1364 /**
1365 * Name -> GID from groups database
1366 */
1367 2 bool GetGidOf(const std::string &groupname, gid_t *gid) {
1368 struct group grp;
1369 2 struct group *result = NULL;
1370 2 int bufsize = 16 * 1024;
1371 2 char *buf = static_cast<char *>(smalloc(bufsize));
1372
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) {
1373 bufsize *= 2;
1374 buf = static_cast<char *>(srealloc(buf, bufsize));
1375 }
1376
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (result == NULL) {
1377 1 free(buf);
1378 1 return false;
1379 }
1380 1 *gid = result->gr_gid;
1381 1 free(buf);
1382 1 return true;
1383 }
1384
1385 /**
1386 * read the current umask of this process
1387 * Note: umask query is guarded by a global mutex. Hence, always use
1388 * this function and beware of scalability bottlenecks
1389 */
1390 73 mode_t GetUmask() {
1391 73 MutexLockGuard m(&getumask_mutex);
1392 73 const mode_t my_umask = umask(0);
1393 73 umask(my_umask);
1394 146 return my_umask;
1395 73 }
1396
1397
1398 /**
1399 * Adds gid to the list of supplementary groups
1400 */
1401 bool AddGroup2Persona(const gid_t gid) {
1402 int ngroups = getgroups(0, NULL);
1403 if (ngroups < 0)
1404 return false;
1405 gid_t *groups = static_cast<gid_t *>(smalloc((ngroups+1) * sizeof(gid_t)));
1406 int retval = getgroups(ngroups, groups);
1407 if (retval < 0) {
1408 free(groups);
1409 return false;
1410 }
1411 for (int i = 0; i < ngroups; ++i) {
1412 if (groups[i] == gid) {
1413 free(groups);
1414 return true;
1415 }
1416 }
1417 groups[ngroups] = gid;
1418 retval = setgroups(ngroups+1, groups);
1419 free(groups);
1420 return retval == 0;
1421 }
1422
1423
1424 2 std::string GetHomeDirectory() {
1425 2 uid_t uid = getuid();
1426 struct passwd pwd;
1427 2 struct passwd *result = NULL;
1428 2 int bufsize = 16 * 1024;
1429 2 char *buf = static_cast<char *>(smalloc(bufsize));
1430
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) {
1431 bufsize *= 2;
1432 buf = static_cast<char *>(srealloc(buf, bufsize));
1433 }
1434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (result == NULL) {
1435 free(buf);
1436 return "";
1437 }
1438
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 std::string home_dir = result->pw_dir;
1439 2 free(buf);
1440 2 return home_dir;
1441 2 }
1442
1443 /**
1444 * Returns the output of `uname -m`
1445 */
1446 155 std::string GetArch() {
1447 struct utsname info;
1448 155 int retval = uname(&info);
1449
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
155 assert(retval == 0);
1450
1/2
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
155 return info.machine;
1451 }
1452
1453
1454 /**
1455 * Sets soft and hard limit for maximum number of open file descriptors.
1456 * Returns 0 on success, -1 on failure, -2 if running under valgrind.
1457 */
1458 11 int SetLimitNoFile(unsigned limit_nofile) {
1459 struct rlimit rpl;
1460 11 memset(&rpl, 0, sizeof(rpl));
1461 11 getrlimit(RLIMIT_NOFILE, &rpl);
1462
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
11 if (rpl.rlim_max < limit_nofile)
1463 6 rpl.rlim_max = limit_nofile;
1464 11 rpl.rlim_cur = limit_nofile;
1465 11 int retval = setrlimit(RLIMIT_NOFILE, &rpl);
1466
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if (retval == 0)
1467 5 return 0;
1468
1469 #ifdef HAS_VALGRIND_HEADERS
1470
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 return RUNNING_ON_VALGRIND ? -2 : -1;
1471 #else
1472 return -1;
1473 #endif
1474 }
1475
1476
1477 /**
1478 * Get the file descriptor limits
1479 */
1480 24 void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit) {
1481 24 *soft_limit = 0;
1482 24 *hard_limit = 0;
1483
1484 struct rlimit rpl;
1485 24 memset(&rpl, 0, sizeof(rpl));
1486 24 getrlimit(RLIMIT_NOFILE, &rpl);
1487 24 *soft_limit = rpl.rlim_cur;
1488
1489 #ifdef __APPLE__
1490 int value = sysconf(_SC_OPEN_MAX);
1491 assert(value > 0);
1492 *hard_limit = value;
1493 #else
1494 24 *hard_limit = rpl.rlim_max;
1495 #endif
1496 24 }
1497
1498
1499 1 std::vector<LsofEntry> Lsof(const std::string &path) {
1500 1 std::vector<LsofEntry> result;
1501
1502 1 std::vector<std::string> proc_names;
1503 1 std::vector<mode_t> proc_modes;
1504
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);
1505
1506
2/2
✓ Branch 1 taken 63 times.
✓ Branch 2 taken 1 times.
64 for (unsigned i = 0; i < proc_names.size(); ++i) {
1507
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 14 times.
63 if (!S_ISDIR(proc_modes[i]))
1508 58 continue;
1509
2/2
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 5 times.
14 if (proc_names[i].find_first_not_of("1234567890") != std::string::npos)
1510 9 continue;
1511
1512 5 std::vector<std::string> fd_names;
1513 5 std::vector<mode_t> fd_modes;
1514
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 std::string proc_dir = "/proc/" + proc_names[i];
1515
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 std::string fd_dir = proc_dir + "/fd";
1516
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 bool rvb = ListDirectory(fd_dir, &fd_names, &fd_modes);
1517 5 uid_t proc_uid = 0;
1518
1519 // The working directory of the process requires special handling
1520
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (rvb) {
1521 platform_stat64 info;
1522 4 platform_stat(proc_dir.c_str(), &info);
1523 4 proc_uid = info.st_uid;
1524
1525
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");
1526
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 */)) {
1527 1 LsofEntry entry;
1528
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 entry.pid = static_cast<pid_t>(String2Uint64(proc_names[i]));
1529 1 entry.owner = proc_uid;
1530 1 entry.read_only = true; // A bit sloppy but good enough for the moment
1531
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");
1532
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 entry.path = cwd;
1533
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 result.push_back(entry);
1534 1 }
1535 4 }
1536
1537
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 5 times.
31 for (unsigned j = 0; j < fd_names.size(); ++j) {
1538
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if (!S_ISLNK(fd_modes[j]))
1539 24 continue;
1540
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
26 if (fd_names[j].find_first_not_of("1234567890") != std::string::npos)
1541 continue;
1542
1543
3/6
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 26 times.
✗ Branch 9 not taken.
52 std::string target = ReadSymlink(fd_dir + "/" + fd_names[j]);
1544
5/9
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 26 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 24 times.
✓ Branch 12 taken 2 times.
26 if (!HasPrefix(target + "/", path + "/", false /* ignore_case */))
1545 24 continue;
1546
1547 2 LsofEntry entry;
1548
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 entry.pid = static_cast<pid_t>(String2Uint64(proc_names[i]));
1549 2 entry.owner = proc_uid;
1550 2 entry.read_only = !((fd_modes[j] & S_IWUSR) == S_IWUSR);
1551
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 entry.executable = ReadSymlink(proc_dir + "/exe");
1552
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 entry.path = target;
1553
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 result.push_back(entry);
1554
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 24 times.
26 }
1555 5 }
1556
1557 2 return result;
1558 1 }
1559
1560
1561 3 bool ProcessExists(pid_t pid) {
1562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 assert(pid > 0);
1563 3 int retval = kill(pid, 0);
1564
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (retval == 0)
1565 2 return true;
1566 1 return (errno != ESRCH);
1567 }
1568
1569
1570 /**
1571 * Blocks a signal for the calling thread.
1572 */
1573 2 void BlockSignal(int signum) {
1574 sigset_t sigset;
1575 2 int retval = sigemptyset(&sigset);
1576
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(retval == 0);
1577 2 retval = sigaddset(&sigset, signum);
1578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(retval == 0);
1579 2 retval = pthread_sigmask(SIG_BLOCK, &sigset, NULL);
1580
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(retval == 0);
1581 2 }
1582
1583
1584 /**
1585 * Waits for a signal. The signal should be blocked before for all threads.
1586 * Threads inherit their parent's signal mask.
1587 */
1588 1 void WaitForSignal(int signum) {
1589 int retval;
1590 do {
1591 1 retval = platform_sigwait(signum);
1592
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));
1593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 assert(retval == signum);
1594 1 }
1595
1596
1597 /**
1598 * Returns -1 if the child crashed or the exit code otherwise.
1599 * @param pid Process identifier.
1600 * @param sig_ok List of signals that are still considered a successful termination.
1601 */
1602 7 int WaitForChild(pid_t pid, const std::vector<int> &sig_ok) {
1603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 assert(pid > 0);
1604 int statloc;
1605 while (true) {
1606
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 pid_t retval = waitpid(pid, &statloc, 0);
1607
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (retval == -1) {
1608
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (errno == EINTR)
1609 continue;
1610 1 PANIC(kLogSyslogErr | kLogDebug,
1611 "waitpid failed with errno %d", errno);
1612 }
1613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 assert(retval == pid);
1614 6 break;
1615 }
1616
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (WIFEXITED(statloc))
1617 5 return WEXITSTATUS(statloc);
1618
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(),
1619
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
3 WTERMSIG(statloc)) != sig_ok.end()))
1620 return 0;
1621 1 return -1;
1622 }
1623
1624
1625 /**
1626 * Makes a daemon. The daemon() call is deprecated on OS X
1627 */
1628 void Daemonize() {
1629 pid_t pid;
1630 int statloc;
1631 if ((pid = fork()) == 0) {
1632 int retval = setsid();
1633 assert(retval != -1);
1634 if ((pid = fork()) == 0) {
1635
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 int null_read = open("/dev/null", O_RDONLY);
1636
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 int null_write = open("/dev/null", O_WRONLY);
1637
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));
1638 48 retval = dup2(null_read, 0);
1639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 assert(retval == 0);
1640 48 retval = dup2(null_write, 1);
1641
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 assert(retval == 1);
1642 48 retval = dup2(null_write, 2);
1643
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 assert(retval == 2);
1644
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 close(null_read);
1645
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 close(null_write);
1646
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 LogCvmfs(kLogCvmfs, kLogDebug, "daemonized");
1647 } else {
1648 assert(pid > 0);
1649 _exit(0);
1650 }
1651 } else {
1652 assert(pid > 0);
1653 waitpid(pid, &statloc, 0);
1654 _exit(0);
1655 }
1656 48 }
1657
1658
1659 26 bool ExecuteBinary(
1660 int *fd_stdin,
1661 int *fd_stdout,
1662 int *fd_stderr,
1663 const std::string &binary_path,
1664 const std::vector<std::string> &argv,
1665 const bool double_fork,
1666 pid_t *child_pid
1667 ) {
1668 int pipe_stdin[2];
1669 int pipe_stdout[2];
1670 int pipe_stderr[2];
1671
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 MakePipe(pipe_stdin);
1672
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 MakePipe(pipe_stdout);
1673
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 MakePipe(pipe_stderr);
1674
1675 26 std::set<int> preserve_fildes;
1676
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 preserve_fildes.insert(0);
1677
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 preserve_fildes.insert(1);
1678
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 preserve_fildes.insert(2);
1679 26 std::map<int, int> map_fildes;
1680
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 map_fildes[pipe_stdin[0]] = 0; // Reading end of pipe_stdin
1681
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 map_fildes[pipe_stdout[1]] = 1; // Writing end of pipe_stdout
1682
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 map_fildes[pipe_stderr[1]] = 2; // Writing end of pipe_stderr
1683 26 std::vector<std::string> cmd_line;
1684
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 cmd_line.push_back(binary_path);
1685
1/2
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
26 cmd_line.insert(cmd_line.end(), argv.begin(), argv.end());
1686
1687
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,
1688 preserve_fildes,
1689 map_fildes,
1690 true /* drop_credentials */,
1691 false /* clear_env */,
1692 double_fork,
1693 child_pid))
1694 {
1695 ClosePipe(pipe_stdin);
1696 ClosePipe(pipe_stdout);
1697 ClosePipe(pipe_stderr);
1698 return false;
1699 }
1700
1701
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 close(pipe_stdin[0]);
1702
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 close(pipe_stdout[1]);
1703
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 close(pipe_stderr[1]);
1704 26 *fd_stdin = pipe_stdin[1];
1705 26 *fd_stdout = pipe_stdout[0];
1706 26 *fd_stderr = pipe_stderr[0];
1707 26 return true;
1708 26 }
1709
1710
1711 /**
1712 * Opens /bin/sh and provides file descriptors to write into stdin and
1713 * read from stdout. Quit shell simply by closing stderr, stdout, and stdin.
1714 */
1715 23 bool Shell(int *fd_stdin, int *fd_stdout, int *fd_stderr) {
1716 23 const bool double_fork = true;
1717
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",
1718 69 std::vector<std::string>(), double_fork);
1719 }
1720
1721 struct ForkFailures { // TODO(rmeusel): C++11 (type safe enum)
1722 enum Names {
1723 kSendPid,
1724 kUnknown,
1725 kFailDupFd,
1726 kFailCloseFds,
1727 kFailGetFdFlags,
1728 kFailSetFdFlags,
1729 kFailDropCredentials,
1730 kFailExec,
1731 };
1732
1733 static std::string ToString(const Names name) {
1734 switch (name) {
1735 case kSendPid:
1736 return "Sending PID";
1737
1738 default:
1739 case kUnknown:
1740 return "Unknown Status";
1741 case kFailDupFd:
1742 return "Duplicate File Descriptor";
1743 case kFailCloseFds:
1744 return "Close File Descriptors";
1745 case kFailGetFdFlags:
1746 return "Read File Descriptor Flags";
1747 case kFailSetFdFlags:
1748 return "Set File Descriptor Flags";
1749 case kFailDropCredentials:
1750 return "Lower User Permissions";
1751 case kFailExec:
1752 return "Invoking execvp()";
1753 }
1754 }
1755 };
1756
1757 /**
1758 * Loop through all possible FDs and close them.
1759 */
1760 13 static bool CloseAllFildesUntilMaxFD(
1761 const std::set<int> &preserve_fildes,
1762 int max_fd
1763 ) {
1764
2/2
✓ Branch 0 taken 6500 times.
✓ Branch 1 taken 13 times.
6513 for (int fd = 0; fd < max_fd; fd++) {
1765
3/4
✓ Branch 1 taken 6500 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6435 times.
✓ Branch 4 taken 65 times.
6500 if (preserve_fildes.count(fd) == 0) {
1766
1/2
✓ Branch 1 taken 6435 times.
✗ Branch 2 not taken.
6435 close(fd);
1767 }
1768 }
1769
1770 13 return true;
1771 }
1772
1773 /**
1774 * Loop through /proc/self/fd and close the listed FDs.
1775 * Not used on macOS.
1776 */
1777 #ifndef __APPLE__
1778 37 static bool CloseAllFildesInProcSelfFd(const std::set<int> &preserve_fildes)
1779 {
1780 37 DIR *dirp = opendir("/proc/self/fd");
1781
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (!dirp)
1782 return false;
1783
1784 platform_dirent64 *dirent;
1785
1786
2/2
✓ Branch 1 taken 649 times.
✓ Branch 2 taken 37 times.
686 while ((dirent = platform_readdir(dirp))) {
1787
1/2
✓ Branch 2 taken 649 times.
✗ Branch 3 not taken.
649 const std::string name(dirent->d_name);
1788 uint64_t name_uint64;
1789
1790 // Make sure the dir name is digits only (skips ".", ".." and similar).
1791
3/4
✓ Branch 1 taken 649 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 74 times.
✓ Branch 4 taken 575 times.
649 if (!String2Uint64Parse(name, &name_uint64)) {
1792 74 continue;
1793 }
1794
1795 575 int fd = static_cast<int>(name_uint64);
1796
3/4
✓ Branch 1 taken 575 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 183 times.
✓ Branch 4 taken 392 times.
575 if (preserve_fildes.count(fd)) {
1797 183 continue;
1798 }
1799
1800
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 close(fd);
1801
2/2
✓ Branch 1 taken 392 times.
✓ Branch 2 taken 257 times.
649 }
1802
1803 37 closedir(dirp);
1804
1805 37 return true;
1806 }
1807 #endif
1808
1809 /**
1810 * Closes all file descriptors except the ones in preserve_fildes.
1811 * To be used after fork but before exec.
1812 */
1813 50 bool CloseAllFildes(const std::set<int> &preserve_fildes)
1814 {
1815 50 int max_fd = static_cast<int>(sysconf(_SC_OPEN_MAX));
1816
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (max_fd < 0) {
1817 return false;
1818 }
1819
1820 #ifdef __APPLE__
1821 return CloseAllFildesUntilMaxFD(preserve_fildes, max_fd);
1822 #else // ifdef __APPLE__
1823
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 13 times.
50 if (max_fd > 100000) {
1824 // CloseAllFildesUntilMaxFD is inefficient with very large max_fd.
1825 // Looping through /proc/self/fd performs better.
1826 37 return CloseAllFildesInProcSelfFd(preserve_fildes);
1827 }
1828
1829 13 return CloseAllFildesUntilMaxFD(preserve_fildes, max_fd);
1830 #endif // #ifdef __APPLE__
1831 }
1832
1833 /**
1834 * Execve to the given command line, preserving the given file descriptors.
1835 * If stdin, stdout, stderr should be preserved, add 0, 1, 2.
1836 * File descriptors from the parent process can also be mapped to the new
1837 * process (dup2) using map_fildes. Can be useful for
1838 * stdout/in/err redirection.
1839 * NOTE: The destination fildes have to be preserved!
1840 * Does a double fork to detach child.
1841 * The command_line parameter contains the binary at index 0 and the arguments
1842 * in the rest of the vector.
1843 * Using the optional parameter *pid it is possible to retrieve the process ID
1844 * of the spawned process.
1845 */
1846 30 bool ManagedExec(const std::vector<std::string> &command_line,
1847 const std::set<int> &preserve_fildes,
1848 const std::map<int, int> &map_fildes,
1849 const bool drop_credentials,
1850 const bool clear_env,
1851 const bool double_fork,
1852 pid_t *child_pid)
1853 {
1854
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 assert(command_line.size() >= 1);
1855
1856
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 Pipe<kPipeDetachedChild> pipe_fork;
1857 30 pid_t pid = fork();
1858
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 assert(pid >= 0);
1859
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 30 times.
32 if (pid == 0) {
1860 pid_t pid_grand_child;
1861 int fd_flags;
1862 2 ForkFailures::Names failed = ForkFailures::kUnknown;
1863
1864 2 std::set<int> skip_fds = preserve_fildes;
1865
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 skip_fds.insert(pipe_fork.GetWriteFd());
1866
1867
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (clear_env) {
1868 #ifdef __APPLE__
1869 environ = NULL;
1870 #else
1871 int retval = clearenv();
1872 assert(retval == 0);
1873 #endif
1874 }
1875
1876 2 const char *argv[command_line.size() + 1];
1877
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
5 for (unsigned i = 0; i < command_line.size(); ++i)
1878 3 argv[i] = command_line[i].c_str();
1879 2 argv[command_line.size()] = NULL;
1880
1881 // Child, map file descriptors
1882 4 for (std::map<int, int>::const_iterator i = map_fildes.begin(),
1883
2/2
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 2 times.
10 iEnd = map_fildes.end(); i != iEnd; ++i)
1884 {
1885 6 int retval = dup2(i->first, i->second);
1886
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (retval == -1) {
1887 failed = ForkFailures::kFailDupFd;
1888 goto fork_failure;
1889 }
1890 }
1891
1892 // Child, close file descriptors
1893
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)) {
1894 failed = ForkFailures::kFailCloseFds;
1895 goto fork_failure;
1896 }
1897
1898 // Double fork to disconnect from parent
1899
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (double_fork) {
1900 pid_grand_child = fork();
1901
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 assert(pid_grand_child >= 0);
1902
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (pid_grand_child != 0) _exit(0);
1903 }
1904
1905
1/2
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
30 fd_flags = fcntl(pipe_fork.GetWriteFd(), F_GETFD);
1906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (fd_flags < 0) {
1907 failed = ForkFailures::kFailGetFdFlags;
1908 goto fork_failure;
1909 }
1910 30 fd_flags |= FD_CLOEXEC;
1911
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) {
1912 failed = ForkFailures::kFailSetFdFlags;
1913 goto fork_failure;
1914 }
1915
1916 #ifdef DEBUGMSG
1917
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 assert(setenv("__CVMFS_DEBUG_MODE__", "yes", 1) == 0);
1918 #endif
1919
5/8
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2 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 30 times.
30 if (drop_credentials && !SwitchCredentials(geteuid(), getegid(), false)) {
1920 failed = ForkFailures::kFailDropCredentials;
1921 goto fork_failure;
1922 }
1923
1924 // retrieve the PID of the new (grand) child process and send it to the
1925 // grand father
1926 30 pid_grand_child = getpid();
1927 30 failed = ForkFailures::kSendPid;
1928
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 pipe_fork.Write<ForkFailures::Names>(failed);
1929
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 pipe_fork.Write<pid_t>(pid_grand_child);
1930
1931 30 execvp(command_line[0].c_str(), const_cast<char **>(argv));
1932
1933 failed = ForkFailures::kFailExec;
1934
1935 fork_failure:
1936 pipe_fork.Write<ForkFailures::Names>(failed);
1937 _exit(1);
1938
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 }
1939
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2 times.
30 if (double_fork) {
1940 int statloc;
1941
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 waitpid(pid, &statloc, 0);
1942 }
1943
1944
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 pipe_fork.CloseWriteFd();
1945
1946 // Either the PID or a return value is sent
1947 ForkFailures::Names status_code;
1948
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 pipe_fork.Read<ForkFailures::Names>(&status_code);
1949
1950
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (status_code != ForkFailures::kSendPid) {
1951 pipe_fork.CloseReadFd();
1952 LogCvmfs(kLogCvmfs, kLogDebug, "managed execve failed (%s)",
1953 ForkFailures::ToString(status_code).c_str());
1954 return false;
1955 }
1956
1957 // read the PID of the spawned process if requested
1958 // (the actual read needs to be done in any case!)
1959 30 pid_t buf_child_pid = 0;
1960
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 pipe_fork.Read(&buf_child_pid);
1961
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 25 times.
30 if (child_pid != NULL)
1962 5 *child_pid = buf_child_pid;
1963
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 pipe_fork.CloseReadFd();
1964
1/2
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
30 LogCvmfs(kLogCvmfs, kLogDebug, "execve'd %s (PID: %d)",
1965 30 command_line[0].c_str(),
1966 static_cast<int>(buf_child_pid));
1967 30 return true;
1968 30 }
1969
1970
1971 /**
1972 * Sleeps using select. This is without signals and doesn't interfere with
1973 * other uses of the ALRM signal.
1974 */
1975 62 void SafeSleepMs(const unsigned ms) {
1976 struct timeval wait_for;
1977 62 wait_for.tv_sec = ms / 1000;
1978 62 wait_for.tv_usec = (ms % 1000) * 1000;
1979
2/2
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 1 times.
62 select(0, NULL, NULL, NULL, &wait_for);
1980 61 }
1981
1982
1983 /**
1984 * Deal with EINTR and partial writes.
1985 */
1986 221 bool SafeWrite(int fd, const void *buf, size_t nbyte) {
1987
2/2
✓ Branch 0 taken 221 times.
✓ Branch 1 taken 217 times.
438 while (nbyte) {
1988 221 ssize_t retval = write(fd, buf, nbyte);
1989
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 217 times.
221 if (retval < 0) {
1990
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (errno == EINTR)
1991 continue;
1992 4 return false;
1993 }
1994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
217 assert(static_cast<size_t>(retval) <= nbyte);
1995 217 buf = reinterpret_cast<const char *>(buf) + retval;
1996 217 nbyte -= retval;
1997 }
1998 217 return true;
1999 }
2000
2001 /**
2002 * The contents of the iov vector might be modified by the function.
2003 */
2004 12635 bool SafeWriteV(int fd, struct iovec *iov, unsigned iovcnt) {
2005 12635 unsigned nbytes = 0;
2006
2/2
✓ Branch 0 taken 35729 times.
✓ Branch 1 taken 12635 times.
48364 for (unsigned i = 0; i < iovcnt; ++i)
2007 35729 nbytes += iov[i].iov_len;
2008 12635 unsigned iov_idx = 0;
2009
2010
1/2
✓ Branch 0 taken 12635 times.
✗ Branch 1 not taken.
12635 while (nbytes) {
2011 ssize_t retval =
2012 12635 writev(fd, &iov[iov_idx], static_cast<int>(iovcnt - iov_idx));
2013
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12635 times.
12635 if (retval < 0) {
2014 if (errno == EINTR)
2015 continue;
2016 return false;
2017 }
2018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12635 times.
12635 assert(static_cast<size_t>(retval) <= nbytes);
2019 12635 nbytes -= retval;
2020
2021 12635 unsigned sum_written_blocks = 0;
2022 12635 while ((sum_written_blocks + iov[iov_idx].iov_len) <=
2023
1/2
✓ Branch 0 taken 35729 times.
✗ Branch 1 not taken.
35729 static_cast<size_t>(retval))
2024 {
2025 35729 sum_written_blocks += iov[iov_idx].iov_len;
2026 35729 iov_idx++;
2027
2/2
✓ Branch 0 taken 12635 times.
✓ Branch 1 taken 23094 times.
35729 if (iov_idx == iovcnt) {
2028
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12635 times.
12635 assert(sum_written_blocks == static_cast<size_t>(retval));
2029 12635 return true;
2030 }
2031 }
2032 unsigned offset = retval - sum_written_blocks;
2033 iov[iov_idx].iov_len -= offset;
2034 iov[iov_idx].iov_base =
2035 reinterpret_cast<char *>(iov[iov_idx].iov_base) + offset;
2036 }
2037
2038 return true;
2039 }
2040
2041
2042 /**
2043 * Deal with EINTR and partial reads.
2044 */
2045 480478 ssize_t SafeRead(int fd, void *buf, size_t nbyte) {
2046 480478 ssize_t total_bytes = 0;
2047
2/2
✓ Branch 0 taken 491175 times.
✓ Branch 1 taken 480058 times.
971233 while (nbyte) {
2048 491175 ssize_t retval = read(fd, buf, nbyte);
2049
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 491929 times.
491931 if (retval < 0) {
2050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (errno == EINTR)
2051 continue;
2052 2 return -1;
2053
2/2
✓ Branch 0 taken 1174 times.
✓ Branch 1 taken 490755 times.
491929 } else if (retval == 0) {
2054 1174 return total_bytes;
2055 }
2056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 490755 times.
490755 assert(static_cast<size_t>(retval) <= nbyte);
2057 490755 buf = reinterpret_cast<char *>(buf) + retval;
2058 490755 nbyte -= retval;
2059 490755 total_bytes += retval;
2060 }
2061 480058 return total_bytes;
2062 }
2063
2064
2065 /**
2066 * Pull file contents into a string
2067 */
2068 37 bool SafeReadToString(int fd, std::string *final_result) {
2069
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (!final_result) {return false;}
2070
2071 37 std::string tmp_result;
2072 static const int buf_size = 4096;
2073 char buf[4096];
2074 37 ssize_t total_bytes = -1;
2075 do {
2076
1/2
✓ Branch 1 taken 397 times.
✗ Branch 2 not taken.
397 total_bytes = SafeRead(fd, buf, buf_size);
2077
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 396 times.
397 if (total_bytes < 0) {return false;}
2078
1/2
✓ Branch 1 taken 396 times.
✗ Branch 2 not taken.
396 tmp_result.append(buf, total_bytes);
2079
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 36 times.
396 } while (total_bytes == buf_size);
2080 36 final_result->swap(tmp_result);
2081 36 return true;
2082 37 }
2083
2084 159 bool SafeWriteToFile(const std::string &content,
2085 const std::string &path,
2086 int mode) {
2087 159 int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
2088
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159 times.
159 if (fd < 0) return false;
2089 159 bool retval = SafeWrite(fd, content.data(), content.size());
2090 159 close(fd);
2091 159 return retval;
2092 }
2093
2094
2095 #ifdef CVMFS_NAMESPACE_GUARD
2096 } // namespace CVMFS_NAMESPACE_GUARD
2097 #endif
2098