fish: Allows win:... paths to work with drives mounted anywhere.

This allows you to mount disks on (eg) /c and /e and have the
guestfish win:... path mechanism map drive letters to the
right places.
This commit is contained in:
Richard W.M. Jones
2011-04-12 17:03:14 +01:00
parent c2c755440b
commit 11374abead
3 changed files with 112 additions and 92 deletions

4
TODO
View File

@@ -431,7 +431,3 @@ guestfish drive letters
There should be an option to mount all Windows drives as separate
paths, like C: => /c/, D: => /d/ etc.
Also the code which detects if a drive letter is already mounted
should be smarter: it should be able to detect if the drive is mounted
on any path (not just /) and rewrite the path accordingly.

View File

@@ -1376,10 +1376,13 @@ xwrite (int fd, const void *v_buf, size_t len)
}
/* Resolve the special "win:..." form for Windows-specific paths. The
* generated code calls this for all device or path arguments. The
* function must return a newly allocated string (caller frees) or
* display an error and return NULL.
* generated code calls this for all device or path arguments.
*
* The function returns a newly allocated string, and the caller must
* free this string; else display an error and return NULL.
*/
static char *win_prefix_drive_letter (char drive_letter, const char *path);
char *
win_prefix (const char *path)
{
@@ -1396,87 +1399,27 @@ win_prefix (const char *path)
path += 4;
/* Is there a drive letter? */
/* If there is a drive letter, rewrite the path. */
if (c_isalpha (path[0]) && path[1] == ':') {
char drive_letter;
char **roots, **drives, **mountpoints, *device;
size_t i;
drive_letter = c_tolower (path[0]);
path += 2;
/* Resolve the drive letter using the drive mappings table. */
roots = guestfs_inspect_get_roots (g);
if (roots == NULL)
return NULL;
if (roots[0] == NULL) {
fprintf (stderr, _("%s: to use Windows drive letters, you must inspect the guest (\"-i\" option or run \"inspect-os\" command)\n"),
program_name);
free_strings (roots);
return NULL;
}
drives = guestfs_inspect_get_drive_mappings (g, roots[0]);
if (drives == NULL || drives[0] == NULL) {
fprintf (stderr, _("%s: to use Windows drive letters, this must be a Windows guest\n"),
program_name);
free_strings (roots);
free_strings (drives);
return NULL;
}
device = NULL;
for (i = 0; drives[i] != NULL; i += 2) {
if (c_tolower (drives[i][0]) == drive_letter && drives[i][1] == '\0') {
device = drives[i+1];
break;
}
}
if (device == NULL) {
fprintf (stderr, _("%s: drive '%c:' not found. To list available drives do:\n inspect-get-drive-mappings %s\n"),
program_name, drive_letter, roots[0]);
free_strings (roots);
free_strings (drives);
return NULL;
}
/* This drive letter must be mounted on / (we won't do it). */
mountpoints = guestfs_mountpoints (g);
if (mountpoints == NULL) {
free_strings (roots);
free_strings (drives);
return NULL;
}
for (i = 0; mountpoints[i] != NULL; i += 2) {
if (STREQ (mountpoints[i+1], "/")) {
if (STRNEQ (mountpoints[i], device)) {
fprintf (stderr, _("%s: to access '%c:', mount %s on / first. One way to do this is:\n umount-all\n mount %s /\n"),
program_name, drive_letter, device, device);
free_strings (roots);
free_strings (drives);
free_strings (mountpoints);
return NULL;
}
}
}
free_strings (roots);
free_strings (drives);
free_strings (mountpoints);
}
if (!*path) {
ret = strdup ("/");
char drive_letter = c_tolower (path[0]);
/* This returns the newly allocated string. */
ret = win_prefix_drive_letter (drive_letter, path + 2);
if (ret == NULL)
perror ("strdup");
return ret;
return NULL;
}
ret = strdup (path);
if (ret == NULL) {
perror ("strdup");
return NULL;
else if (!*path) {
ret = strdup ("/");
if (ret == NULL) {
perror ("strdup");
return NULL;
}
}
else {
ret = strdup (path);
if (ret == NULL) {
perror ("strdup");
return NULL;
}
}
/* Blindly convert any backslashes into forward slashes. Is this good? */
@@ -1491,6 +1434,82 @@ win_prefix (const char *path)
return ret;
}
static char *
win_prefix_drive_letter (char drive_letter, const char *path)
{
char **roots = NULL;
char **drives = NULL;
char **mountpoints = NULL;
char *device, *mountpoint, *ret = NULL;
size_t i;
/* Resolve the drive letter using the drive mappings table. */
roots = guestfs_inspect_get_roots (g);
if (roots == NULL)
goto out;
if (roots[0] == NULL) {
fprintf (stderr, _("%s: to use Windows drive letters, you must inspect the guest (\"-i\" option or run \"inspect-os\" command)\n"),
program_name);
goto out;
}
drives = guestfs_inspect_get_drive_mappings (g, roots[0]);
if (drives == NULL || drives[0] == NULL) {
fprintf (stderr, _("%s: to use Windows drive letters, this must be a Windows guest\n"),
program_name);
goto out;
}
device = NULL;
for (i = 0; drives[i] != NULL; i += 2) {
if (c_tolower (drives[i][0]) == drive_letter && drives[i][1] == '\0') {
device = drives[i+1];
break;
}
}
if (device == NULL) {
fprintf (stderr, _("%s: drive '%c:' not found. To list available drives do:\n inspect-get-drive-mappings %s\n"),
program_name, drive_letter, roots[0]);
goto out;
}
/* This drive letter must be mounted somewhere (we won't do it). */
mountpoints = guestfs_mountpoints (g);
if (mountpoints == NULL)
goto out;
mountpoint = NULL;
for (i = 0; mountpoints[i] != NULL; i += 2) {
if (STREQ (mountpoints[i], device)) {
mountpoint = mountpoints[i+1];
break;
}
}
if (mountpoint == NULL) {
fprintf (stderr, _("%s: to access '%c:', mount %s first. One way to do this is:\n umount-all\n mount %s /\n"),
program_name, drive_letter, device, device);
goto out;
}
/* Rewrite the path, eg. if C: => /c then C:/foo => /c/foo */
if (asprintf (&ret, "%s%s%s",
mountpoint, STRNEQ (mountpoint, "/") ? "/" : "", path) == -1) {
perror ("asprintf");
goto out;
}
out:
if (roots)
free_strings (roots);
if (drives)
free_strings (drives);
if (mountpoints)
free_strings (mountpoints);
return ret;
}
/* Resolve the special FileIn paths ("-" or "-<<END" or filename).
* The caller (cmds.c) will call free_file_in after the command has
* run which should clean up resources.

View File

@@ -798,19 +798,24 @@ on each one. Then you can close the mapper device:
=head1 WINDOWS PATHS
If a path is prefixed with C<win:> then you can use Windows-style
paths (with some limitations). The following commands are equivalent:
drive letters and paths (with some limitations). The following
commands are equivalent:
file /WINDOWS/system32/config/system.LOG
file win:/windows/system32/config/system.log
file win:\windows\system32\config\system.log
file WIN:C:\Windows\SYSTEM32\conFIG\SYSTEM.LOG
file WIN:C:\Windows\SYSTEM32\CONFIG\SYSTEM.LOG
This syntax implicitly calls C<case-sensitive-path> (q.v.) so it also
handles case insensitivity like Windows would. This only works in
argument positions that expect a path.
The parameter is rewritten "behind the scenes" by looking up the
position where the drive is mounted, prepending that to the path,
changing all backslash characters to forward slash, then resolving the
result using L</case-sensitive-path>. For example if the E: drive
was mounted on C</e> then the parameter might be rewritten like this:
win:e:\foo\bar => /e/FOO/bar
This only works in argument positions that expect a path.
=head1 UPLOADING AND DOWNLOADING FILES