diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2009-07-01 20:56:58 +0100 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2009-07-02 10:11:55 +0100 |
commit | 5186251f8f681f2ebb028423bb49a748861fd11e (patch) | |
tree | b95ea92a8ed1b9443dc04aaf7cdacc8191291bc0 | |
parent | f20854ec61eef1aea313920f0cf193a78c1a9219 (diff) | |
download | libguestfs-5186251f8f681f2ebb028423bb49a748861fd11e.tar.gz libguestfs-5186251f8f681f2ebb028423bb49a748861fd11e.tar.xz libguestfs-5186251f8f681f2ebb028423bb49a748861fd11e.zip |
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.
36 files changed, 1287 insertions, 37 deletions
@@ -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 df49dfe5..9124ea99 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 8884c6b2..8b909c50 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 ad44d535..9eaf87bb 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 00000000..cea6fdd4 --- /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 <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> + +#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 033f42cd..9c2bfefe 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 fe59737e..9b8dfbb1 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 <mask>\n\nThis function sets the mask used for creating new files and\ndevice nodes to C<mask & 0777>.\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<umask(2)>, C<mknod>, C<mkdir>.\n\nThis call returns the previous umask."); else + if (strcasecmp (cmd, "readdir") == 0) + pod2text ("readdir - read directories entries", " readdir <dir>\n\nThis returns the list of directory entries in directory C<dir>.\n\nAll entries in the directory are returned, including C<.> and\nC<..>. The entries are I<not> 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<ls>. To get a printable\ndirectory for human consumption, use C<ll>."); + 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 2ddae2e0..b506559b 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 e727120f..e635e548 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<read-file> function which has a more complex interface. +=head2 readdir + + readdir dir + +This returns the list of directory entries in directory C<dir>. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I<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 C<ls>. To get a printable +directory for human consumption, use C<ll>. + =head2 resize2fs resize2fs device diff --git a/guestfs-actions.pod b/guestfs-actions.pod index 5f66318c..5821e0ee 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -1647,6 +1647,26 @@ This function returns a NULL-terminated array of strings (like L<environ(3)>), or NULL if there was an error. I<The caller must free the strings and the array after use>. +=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<dir>. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I<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 C<guestfs_ls>. To get a printable +directory for human consumption, use C<guestfs_ll>. + +This function returns a C<struct guestfs_dirent_list *> +(see E<lt>guestfs-structs.hE<gt>), +or NULL if there was an error. +I<The caller must call C<guestfs_free_dirent_list> after use>. + =head2 guestfs_resize2fs int guestfs_resize2fs (guestfs_h *handle, diff --git a/guestfs-structs.pod b/guestfs-structs.pod index 31b9ff28..dd6b0ed2 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 633ecdad..ea90a482 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 00000000..f6ef3d8d --- /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 cc94a987..e8f36ff4 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<String,String> test0rhashtableerr () private native int _umask (long g, int mask) throws LibGuestFSException; + /** + * read directories entries + * <p> + * This returns the list of directory entries in directory + * "dir". + * <p> + * All entries in the directory are returned, including "." + * and "..". The entries are *not* sorted, but returned in + * the same order as the underlying filesystem. + * <p> + * 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". + * <p> + * @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 91095a48..52137ecd 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 cb4ad347..bed1f929 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 0bf458c4..0d3349f4 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 5cab5887..d15335e7 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 6db72394..498b4b1e 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 00000000..f333810e --- /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 8f4ab5f3..e1cd84a3 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 cd26bedc..e624d552 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-E<gt>read_file> function which has a more complex interface. +=item @entries = $h->readdir ($dir); + +This returns the list of directory entries in directory C<dir>. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I<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 C<$h-E<gt>ls>. To get a printable +directory for human consumption, use C<$h-E<gt>ll>. + =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 00000000..17dfd24b --- /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 98a14a03..44b446aa 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 ad44df7f..784c5671 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 606e9a2a..38776b6b 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 065fd3e7..93e78032 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 69fd7066..df5ff7e2 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<umask(2)>, C<guestfs_mknod>, C<guestfs_mkdir>. 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<dir>. + +All entries in the directory are returned, including C<.> and +C<..>. The entries are I<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 C<guestfs_ls>. To get a printable +directory for human consumption, use C<guestfs_ll>."); + ] 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<n> keys and values alternate, followed by the trailing NULL entry. I<The caller must free the strings and the array after use>.\n\n" + | RDirentList _ -> + pr "This function returns a C<struct guestfs_dirent_list *> +(see E<lt>guestfs-structs.hE<gt>), +or NULL if there was an error. +I<The caller must call C<guestfs_free_dirent_list> 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_<action> 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<String,String> "; + | 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 <guestfs.h> #-} @@ -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 a46b3395..189eebee 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 5300d15e..4f589490 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 76ac8199..44f0ce0f 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 2d4db66c..9cdb2dde 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 b5ed0f78..201d60ca 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 ef0b8059..69ea4ae9 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 3b508b28..9ce9af9d 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 bcc0138f..0fe1dea9 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 }; |