From: Richard W.M. Jones Date: Wed, 1 Jul 2009 19:56:58 +0000 (+0100) Subject: Add 'readdir' call. X-Git-Tag: 1.0.55~21 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=5186251f8f681f2ebb028423bb49a748861fd11e Add 'readdir' call. This adds a readdir call (mostly intended for programs). The return value is a list of guestfs_dirent structures. This adds the new types 'struct guestfs_dirent' and 'struct guestfs_dirent_list', along with all the code to return these in the different language bindings. Also includes additional tests for OCaml and Perl bindings to test this. --- diff --git a/.gitignore b/.gitignore index 4d1da44..6c58e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,7 @@ ocaml/examples/lvs ocaml/t/guestfs_005_load ocaml/t/guestfs_010_launch ocaml/t/guestfs_050_lvcreate +ocaml/t/guestfs_060_readdir perl/Guestfs.c perl/Guestfs.bs perl/Makefile-pl diff --git a/capitests/tests.c b/capitests/tests.c index df49dfe..9124ea9 100644 --- a/capitests/tests.c +++ b/capitests/tests.c @@ -157,6 +157,7 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_df_h\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_mount_loop\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_umask\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_readdir\" has no tests\n"); } static int test_mknod_c_0_skip (void) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 8884c6b..8b909c5 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -51,6 +51,7 @@ guestfsd_SOURCES = \ ntfs.c \ pingdaemon.c \ proto.c \ + readdir.c \ scrub.c \ sfdisk.c \ sleep.c \ diff --git a/daemon/actions.h b/daemon/actions.h index ad44d53..9eaf87b 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -158,3 +158,4 @@ extern int do_mkfifo (int mode, char *path); extern int do_mknod_b (int mode, int devmajor, int devminor, char *path); extern int do_mknod_c (int mode, int devmajor, int devminor, char *path); extern int do_umask (int mask); +extern guestfs_int_dirent_list *do_readdir (char *dir); diff --git a/daemon/readdir.c b/daemon/readdir.c new file mode 100644 index 0000000..cea6fdd --- /dev/null +++ b/daemon/readdir.c @@ -0,0 +1,105 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include +#include + +#include "daemon.h" +#include "actions.h" + +guestfs_int_dirent_list * +do_readdir (char *path) +{ + guestfs_int_dirent_list *ret; + guestfs_int_dirent v; + DIR *dir; + struct dirent *d; + int i; + + NEED_ROOT (NULL); + ABS_PATH (path, NULL); + + ret = malloc (sizeof *ret); + if (ret == NULL) { + reply_with_perror ("malloc"); + return NULL; + } + + ret->guestfs_int_dirent_list_len = 0; + ret->guestfs_int_dirent_list_val = NULL; + + CHROOT_IN; + dir = opendir (path); + CHROOT_OUT; + + if (dir == NULL) { + reply_with_perror ("opendir: %s", path); + free (ret); + return NULL; + } + + i = 0; + while ((d = readdir (dir)) != NULL) { + guestfs_int_dirent *p; + + p = realloc (ret->guestfs_int_dirent_list_val, + sizeof (guestfs_int_dirent) * (i+1)); + v.name = strdup (d->d_name); + if (!p || !v.name) { + reply_with_perror ("allocate"); + free (ret->guestfs_int_dirent_list_val); + free (ret); + closedir (dir); + return NULL; + } + ret->guestfs_int_dirent_list_val = p; + + v.ino = d->d_ino; + switch (d->d_type) { + case DT_BLK: v.ftyp = 'b'; break; + case DT_CHR: v.ftyp = 'c'; break; + case DT_DIR: v.ftyp = 'd'; break; + case DT_FIFO: v.ftyp = 'f'; break; + case DT_LNK: v.ftyp = 'l'; break; + case DT_REG: v.ftyp = 'r'; break; + case DT_SOCK: v.ftyp = 's'; break; + case DT_UNKNOWN: v.ftyp = 'u'; break; + default: v.ftyp = '?'; break; + } + + ret->guestfs_int_dirent_list_val[i] = v; + + i++; + } + + ret->guestfs_int_dirent_list_len = i; + + if (closedir (dir) == -1) { + reply_with_perror ("closedir"); + free (ret->guestfs_int_dirent_list_val); + free (ret); + return NULL; + } + + return ret; +} diff --git a/daemon/stubs.c b/daemon/stubs.c index 033f42c..9c2bfef 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -3477,6 +3477,33 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_umask_args, (char *) &args); } +static void readdir_stub (XDR *xdr_in) +{ + guestfs_int_dirent_list *r; + struct guestfs_readdir_args args; + char *dir; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_readdir_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "readdir"); + return; + } + dir = args.dir; + + r = do_readdir (dir); + if (r == NULL) + /* do_readdir has already called reply_with_error */ + goto done; + + struct guestfs_readdir_ret ret; + ret.entries = *r; + reply ((xdrproc_t) xdr_guestfs_readdir_ret, (char *) &ret); + xdr_free ((xdrproc_t) xdr_guestfs_readdir_ret, (char *) &ret); +done: + xdr_free ((xdrproc_t) xdr_guestfs_readdir_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -3891,6 +3918,9 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_UMASK: umask_stub (xdr_in); break; + case GUESTFS_PROC_READDIR: + readdir_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index fe59737..9b8dfbb 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -139,6 +139,7 @@ void list_commands (void) printf ("%-20s %s\n", "pvs", "list the LVM physical volumes (PVs)"); printf ("%-20s %s\n", "pvs-full", "list the LVM physical volumes (PVs)"); printf ("%-20s %s\n", "read-lines", "read file as lines"); + printf ("%-20s %s\n", "readdir", "read directories entries"); printf ("%-20s %s\n", "resize2fs", "resize an ext2/ext3 filesystem"); printf ("%-20s %s\n", "rm", "remove a file"); printf ("%-20s %s\n", "rm-rf", "remove a file or directory recursively"); @@ -676,6 +677,9 @@ void display_command (const char *cmd) if (strcasecmp (cmd, "umask") == 0) pod2text ("umask - set file mode creation mask (umask)", " umask \n\nThis function sets the mask used for creating new files and\ndevice nodes to C.\n\nTypical umask values would be C<022> which creates new files\nwith permissions like \"-rw-r--r--\" or \"-rwxr-xr-x\", and\nC<002> which creates new files with permissions like\n\"-rw-rw-r--\" or \"-rwxrwxr-x\".\n\nThe default umask is C<022>. This is important because it\nmeans that directories and device nodes will be created with\nC<0644> or C<0755> mode even if you specify C<0777>.\n\nSee also L, C, C.\n\nThis call returns the previous umask."); else + if (strcasecmp (cmd, "readdir") == 0) + pod2text ("readdir - read directories entries", " readdir \n\nThis returns the list of directory entries in directory C.\n\nAll entries in the directory are returned, including C<.> and\nC<..>. The entries are I sorted, but returned in the same\norder as the underlying filesystem.\n\nThis function is primarily intended for use by programs. To\nget a simple list of names, use C. To get a printable\ndirectory for human consumption, use C."); + else display_builtin_command (cmd); } @@ -813,6 +817,21 @@ static void print_statvfs (struct guestfs_statvfs *statvfs) printf ("namemax: %" PRIi64 "\n", statvfs->namemax); } +static void print_dirent (struct guestfs_dirent *dirent) +{ + printf ("ino: %" PRIi64 "\n", dirent->ino); + printf ("ftyp: %c\n", dirent->ftyp); + printf ("name: %s\n", dirent->name); +} + +static void print_dirent_list (struct guestfs_dirent_list *dirents) +{ + int i; + + for (i = 0; i < dirents->len; ++i) + print_dirent (&dirents->val[i]); +} + static int run_launch (const char *cmd, int argc, char *argv[]) { int r; @@ -3329,6 +3348,23 @@ static int run_umask (const char *cmd, int argc, char *argv[]) return 0; } +static int run_readdir (const char *cmd, int argc, char *argv[]) +{ + struct guestfs_dirent_list *r; + const char *dir; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + dir = argv[0]; + r = guestfs_readdir (g, dir); + if (r == NULL) return -1; + print_dirent_list (r); + guestfs_free_dirent_list (r); + return 0; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -3811,6 +3847,9 @@ int run_action (const char *cmd, int argc, char *argv[]) if (strcasecmp (cmd, "umask") == 0) return run_umask (cmd, argc, argv); else + if (strcasecmp (cmd, "readdir") == 0) + return run_readdir (cmd, argc, argv); + else { fprintf (stderr, "%s: unknown command\n", cmd); return -1; diff --git a/fish/completion.c b/fish/completion.c index 2ddae2e..b506559 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -207,6 +207,7 @@ static const char *const commands[] = { "mknod-b", "mknod-c", "umask", + "readdir", NULL }; diff --git a/guestfish-actions.pod b/guestfish-actions.pod index e727120..e635e54 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -1235,6 +1235,20 @@ Note that this function cannot correctly handle binary files as end of line). For those you need to use the C function which has a more complex interface. +=head2 readdir + + readdir dir + +This returns the list of directory entries in directory C. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I sorted, but returned in the same +order as the underlying filesystem. + +This function is primarily intended for use by programs. To +get a simple list of names, use C. To get a printable +directory for human consumption, use C. + =head2 resize2fs resize2fs device diff --git a/guestfs-actions.pod b/guestfs-actions.pod index 5f66318..5821e0e 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -1647,6 +1647,26 @@ This function returns a NULL-terminated array of strings (like L), or NULL if there was an error. I. +=head2 guestfs_readdir + + struct guestfs_dirent_list *guestfs_readdir (guestfs_h *handle, + const char *dir); + +This returns the list of directory entries in directory C. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I sorted, but returned in the same +order as the underlying filesystem. + +This function is primarily intended for use by programs. To +get a simple list of names, use C. To get a printable +directory for human consumption, use C. + +This function returns a C +(see Eguestfs-structs.hE), +or NULL if there was an error. +I after use>. + =head2 guestfs_resize2fs int guestfs_resize2fs (guestfs_h *handle, diff --git a/guestfs-structs.pod b/guestfs-structs.pod index 31b9ff2..dd6b0ed 100644 --- a/guestfs-structs.pod +++ b/guestfs-structs.pod @@ -85,3 +85,52 @@ void guestfs_free_lvm_lv_list (struct guestfs_free_lvm_lv_list *); +=head2 guestfs_stat + + struct guestfs_stat { + int64_t dev; + int64_t ino; + int64_t mode; + int64_t nlink; + int64_t uid; + int64_t gid; + int64_t rdev; + int64_t size; + int64_t blksize; + int64_t blocks; + int64_t atime; + int64_t mtime; + int64_t ctime; + }; + +=head2 guestfs_statvfs + + struct guestfs_statvfs { + int64_t bsize; + int64_t frsize; + int64_t blocks; + int64_t bfree; + int64_t bavail; + int64_t files; + int64_t ffree; + int64_t favail; + int64_t fsid; + int64_t flag; + int64_t namemax; + }; + +=head2 guestfs_dirent + + struct guestfs_dirent { + int64_t ino; + char ftyp; + char *name; + }; + + struct guestfs_dirent_list { + uint32_t len; /* Number of elements in list. */ + struct guestfs_dirent *val; /* Elements. */ + }; + + void guestfs_free_dirent_list (struct guestfs_free_dirent_list *); + diff --git a/java/Makefile.am b/java/Makefile.am index 633ecda..ea90a48 100644 --- a/java/Makefile.am +++ b/java/Makefile.am @@ -25,6 +25,7 @@ java_sources = \ $(CPTH)/LV.java \ $(CPTH)/Stat.java \ $(CPTH)/StatVFS.java \ + $(CPTH)/Dirent.java \ $(CPTH)/GuestFS.java java_tests = \ diff --git a/java/com/redhat/et/libguestfs/Dirent.java b/java/com/redhat/et/libguestfs/Dirent.java new file mode 100644 index 0000000..f6ef3d8 --- /dev/null +++ b/java/com/redhat/et/libguestfs/Dirent.java @@ -0,0 +1,34 @@ +/* libguestfs generated file + * WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'. + * ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST. + * + * Copyright (C) 2009 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.redhat.et.libguestfs; + +/** + * Libguestfs Dirent structure. + * + * @author rjones + * @see GuestFS + */ +public class Dirent { + public long ino; + public char ftyp; + public String name; +} diff --git a/java/com/redhat/et/libguestfs/GuestFS.java b/java/com/redhat/et/libguestfs/GuestFS.java index cc94a98..e8f36ff 100644 --- a/java/com/redhat/et/libguestfs/GuestFS.java +++ b/java/com/redhat/et/libguestfs/GuestFS.java @@ -29,6 +29,7 @@ import com.redhat.et.libguestfs.LV; import com.redhat.et.libguestfs.Stat; import com.redhat.et.libguestfs.StatVFS; import com.redhat.et.libguestfs.IntBool; +import com.redhat.et.libguestfs.Dirent; /** * The GuestFS object is a libguestfs handle. @@ -4054,4 +4055,30 @@ public HashMap test0rhashtableerr () private native int _umask (long g, int mask) throws LibGuestFSException; + /** + * read directories entries + *

