GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/util/posix.cc Lines: 602 769 78.3 %
Date: 2019-02-03 02:48:13 Branches: 310 548 56.6 %

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