[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