+ * This returns the list of directory entries in directory + * "dir". + *

+ * All entries in the directory are returned, including "." + * and "..". The entries are *not* sorted, but returned in + * the same order as the underlying filesystem. + *

+ * This function is primarily intended for use by programs. + * To get a simple list of names, use "g.ls". To get a + * printable directory for human consumption, use "g.ll". + *

+ * @throws LibGuestFSException + */ + public Dirent[] readdir (String dir) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("readdir: handle is closed"); + return _readdir (g, dir); + } + private native Dirent[] _readdir (long g, String dir) + throws LibGuestFSException; + } diff --git a/java/com_redhat_et_libguestfs_GuestFS.c b/java/com_redhat_et_libguestfs_GuestFS.c index 91095a4..52137ec 100644 --- a/java/com_redhat_et_libguestfs_GuestFS.c +++ b/java/com_redhat_et_libguestfs_GuestFS.c @@ -4635,3 +4635,39 @@ Java_com_redhat_et_libguestfs_GuestFS__1umask return (jint) r; } +JNIEXPORT jobjectArray JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1readdir + (JNIEnv *env, jobject obj, jlong jg, jstring jdir) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jobjectArray jr; + jclass cl; + jfieldID fl; + jobject jfl; + struct guestfs_dirent_list *r; + const char *dir; + int i; + + dir = (*env)->GetStringUTFChars (env, jdir, NULL); + r = guestfs_readdir (g, dir); + (*env)->ReleaseStringUTFChars (env, jdir, dir); + if (r == NULL) { + throw_exception (env, guestfs_last_error (g)); + return NULL; + } + cl = (*env)->FindClass (env, "com/redhat/et/libguestfs/Dirent"); + jr = (*env)->NewObjectArray (env, r->len, cl, NULL); + for (i = 0; i < r->len; ++i) { + jfl = (*env)->AllocObject (env, cl); + fl = (*env)->GetFieldID (env, cl, "ino", "J"); + (*env)->SetLongField (env, jfl, fl, r->val[i].ino); + fl = (*env)->GetFieldID (env, cl, "ftyp", "J"); + (*env)->SetLongField (env, jfl, fl, r->val[i].ftyp); + fl = (*env)->GetFieldID (env, cl, "name", "Ljava/lang/String;"); + (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, r->val[i].name)); + (*env)->SetObjectArrayElement (env, jfl, i, jfl); + } + guestfs_free_dirent_list (r); + return jr; +} + diff --git a/ocaml/Makefile.am b/ocaml/Makefile.am index cb4ad34..bed1f92 100644 --- a/ocaml/Makefile.am +++ b/ocaml/Makefile.am @@ -50,9 +50,11 @@ TESTS_ENVIRONMENT = \ $(VG) TESTS = run-bindtests \ - t/guestfs_005_load t/guestfs_010_launch t/guestfs_050_lvcreate + t/guestfs_005_load t/guestfs_010_launch t/guestfs_050_lvcreate \ + t/guestfs_060_readdir noinst_DATA += bindtests \ - t/guestfs_005_load t/guestfs_010_launch t/guestfs_050_lvcreate + t/guestfs_005_load t/guestfs_010_launch t/guestfs_050_lvcreate \ + t/guestfs_060_readdir bindtests: bindtests.ml mlguestfs.cmxa $(OCAMLFIND) ocamlopt -cclib -L$(top_builddir)/src/.libs -I . unix.cmxa mlguestfs.cmxa $< -o $@ @@ -66,6 +68,9 @@ t/guestfs_010_launch: t/guestfs_010_launch.ml mlguestfs.cmxa t/guestfs_050_lvcreate: t/guestfs_050_lvcreate.ml mlguestfs.cmxa $(OCAMLFIND) ocamlopt -cclib -L$(top_builddir)/src/.libs -I . unix.cmxa mlguestfs.cmxa $< -o $@ +t/guestfs_060_readdir: t/guestfs_060_readdir.ml mlguestfs.cmxa + $(OCAMLFIND) ocamlopt -cclib -L$(top_builddir)/src/.libs -I . unix.cmxa mlguestfs.cmxa $< -o $@ + .mli.cmi: $(OCAMLFIND) ocamlc -c $< .ml.cmo: diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index 0bf458c..0d3349f 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -115,6 +115,12 @@ type statvfs = { namemax : int64; } +type dirent = { + ino : int64; + ftyp : char; + name : string; +} + external test0 : t -> string -> string option -> string array -> bool -> int -> string -> string -> unit = "ocaml_guestfs_test0_byte" "ocaml_guestfs_test0" external test0rint : t -> string -> int = "ocaml_guestfs_test0rint" external test0rinterr : t -> int = "ocaml_guestfs_test0rinterr" @@ -306,3 +312,4 @@ external mkfifo : t -> int -> string -> unit = "ocaml_guestfs_mkfifo" external mknod_b : t -> int -> int -> int -> string -> unit = "ocaml_guestfs_mknod_b" external mknod_c : t -> int -> int -> int -> string -> unit = "ocaml_guestfs_mknod_c" external umask : t -> int -> int = "ocaml_guestfs_umask" +external readdir : t -> string -> dirent array = "ocaml_guestfs_readdir" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 5cab588..d15335e 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -124,6 +124,12 @@ type statvfs = { namemax : int64; } +type dirent = { + ino : int64; + ftyp : char; + name : string; +} + val test0 : t -> string -> string option -> string array -> bool -> int -> string -> string -> unit (** internal test function - do not use *) @@ -697,3 +703,6 @@ val mknod_c : t -> int -> int -> int -> string -> unit val umask : t -> int -> int (** set file mode creation mask (umask) *) +val readdir : t -> string -> dirent array +(** read directories entries *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index 6db7239..498b4b1 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -328,6 +328,41 @@ copy_statvfs (const struct guestfs_statvfs *statvfs) CAMLreturn (rv); } +static CAMLprim value +copy_dirent (const struct guestfs_dirent *dirent) +{ + CAMLparam0 (); + CAMLlocal2 (rv, v); + + rv = caml_alloc (3, 0); + v = caml_copy_int64 (dirent->ino); + Store_field (rv, 0, v); + v = Val_int (dirent->ftyp); + Store_field (rv, 1, v); + v = caml_copy_string (dirent->name); + Store_field (rv, 2, v); + CAMLreturn (rv); +} + +static CAMLprim value +copy_dirent_list (const struct guestfs_dirent_list *dirents) +{ + CAMLparam0 (); + CAMLlocal2 (rv, v); + int i; + + if (dirents->len == 0) + CAMLreturn (Atom (0)); + else { + rv = caml_alloc (dirents->len, 0); + for (i = 0; i < dirents->len; ++i) { + v = copy_dirent (&dirents->val[i]); + caml_modify (&Field (rv, i), v); + } + CAMLreturn (rv); + } +} + CAMLprim value ocaml_guestfs_test0 (value gv, value strv, value optstrv, value strlistv, value bv, value integerv, value fileinv, value fileoutv) { @@ -4902,3 +4937,27 @@ ocaml_guestfs_umask (value gv, value maskv) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_readdir (value gv, value dirv) +{ + CAMLparam2 (gv, dirv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("readdir: used handle after closing it"); + + const char *dir = String_val (dirv); + struct guestfs_dirent_list *r; + + caml_enter_blocking_section (); + r = guestfs_readdir (g, dir); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "readdir"); + + rv = copy_dirent_list (r); + guestfs_free_dirent_list (r); + CAMLreturn (rv); +} + diff --git a/ocaml/t/guestfs_060_readdir.ml b/ocaml/t/guestfs_060_readdir.ml new file mode 100644 index 0000000..f333810 --- /dev/null +++ b/ocaml/t/guestfs_060_readdir.ml @@ -0,0 +1,54 @@ +(* libguestfs OCaml bindings + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + *) + +open Unix + +let () = + let g = Guestfs.create () in + + let fd = openfile "test.img" [O_WRONLY;O_CREAT;O_NOCTTY;O_TRUNC] 0o666 in + ftruncate fd (10 * 1024 * 1024); + close fd; + + Guestfs.add_drive g "test.img"; + Guestfs.launch g; + Guestfs.wait_ready g; + + Guestfs.sfdisk g "/dev/sda" 0 0 0 [|","|]; + Guestfs.mkfs g "ext2" "/dev/sda1"; + Guestfs.mount g "/dev/sda1" "/"; + Guestfs.mkdir g "/p"; + Guestfs.touch g "/q"; + + let dirs = Guestfs.readdir g "/" in + let dirs = Array.to_list dirs in + let cmp { Guestfs.name = n1 } { Guestfs.name = n2 } = compare n1 n2 in + let dirs = List.sort cmp dirs in + let dirs = List.map ( + fun { Guestfs.name = name; Guestfs.ftyp = ftyp } -> (name, ftyp) + ) dirs in + + if dirs <> [ ".", 'd'; + "..", 'd'; + "lost+found", 'd'; + "p", 'd'; + "q", 'r' ] then + failwith "Guestfs.readdir returned incorrect result"; + + Guestfs.close g; + unlink "test.img" diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index 8f4ab5f..e1cd84a 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -356,7 +356,7 @@ PREINIT: (void) hv_store (hv, "pe_start", 8, my_newSVull (valout->val[i].pe_start), 0); (void) hv_store (hv, "pv_mda_count", 12, my_newSVll (valout->val[i].pv_mda_count), 0); (void) hv_store (hv, "pv_mda_free", 11, my_newSVull (valout->val[i].pv_mda_free), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_pv_list (valout); @@ -388,7 +388,7 @@ PREINIT: (void) hv_store (hv, "pe_start", 8, my_newSVull (valout->val[i].pe_start), 0); (void) hv_store (hv, "pv_mda_count", 12, my_newSVll (valout->val[i].pv_mda_count), 0); (void) hv_store (hv, "pv_mda_free", 11, my_newSVull (valout->val[i].pv_mda_free), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_pv_list (valout); @@ -426,7 +426,7 @@ PREINIT: (void) hv_store (hv, "vg_tags", 7, newSVpv (valout->val[i].vg_tags, 0), 0); (void) hv_store (hv, "vg_mda_count", 12, my_newSVll (valout->val[i].vg_mda_count), 0); (void) hv_store (hv, "vg_mda_free", 11, my_newSVull (valout->val[i].vg_mda_free), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_vg_list (valout); @@ -463,7 +463,7 @@ PREINIT: (void) hv_store (hv, "vg_tags", 7, newSVpv (valout->val[i].vg_tags, 0), 0); (void) hv_store (hv, "vg_mda_count", 12, my_newSVll (valout->val[i].vg_mda_count), 0); (void) hv_store (hv, "vg_mda_free", 11, my_newSVull (valout->val[i].vg_mda_free), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_vg_list (valout); @@ -498,7 +498,7 @@ PREINIT: (void) hv_store (hv, "lv_tags", 7, newSVpv (valout->val[i].lv_tags, 0), 0); (void) hv_store (hv, "mirror_log", 10, newSVpv (valout->val[i].mirror_log, 0), 0); (void) hv_store (hv, "modules", 7, newSVpv (valout->val[i].modules, 0), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_lv_list (valout); @@ -532,7 +532,7 @@ PREINIT: (void) hv_store (hv, "lv_tags", 7, newSVpv (valout->val[i].lv_tags, 0), 0); (void) hv_store (hv, "mirror_log", 10, newSVpv (valout->val[i].mirror_log, 0), 0); (void) hv_store (hv, "modules", 7, newSVpv (valout->val[i].modules, 0), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_lv_list (valout); @@ -1185,7 +1185,7 @@ PREINIT: (void) hv_store (hv, "pe_start", 8, my_newSVull (physvols->val[i].pe_start), 0); (void) hv_store (hv, "pv_mda_count", 12, my_newSVll (physvols->val[i].pv_mda_count), 0); (void) hv_store (hv, "pv_mda_free", 11, my_newSVull (physvols->val[i].pv_mda_free), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_pv_list (physvols); @@ -1222,7 +1222,7 @@ PREINIT: (void) hv_store (hv, "vg_tags", 7, newSVpv (volgroups->val[i].vg_tags, 0), 0); (void) hv_store (hv, "vg_mda_count", 12, my_newSVll (volgroups->val[i].vg_mda_count), 0); (void) hv_store (hv, "vg_mda_free", 11, my_newSVull (volgroups->val[i].vg_mda_free), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_vg_list (volgroups); @@ -1256,7 +1256,7 @@ PREINIT: (void) hv_store (hv, "lv_tags", 7, newSVpv (logvols->val[i].lv_tags, 0), 0); (void) hv_store (hv, "mirror_log", 10, newSVpv (logvols->val[i].mirror_log, 0), 0); (void) hv_store (hv, "modules", 7, newSVpv (logvols->val[i].modules, 0), 0); - PUSHs (sv_2mortal ((SV *) hv)); + PUSHs (sv_2mortal (newRV ((SV *) hv))); } guestfs_free_lvm_lv_list (logvols); @@ -2970,3 +2970,25 @@ PREINIT: OUTPUT: RETVAL +void +readdir (g, dir) + guestfs_h *g; + char *dir; +PREINIT: + struct guestfs_dirent_list *entries; + int i; + HV *hv; + PPCODE: + entries = guestfs_readdir (g, dir); + if (entries == NULL) + croak ("readdir: %s", guestfs_last_error (g)); + EXTEND (SP, entries->len); + for (i = 0; i < entries->len; ++i) { + hv = newHV (); + (void) hv_store (hv, "ino", 3, my_newSVull (entries->val[i].ino), 0); + (void) hv_store (hv, "ftyp", 4, newSVpv (&entries->val[i].ftyp, 1), 0); + (void) hv_store (hv, "name", 4, newSVpv (entries->val[i].name, 0), 0); + PUSHs (newRV (sv_2mortal ((SV *) hv))); + } + guestfs_free_dirent_list (entries); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index cd26bed..e624d55 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -1118,6 +1118,18 @@ Note that this function cannot correctly handle binary files as end of line). For those you need to use the C<$h-Eread_file> function which has a more complex interface. +=item @entries = $h->readdir ($dir); + +This returns the list of directory entries in directory C

. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I sorted, but returned in the same +order as the underlying filesystem. + +This function is primarily intended for use by programs. To +get a simple list of names, use C<$h-Els>. To get a printable +directory for human consumption, use C<$h-Ell>. + =item $h->resize2fs ($device); This resizes an ext2 or ext3 filesystem to match the size of diff --git a/perl/t/060-readdir.t b/perl/t/060-readdir.t new file mode 100644 index 0000000..17dfd24 --- /dev/null +++ b/perl/t/060-readdir.t @@ -0,0 +1,63 @@ +# libguestfs Perl bindings -*- perl -*- +# Copyright (C) 2009 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +use strict; +use warnings; +use Test::More tests => 13; + +use Sys::Guestfs; + +my $h = Sys::Guestfs->new (); +ok ($h); +open FILE, ">test.img"; +truncate FILE, 10*1024*1024; +close FILE; +ok (1); + +$h->add_drive ("test.img"); +ok (1); + +$h->launch (); +ok (1); +$h->wait_ready (); +ok (1); + +$h->sfdisk ("/dev/sda", 0, 0, 0, [","]); +ok (1); +$h->mkfs ("ext2", "/dev/sda1"); +ok (1); +$h->mount ("/dev/sda1", "/"); +ok (1); +$h->mkdir ("/p"); +ok (1); +$h->touch ("/q"); +ok (1); + +my @dirs = $h->readdir ("/"); +@dirs = sort { $a->{name} cmp $b->{name} } @dirs; +foreach (@dirs) { + print "$_->{name} $_->{ino} $_->{ftyp}\n"; +} +ok (1); + +$h->sync (); +ok (1); + +undef $h; +ok (1); + +unlink ("test.img"); diff --git a/python/guestfs-py.c b/python/guestfs-py.c index 98a14a0..44b446a 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -389,6 +389,33 @@ put_statvfs (struct guestfs_statvfs *statvfs) }; static PyObject * +put_dirent (struct guestfs_dirent *dirent) +{ + PyObject *dict; + + dict = PyDict_New (); + PyDict_SetItemString (dict, "ino", + PyLong_FromLongLong (dirent->ino)); + PyDict_SetItemString (dict, "ftyp", + PyString_FromStringAndSize (&dirent->ftyp, 1)); + PyDict_SetItemString (dict, "name", + PyString_FromString (dirent->name)); + return dict; +}; + +static PyObject * +put_dirent_list (struct guestfs_dirent_list *dirents) +{ + PyObject *list; + int i; + + list = PyList_New (dirents->len); + for (i = 0; i < dirents->len; ++i) + PyList_SetItem (list, i, put_dirent (&dirents->val[i])); + return list; +}; + +static PyObject * py_guestfs_test0 (PyObject *self, PyObject *args) { PyObject *py_g; @@ -5187,6 +5214,31 @@ py_guestfs_umask (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_readdir (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + struct guestfs_dirent_list *r; + const char *dir; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_readdir", + &py_g, &dir)) + return NULL; + g = get_handle (py_g); + + r = guestfs_readdir (g, dir); + if (r == NULL) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + py_r = put_dirent_list (r); + guestfs_free_dirent_list (r); + return py_r; +} + static PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -5381,6 +5433,7 @@ static PyMethodDef methods[] = { { (char *) "mknod_b", py_guestfs_mknod_b, METH_VARARGS, NULL }, { (char *) "mknod_c", py_guestfs_mknod_c, METH_VARARGS, NULL }, { (char *) "umask", py_guestfs_umask, METH_VARARGS, NULL }, + { (char *) "readdir", py_guestfs_readdir, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index ad44df7..784c567 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -1958,3 +1958,20 @@ class GuestFS: """ return libguestfsmod.umask (self._o, mask) + def readdir (self, dir): + u"""This returns the list of directory entries in directory + "dir". + + All entries in the directory are returned, including "." + and "..". The entries are *not* sorted, but returned in + the same order as the underlying filesystem. + + This function is primarily intended for use by programs. + To get a simple list of names, use "g.ls". To get a + printable directory for human consumption, use "g.ll". + + This function returns a list of directory entries. Each + directory entry is represented as a dictionary. + """ + return libguestfsmod.readdir (self._o, dir) + diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index 606e9a2..38776b6 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -4865,6 +4865,38 @@ static VALUE ruby_guestfs_umask (VALUE gv, VALUE maskv) return INT2NUM (r); } +static VALUE ruby_guestfs_readdir (VALUE gv, VALUE dirv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "readdir"); + + Check_Type (dirv, T_STRING); + const char *dir = StringValueCStr (dirv); + if (!dir) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "dir", "readdir"); + + struct guestfs_dirent_list *r; + + r = guestfs_readdir (g, dir); + if (r == NULL) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + VALUE rv = rb_ary_new2 (r->len); + int i; + for (i = 0; i < r->len; ++i) { + VALUE hv = rb_hash_new (); + rb_hash_aset (rv, rb_str_new2 ("ino"), ULL2NUM (r->val[i].ino)); + rb_hash_aset (rv, rb_str_new2 ("ftyp"), ULL2NUM (r->val[i].ftyp)); + rb_hash_aset (rv, rb_str_new2 ("name"), rb_str_new2 (r->val[i].name)); + rb_ary_push (rv, hv); + } + guestfs_free_dirent_list (r); + return rv; +} + /* Initialize the module. */ void Init__guestfs () { @@ -5257,4 +5289,6 @@ void Init__guestfs () ruby_guestfs_mknod_c, 4); rb_define_method (c_guestfs, "umask", ruby_guestfs_umask, 1); + rb_define_method (c_guestfs, "readdir", + ruby_guestfs_readdir, 1); } diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 065fd3e..93e7803 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -137 +138 diff --git a/src/generator.ml b/src/generator.ml index 69fd706..df5ff7e 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -83,6 +83,8 @@ and ret = * inefficient. Keys should be unique. NULLs are not permitted. *) | RHashtable of string + (* List of directory entries (the result of readdir(3)). *) + | RDirentList of string and args = argt list (* Function parameters, guestfs handle is implicit. *) @@ -2751,6 +2753,20 @@ See also L, C, C. This call returns the previous umask."); + ("readdir", (RDirentList "entries", [String "dir"]), 138, [], + [], + "read directories entries", + "\ +This returns the list of directory entries in directory C. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I sorted, but returned in the same +order as the underlying filesystem. + +This function is primarily intended for use by programs. To +get a simple list of names, use C. To get a printable +directory for human consumption, use C."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -2858,6 +2874,13 @@ let statvfs_cols = [ "namemax", `Int; ] +(* Column names in dirent structure. *) +let dirent_cols = [ + "ino", `Int; + "ftyp", `Char; (* 'b' 'c' 'd' 'f' (FIFO) 'l' 'r' (regular file) 's' 'u' '?' *) + "name", `String; +] + (* Used for testing language bindings. *) type callt = | CallString of string @@ -3042,7 +3065,8 @@ let check_functions () = | RInt n | RInt64 n | RBool n | RConstString n | RString n | RStringList n | RPVList n | RVGList n | RLVList n | RStat n | RStatVFS n - | RHashtable n -> + | RHashtable n + | RDirentList n -> check_arg_ret_name n | RIntBool (n,m) -> check_arg_ret_name n; @@ -3247,6 +3271,11 @@ strings, or NULL if there was an error. The array of strings will always have length C<2n+1>, where C keys and values alternate, followed by the trailing NULL entry. I.\n\n" + | RDirentList _ -> + pr "This function returns a C +(see Eguestfs-structs.hE), +or NULL if there was an error. +I after use>.\n\n" ); if List.mem ProtocolLimitWarning flags then pr "%s\n\n" protocol_limit_warning; @@ -3283,7 +3312,41 @@ and generate_structs_pod () = pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n" typ typ; pr "\n" - ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols] + ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]; + + (* Stat *) + List.iter ( + fun (typ, cols) -> + pr "=head2 guestfs_%s\n" typ; + pr "\n"; + pr " struct guestfs_%s {\n" typ; + List.iter ( + function + | name, `Int -> pr " int64_t %s;\n" name + ) cols; + pr " };\n"; + pr "\n"; + ) [ "stat", stat_cols; "statvfs", statvfs_cols ]; + + (* DirentList *) + pr "=head2 guestfs_dirent\n"; + pr "\n"; + pr " struct guestfs_dirent {\n"; + List.iter ( + function + | name, `String -> pr " char *%s;\n" name + | name, `Int -> pr " int64_t %s;\n" name + | name, `Char -> pr " char %s;\n" name + ) dirent_cols; + pr " };\n"; + pr "\n"; + pr " struct guestfs_dirent_list {\n"; + pr " uint32_t len; /* Number of elements in list. */\n"; + pr " struct guestfs_dirent *val; /* Elements. */\n"; + pr " };\n"; + pr " \n"; + pr " void guestfs_free_dirent_list (struct guestfs_free_dirent_list *);\n"; + pr "\n" (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'. @@ -3330,6 +3393,18 @@ and generate_xdr () = pr "\n"; ) ["stat", stat_cols; "statvfs", statvfs_cols]; + (* Dirent structures. *) + pr "struct guestfs_int_dirent {\n"; + List.iter (function + | name, `Int -> pr " hyper %s;\n" name + | name, `Char -> pr " char %s;\n" name + | name, `String -> pr " string %s<>;\n" name + ) dirent_cols; + pr "};\n"; + pr "\n"; + pr "typedef struct guestfs_int_dirent guestfs_int_dirent_list<>;\n"; + pr "\n"; + List.iter ( fun (shortname, style, _, _, _, _, _) -> let name = "guestfs_" ^ shortname in @@ -3402,6 +3477,10 @@ and generate_xdr () = pr "struct %s_ret {\n" name; pr " str %s<>;\n" n; pr "};\n\n" + | RDirentList n -> + pr "struct %s_ret {\n" name; + pr " guestfs_int_dirent_list %s;\n" n; + pr "};\n\n" ); ) daemon_functions; @@ -3529,7 +3608,23 @@ and generate_structs_h () = ) cols; pr "};\n"; pr "\n" - ) ["stat", stat_cols; "statvfs", statvfs_cols] + ) ["stat", stat_cols; "statvfs", statvfs_cols]; + + (* Dirent structures. *) + pr "struct guestfs_dirent {\n"; + List.iter ( + function + | name, `Int -> pr " int64_t %s;\n" name + | name, `Char -> pr " char %s;\n" name + | name, `String -> pr " char *%s;\n" name + ) dirent_cols; + pr "};\n"; + pr "\n"; + pr "struct guestfs_dirent_list {\n"; + pr " uint32_t len;\n"; + pr " struct guestfs_dirent *val;\n"; + pr "};\n"; + pr "\n" (* Generate the guestfs-actions.h file. *) and generate_actions_h () = @@ -3637,7 +3732,8 @@ check_state (guestfs_h *g, const char *caller) | RIntBool _ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _ - | RHashtable _ -> + | RHashtable _ + | RDirentList _ -> pr " struct %s_ret ret;\n" name ); pr "};\n"; @@ -3680,7 +3776,8 @@ check_state (guestfs_h *g, const char *caller) | RIntBool _ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _ - | RHashtable _ -> + | RHashtable _ + | RDirentList _ -> pr " if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name; pr " error (g, \"%%s: failed to parse reply\", \"%s\");\n" name; pr " return;\n"; @@ -3703,7 +3800,8 @@ check_state (guestfs_h *g, const char *caller) | RString _ | RStringList _ | RIntBool _ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _ - | RHashtable _ -> + | RHashtable _ + | RDirentList _ -> "NULL" in pr "{\n"; @@ -3839,7 +3937,8 @@ check_state (guestfs_h *g, const char *caller) pr " /* caller with free this */\n"; pr " return safe_memdup (g, &ctx.ret, sizeof (ctx.ret));\n" | RPVList n | RVGList n | RLVList n - | RStat n | RStatVFS n -> + | RStat n | RStatVFS n + | RDirentList n -> pr " /* caller will free this */\n"; pr " return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n ); @@ -3899,7 +3998,8 @@ and generate_daemon_actions () = | RVGList _ -> pr " guestfs_lvm_int_vg_list *r;\n"; "NULL" | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL" | RStat _ -> pr " guestfs_int_stat *r;\n"; "NULL" - | RStatVFS _ -> pr " guestfs_int_statvfs *r;\n"; "NULL" in + | RStatVFS _ -> pr " guestfs_int_statvfs *r;\n"; "NULL" + | RDirentList _ -> pr " guestfs_int_dirent_list *r;\n"; "NULL" in (match snd style with | [] -> () @@ -4000,7 +4100,8 @@ and generate_daemon_actions () = name; pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name | RPVList n | RVGList n | RLVList n - | RStat n | RStatVFS n -> + | RStat n | RStatVFS n + | RDirentList n -> pr " struct guestfs_%s_ret ret;\n" name; pr " ret.%s = *r;\n" n; pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" @@ -4781,7 +4882,9 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = | RStat _ -> pr " struct guestfs_stat *r;\n"; "NULL" | RStatVFS _ -> - pr " struct guestfs_statvfs *r;\n"; "NULL" in + pr " struct guestfs_statvfs *r;\n"; "NULL" + | RDirentList _ -> + pr " struct guestfs_dirent_list *r;\n"; "NULL" in pr " suppress_error = %d;\n" (if expect_error then 1 else 0); pr " r = guestfs_%s (g" name; @@ -4837,6 +4940,8 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd = pr " guestfs_free_lvm_lv_list (r);\n" | RStat _ | RStatVFS _ -> pr " free (r);\n" + | RDirentList _ -> + pr " guestfs_free_dirent_list (r);\n" ); pr " }\n" @@ -4992,6 +5097,29 @@ and generate_fish_cmds () = pr "\n"; ) ["stat", stat_cols; "statvfs", statvfs_cols]; + (* print_dirent_list function *) + pr "static void print_dirent (struct guestfs_dirent *dirent)\n"; + pr "{\n"; + List.iter ( + function + | name, `String -> + pr " printf (\"%s: %%s\\n\", dirent->%s);\n" name name + | name, `Int -> + pr " printf (\"%s: %%\" PRIi64 \"\\n\", dirent->%s);\n" name name + | name, `Char -> + pr " printf (\"%s: %%c\\n\", dirent->%s);\n" name name + ) dirent_cols; + pr "}\n"; + pr "\n"; + pr "static void print_dirent_list (struct guestfs_dirent_list *dirents)\n"; + pr "{\n"; + pr " int i;\n"; + pr "\n"; + pr " for (i = 0; i < dirents->len; ++i)\n"; + pr " print_dirent (&dirents->val[i]);\n"; + pr "}\n"; + pr "\n"; + (* run_ actions *) List.iter ( fun (name, style, _, flags, _, _, _) -> @@ -5011,6 +5139,7 @@ and generate_fish_cmds () = | RLVList _ -> pr " struct guestfs_lvm_lv_list *r;\n" | RStat _ -> pr " struct guestfs_stat *r;\n" | RStatVFS _ -> pr " struct guestfs_statvfs *r;\n" + | RDirentList _ -> pr " struct guestfs_dirent_list *r;\n" ); List.iter ( function @@ -5125,6 +5254,11 @@ and generate_fish_cmds () = pr " print_table (r);\n"; pr " free_strings (r);\n"; pr " return 0;\n" + | RDirentList _ -> + pr " if (r == NULL) return -1;\n"; + pr " print_dirent_list (r);\n"; + pr " guestfs_free_dirent_list (r);\n"; + pr " return 0;\n" ); pr "}\n"; pr "\n" @@ -5335,6 +5469,9 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) | RStatVFS _ -> if not in_daemon then pr "struct guestfs_statvfs *" else pr "guestfs_int_statvfs *" + | RDirentList _ -> + if not in_daemon then pr "struct guestfs_dirent_list *" + else pr "guestfs_int_dirent_list *" ); pr "%s%s (" prefix name; if handle = None && List.length (snd style) = 0 then @@ -5416,6 +5553,8 @@ val close : t -> unit generate_ocaml_stat_structure_decls (); + generate_ocaml_dirent_structure_decls (); + (* The actions. *) List.iter ( fun (name, style, _, _, _, shortdesc, _) -> @@ -5443,6 +5582,8 @@ let () = generate_ocaml_stat_structure_decls (); + generate_ocaml_dirent_structure_decls (); + (* The actions. *) List.iter ( fun (name, style, _, _, _, shortdesc, _) -> @@ -5585,6 +5726,50 @@ copy_table (char * const * argv) pr "\n"; ) ["stat", stat_cols; "statvfs", statvfs_cols]; + (* Dirent copy functions. *) + pr "static CAMLprim value\n"; + pr "copy_dirent (const struct guestfs_dirent *dirent)\n"; + pr "{\n"; + pr " CAMLparam0 ();\n"; + pr " CAMLlocal2 (rv, v);\n"; + pr "\n"; + pr " rv = caml_alloc (%d, 0);\n" (List.length dirent_cols); + iteri ( + fun i col -> + (match col with + | name, `String -> + pr " v = caml_copy_string (dirent->%s);\n" name + | name, `Int -> + pr " v = caml_copy_int64 (dirent->%s);\n" name + | name, `Char -> + pr " v = Val_int (dirent->%s);\n" name + ); + pr " Store_field (rv, %d, v);\n" i + ) dirent_cols; + pr " CAMLreturn (rv);\n"; + pr "}\n"; + pr "\n"; + + pr "static CAMLprim value\n"; + pr "copy_dirent_list (const struct guestfs_dirent_list *dirents)\n"; + pr "{\n"; + pr " CAMLparam0 ();\n"; + pr " CAMLlocal2 (rv, v);\n"; + pr " int i;\n"; + pr "\n"; + pr " if (dirents->len == 0)\n"; + pr " CAMLreturn (Atom (0));\n"; + pr " else {\n"; + pr " rv = caml_alloc (dirents->len, 0);\n"; + pr " for (i = 0; i < dirents->len; ++i) {\n"; + pr " v = copy_dirent (&dirents->val[i]);\n"; + pr " caml_modify (&Field (rv, i), v);\n"; + pr " }\n"; + pr " CAMLreturn (rv);\n"; + pr " }\n"; + pr "}\n"; + pr "\n"; + (* The wrappers. *) List.iter ( fun (name, style, _, _, _, _, _) -> @@ -5659,7 +5844,9 @@ copy_table (char * const * argv) | RHashtable _ -> pr " int i;\n"; pr " char **r;\n"; - "NULL" in + "NULL" + | RDirentList _ -> + pr " struct guestfs_dirent_list *r;\n"; "NULL" in pr "\n"; pr " caml_enter_blocking_section ();\n"; @@ -5717,6 +5904,9 @@ copy_table (char * const * argv) pr " rv = copy_table (r);\n"; pr " for (i = 0; r[i] != NULL; ++i) free (r[i]);\n"; pr " free (r);\n"; + | RDirentList _ -> + pr " rv = copy_dirent_list (r);\n"; + pr " guestfs_free_dirent_list (r);\n"; ); pr " CAMLreturn (rv);\n"; @@ -5763,6 +5953,17 @@ and generate_ocaml_stat_structure_decls () = pr "\n" ) ["stat", stat_cols; "statvfs", statvfs_cols] +and generate_ocaml_dirent_structure_decls () = + pr "type dirent = {\n"; + List.iter ( + function + | name, `Int -> pr " %s : int64;\n" name + | name, `Char -> pr " %s : char;\n" name + | name, `String -> pr " %s : string;\n" name + ) dirent_cols; + pr "}\n"; + pr "\n" + and generate_ocaml_prototype ?(is_external = false) name style = if is_external then pr "external " else pr "val "; pr "%s : t -> " name; @@ -5789,6 +5990,7 @@ and generate_ocaml_prototype ?(is_external = false) name style = | RStat _ -> pr "stat" | RStatVFS _ -> pr "statvfs" | RHashtable _ -> pr "(string * string) list" + | RDirentList _ -> pr "dirent array" ); if is_external then ( pr " = "; @@ -5905,7 +6107,8 @@ DESTROY (g) | RIntBool _ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _ - | RHashtable _ -> + | RHashtable _ + | RDirentList _ -> pr "void\n" (* all lists returned implictly on the stack *) ); (* Call and arguments. *) @@ -6046,6 +6249,9 @@ DESTROY (g) | RStatVFS n -> generate_perl_stat_code "statvfs" statvfs_cols name style n do_cleanups + | RDirentList n -> + generate_perl_dirent_code + "dirent" dirent_cols name style n do_cleanups ); pr "\n" @@ -6084,7 +6290,7 @@ and generate_perl_lvm_code typ cols name style n do_cleanups = pr " (void) hv_store (hv, \"%s\", %d, newSVnv (%s->val[i].%s), 0);\n" name (String.length name) n name ) cols; - pr " PUSHs (sv_2mortal ((SV *) hv));\n"; + pr " PUSHs (sv_2mortal (newRV ((SV *) hv)));\n"; pr " }\n"; pr " guestfs_free_lvm_%s_list (%s);\n" typ n @@ -6106,6 +6312,37 @@ and generate_perl_stat_code typ cols name style n do_cleanups = ) cols; pr " free (%s);\n" n +and generate_perl_dirent_code typ cols name style n do_cleanups = + pr "PREINIT:\n"; + pr " struct guestfs_%s_list *%s;\n" typ n; + pr " int i;\n"; + pr " HV *hv;\n"; + pr " PPCODE:\n"; + pr " %s = guestfs_%s " n name; + generate_call_args ~handle:"g" (snd style); + pr ";\n"; + do_cleanups (); + pr " if (%s == NULL)\n" n; + pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name; + pr " EXTEND (SP, %s->len);\n" n; + pr " for (i = 0; i < %s->len; ++i) {\n" n; + pr " hv = newHV ();\n"; + List.iter ( + function + | name, `String -> + pr " (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 0), 0);\n" + name (String.length name) n name + | name, `Int -> + pr " (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n" + name (String.length name) n name + | name, `Char -> + pr " (void) hv_store (hv, \"%s\", %d, newSVpv (&%s->val[i].%s, 1), 0);\n" + name (String.length name) n name + ) cols; + pr " PUSHs (newRV (sv_2mortal ((SV *) hv)));\n"; + pr " }\n"; + pr " guestfs_free_%s_list (%s);\n" typ n + (* Generate Sys/Guestfs.pm. *) and generate_perl_pm () = generate_header HashStyle LGPLv2; @@ -6239,7 +6476,8 @@ and generate_perl_prototype name style = | RStringList n | RPVList n | RVGList n - | RLVList n -> pr "@%s = " n + | RLVList n + | RDirentList n -> pr "@%s = " n | RStat n | RStatVFS n | RHashtable n -> pr "%%%s = " n @@ -6475,6 +6713,42 @@ py_guestfs_close (PyObject *self, PyObject *args) pr "\n"; ) ["stat", stat_cols; "statvfs", statvfs_cols]; + (* Dirent structures, turned into Python dictionaries. *) + pr "static PyObject *\n"; + pr "put_dirent (struct guestfs_dirent *dirent)\n"; + pr "{\n"; + pr " PyObject *dict;\n"; + pr "\n"; + pr " dict = PyDict_New ();\n"; + List.iter ( + function + | name, `Int -> + pr " PyDict_SetItemString (dict, \"%s\",\n" name; + pr " PyLong_FromLongLong (dirent->%s));\n" name + | name, `Char -> + pr " PyDict_SetItemString (dict, \"%s\",\n" name; + pr " PyString_FromStringAndSize (&dirent->%s, 1));\n" name + | name, `String -> + pr " PyDict_SetItemString (dict, \"%s\",\n" name; + pr " PyString_FromString (dirent->%s));\n" name + ) dirent_cols; + pr " return dict;\n"; + pr "};\n"; + pr "\n"; + + pr "static PyObject *\n"; + pr "put_dirent_list (struct guestfs_dirent_list *dirents)\n"; + pr "{\n"; + pr " PyObject *list;\n"; + pr " int i;\n"; + pr "\n"; + pr " list = PyList_New (dirents->len);\n"; + pr " for (i = 0; i < dirents->len; ++i)\n"; + pr " PyList_SetItem (list, i, put_dirent (&dirents->val[i]));\n"; + pr " return list;\n"; + pr "};\n"; + pr "\n"; + (* Python wrapper functions. *) List.iter ( fun (name, style, _, _, _, _, _) -> @@ -6498,7 +6772,8 @@ py_guestfs_close (PyObject *self, PyObject *args) | RVGList n -> pr " struct guestfs_lvm_vg_list *r;\n"; "NULL" | RLVList n -> pr " struct guestfs_lvm_lv_list *r;\n"; "NULL" | RStat n -> pr " struct guestfs_stat *r;\n"; "NULL" - | RStatVFS n -> pr " struct guestfs_statvfs *r;\n"; "NULL" in + | RStatVFS n -> pr " struct guestfs_statvfs *r;\n"; "NULL" + | RDirentList n -> pr " struct guestfs_dirent_list *r;\n"; "NULL" in List.iter ( function @@ -6602,6 +6877,9 @@ py_guestfs_close (PyObject *self, PyObject *args) | RHashtable n -> pr " py_r = put_table (r);\n"; pr " free_strings (r);\n" + | RDirentList n -> + pr " py_r = put_dirent_list (r);\n"; + pr " guestfs_free_dirent_list (r);\n" ); pr " return py_r;\n"; @@ -6729,7 +7007,9 @@ class GuestFS: | RStatVFS _ -> doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure." | RHashtable _ -> - doc ^ "\n\nThis function returns a dictionary." in + doc ^ "\n\nThis function returns a dictionary." + | RDirentList _ -> + doc ^ "\n\nThis function returns a list of directory entries. Each directory entry is represented as a dictionary." in let doc = if List.mem ProtocolLimitWarning flags then doc ^ "\n\n" ^ protocol_limit_warning @@ -6894,7 +7174,8 @@ static VALUE ruby_guestfs_close (VALUE gv) | RVGList n -> pr " struct guestfs_lvm_vg_list *r;\n"; "NULL" | RLVList n -> pr " struct guestfs_lvm_lv_list *r;\n"; "NULL" | RStat n -> pr " struct guestfs_stat *r;\n"; "NULL" - | RStatVFS n -> pr " struct guestfs_statvfs *r;\n"; "NULL" in + | RStatVFS n -> pr " struct guestfs_statvfs *r;\n"; "NULL" + | RDirentList n -> pr " struct guestfs_dirent_list *r;\n"; "NULL" in pr "\n"; pr " r = guestfs_%s " name; @@ -6975,6 +7256,8 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " }\n"; pr " free (r);\n"; pr " return rv;\n" + | RDirentList n -> + generate_ruby_dirent_code "dirent" dirent_cols ); pr "}\n"; @@ -7025,6 +7308,24 @@ and generate_ruby_lvm_code typ cols = pr " guestfs_free_lvm_%s_list (r);\n" typ; pr " return rv;\n" +(* Ruby code to return a dirent struct list. *) +and generate_ruby_dirent_code typ cols = + pr " VALUE rv = rb_ary_new2 (r->len);\n"; + pr " int i;\n"; + pr " for (i = 0; i < r->len; ++i) {\n"; + pr " VALUE hv = rb_hash_new ();\n"; + List.iter ( + function + | name, `String -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name + | name, (`Char|`Int) -> + pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name + ) cols; + pr " rb_ary_push (rv, hv);\n"; + pr " }\n"; + pr " guestfs_free_%s_list (r);\n" typ; + pr " return rv;\n" + (* Generate Java bindings GuestFS.java file. *) and generate_java_java () = generate_header CStyle LGPLv2; @@ -7040,6 +7341,7 @@ import com.redhat.et.libguestfs.LV; import com.redhat.et.libguestfs.Stat; import com.redhat.et.libguestfs.StatVFS; import com.redhat.et.libguestfs.IntBool; +import com.redhat.et.libguestfs.Dirent; /** * The GuestFS object is a libguestfs handle. @@ -7163,6 +7465,7 @@ and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false) | RStat _ -> pr "Stat "; | RStatVFS _ -> pr "StatVFS "; | RHashtable _ -> pr "HashMap "; + | RDirentList _ -> pr "Dirent[] "; ); if native then pr "_%s " name else pr "%s " name; @@ -7218,6 +7521,7 @@ public class %s { | name, `UUID -> pr " public String %s;\n" name | name, `Bytes | name, `Int -> pr " public long %s;\n" name + | name, `Char -> pr " public char %s;\n" name | name, `OptPercent -> pr " /* The next field is [0..100] or -1 meaning 'not present': */\n"; pr " public float %s;\n" name @@ -7284,7 +7588,7 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | RConstString _ | RString _ -> pr "jstring "; | RIntBool _ | RStat _ | RStatVFS _ | RHashtable _ -> pr "jobject "; - | RStringList _ | RPVList _ | RVGList _ | RLVList _ -> + | RStringList _ | RPVList _ | RVGList _ | RLVList _ | RDirentList _ -> pr "jobjectArray "; ); pr "JNICALL\n"; @@ -7358,7 +7662,13 @@ Java_com_redhat_et_libguestfs_GuestFS__1close pr " jfieldID fl;\n"; pr " jobject jfl;\n"; pr " struct guestfs_lvm_lv_list *r;\n"; "NULL", "NULL" - | RHashtable _ -> pr " char **r;\n"; "NULL", "NULL" in + | RHashtable _ -> pr " char **r;\n"; "NULL", "NULL" + | RDirentList _ -> + pr " jobjectArray jr;\n"; + pr " jclass cl;\n"; + pr " jfieldID fl;\n"; + pr " jobject jfl;\n"; + pr " struct guestfs_dirent_list *r;\n"; "NULL", "NULL" in List.iter ( function | String n @@ -7376,7 +7686,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close let needs_i = (match fst style with - | RStringList _ | RPVList _ | RVGList _ | RLVList _ -> true + | RStringList _ | RPVList _ | RVGList _ | RLVList _ + | RDirentList _ -> true | RErr | RBool _ | RInt _ | RInt64 _ | RConstString _ | RString _ | RIntBool _ | RStat _ | RStatVFS _ | RHashtable _ -> false) || @@ -7510,6 +7821,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close (* XXX *) pr " throw_exception (env, \"%s: internal error: please let us know how to make a Java HashMap from JNI bindings!\");\n" name; pr " return NULL;\n" + | RDirentList _ -> + generate_java_dirent_return "dirent" "Dirent" dirent_cols ); pr "}\n"; @@ -7546,6 +7859,25 @@ and generate_java_lvm_return typ jtyp cols = pr " guestfs_free_lvm_%s_list (r);\n" typ; pr " return jr;\n" +and generate_java_dirent_return typ jtyp cols = + pr " cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/%s\");\n" jtyp; + pr " jr = (*env)->NewObjectArray (env, r->len, cl, NULL);\n"; + pr " for (i = 0; i < r->len; ++i) {\n"; + pr " jfl = (*env)->AllocObject (env, cl);\n"; + List.iter ( + function + | name, `String -> + pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name; + pr " (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, r->val[i].%s));\n" name; + | name, (`Char|`Int) -> + pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name; + pr " (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name; + ) cols; + pr " (*env)->SetObjectArrayElement (env, jfl, i, jfl);\n"; + pr " }\n"; + pr " guestfs_free_%s_list (r);\n" typ; + pr " return jr;\n" + and generate_haskell_hs () = generate_header HaskellStyle LGPLv2; @@ -7567,7 +7899,8 @@ and generate_haskell_hs () = | RLVList _, _ | RStat _, _ | RStatVFS _, _ - | RHashtable _, _ -> false in + | RHashtable _, _ + | RDirentList _, _ -> false in pr "\ {-# INCLUDE #-} @@ -7678,7 +8011,7 @@ last_error h = do pr " fail err\n"; | RConstString _ | RString _ | RStringList _ | RIntBool _ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _ - | RHashtable _ -> + | RHashtable _ | RDirentList _ -> pr " if (r == nullPtr)\n"; pr " then do\n"; pr " err <- last_error h\n"; @@ -7702,7 +8035,8 @@ last_error h = do | RLVList _ | RStat _ | RStatVFS _ - | RHashtable _ -> + | RHashtable _ + | RDirentList _ -> pr " else return ()\n" (* XXXXXXXXXXXXXXXXXXXX *) ); pr "\n"; @@ -7744,6 +8078,7 @@ and generate_haskell_prototype ~handle ?(hs = false) style = | RStat _ -> pr "Stat" | RStatVFS _ -> pr "StatVFS" | RHashtable _ -> pr "Hashtable" + | RDirentList _ -> pr "[Dirent]" ); pr ")" @@ -7904,6 +8239,15 @@ print_strings (char * const* const argv) pr " }\n"; pr " strs[n*2] = NULL;\n"; pr " return strs;\n" + | RDirentList _ -> + pr " struct guestfs_dirent_list *r;\n"; + pr " int i;\n"; + pr " r = malloc (sizeof (struct guestfs_dirent_list));\n"; + pr " sscanf (val, \"%%d\", &r->len);\n"; + pr " r->val = calloc (r->len, sizeof (struct guestfs_dirent));\n"; + pr " for (i = 0; i < r->len; ++i)\n"; + pr " r->val[i].ino = i;\n"; + pr " return r;\n" ); pr "}\n"; pr "\n" @@ -7919,7 +8263,8 @@ print_strings (char * const* const argv) | RConstString _ | RString _ | RStringList _ | RIntBool _ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _ - | RHashtable _ -> + | RHashtable _ + | RDirentList _ -> pr " return NULL;\n" ); pr "}\n"; @@ -8344,6 +8689,10 @@ Run it from the top source directory using the command generate_java_struct "StatVFS" statvfs_cols; close (); + let close = output_to "java/com/redhat/et/libguestfs/Dirent.java" in + generate_java_struct "Dirent" dirent_cols; + close (); + let close = output_to "java/com_redhat_et_libguestfs_GuestFS.c" in generate_java_c (); close (); diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index a46b339..189eebe 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -12560,3 +12560,96 @@ int guestfs_umask (guestfs_h *g, return ctx.ret.oldmask; } +struct readdir_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_readdir_ret ret; +}; + +static void readdir_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct readdir_ctx *ctx = (struct readdir_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_readdir"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_readdir"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_readdir"); + return; + } + goto done; + } + if (!xdr_guestfs_readdir_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_readdir"); + return; + } + done: + ctx->cb_sequence = 1; +} + +struct guestfs_dirent_list *guestfs_readdir (guestfs_h *g, + const char *dir) +{ + struct guestfs_readdir_args args; + struct readdir_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_readdir") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.dir = (char *) dir; + serial = guestfs__send_sync (g, GUESTFS_PROC_READDIR, + (xdrproc_t) xdr_guestfs_readdir_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, readdir_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_readdir"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_READDIR, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + /* caller will free this */ + return safe_memdup (g, &ctx.ret.entries, sizeof (ctx.ret.entries)); +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index 5300d15..4f58949 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -210,3 +210,4 @@ extern int guestfs_mkfifo (guestfs_h *handle, int mode, const char *path); extern int guestfs_mknod_b (guestfs_h *handle, int mode, int devmajor, int devminor, const char *path); extern int guestfs_mknod_c (guestfs_h *handle, int mode, int devmajor, int devminor, const char *path); extern int guestfs_umask (guestfs_h *handle, int mask); +extern struct guestfs_dirent_list *guestfs_readdir (guestfs_h *handle, const char *dir); diff --git a/src/guestfs-structs.h b/src/guestfs-structs.h index 76ac819..44f0ce0 100644 --- a/src/guestfs-structs.h +++ b/src/guestfs-structs.h @@ -127,3 +127,14 @@ struct guestfs_statvfs { int64_t namemax; }; +struct guestfs_dirent { + int64_t ino; + char ftyp; + char *name; +}; + +struct guestfs_dirent_list { + uint32_t len; + struct guestfs_dirent *val; +}; + diff --git a/src/guestfs.c b/src/guestfs.c index 2d4db66..9cdb2dd 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -1473,6 +1473,13 @@ guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *x) free (x); } +void +guestfs_free_dirent_list (struct guestfs_dirent_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_int_dirent_list, (char *) x); + free (x); +} + /* We don't know if stdout_event or sock_read_event will be the * first to receive EOF if the qemu process dies. This function * has the common cleanup code for both. diff --git a/src/guestfs.h b/src/guestfs.h index b5ed0f7..201d60c 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -57,6 +57,7 @@ extern void guestfs_free_int_bool (struct guestfs_int_bool *); extern void guestfs_free_lvm_pv_list (struct guestfs_lvm_pv_list *); extern void guestfs_free_lvm_vg_list (struct guestfs_lvm_vg_list *); extern void guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *); +extern void guestfs_free_dirent_list (struct guestfs_dirent_list *); /* Low-level event API. */ typedef void (*guestfs_send_cb) (guestfs_h *g, void *data); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index ef0b805..69ea4ae 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -238,6 +238,31 @@ xdr_guestfs_int_statvfs (XDR *xdrs, guestfs_int_statvfs *objp) } bool_t +xdr_guestfs_int_dirent (XDR *xdrs, guestfs_int_dirent *objp) +{ + register int32_t *buf; + + if (!xdr_quad_t (xdrs, &objp->ino)) + return FALSE; + if (!xdr_char (xdrs, &objp->ftyp)) + return FALSE; + if (!xdr_string (xdrs, &objp->name, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_int_dirent_list (XDR *xdrs, guestfs_int_dirent_list *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->guestfs_int_dirent_list_val, (u_int *) &objp->guestfs_int_dirent_list_len, ~0, + sizeof (guestfs_int_dirent), (xdrproc_t) xdr_guestfs_int_dirent)) + return FALSE; + return TRUE; +} + +bool_t xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp) { register int32_t *buf; @@ -2435,6 +2460,26 @@ xdr_guestfs_umask_ret (XDR *xdrs, guestfs_umask_ret *objp) } bool_t +xdr_guestfs_readdir_args (XDR *xdrs, guestfs_readdir_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->dir, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_readdir_ret (XDR *xdrs, guestfs_readdir_ret *objp) +{ + register int32_t *buf; + + if (!xdr_guestfs_int_dirent_list (xdrs, &objp->entries)) + return FALSE; + return TRUE; +} + +bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { register int32_t *buf; diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 3b508b2..9ce9af9 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -124,6 +124,18 @@ struct guestfs_int_statvfs { }; typedef struct guestfs_int_statvfs guestfs_int_statvfs; +struct guestfs_int_dirent { + quad_t ino; + char ftyp; + char *name; +}; +typedef struct guestfs_int_dirent guestfs_int_dirent; + +typedef struct { + u_int guestfs_int_dirent_list_len; + guestfs_int_dirent *guestfs_int_dirent_list_val; +} guestfs_int_dirent_list; + struct guestfs_mount_args { char *device; char *mountpoint; @@ -1187,6 +1199,16 @@ struct guestfs_umask_ret { }; typedef struct guestfs_umask_ret guestfs_umask_ret; +struct guestfs_readdir_args { + char *dir; +}; +typedef struct guestfs_readdir_args guestfs_readdir_args; + +struct guestfs_readdir_ret { + guestfs_int_dirent_list entries; +}; +typedef struct guestfs_readdir_ret guestfs_readdir_ret; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -1325,7 +1347,8 @@ enum guestfs_procedure { GUESTFS_PROC_MKNOD_B = 135, GUESTFS_PROC_MKNOD_C = 136, GUESTFS_PROC_UMASK = 137, - GUESTFS_PROC_NR_PROCS = 137 + 1, + GUESTFS_PROC_READDIR = 138, + GUESTFS_PROC_NR_PROCS = 138 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -1384,6 +1407,8 @@ extern bool_t xdr_guestfs_lvm_int_lv (XDR *, guestfs_lvm_int_lv*); extern bool_t xdr_guestfs_lvm_int_lv_list (XDR *, guestfs_lvm_int_lv_list*); extern bool_t xdr_guestfs_int_stat (XDR *, guestfs_int_stat*); extern bool_t xdr_guestfs_int_statvfs (XDR *, guestfs_int_statvfs*); +extern bool_t xdr_guestfs_int_dirent (XDR *, guestfs_int_dirent*); +extern bool_t xdr_guestfs_int_dirent_list (XDR *, guestfs_int_dirent_list*); extern bool_t xdr_guestfs_mount_args (XDR *, guestfs_mount_args*); extern bool_t xdr_guestfs_touch_args (XDR *, guestfs_touch_args*); extern bool_t xdr_guestfs_cat_args (XDR *, guestfs_cat_args*); @@ -1567,6 +1592,8 @@ extern bool_t xdr_guestfs_mknod_b_args (XDR *, guestfs_mknod_b_args*); extern bool_t xdr_guestfs_mknod_c_args (XDR *, guestfs_mknod_c_args*); extern bool_t xdr_guestfs_umask_args (XDR *, guestfs_umask_args*); extern bool_t xdr_guestfs_umask_ret (XDR *, guestfs_umask_ret*); +extern bool_t xdr_guestfs_readdir_args (XDR *, guestfs_readdir_args*); +extern bool_t xdr_guestfs_readdir_ret (XDR *, guestfs_readdir_ret*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -1584,6 +1611,8 @@ extern bool_t xdr_guestfs_lvm_int_lv (); extern bool_t xdr_guestfs_lvm_int_lv_list (); extern bool_t xdr_guestfs_int_stat (); extern bool_t xdr_guestfs_int_statvfs (); +extern bool_t xdr_guestfs_int_dirent (); +extern bool_t xdr_guestfs_int_dirent_list (); extern bool_t xdr_guestfs_mount_args (); extern bool_t xdr_guestfs_touch_args (); extern bool_t xdr_guestfs_cat_args (); @@ -1767,6 +1796,8 @@ extern bool_t xdr_guestfs_mknod_b_args (); extern bool_t xdr_guestfs_mknod_c_args (); extern bool_t xdr_guestfs_umask_args (); extern bool_t xdr_guestfs_umask_ret (); +extern bool_t xdr_guestfs_readdir_args (); +extern bool_t xdr_guestfs_readdir_ret (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index bcc0138..0fe1dea 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -115,6 +115,14 @@ struct guestfs_int_statvfs { hyper namemax; }; +struct guestfs_int_dirent { + hyper ino; + char ftyp; + string name<>; +}; + +typedef struct guestfs_int_dirent guestfs_int_dirent_list<>; + struct guestfs_mount_args { string device<>; string mountpoint<>; @@ -911,6 +919,14 @@ struct guestfs_umask_ret { int oldmask; }; +struct guestfs_readdir_args { + string dir<>; +}; + +struct guestfs_readdir_ret { + guestfs_int_dirent_list entries; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -1049,6 +1065,7 @@ enum guestfs_procedure { GUESTFS_PROC_MKNOD_B = 135, GUESTFS_PROC_MKNOD_C = 136, GUESTFS_PROC_UMASK = 137, + GUESTFS_PROC_READDIR = 138, GUESTFS_PROC_NR_PROCS };