GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
#define __STDC_FORMAT_MACROS |
||
9 |
#endif |
||
10 |
|||
11 |
#include "cvmfs_config.h" |
||
12 |
#include "posix.h" |
||
13 |
|||
14 |
#include <arpa/inet.h> |
||
15 |
#include <errno.h> |
||
16 |
#include <fcntl.h> |
||
17 |
#include <grp.h> |
||
18 |
#include <inttypes.h> |
||
19 |
#include <netinet/in.h> |
||
20 |
#include <pthread.h> |
||
21 |
#include <pwd.h> |
||
22 |
#include <signal.h> |
||
23 |
#include <stdint.h> |
||
24 |
#include <sys/resource.h> |
||
25 |
#include <sys/socket.h> |
||
26 |
#include <sys/stat.h> |
||
27 |
#include <sys/time.h> |
||
28 |
#include <sys/types.h> |
||
29 |
#include <sys/un.h> |
||
30 |
#include <sys/wait.h> |
||
31 |
#include <unistd.h> |
||
32 |
// If valgrind headers are present on the build system, then we can detect |
||
33 |
// valgrind at runtime. |
||
34 |
#ifdef HAS_VALGRIND_HEADERS |
||
35 |
#include <valgrind/valgrind.h> |
||
36 |
#endif |
||
37 |
|||
38 |
#include <algorithm> |
||
39 |
#include <cassert> |
||
40 |
#include <cstdio> |
||
41 |
#include <cstring> |
||
42 |
#include <map> |
||
43 |
#include <set> |
||
44 |
#include <string> |
||
45 |
#include <vector> |
||
46 |
|||
47 |
#include "fs_traversal.h" |
||
48 |
#include "logging.h" |
||
49 |
#include "platform.h" |
||
50 |
|||
51 |
//using namespace std; // NOLINT |
||
52 |
|||
53 |
#ifdef CVMFS_NAMESPACE_GUARD |
||
54 |
namespace CVMFS_NAMESPACE_GUARD { |
||
55 |
#endif |
||
56 |
|||
57 |
static pthread_mutex_t getumask_mutex = PTHREAD_MUTEX_INITIALIZER; |
||
58 |
|||
59 |
|||
60 |
/** |
||
61 |
* Removes a trailing "/" from a path. |
||
62 |
*/ |
||
63 |
495 |
std::string MakeCanonicalPath(const std::string &path) { |
|
64 |
✓✓ | 495 |
if (path.length() == 0) return path; |
65 |
|||
66 |
✓✓ | 494 |
if (path[path.length()-1] == '/') |
67 |
2 |
return path.substr(0, path.length()-1); |
|
68 |
else |
||
69 |
492 |
return path; |
|
70 |
} |
||
71 |
|||
72 |
|||
73 |
/** |
||
74 |
* Return both the file and directory name for a given path. |
||
75 |
* |
||
76 |
* NOTE: If only a filename is given, the directory is returned as "." |
||
77 |
*/ |
||
78 |
8 |
void SplitPath( |
|
79 |
const std::string &path, |
||
80 |
std::string *dirname, |
||
81 |
std::string *filename) |
||
82 |
{ |
||
83 |
8 |
size_t dir_sep = path.rfind('/'); |
|
84 |
✓✓ | 8 |
if (dir_sep != std::string::npos) { |
85 |
6 |
*dirname = path.substr(0, dir_sep); |
|
86 |
6 |
*filename = path.substr(dir_sep+1); |
|
87 |
} else { |
||
88 |
2 |
*dirname = "."; |
|
89 |
2 |
*filename = path; |
|
90 |
} |
||
91 |
8 |
} |
|
92 |
|||
93 |
|||
94 |
/** |
||
95 |
* Gets the directory part of a path. |
||
96 |
*/ |
||
97 |
657 |
std::string GetParentPath(const std::string &path) { |
|
98 |
657 |
const std::string::size_type idx = path.find_last_of('/'); |
|
99 |
✓✓ | 657 |
if (idx != std::string::npos) |
100 |
652 |
return path.substr(0, idx); |
|
101 |
else |
||
102 |
5 |
return ""; |
|
103 |
} |
||
104 |
|||
105 |
|||
106 |
/** |
||
107 |
* Gets the file name part of a path. |
||
108 |
*/ |
||
109 |
1032 |
PathString GetParentPath(const PathString &path) { |
|
110 |
1032 |
unsigned length = path.GetLength(); |
|
111 |
✓✓ | 1032 |
if (length == 0) |
112 |
1 |
return path; |
|
113 |
1031 |
const char *chars = path.GetChars(); |
|
114 |
|||
115 |
✓✓ | 4059 |
for (int i = length-1; i >= 0; --i) { |
116 |
✓✓ | 4058 |
if (chars[i] == '/') |
117 |
1030 |
return PathString(chars, i); |
|
118 |
} |
||
119 |
|||
120 |
1 |
return path; |
|
121 |
} |
||
122 |
|||
123 |
|||
124 |
/** |
||
125 |
* Gets the file name part of a path. |
||
126 |
*/ |
||
127 |
12095 |
std::string GetFileName(const std::string &path) { |
|
128 |
12095 |
const std::string::size_type idx = path.find_last_of('/'); |
|
129 |
✓✓ | 12095 |
if (idx != std::string::npos) |
130 |
12085 |
return path.substr(idx+1); |
|
131 |
else |
||
132 |
10 |
return path; |
|
133 |
} |
||
134 |
|||
135 |
|||
136 |
3 |
NameString GetFileName(const PathString &path) { |
|
137 |
3 |
NameString name; |
|
138 |
3 |
int length = path.GetLength(); |
|
139 |
3 |
const char *chars = path.GetChars(); |
|
140 |
|||
141 |
int i; |
||
142 |
✓✓ | 13 |
for (i = length-1; i >= 0; --i) { |
143 |
✓✓ | 12 |
if (chars[i] == '/') |
144 |
2 |
break; |
|
145 |
} |
||
146 |
3 |
i++; |
|
147 |
✓✓ | 3 |
if (i < length) { |
148 |
2 |
name.Append(chars+i, length-i); |
|
149 |
} |
||
150 |
|||
151 |
3 |
return name; |
|
152 |
} |
||
153 |
|||
154 |
|||
155 |
8 |
bool IsAbsolutePath(const std::string &path) { |
|
156 |
✓✓✓✓ |
8 |
return (!path.empty() && path[0] == '/'); |
157 |
} |
||
158 |
|||
159 |
|||
160 |
5 |
std::string GetAbsolutePath(const std::string &path) { |
|
161 |
✓✓ | 5 |
if (IsAbsolutePath(path)) |
162 |
1 |
return path; |
|
163 |
|||
164 |
4 |
return GetCurrentWorkingDirectory() + "/" + path; |
|
165 |
} |
||
166 |
|||
167 |
|||
168 |
10 |
bool IsHttpUrl(const std::string &path) { |
|
169 |
✓✓ | 10 |
if (path.length() < 7) { |
170 |
1 |
return false; |
|
171 |
} |
||
172 |
|||
173 |
9 |
std::string prefix = path.substr(0, 7); |
|
174 |
9 |
std::transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower); |
|
175 |
|||
176 |
9 |
return prefix == "http://"; |
|
177 |
} |
||
178 |
|||
179 |
|||
180 |
/** |
||
181 |
* By default abort() on failure |
||
182 |
*/ |
||
183 |
118 |
void CreateFile( |
|
184 |
const std::string &path, |
||
185 |
const int mode, |
||
186 |
const bool ignore_failure) |
||
187 |
{ |
||
188 |
118 |
int fd = open(path.c_str(), O_CREAT, mode); |
|
189 |
✓✗ | 118 |
if (fd >= 0) { |
190 |
118 |
close(fd); |
|
191 |
118 |
return; |
|
192 |
} |
||
193 |
if (ignore_failure) |
||
194 |
return; |
||
195 |
assert(false); |
||
196 |
} |
||
197 |
|||
198 |
|||
199 |
/** |
||
200 |
* Symlinks /tmp/cvmfs.XYZ/l --> ParentPath(path) to make it shorter |
||
201 |
*/ |
||
202 |
5 |
static std::string MakeShortSocketLink(const std::string &path) { |
|
203 |
struct sockaddr_un sock_addr; |
||
204 |
5 |
unsigned max_length = sizeof(sock_addr.sun_path); |
|
205 |
|||
206 |
5 |
std::string result; |
|
207 |
5 |
std::string tmp_path = CreateTempDir("/tmp/cvmfs"); |
|
208 |
✗✓ | 5 |
if (tmp_path.empty()) |
209 |
return ""; |
||
210 |
5 |
std::string link = tmp_path + "/l"; |
|
211 |
5 |
result = link + "/" + GetFileName(path); |
|
212 |
✓✓ | 5 |
if (result.length() >= max_length) { |
213 |
2 |
rmdir(tmp_path.c_str()); |
|
214 |
2 |
return ""; |
|
215 |
} |
||
216 |
3 |
int retval = symlink(GetParentPath(path).c_str(), link.c_str()); |
|
217 |
✗✓ | 3 |
if (retval != 0) { |
218 |
rmdir(tmp_path.c_str()); |
||
219 |
return ""; |
||
220 |
} |
||
221 |
3 |
return result; |
|
222 |
} |
||
223 |
|||
224 |
3 |
static void RemoveShortSocketLink(const std::string &short_path) { |
|
225 |
3 |
std::string link = GetParentPath(short_path); |
|
226 |
3 |
unlink(link.c_str()); |
|
227 |
3 |
rmdir(GetParentPath(link).c_str()); |
|
228 |
3 |
} |
|
229 |
|||
230 |
|||
231 |
/** |
||
232 |
* Creates and binds to a named socket. |
||
233 |
*/ |
||
234 |
65 |
int MakeSocket(const std::string &path, const int mode) { |
|
235 |
65 |
std::string short_path(path); |
|
236 |
struct sockaddr_un sock_addr; |
||
237 |
✓✓ | 65 |
if (path.length() >= sizeof(sock_addr.sun_path)) { |
238 |
// Socket paths are limited to 108 bytes (on some systems to 92 bytes), |
||
239 |
// try working around |
||
240 |
3 |
short_path = MakeShortSocketLink(path); |
|
241 |
✓✓ | 3 |
if (short_path.empty()) |
242 |
1 |
return -1; |
|
243 |
} |
||
244 |
64 |
sock_addr.sun_family = AF_UNIX; |
|
245 |
strncpy(sock_addr.sun_path, short_path.c_str(), |
||
246 |
64 |
sizeof(sock_addr.sun_path)); |
|
247 |
|||
248 |
64 |
const int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); |
|
249 |
✗✓ | 64 |
assert(socket_fd != -1); |
250 |
|||
251 |
#ifndef __APPLE__ |
||
252 |
// fchmod on a socket is not allowed under Mac OS X |
||
253 |
// using default 0770 here |
||
254 |
✗✓ | 64 |
if (fchmod(socket_fd, mode) != 0) |
255 |
goto make_socket_failure; |
||
256 |
#endif |
||
257 |
|||
258 |
✓✓ | 64 |
if (bind(socket_fd, (struct sockaddr *)&sock_addr, |
259 |
sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path)) < 0) |
||
260 |
{ |
||
261 |
✓✗✓✗ ✓✗ |
1 |
if ((errno == EADDRINUSE) && (unlink(path.c_str()) == 0)) { |
262 |
// Second try, perhaps the file was left over |
||
263 |
✗✓ | 1 |
if (bind(socket_fd, (struct sockaddr *)&sock_addr, |
264 |
sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path)) < 0) |
||
265 |
{ |
||
266 |
LogCvmfs(kLogCvmfs, kLogDebug, "binding socket failed (%d)", errno); |
||
267 |
goto make_socket_failure; |
||
268 |
} |
||
269 |
} else { |
||
270 |
LogCvmfs(kLogCvmfs, kLogDebug, "binding socket failed (%d)", errno); |
||
271 |
goto make_socket_failure; |
||
272 |
} |
||
273 |
} |
||
274 |
|||
275 |
✓✓ | 64 |
if (short_path != path) |
276 |
2 |
RemoveShortSocketLink(short_path); |
|
277 |
|||
278 |
64 |
return socket_fd; |
|
279 |
|||
280 |
make_socket_failure: |
||
281 |
close(socket_fd); |
||
282 |
if (short_path != path) |
||
283 |
RemoveShortSocketLink(short_path); |
||
284 |
return -1; |
||
285 |
} |
||
286 |
|||
287 |
|||
288 |
/** |
||
289 |
* Creates and binds a TCP/IPv4 socket. An empty address binds to the "any" |
||
290 |
* address. |
||
291 |
*/ |
||
292 |
4 |
int MakeTcpEndpoint(const std::string &ipv4_address, int portno) { |
|
293 |
4 |
const int socket_fd = socket(AF_INET, SOCK_STREAM, 0); |
|
294 |
✗✓ | 4 |
assert(socket_fd != -1); |
295 |
4 |
const int on = 1; |
|
296 |
4 |
int retval = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
|
297 |
✗✓ | 4 |
assert(retval == 0); |
298 |
|||
299 |
struct sockaddr_in endpoint_addr; |
||
300 |
4 |
memset(&endpoint_addr, 0, sizeof(endpoint_addr)); |
|
301 |
4 |
endpoint_addr.sin_family = AF_INET; |
|
302 |
✓✓ | 4 |
if (ipv4_address.empty()) { |
303 |
1 |
endpoint_addr.sin_addr.s_addr = INADDR_ANY; |
|
304 |
} else { |
||
305 |
3 |
retval = inet_aton(ipv4_address.c_str(), &(endpoint_addr.sin_addr)); |
|
306 |
✓✓ | 3 |
if (retval == 0) { |
307 |
1 |
LogCvmfs(kLogCvmfs, kLogDebug, "invalid IPv4 address"); |
|
308 |
1 |
close(socket_fd); |
|
309 |
1 |
return -1; |
|
310 |
} |
||
311 |
} |
||
312 |
3 |
endpoint_addr.sin_port = htons(portno); |
|
313 |
|||
314 |
retval = bind(socket_fd, (struct sockaddr *)&endpoint_addr, |
||
315 |
3 |
sizeof(endpoint_addr)); |
|
316 |
✓✓ | 3 |
if (retval < 0) { |
317 |
1 |
LogCvmfs(kLogCvmfs, kLogDebug, "binding TCP endpoint failed (%d)", errno); |
|
318 |
1 |
close(socket_fd); |
|
319 |
1 |
return -1; |
|
320 |
} |
||
321 |
2 |
return socket_fd; |
|
322 |
} |
||
323 |
|||
324 |
|||
325 |
/** |
||
326 |
* Connects to a named socket. |
||
327 |
* |
||
328 |
* \return socket file descriptor on success, -1 else |
||
329 |
*/ |
||
330 |
22 |
int ConnectSocket(const std::string &path) { |
|
331 |
22 |
std::string short_path(path); |
|
332 |
struct sockaddr_un sock_addr; |
||
333 |
✓✓ | 22 |
if (path.length() >= sizeof(sock_addr.sun_path)) { |
334 |
// Socket paths are limited to 108 bytes (on some systems to 92 bytes), |
||
335 |
// try working around |
||
336 |
2 |
short_path = MakeShortSocketLink(path); |
|
337 |
✓✓ | 2 |
if (short_path.empty()) |
338 |
1 |
return -1; |
|
339 |
} |
||
340 |
21 |
sock_addr.sun_family = AF_UNIX; |
|
341 |
21 |
strncpy(sock_addr.sun_path, short_path.c_str(), sizeof(sock_addr.sun_path)); |
|
342 |
|||
343 |
21 |
const int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); |
|
344 |
✗✓ | 21 |
assert(socket_fd != -1); |
345 |
|||
346 |
int retval = |
||
347 |
connect(socket_fd, (struct sockaddr *)&sock_addr, |
||
348 |
21 |
sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path)); |
|
349 |
✓✓ | 21 |
if (short_path != path) |
350 |
1 |
RemoveShortSocketLink(short_path); |
|
351 |
|||
352 |
✓✓ | 21 |
if (retval < 0) { |
353 |
// LogCvmfs(kLogCvmfs, kLogStderr, "ERROR %d", errno); |
||
354 |
1 |
close(socket_fd); |
|
355 |
1 |
return -1; |
|
356 |
} |
||
357 |
|||
358 |
20 |
return socket_fd; |
|
359 |
} |
||
360 |
|||
361 |
|||
362 |
/** |
||
363 |
* Connects to a (remote) TCP server |
||
364 |
*/ |
||
365 |
27 |
int ConnectTcpEndpoint(const std::string &ipv4_address, int portno) { |
|
366 |
27 |
const int socket_fd = socket(AF_INET, SOCK_STREAM, 0); |
|
367 |
✗✓ | 27 |
assert(socket_fd != -1); |
368 |
|||
369 |
struct sockaddr_in endpoint_addr; |
||
370 |
27 |
memset(&endpoint_addr, 0, sizeof(endpoint_addr)); |
|
371 |
27 |
endpoint_addr.sin_family = AF_INET; |
|
372 |
27 |
int retval = inet_aton(ipv4_address.c_str(), &(endpoint_addr.sin_addr)); |
|
373 |
✓✓ | 27 |
if (retval == 0) { |
374 |
1 |
LogCvmfs(kLogCvmfs, kLogDebug, "invalid IPv4 address"); |
|
375 |
1 |
close(socket_fd); |
|
376 |
1 |
return -1; |
|
377 |
} |
||
378 |
26 |
endpoint_addr.sin_port = htons(portno); |
|
379 |
|||
380 |
retval = connect(socket_fd, (struct sockaddr *)&endpoint_addr, |
||
381 |
26 |
sizeof(endpoint_addr)); |
|
382 |
✓✓ | 26 |
if (retval != 0) { |
383 |
LogCvmfs(kLogCvmfs, kLogDebug, "failed to connect to TCP endpoint (%d)", |
||
384 |
1 |
errno); |
|
385 |
1 |
close(socket_fd); |
|
386 |
1 |
return -1; |
|
387 |
} |
||
388 |
25 |
return socket_fd; |
|
389 |
} |
||
390 |
|||
391 |
|||
392 |
/** |
||
393 |
* Creating a pipe should always succeed. |
||
394 |
*/ |
||
395 |
1066 |
void MakePipe(int pipe_fd[2]) { |
|
396 |
1066 |
int retval = pipe(pipe_fd); |
|
397 |
✗✓ | 1066 |
assert(retval == 0); |
398 |
1066 |
} |
|
399 |
|||
400 |
|||
401 |
/** |
||
402 |
* Writes to a pipe should always succeed. |
||
403 |
*/ |
||
404 |
4622 |
void WritePipe(int fd, const void *buf, size_t nbyte) { |
|
405 |
int num_bytes; |
||
406 |
✗✓✗✗ ✗✓ |
4622 |
do { |
407 |
4622 |
num_bytes = write(fd, buf, nbyte); |
|
408 |
} while ((num_bytes < 0) && (errno == EINTR)); |
||
409 |
✓✗✗✓ |
4622 |
assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte)); |
410 |
4622 |
} |
|
411 |
|||
412 |
|||
413 |
/** |
||
414 |
* Reads from a pipe should always succeed. |
||
415 |
*/ |
||
416 |
4195491 |
void ReadPipe(int fd, void *buf, size_t nbyte) { |
|
417 |
int num_bytes; |
||
418 |
✗✓✗✗ ✗✓ |
4195491 |
do { |
419 |
4195491 |
num_bytes = read(fd, buf, nbyte); |
|
420 |
} while ((num_bytes < 0) && (errno == EINTR)); |
||
421 |
✓✗✗✓ |
4195491 |
assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte)); |
422 |
4195491 |
} |
|
423 |
|||
424 |
|||
425 |
/** |
||
426 |
* Reads from a pipe where writer's end is not yet necessarily connected |
||
427 |
*/ |
||
428 |
144 |
void ReadHalfPipe(int fd, void *buf, size_t nbyte) { |
|
429 |
int num_bytes; |
||
430 |
144 |
unsigned i = 0; |
|
431 |
144 |
unsigned backoff_ms = 1; |
|
432 |
144 |
const unsigned max_backoff_ms = 256; |
|
433 |
✗✓ | 144 |
do { |
434 |
// When the writer is not connected, this takes ~200-300ns per call as per |
||
435 |
// micro benchmarks |
||
436 |
144 |
num_bytes = read(fd, buf, nbyte); |
|
437 |
✗✓✗✗ |
144 |
if ((num_bytes < 0) && (errno == EINTR)) |
438 |
continue; |
||
439 |
144 |
i++; |
|
440 |
// Start backing off when the busy loop reaches the ballpark of 1ms |
||
441 |
✗✓✗✗ |
144 |
if ((i > 3000) && (num_bytes == 0)) { |
442 |
// The BackoffThrottle would pull in too many dependencies |
||
443 |
SafeSleepMs(backoff_ms); |
||
444 |
if (backoff_ms < max_backoff_ms) backoff_ms *= 2; |
||
445 |
} |
||
446 |
} while (num_bytes == 0); |
||
447 |
✓✗✗✓ |
144 |
assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte)); |
448 |
144 |
} |
|
449 |
|||
450 |
|||
451 |
/** |
||
452 |
* Closes both ends of a pipe |
||
453 |
*/ |
||
454 |
290 |
void ClosePipe(int pipe_fd[2]) { |
|
455 |
290 |
close(pipe_fd[0]); |
|
456 |
290 |
close(pipe_fd[1]); |
|
457 |
290 |
} |
|
458 |
|||
459 |
|||
460 |
/** |
||
461 |
* Compares two directory trees on the meta-data level. Returns true iff the |
||
462 |
* trees have identical content. |
||
463 |
*/ |
||
464 |
5797 |
bool DiffTree(const std::string &path_a, const std::string &path_b) { |
|
465 |
int retval; |
||
466 |
5797 |
std::vector<std::string> ls_a; |
|
467 |
5797 |
std::vector<std::string> ls_b; |
|
468 |
5797 |
std::vector<std::string> subdirs; |
|
469 |
|||
470 |
5797 |
DIR *dirp_a = opendir(path_a.c_str()); |
|
471 |
✗✓ | 5797 |
if (dirp_a == NULL) return false; |
472 |
5797 |
DIR *dirp_b = opendir(path_b.c_str()); |
|
473 |
✗✓ | 5797 |
if (dirp_b == NULL) { |
474 |
closedir(dirp_a); |
||
475 |
return false; |
||
476 |
} |
||
477 |
|||
478 |
platform_dirent64 *dirent; |
||
479 |
✗✗✓✗ ✗✓✗✗ ✓✓ |
5797 |
while ((dirent = platform_readdir(dirp_a))) { |
480 |
17650 |
const std::string name(dirent->d_name); |
|
481 |
✓✓✓✓ ✓✓ |
17650 |
if ((name == ".") || (name == "..")) |
482 |
11594 |
continue; |
|
483 |
6056 |
const std::string path = path_a + "/" + name; |
|
484 |
6056 |
ls_a.push_back(path); |
|
485 |
|||
486 |
platform_stat64 info; |
||
487 |
6056 |
retval = platform_lstat(path.c_str(), &info); |
|
488 |
✗✓ | 6056 |
if (retval != 0) { |
489 |
closedir(dirp_a); |
||
490 |
closedir(dirp_b); |
||
491 |
return false; |
||
492 |
} |
||
493 |
✓✓ | 6056 |
if (S_ISDIR(info.st_mode)) subdirs.push_back(name); |
494 |
} |
||
495 |
✗✗✓✓ |
5797 |
while ((dirent = platform_readdir(dirp_b))) { |
496 |
17627 |
const std::string name(dirent->d_name); |
|
497 |
✓✓✓✓ ✓✓ |
17627 |
if ((name == ".") || (name == "..")) |
498 |
11594 |
continue; |
|
499 |
6033 |
const std::string path = path_b + "/" + name; |
|
500 |
6033 |
ls_b.push_back(path); |
|
501 |
} |
||
502 |
5797 |
closedir(dirp_a); |
|
503 |
5797 |
closedir(dirp_b); |
|
504 |
|||
505 |
5797 |
sort(ls_a.begin(), ls_a.end()); |
|
506 |
5797 |
sort(ls_b.begin(), ls_b.end()); |
|
507 |
✓✓ | 5797 |
if (ls_a.size() != ls_b.size()) |
508 |
1 |
return false; |
|
509 |
✓✓ | 11808 |
for (unsigned i = 0; i < ls_a.size(); ++i) { |
510 |
✗✗✓✗ |
6012 |
if (GetFileName(ls_a[i]) != GetFileName(ls_b[i])) return false; |
511 |
platform_stat64 info_a; |
||
512 |
platform_stat64 info_b; |
||
513 |
6012 |
retval = platform_lstat(ls_a[i].c_str(), &info_a); |
|
514 |
✗✓ | 6012 |
if (retval != 0) return false; |
515 |
6012 |
retval = platform_lstat(ls_b[i].c_str(), &info_b); |
|
516 |
✗✓ | 6012 |
if (retval != 0) return false; |
517 |
✓✗✓✗ ✓✗✗✓ |
6012 |
if ((info_a.st_mode != info_b.st_mode) || |
518 |
(info_a.st_uid != info_b.st_uid) || |
||
519 |
(info_a.st_gid != info_b.st_gid) || |
||
520 |
(info_a.st_size != info_b.st_size)) |
||
521 |
{ |
||
522 |
return false; |
||
523 |
} |
||
524 |
} |
||
525 |
|||
526 |
✓✓ | 11590 |
for (unsigned i = 0; i < subdirs.size(); ++i) { |
527 |
bool retval_subtree = DiffTree(path_a + "/" + subdirs[i], |
||
528 |
5794 |
path_b + "/" + subdirs[i]); |
|
529 |
✗✓ | 5794 |
if (!retval_subtree) return false; |
530 |
} |
||
531 |
|||
532 |
5796 |
return true; |
|
533 |
} |
||
534 |
|||
535 |
|||
536 |
/** |
||
537 |
* Changes a non-blocking file descriptor to a blocking one. |
||
538 |
*/ |
||
539 |
5 |
void Nonblock2Block(int filedes) { |
|
540 |
5 |
int flags = fcntl(filedes, F_GETFL); |
|
541 |
✗✓ | 5 |
assert(flags != -1); |
542 |
5 |
int retval = fcntl(filedes, F_SETFL, flags & ~O_NONBLOCK); |
|
543 |
✗✓ | 5 |
assert(retval != -1); |
544 |
5 |
} |
|
545 |
|||
546 |
|||
547 |
/** |
||
548 |
* Changes a blocking file descriptor to a non-blocking one. |
||
549 |
*/ |
||
550 |
5 |
void Block2Nonblock(int filedes) { |
|
551 |
5 |
int flags = fcntl(filedes, F_GETFL); |
|
552 |
✗✓ | 5 |
assert(flags != -1); |
553 |
5 |
int retval = fcntl(filedes, F_SETFL, flags | O_NONBLOCK); |
|
554 |
✗✓ | 5 |
assert(retval != -1); |
555 |
5 |
} |
|
556 |
|||
557 |
|||
558 |
/** |
||
559 |
* Drops the characters of string to a socket. It doesn't matter |
||
560 |
* if the other side has hung up. |
||
561 |
*/ |
||
562 |
10 |
void SendMsg2Socket(const int fd, const std::string &msg) { |
|
563 |
10 |
(void)send(fd, &msg[0], msg.length(), MSG_NOSIGNAL); |
|
564 |
10 |
} |
|
565 |
|||
566 |
|||
567 |
9318343673 |
void LockMutex(pthread_mutex_t *mutex) { |
|
568 |
9318343673 |
int retval = pthread_mutex_lock(mutex); |
|
569 |
✗✓ | 9318350903 |
assert(retval == 0); |
570 |
9318350903 |
} |
|
571 |
|||
572 |
|||
573 |
9318350903 |
void UnlockMutex(pthread_mutex_t *mutex) { |
|
574 |
9318350903 |
int retval = pthread_mutex_unlock(mutex); |
|
575 |
✗✓ | 9318350888 |
assert(retval == 0); |
576 |
9318350888 |
} |
|
577 |
|||
578 |
|||
579 |
/** |
||
580 |
* set(e){g/u}id wrapper. |
||
581 |
*/ |
||
582 |
1 |
bool SwitchCredentials(const uid_t uid, const gid_t gid, |
|
583 |
const bool temporarily) |
||
584 |
{ |
||
585 |
LogCvmfs(kLogCvmfs, kLogDebug, "current credentials uid %d gid %d " |
||
586 |
"euid %d egid %d, switching to %d %d (temp: %d)", |
||
587 |
1 |
getuid(), getgid(), geteuid(), getegid(), uid, gid, temporarily); |
|
588 |
1 |
int retval = 0; |
|
589 |
✗✓ | 1 |
if (temporarily) { |
590 |
if (gid != getegid()) |
||
591 |
retval = setegid(gid); |
||
592 |
if ((retval == 0) && (uid != geteuid())) |
||
593 |
retval = seteuid(uid); |
||
594 |
} else { |
||
595 |
// If effective uid is not root, we must first gain root access back |
||
596 |
✗✓✗✗ ✗✓ |
1 |
if ((getuid() == 0) && (getuid() != geteuid())) { |
597 |
retval = SwitchCredentials(0, getgid(), true); |
||
598 |
if (!retval) |
||
599 |
return false; |
||
600 |
} |
||
601 |
✗✓✗✗ |
1 |
retval = setgid(gid) || setuid(uid); |
602 |
} |
||
603 |
LogCvmfs(kLogCvmfs, kLogDebug, "switch credentials result %d (%d)", |
||
604 |
1 |
retval, errno); |
|
605 |
1 |
return retval == 0; |
|
606 |
} |
||
607 |
|||
608 |
|||
609 |
/** |
||
610 |
* Checks if the regular file path exists. |
||
611 |
*/ |
||
612 |
5630 |
bool FileExists(const std::string &path) { |
|
613 |
platform_stat64 info; |
||
614 |
return ((platform_lstat(path.c_str(), &info) == 0) && |
||
615 |
✓✓✓✗ |
5630 |
S_ISREG(info.st_mode)); |
616 |
} |
||
617 |
|||
618 |
|||
619 |
/** |
||
620 |
* Returns -1 on failure. |
||
621 |
*/ |
||
622 |
17126 |
int64_t GetFileSize(const std::string &path) { |
|
623 |
platform_stat64 info; |
||
624 |
17126 |
int retval = platform_stat(path.c_str(), &info); |
|
625 |
✓✓ | 17126 |
if (retval != 0) |
626 |
6718 |
return -1; |
|
627 |
10408 |
return info.st_size; |
|
628 |
} |
||
629 |
|||
630 |
|||
631 |
/** |
||
632 |
* Checks if the directory (not symlink) path exists. |
||
633 |
*/ |
||
634 |
268 |
bool DirectoryExists(const std::string &path) { |
|
635 |
platform_stat64 info; |
||
636 |
return ((platform_lstat(path.c_str(), &info) == 0) && |
||
637 |
✓✓✓✗ |
268 |
S_ISDIR(info.st_mode)); |
638 |
} |
||
639 |
|||
640 |
|||
641 |
/** |
||
642 |
* Checks if the symlink file path exists. |
||
643 |
*/ |
||
644 |
4 |
bool SymlinkExists(const std::string &path) { |
|
645 |
platform_stat64 info; |
||
646 |
return ((platform_lstat(path.c_str(), &info) == 0) && |
||
647 |
✓✓✓✗ |
4 |
S_ISLNK(info.st_mode)); |
648 |
} |
||
649 |
|||
650 |
|||
651 |
/** |
||
652 |
* Equivalent of `ln -sf $src $dest` |
||
653 |
*/ |
||
654 |
5 |
bool SymlinkForced(const std::string &src, const std::string &dest) { |
|
655 |
5 |
int retval = unlink(dest.c_str()); |
|
656 |
✓✓✗✓ |
5 |
if ((retval != 0) && (errno != ENOENT)) |
657 |
return false; |
||
658 |
5 |
retval = symlink(src.c_str(), dest.c_str()); |
|
659 |
5 |
return retval == 0; |
|
660 |
} |
||
661 |
|||
662 |
|||
663 |
/** |
||
664 |
* The mkdir -p command. Additionally checks if the directory is writable |
||
665 |
* if it exists. |
||
666 |
*/ |
||
667 |
89748 |
bool MkdirDeep( |
|
668 |
const std::string &path, |
||
669 |
const mode_t mode, |
||
670 |
bool verify_writable) |
||
671 |
{ |
||
672 |
✓✓ | 89748 |
if (path == "") return false; |
673 |
|||
674 |
89746 |
int retval = mkdir(path.c_str(), mode); |
|
675 |
✓✓ | 89746 |
if (retval == 0) return true; |
676 |
|||
677 |
✓✓✓✗ ✓✓✗✗ ✗✓✓ |
431 |
if ((errno == ENOENT) && |
678 |
(MkdirDeep(GetParentPath(path), mode, verify_writable))) |
||
679 |
{ |
||
680 |
341 |
return MkdirDeep(path, mode, verify_writable); |
|
681 |
} |
||
682 |
|||
683 |
✓✓ | 90 |
if (errno == EEXIST) { |
684 |
platform_stat64 info; |
||
685 |
✓✗✓✓ ✓✓ |
88 |
if ((platform_stat(path.c_str(), &info) == 0) && S_ISDIR(info.st_mode)) { |
686 |
✓✓ | 87 |
if (verify_writable) { |
687 |
10 |
retval = utimes(path.c_str(), NULL); |
|
688 |
✓✗ | 10 |
if (retval == 0) |
689 |
10 |
return true; |
|
690 |
} else { |
||
691 |
77 |
return true; |
|
692 |
} |
||
693 |
} |
||
694 |
} |
||
695 |
|||
696 |
3 |
return false; |
|
697 |
} |
||
698 |
|||
699 |
|||
700 |
/** |
||
701 |
* Creates the "hash cache" directory structure in path. |
||
702 |
*/ |
||
703 |
277 |
bool MakeCacheDirectories(const std::string &path, const mode_t mode) { |
|
704 |
277 |
const std::string canonical_path = MakeCanonicalPath(path); |
|
705 |
|||
706 |
277 |
std::string this_path = canonical_path + "/quarantaine"; |
|
707 |
✓✓ | 277 |
if (!MkdirDeep(this_path, mode, false)) return false; |
708 |
|||
709 |
275 |
this_path = canonical_path + "/ff"; |
|
710 |
|||
711 |
platform_stat64 stat_info; |
||
712 |
✓✓ | 275 |
if (platform_stat(this_path.c_str(), &stat_info) != 0) { |
713 |
231 |
this_path = canonical_path + "/txn"; |
|
714 |
✗✓ | 231 |
if (!MkdirDeep(this_path, mode, false)) |
715 |
return false; |
||
716 |
✓✓ | 59367 |
for (int i = 0; i <= 0xff; i++) { |
717 |
char hex[4]; |
||
718 |
59136 |
snprintf(hex, sizeof(hex), "%02x", i); |
|
719 |
59136 |
this_path = canonical_path + "/" + std::string(hex); |
|
720 |
✗✓ | 59136 |
if (!MkdirDeep(this_path, mode, false)) |
721 |
return false; |
||
722 |
} |
||
723 |
} |
||
724 |
275 |
return true; |
|
725 |
} |
||
726 |
|||
727 |
|||
728 |
/** |
||
729 |
* Tries to locks file path, return an error if file is already locked. |
||
730 |
* Creates path if required. |
||
731 |
* |
||
732 |
* \return file descriptor, -1 on error, -2 if it would block |
||
733 |
*/ |
||
734 |
155 |
int TryLockFile(const std::string &path) { |
|
735 |
155 |
const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600); |
|
736 |
✓✓ | 155 |
if (fd_lockfile < 0) |
737 |
1 |
return -1; |
|
738 |
|||
739 |
✓✓ | 154 |
if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) { |
740 |
4 |
close(fd_lockfile); |
|
741 |
✗✓ | 4 |
if (errno != EWOULDBLOCK) |
742 |
return -1; |
||
743 |
4 |
return -2; |
|
744 |
} |
||
745 |
|||
746 |
150 |
return fd_lockfile; |
|
747 |
} |
||
748 |
|||
749 |
|||
750 |
/** |
||
751 |
* Tries to write the process id in a /var/run/progname.pid like file. Returns |
||
752 |
* the same as TryLockFile. |
||
753 |
* |
||
754 |
* \return file descriptor, -1 on error, -2 if it would block |
||
755 |
*/ |
||
756 |
4 |
int WritePidFile(const std::string &path) { |
|
757 |
4 |
const int fd = open(path.c_str(), O_CREAT | O_RDWR, 0600); |
|
758 |
✓✓ | 4 |
if (fd < 0) |
759 |
1 |
return -1; |
|
760 |
✓✓ | 3 |
if (flock(fd, LOCK_EX | LOCK_NB) != 0) { |
761 |
1 |
close(fd); |
|
762 |
✗✓ | 1 |
if (errno != EWOULDBLOCK) |
763 |
return -1; |
||
764 |
1 |
return -2; |
|
765 |
} |
||
766 |
|||
767 |
// Don't leak the file descriptor to exec'd children |
||
768 |
2 |
int flags = fcntl(fd, F_GETFD); |
|
769 |
✗✓ | 2 |
assert(flags != -1); |
770 |
2 |
flags |= FD_CLOEXEC; |
|
771 |
2 |
flags = fcntl(fd, F_SETFD, flags); |
|
772 |
✗✓ | 2 |
assert(flags != -1); |
773 |
|||
774 |
char buf[64]; |
||
775 |
|||
776 |
2 |
snprintf(buf, sizeof(buf), "%" PRId64 "\n", static_cast<uint64_t>(getpid())); |
|
777 |
bool retval = |
||
778 |
✓✗✓✗ |
2 |
(ftruncate(fd, 0) == 0) && SafeWrite(fd, buf, strlen(buf)); |
779 |
✗✓ | 2 |
if (!retval) { |
780 |
UnlockFile(fd); |
||
781 |
return -1; |
||
782 |
} |
||
783 |
2 |
return fd; |
|
784 |
} |
||
785 |
|||
786 |
|||
787 |
/** |
||
788 |
* Locks file path, blocks if file is already locked. Creates path if required. |
||
789 |
* |
||
790 |
* \return file descriptor, -1 on error |
||
791 |
*/ |
||
792 |
123 |
int LockFile(const std::string &path) { |
|
793 |
123 |
const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600); |
|
794 |
✓✓ | 123 |
if (fd_lockfile < 0) |
795 |
4 |
return -1; |
|
796 |
|||
797 |
|||
798 |
✓✓ | 119 |
if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) { |
799 |
✗✓ | 1 |
if (errno != EWOULDBLOCK) { |
800 |
close(fd_lockfile); |
||
801 |
return -1; |
||
802 |
} |
||
803 |
LogCvmfs(kLogCvmfs, kLogSyslog, "another process holds %s, waiting.", |
||
804 |
1 |
path.c_str()); |
|
805 |
✗✓ | 1 |
if (flock(fd_lockfile, LOCK_EX) != 0) { |
806 |
close(fd_lockfile); |
||
807 |
return -1; |
||
808 |
} |
||
809 |
1 |
LogCvmfs(kLogCvmfs, kLogSyslog, "lock %s acquired", path.c_str()); |
|
810 |
} |
||
811 |
|||
812 |
119 |
return fd_lockfile; |
|
813 |
} |
||
814 |
|||
815 |
|||
816 |
262 |
void UnlockFile(const int filedes) { |
|
817 |
262 |
int retval = flock(filedes, LOCK_UN); |
|
818 |
✗✓ | 262 |
assert(retval == 0); |
819 |
262 |
close(filedes); |
|
820 |
262 |
} |
|
821 |
|||
822 |
|||
823 |
/** |
||
824 |
* Wrapper around mkstemp. |
||
825 |
*/ |
||
826 |
2847 |
FILE *CreateTempFile(const std::string &path_prefix, const int mode, |
|
827 |
const char *open_flags, std::string *final_path) |
||
828 |
{ |
||
829 |
2847 |
*final_path = path_prefix + ".XXXXXX"; |
|
830 |
2847 |
char *tmp_file = strdupa(final_path->c_str()); |
|
831 |
2847 |
int tmp_fd = mkstemp(tmp_file); |
|
832 |
✓✓ | 2847 |
if (tmp_fd < 0) { |
833 |
17 |
return NULL; |
|
834 |
} |
||
835 |
✗✓ | 2830 |
if (fchmod(tmp_fd, mode) != 0) { |
836 |
close(tmp_fd); |
||
837 |
return NULL; |
||
838 |
} |
||
839 |
|||
840 |
2830 |
*final_path = tmp_file; |
|
841 |
2830 |
FILE *tmp_fp = fdopen(tmp_fd, open_flags); |
|
842 |
✗✓ | 2830 |
if (!tmp_fp) { |
843 |
close(tmp_fd); |
||
844 |
unlink(tmp_file); |
||
845 |
return NULL; |
||
846 |
} |
||
847 |
|||
848 |
2830 |
return tmp_fp; |
|
849 |
} |
||
850 |
|||
851 |
|||
852 |
/** |
||
853 |
* Create the file but don't open. Use only in non-public tmp directories. |
||
854 |
*/ |
||
855 |
2224 |
std::string CreateTempPath(const std::string &path_prefix, const int mode) { |
|
856 |
2224 |
std::string result; |
|
857 |
2224 |
FILE *f = CreateTempFile(path_prefix, mode, "w", &result); |
|
858 |
✓✓ | 2224 |
if (!f) |
859 |
1 |
return ""; |
|
860 |
2223 |
fclose(f); |
|
861 |
2223 |
return result; |
|
862 |
} |
||
863 |
|||
864 |
|||
865 |
/** |
||
866 |
* Create a directory with a unique name. |
||
867 |
*/ |
||
868 |
282 |
std::string CreateTempDir(const std::string &path_prefix) { |
|
869 |
282 |
std::string dir = path_prefix + ".XXXXXX"; |
|
870 |
282 |
char *tmp_dir = strdupa(dir.c_str()); |
|
871 |
282 |
tmp_dir = mkdtemp(tmp_dir); |
|
872 |
✓✓ | 282 |
if (tmp_dir == NULL) |
873 |
1 |
return ""; |
|
874 |
281 |
return std::string(tmp_dir); |
|
875 |
} |
||
876 |
|||
877 |
|||
878 |
/** |
||
879 |
* Get the current working directory of the running process |
||
880 |
*/ |
||
881 |
224 |
std::string GetCurrentWorkingDirectory() { |
|
882 |
char cwd[PATH_MAX]; |
||
883 |
✓✗✗✗ ✓✗ |
224 |
return (getcwd(cwd, sizeof(cwd)) != NULL) ? std::string(cwd) : std::string(); |
884 |
} |
||
885 |
|||
886 |
|||
887 |
/** |
||
888 |
* Helper class that provides callback funtions for the file system traversal. |
||
889 |
*/ |
||
890 |
class RemoveTreeHelper { |
||
891 |
public: |
||
892 |
bool success; |
||
893 |
891 |
RemoveTreeHelper() { |
|
894 |
891 |
success = true; |
|
895 |
891 |
} |
|
896 |
5503 |
void RemoveFile(const std::string &parent_path, const std::string &name) { |
|
897 |
5503 |
int retval = unlink((parent_path + "/" + name).c_str()); |
|
898 |
✗✓ | 5503 |
if (retval != 0) |
899 |
success = false; |
||
900 |
5503 |
} |
|
901 |
3043 |
void RemoveDir(const std::string &parent_path, const std::string &name) { |
|
902 |
3043 |
int retval = rmdir((parent_path + "/" + name).c_str()); |
|
903 |
✗✓ | 3043 |
if (retval != 0) |
904 |
success = false; |
||
905 |
3043 |
} |
|
906 |
90343 |
bool TryRemoveDir(const std::string &parent_path, const std::string &name) { |
|
907 |
90343 |
int retval = rmdir((parent_path + "/" + name).c_str()); |
|
908 |
90343 |
return (retval != 0); |
|
909 |
} |
||
910 |
}; |
||
911 |
|||
912 |
|||
913 |
/** |
||
914 |
* Does rm -rf on path. |
||
915 |
*/ |
||
916 |
948 |
bool RemoveTree(const std::string &path) { |
|
917 |
platform_stat64 info; |
||
918 |
948 |
int retval = platform_lstat(path.c_str(), &info); |
|
919 |
✓✓ | 948 |
if (retval != 0) |
920 |
57 |
return errno == ENOENT; |
|
921 |
✗✓ | 891 |
if (!S_ISDIR(info.st_mode)) |
922 |
return false; |
||
923 |
|||
924 |
891 |
RemoveTreeHelper *remove_tree_helper = new RemoveTreeHelper(); |
|
925 |
FileSystemTraversal<RemoveTreeHelper> traversal(remove_tree_helper, "", |
||
926 |
891 |
true); |
|
927 |
891 |
traversal.fn_new_file = &RemoveTreeHelper::RemoveFile; |
|
928 |
891 |
traversal.fn_new_symlink = &RemoveTreeHelper::RemoveFile; |
|
929 |
891 |
traversal.fn_new_socket = &RemoveTreeHelper::RemoveFile; |
|
930 |
891 |
traversal.fn_new_fifo = &RemoveTreeHelper::RemoveFile; |
|
931 |
891 |
traversal.fn_leave_dir = &RemoveTreeHelper::RemoveDir; |
|
932 |
891 |
traversal.fn_new_dir_prefix = &RemoveTreeHelper::TryRemoveDir; |
|
933 |
891 |
traversal.Recurse(path); |
|
934 |
891 |
bool result = remove_tree_helper->success; |
|
935 |
891 |
delete remove_tree_helper; |
|
936 |
|||
937 |
891 |
return result; |
|
938 |
} |
||
939 |
|||
940 |
|||
941 |
/** |
||
942 |
* Returns ls $dir/GLOB$suffix |
||
943 |
*/ |
||
944 |
9 |
std::vector<std::string> FindFilesBySuffix( |
|
945 |
const std::string &dir, |
||
946 |
const std::string &suffix) |
||
947 |
{ |
||
948 |
9 |
std::vector<std::string> result; |
|
949 |
9 |
DIR *dirp = opendir(dir.c_str()); |
|
950 |
✓✓ | 9 |
if (!dirp) |
951 |
4 |
return result; |
|
952 |
|||
953 |
platform_dirent64 *dirent; |
||
954 |
✗✓✓ | 5 |
while ((dirent = platform_readdir(dirp))) { |
955 |
26 |
const std::string name(dirent->d_name); |
|
956 |
✓✓✓✓ ✓✓✗✗ ✗✓✓ |
26 |
if ((name.length() >= suffix.length()) && |
957 |
(name.substr(name.length()-suffix.length()) == suffix)) |
||
958 |
{ |
||
959 |
11 |
result.push_back(dir + "/" + name); |
|
960 |
} |
||
961 |
} |
||
962 |
5 |
closedir(dirp); |
|
963 |
5 |
std::sort(result.begin(), result.end()); |
|
964 |
5 |
return result; |
|
965 |
} |
||
966 |
|||
967 |
|||
968 |
/** |
||
969 |
* Returns ls $dir/$prefixGLOB |
||
970 |
*/ |
||
971 |
17 |
std::vector<std::string> FindFilesByPrefix( |
|
972 |
const std::string &dir, |
||
973 |
const std::string &prefix) |
||
974 |
{ |
||
975 |
17 |
std::vector<std::string> result; |
|
976 |
17 |
DIR *dirp = opendir(dir.c_str()); |
|
977 |
✓✓ | 17 |
if (!dirp) |
978 |
1 |
return result; |
|
979 |
|||
980 |
platform_dirent64 *dirent; |
||
981 |
✗✓✓ | 16 |
while ((dirent = platform_readdir(dirp))) { |
982 |
3159 |
const std::string name(dirent->d_name); |
|
983 |
✓✓✓✓ ✓✓✗✗ ✗✓✓ |
3159 |
if ((name.length() >= prefix.length()) && |
984 |
(name.substr(0, prefix.length()) == prefix)) |
||
985 |
{ |
||
986 |
14 |
result.push_back(dir + "/" + name); |
|
987 |
} |
||
988 |
} |
||
989 |
16 |
closedir(dirp); |
|
990 |
16 |
std::sort(result.begin(), result.end()); |
|
991 |
16 |
return result; |
|
992 |
} |
||
993 |
|||
994 |
|||
995 |
/** |
||
996 |
* Finds all direct subdirectories under parent_dir (except ., ..). Used, |
||
997 |
* for instance, to parse /etc/cvmfs/repositories.d/<reponoame> |
||
998 |
*/ |
||
999 |
4 |
std::vector<std::string> FindDirectories(const std::string &parent_dir) { |
|
1000 |
4 |
std::vector<std::string> result; |
|
1001 |
4 |
DIR *dirp = opendir(parent_dir.c_str()); |
|
1002 |
✗✓ | 4 |
if (!dirp) |
1003 |
return result; |
||
1004 |
|||
1005 |
platform_dirent64 *dirent; |
||
1006 |
✗✗✓✗ ✗✓✓ |
4 |
while ((dirent = platform_readdir(dirp))) { |
1007 |
17 |
const std::string name(dirent->d_name); |
|
1008 |
✓✓✓✓ ✓✓ |
17 |
if ((name == ".") || (name == "..")) |
1009 |
8 |
continue; |
|
1010 |
9 |
const std::string path = parent_dir + "/" + name; |
|
1011 |
|||
1012 |
platform_stat64 info; |
||
1013 |
9 |
int retval = platform_stat(path.c_str(), &info); |
|
1014 |
✗✓ | 9 |
if (retval != 0) |
1015 |
continue; |
||
1016 |
✓✓ | 9 |
if (S_ISDIR(info.st_mode)) |
1017 |
7 |
result.push_back(path); |
|
1018 |
} |
||
1019 |
4 |
closedir(dirp); |
|
1020 |
4 |
sort(result.begin(), result.end()); |
|
1021 |
4 |
return result; |
|
1022 |
} |
||
1023 |
|||
1024 |
/** |
||
1025 |
* Name -> UID from passwd database |
||
1026 |
*/ |
||
1027 |
2 |
bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid) { |
|
1028 |
struct passwd pwd; |
||
1029 |
2 |
struct passwd *result = NULL; |
|
1030 |
2 |
int bufsize = 16 * 1024; |
|
1031 |
2 |
char *buf = static_cast<char *>(smalloc(bufsize)); |
|
1032 |
✗✓ | 4 |
while (getpwnam_r(username.c_str(), &pwd, buf, bufsize, &result) == ERANGE) { |
1033 |
bufsize *= 2; |
||
1034 |
buf = static_cast<char *>(srealloc(buf, bufsize)); |
||
1035 |
} |
||
1036 |
✓✓ | 2 |
if (result == NULL) { |
1037 |
1 |
free(buf); |
|
1038 |
1 |
return false; |
|
1039 |
} |
||
1040 |
1 |
*uid = result->pw_uid; |
|
1041 |
1 |
*main_gid = result->pw_gid; |
|
1042 |
1 |
free(buf); |
|
1043 |
1 |
return true; |
|
1044 |
} |
||
1045 |
|||
1046 |
|||
1047 |
/** |
||
1048 |
* Name -> GID from groups database |
||
1049 |
*/ |
||
1050 |
2 |
bool GetGidOf(const std::string &groupname, gid_t *gid) { |
|
1051 |
struct group grp; |
||
1052 |
2 |
struct group *result = NULL; |
|
1053 |
2 |
int bufsize = 16 * 1024; |
|
1054 |
2 |
char *buf = static_cast<char *>(smalloc(bufsize)); |
|
1055 |
✗✓ | 4 |
while (getgrnam_r(groupname.c_str(), &grp, buf, bufsize, &result) == ERANGE) { |
1056 |
bufsize *= 2; |
||
1057 |
buf = static_cast<char *>(srealloc(buf, bufsize)); |
||
1058 |
} |
||
1059 |
✓✓ | 2 |
if (result == NULL) { |
1060 |
1 |
free(buf); |
|
1061 |
1 |
return false; |
|
1062 |
} |
||
1063 |
1 |
*gid = result->gr_gid; |
|
1064 |
1 |
free(buf); |
|
1065 |
1 |
return true; |
|
1066 |
} |
||
1067 |
|||
1068 |
/** |
||
1069 |
* read the current umask of this process |
||
1070 |
* Note: umask query is guarded by a global mutex. Hence, always use |
||
1071 |
* this function and beware of scalability bottlenecks |
||
1072 |
*/ |
||
1073 |
33 |
mode_t GetUmask() { |
|
1074 |
33 |
LockMutex(&getumask_mutex); |
|
1075 |
33 |
const mode_t my_umask = umask(0); |
|
1076 |
33 |
umask(my_umask); |
|
1077 |
33 |
UnlockMutex(&getumask_mutex); |
|
1078 |
33 |
return my_umask; |
|
1079 |
} |
||
1080 |
|||
1081 |
|||
1082 |
/** |
||
1083 |
* Adds gid to the list of supplementary groups |
||
1084 |
*/ |
||
1085 |
bool AddGroup2Persona(const gid_t gid) { |
||
1086 |
int ngroups = getgroups(0, NULL); |
||
1087 |
if (ngroups < 0) |
||
1088 |
return false; |
||
1089 |
gid_t *groups = static_cast<gid_t *>(smalloc((ngroups+1) * sizeof(gid_t))); |
||
1090 |
int retval = getgroups(ngroups, groups); |
||
1091 |
if (retval < 0) { |
||
1092 |
free(groups); |
||
1093 |
return false; |
||
1094 |
} |
||
1095 |
for (int i = 0; i < ngroups; ++i) { |
||
1096 |
if (groups[i] == gid) { |
||
1097 |
free(groups); |
||
1098 |
return true; |
||
1099 |
} |
||
1100 |
} |
||
1101 |
groups[ngroups] = gid; |
||
1102 |
retval = setgroups(ngroups+1, groups); |
||
1103 |
free(groups); |
||
1104 |
return retval == 0; |
||
1105 |
} |
||
1106 |
|||
1107 |
|||
1108 |
/** |
||
1109 |
* Sets soft and hard limit for maximum number of open file descriptors. |
||
1110 |
* Returns 0 on success, -1 on failure, -2 if running under valgrind. |
||
1111 |
*/ |
||
1112 |
11 |
int SetLimitNoFile(unsigned limit_nofile) { |
|
1113 |
struct rlimit rpl; |
||
1114 |
11 |
memset(&rpl, 0, sizeof(rpl)); |
|
1115 |
11 |
getrlimit(RLIMIT_NOFILE, &rpl); |
|
1116 |
✓✓ | 11 |
if (rpl.rlim_max < limit_nofile) |
1117 |
6 |
rpl.rlim_max = limit_nofile; |
|
1118 |
11 |
rpl.rlim_cur = limit_nofile; |
|
1119 |
11 |
int retval = setrlimit(RLIMIT_NOFILE, &rpl); |
|
1120 |
✓✓ | 11 |
if (retval == 0) |
1121 |
5 |
return 0; |
|
1122 |
|||
1123 |
#ifdef HAS_VALGRIND_HEADERS |
||
1124 |
✗✓ | 6 |
return RUNNING_ON_VALGRIND ? -2 : -1; |
1125 |
#else |
||
1126 |
return -1; |
||
1127 |
#endif |
||
1128 |
} |
||
1129 |
|||
1130 |
|||
1131 |
/** |
||
1132 |
* Blocks a signal for the calling thread. |
||
1133 |
*/ |
||
1134 |
2 |
void BlockSignal(int signum) { |
|
1135 |
sigset_t sigset; |
||
1136 |
2 |
int retval = sigemptyset(&sigset); |
|
1137 |
✗✓ | 2 |
assert(retval == 0); |
1138 |
2 |
retval = sigaddset(&sigset, signum); |
|
1139 |
✗✓ | 2 |
assert(retval == 0); |
1140 |
2 |
retval = pthread_sigmask(SIG_BLOCK, &sigset, NULL); |
|
1141 |
✗✓ | 2 |
assert(retval == 0); |
1142 |
2 |
} |
|
1143 |
|||
1144 |
|||
1145 |
/** |
||
1146 |
* Waits for a signal. The signal should be blocked before for all threads. |
||
1147 |
* Threads inherit their parent's signal mask. |
||
1148 |
*/ |
||
1149 |
1 |
void WaitForSignal(int signum) { |
|
1150 |
int retval; |
||
1151 |
✗✓✗✗ ✗✓ |
1 |
do { |
1152 |
1 |
retval = platform_sigwait(signum); |
|
1153 |
} while ((retval != signum) && (errno == EINTR)); |
||
1154 |
✗✓ | 1 |
assert(retval == signum); |
1155 |
1 |
} |
|
1156 |
|||
1157 |
|||
1158 |
/** |
||
1159 |
* Returns -1 of the child crashed or the exit code otherwise |
||
1160 |
*/ |
||
1161 |
4 |
int WaitForChild(pid_t pid) { |
|
1162 |
✗✓ | 4 |
assert(pid > 0); |
1163 |
int statloc; |
||
1164 |
while (true) { |
||
1165 |
4 |
pid_t retval = waitpid(pid, &statloc, 0); |
|
1166 |
✗✓ | 4 |
if (retval == -1) { |
1167 |
if (errno == EINTR) |
||
1168 |
continue; |
||
1169 |
assert(false); |
||
1170 |
} |
||
1171 |
✗✓ | 4 |
assert(retval == pid); |
1172 |
4 |
break; |
|
1173 |
} |
||
1174 |
✓✓ | 4 |
if (WIFEXITED(statloc)) |
1175 |
3 |
return WEXITSTATUS(statloc); |
|
1176 |
1 |
return -1; |
|
1177 |
} |
||
1178 |
|||
1179 |
|||
1180 |
/** |
||
1181 |
* Makes a daemon. The daemon() call is deprecated on OS X |
||
1182 |
*/ |
||
1183 |
void Daemonize() { |
||
1184 |
pid_t pid; |
||
1185 |
int statloc; |
||
1186 |
if ((pid = fork()) == 0) { |
||
1187 |
int retval = setsid(); |
||
1188 |
assert(retval != -1); |
||
1189 |
if ((pid = fork()) == 0) { |
||
1190 |
int null_read = open("/dev/null", O_RDONLY); |
||
1191 |
int null_write = open("/dev/null", O_WRONLY); |
||
1192 |
assert((null_read >= 0) && (null_write >= 0)); |
||
1193 |
retval = dup2(null_read, 0); |
||
1194 |
assert(retval == 0); |
||
1195 |
retval = dup2(null_write, 1); |
||
1196 |
assert(retval == 1); |
||
1197 |
retval = dup2(null_write, 2); |
||
1198 |
assert(retval == 2); |
||
1199 |
close(null_read); |
||
1200 |
close(null_write); |
||
1201 |
LogCvmfs(kLogCvmfs, kLogDebug, "daemonized"); |
||
1202 |
} else { |
||
1203 |
assert(pid > 0); |
||
1204 |
_exit(0); |
||
1205 |
} |
||
1206 |
} else { |
||
1207 |
assert(pid > 0); |
||
1208 |
waitpid(pid, &statloc, 0); |
||
1209 |
_exit(0); |
||
1210 |
} |
||
1211 |
} |
||
1212 |
|||
1213 |
|||
1214 |
112 |
bool ExecuteBinary( |
|
1215 |
int *fd_stdin, |
||
1216 |
int *fd_stdout, |
||
1217 |
int *fd_stderr, |
||
1218 |
const std::string &binary_path, |
||
1219 |
const std::vector<std::string> &argv, |
||
1220 |
const bool double_fork, |
||
1221 |
pid_t *child_pid |
||
1222 |
) { |
||
1223 |
int pipe_stdin[2]; |
||
1224 |
int pipe_stdout[2]; |
||
1225 |
int pipe_stderr[2]; |
||
1226 |
112 |
MakePipe(pipe_stdin); |
|
1227 |
112 |
MakePipe(pipe_stdout); |
|
1228 |
112 |
MakePipe(pipe_stderr); |
|
1229 |
|||
1230 |
112 |
std::set<int> preserve_fildes; |
|
1231 |
112 |
preserve_fildes.insert(0); |
|
1232 |
112 |
preserve_fildes.insert(1); |
|
1233 |
112 |
preserve_fildes.insert(2); |
|
1234 |
112 |
std::map<int, int> map_fildes; |
|
1235 |
112 |
map_fildes[pipe_stdin[0]] = 0; // Reading end of pipe_stdin |
|
1236 |
112 |
map_fildes[pipe_stdout[1]] = 1; // Writing end of pipe_stdout |
|
1237 |
112 |
map_fildes[pipe_stderr[1]] = 2; // Writing end of pipe_stderr |
|
1238 |
112 |
std::vector<std::string> cmd_line; |
|
1239 |
112 |
cmd_line.push_back(binary_path); |
|
1240 |
112 |
cmd_line.insert(cmd_line.end(), argv.begin(), argv.end()); |
|
1241 |
|||
1242 |
✗✓ | 112 |
if (!ManagedExec(cmd_line, |
1243 |
preserve_fildes, |
||
1244 |
map_fildes, |
||
1245 |
true, |
||
1246 |
double_fork, |
||
1247 |
child_pid)) |
||
1248 |
{ |
||
1249 |
ClosePipe(pipe_stdin); |
||
1250 |
ClosePipe(pipe_stdout); |
||
1251 |
ClosePipe(pipe_stderr); |
||
1252 |
return false; |
||
1253 |
} |
||
1254 |
|||
1255 |
112 |
close(pipe_stdin[0]); |
|
1256 |
112 |
close(pipe_stdout[1]); |
|
1257 |
112 |
close(pipe_stderr[1]); |
|
1258 |
112 |
*fd_stdin = pipe_stdin[1]; |
|
1259 |
112 |
*fd_stdout = pipe_stdout[0]; |
|
1260 |
112 |
*fd_stderr = pipe_stderr[0]; |
|
1261 |
112 |
return true; |
|
1262 |
} |
||
1263 |
|||
1264 |
|||
1265 |
/** |
||
1266 |
* Opens /bin/sh and provides file descriptors to write into stdin and |
||
1267 |
* read from stdout. Quit shell simply by closing stderr, stdout, and stdin. |
||
1268 |
*/ |
||
1269 |
109 |
bool Shell(int *fd_stdin, int *fd_stdout, int *fd_stderr) { |
|
1270 |
109 |
const bool double_fork = true; |
|
1271 |
return ExecuteBinary(fd_stdin, fd_stdout, fd_stderr, "/bin/sh", |
||
1272 |
109 |
std::vector<std::string>(), double_fork); |
|
1273 |
} |
||
1274 |
|||
1275 |
struct ForkFailures { // TODO(rmeusel): C++11 (type safe enum) |
||
1276 |
enum Names { |
||
1277 |
kSendPid, |
||
1278 |
kUnknown, |
||
1279 |
kFailDupFd, |
||
1280 |
kFailGetMaxFd, |
||
1281 |
kFailGetFdFlags, |
||
1282 |
kFailSetFdFlags, |
||
1283 |
kFailDropCredentials, |
||
1284 |
kFailExec, |
||
1285 |
}; |
||
1286 |
|||
1287 |
static std::string ToString(const Names name) { |
||
1288 |
switch (name) { |
||
1289 |
case kSendPid: |
||
1290 |
return "Sending PID"; |
||
1291 |
|||
1292 |
default: |
||
1293 |
case kUnknown: |
||
1294 |
return "Unknown Status"; |
||
1295 |
case kFailDupFd: |
||
1296 |
return "Duplicate File Descriptor"; |
||
1297 |
case kFailGetMaxFd: |
||
1298 |
return "Read maximal File Descriptor"; |
||
1299 |
case kFailGetFdFlags: |
||
1300 |
return "Read File Descriptor Flags"; |
||
1301 |
case kFailSetFdFlags: |
||
1302 |
return "Set File Descriptor Flags"; |
||
1303 |
case kFailDropCredentials: |
||
1304 |
return "Lower User Permissions"; |
||
1305 |
case kFailExec: |
||
1306 |
return "Invoking execvp()"; |
||
1307 |
} |
||
1308 |
} |
||
1309 |
}; |
||
1310 |
|||
1311 |
/** |
||
1312 |
* Execve to the given command line, preserving the given file descriptors. |
||
1313 |
* If stdin, stdout, stderr should be preserved, add 0, 1, 2. |
||
1314 |
* File descriptors from the parent process can also be mapped to the new |
||
1315 |
* process (dup2) using map_fildes. Can be useful for |
||
1316 |
* stdout/in/err redirection. |
||
1317 |
* NOTE: The destination fildes have to be preserved! |
||
1318 |
* Does a double fork to detach child. |
||
1319 |
* The command_line parameter contains the binary at index 0 and the arguments |
||
1320 |
* in the rest of the vector. |
||
1321 |
* Using the optional parameter *pid it is possible to retrieve the process ID |
||
1322 |
* of the spawned process. |
||
1323 |
*/ |
||
1324 |
116 |
bool ManagedExec(const std::vector<std::string> &command_line, |
|
1325 |
const std::set<int> &preserve_fildes, |
||
1326 |
const std::map<int, int> &map_fildes, |
||
1327 |
const bool drop_credentials, |
||
1328 |
const bool double_fork, |
||
1329 |
pid_t *child_pid) |
||
1330 |
{ |
||
1331 |
✗✓ | 116 |
assert(command_line.size() >= 1); |
1332 |
|||
1333 |
116 |
Pipe pipe_fork; |
|
1334 |
116 |
pid_t pid = fork(); |
|
1335 |
✗✓ | 116 |
assert(pid >= 0); |
1336 |
✗✓ | 116 |
if (pid == 0) { |
1337 |
pid_t pid_grand_child; |
||
1338 |
int max_fd; |
||
1339 |
int fd_flags; |
||
1340 |
ForkFailures::Names failed = ForkFailures::kUnknown; |
||
1341 |
|||
1342 |
const char *argv[command_line.size() + 1]; |
||
1343 |
for (unsigned i = 0; i < command_line.size(); ++i) |
||
1344 |
argv[i] = command_line[i].c_str(); |
||
1345 |
argv[command_line.size()] = NULL; |
||
1346 |
|||
1347 |
// Child, map file descriptors |
||
1348 |
for (std::map<int, int>::const_iterator i = map_fildes.begin(), |
||
1349 |
iEnd = map_fildes.end(); i != iEnd; ++i) |
||
1350 |
{ |
||
1351 |
int retval = dup2(i->first, i->second); |
||
1352 |
if (retval == -1) { |
||
1353 |
failed = ForkFailures::kFailDupFd; |
||
1354 |
goto fork_failure; |
||
1355 |
} |
||
1356 |
} |
||
1357 |
|||
1358 |
// Child, close file descriptors |
||
1359 |
max_fd = sysconf(_SC_OPEN_MAX); |
||
1360 |
if (max_fd < 0) { |
||
1361 |
failed = ForkFailures::kFailGetMaxFd; |
||
1362 |
goto fork_failure; |
||
1363 |
} |
||
1364 |
for (int fd = 0; fd < max_fd; fd++) { |
||
1365 |
if ((fd != pipe_fork.write_end) && (preserve_fildes.count(fd) == 0)) { |
||
1366 |
close(fd); |
||
1367 |
} |
||
1368 |
} |
||
1369 |
|||
1370 |
// Double fork to disconnect from parent |
||
1371 |
if (double_fork) { |
||
1372 |
pid_grand_child = fork(); |
||
1373 |
assert(pid_grand_child >= 0); |
||
1374 |
if (pid_grand_child != 0) _exit(0); |
||
1375 |
} |
||
1376 |
|||
1377 |
fd_flags = fcntl(pipe_fork.write_end, F_GETFD); |
||
1378 |
if (fd_flags < 0) { |
||
1379 |
failed = ForkFailures::kFailGetFdFlags; |
||
1380 |
goto fork_failure; |
||
1381 |
} |
||
1382 |
fd_flags |= FD_CLOEXEC; |
||
1383 |
if (fcntl(pipe_fork.write_end, F_SETFD, fd_flags) < 0) { |
||
1384 |
failed = ForkFailures::kFailSetFdFlags; |
||
1385 |
goto fork_failure; |
||
1386 |
} |
||
1387 |
|||
1388 |
#ifdef DEBUGMSG |
||
1389 |
assert(setenv("__CVMFS_DEBUG_MODE__", "yes", 1) == 0); |
||
1390 |
#endif |
||
1391 |
if (drop_credentials && !SwitchCredentials(geteuid(), getegid(), false)) { |
||
1392 |
failed = ForkFailures::kFailDropCredentials; |
||
1393 |
goto fork_failure; |
||
1394 |
} |
||
1395 |
|||
1396 |
// retrieve the PID of the new (grand) child process and send it to the |
||
1397 |
// grand father |
||
1398 |
pid_grand_child = getpid(); |
||
1399 |
pipe_fork.Write(ForkFailures::kSendPid); |
||
1400 |
pipe_fork.Write(pid_grand_child); |
||
1401 |
|||
1402 |
execvp(command_line[0].c_str(), const_cast<char **>(argv)); |
||
1403 |
|||
1404 |
failed = ForkFailures::kFailExec; |
||
1405 |
|||
1406 |
fork_failure: |
||
1407 |
pipe_fork.Write(failed); |
||
1408 |
_exit(1); |
||
1409 |
} |
||
1410 |
✓✓ | 116 |
if (double_fork) { |
1411 |
int statloc; |
||
1412 |
114 |
waitpid(pid, &statloc, 0); |
|
1413 |
} |
||
1414 |
|||
1415 |
116 |
close(pipe_fork.write_end); |
|
1416 |
|||
1417 |
// Either the PID or a return value is sent |
||
1418 |
ForkFailures::Names status_code; |
||
1419 |
116 |
bool retcode = pipe_fork.Read(&status_code); |
|
1420 |
✗✓ | 116 |
assert(retcode); |
1421 |
✗✓ | 116 |
if (status_code != ForkFailures::kSendPid) { |
1422 |
close(pipe_fork.read_end); |
||
1423 |
LogCvmfs(kLogCvmfs, kLogDebug, "managed execve failed (%s)", |
||
1424 |
ForkFailures::ToString(status_code).c_str()); |
||
1425 |
return false; |
||
1426 |
} |
||
1427 |
|||
1428 |
// read the PID of the spawned process if requested |
||
1429 |
// (the actual read needs to be done in any case!) |
||
1430 |
116 |
pid_t buf_child_pid = 0; |
|
1431 |
116 |
retcode = pipe_fork.Read(&buf_child_pid); |
|
1432 |
✗✓ | 116 |
assert(retcode); |
1433 |
✓✓ | 116 |
if (child_pid != NULL) |
1434 |
4 |
*child_pid = buf_child_pid; |
|
1435 |
116 |
close(pipe_fork.read_end); |
|
1436 |
LogCvmfs(kLogCvmfs, kLogDebug, "execve'd %s (PID: %d)", |
||
1437 |
command_line[0].c_str(), |
||
1438 |
116 |
static_cast<int>(buf_child_pid)); |
|
1439 |
116 |
return true; |
|
1440 |
} |
||
1441 |
|||
1442 |
|||
1443 |
/** |
||
1444 |
* Sleeps using select. This is without signals and doesn't interfere with |
||
1445 |
* other uses of the ALRM signal. |
||
1446 |
*/ |
||
1447 |
101 |
void SafeSleepMs(const unsigned ms) { |
|
1448 |
struct timeval wait_for; |
||
1449 |
101 |
wait_for.tv_sec = ms / 1000; |
|
1450 |
101 |
wait_for.tv_usec = (ms % 1000) * 1000; |
|
1451 |
101 |
select(0, NULL, NULL, NULL, &wait_for); |
|
1452 |
100 |
} |
|
1453 |
|||
1454 |
|||
1455 |
/** |
||
1456 |
* Deal with EINTR and partial writes. |
||
1457 |
*/ |
||
1458 |
7624 |
bool SafeWrite(int fd, const void *buf, size_t nbyte) { |
|
1459 |
✓✓ | 22840 |
while (nbyte) { |
1460 |
7624 |
ssize_t retval = write(fd, buf, nbyte); |
|
1461 |
✓✓ | 7624 |
if (retval < 0) { |
1462 |
✗✓ | 32 |
if (errno == EINTR) |
1463 |
continue; |
||
1464 |
32 |
return false; |
|
1465 |
} |
||
1466 |
✗✓ | 7592 |
assert(static_cast<size_t>(retval) <= nbyte); |
1467 |
7592 |
buf = reinterpret_cast<const char *>(buf) + retval; |
|
1468 |
7592 |
nbyte -= retval; |
|
1469 |
} |
||
1470 |
7592 |
return true; |
|
1471 |
} |
||
1472 |
|||
1473 |
/** |
||
1474 |
* The contents of the iov vector might be modified by the function. |
||
1475 |
*/ |
||
1476 |
12623 |
bool SafeWriteV(int fd, struct iovec *iov, unsigned iovcnt) { |
|
1477 |
12623 |
unsigned nbytes = 0; |
|
1478 |
✓✓ | 48328 |
for (unsigned i = 0; i < iovcnt; ++i) |
1479 |
35705 |
nbytes += iov[i].iov_len; |
|
1480 |
12623 |
unsigned iov_idx = 0; |
|
1481 |
|||
1482 |
✓✗ | 25246 |
while (nbytes) { |
1483 |
12623 |
ssize_t retval = writev(fd, &iov[iov_idx], iovcnt - iov_idx); |
|
1484 |
✗✓ | 12623 |
if (retval < 0) { |
1485 |
if (errno == EINTR) |
||
1486 |
continue; |
||
1487 |
return false; |
||
1488 |
} |
||
1489 |
✗✓ | 12623 |
assert(static_cast<size_t>(retval) <= nbytes); |
1490 |
12623 |
nbytes -= retval; |
|
1491 |
|||
1492 |
12623 |
unsigned sum_written_blocks = 0; |
|
1493 |
✓✗ | 48328 |
while ((sum_written_blocks + iov[iov_idx].iov_len) <= |
1494 |
static_cast<size_t>(retval)) |
||
1495 |
{ |
||
1496 |
35705 |
sum_written_blocks += iov[iov_idx].iov_len; |
|
1497 |
35705 |
iov_idx++; |
|
1498 |
✓✓ | 35705 |
if (iov_idx == iovcnt) { |
1499 |
✗✓ | 12623 |
assert(sum_written_blocks == static_cast<size_t>(retval)); |
1500 |
12623 |
return true; |
|
1501 |
} |
||
1502 |
} |
||
1503 |
unsigned offset = retval - sum_written_blocks; |
||
1504 |
iov[iov_idx].iov_len -= offset; |
||
1505 |
iov[iov_idx].iov_base = |
||
1506 |
reinterpret_cast<char *>(iov[iov_idx].iov_base) + offset; |
||
1507 |
} |
||
1508 |
|||
1509 |
return true; |
||
1510 |
} |
||
1511 |
|||
1512 |
|||
1513 |
/** |
||
1514 |
* Deal with EINTR and partial reads. |
||
1515 |
*/ |
||
1516 |
243865 |
ssize_t SafeRead(int fd, void *buf, size_t nbyte) { |
|
1517 |
243865 |
ssize_t total_bytes = 0; |
|
1518 |
✓✓ | 733060 |
while (nbyte) { |
1519 |
245553 |
ssize_t retval = read(fd, buf, nbyte); |
|
1520 |
✓✓ | 245545 |
if (retval < 0) { |
1521 |
✗✓ | 2 |
if (errno == EINTR) |
1522 |
continue; |
||
1523 |
2 |
return -1; |
|
1524 |
✓✓ | 245543 |
} else if (retval == 0) { |
1525 |
213 |
return total_bytes; |
|
1526 |
} |
||
1527 |
✗✓ | 245330 |
assert(static_cast<size_t>(retval) <= nbyte); |
1528 |
245330 |
buf = reinterpret_cast<char *>(buf) + retval; |
|
1529 |
245330 |
nbyte -= retval; |
|
1530 |
245330 |
total_bytes += retval; |
|
1531 |
} |
||
1532 |
243642 |
return total_bytes; |
|
1533 |
} |
||
1534 |
|||
1535 |
|||
1536 |
/** |
||
1537 |
* Pull file contents into a string |
||
1538 |
*/ |
||
1539 |
3 |
bool SafeReadToString(int fd, std::string *final_result) { |
|
1540 |
✗✓ | 3 |
if (!final_result) {return false;} |
1541 |
|||
1542 |
3 |
std::string tmp_result; |
|
1543 |
static const int buf_size = 4096; |
||
1544 |
char buf[4096]; |
||
1545 |
3 |
ssize_t total_bytes = -1; |
|
1546 |
✗✓ | 2 |
do { |
1547 |
3 |
total_bytes = SafeRead(fd, buf, buf_size); |
|
1548 |
✓✓ | 3 |
if (total_bytes < 0) {return false;} |
1549 |
2 |
tmp_result.append(buf, total_bytes); |
|
1550 |
} while (total_bytes == buf_size); |
||
1551 |
2 |
final_result->swap(tmp_result); |
|
1552 |
2 |
return true; |
|
1553 |
} |
||
1554 |
|||
1555 |
140 |
bool SafeWriteToFile(const std::string &content, |
|
1556 |
const std::string &path, |
||
1557 |
int mode) { |
||
1558 |
140 |
int fd = open(path.c_str(), O_WRONLY | O_CREAT, mode); |
|
1559 |
✗✓ | 140 |
if (fd < 0) return false; |
1560 |
140 |
bool retval = SafeWrite(fd, content.data(), content.size()); |
|
1561 |
140 |
close(fd); |
|
1562 |
140 |
return retval; |
|
1563 |
} |
||
1564 |
|||
1565 |
#ifdef CVMFS_NAMESPACE_GUARD |
||
1566 |
} // namespace CVMFS_NAMESPACE_GUARD |
||
1567 |
#endif |
Generated by: GCOVR (Version 4.1) |