コード例 #1
0
ファイル: realpath.c プロジェクト: mdbooth/libguestfs
char *
do_case_sensitive_path (const char *path)
{
    char ret[PATH_MAX+1] = "/";
    size_t next = 1;
    int fd_cwd;

    /* 'fd_cwd' here is a surrogate for the current working directory, so
     * that we don't have to actually call chdir(2).
     */
    fd_cwd = open (sysroot, O_RDONLY | O_DIRECTORY);
    if (fd_cwd == -1) {
        reply_with_perror ("%s", sysroot);
        return NULL;
    }

    /* First character is a '/'.  Take each subsequent path element
     * and follow it.
     */
    while (*path) {
        size_t i = strcspn (path, "/");
        if (i == 0) {
            path++;
            continue;
        }

        if ((i == 1 && path[0] == '.') ||
                (i == 2 && path[0] == '.' && path[1] == '.')) {
            reply_with_error ("path contained . or .. elements");
            goto error;
        }
        if (i > NAME_MAX) {
            reply_with_error ("path element too long");
            goto error;
        }

        char name[NAME_MAX+1];
        memcpy (name, path, i);
        name[i] = '\0';

        /* Skip to next element in path (for the next loop iteration). */
        path += i;

        /* Read the current directory looking (case insensitively) for
         * this element of the path.  This replaces 'name' with the
         * correct case version.
         */
        if (find_path_element (fd_cwd, name, &i) == -1)
            goto error;

        /* Add the real name of this path element to the return value. */
        if (next > 1)
            ret[next++] = '/';

        if (next + i >= PATH_MAX) {
            reply_with_error ("final path too long");
            goto error;
        }

        strcpy (&ret[next], name);
        next += i;

        /* Is it a directory?  Try going into it. */
        int fd2 = openat (fd_cwd, name, O_RDONLY | O_DIRECTORY);
        int err = errno;
        close (fd_cwd);
        fd_cwd = fd2;
        errno = err;
        if (fd_cwd == -1) {
            /* ENOTDIR is OK provided we've reached the end of the path. */
            if (errno != ENOTDIR) {
                reply_with_perror ("openat: %s", name);
                goto error;
            }

            if (*path) {
                reply_with_error ("%s: non-directory element in path", name);
                goto error;
            }
        }
    }

    if (fd_cwd >= 0)
        close (fd_cwd);

    ret[next] = '\0';
    char *retp = strdup (ret);
    if (retp == NULL) {
        reply_with_perror ("strdup");
        return NULL;
    }
    return retp;                  /* caller frees */

error:
    if (fd_cwd >= 0)
        close (fd_cwd);

    return NULL;
}
コード例 #2
0
ファイル: realpath.c プロジェクト: AlphaStaxLLC/libguestfs
char *
do_case_sensitive_path (const char *path)
{
  size_t next;
  int fd_cwd, fd2, err, is_end;
  char *ret;

  ret = strdup ("/");
  if (ret == NULL) {
    reply_with_perror ("strdup");
    return NULL;
  }
  next = 1; /* next position in 'ret' buffer */

  /* 'fd_cwd' here is a surrogate for the current working directory, so
   * that we don't have to actually call chdir(2).
   */
  fd_cwd = open (sysroot, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
  if (fd_cwd == -1) {
    reply_with_perror ("%s", sysroot);
    goto error;
  }

  /* First character is a '/'.  Take each subsequent path element
   * and follow it.
   */
  while (*path) {
    char *t;
    size_t i, len;
    CLEANUP_FREE char *name_in = NULL, *name_out = NULL;

    i = strcspn (path, "/");
    if (i == 0) {
      path++;
      continue;
    }

    if ((i == 1 && path[0] == '.') ||
        (i == 2 && path[0] == '.' && path[1] == '.')) {
      reply_with_error ("path contained . or .. elements");
      goto error;
    }

    name_in = strndup (path, i);
    if (name_in == NULL) {
      reply_with_perror ("strdup");
      goto error;
    }

    /* Skip to next element in path (for the next loop iteration). */
    path += i;
    is_end = *path == 0;

    /* Read the current directory looking (case insensitively) for
     * this element of the path.  This replaces 'name' with the
     * correct case version.
     */
    if (find_path_element (fd_cwd, is_end, name_in, &name_out) == -1)
      goto error;
    len = strlen (name_out);

    /* Add the real name of this path element to the return value. */
    if (next > 1)
      ret[next++] = '/';

    t = realloc (ret, next+len+1);
    if (t == NULL) {
      reply_with_perror ("realloc");
      goto error;
    }
    ret = t;

    strcpy (&ret[next], name_out);
    next += len;

    /* Is it a directory?  Try going into it. */
    fd2 = openat (fd_cwd, name_out, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
    err = errno;
    close (fd_cwd);
    fd_cwd = fd2;
    errno = err;
    if (fd_cwd == -1) {
      /* Some errors are OK provided we've reached the end of the path. */
      if (is_end && (errno == ENOTDIR || errno == ENOENT))
        break;

      reply_with_perror ("openat: %s", name_out);
      goto error;
    }
  }

  if (fd_cwd >= 0)
    close (fd_cwd);

  return ret;                   /* caller frees */

 error:
  if (fd_cwd >= 0)
    close (fd_cwd);
  free (ret);

  return NULL;
}