CernVM-FS  2.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
posix.cc
Go to the documentation of this file.
1 
7 #ifndef __STDC_FORMAT_MACROS
8 // NOLINTNEXTLINE
9 #define __STDC_FORMAT_MACROS
10 #endif
11 
12 #include "cvmfs_config.h"
13 #include "posix.h"
14 
15 #include <arpa/inet.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <grp.h>
19 #include <inttypes.h>
20 #include <netinet/in.h>
21 #include <pthread.h>
22 #include <pwd.h>
23 #include <signal.h>
24 #include <stdint.h>
25 #include <sys/resource.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #ifdef __APPLE__
29 #include <sys/mount.h> // for statfs()
30 #else
31 #include <sys/statfs.h>
32 #endif
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/un.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38 // If valgrind headers are present on the build system, then we can detect
39 // valgrind at runtime.
40 #ifdef HAS_VALGRIND_HEADERS
41 #include <valgrind/valgrind.h>
42 #endif
43 
44 #include <algorithm>
45 #include <cassert>
46 #include <cstdio>
47 #include <cstring>
48 #include <map>
49 #include <set>
50 #include <string>
51 #include <vector>
52 
53 #include "fs_traversal.h"
54 #include "logging.h"
55 #include "platform.h"
56 #include "util/algorithm.h"
57 #include "util/exception.h"
58 #include "util/string.h"
59 #include "util_concurrency.h"
60 
61 //using namespace std; // NOLINT
62 
63 #ifndef ST_RDONLY
64 // On Linux, this is in sys/statvfs.h
65 // On macOS, this flag is called MNT_RDONLY /usr/include/sys/mount.h
66 #define ST_RDONLY 1
67 #endif
68 
69 // Older Linux glibc versions do not provide the f_flags member in struct statfs
70 #define CVMFS_HAS_STATFS_F_FLAGS
71 #ifndef __APPLE__
72 #ifdef __GLIBC_MINOR__
73 #if __GLIBC_MINOR__ < 12
74 #undef CVMFS_HAS_STATFS_F_FLAGS
75 #endif
76 #endif
77 #endif
78 
79 // Work around missing clearenv()
80 #ifdef __APPLE__
81 extern "C" {
82 extern char **environ;
83 }
84 #endif
85 
86 #ifdef CVMFS_NAMESPACE_GUARD
87 namespace CVMFS_NAMESPACE_GUARD {
88 #endif
89 
90 static pthread_mutex_t getumask_mutex = PTHREAD_MUTEX_INITIALIZER;
91 
92 
96 std::string MakeCanonicalPath(const std::string &path) {
97  if (path.length() == 0) return path;
98 
99  if (path[path.length()-1] == '/') {
100  return path.substr(0, path.length()-1);
101  } else {
102  return path;
103  }
104 }
105 
112  const std::string &path,
113  std::string *dirname,
114  std::string *filename)
115 {
116  size_t dir_sep = path.rfind('/');
117  if (dir_sep != std::string::npos) {
118  *dirname = path.substr(0, dir_sep);
119  *filename = path.substr(dir_sep+1);
120  } else {
121  *dirname = ".";
122  *filename = path;
123  }
124 }
125 
126 
130 std::string GetParentPath(const std::string &path) {
131  const std::string::size_type idx = path.find_last_of('/');
132  if (idx != std::string::npos) {
133  return path.substr(0, idx);
134  } else {
135  return "";
136  }
137 }
138 
139 
144  int length = static_cast<int>(path.GetLength());
145  if (length == 0)
146  return path;
147  const char *chars = path.GetChars();
148 
149  for (int i = length-1; i >= 0; --i) {
150  if (chars[i] == '/')
151  return PathString(chars, i);
152  }
153 
154  return path;
155 }
156 
157 
161 std::string GetFileName(const std::string &path) {
162  const std::string::size_type idx = path.find_last_of('/');
163  if (idx != std::string::npos) {
164  return path.substr(idx+1);
165  } else {
166  return path;
167  }
168 }
169 
170 
172  NameString name;
173  int length = static_cast<int>(path.GetLength());
174  const char *chars = path.GetChars();
175 
176  int i;
177  for (i = length-1; i >= 0; --i) {
178  if (chars[i] == '/')
179  break;
180  }
181  i++;
182  if (i < length) {
183  name.Append(chars+i, length-i);
184  }
185 
186  return name;
187 }
188 
189 
190 bool IsAbsolutePath(const std::string &path) {
191  return (!path.empty() && path[0] == '/');
192 }
193 
194 
195 std::string GetAbsolutePath(const std::string &path) {
196  if (IsAbsolutePath(path))
197  return path;
198 
199  return GetCurrentWorkingDirectory() + "/" + path;
200 }
201 
202 
203 bool IsHttpUrl(const std::string &path) {
204  if (path.length() < 7) {
205  return false;
206  }
207 
208  std::string prefix = path.substr(0, 8);
209  std::transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
210 
211  return prefix.substr(0, 7) == "http://" || prefix == "https://";
212 }
213 
214 
215 FileSystemInfo GetFileSystemInfo(const std::string &path) {
216  FileSystemInfo result;
217 
218  struct statfs info;
219  int retval = statfs(path.c_str(), &info);
220  if (retval != 0)
221  return result;
222 
223  switch (info.f_type) {
224  case kFsTypeAutofs:
225  result.type = kFsTypeAutofs;
226  break;
227  case kFsTypeNFS:
228  result.type = kFsTypeNFS;
229  break;
230  case kFsTypeProc:
231  result.type = kFsTypeProc;
232  break;
233  case kFsTypeBeeGFS:
234  result.type = kFsTypeBeeGFS;
235  break;
236  default:
237  result.type = kFsTypeUnknown;
238  }
239 
240 #ifdef CVMFS_HAS_STATFS_F_FLAGS
241  if (info.f_flags & ST_RDONLY)
242  result.is_rdonly = true;
243 #else
244  // On old Linux systems, fall back to access()
245  retval = access(path.c_str(), W_OK);
246  result.is_rdonly = (retval != 0);
247 #endif
248 
249 
250 
251  return result;
252 }
253 
254 
255 std::string ReadSymlink(const std::string &path) {
256  // TODO(jblomer): avoid PATH_MAX
257  char buf[PATH_MAX + 1];
258  ssize_t nchars = readlink(path.c_str(), buf, PATH_MAX);
259  if (nchars >= 0) {
260  buf[nchars] = '\0';
261  return std::string(buf);
262  }
263  return "";
264 }
265 
266 
271 std::string ResolvePath(const std::string &path) {
272  if (path.empty() || (path == "/"))
273  return "/";
274  std::string name = GetFileName(path);
275  std::string result = name;
276  if (name != path) {
277  // There is a parent path of 'path'
278  std::string parent = ResolvePath(GetParentPath(path));
279  result = parent + (parent == "/" ? "" : "/") + name;
280  }
281  char *real_result = realpath(result.c_str(), NULL);
282  if (real_result) {
283  result = real_result;
284  free(real_result);
285  }
286  if (SymlinkExists(result)) {
287  char buf[PATH_MAX + 1];
288  ssize_t nchars = readlink(result.c_str(), buf, PATH_MAX);
289  if (nchars >= 0) {
290  buf[nchars] = '\0';
291  result = buf;
292  }
293  }
294  return result;
295 }
296 
297 
298 bool IsMountPoint(const std::string &path) {
299  std::vector<std::string> mount_list = platform_mountlist();
300  std::string resolved_path = ResolvePath(path);
301  for (unsigned i = 0; i < mount_list.size(); ++i) {
302  if (mount_list[i] == resolved_path)
303  return true;
304  }
305  return false;
306 }
307 
308 
313  const std::string &path,
314  const int mode,
315  const bool ignore_failure)
316 {
317  int fd = open(path.c_str(), O_CREAT, mode);
318  if (fd >= 0) {
319  close(fd);
320  return;
321  }
322  if (ignore_failure)
323  return;
324  PANIC(NULL);
325 }
326 
327 
331 static std::string MakeShortSocketLink(const std::string &path) {
332  struct sockaddr_un sock_addr;
333  unsigned max_length = sizeof(sock_addr.sun_path);
334 
335  std::string result;
336  std::string tmp_path = CreateTempDir("/tmp/cvmfs");
337  if (tmp_path.empty())
338  return "";
339  std::string link = tmp_path + "/l";
340  result = link + "/" + GetFileName(path);
341  if (result.length() >= max_length) {
342  rmdir(tmp_path.c_str());
343  return "";
344  }
345  int retval = symlink(GetParentPath(path).c_str(), link.c_str());
346  if (retval != 0) {
347  rmdir(tmp_path.c_str());
348  return "";
349  }
350  return result;
351 }
352 
353 static void RemoveShortSocketLink(const std::string &short_path) {
354  std::string link = GetParentPath(short_path);
355  unlink(link.c_str());
356  rmdir(GetParentPath(link).c_str());
357 }
358 
359 
363 int MakeSocket(const std::string &path, const int mode) {
364  std::string short_path(path);
365  struct sockaddr_un sock_addr;
366  if (path.length() >= sizeof(sock_addr.sun_path)) {
367  // Socket paths are limited to 108 bytes (on some systems to 92 bytes),
368  // try working around
369  short_path = MakeShortSocketLink(path);
370  if (short_path.empty())
371  return -1;
372  }
373  sock_addr.sun_family = AF_UNIX;
374  strncpy(sock_addr.sun_path, short_path.c_str(),
375  sizeof(sock_addr.sun_path));
376 
377  const int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
378  assert(socket_fd != -1);
379 
380 #ifndef __APPLE__
381  // fchmod on a socket is not allowed under Mac OS X
382  // using default 0770 here
383  if (fchmod(socket_fd, mode) != 0)
384  goto make_socket_failure;
385 #endif
386 
387  if (bind(socket_fd, reinterpret_cast<struct sockaddr *>(&sock_addr),
388  sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path)) < 0)
389  {
390  if ((errno == EADDRINUSE) && (unlink(path.c_str()) == 0)) {
391  // Second try, perhaps the file was left over
392  if (bind(socket_fd, reinterpret_cast<struct sockaddr *>(&sock_addr),
393  sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path)) < 0)
394  {
395  LogCvmfs(kLogCvmfs, kLogDebug, "binding socket failed (%d)", errno);
396  goto make_socket_failure;
397  }
398  } else {
399  LogCvmfs(kLogCvmfs, kLogDebug, "binding socket failed (%d)", errno);
400  goto make_socket_failure;
401  }
402  }
403 
404  if (short_path != path)
405  RemoveShortSocketLink(short_path);
406 
407  return socket_fd;
408 
409  make_socket_failure:
410  close(socket_fd);
411  if (short_path != path)
412  RemoveShortSocketLink(short_path);
413  return -1;
414 }
415 
416 
421 int MakeTcpEndpoint(const std::string &ipv4_address, int portno) {
422  const int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
423  assert(socket_fd != -1);
424  const int on = 1;
425  int retval = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
426  assert(retval == 0);
427 
428  struct sockaddr_in endpoint_addr;
429  memset(&endpoint_addr, 0, sizeof(endpoint_addr));
430  endpoint_addr.sin_family = AF_INET;
431  if (ipv4_address.empty()) {
432  endpoint_addr.sin_addr.s_addr = INADDR_ANY;
433  } else {
434  retval = inet_aton(ipv4_address.c_str(), &(endpoint_addr.sin_addr));
435  if (retval == 0) {
436  LogCvmfs(kLogCvmfs, kLogDebug, "invalid IPv4 address");
437  close(socket_fd);
438  return -1;
439  }
440  }
441  endpoint_addr.sin_port = htons(portno);
442 
443  retval = bind(socket_fd, reinterpret_cast<struct sockaddr *>(&endpoint_addr),
444  sizeof(endpoint_addr));
445  if (retval < 0) {
446  LogCvmfs(kLogCvmfs, kLogDebug, "binding TCP endpoint failed (%d)", errno);
447  close(socket_fd);
448  return -1;
449  }
450  return socket_fd;
451 }
452 
453 
459 int ConnectSocket(const std::string &path) {
460  std::string short_path(path);
461  struct sockaddr_un sock_addr;
462  if (path.length() >= sizeof(sock_addr.sun_path)) {
463  // Socket paths are limited to 108 bytes (on some systems to 92 bytes),
464  // try working around
465  short_path = MakeShortSocketLink(path);
466  if (short_path.empty())
467  return -1;
468  }
469  sock_addr.sun_family = AF_UNIX;
470  strncpy(sock_addr.sun_path, short_path.c_str(), sizeof(sock_addr.sun_path));
471 
472  const int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
473  assert(socket_fd != -1);
474 
475  int retval =
476  connect(socket_fd, reinterpret_cast<struct sockaddr *>(&sock_addr),
477  sizeof(sock_addr.sun_family) + sizeof(sock_addr.sun_path));
478  if (short_path != path)
479  RemoveShortSocketLink(short_path);
480 
481  if (retval < 0) {
482  close(socket_fd);
483  return -1;
484  }
485 
486  return socket_fd;
487 }
488 
489 
493 int ConnectTcpEndpoint(const std::string &ipv4_address, int portno) {
494  const int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
495  assert(socket_fd != -1);
496 
497  struct sockaddr_in endpoint_addr;
498  memset(&endpoint_addr, 0, sizeof(endpoint_addr));
499  endpoint_addr.sin_family = AF_INET;
500  int retval = inet_aton(ipv4_address.c_str(), &(endpoint_addr.sin_addr));
501  if (retval == 0) {
502  LogCvmfs(kLogCvmfs, kLogDebug, "invalid IPv4 address");
503  close(socket_fd);
504  return -1;
505  }
506  endpoint_addr.sin_port = htons(portno);
507 
508  retval =
509  connect(socket_fd, reinterpret_cast<struct sockaddr *>(&endpoint_addr),
510  sizeof(endpoint_addr));
511  if (retval != 0) {
512  LogCvmfs(kLogCvmfs, kLogDebug, "failed to connect to TCP endpoint (%d)",
513  errno);
514  close(socket_fd);
515  return -1;
516  }
517  return socket_fd;
518 }
519 
520 
524 void MakePipe(int pipe_fd[2]) {
525  int retval = pipe(pipe_fd);
526  assert(retval == 0);
527 }
528 
529 
533 void WritePipe(int fd, const void *buf, size_t nbyte) {
534  ssize_t num_bytes;
535  do {
536  num_bytes = write(fd, buf, nbyte);
537  } while ((num_bytes < 0) && (errno == EINTR));
538  assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
539 }
540 
541 
545 void ReadPipe(int fd, void *buf, size_t nbyte) {
546  ssize_t num_bytes;
547  do {
548  num_bytes = read(fd, buf, nbyte);
549  } while ((num_bytes < 0) && (errno == EINTR));
550  assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
551 }
552 
553 
557 void ReadHalfPipe(int fd, void *buf, size_t nbyte) {
558  ssize_t num_bytes;
559  unsigned i = 0;
560  unsigned backoff_ms = 1;
561  const unsigned max_backoff_ms = 256;
562  do {
563  // When the writer is not connected, this takes ~200-300ns per call as per
564  // micro benchmarks
565  num_bytes = read(fd, buf, nbyte);
566  if ((num_bytes < 0) && (errno == EINTR))
567  continue;
568  i++;
569  // Start backing off when the busy loop reaches the ballpark of 1ms
570  if ((i > 3000) && (num_bytes == 0)) {
571  // The BackoffThrottle would pull in too many dependencies
572  SafeSleepMs(backoff_ms);
573  if (backoff_ms < max_backoff_ms) backoff_ms *= 2;
574  }
575  } while (num_bytes == 0);
576  assert((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte));
577 }
578 
579 
583 void ClosePipe(int pipe_fd[2]) {
584  close(pipe_fd[0]);
585  close(pipe_fd[1]);
586 }
587 
588 
593 bool DiffTree(const std::string &path_a, const std::string &path_b) {
594  int retval;
595  std::vector<std::string> ls_a;
596  std::vector<std::string> ls_b;
597  std::vector<std::string> subdirs;
598 
599  DIR *dirp_a = opendir(path_a.c_str());
600  if (dirp_a == NULL) return false;
601  DIR *dirp_b = opendir(path_b.c_str());
602  if (dirp_b == NULL) {
603  closedir(dirp_a);
604  return false;
605  }
606 
607  platform_dirent64 *dirent;
608  while ((dirent = platform_readdir(dirp_a))) {
609  const std::string name(dirent->d_name);
610  if ((name == ".") || (name == ".."))
611  continue;
612  const std::string path = path_a + "/" + name;
613  ls_a.push_back(path);
614 
615  platform_stat64 info;
616  retval = platform_lstat(path.c_str(), &info);
617  if (retval != 0) {
618  closedir(dirp_a);
619  closedir(dirp_b);
620  return false;
621  }
622  if (S_ISDIR(info.st_mode)) subdirs.push_back(name);
623  }
624  while ((dirent = platform_readdir(dirp_b))) {
625  const std::string name(dirent->d_name);
626  if ((name == ".") || (name == ".."))
627  continue;
628  const std::string path = path_b + "/" + name;
629  ls_b.push_back(path);
630  }
631  closedir(dirp_a);
632  closedir(dirp_b);
633 
634  sort(ls_a.begin(), ls_a.end());
635  sort(ls_b.begin(), ls_b.end());
636  if (ls_a.size() != ls_b.size())
637  return false;
638  for (unsigned i = 0; i < ls_a.size(); ++i) {
639  if (GetFileName(ls_a[i]) != GetFileName(ls_b[i])) return false;
640  platform_stat64 info_a;
641  platform_stat64 info_b;
642  retval = platform_lstat(ls_a[i].c_str(), &info_a);
643  if (retval != 0) return false;
644  retval = platform_lstat(ls_b[i].c_str(), &info_b);
645  if (retval != 0) return false;
646  if ((info_a.st_mode != info_b.st_mode) ||
647  (info_a.st_uid != info_b.st_uid) ||
648  (info_a.st_gid != info_b.st_gid) ||
649  (info_a.st_size != info_b.st_size))
650  {
651  return false;
652  }
653  }
654 
655  for (unsigned i = 0; i < subdirs.size(); ++i) {
656  bool retval_subtree = DiffTree(path_a + "/" + subdirs[i],
657  path_b + "/" + subdirs[i]);
658  if (!retval_subtree) return false;
659  }
660 
661  return true;
662 }
663 
664 
668 void Nonblock2Block(int filedes) {
669  int flags = fcntl(filedes, F_GETFL);
670  assert(flags != -1);
671  int retval = fcntl(filedes, F_SETFL, flags & ~O_NONBLOCK);
672  assert(retval != -1);
673 }
674 
675 
679 void Block2Nonblock(int filedes) {
680  int flags = fcntl(filedes, F_GETFL);
681  assert(flags != -1);
682  int retval = fcntl(filedes, F_SETFL, flags | O_NONBLOCK);
683  assert(retval != -1);
684 }
685 
686 
691 void SendMsg2Socket(const int fd, const std::string &msg) {
692  (void)send(fd, &msg[0], msg.length(), MSG_NOSIGNAL);
693 }
694 
700 bool SendFd2Socket(int socket_fd, int passing_fd) {
701  union {
702  // Make sure that ctrl_msg is properly aligned.
703  struct cmsghdr align;
704  // Buffer large enough to store the file descriptor (ancillary data)
705  unsigned char buf[CMSG_SPACE(sizeof(int))];
706  } ctrl_msg;
707 
708  memset(ctrl_msg.buf, 0, sizeof(ctrl_msg.buf));
709 
710  struct msghdr msgh;
711  msgh.msg_name = NULL;
712  msgh.msg_namelen = 0;
713 
714  unsigned char dummy = 0;
715  struct iovec iov;
716  iov.iov_base = &dummy;
717  iov.iov_len = 1;
718  msgh.msg_iov = &iov;
719  msgh.msg_iovlen = 1;
720 
721  msgh.msg_control = ctrl_msg.buf;
722  msgh.msg_controllen = sizeof(ctrl_msg.buf);
723  struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh);
724  cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
725  cmsgp->cmsg_level = SOL_SOCKET;
726  cmsgp->cmsg_type = SCM_RIGHTS;
727  memcpy(CMSG_DATA(cmsgp), &passing_fd, sizeof(int));
728 
729  ssize_t retval = sendmsg(socket_fd, &msgh, 0);
730  return (retval != -1);
731 }
732 
733 
740 int RecvFdFromSocket(int msg_fd) {
741  union {
742  // Make sure that ctrl_msg is properly aligned.
743  struct cmsghdr align;
744  // Buffer large enough to store the file descriptor (ancillary data)
745  unsigned char buf[CMSG_SPACE(sizeof(int))];
746  } ctrl_msg;
747 
748  memset(ctrl_msg.buf, 0, sizeof(ctrl_msg.buf));
749 
750  struct msghdr msgh;
751  msgh.msg_name = NULL;
752  msgh.msg_namelen = 0;
753 
754  unsigned char dummy;
755  struct iovec iov;
756  iov.iov_base = &dummy;
757  iov.iov_len = 1;
758  msgh.msg_iov = &iov;
759  msgh.msg_iovlen = 1;
760 
761  msgh.msg_control = ctrl_msg.buf;
762  msgh.msg_controllen = sizeof(ctrl_msg.buf);
763 
764  ssize_t retval = recvmsg(msg_fd, &msgh, 0);
765  if (retval == -1)
766  return -errno;
767 
768  struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh);
769  assert(cmsgp != NULL);
770  if (cmsgp->cmsg_len != CMSG_LEN(sizeof(int)))
771  return -ERANGE;
772  assert(cmsgp->cmsg_level == SOL_SOCKET);
773  assert(cmsgp->cmsg_type == SCM_RIGHTS);
774 
775  int passing_fd;
776  memcpy(&passing_fd, CMSG_DATA(cmsgp), sizeof(int));
777  assert(passing_fd >= 0);
778  return passing_fd;
779 }
780 
781 
785 bool SwitchCredentials(const uid_t uid, const gid_t gid,
786  const bool temporarily)
787 {
788  LogCvmfs(kLogCvmfs, kLogDebug, "current credentials uid %d gid %d "
789  "euid %d egid %d, switching to %d %d (temp: %d)",
790  getuid(), getgid(), geteuid(), getegid(), uid, gid, temporarily);
791  int retval = 0;
792  if (temporarily) {
793  if (gid != getegid())
794  retval = setegid(gid);
795  if ((retval == 0) && (uid != geteuid()))
796  retval = seteuid(uid);
797  } else {
798  // If effective uid is not root, we must first gain root access back
799  if ((getuid() == 0) && (getuid() != geteuid())) {
800  retval = SwitchCredentials(0, getgid(), true);
801  if (!retval)
802  return false;
803  }
804  retval = setgid(gid) || setuid(uid);
805  }
806  LogCvmfs(kLogCvmfs, kLogDebug, "switch credentials result %d (%d)",
807  retval, errno);
808  return retval == 0;
809 }
810 
811 
815 bool FileExists(const std::string &path) {
816  platform_stat64 info;
817  return ((platform_lstat(path.c_str(), &info) == 0) &&
818  S_ISREG(info.st_mode));
819 }
820 
821 
825 int64_t GetFileSize(const std::string &path) {
826  platform_stat64 info;
827  int retval = platform_stat(path.c_str(), &info);
828  if (retval != 0)
829  return -1;
830  return info.st_size;
831 }
832 
833 
837 bool DirectoryExists(const std::string &path) {
838  platform_stat64 info;
839  return ((platform_lstat(path.c_str(), &info) == 0) &&
840  S_ISDIR(info.st_mode));
841 }
842 
843 
847 bool SymlinkExists(const std::string &path) {
848  platform_stat64 info;
849  return ((platform_lstat(path.c_str(), &info) == 0) &&
850  S_ISLNK(info.st_mode));
851 }
852 
853 
857 bool SymlinkForced(const std::string &src, const std::string &dest) {
858  int retval = unlink(dest.c_str());
859  if ((retval != 0) && (errno != ENOENT))
860  return false;
861  retval = symlink(src.c_str(), dest.c_str());
862  return retval == 0;
863 }
864 
865 
871  const std::string &path,
872  const mode_t mode,
873  bool verify_writable)
874 {
875  if (path == "") return false;
876 
877  int retval = mkdir(path.c_str(), mode);
878  if (retval == 0) return true;
879 
880  if ((errno == ENOENT) &&
881  (MkdirDeep(GetParentPath(path), mode, verify_writable)))
882  {
883  return MkdirDeep(path, mode, verify_writable);
884  }
885 
886  if (errno == EEXIST) {
887  platform_stat64 info;
888  if ((platform_stat(path.c_str(), &info) == 0) && S_ISDIR(info.st_mode)) {
889  if (verify_writable) {
890  retval = utimes(path.c_str(), NULL);
891  if (retval == 0)
892  return true;
893  } else {
894  return true;
895  }
896  }
897  }
898 
899  return false;
900 }
901 
902 
906 bool MakeCacheDirectories(const std::string &path, const mode_t mode) {
907  const std::string canonical_path = MakeCanonicalPath(path);
908 
909  std::string this_path = canonical_path + "/quarantaine";
910  if (!MkdirDeep(this_path, mode, false)) return false;
911 
912  this_path = canonical_path + "/ff";
913 
914  platform_stat64 stat_info;
915  if (platform_stat(this_path.c_str(), &stat_info) != 0) {
916  this_path = canonical_path + "/txn";
917  if (!MkdirDeep(this_path, mode, false))
918  return false;
919  for (int i = 0; i <= 0xff; i++) {
920  char hex[4];
921  snprintf(hex, sizeof(hex), "%02x", i);
922  this_path = canonical_path + "/" + std::string(hex);
923  if (!MkdirDeep(this_path, mode, false))
924  return false;
925  }
926  }
927  return true;
928 }
929 
930 
937 int TryLockFile(const std::string &path) {
938  const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600);
939  if (fd_lockfile < 0)
940  return -1;
941 
942  if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) {
943  close(fd_lockfile);
944  if (errno != EWOULDBLOCK)
945  return -1;
946  return -2;
947  }
948 
949  return fd_lockfile;
950 }
951 
952 
959 int WritePidFile(const std::string &path) {
960  const int fd = open(path.c_str(), O_CREAT | O_RDWR, 0600);
961  if (fd < 0)
962  return -1;
963  if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
964  close(fd);
965  if (errno != EWOULDBLOCK)
966  return -1;
967  return -2;
968  }
969 
970  // Don't leak the file descriptor to exec'd children
971  int flags = fcntl(fd, F_GETFD);
972  assert(flags != -1);
973  flags |= FD_CLOEXEC;
974  flags = fcntl(fd, F_SETFD, flags);
975  assert(flags != -1);
976 
977  char buf[64];
978 
979  snprintf(buf, sizeof(buf), "%" PRId64 "\n", static_cast<uint64_t>(getpid()));
980  bool retval =
981  (ftruncate(fd, 0) == 0) && SafeWrite(fd, buf, strlen(buf));
982  if (!retval) {
983  UnlockFile(fd);
984  return -1;
985  }
986  return fd;
987 }
988 
989 
995 int LockFile(const std::string &path) {
996  const int fd_lockfile = open(path.c_str(), O_RDONLY | O_CREAT, 0600);
997  if (fd_lockfile < 0)
998  return -1;
999 
1000 
1001  if (flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) {
1002  if (errno != EWOULDBLOCK) {
1003  close(fd_lockfile);
1004  return -1;
1005  }
1006  LogCvmfs(kLogCvmfs, kLogSyslog, "another process holds %s, waiting.",
1007  path.c_str());
1008  if (flock(fd_lockfile, LOCK_EX) != 0) {
1009  close(fd_lockfile);
1010  return -1;
1011  }
1012  LogCvmfs(kLogCvmfs, kLogSyslog, "lock %s acquired", path.c_str());
1013  }
1014 
1015  return fd_lockfile;
1016 }
1017 
1018 
1019 void UnlockFile(const int filedes) {
1020  int retval = flock(filedes, LOCK_UN);
1021  assert(retval == 0);
1022  close(filedes);
1023 }
1024 
1025 
1029 FILE *CreateTempFile(const std::string &path_prefix, const int mode,
1030  const char *open_flags, std::string *final_path)
1031 {
1032  *final_path = path_prefix + ".XXXXXX";
1033  char *tmp_file = strdupa(final_path->c_str());
1034  int tmp_fd = mkstemp(tmp_file);
1035  if (tmp_fd < 0) {
1036  return NULL;
1037  }
1038  if (fchmod(tmp_fd, mode) != 0) {
1039  close(tmp_fd);
1040  return NULL;
1041  }
1042 
1043  *final_path = tmp_file;
1044  FILE *tmp_fp = fdopen(tmp_fd, open_flags);
1045  if (!tmp_fp) {
1046  close(tmp_fd);
1047  unlink(tmp_file);
1048  return NULL;
1049  }
1050 
1051  return tmp_fp;
1052 }
1053 
1054 
1058 std::string CreateTempPath(const std::string &path_prefix, const int mode) {
1059  std::string result;
1060  FILE *f = CreateTempFile(path_prefix, mode, "w", &result);
1061  if (!f)
1062  return "";
1063  fclose(f);
1064  return result;
1065 }
1066 
1067 
1071 std::string CreateTempDir(const std::string &path_prefix) {
1072  std::string dir = path_prefix + ".XXXXXX";
1073  char *tmp_dir = strdupa(dir.c_str());
1074  tmp_dir = mkdtemp(tmp_dir);
1075  if (tmp_dir == NULL)
1076  return "";
1077  return std::string(tmp_dir);
1078 }
1079 
1080 
1085  char cwd[PATH_MAX];
1086  return (getcwd(cwd, sizeof(cwd)) != NULL) ? std::string(cwd) : std::string();
1087 }
1088 
1089 
1094  public:
1095  bool success;
1097  success = true;
1098  }
1099  void RemoveFile(const std::string &parent_path, const std::string &name) {
1100  int retval = unlink((parent_path + "/" + name).c_str());
1101  if (retval != 0)
1102  success = false;
1103  }
1104  void RemoveDir(const std::string &parent_path, const std::string &name) {
1105  int retval = rmdir((parent_path + "/" + name).c_str());
1106  if (retval != 0)
1107  success = false;
1108  }
1109  bool TryRemoveDir(const std::string &parent_path, const std::string &name) {
1110  int retval = rmdir((parent_path + "/" + name).c_str());
1111  return (retval != 0);
1112  }
1113 };
1114 
1115 
1119 bool RemoveTree(const std::string &path) {
1120  platform_stat64 info;
1121  int retval = platform_lstat(path.c_str(), &info);
1122  if (retval != 0)
1123  return errno == ENOENT;
1124  if (!S_ISDIR(info.st_mode))
1125  return false;
1126 
1127  RemoveTreeHelper *remove_tree_helper = new RemoveTreeHelper();
1128  FileSystemTraversal<RemoveTreeHelper> traversal(remove_tree_helper, "",
1129  true);
1137  traversal.Recurse(path);
1138  bool result = remove_tree_helper->success;
1139  delete remove_tree_helper;
1140 
1141  return result;
1142 }
1143 
1144 
1148 std::vector<std::string> FindFilesBySuffix(
1149  const std::string &dir,
1150  const std::string &suffix)
1151 {
1152  std::vector<std::string> result;
1153  DIR *dirp = opendir(dir.c_str());
1154  if (!dirp)
1155  return result;
1156 
1157  platform_dirent64 *dirent;
1158  while ((dirent = platform_readdir(dirp))) {
1159  const std::string name(dirent->d_name);
1160  if ((name.length() >= suffix.length()) &&
1161  (name.substr(name.length()-suffix.length()) == suffix))
1162  {
1163  result.push_back(dir + "/" + name);
1164  }
1165  }
1166  closedir(dirp);
1167  std::sort(result.begin(), result.end());
1168  return result;
1169 }
1170 
1171 
1175 std::vector<std::string> FindFilesByPrefix(
1176  const std::string &dir,
1177  const std::string &prefix)
1178 {
1179  std::vector<std::string> result;
1180  DIR *dirp = opendir(dir.c_str());
1181  if (!dirp)
1182  return result;
1183 
1184  platform_dirent64 *dirent;
1185  while ((dirent = platform_readdir(dirp))) {
1186  const std::string name(dirent->d_name);
1187  if ((name.length() >= prefix.length()) &&
1188  (name.substr(0, prefix.length()) == prefix))
1189  {
1190  result.push_back(dir + "/" + name);
1191  }
1192  }
1193  closedir(dirp);
1194  std::sort(result.begin(), result.end());
1195  return result;
1196 }
1197 
1198 
1203 std::vector<std::string> FindDirectories(const std::string &parent_dir) {
1204  std::vector<std::string> result;
1205  DIR *dirp = opendir(parent_dir.c_str());
1206  if (!dirp)
1207  return result;
1208 
1209  platform_dirent64 *dirent;
1210  while ((dirent = platform_readdir(dirp))) {
1211  const std::string name(dirent->d_name);
1212  if ((name == ".") || (name == ".."))
1213  continue;
1214  const std::string path = parent_dir + "/" + name;
1215 
1216  platform_stat64 info;
1217  int retval = platform_stat(path.c_str(), &info);
1218  if (retval != 0)
1219  continue;
1220  if (S_ISDIR(info.st_mode))
1221  result.push_back(path);
1222  }
1223  closedir(dirp);
1224  sort(result.begin(), result.end());
1225  return result;
1226 }
1227 
1228 
1232 bool ListDirectory(const std::string &directory,
1233  std::vector<std::string> *names,
1234  std::vector<mode_t> *modes)
1235 {
1236  DIR *dirp = opendir(directory.c_str());
1237  if (!dirp)
1238  return false;
1239 
1240  platform_dirent64 *dirent;
1241  while ((dirent = platform_readdir(dirp))) {
1242  const std::string name(dirent->d_name);
1243  if ((name == ".") || (name == ".."))
1244  continue;
1245  const std::string path = directory + "/" + name;
1246 
1247  platform_stat64 info;
1248  int retval = platform_lstat(path.c_str(), &info);
1249  if (retval != 0) {
1250  closedir(dirp);
1251  return false;
1252  }
1253 
1254  names->push_back(name);
1255  modes->push_back(info.st_mode);
1256  }
1257  closedir(dirp);
1258 
1259  SortTeam(names, modes);
1260  return true;
1261 }
1262 
1263 
1268 std::string FindExecutable(const std::string &exe) {
1269  if (exe.empty())
1270  return "";
1271 
1272  std::vector<std::string> search_paths;
1273  if (exe[0] == '/') {
1274  search_paths.push_back(GetParentPath(exe));
1275  } else {
1276  char *path_env = getenv("PATH");
1277  if (path_env) {
1278  search_paths = SplitString(path_env, ':');
1279  }
1280  }
1281 
1282  for (unsigned i = 0; i < search_paths.size(); ++i) {
1283  if (search_paths[i].empty())
1284  continue;
1285  if (search_paths[i][0] != '/')
1286  continue;
1287 
1288  std::string path = search_paths[i] + "/" + GetFileName(exe);
1289  platform_stat64 info;
1290  int retval = platform_stat(path.c_str(), &info);
1291  if (retval != 0)
1292  continue;
1293  if (!S_ISREG(info.st_mode))
1294  continue;
1295  retval = access(path.c_str(), X_OK);
1296  if (retval != 0)
1297  continue;
1298 
1299  return path;
1300  }
1301 
1302  return "";
1303 }
1304 
1305 
1306 std::string GetUserName() {
1307  struct passwd pwd;
1308  struct passwd *result = NULL;
1309  int bufsize = 16 * 1024;
1310  char *buf = static_cast<char *>(smalloc(bufsize));
1311  while (getpwuid_r(geteuid(), &pwd, buf, bufsize, &result) == ERANGE) {
1312  bufsize *= 2;
1313  buf = static_cast<char *>(srealloc(buf, bufsize));
1314  }
1315  if (result == NULL) {
1316  free(buf);
1317  return "";
1318  }
1319  std::string user_name = pwd.pw_name;
1320  free(buf);
1321  return user_name;
1322 }
1323 
1324 std::string GetShell() {
1325  struct passwd pwd;
1326  struct passwd *result = NULL;
1327  int bufsize = 16 * 1024;
1328  char *buf = static_cast<char *>(smalloc(bufsize));
1329  while (getpwuid_r(geteuid(), &pwd, buf, bufsize, &result) == ERANGE) {
1330  bufsize *= 2;
1331  buf = static_cast<char *>(srealloc(buf, bufsize));
1332  }
1333  if (result == NULL) {
1334  free(buf);
1335  return "";
1336  }
1337  std::string shell = pwd.pw_shell;
1338  free(buf);
1339  return shell;
1340 }
1341 
1345 bool GetUserNameOf(uid_t uid, std::string *username) {
1346  struct passwd pwd;
1347  struct passwd *result = NULL;
1348  int bufsize = 16 * 1024;
1349  char *buf = static_cast<char *>(smalloc(bufsize));
1350  while (getpwuid_r(uid, &pwd, buf, bufsize, &result) == ERANGE) {
1351  bufsize *= 2;
1352  buf = static_cast<char *>(srealloc(buf, bufsize));
1353  }
1354  if (result == NULL) {
1355  free(buf);
1356  return false;
1357  }
1358  if (username)
1359  *username = result->pw_name;
1360  free(buf);
1361  return true;
1362 }
1363 
1364 
1368 bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid) {
1369  struct passwd pwd;
1370  struct passwd *result = NULL;
1371  int bufsize = 16 * 1024;
1372  char *buf = static_cast<char *>(smalloc(bufsize));
1373  while (getpwnam_r(username.c_str(), &pwd, buf, bufsize, &result) == ERANGE) {
1374  bufsize *= 2;
1375  buf = static_cast<char *>(srealloc(buf, bufsize));
1376  }
1377  if (result == NULL) {
1378  free(buf);
1379  return false;
1380  }
1381  *uid = result->pw_uid;
1382  *main_gid = result->pw_gid;
1383  free(buf);
1384  return true;
1385 }
1386 
1387 
1391 bool GetGidOf(const std::string &groupname, gid_t *gid) {
1392  struct group grp;
1393  struct group *result = NULL;
1394  int bufsize = 16 * 1024;
1395  char *buf = static_cast<char *>(smalloc(bufsize));
1396  while (getgrnam_r(groupname.c_str(), &grp, buf, bufsize, &result) == ERANGE) {
1397  bufsize *= 2;
1398  buf = static_cast<char *>(srealloc(buf, bufsize));
1399  }
1400  if (result == NULL) {
1401  free(buf);
1402  return false;
1403  }
1404  *gid = result->gr_gid;
1405  free(buf);
1406  return true;
1407 }
1408 
1414 mode_t GetUmask() {
1416  const mode_t my_umask = umask(0);
1417  umask(my_umask);
1418  return my_umask;
1419 }
1420 
1421 
1425 bool AddGroup2Persona(const gid_t gid) {
1426  int ngroups = getgroups(0, NULL);
1427  if (ngroups < 0)
1428  return false;
1429  gid_t *groups = static_cast<gid_t *>(smalloc((ngroups+1) * sizeof(gid_t)));
1430  int retval = getgroups(ngroups, groups);
1431  if (retval < 0) {
1432  free(groups);
1433  return false;
1434  }
1435  for (int i = 0; i < ngroups; ++i) {
1436  if (groups[i] == gid) {
1437  free(groups);
1438  return true;
1439  }
1440  }
1441  groups[ngroups] = gid;
1442  retval = setgroups(ngroups+1, groups);
1443  free(groups);
1444  return retval == 0;
1445 }
1446 
1447 
1448 std::string GetHomeDirectory() {
1449  uid_t uid = getuid();
1450  struct passwd pwd;
1451  struct passwd *result = NULL;
1452  int bufsize = 16 * 1024;
1453  char *buf = static_cast<char *>(smalloc(bufsize));
1454  while (getpwuid_r(uid, &pwd, buf, bufsize, &result) == ERANGE) {
1455  bufsize *= 2;
1456  buf = static_cast<char *>(srealloc(buf, bufsize));
1457  }
1458  if (result == NULL) {
1459  free(buf);
1460  return "";
1461  }
1462  std::string home_dir = result->pw_dir;
1463  free(buf);
1464  return home_dir;
1465 }
1466 
1467 
1472 int SetLimitNoFile(unsigned limit_nofile) {
1473  struct rlimit rpl;
1474  memset(&rpl, 0, sizeof(rpl));
1475  getrlimit(RLIMIT_NOFILE, &rpl);
1476  if (rpl.rlim_max < limit_nofile)
1477  rpl.rlim_max = limit_nofile;
1478  rpl.rlim_cur = limit_nofile;
1479  int retval = setrlimit(RLIMIT_NOFILE, &rpl);
1480  if (retval == 0)
1481  return 0;
1482 
1483 #ifdef HAS_VALGRIND_HEADERS
1484  return RUNNING_ON_VALGRIND ? -2 : -1;
1485 #else
1486  return -1;
1487 #endif
1488 }
1489 
1490 
1494 void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit) {
1495  *soft_limit = 0;
1496  *hard_limit = 0;
1497 
1498  struct rlimit rpl;
1499  memset(&rpl, 0, sizeof(rpl));
1500  getrlimit(RLIMIT_NOFILE, &rpl);
1501  *soft_limit = rpl.rlim_cur;
1502 
1503 #ifdef __APPLE__
1504  int value = sysconf(_SC_OPEN_MAX);
1505  assert(value > 0);
1506  *hard_limit = value;
1507 #else
1508  *hard_limit = rpl.rlim_max;
1509 #endif
1510 }
1511 
1512 
1513 std::vector<LsofEntry> Lsof(const std::string &path) {
1514  std::vector<LsofEntry> result;
1515 
1516  std::vector<std::string> proc_names;
1517  std::vector<mode_t> proc_modes;
1518  ListDirectory("/proc", &proc_names, &proc_modes);
1519 
1520  for (unsigned i = 0; i < proc_names.size(); ++i) {
1521  if (!S_ISDIR(proc_modes[i]))
1522  continue;
1523  if (proc_names[i].find_first_not_of("1234567890") != std::string::npos)
1524  continue;
1525 
1526  std::vector<std::string> fd_names;
1527  std::vector<mode_t> fd_modes;
1528  std::string proc_dir = "/proc/" + proc_names[i];
1529  std::string fd_dir = proc_dir + "/fd";
1530  bool rvb = ListDirectory(fd_dir, &fd_names, &fd_modes);
1531  uid_t proc_uid = 0;
1532 
1533  // The working directory of the process requires special handling
1534  if (rvb) {
1535  platform_stat64 info;
1536  platform_stat(proc_dir.c_str(), &info);
1537  proc_uid = info.st_uid;
1538 
1539  std::string cwd = ReadSymlink(proc_dir + "/cwd");
1540  if (HasPrefix(cwd + "/", path + "/", false /* ignore_case */)) {
1541  LsofEntry entry;
1542  entry.pid = static_cast<pid_t>(String2Uint64(proc_names[i]));
1543  entry.owner = proc_uid;
1544  entry.read_only = true; // A bit sloppy but good enough for the moment
1545  entry.executable = ReadSymlink(proc_dir + "/exe");
1546  entry.path = cwd;
1547  result.push_back(entry);
1548  }
1549  }
1550 
1551  for (unsigned j = 0; j < fd_names.size(); ++j) {
1552  if (!S_ISLNK(fd_modes[j]))
1553  continue;
1554  if (fd_names[j].find_first_not_of("1234567890") != std::string::npos)
1555  continue;
1556 
1557  std::string target = ReadSymlink(fd_dir + "/" + fd_names[j]);
1558  if (!HasPrefix(target + "/", path + "/", false /* ignore_case */))
1559  continue;
1560 
1561  LsofEntry entry;
1562  entry.pid = static_cast<pid_t>(String2Uint64(proc_names[i]));
1563  entry.owner = proc_uid;
1564  entry.read_only = !((fd_modes[j] & S_IWUSR) == S_IWUSR);
1565  entry.executable = ReadSymlink(proc_dir + "/exe");
1566  entry.path = target;
1567  result.push_back(entry);
1568  }
1569  }
1570 
1571  return result;
1572 }
1573 
1574 
1575 bool ProcessExists(pid_t pid) {
1576  assert(pid > 0);
1577  int retval = kill(pid, 0);
1578  if (retval == 0)
1579  return true;
1580  return (errno != ESRCH);
1581 }
1582 
1583 
1587 void BlockSignal(int signum) {
1588  sigset_t sigset;
1589  int retval = sigemptyset(&sigset);
1590  assert(retval == 0);
1591  retval = sigaddset(&sigset, signum);
1592  assert(retval == 0);
1593  retval = pthread_sigmask(SIG_BLOCK, &sigset, NULL);
1594  assert(retval == 0);
1595 }
1596 
1597 
1602 void WaitForSignal(int signum) {
1603  int retval;
1604  do {
1605  retval = platform_sigwait(signum);
1606  } while ((retval != signum) && (errno == EINTR));
1607  assert(retval == signum);
1608 }
1609 
1610 
1616 int WaitForChild(pid_t pid, const std::vector<int> &sig_ok) {
1617  assert(pid > 0);
1618  int statloc;
1619  while (true) {
1620  pid_t retval = waitpid(pid, &statloc, 0);
1621  if (retval == -1) {
1622  if (errno == EINTR)
1623  continue;
1624  PANIC(NULL);
1625  }
1626  assert(retval == pid);
1627  break;
1628  }
1629  if (WIFEXITED(statloc))
1630  return WEXITSTATUS(statloc);
1631  if (WIFSIGNALED(statloc) && (std::find(sig_ok.begin(), sig_ok.end(),
1632  WTERMSIG(statloc)) != sig_ok.end()))
1633  return 0;
1634  return -1;
1635 }
1636 
1637 
1641 void Daemonize() {
1642  pid_t pid;
1643  int statloc;
1644  if ((pid = fork()) == 0) {
1645  int retval = setsid();
1646  assert(retval != -1);
1647  if ((pid = fork()) == 0) {
1648  int null_read = open("/dev/null", O_RDONLY);
1649  int null_write = open("/dev/null", O_WRONLY);
1650  assert((null_read >= 0) && (null_write >= 0));
1651  retval = dup2(null_read, 0);
1652  assert(retval == 0);
1653  retval = dup2(null_write, 1);
1654  assert(retval == 1);
1655  retval = dup2(null_write, 2);
1656  assert(retval == 2);
1657  close(null_read);
1658  close(null_write);
1659  LogCvmfs(kLogCvmfs, kLogDebug, "daemonized");
1660  } else {
1661  assert(pid > 0);
1662  _exit(0);
1663  }
1664  } else {
1665  assert(pid > 0);
1666  waitpid(pid, &statloc, 0);
1667  _exit(0);
1668  }
1669 }
1670 
1671 
1673  int *fd_stdin,
1674  int *fd_stdout,
1675  int *fd_stderr,
1676  const std::string &binary_path,
1677  const std::vector<std::string> &argv,
1678  const bool double_fork,
1679  pid_t *child_pid
1680 ) {
1681  int pipe_stdin[2];
1682  int pipe_stdout[2];
1683  int pipe_stderr[2];
1684  MakePipe(pipe_stdin);
1685  MakePipe(pipe_stdout);
1686  MakePipe(pipe_stderr);
1687 
1688  std::set<int> preserve_fildes;
1689  preserve_fildes.insert(0);
1690  preserve_fildes.insert(1);
1691  preserve_fildes.insert(2);
1692  std::map<int, int> map_fildes;
1693  map_fildes[pipe_stdin[0]] = 0; // Reading end of pipe_stdin
1694  map_fildes[pipe_stdout[1]] = 1; // Writing end of pipe_stdout
1695  map_fildes[pipe_stderr[1]] = 2; // Writing end of pipe_stderr
1696  std::vector<std::string> cmd_line;
1697  cmd_line.push_back(binary_path);
1698  cmd_line.insert(cmd_line.end(), argv.begin(), argv.end());
1699 
1700  if (!ManagedExec(cmd_line,
1701  preserve_fildes,
1702  map_fildes,
1703  true /* drop_credentials */,
1704  false /* clear_env */,
1705  double_fork,
1706  child_pid))
1707  {
1708  ClosePipe(pipe_stdin);
1709  ClosePipe(pipe_stdout);
1710  ClosePipe(pipe_stderr);
1711  return false;
1712  }
1713 
1714  close(pipe_stdin[0]);
1715  close(pipe_stdout[1]);
1716  close(pipe_stderr[1]);
1717  *fd_stdin = pipe_stdin[1];
1718  *fd_stdout = pipe_stdout[0];
1719  *fd_stderr = pipe_stderr[0];
1720  return true;
1721 }
1722 
1723 
1728 bool Shell(int *fd_stdin, int *fd_stdout, int *fd_stderr) {
1729  const bool double_fork = true;
1730  return ExecuteBinary(fd_stdin, fd_stdout, fd_stderr, "/bin/sh",
1731  std::vector<std::string>(), double_fork);
1732 }
1733 
1734 struct ForkFailures { // TODO(rmeusel): C++11 (type safe enum)
1735  enum Names {
1744  };
1745 
1746  static std::string ToString(const Names name) {
1747  switch (name) {
1748  case kSendPid:
1749  return "Sending PID";
1750 
1751  default:
1752  case kUnknown:
1753  return "Unknown Status";
1754  case kFailDupFd:
1755  return "Duplicate File Descriptor";
1756  case kFailGetMaxFd:
1757  return "Read maximal File Descriptor";
1758  case kFailGetFdFlags:
1759  return "Read File Descriptor Flags";
1760  case kFailSetFdFlags:
1761  return "Set File Descriptor Flags";
1762  case kFailDropCredentials:
1763  return "Lower User Permissions";
1764  case kFailExec:
1765  return "Invoking execvp()";
1766  }
1767  }
1768 };
1769 
1783 bool ManagedExec(const std::vector<std::string> &command_line,
1784  const std::set<int> &preserve_fildes,
1785  const std::map<int, int> &map_fildes,
1786  const bool drop_credentials,
1787  const bool clear_env,
1788  const bool double_fork,
1789  pid_t *child_pid)
1790 {
1791  assert(command_line.size() >= 1);
1792 
1793  Pipe pipe_fork;
1794  pid_t pid = fork();
1795  assert(pid >= 0);
1796  if (pid == 0) {
1797  pid_t pid_grand_child;
1798  int max_fd;
1799  int fd_flags;
1801 
1802  if (clear_env) {
1803 #ifdef __APPLE__
1804  environ = NULL;
1805 #else
1806  int retval = clearenv();
1807  assert(retval == 0);
1808 #endif
1809  }
1810 
1811  const char *argv[command_line.size() + 1];
1812  for (unsigned i = 0; i < command_line.size(); ++i)
1813  argv[i] = command_line[i].c_str();
1814  argv[command_line.size()] = NULL;
1815 
1816  // Child, map file descriptors
1817  for (std::map<int, int>::const_iterator i = map_fildes.begin(),
1818  iEnd = map_fildes.end(); i != iEnd; ++i)
1819  {
1820  int retval = dup2(i->first, i->second);
1821  if (retval == -1) {
1822  failed = ForkFailures::kFailDupFd;
1823  goto fork_failure;
1824  }
1825  }
1826 
1827  // Child, close file descriptors
1828  max_fd = static_cast<int>(sysconf(_SC_OPEN_MAX));
1829  if (max_fd < 0) {
1830  failed = ForkFailures::kFailGetMaxFd;
1831  goto fork_failure;
1832  }
1833  for (int fd = 0; fd < max_fd; fd++) {
1834  if ((fd != pipe_fork.write_end) && (preserve_fildes.count(fd) == 0)) {
1835  close(fd);
1836  }
1837  }
1838 
1839  // Double fork to disconnect from parent
1840  if (double_fork) {
1841  pid_grand_child = fork();
1842  assert(pid_grand_child >= 0);
1843  if (pid_grand_child != 0) _exit(0);
1844  }
1845 
1846  fd_flags = fcntl(pipe_fork.write_end, F_GETFD);
1847  if (fd_flags < 0) {
1849  goto fork_failure;
1850  }
1851  fd_flags |= FD_CLOEXEC;
1852  if (fcntl(pipe_fork.write_end, F_SETFD, fd_flags) < 0) {
1854  goto fork_failure;
1855  }
1856 
1857 #ifdef DEBUGMSG
1858  assert(setenv("__CVMFS_DEBUG_MODE__", "yes", 1) == 0);
1859 #endif
1860  if (drop_credentials && !SwitchCredentials(geteuid(), getegid(), false)) {
1862  goto fork_failure;
1863  }
1864 
1865  // retrieve the PID of the new (grand) child process and send it to the
1866  // grand father
1867  pid_grand_child = getpid();
1868  failed = ForkFailures::kSendPid;
1869  pipe_fork.Write(&failed, sizeof(failed));
1870  pipe_fork.Write(pid_grand_child);
1871 
1872  execvp(command_line[0].c_str(), const_cast<char **>(argv));
1873 
1874  failed = ForkFailures::kFailExec;
1875 
1876  fork_failure:
1877  pipe_fork.Write(&failed, sizeof(failed));
1878  _exit(1);
1879  }
1880  if (double_fork) {
1881  int statloc;
1882  waitpid(pid, &statloc, 0);
1883  }
1884 
1885  close(pipe_fork.write_end);
1886 
1887  // Either the PID or a return value is sent
1888  ForkFailures::Names status_code;
1889  bool retcode = pipe_fork.Read(&status_code, sizeof(status_code));
1890  assert(retcode);
1891  if (status_code != ForkFailures::kSendPid) {
1892  close(pipe_fork.read_end);
1893  LogCvmfs(kLogCvmfs, kLogDebug, "managed execve failed (%s)",
1894  ForkFailures::ToString(status_code).c_str());
1895  return false;
1896  }
1897 
1898  // read the PID of the spawned process if requested
1899  // (the actual read needs to be done in any case!)
1900  pid_t buf_child_pid = 0;
1901  pipe_fork.Read(&buf_child_pid);
1902  if (child_pid != NULL)
1903  *child_pid = buf_child_pid;
1904  close(pipe_fork.read_end);
1905  LogCvmfs(kLogCvmfs, kLogDebug, "execve'd %s (PID: %d)",
1906  command_line[0].c_str(),
1907  static_cast<int>(buf_child_pid));
1908  return true;
1909 }
1910 
1911 
1916 void SafeSleepMs(const unsigned ms) {
1917  struct timeval wait_for;
1918  wait_for.tv_sec = ms / 1000;
1919  wait_for.tv_usec = (ms % 1000) * 1000;
1920  select(0, NULL, NULL, NULL, &wait_for);
1921 }
1922 
1923 
1927 bool SafeWrite(int fd, const void *buf, size_t nbyte) {
1928  while (nbyte) {
1929  ssize_t retval = write(fd, buf, nbyte);
1930  if (retval < 0) {
1931  if (errno == EINTR)
1932  continue;
1933  return false;
1934  }
1935  assert(static_cast<size_t>(retval) <= nbyte);
1936  buf = reinterpret_cast<const char *>(buf) + retval;
1937  nbyte -= retval;
1938  }
1939  return true;
1940 }
1941 
1945 bool SafeWriteV(int fd, struct iovec *iov, unsigned iovcnt) {
1946  unsigned nbytes = 0;
1947  for (unsigned i = 0; i < iovcnt; ++i)
1948  nbytes += iov[i].iov_len;
1949  unsigned iov_idx = 0;
1950 
1951  while (nbytes) {
1952  ssize_t retval =
1953  writev(fd, &iov[iov_idx], static_cast<int>(iovcnt - iov_idx));
1954  if (retval < 0) {
1955  if (errno == EINTR)
1956  continue;
1957  return false;
1958  }
1959  assert(static_cast<size_t>(retval) <= nbytes);
1960  nbytes -= retval;
1961 
1962  unsigned sum_written_blocks = 0;
1963  while ((sum_written_blocks + iov[iov_idx].iov_len) <=
1964  static_cast<size_t>(retval))
1965  {
1966  sum_written_blocks += iov[iov_idx].iov_len;
1967  iov_idx++;
1968  if (iov_idx == iovcnt) {
1969  assert(sum_written_blocks == static_cast<size_t>(retval));
1970  return true;
1971  }
1972  }
1973  unsigned offset = retval - sum_written_blocks;
1974  iov[iov_idx].iov_len -= offset;
1975  iov[iov_idx].iov_base =
1976  reinterpret_cast<char *>(iov[iov_idx].iov_base) + offset;
1977  }
1978 
1979  return true;
1980 }
1981 
1982 
1986 ssize_t SafeRead(int fd, void *buf, size_t nbyte) {
1987  ssize_t total_bytes = 0;
1988  while (nbyte) {
1989  ssize_t retval = read(fd, buf, nbyte);
1990  if (retval < 0) {
1991  if (errno == EINTR)
1992  continue;
1993  return -1;
1994  } else if (retval == 0) {
1995  return total_bytes;
1996  }
1997  assert(static_cast<size_t>(retval) <= nbyte);
1998  buf = reinterpret_cast<char *>(buf) + retval;
1999  nbyte -= retval;
2000  total_bytes += retval;
2001  }
2002  return total_bytes;
2003 }
2004 
2005 
2009 bool SafeReadToString(int fd, std::string *final_result) {
2010  if (!final_result) {return false;}
2011 
2012  std::string tmp_result;
2013  static const int buf_size = 4096;
2014  char buf[4096];
2015  ssize_t total_bytes = -1;
2016  do {
2017  total_bytes = SafeRead(fd, buf, buf_size);
2018  if (total_bytes < 0) {return false;}
2019  tmp_result.append(buf, total_bytes);
2020  } while (total_bytes == buf_size);
2021  final_result->swap(tmp_result);
2022  return true;
2023 }
2024 
2025 bool SafeWriteToFile(const std::string &content,
2026  const std::string &path,
2027  int mode) {
2028  int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
2029  if (fd < 0) return false;
2030  bool retval = SafeWrite(fd, content.data(), content.size());
2031  close(fd);
2032  return retval;
2033 }
2034 
2035 
2036 #ifdef CVMFS_NAMESPACE_GUARD
2037 } // namespace CVMFS_NAMESPACE_GUARD
2038 #endif
bool MakeCacheDirectories(const std::string &path, const mode_t mode)
Definition: posix.cc:906
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
mode_t GetUmask()
Definition: posix.cc:1414
uid_t owner
Definition: posix.h:58
void RemoveFile(const std::string &parent_path, const std::string &name)
Definition: posix.cc:1099
int MakeSocket(const std::string &path, const int mode)
Definition: posix.cc:363
struct stat64 platform_stat64
bool SymlinkForced(const std::string &src, const std::string &dest)
Definition: posix.cc:857
std::string GetFileName(const std::string &path)
Definition: posix.cc:161
int MakeTcpEndpoint(const std::string &ipv4_address, int portno)
Definition: posix.cc:421
vector< string > SplitString(const string &str, const char delim, const unsigned max_chunks)
Definition: string.cc:288
std::string GetUserName()
Definition: posix.cc:1306
VoidCallback fn_new_symlink
Definition: fs_traversal.h:48
VoidCallback fn_new_character_dev
Definition: fs_traversal.h:51
void Recurse(const std::string &dir_path) const
Definition: fs_traversal.h:112
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
Definition: posix.cc:312
#define PANIC(...)
Definition: exception.h:26
bool GetUserNameOf(uid_t uid, std::string *username)
Definition: posix.cc:1345
FILE * CreateTempFile(const std::string &path_prefix, const int mode, const char *open_flags, std::string *final_path)
Definition: posix.cc:1029
int ConnectTcpEndpoint(const std::string &ipv4_address, int portno)
Definition: posix.cc:493
EFileSystemTypes type
Definition: posix.h:52
void RemoveDir(const std::string &parent_path, const std::string &name)
Definition: posix.cc:1104
bool Shell(int *fd_stdin, int *fd_stdout, int *fd_stderr)
Definition: posix.cc:1728
bool IsHttpUrl(const std::string &path)
Definition: posix.cc:203
bool ManagedExec(const std::vector< std::string > &command_line, const std::set< int > &preserve_fildes, const std::map< int, int > &map_fildes, const bool drop_credentials, const bool clear_env, const bool double_fork, pid_t *child_pid)
Definition: posix.cc:1783
static void RemoveShortSocketLink(const std::string &short_path)
Definition: posix.cc:353
void Daemonize()
Definition: posix.cc:1641
static pthread_mutex_t getumask_mutex
Definition: posix.cc:90
std::string CreateTempPath(const std::string &path_prefix, const int mode)
Definition: posix.cc:1058
#define ST_RDONLY
Definition: posix.cc:66
A simple recursion engine to abstract the recursion of directories. It provides several callback hook...
Definition: fs_traversal.h:37
bool SafeWrite(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:1927
void SendMsg2Socket(const int fd, const std::string &msg)
Definition: posix.cc:691
assert((mem||(size==0))&&"Out Of Memory")
bool SafeWriteToFile(const std::string &content, const std::string &path, int mode)
Definition: posix.cc:2025
std::string FindExecutable(const std::string &exe)
Definition: posix.cc:1268
bool SendFd2Socket(int socket_fd, int passing_fd)
Definition: posix.cc:700
VoidCallback fn_leave_dir
Definition: fs_traversal.h:46
int WaitForChild(pid_t pid, const std::vector< int > &sig_ok)
Definition: posix.cc:1616
bool TryRemoveDir(const std::string &parent_path, const std::string &name)
Definition: posix.cc:1109
std::string GetParentPath(const std::string &path)
Definition: posix.cc:130
int platform_stat(const char *path, platform_stat64 *buf)
bool AddGroup2Persona(const gid_t gid)
Definition: posix.cc:1425
void MakePipe(int pipe_fd[2])
Definition: posix.cc:524
std::vector< std::string > FindDirectories(const std::string &parent_dir)
Definition: posix.cc:1203
bool is_rdonly
Definition: posix.h:53
int SetLimitNoFile(unsigned limit_nofile)
Definition: posix.cc:1472
std::string path
Definition: posix.h:61
bool SymlinkExists(const std::string &path)
Definition: posix.cc:847
bool FileExists(const std::string &path)
Definition: posix.cc:815
std::string GetAbsolutePath(const std::string &path)
Definition: posix.cc:195
void SplitPath(const std::string &path, std::string *dirname, std::string *filename)
Definition: posix.cc:111
void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit)
Definition: posix.cc:1494
std::string executable
Definition: posix.h:60
#define strdupa(s)
Definition: platform_osx.h:285
VoidCallback fn_new_file
Definition: fs_traversal.h:47
void ReadHalfPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:557
ssize_t SafeRead(int fd, void *buf, size_t nbyte)
Definition: posix.cc:1986
FileSystemInfo GetFileSystemInfo(const std::string &path)
Definition: posix.cc:215
void Nonblock2Block(int filedes)
Definition: posix.cc:668
int platform_sigwait(const int signum)
static std::string MakeShortSocketLink(const std::string &path)
Definition: posix.cc:331
std::vector< std::string > platform_mountlist()
bool read_only
Definition: posix.h:59
int platform_lstat(const char *path, platform_stat64 *buf)
int TryLockFile(const std::string &path)
Definition: posix.cc:937
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
Definition: posix.cc:870
int LockFile(const std::string &path)
Definition: posix.cc:995
string ResolvePath(const std::string &path)
std::string GetHomeDirectory()
Definition: posix.cc:1448
void WaitForSignal(int signum)
Definition: posix.cc:1602
std::string GetShell()
Definition: posix.cc:1324
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:70
pid_t pid
Definition: posix.h:57
bool GetGidOf(const std::string &groupname, gid_t *gid)
Definition: posix.cc:1391
std::string CreateTempDir(const std::string &path_prefix)
Definition: posix.cc:1071
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:265
bool DirectoryExists(const std::string &path)
Definition: posix.cc:837
bool ExecuteBinary(int *fd_stdin, int *fd_stdout, int *fd_stderr, const std::string &binary_path, const std::vector< std::string > &argv, const bool double_fork, pid_t *child_pid)
Definition: posix.cc:1672
bool RemoveTree(const std::string &path)
Definition: posix.cc:1119
bool SafeReadToString(int fd, std::string *final_result)
Definition: posix.cc:2009
int WritePidFile(const std::string &path)
Definition: posix.cc:959
int ConnectSocket(const std::string &path)
Definition: posix.cc:459
uint64_t String2Uint64(const string &value)
Definition: string.cc:228
Definition: posix.h:185
VoidCallback fn_new_socket
Definition: fs_traversal.h:49
bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid)
Definition: posix.cc:1368
ShortString< kDefaultMaxPath, 0 > PathString
Definition: shortstring.h:190
std::vector< std::string > FindFilesByPrefix(const std::string &dir, const std::string &prefix)
Definition: posix.cc:1175
bool SwitchCredentials(const uid_t uid, const gid_t gid, const bool temporarily)
Definition: posix.cc:785
std::string ReadSymlink(const std::string &path)
Definition: posix.cc:255
Definition: mutex.h:42
std::vector< LsofEntry > Lsof(const std::string &path)
Definition: posix.cc:1513
BoolCallback fn_new_dir_prefix
Definition: fs_traversal.h:69
VoidCallback fn_new_fifo
Definition: fs_traversal.h:52
bool ListDirectory(const std::string &directory, std::vector< std::string > *names, std::vector< mode_t > *modes)
Definition: posix.cc:1232
platform_dirent64 * platform_readdir(DIR *dirp)
Definition: posix.h:56
int64_t GetFileSize(const std::string &path)
Definition: posix.cc:825
#define MSG_NOSIGNAL
Definition: platform_osx.h:53
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:1916
bool DiffTree(const std::string &path_a, const std::string &path_b)
Definition: posix.cc:593
void SortTeam(std::vector< T > *tractor, std::vector< U > *towed)
Definition: algorithm.h:67
static std::string ToString(const Names name)
Definition: posix.cc:1746
void Block2Nonblock(int filedes)
Definition: posix.cc:679
unsigned GetLength() const
Definition: shortstring.h:104
bool IsAbsolutePath(const std::string &path)
Definition: posix.cc:190
bool ProcessExists(pid_t pid)
Definition: posix.cc:1575
std::string MakeCanonicalPath(const std::string &path)
Definition: posix.cc:96
const char * GetChars() const
Definition: shortstring.h:96
void WritePipe(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:533
std::string GetCurrentWorkingDirectory()
Definition: posix.cc:1084
void ReadPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:545
std::vector< std::string > FindFilesBySuffix(const std::string &dir, const std::string &suffix)
Definition: posix.cc:1148
void ClosePipe(int pipe_fd[2])
Definition: posix.cc:583
bool IsMountPoint(const std::string &path)
Definition: posix.cc:298
int RecvFdFromSocket(int msg_fd)
Definition: posix.cc:740
bool SafeWriteV(int fd, struct iovec *iov, unsigned iovcnt)
Definition: posix.cc:1945
void UnlockFile(const int filedes)
Definition: posix.cc:1019
void BlockSignal(int signum)
Definition: posix.cc:1587
struct dirent64 platform_dirent64