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