Richard W.M. Jones
2009-Oct-26 13:10 UTC
[Libguestfs] [PATCH 0/2] New API: case_sensitive_path
This is the previously controversial 1/3 patch, split into two parts. The first patch contains these changes from last time: - set errno = 0 before readdir, and check for errors after - check for other errors from the chdir call which weren't ENOTDIR - the documentation is clarified about when you might use this function - more comprehensive tests, including the corner cases Matt mentioned. The second patch changes the chdir(2) implementation to use openat/ fdopendir. I'm not convinced that this is a good thing since the chdir implementation is much clearer to understand, and the chance of the daemon ever becoming multithreaded is next to nil. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html
Richard W.M. Jones
2009-Oct-26 13:11 UTC
[Libguestfs] [PATCH 1/2] New API: case-sensitive-path to return case sensitive path on NTFS 3g fs
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top -------------- next part -------------->From 9add3c10a3b769e309f476bd0fd05e2a7126d31d Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Mon, 26 Oct 2009 08:20:00 +0000 Subject: [PATCH 1/2] New API: case-sensitive-path to return case sensitive path on NTFS 3g fs This function handles an annoyance/peculiarity of the Linux NTFS 3g driver, which is that it exports NTFS filesystems with names case sensitive, even though under Windows they would be case insensitive. This causes problems because the location of (eg.) c:\windows might appear as /windows or /WINDOWS (etc) depending on the inconsequential details of how it was originally created. Example of this problem on a real Windows guest: ><fs> file /windows/system32/config/system.log libguestfs: error: file: access: /windows/system32/config/system.log: No such file or directory ><fs> case-sensitive-path /windows/system32/config/system.log /WINDOWS/system32/config/system.LOG ><fs> file /WINDOWS/system32/config/system.LOG MS Windows registry file, NT/2000 or above --- daemon/realpath.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/MAX_PROC_NR | 2 +- src/generator.ml | 58 +++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletions(-) diff --git a/daemon/realpath.c b/daemon/realpath.c index 706af42..bfe8e67 100644 --- a/daemon/realpath.c +++ b/daemon/realpath.c @@ -23,6 +23,10 @@ #include <string.h> #include <unistd.h> #include <limits.h> +#include <sys/types.h> +#include <dirent.h> + +#include "ignore-value.h" #include "daemon.h" #include "actions.h" @@ -42,3 +46,121 @@ 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) { + 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 (verbose) + fprintf (stderr, "case_sensitive_path: path = %s, next = %zu, i = %zu\n", + path, next, i); + + 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; + + /* 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; + + errno = 0; + while ((d = readdir (dir)) != NULL) { + if (strcasecmp (d->d_name, name) == 0) + break; + } + + if (d == NULL && errno != 0) { + reply_with_perror ("readdir"); + goto error; + } + + 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) == -1) { + /* ENOTDIR is OK provided we've reached the end of the path. */ + if (errno != ENOTDIR) { + reply_with_perror ("chdir: %s", d->d_name); + goto error; + } + + if (*path) { + reply_with_error ("%s: non-directory element in path", d->d_name); + goto error; + } + } + } + + 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; +} diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 0f11735..5381652 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -196 +197 diff --git a/src/generator.ml b/src/generator.ml index cea5178..39f363d 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -3645,6 +3645,64 @@ The result list is not sorted. =back"); + ("case_sensitive_path", (RString "rpath", [Pathname "path"]), 197, [], + [InitISOFS, Always, TestOutput ( + [["case_sensitive_path"; "/DIRECTORY"]], "/directory"); + InitISOFS, Always, TestOutput ( + [["case_sensitive_path"; "/DIRECTORY/"]], "/directory"); + InitISOFS, Always, TestOutput ( + [["case_sensitive_path"; "/Known-1"]], "/known-1"); + InitISOFS, Always, TestLastFail ( + [["case_sensitive_path"; "/Known-1/"]]); + InitBasicFS, Always, TestOutput ( + [["mkdir"; "/a"]; + ["mkdir"; "/a/bbb"]; + ["touch"; "/a/bbb/c"]; + ["case_sensitive_path"; "/A/bbB/C"]], "/a/bbb/c"); + InitBasicFS, Always, TestOutput ( + [["mkdir"; "/a"]; + ["mkdir"; "/a/bbb"]; + ["touch"; "/a/bbb/c"]; + ["case_sensitive_path"; "/A////bbB/C"]], "/a/bbb/c"); + InitBasicFS, Always, TestLastFail ( + [["mkdir"; "/a"]; + ["mkdir"; "/a/bbb"]; + ["touch"; "/a/bbb/c"]; + ["case_sensitive_path"; "/A/bbb/../bbb/C"]])], + "return true path on case-insensitive filesystem", + "\ +This can be used to resolve case insensitive paths on +a filesystem which is case sensitive. The use case is +to resolve paths which you have read from Windows configuration +files or the Windows Registry, to the true path. + +The command handles a peculiarity of the Linux ntfs-3g +filesystem driver (and probably others), which is that although +the underlying filesystem is case-insensitive, the driver +exports the filesystem to Linux as case-sensitive. + +One consequence of this is that special directories such +as C<c:\\windows> may appear as C</WINDOWS> or C</windows> +(or other things) depending on the precise details of how +they were created. In Windows itself this would not be +a problem. + +Bug or feature? You decide: +L<http://www.tuxera.com/community/ntfs-3g-faq/#posixfilenames1> + +This function resolves the true case of each element in the +path and returns the case-sensitive path. + +Thus C<guestfs_case_sensitive_path> (\"/Windows/System32\") +might return C<\"/WINDOWS/system32\"> (the exact return value +would depend on details of how the directories were originally +created under Windows). + +I<Note>: +This function does not handle drive names, backslashes etc. + +See also C<guestfs_realpath>."); + ] let all_functions = non_daemon_functions @ daemon_functions -- 1.6.5.rc2
Richard W.M. Jones
2009-Oct-26 13:11 UTC
[Libguestfs] [PATCH 2/2] daemon: Change chdir to use openat/fdopendir.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw -------------- next part -------------->From f1d199310e2c93509e50e8b0b70bfde344328c02 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Mon, 26 Oct 2009 13:00:46 +0000 Subject: [PATCH 2/2] daemon: Change chdir to use openat/fdopendir. --- daemon/realpath.c | 35 ++++++++++++++++++++++++++--------- 1 files changed, 26 insertions(+), 9 deletions(-) diff --git a/daemon/realpath.c b/daemon/realpath.c index bfe8e67..b4f2dcc 100644 --- a/daemon/realpath.c +++ b/daemon/realpath.c @@ -18,16 +18,19 @@ #include <config.h> +#ifndef _ATFILE_SOURCE +#define _ATFILE_SOURCE +#endif + #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <fcntl.h> #include <limits.h> #include <sys/types.h> #include <dirent.h> -#include "ignore-value.h" - #include "daemon.h" #include "actions.h" @@ -52,9 +55,13 @@ do_case_sensitive_path (const char *path) { char ret[PATH_MAX+1] = "/"; size_t next = 1; + int fd; - /* MUST chdir ("/") before leaving this function. */ - if (chdir (sysroot) == -1) { + /* 'fd' here is a surrogate for the current working directory, so + * that we don't have to actually call chdir(2). + */ + fd = open (sysroot, O_RDONLY | O_DIRECTORY); + if (fd == -1) { reply_with_perror ("%s", sysroot); return NULL; } @@ -93,7 +100,12 @@ do_case_sensitive_path (const char *path) /* Read the current directory looking (case insensitively) for * this element of the path. */ - DIR *dir = opendir ("."); + int fd2 = dup (fd); /* because closedir will close it */ + if (fd2 == -1) { + reply_with_perror ("dup"); + goto error; + } + DIR *dir = fdopendir (fd2); if (dir == NULL) { reply_with_perror ("opendir"); goto error; @@ -136,10 +148,15 @@ do_case_sensitive_path (const char *path) next += i; /* Is it a directory? Try going into it. */ - if (chdir (d->d_name) == -1) { + fd2 = openat (fd, d->d_name, O_RDONLY | O_DIRECTORY); + int err = errno; + close (fd); + fd = fd2; + errno = err; + if (fd == -1) { /* ENOTDIR is OK provided we've reached the end of the path. */ if (errno != ENOTDIR) { - reply_with_perror ("chdir: %s", d->d_name); + reply_with_perror ("openat: %s", d->d_name); goto error; } @@ -150,7 +167,7 @@ do_case_sensitive_path (const char *path) } } - ignore_value (chdir ("/")); + close (fd); ret[next] = '\0'; char *retp = strdup (ret); @@ -161,6 +178,6 @@ do_case_sensitive_path (const char *path) return retp; /* caller frees */ error: - ignore_value (chdir ("/")); + close (fd); return NULL; } -- 1.6.5.rc2
Apparently Analagous Threads
- [PATCH 0/7] Add libvirt domain to core API
- [PATCH 0/8 v2 DISCUSSION ONLY] Connecting to live virtual machines
- [PATCH febootstrap 0/8] Add support for building an ext2-based appliance
- builder-debian febootstrap success b5ed2a56e1d9c826e494fea5c6a353d45f3c857f
- builder-debian febootstrap success e56ae34bcfc3e355dc591b4bd99bbe8e593d33af