/* 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 */
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
#include <fcntl.h>
#include <sys/stat.h>
+#include "../src/guestfs_protocol.h"
#include "daemon.h"
#include "actions.h"
{
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);
if (fd == -1) {
reply_with_perror ("open: %s", path);
- close (fd);
return -1;
}
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 */
}
}
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;
#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 *
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
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.");
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.
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.
("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)",
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",