w_string FileDescriptor::getOpenedPath()

in watchman/fs/FileDescriptor.cpp [250:358]


w_string FileDescriptor::getOpenedPath() const {
#if defined(F_GETPATH)
  // macOS.  The kernel interface only allows MAXPATHLEN
  char buf[MAXPATHLEN + 1];
  if (fcntl(fd_, F_GETPATH, buf) == -1) {
    throw std::system_error(
        errno, std::generic_category(), "fcntl for getOpenedPath");
  }
  return w_string(buf);
#elif defined(__linux__) || defined(__sun)
  char procpath[1024];
#if defined(__linux__)
  snprintf(
      procpath,
      sizeof(procpath),
      "/proc/%d/fd/%d",
      folly::get_cached_pid(),
      fd_);
#elif defined(__sun)
  snprintf(
      procpath,
      sizeof(procpath),
      "/proc/%d/path/%d",
      folly::get_cached_pid(),
      fd_);
#endif

  // Avoid an extra stat by speculatively attempting to read into
  // a reasonably sized buffer.
  char buf[WATCHMAN_NAME_MAX];
  auto len = readlink(procpath, buf, sizeof(buf));
  if (len == sizeof(buf)) {
    len = -1;
    // We need to stat it to discover the required length
    errno = ENAMETOOLONG;
  }

  if (len >= 0) {
    return w_string(buf, len);
  }

  if (errno == ENOENT) {
    // For this path to not exist must mean that /proc is not mounted.
    // Report this with an actionable message
    throw std::system_error(
        ENOSYS,
        std::generic_category(),
        "getOpenedPath: need /proc to be mounted!");
  }

  if (errno != ENAMETOOLONG) {
    throw std::system_error(
        errno, std::generic_category(), "readlink for getOpenedPath");
  }

  // Figure out how much space we need
  struct stat st;
  if (fstat(fd_, &st)) {
    throw std::system_error(
        errno, std::generic_category(), "fstat for getOpenedPath");
  }
  std::string result;
  result.resize(st.st_size + 1, 0);

  len = readlink(procpath, &result[0], result.size());
  if (len == int(result.size())) {
    // It's longer than we expected; TOCTOU detected!
    throw std::system_error(
        ENAMETOOLONG,
        std::generic_category(),
        "readlinkat: link contents grew while examining file");
  }
  if (len >= 0) {
    return w_string(&result[0], len);
  }

  throw std::system_error(
      errno, std::generic_category(), "readlink for getOpenedPath");
#elif defined(_WIN32)
  std::wstring wchar;
  wchar.resize(WATCHMAN_NAME_MAX);
  auto len = GetFinalPathNameByHandleW(
      (HANDLE)fd_,
      &wchar[0],
      wchar.size(),
      FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
  auto err = GetLastError();

  if (len >= wchar.size()) {
    // Grow it
    wchar.resize(len);
    len = GetFinalPathNameByHandleW(
        (HANDLE)fd_, &wchar[0], len, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
    err = GetLastError();
  }

  if (len == 0) {
    throw std::system_error(
        GetLastError(), std::system_category(), "GetFinalPathNameByHandleW");
  }

  return w_string(wchar.data(), len);
#else
  throw std::system_error(
      ENOSYS,
      std::generic_category(),
      "getOpenedPath not implemented on this platform");
#endif
}