New API: case-sensitive-path to return case sensitive path on NTFS 3g fs
[libguestfs.git] / src / generator.ml
index 1dd8b20..39f363d 100755 (executable)
@@ -2488,7 +2488,7 @@ C<resize2fs> sometimes gives an error about this and sometimes not.
 In any case, it is always safe to call C<guestfs_e2fsck_f> before
 calling this function.");
 
-  ("find", (RStringList "names", [Pathname "directory"]), 107, [],
+  ("find", (RStringList "names", [Pathname "directory"]), 107, [ProtocolLimitWarning],
    [InitBasicFS, Always, TestOutputList (
       [["find"; "/"]], ["lost+found"]);
     InitBasicFS, Always, TestOutputList (
@@ -2525,7 +2525,9 @@ then the returned list from C<guestfs_find> C</tmp> would be
 If C<directory> is not a directory, then this command returns
 an error.
 
-The returned list is sorted.");
+The returned list is sorted.
+
+See also C<guestfs_find0>.");
 
   ("e2fsck_f", (RErr, [Device "device"]), 108, [],
    [], (* lvresize tests this *)
@@ -3610,6 +3612,97 @@ You can use this command to test the connection through to the daemon.
 
 See also C<guestfs_ping_daemon>.");
 
+  ("find0", (RErr, [Pathname "directory"; FileOut "files"]), 196, [],
+   [], (* There is a regression test for this. *)
+   "find all files and directories, returning NUL-separated list",
+   "\
+This command lists out all files and directories, recursively,
+starting at C<directory>, placing the resulting list in the
+external file called C<files>.
+
+This command works the same way as C<guestfs_find> with the
+following exceptions:
+
+=over 4
+
+=item *
+
+The resulting list is written to an external file.
+
+=item *
+
+Items (filenames) in the result are separated
+by C<\\0> characters.  See L<find(1)> option I<-print0>.
+
+=item *
+
+This command is not limited in the number of names that it
+can return.
+
+=item *
+
+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
@@ -4674,8 +4767,8 @@ check_state (guestfs_h *g, const char *caller)
 
     let needs_i =
       List.exists (function
-                  | StringList _ | DeviceList _ -> true
-                  | _ -> false) (snd style) in
+                   | StringList _ | DeviceList _ -> true
+                   | _ -> false) (snd style) in
     if needs_i then (
       pr "    int i;\n";
       pr "\n"
@@ -4690,24 +4783,24 @@ check_state (guestfs_h *g, const char *caller)
       | Dev_or_Path n
       | FileIn n
       | FileOut n ->
-         (* guestfish doesn't support string escaping, so neither do we *)
-         pr "    printf (\" \\\"%%s\\\"\", %s);\n" n
+          (* guestfish doesn't support string escaping, so neither do we *)
+          pr "    printf (\" \\\"%%s\\\"\", %s);\n" n
       | OptString n ->                 (* string option *)
-         pr "    if (%s) printf (\" \\\"%%s\\\"\", %s);\n" n n;
-         pr "    else printf (\" null\");\n"
+          pr "    if (%s) printf (\" \\\"%%s\\\"\", %s);\n" n n;
+          pr "    else printf (\" null\");\n"
       | StringList n
       | DeviceList n ->                        (* string list *)
-         pr "    putchar (' ');\n";
-         pr "    putchar ('\"');\n";
-         pr "    for (i = 0; %s[i]; ++i) {\n" n;
-         pr "      if (i > 0) putchar (' ');\n";
-         pr "      fputs (%s[i], stdout);\n" n;
-         pr "    }\n";
-         pr "    putchar ('\"');\n";
+          pr "    putchar (' ');\n";
+          pr "    putchar ('\"');\n";
+          pr "    for (i = 0; %s[i]; ++i) {\n" n;
+          pr "      if (i > 0) putchar (' ');\n";
+          pr "      fputs (%s[i], stdout);\n" n;
+          pr "    }\n";
+          pr "    putchar ('\"');\n";
       | Bool n ->                      (* boolean *)
-         pr "    fputs (%s ? \" true\" : \" false\", stdout);\n" n
+          pr "    fputs (%s ? \" true\" : \" false\", stdout);\n" n
       | Int n ->                       (* int *)
-         pr "    printf (\" %%d\", %s);\n" n
+          pr "    printf (\" %%d\", %s);\n" n
     ) (snd style);
     pr "    putchar ('\\n');\n";
     pr "  }\n";
@@ -4759,16 +4852,16 @@ check_state (guestfs_h *g, const char *caller)
       pr "  guestfs_message_header hdr;\n";
       pr "  guestfs_message_error err;\n";
       let has_ret =
-       match fst style with
-       | RErr -> false
-       | RConstString _ | RConstOptString _ ->
+        match fst style with
+        | RErr -> false
+        | RConstString _ | RConstOptString _ ->
             failwithf "RConstString|RConstOptString cannot be used by daemon functions"
-       | RInt _ | RInt64 _
-       | RBool _ | RString _ | RStringList _
-       | RStruct _ | RStructList _
-       | RHashtable _ | RBufferOut _ ->
+        | RInt _ | RInt64 _
+        | RBool _ | RString _ | RStringList _
+        | RStruct _ | RStructList _
+        | RHashtable _ | RBufferOut _ ->
             pr "  struct %s_ret ret;\n" name;
-           true in
+            true in
 
       pr "  int serial;\n";
       pr "  int r;\n";
@@ -4835,7 +4928,7 @@ check_state (guestfs_h *g, const char *caller)
       pr "\n";
       pr "  r = guestfs___recv (g, \"%s\", &hdr, &err,\n        " shortname;
       if not has_ret then
-       pr "NULL, NULL"
+        pr "NULL, NULL"
       else
         pr "(xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret" shortname;
       pr ");\n";
@@ -4956,11 +5049,11 @@ and generate_daemon_actions () =
   pr "#include <stdlib.h>\n";
   pr "#include <string.h>\n";
   pr "#include <inttypes.h>\n";
-  pr "#include <ctype.h>\n";
   pr "#include <rpc/types.h>\n";
   pr "#include <rpc/xdr.h>\n";
   pr "\n";
   pr "#include \"daemon.h\"\n";
+  pr "#include \"c-ctype.h\"\n";
   pr "#include \"../src/guestfs_protocol.h\"\n";
   pr "#include \"actions.h\"\n";
   pr "\n";
@@ -5186,7 +5279,7 @@ and generate_daemon_actions () =
         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
         pr "    return -1;\n";
         pr "  }\n";
-        pr "  if (!*str || isspace (*str)) {\n";
+        pr "  if (!*str || c_isspace (*str)) {\n";
         pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
         pr "    return -1;\n";
         pr "  }\n";
@@ -5288,7 +5381,7 @@ and generate_daemon_actions () =
         pr "      pend++;\n";
         pr "    }\n";
         pr "\n";
-        pr "    while (*p && isspace (*p))     /* Skip any leading whitespace. */\n";
+        pr "    while (*p && c_isspace (*p))   /* Skip any leading whitespace. */\n";
         pr "      p++;\n";
         pr "\n";
         pr "    if (!*p) {                     /* Empty line?  Skip it. */\n";
@@ -6073,9 +6166,9 @@ and generate_fish_cmds () =
   pr "#include <stdlib.h>\n";
   pr "#include <string.h>\n";
   pr "#include <inttypes.h>\n";
-  pr "#include <ctype.h>\n";
   pr "\n";
   pr "#include <guestfs.h>\n";
+  pr "#include \"c-ctype.h\"\n";
   pr "#include \"fish.h\"\n";
   pr "\n";
 
@@ -6193,7 +6286,7 @@ and generate_fish_cmds () =
         | name, FBuffer ->
             pr "  printf (\"%%s%s: \", indent);\n" name;
             pr "  for (i = 0; i < %s->%s_len; ++i)\n" typ name;
-            pr "    if (isprint (%s->%s[i]))\n" typ name;
+            pr "    if (c_isprint (%s->%s[i]))\n" typ name;
             pr "      printf (\"%%s%%c\", indent, %s->%s[i]);\n" typ name;
             pr "    else\n";
             pr "      printf (\"%%s\\\\x%%02x\", indent, %s->%s[i]);\n" typ name;