#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"
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;
+}
=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