From 1cf85b1e60e85c4940869c6291d75ac44a5bd190 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 6 Apr 2009 11:44:48 +0100 Subject: [PATCH] Implementations of 'cat', 'ls', and some cleanups. --- daemon/daemon.h | 15 +++++++++-- daemon/file.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++------- daemon/guestfsd.c | 32 ++++++++++++++++++++++ daemon/ls.c | 57 ++++++++++++++++++++++++++++++++++----- fish/cmds.c | 4 +-- guestfs-actions.pod | 8 +++++- src/generator.ml | 10 +++++-- 7 files changed, 182 insertions(+), 22 deletions(-) diff --git a/daemon/daemon.h b/daemon/daemon.h index 42d7727..3740595 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -26,8 +26,11 @@ /* in guestfsd.c */ extern void xwrite (int sock, const void *buf, size_t len); extern void xread (int sock, void *buf, size_t len); + +extern int add_string (char ***argv, int *size, int *alloc, const char *str); extern int count_strings (char **argv); extern void free_strings (char **argv); + extern int command (char **stdoutput, char **stderror, const char *name, ...); /* in proto.c */ @@ -46,15 +49,23 @@ extern void reply_with_error (const char *fs, ...); extern void reply_with_perror (const char *fs, ...); extern void reply (xdrproc_t xdrp, char *ret); -#define NEED_ROOT \ +#define NEED_ROOT(errcode) \ do { \ if (!root_mounted) { \ reply_with_error ("%s: you must call 'mount' first to mount the root filesystem", __func__); \ - return -1; \ + return (errcode); \ } \ } \ while (0) +#define ABS_PATH(path,errcode) \ + do { \ + if ((path)[0] != '/') { \ + reply_with_error ("%s: path must start with a / character", __func__); \ + return (errcode); \ + } \ + } while (0) + /* NB: * (1) You must match CHROOT_IN and CHROOT_OUT even along error paths. * (2) You must not change directory! cwd must always be "/", otherwise diff --git a/daemon/file.c b/daemon/file.c index db19918..537618f 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -27,6 +27,7 @@ #include #include +#include "../src/guestfs_protocol.h" #include "daemon.h" #include "actions.h" @@ -35,12 +36,8 @@ do_touch (const char *path) { int fd; - NEED_ROOT; - - if (path[0] != '/') { - reply_with_error ("touch: path must start with a / character"); - return -1; - } + NEED_ROOT (-1); + ABS_PATH (path, -1); CHROOT_IN; fd = open (path, O_WRONLY | O_CREAT | O_NOCTTY | O_NONBLOCK, 0666); @@ -48,7 +45,6 @@ do_touch (const char *path) if (fd == -1) { reply_with_perror ("open: %s", path); - close (fd); return -1; } @@ -65,6 +61,70 @@ do_touch (const char *path) char * do_cat (const char *path) { - reply_with_error ("cat command is not yet implemented"); - return NULL; + int fd; + int alloc, size, r, max; + char *buf, *buf2; + + NEED_ROOT (NULL); + ABS_PATH (path,NULL); + + CHROOT_IN; + fd = open (path, O_RDONLY); + CHROOT_OUT; + + if (fd == -1) { + reply_with_perror ("open: %s", path); + return NULL; + } + + /* Read up to GUESTFS_MESSAGE_MAX - bytes. If it's + * larger than that, we need to return an error instead (for + * correctness). + */ + max = GUESTFS_MESSAGE_MAX - 1000; + buf = NULL; + size = alloc = 0; + + for (;;) { + if (size >= alloc) { + alloc += 8192; + if (alloc > max) { + reply_with_error ("cat: %s: file is too large for message buffer", + path); + free (buf); + close (fd); + return NULL; + } + buf2 = realloc (buf, alloc); + if (buf2 == NULL) { + reply_with_perror ("realloc"); + free (buf); + close (fd); + return NULL; + } + buf = buf2; + } + + r = read (fd, buf + size, alloc - size); + if (r == -1) { + reply_with_perror ("read: %s", path); + free (buf); + close (fd); + return NULL; + } + if (r == 0) { + buf[size] = '\0'; + break; + } + if (r > 0) + size += r; + } + + if (close (fd) == -1) { + reply_with_perror ("close: %s", path); + free (buf); + return NULL; + } + + return buf; /* caller will free */ } diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index 9d110d7..6730c1d 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -231,6 +231,38 @@ usage (void) } int +add_string (char ***argv, int *size, int *alloc, const char *str) +{ + char **new_argv; + char *new_str; + + if (*size >= *alloc) { + *alloc += 64; + new_argv = realloc (*argv, *alloc * sizeof (char *)); + if (new_argv == NULL) { + reply_with_perror ("realloc"); + free_strings (*argv); + return -1; + } + *argv = new_argv; + } + + if (str) { + new_str = strdup (str); + if (new_str == NULL) { + reply_with_perror ("strdup"); + free_strings (*argv); + } + } else + new_str = NULL; + + (*argv)[*size] = new_str; + + (*size)++; + return 0; +} + +int count_strings (char **argv) { int argc; diff --git a/daemon/ls.c b/daemon/ls.c index 1bea5f1..05e2cc7 100644 --- a/daemon/ls.c +++ b/daemon/ls.c @@ -23,16 +23,63 @@ #include #include #include +#include #include #include "daemon.h" #include "actions.h" +static int +compare (const void *vp1, const void *vp2) +{ + char * const *p1 = (char * const *) vp1; + char * const *p2 = (char * const *) vp2; + return strcmp (*p1, *p2); +} + char ** do_ls (const char *path) { - reply_with_error ("ls command is not yet implemented"); - return NULL; + char **r = NULL; + int size = 0, alloc = 0; + DIR *dir; + struct dirent *d; + + NEED_ROOT (NULL); + ABS_PATH (path, NULL); + + CHROOT_IN; + dir = opendir (path); + CHROOT_OUT; + + if (!dir) { + reply_with_perror ("opendir: %s", path); + return NULL; + } + + while ((d = readdir (dir)) != NULL) { + if (strcmp (d->d_name, ".") == 0 || strcmp (d->d_name, "..") == 0) + continue; + + if (add_string (&r, &size, &alloc, d->d_name) == -1) { + closedir (dir); + return NULL; + } + } + + if (add_string (&r, &size, &alloc, NULL) == -1) { + closedir (dir); + return NULL; + } + + if (closedir (dir) == -1) { + reply_with_perror ("closedir: %s", path); + free_strings (r); + return NULL; + } + + qsort (r, size-1, sizeof (char *), compare); + return r; } char * @@ -42,10 +89,8 @@ do_ll (const char *path) char *out, *err; char *spath; - if (path[0] != '/') { - reply_with_error ("ll: path must start with a / character"); - return NULL; - } + //NEED_ROOT + ABS_PATH (path, NULL); /* This exposes the /sysroot, because we can't chroot and run the ls * command (since 'ls' won't necessarily exist in the chroot). This diff --git a/fish/cmds.c b/fish/cmds.c index 6ab8e16..3116c94 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -41,13 +41,13 @@ void list_commands (void) void display_command (const char *cmd) { if (strcasecmp (cmd, "cat") == 0) - pod2text ("cat - list the contents of a file", " cat \n\nReturn the contents of the file named C.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); + pod2text ("cat - list the contents of a file", " cat \n\nReturn the contents of the file named C.\n\nNote that this function cannot correctly handle binary files\n(specifically, files containing C<\\0> character which is treated\nas end of string). For those you need to use the C\nfunction which has a more complex interface.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); else if (strcasecmp (cmd, "ll") == 0) pod2text ("ll - list the files in a directory (long format)", " ll \n\nList the files in C (relative to the root directory,\nthere is no cwd) in the format of 'ls -la'.\n\nThis command is mostly useful for interactive sessions. It\nis I intended that you try to parse the output string."); else if (strcasecmp (cmd, "ls") == 0) - pod2text ("ls - list the files in a directory", " ls \n\nList the files in C (relative to the root directory,\nthere is no cwd). The '.' and '..' entries are not returned, but\nhidden files are shown.\n\nThis command is mostly useful for interactive sessions."); + pod2text ("ls - list the files in a directory", " ls \n\nList the files in C (relative to the root directory,\nthere is no cwd). The '.' and '..' entries are not returned, but\nhidden files are shown.\n\nThis command is mostly useful for interactive sessions. Programs\nshould probably use C instead."); else if (strcasecmp (cmd, "mount") == 0) pod2text ("mount - mount a guest disk at a position in the filesystem", " mount \n\nMount a guest disk at a position in the filesystem. Block devices\nare named C, C and so on, as they were added to\nthe guest. If those block devices contain partitions, they will have\nthe usual names (eg. C). Also LVM C-style\nnames can be used.\n\nThe rules are the same as for L: A filesystem must\nfirst be mounted on C before others can be mounted. Other\nfilesystems can only be mounted on directories which already\nexist.\n\nThe mounted filesystem is writable, if we have sufficient permissions\non the underlying device.\n\nThe filesystem options C and C are set with this\ncall, in order to improve reliability."); diff --git a/guestfs-actions.pod b/guestfs-actions.pod index 352760d..eec6697 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -5,6 +5,11 @@ Return the contents of the file named C. +Note that this function cannot correctly handle binary files +(specifically, files containing C<\0> character which is treated +as end of string). For those you need to use the C +function which has a more complex interface. + This function returns a string or NULL on error. The caller must free the returned string after use. @@ -35,7 +40,8 @@ List the files in C (relative to the root directory, there is no cwd). The '.' and '..' entries are not returned, but hidden files are shown. -This command is mostly useful for interactive sessions. +This command is mostly useful for interactive sessions. Programs +should probably use C instead. This function returns a NULL-terminated array of strings (like L), or NULL if there was an error. diff --git a/src/generator.ml b/src/generator.ml index 44cc06b..da35a0f 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -51,7 +51,12 @@ let functions = [ ("cat", (RString "content", P1 (String "path")), 4, [ProtocolLimitWarning], "list the contents of a file", "\ -Return the contents of the file named C."); +Return the contents of the file named C. + +Note that this function cannot correctly handle binary files +(specifically, files containing C<\\0> character which is treated +as end of string). For those you need to use the C +function which has a more complex interface."); ("ll", (RString "listing", P1 (String "directory")), 5, [], "list the files in a directory (long format)", @@ -69,7 +74,8 @@ List the files in C (relative to the root directory, there is no cwd). The '.' and '..' entries are not returned, but hidden files are shown. -This command is mostly useful for interactive sessions."); +This command is mostly useful for interactive sessions. Programs +should probably use C instead."); ("mount", (Err, P2 (String "device", String "mountpoint")), 1, [], "mount a guest disk at a position in the filesystem", -- 1.8.3.1