get_path_for_fd (int fd,
                 gboolean *writable_out,
                 GError **error)
{
  g_autofree char *proc_path = NULL;
  int fd_flags;
  struct stat st_buf;
  struct stat real_st_buf;
  g_autofree char *path = NULL;
  gboolean writable = FALSE;
  int read_access_mode;

  /* Must be able to get fd flags */
  fd_flags = fcntl (fd, F_GETFL);
  if (fd_flags == -1)
    return glnx_null_throw_errno_prefix (error, "fcntl F_GETFL");

  /* Must be O_PATH */
  if ((fd_flags & O_PATH) != O_PATH)
    return glnx_null_throw (error, "not opened with O_PATH");

  /* We don't want to allow exposing symlinks, because if they are
   * under the callers control they could be changed between now and
   * starting the child allowing it to point anywhere, so enforce NOFOLLOW.
   * and verify that stat is not a link.
   */
  if ((fd_flags & O_NOFOLLOW) != O_NOFOLLOW)
    return glnx_null_throw (error, "not opened with O_NOFOLLOW");

  /* Must be able to fstat */
  if (fstat (fd, &st_buf) < 0)
    return glnx_null_throw_errno_prefix (error, "fstat");

  /* As per above, no symlinks */
  if (S_ISLNK (st_buf.st_mode))
    return glnx_null_throw (error, "is a symbolic link");

  proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);

  /* Must be able to read valid path from /proc/self/fd */
  /* This is an absolute and (at least at open time) symlink-expanded path */
  path = verify_proc_self_fd (proc_path, error);
  if (path == NULL)
    return NULL;

  /* Verify that this is the same file as the app opened */
  if (stat (path, &real_st_buf) < 0 ||
      st_buf.st_dev != real_st_buf.st_dev ||
      st_buf.st_ino != real_st_buf.st_ino)
    {
      /* Different files on the inside and the outside, reject the request */
      return glnx_null_throw (error,
                              "different file inside and outside sandbox");
    }

  read_access_mode = R_OK;
  if (S_ISDIR (st_buf.st_mode))
    read_access_mode |= X_OK;

  /* Must be able to access the path via the sandbox supplied O_PATH fd,
     which applies the sandbox side mount options (like readonly). */
  if (access (proc_path, read_access_mode) != 0)
    return glnx_null_throw (error, "not %s in sandbox",
                            read_access_mode & X_OK ? "accessible" : "readable");

  if (access (proc_path, W_OK) == 0)
    writable = TRUE;

  *writable_out = writable;
  return g_steal_pointer (&path);
}
get_path_for_fd (int fd,
                 gboolean *writable_out,
                 GError **error)
{
  g_autofree char *proc_path = NULL;
  int fd_flags;
  struct stat st_buf;
  struct stat real_st_buf;
  g_autofree char *path = NULL;
  gboolean writable = FALSE;
  int read_access_mode;

   
  fd_flags = fcntl (fd, F_GETFL);
  if (fd_flags == -1)
    return glnx_null_throw_errno_prefix (error, "fcntl F_GETFL");

   
  if ((fd_flags & O_PATH) != O_PATH)
    return glnx_null_throw (error, "not opened with O_PATH");

   
  if ((fd_flags & O_NOFOLLOW) != O_NOFOLLOW)
    return glnx_null_throw (error, "not opened with O_NOFOLLOW");

   
  if (fstat (fd, &st_buf) < 0)
    return glnx_null_throw_errno_prefix (error, "fstat");

   
  if (S_ISLNK (st_buf.st_mode))
    return glnx_null_throw (error, "is a symbolic link");

  proc_path = g_strdup_printf ("/proc/self/fd/%d", fd);

   
   
  path = verify_proc_self_fd (proc_path, error);
  if (path == NULL)
    return NULL;

   
  if (stat (path, &real_st_buf) < 0 ||
      st_buf.st_dev != real_st_buf.st_dev ||
      st_buf.st_ino != real_st_buf.st_ino)
    {
       
      return glnx_null_throw (error,
                              "different file inside and outside sandbox");
    }

  read_access_mode = R_OK;
  if (S_ISDIR (st_buf.st_mode))
    read_access_mode |= X_OK;

   
  if (access (proc_path, read_access_mode) != 0)
    return glnx_null_throw (error, "not %s in sandbox",
                            read_access_mode & X_OK ? "accessible" : "readable");

  if (access (proc_path, W_OK) == 0)
    writable = TRUE;

  *writable_out = writable;
  return g_steal_pointer (&path);
}