Implementations of 'cat', 'ls', and some cleanups.
authorRichard Jones <rjones@redhat.com>
Mon, 6 Apr 2009 10:44:48 +0000 (11:44 +0100)
committerRichard Jones <rjones@redhat.com>
Mon, 6 Apr 2009 10:44:48 +0000 (11:44 +0100)
daemon/daemon.h
daemon/file.c
daemon/guestfsd.c
daemon/ls.c
fish/cmds.c
guestfs-actions.pod
src/generator.ml

index 42d7727..3740595 100644 (file)
 /* 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
index db19918..537618f 100644 (file)
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 
+#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 - <overhead> 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 */
 }
index 9d110d7..6730c1d 100644 (file)
@@ -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;
index 1bea5f1..05e2cc7 100644 (file)
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <dirent.h>
 #include <sys/stat.h>
 
 #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
index 6ab8e16..3116c94 100644 (file)
@@ -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 <path>\n\nReturn the contents of the file named C<path>.\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 <path>\n\nReturn the contents of the file named C<path>.\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<guestfs_read>\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 <directory>\n\nList the files in C<directory> (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<not> intended that you try to parse the output string.");
   else
   if (strcasecmp (cmd, "ls") == 0)
-    pod2text ("ls - list the files in a directory", " ls <directory>\n\nList the files in C<directory> (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 <directory>\n\nList the files in C<directory> (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<guestfs_readdir> instead.");
   else
   if (strcasecmp (cmd, "mount") == 0)
     pod2text ("mount - mount a guest disk at a position in the filesystem", " mount <device> <mountpoint>\n\nMount a guest disk at a position in the filesystem.  Block devices\nare named C</dev/sda>, C</dev/sdb> and so on, as they were added to\nthe guest.  If those block devices contain partitions, they will have\nthe usual names (eg. C</dev/sda1>).  Also LVM C</dev/VG/LV>-style\nnames can be used.\n\nThe rules are the same as for L<mount(2)>:  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<sync> and C<noatime> are set with this\ncall, in order to improve reliability.");
index 352760d..eec6697 100644 (file)
@@ -5,6 +5,11 @@
 
 Return the contents of the file named C<path>.
 
+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<guestfs_read>
+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<directory> (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<guestfs_readdir> instead.
 
 This function returns a NULL-terminated array of strings
 (like L<environ(3)>), or NULL if there was an error.
index 44cc06b..da35a0f 100755 (executable)
@@ -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<path>.");
+Return the contents of the file named C<path>.
+
+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<guestfs_read>
+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<directory> (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<guestfs_readdir> instead.");
 
   ("mount", (Err, P2 (String "device", String "mountpoint")), 1, [],
    "mount a guest disk at a position in the filesystem",