[Libguestfs] [PATCH 1/3] New API: case-sensitive-path to return case sensitive path on NTFS 3g fs

Jim Meyering jim at meyering.net
Mon Oct 26 10:41:22 UTC 2009


Richard W.M. Jones wrote:
...
> diff --git a/daemon/realpath.c b/daemon/realpath.c
...

Hi Rich,

> @@ -42,3 +46,111 @@ do_realpath (const char *path)
>
>    return ret;			/* caller frees */
>  }
> +
> +char *
> +do_case_sensitive_path (const char *path)
> +{
> +  char ret[PATH_MAX+1] = "/";
> +  size_t next = 1;
> +
> +  /* MUST chdir ("/") before leaving this function. */
> +  if (chdir (sysroot) == -1) {

If this function might ever be used from a multi-threaded
application, then you'll want to change it not to use chdir,
since chdir changes the process-wide current directory.
That can cause rare but particularly hard to debug problems.

Another (albeit small) advantage of not performing the chdir/readdir is
that the function will be able to handle those rare names
with a directory component that grants "x" but not "r" access.

> +    reply_with_perror ("%s", sysroot);
> +    return NULL;
> +  }
> +
> +  /* First character is a '/'.  Take each subsequent path element
> +   * and follow it.
> +   */
> +  path++;
> +  while (*path) {
> +    size_t i = strcspn (path, "/");
> +    if (i == 0) {               /* "//" in path */
> +      path++;
> +      continue;
> +    }
> +    if ((i == 1 && path[0] == '.') ||
> +        (i == 2 && path[0] == '.' && path[1] == '.')) {
> +      reply_with_error ("case_sensitive_path: path contained . or .. elements");
> +      goto error;
> +    }
> +    if (i > NAME_MAX) {
> +      reply_with_error ("case_sensitive_path: 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;
> +    if (*path == '/') path++;
> +
> +    /* Read the current directory looking (case insensitively) for
> +     * this element of the path.
> +     */
> +    DIR *dir = opendir (".");
> +    if (dir == NULL) {
> +      reply_with_perror ("opendir");
> +      goto error;
> +    }
> +
> +    struct dirent *d = NULL;
> +
> +    while ((d = readdir (dir)) != NULL) {

If you want to distinguish readdir "EOF" from failure (e.g., EIO),
you'll have to set errno=0 before each call and test whether it's
still 0 upon NULL return.

> +      if (strcasecmp (d->d_name, name) == 0)
> +        break;
> +    }
> +
> +    if (closedir (dir) == -1) {
> +      reply_with_perror ("closedir");
> +      goto error;
> +    }
> +
> +    if (d == NULL) {
> +      reply_with_error ("%s: no file or directory found with this name", name);
> +      goto error;
> +    }
> +
> +    /* Add the real name of this path element to the return value. */
> +    if (next > 1)
> +      ret[next++] = '/';
> +
> +    i = strlen (d->d_name);
> +    if (next + i >= PATH_MAX) {
> +      reply_with_error ("final path too long");
> +      goto error;
> +    }
> +
> +    strcpy (&ret[next], d->d_name);
> +    next += i;
> +
> +    /* Is it a directory?  Try going into it. */
> +    if (chdir (d->d_name) == 0)
> +      continue;
> +
> +    /* This is OK provided we've reached the end of the path. */
> +    if (errno == ENOTDIR) {
> +      if (*path) {
> +        reply_with_error ("non-directory element in path");
> +        goto error;
> +      }
> +      break;
> +    }

What about other errno values?

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




More information about the Libguestfs mailing list