]> Zhao Yanbai Git Server - minix.git/commitdiff
Add libvboxfs: VirtualBox shared folders library
authorDavid van Moolenbroek <david@minix3.org>
Mon, 9 Apr 2012 17:20:37 +0000 (19:20 +0200)
committerDavid van Moolenbroek <david@minix3.org>
Mon, 9 Apr 2012 17:25:18 +0000 (19:25 +0200)
17 files changed:
include/Makefile.minix.inc
include/minix/vboxfs.h [new file with mode: 0644]
lib/Makefile
lib/libvboxfs/Makefile [new file with mode: 0644]
lib/libvboxfs/attr.c [new file with mode: 0644]
lib/libvboxfs/dir.c [new file with mode: 0644]
lib/libvboxfs/file.c [new file with mode: 0644]
lib/libvboxfs/glo.h [new file with mode: 0644]
lib/libvboxfs/handle.c [new file with mode: 0644]
lib/libvboxfs/inc.h [new file with mode: 0644]
lib/libvboxfs/info.c [new file with mode: 0644]
lib/libvboxfs/link.c [new file with mode: 0644]
lib/libvboxfs/path.c [new file with mode: 0644]
lib/libvboxfs/proto.h [new file with mode: 0644]
lib/libvboxfs/vboxfs.c [new file with mode: 0644]
lib/libvboxfs/vboxfs.h [new file with mode: 0644]
share/mk/bsd.prog.mk

index d095e992d88e1a2aeb2834a6da3bd8c5607501b0..5bf445f63f98bafe3d60b74a7f5c169d0afeb1f6 100644 (file)
@@ -18,7 +18,7 @@ INCS+=        minix/acpi.h minix/audio_fw.h minix/bitmap.h \
        minix/sound.h minix/spin.h minix/sys_config.h minix/sysinfo.h \
        minix/syslib.h minix/sysutil.h minix/timers.h minix/type.h \
        minix/tty.h minix/u64.h minix/usb.h minix/usb_ch9.h minix/vbox.h \
-       minix/vboxif.h minix/vboxtype.h minix/vm.h \
+       minix/vboxfs.h minix/vboxif.h minix/vboxtype.h minix/vm.h \
        minix/vfsif.h minix/vtreefs.h minix/libminixfs.h minix/netsock.h
 
 INCS+= net/gen/arp_io.h net/gen/dhcp.h net/gen/ether.h \
diff --git a/include/minix/vboxfs.h b/include/minix/vboxfs.h
new file mode 100644 (file)
index 0000000..20a889c
--- /dev/null
@@ -0,0 +1,12 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#ifndef _MINIX_VBOXFS_H
+#define _MINIX_VBOXFS_H
+
+#include <minix/sffs.h>
+
+int vboxfs_init(char *share, const struct sffs_table **tablep,
+       int *case_insens, int *read_only);
+void vboxfs_cleanup(void);
+
+#endif /* _MINIX_VBOXFS_H */
index 86c4c9f84547e16f6cf68a5fb834534a614821b7..30a7b869ff60d3da928abcc7483d6b62708480de 100644 (file)
@@ -6,7 +6,7 @@ SUBDIR= csu libcompat_minix libc libblockdriver libchardriver     \
        libexec libdevman libusb libminlib libasyn          \
        libddekit libminixfs libbdev libelf libminc libcrypt libterminfo \
        libcurses libvassert libutil libpuffs librefuse libbz2 libarchive \
-       libprop libnetsock libsffs libhgfs
+       libprop libnetsock libsffs libhgfs libvboxfs
 
 SUBDIR+=        ../external/public-domain/xz/lib
 
diff --git a/lib/libvboxfs/Makefile b/lib/libvboxfs/Makefile
new file mode 100644 (file)
index 0000000..908c336
--- /dev/null
@@ -0,0 +1,8 @@
+# Makefile for libvboxfs
+.include <bsd.own.mk>
+
+LIB=   vboxfs
+
+SRCS=  attr.c dir.c file.c handle.c info.c link.c path.c vboxfs.c
+
+.include <bsd.lib.mk>
diff --git a/lib/libvboxfs/attr.c b/lib/libvboxfs/attr.c
new file mode 100644 (file)
index 0000000..ba3662c
--- /dev/null
@@ -0,0 +1,169 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*
+ * Convert a VirtualBox timestamp to a POSIX timespec structure.
+ * VirtualBox' timestamps are in nanoseconds since the UNIX epoch.
+ */
+static void
+get_time(struct timespec *tsp, u64_t nsecs)
+{
+
+       tsp->tv_sec = div64u(nsecs, 1000000000);
+       tsp->tv_nsec = rem64u(nsecs, 1000000000);
+}
+
+/*
+ * Convert a POSIX timespec structure to a VirtualBox timestamp.
+ */
+static u64_t
+set_time(struct timespec *tsp)
+{
+
+       return add64u(mul64u(tsp->tv_sec, 1000000000), tsp->tv_nsec);
+}
+
+/*
+ * Fill the given attribute structure with VirtualBox object information.
+ */
+void
+vboxfs_get_attr(struct sffs_attr *attr, vboxfs_objinfo_t *info)
+{
+
+       if (attr->a_mask & SFFS_ATTR_SIZE)
+               attr->a_size = info->size;
+       if (attr->a_mask & SFFS_ATTR_MODE)
+               attr->a_mode = VBOXFS_GET_MODE(info->attr.mode);
+       if (attr->a_mask & SFFS_ATTR_ATIME)
+               get_time(&attr->a_atime, info->atime);
+       if (attr->a_mask & SFFS_ATTR_MTIME)
+               get_time(&attr->a_mtime, info->mtime);
+       if (attr->a_mask & SFFS_ATTR_CTIME)
+               get_time(&attr->a_ctime, info->ctime);
+       if (attr->a_mask & SFFS_ATTR_CRTIME)
+               get_time(&attr->a_crtime, info->crtime);
+}
+
+/*
+ * Get file attributes.
+ */
+int
+vboxfs_getattr(char *path, struct sffs_attr *attr)
+{
+       vbox_param_t param[3];
+       vboxfs_path_t pathbuf;
+       vboxfs_crinfo_t crinfo;
+       int r;
+
+       if ((r = vboxfs_set_path(&pathbuf, path)) != OK)
+               return r;
+
+       memset(&crinfo, 0, sizeof(crinfo));
+       crinfo.flags = VBOXFS_CRFLAG_LOOKUP;
+       /* crinfo.info.attr.add is not checked */
+
+       vbox_set_u32(&param[0], vboxfs_root);
+       vbox_set_ptr(&param[1], &pathbuf, vboxfs_get_path_size(&pathbuf),
+           VBOX_DIR_OUT);
+       vbox_set_ptr(&param[2], &crinfo, sizeof(crinfo), VBOX_DIR_INOUT);
+
+       r = vbox_call(vboxfs_conn, VBOXFS_CALL_CREATE, param, 3, NULL);
+       if (r != OK)
+               return r;
+
+       switch (crinfo.result) {
+       case VBOXFS_PATH_NOT_FOUND:
+               /* This could also be ENOTDIR. See note in handle.c. */
+       case VBOXFS_FILE_NOT_FOUND:
+               return ENOENT;
+       case VBOXFS_FILE_EXISTS:
+               break;                  /* success */
+       default:
+               return EIO;             /* should never happen */
+       }
+
+       vboxfs_get_attr(attr, &crinfo.info);
+
+       return OK;
+}
+
+/*
+ * Set file size.
+ */
+static int
+set_size(char *path, u64_t size)
+{
+       vboxfs_objinfo_t info;
+       vboxfs_handle_t h;
+       int r;
+
+       if ((r = vboxfs_open_file(path, O_WRONLY, S_IFREG, &h, NULL)) != OK)
+               return r;
+
+       memset(&info, 0, sizeof(info));
+       info.size = size;
+
+       r = vboxfs_getset_info(h, VBOXFS_INFO_SET | VBOXFS_INFO_SIZE, &info,
+           sizeof(info));
+
+       vboxfs_close_file(h);
+
+       return r;
+}
+
+/*
+ * Set file attributes.
+ */
+int
+vboxfs_setattr(char *path, struct sffs_attr *attr)
+{
+       vboxfs_objinfo_t info;
+       vboxfs_handle_t h;
+       int r;
+
+       /*
+        * Setting the size of a path cannot be combined with other attribute
+        * modifications, because we cannot fail atomically.
+        */
+       if (attr->a_mask & SFFS_ATTR_SIZE) {
+               assert(attr->a_mask == SFFS_ATTR_SIZE);
+
+               return set_size(path, attr->a_size);
+       }
+
+       /*
+        * By passing a pointer to an object information structure, we open the
+        * file for attribute manipulation.  Note that this call will open the
+        * file as a regular file.  This works on directories as well.
+        */
+       if ((r = vboxfs_open_file(path, O_WRONLY, 0, &h, &info)) != OK)
+               return r;
+
+       info.attr.add = VBOXFS_OBJATTR_ADD_NONE;
+
+       /* Update the file's permissions if requested. */
+       if (attr->a_mask & SFFS_ATTR_MODE)
+               info.attr.mode =
+                   VBOXFS_SET_MODE(info.attr.mode & S_IFMT, attr->a_mode);
+
+       /*
+        * Update various file times if requested.  Not all changes may
+        * be honered.  A zero time indicates no change.
+        */
+       info.atime = (attr->a_mask & SFFS_ATTR_ATIME) ?
+           set_time(&attr->a_atime) : 0;
+       info.mtime = (attr->a_mask & SFFS_ATTR_MTIME) ?
+           set_time(&attr->a_ctime) : 0;
+       info.ctime = (attr->a_mask & SFFS_ATTR_CTIME) ?
+           set_time(&attr->a_ctime) : 0;
+       info.crtime = (attr->a_mask & SFFS_ATTR_CRTIME) ?
+           set_time(&attr->a_crtime) : 0;
+
+       r = vboxfs_getset_info(h, VBOXFS_INFO_SET | VBOXFS_INFO_FILE, &info,
+           sizeof(info));
+
+       vboxfs_close_file(h);
+
+       return r;
+}
diff --git a/lib/libvboxfs/dir.c b/lib/libvboxfs/dir.c
new file mode 100644 (file)
index 0000000..00c7f37
--- /dev/null
@@ -0,0 +1,245 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*
+ * Directories will generally be accessed sequentially, but there is no
+ * guarantee that this is actually the case.  In particular, multiple user
+ * processes may iterate over the same directory concurrently, and since
+ * process information is lost in the VFS/FS protocol, the result is a
+ * nonsequential pattern on the same single directory handle.  Therefore, we
+ * must support random access.  Since the VirtualBox shared folders interface
+ * does not allow for random access (the resume point cannot be used for this),
+ * we choose to read in the contents of the directory upon open, and cache it
+ * until the directory is closed again.  The risk is that the cached contents
+ * will go stale.
+ *
+ * The directory will always be closed once one reader finishes going through
+ * the entire directory, so the problem is rather limited anyway.  Ideally, the
+ * directory contents would be refreshed upon any invalidating local action as
+ * well as after a certain time span (since the file system can be changed from
+ * the host as well).  This is currently not implemented, because the odds of
+ * things going wrong are pretty small, and the effects are not devastating.
+ *
+ * The calling functions may also request the same directory entry twice in a
+ * row, because the entry does not fit in the user buffer the first time.  The
+ * routines here optimize for repeat-sequential access, while supporting fully
+ * random access as well.
+ */
+
+#define VBOXFS_DIRDATA_SIZE    8192    /* data allocation granularity */
+
+typedef struct vboxfs_dirblock_s {
+       STAILQ_ENTRY(vboxfs_dirblock_s) next;
+       unsigned int count;
+       char data[VBOXFS_DIRDATA_SIZE];
+} vboxfs_dirblock_t;
+
+typedef struct {
+       STAILQ_HEAD(blocks, vboxfs_dirblock_s) blocks;
+       unsigned int index;
+       vboxfs_dirblock_t *block;
+       unsigned int bindex;
+       unsigned int bpos;
+} vboxfs_dirdata_t;
+
+/*
+ * Free the memory allocated for the given directory contents storage.
+ */
+static void
+free_dir(vboxfs_dirdata_t *dirdata)
+{
+       vboxfs_dirblock_t *block;
+
+       while (!STAILQ_EMPTY(&dirdata->blocks)) {
+               block = STAILQ_FIRST(&dirdata->blocks);
+
+               STAILQ_REMOVE_HEAD(&dirdata->blocks, next);
+
+               free(block);
+       }
+
+       free(dirdata);
+}
+
+/*
+ * Read all the contents of the given directory, allocating memory as needed to
+ * store the data.
+ */
+static int
+read_dir(vboxfs_handle_t handle, sffs_dir_t *dirp)
+{
+       vboxfs_dirdata_t *dirdata;
+       vboxfs_dirblock_t *block;
+       vbox_param_t param[8];
+       unsigned int count;
+       int r;
+
+       dirdata = (vboxfs_dirdata_t *) malloc(sizeof(vboxfs_dirdata_t));
+       if (dirdata == NULL)
+               return ENOMEM;
+
+       memset(dirdata, 0, sizeof(*dirdata));
+       STAILQ_INIT(&dirdata->blocks);
+
+       r = OK;
+
+       do {
+               block =
+                   (vboxfs_dirblock_t *) malloc(sizeof(vboxfs_dirblock_t));
+               if (block == NULL) {
+                       r = ENOMEM;
+                       break;
+               }
+
+               vbox_set_u32(&param[0], vboxfs_root);
+               vbox_set_u64(&param[1], handle);
+               vbox_set_u32(&param[2], 0);             /* flags */
+               vbox_set_u32(&param[3], sizeof(block->data));
+               vbox_set_ptr(&param[4], NULL, 0, VBOX_DIR_OUT);
+               vbox_set_ptr(&param[5], block->data, sizeof(block->data),
+                   VBOX_DIR_IN);
+               vbox_set_u32(&param[6], 0);             /* resume point */
+               vbox_set_u32(&param[7], 0);             /* number of files */
+
+               /* If the call fails, stop. */
+               if ((r = vbox_call(vboxfs_conn, VBOXFS_CALL_LIST, param, 8,
+                   NULL)) != OK) {
+                       free(block);
+                       break;
+               }
+
+               /* If the number of returned files is zero, stop. */
+               if ((count = vbox_get_u32(&param[7])) == 0) {
+                       free(block);
+                       break;
+               }
+
+               /*
+                * Add the block to the list. We could realloc() the block to
+                * free unused tail space, but this is not likely to gain us
+                * much in general.
+                */
+               block->count = count;
+               STAILQ_INSERT_TAIL(&dirdata->blocks, block, next);
+
+               /* Continue until a zero resume point is returned. */
+       } while (vbox_get_u32(&param[6]) != 0);
+
+       if (r != OK) {
+               free_dir(dirdata);
+
+               return r;
+       }
+
+       dirdata->block = STAILQ_FIRST(&dirdata->blocks);
+
+       *dirp = (sffs_dir_t) dirdata;
+
+       return OK;
+}
+
+/*
+ * Open a directory.
+ */
+int
+vboxfs_opendir(char *path, sffs_dir_t *handle)
+{
+       vboxfs_handle_t h;
+       int r;
+
+       /* Open the directory. */
+       if ((r = vboxfs_open_file(path, O_RDONLY, S_IFDIR, &h, NULL)) != OK)
+               return r;
+
+       /*
+        * Upon success, read in the full contents of the directory right away.
+        * If it succeeds, this will also set the caller's directory handle.
+        */
+       r = read_dir(h, handle);
+
+       /* We do not need the directory to be open anymore now. */
+       vboxfs_close_file(h);
+
+       return r;
+}
+
+/*
+ * Read one entry from a directory.  On success, copy the name into buf, and
+ * return the requested attributes.  If the name (including terminating null)
+ * exceeds size, return ENAMETOOLONG.  Do not return dot and dot-dot entries.
+ * Return ENOENT if the index exceeds the number of files.
+ */
+int
+vboxfs_readdir(sffs_dir_t handle, unsigned int index, char *buf, size_t size,
+       struct sffs_attr *attr)
+{
+       vboxfs_dirdata_t *dirdata;
+       vboxfs_dirinfo_t *dirinfo;
+       int r;
+
+       dirdata = (vboxfs_dirdata_t *) handle;
+
+       /*
+        * If the saved index exceeds the requested index, start from the
+        * beginning.
+        */
+       if (dirdata->index > index) {
+               dirdata->index = 0;
+               dirdata->bindex = 0;
+               dirdata->bpos = 0;
+               dirdata->block = STAILQ_FIRST(&dirdata->blocks);
+       }
+
+       /* Loop until we find the requested entry or we run out of entries. */
+       while (dirdata->block != NULL) {
+               dirinfo =
+                   (vboxfs_dirinfo_t *) &dirdata->block->data[dirdata->bpos];
+
+               /* Consider all entries that are not dot or dot-dot. */
+               if (dirinfo->name.len > 2 || dirinfo->name.data[0] != '.' ||
+                   (dirinfo->name.len == 2 && dirinfo->name.data[1] != '.')) {
+
+                       if (dirdata->index == index)
+                               break;
+
+                       dirdata->index++;
+               }
+
+               /* Advance to the next entry. */
+               dirdata->bpos += offsetof(vboxfs_dirinfo_t, name) +
+                   offsetof(vboxfs_path_t, data) + dirinfo->name.size;
+               if (++dirdata->bindex >= dirdata->block->count) {
+                       dirdata->block = STAILQ_NEXT(dirdata->block, next);
+                       dirdata->bindex = 0;
+                       dirdata->bpos = 0;
+               }
+       }
+
+       /* Not enough files to satisfy the request? */
+       if (dirdata->block == NULL)
+               return ENOENT;
+
+       /* Return the information for the file we found. */
+       if ((r = vboxfs_get_path(&dirinfo->name, buf, size)) != OK)
+               return r;
+
+       vboxfs_get_attr(attr, &dirinfo->info);
+
+       return OK;
+}
+
+/*
+ * Close a directory.
+ */
+int
+vboxfs_closedir(sffs_dir_t handle)
+{
+       vboxfs_dirdata_t *dirdata;
+
+       dirdata = (vboxfs_dirdata_t *) handle;
+
+       free_dir(dirdata);
+
+       return OK;
+}
diff --git a/lib/libvboxfs/file.c b/lib/libvboxfs/file.c
new file mode 100644 (file)
index 0000000..da96174
--- /dev/null
@@ -0,0 +1,119 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*
+ * We perform all file I/O using a local, intermediate buffer.  While in theory
+ * it would be possible to perform direct DMA from/to the user process, this
+ * does not work in practice: on short reads, VirtualBox copies back the entire
+ * provided buffer rather than only the part actually filled, resulting in the
+ * unused part of the buffer being clobbered.  Marking the buffer as bi-
+ * directional would solve this, except it would also eliminate all the
+ * zero-copy benefits for reads; in addition, it is prevented by the protection
+ * set on the given grant.
+ */
+
+#define VBOXFS_MAX_FILEIO      65536   /* maximum I/O chunk size */
+
+static char iobuf[VBOXFS_MAX_FILEIO];
+
+/*
+ * Open a file.
+ */
+int
+vboxfs_open(char *path, int flags, int mode, sffs_file_t *handle)
+{
+       vboxfs_handle_t *handlep;
+       int r;
+
+       handlep = (vboxfs_handle_t *) malloc(sizeof(*handlep));
+
+       if ((r = vboxfs_open_file(path, flags, mode, handlep, NULL)) != OK) {
+               free(handlep);
+
+               return r;
+       }
+
+       *handle = (sffs_file_t) handlep;
+
+       return OK;
+}
+
+/*
+ * Read or write a chunk from or to a file.
+ */
+static ssize_t
+read_write(vboxfs_handle_t handle, char *buf, size_t size, u64_t pos,
+       int write)
+{
+       vbox_param_t param[5];
+       int r, dir, call;
+
+       dir = write ? VBOX_DIR_OUT : VBOX_DIR_IN;
+       call = write ? VBOXFS_CALL_WRITE : VBOXFS_CALL_READ;
+
+       vbox_set_u32(&param[0], vboxfs_root);
+       vbox_set_u64(&param[1], handle);
+       vbox_set_u64(&param[2], pos);
+       vbox_set_u32(&param[3], size);
+       vbox_set_ptr(&param[4], buf, size, dir);
+
+       if ((r = vbox_call(vboxfs_conn, call, param, 5, NULL)) != OK)
+               return r;
+
+       return vbox_get_u32(&param[3]);
+}
+
+/*
+ * Read from a file.
+ */
+ssize_t
+vboxfs_read(sffs_file_t handle, char *buf, size_t size, u64_t pos)
+{
+       vboxfs_handle_t *handlep;
+
+       handlep = (vboxfs_handle_t *) handle;
+
+       return read_write(*handlep, buf, size, pos, FALSE /*write*/);
+}
+
+/*
+ * Write to a file.
+ */
+ssize_t
+vboxfs_write(sffs_file_t handle, char *buf, size_t len, u64_t pos)
+{
+       vboxfs_handle_t *handlep;
+
+       handlep = (vboxfs_handle_t *) handle;
+
+       return read_write(*handlep, buf, len, pos, TRUE /*write*/);
+}
+
+/*
+ * Close a file handle.
+ */
+int
+vboxfs_close(sffs_file_t handle)
+{
+       vboxfs_handle_t *handlep;
+
+       handlep = (vboxfs_handle_t *) handle;
+
+       vboxfs_close_file(*handlep);
+
+       free(handlep);
+
+       return OK;
+}
+
+/*
+ * Return an internal buffer address and size for I/O operations.
+ */
+size_t
+vboxfs_buffer(char **ptr)
+{
+
+       *ptr = iobuf;
+       return sizeof(iobuf);
+}
diff --git a/lib/libvboxfs/glo.h b/lib/libvboxfs/glo.h
new file mode 100644 (file)
index 0000000..b74131b
--- /dev/null
@@ -0,0 +1,9 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#ifndef _VBOXFS_GLO_H
+#define _VBOXFS_GLO_H
+
+extern vbox_conn_t vboxfs_conn;
+extern vboxfs_root_t vboxfs_root;
+
+#endif /* !_VBOXFS_GLO_H */
diff --git a/lib/libvboxfs/handle.c b/lib/libvboxfs/handle.c
new file mode 100644 (file)
index 0000000..c264a8b
--- /dev/null
@@ -0,0 +1,121 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*
+ * Create or open a file or directory.
+ */
+int
+vboxfs_open_file(char *path, int flags, int mode, vboxfs_handle_t *handlep,
+       vboxfs_objinfo_t *infop)
+{
+       vbox_param_t param[3];
+       vboxfs_path_t pathbuf;
+       vboxfs_crinfo_t crinfo;
+       int r, dir, rflag, wflag;
+
+       if ((r = vboxfs_set_path(&pathbuf, path)) != OK)
+               return r;
+
+       memset(&crinfo, 0, sizeof(crinfo));
+
+       /*
+        * Note that the mode may not be set at all.  If no new file may be
+        * created, this is not a problem.  The following test succeeds only if
+        * the caller explicitly specified that a directory is involved.
+        */
+       dir = S_ISDIR(mode);
+
+       /* Convert open(2) flags to VirtualBox creation flags. */
+       if (flags & O_APPEND)
+               return EINVAL;  /* not supported at this time */
+
+       if (flags & O_CREAT) {
+               crinfo.flags = VBOXFS_CRFLAG_CREATE_IF_NEW;
+
+               if (flags & O_EXCL)
+                       crinfo.flags |= VBOXFS_CRFLAG_FAIL_IF_EXISTS;
+               else if (flags & O_TRUNC)
+                       crinfo.flags |= VBOXFS_CRFLAG_TRUNC_IF_EXISTS;
+               else
+                       crinfo.flags |= VBOXFS_CRFLAG_OPEN_IF_EXISTS;
+       } else {
+               crinfo.flags = VBOXFS_CRFLAG_FAIL_IF_NEW;
+
+               if (flags & O_TRUNC)
+                       crinfo.flags |= VBOXFS_CRFLAG_TRUNC_IF_EXISTS;
+               else
+                       crinfo.flags |= VBOXFS_CRFLAG_OPEN_IF_EXISTS;
+       }
+
+       /*
+        * If an object information structure is given, open the file only to
+        * retrieve or change its attributes.
+        */
+       if (infop != NULL) {
+               rflag = VBOXFS_CRFLAG_READ_ATTR;
+               wflag = VBOXFS_CRFLAG_WRITE_ATTR;
+       } else {
+               rflag = VBOXFS_CRFLAG_READ;
+               wflag = VBOXFS_CRFLAG_WRITE;
+       }
+
+       switch (flags & O_ACCMODE) {
+       case O_RDONLY:  crinfo.flags |= rflag;          break;
+       case O_WRONLY:  crinfo.flags |= wflag;          break;
+       case O_RDWR:    crinfo.flags |= rflag | wflag;  break;
+       default:        return EINVAL;
+       }
+
+       if (S_ISDIR(mode))
+               crinfo.flags |= VBOXFS_CRFLAG_DIRECTORY;
+
+       crinfo.info.attr.mode = VBOXFS_SET_MODE(dir ? S_IFDIR : S_IFREG, mode);
+       crinfo.info.attr.add = VBOXFS_OBJATTR_ADD_NONE;
+
+       vbox_set_u32(&param[0], vboxfs_root);
+       vbox_set_ptr(&param[1], &pathbuf, vboxfs_get_path_size(&pathbuf),
+           VBOX_DIR_OUT);
+       vbox_set_ptr(&param[2], &crinfo, sizeof(crinfo), VBOX_DIR_INOUT);
+
+       r = vbox_call(vboxfs_conn, VBOXFS_CALL_CREATE, param, 3, NULL);
+       if (r != OK)
+               return r;
+
+       if (crinfo.handle == VBOXFS_INVALID_HANDLE) {
+               switch (crinfo.result) {
+               case VBOXFS_PATH_NOT_FOUND:
+                       /*
+                        * This could also mean ENOTDIR, but there does not
+                        * appear to be any way to distinguish that case.
+                        * Verifying with extra lookups seems overkill.
+                        */
+               case VBOXFS_FILE_NOT_FOUND:
+                       return ENOENT;
+               case VBOXFS_FILE_EXISTS:
+                       return EEXIST;
+               default:
+                       return EIO;             /* should never happen */
+               }
+       }
+
+       *handlep = crinfo.handle;
+       if (infop != NULL)
+               *infop = crinfo.info;
+       return OK;
+}
+
+/*
+ * Close an open file handle.
+ */
+void
+vboxfs_close_file(vboxfs_handle_t handle)
+{
+       vbox_param_t param[2];
+
+       vbox_set_u32(&param[0], vboxfs_root);
+       vbox_set_u64(&param[1], handle);
+
+       /* Ignore errors here. We cannot do anything with them anyway. */
+       (void) vbox_call(vboxfs_conn, VBOXFS_CALL_CLOSE, param, 2, NULL);
+}
diff --git a/lib/libvboxfs/inc.h b/lib/libvboxfs/inc.h
new file mode 100644 (file)
index 0000000..ba9313c
--- /dev/null
@@ -0,0 +1,18 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#ifndef _VBOXFS_INC_H
+#define _VBOXFS_INC_H
+
+#include <minix/drivers.h>
+#include <minix/vbox.h>
+#include <minix/sffs.h>
+#include <minix/vboxfs.h>
+#include <sys/queue.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "vboxfs.h"
+#include "glo.h"
+#include "proto.h"
+
+#endif /* !_VBOXFS_INC_H */
diff --git a/lib/libvboxfs/info.c b/lib/libvboxfs/info.c
new file mode 100644 (file)
index 0000000..767744d
--- /dev/null
@@ -0,0 +1,58 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*
+ * Get or set file information.
+ */
+int
+vboxfs_getset_info(vboxfs_handle_t handle, u32_t flags, void *data,
+       size_t size)
+{
+       vbox_param_t param[5];
+
+       vbox_set_u32(&param[0], vboxfs_root);
+       vbox_set_u64(&param[1], handle);
+       vbox_set_u32(&param[2], flags);
+       vbox_set_u32(&param[3], size);
+       vbox_set_ptr(&param[4], data, size, VBOX_DIR_INOUT);
+
+       return vbox_call(vboxfs_conn, VBOXFS_CALL_INFO, param, 5, NULL);
+}
+
+/*
+ * Query volume information.
+ */
+int
+vboxfs_query_vol(char *path, vboxfs_volinfo_t *volinfo)
+{
+       vboxfs_handle_t h;
+       int r;
+
+       if ((r = vboxfs_open_file(path, O_RDONLY, 0, &h, NULL)) != OK)
+               return r;
+
+       r = vboxfs_getset_info(h, VBOXFS_INFO_GET | VBOXFS_INFO_VOLUME,
+           volinfo, sizeof(*volinfo));
+
+       vboxfs_close_file(h);
+
+       return r;
+}
+
+/*
+ * Query volume information.
+ */
+int
+vboxfs_queryvol(char *path, u64_t *free, u64_t *total)
+{
+       vboxfs_volinfo_t volinfo;
+       int r;
+
+       if ((r = vboxfs_query_vol(path, &volinfo)) != OK)
+               return r;
+
+       *free = volinfo.free;
+       *total = volinfo.total;
+       return OK;
+}
diff --git a/lib/libvboxfs/link.c b/lib/libvboxfs/link.c
new file mode 100644 (file)
index 0000000..b525d3d
--- /dev/null
@@ -0,0 +1,109 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*
+ * Create a directory.
+ */
+int
+vboxfs_mkdir(char *path, int mode)
+{
+       vboxfs_handle_t h;
+       int r;
+
+       assert(S_ISDIR(mode));
+
+       if ((r = vboxfs_open_file(path, O_CREAT | O_EXCL | O_WRONLY, mode, &h,
+          NULL)) != OK)
+               return r;
+
+       vboxfs_close_file(h);
+
+       return r;
+}
+
+/*
+ * Remove a file or directory.
+ */
+static int
+remove_file(char *path, int dir)
+{
+       vbox_param_t param[3];
+       vboxfs_path_t pathbuf;
+       int r;
+
+       if ((r = vboxfs_set_path(&pathbuf, path)) != OK)
+               return r;
+
+       /* FIXME: symbolic links are not supported at all yet */
+       vbox_set_u32(&param[0], vboxfs_root);
+       vbox_set_ptr(&param[1], &pathbuf, vboxfs_get_path_size(&pathbuf),
+           VBOX_DIR_OUT);
+       vbox_set_u32(&param[2], dir ? VBOXFS_REMOVE_DIR : VBOXFS_REMOVE_FILE);
+
+       return vbox_call(vboxfs_conn, VBOXFS_CALL_REMOVE, param, 3, NULL);
+}
+
+/*
+ * Unlink a file.
+ */
+int
+vboxfs_unlink(char *path)
+{
+
+       return remove_file(path, FALSE /*dir*/);
+}
+
+/*
+ * Remove a directory.
+ */
+int
+vboxfs_rmdir(char *path)
+{
+
+       return remove_file(path, TRUE /*dir*/);
+}
+
+/*
+ * Rename a file or directory.
+ */
+static int
+rename_file(char *opath, char *npath, int dir)
+{
+       vbox_param_t param[4];
+       vboxfs_path_t opathbuf, npathbuf;
+       u32_t flags;
+       int r;
+
+       if ((r = vboxfs_set_path(&opathbuf, opath)) != OK)
+               return r;
+
+       if ((r = vboxfs_set_path(&npathbuf, npath)) != OK)
+               return r;
+
+       flags = dir ? VBOXFS_RENAME_DIR : VBOXFS_RENAME_FILE;
+       flags |= VBOXFS_RENAME_REPLACE;
+
+       vbox_set_u32(&param[0], vboxfs_root);
+       vbox_set_ptr(&param[1], &opathbuf, vboxfs_get_path_size(&opathbuf),
+           VBOX_DIR_OUT);
+       vbox_set_ptr(&param[2], &npathbuf, vboxfs_get_path_size(&npathbuf),
+           VBOX_DIR_OUT);
+       vbox_set_u32(&param[3], flags);
+
+       return vbox_call(vboxfs_conn, VBOXFS_CALL_RENAME, param, 4, NULL);
+}
+
+/*
+ * Rename a file or directory.
+ */
+int
+vboxfs_rename(char *opath, char *npath)
+{
+       int r;
+
+       if ((r = rename_file(opath, npath, FALSE /*dir*/)) != EISDIR)
+               return r;
+
+       return rename_file(opath, npath, TRUE /*dir*/);
+}
diff --git a/lib/libvboxfs/path.c b/lib/libvboxfs/path.c
new file mode 100644 (file)
index 0000000..6b8c021
--- /dev/null
@@ -0,0 +1,60 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*
+ * Store a local path name in the given path object, performing any necessary
+ * conversions.  The path object is expected to be used read-only, so the size
+ * of the path object is set as small as possible.  If 'name' is NULL, the path
+ * will be initialized to the empty string.
+ */
+int
+vboxfs_set_path(vboxfs_path_t *path, char *name)
+{
+       size_t len;
+
+       len = strlen(name);
+
+       /* FIXME: missing UTF-8 conversion */
+
+       if (len >= sizeof(path->data))
+               return ENAMETOOLONG;
+
+       strcpy(path->data, name);
+
+       path->len = len;
+       path->size = len + 1;
+
+       return OK;
+}
+
+/*
+ * Retrieve the path name from the given path object.  Make sure the name fits
+ * in the given name buffer first.  The given size must include room for a
+ * terminating null character.
+ */
+int
+vboxfs_get_path(vboxfs_path_t *path, char *name, size_t size)
+{
+
+       /* FIXME: missing UTF-8 conversion */
+
+       if (path->len >= size)
+               return ENAMETOOLONG;
+
+       assert(path->data[path->len] == 0);
+
+       strcpy(name, path->data);
+
+       return OK;
+}
+
+/*
+ * Return the byte size of a previously initialized path object.
+ */
+size_t
+vboxfs_get_path_size(vboxfs_path_t *path)
+{
+
+       return offsetof(vboxfs_path_t, data) + path->size;
+}
diff --git a/lib/libvboxfs/proto.h b/lib/libvboxfs/proto.h
new file mode 100644 (file)
index 0000000..99cf74a
--- /dev/null
@@ -0,0 +1,46 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#ifndef _VBOXFS_PROTO_H
+#define _VBOXFS_PROTO_H
+
+/* attr.c */
+void vboxfs_get_attr(struct sffs_attr *attr, vboxfs_objinfo_t *info);
+int vboxfs_getattr(char *path, struct sffs_attr *attr);
+int vboxfs_setattr(char *path, struct sffs_attr *attr);
+
+/* dir.c */
+int vboxfs_opendir(char *path, sffs_dir_t *handle);
+int vboxfs_readdir(sffs_dir_t handle, unsigned int index, char *buf,
+       size_t size, struct sffs_attr *attr);
+int vboxfs_closedir(sffs_dir_t handle);
+
+/* file.c */
+int vboxfs_open(char *path, int flags, int mode, sffs_file_t *handle);
+ssize_t vboxfs_read(sffs_file_t handle, char *buf, size_t size, u64_t pos);
+ssize_t vboxfs_write(sffs_file_t handle, char *buf, size_t len, u64_t pos);
+int vboxfs_close(sffs_file_t handle);
+size_t vboxfs_buffer(char **ptr);
+
+/* handle.c */
+int vboxfs_open_file(char *path, int flags, int mode, vboxfs_handle_t *handlep,
+       vboxfs_objinfo_t *infop);
+void vboxfs_close_file(vboxfs_handle_t handle);
+
+/* info.c */
+int vboxfs_getset_info(vboxfs_handle_t handle, u32_t flags, void *data,
+       size_t size);
+int vboxfs_query_vol(char *path, vboxfs_volinfo_t *volinfo);
+int vboxfs_queryvol(char *path, u64_t *free, u64_t *total);
+
+/* link.c */
+int vboxfs_mkdir(char *path, int mode);
+int vboxfs_unlink(char *path);
+int vboxfs_rmdir(char *path);
+int vboxfs_rename(char *opath, char *npath);
+
+/* path.c */
+int vboxfs_set_path(vboxfs_path_t *path, char *name);
+int vboxfs_get_path(vboxfs_path_t *path, char *name, size_t size);
+size_t vboxfs_get_path_size(vboxfs_path_t *path);
+
+#endif /* !_VBOXFS_PROTO_H */
diff --git a/lib/libvboxfs/vboxfs.c b/lib/libvboxfs/vboxfs.c
new file mode 100644 (file)
index 0000000..28f60d6
--- /dev/null
@@ -0,0 +1,98 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+vbox_conn_t vboxfs_conn;
+vboxfs_root_t vboxfs_root;
+
+static struct sffs_table vboxfs_table = {
+       .t_open         = vboxfs_open,
+       .t_read         = vboxfs_read,
+       .t_write        = vboxfs_write,
+       .t_close        = vboxfs_close,
+
+       .t_readbuf      = vboxfs_buffer,
+       .t_writebuf     = vboxfs_buffer,
+
+       .t_opendir      = vboxfs_opendir,
+       .t_readdir      = vboxfs_readdir,
+       .t_closedir     = vboxfs_closedir,
+
+       .t_getattr      = vboxfs_getattr,
+       .t_setattr      = vboxfs_setattr,
+
+       .t_mkdir        = vboxfs_mkdir,
+       .t_unlink       = vboxfs_unlink,
+       .t_rmdir        = vboxfs_rmdir,
+       .t_rename       = vboxfs_rename,
+
+       .t_queryvol     = vboxfs_queryvol
+};
+
+/*
+ * Initialize communication with the VBOX driver, and map the given share.
+ */
+int
+vboxfs_init(char *share, const struct sffs_table **tablep, int *case_insens,
+       int *read_only)
+{
+       vbox_param_t param[4];
+       vboxfs_path_t path;
+       vboxfs_volinfo_t volinfo;
+       int r;
+
+       if ((r = vboxfs_set_path(&path, share)) != OK)
+               return r;
+
+       if ((r = vbox_init()) != OK)
+               return r;
+
+       if ((vboxfs_conn = r = vbox_open("VBoxSharedFolders")) < 0)
+               return r;
+
+       r = vbox_call(vboxfs_conn, VBOXFS_CALL_SET_UTF8, NULL, 0, NULL);
+       if (r != OK) {
+               vbox_close(vboxfs_conn);
+
+               return r;
+       }
+
+       vbox_set_ptr(&param[0], &path, vboxfs_get_path_size(&path),
+           VBOX_DIR_OUT);
+       vbox_set_u32(&param[1], 0);
+       vbox_set_u32(&param[2], '/');   /* path separator */
+       vbox_set_u32(&param[3], TRUE);  /* case sensitivity - no effect? */
+
+       r = vbox_call(vboxfs_conn, VBOXFS_CALL_MAP_FOLDER, param, 4, NULL);
+       if (r != OK) {
+               vbox_close(vboxfs_conn);
+
+               return r;
+       }
+
+       vboxfs_root = vbox_get_u32(&param[1]);
+
+       /* Gather extra information about the mapped shared. */
+       if (vboxfs_query_vol("", &volinfo) == OK) {
+               *case_insens = !volinfo.props.casesens;
+               *read_only = !!volinfo.props.readonly;
+       }
+
+       *tablep = &vboxfs_table;
+       return OK;
+}
+
+/*
+ * Unmap the share, and disconnect from the VBOX driver.
+ */
+void
+vboxfs_cleanup(void)
+{
+       vbox_param_t param[1];
+
+       vbox_set_u32(&param[0], vboxfs_root);
+
+       vbox_call(vboxfs_conn, VBOXFS_CALL_UNMAP_FOLDER, param, 1, NULL);
+
+       vbox_close(vboxfs_conn);
+}
diff --git a/lib/libvboxfs/vboxfs.h b/lib/libvboxfs/vboxfs.h
new file mode 100644 (file)
index 0000000..79137c9
--- /dev/null
@@ -0,0 +1,137 @@
+/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */
+
+#ifndef _VBOXFS_VBOXFS_H
+#define _VBOXFS_VBOXFS_H
+
+#define VBOXFS_CALL_CREATE              3      /* create, open, lookup */
+#define VBOXFS_CALL_CLOSE               4      /* close handle */
+#define VBOXFS_CALL_READ                5      /* read from file */
+#define VBOXFS_CALL_WRITE               6      /* write to file */
+#define VBOXFS_CALL_LIST                8      /* list directory contents */
+#define VBOXFS_CALL_INFO                9      /* get/set file information */
+#define VBOXFS_CALL_REMOVE             11      /* remove file or directory */
+#define VBOXFS_CALL_UNMAP_FOLDER       13      /* unmap folder */
+#define VBOXFS_CALL_RENAME             14      /* rename file or directory */
+#define VBOXFS_CALL_SET_UTF8           16      /* switch to UTF8 */
+#define VBOXFS_CALL_MAP_FOLDER         17      /* map folder */
+
+#define VBOXFS_INVALID_HANDLE  ((vboxfs_handle_t) ~0LL)
+
+typedef u32_t vboxfs_root_t;
+typedef u64_t vboxfs_handle_t;
+
+typedef struct {
+       u16_t size;
+       u16_t len;
+       char data[PATH_MAX];
+} vboxfs_path_t;
+
+#define VBOXFS_NO_RESULT               0
+#define VBOXFS_PATH_NOT_FOUND          1
+#define VBOXFS_FILE_NOT_FOUND          2
+#define VBOXFS_FILE_EXISTS             3
+#define VBOXFS_FILE_CREATED            4
+#define VBOXFS_FILE_REPLACED           5
+
+#define VBOXFS_OBJATTR_ADD_NONE                1       /* no other attributes */
+#define VBOXFS_OBJATTR_ADD_UNIX                2       /* POSIX attributes */
+#define VBOXFS_OBJATTR_ADD_EATTR       3       /* extended attributes */
+
+typedef struct {
+       u32_t mode;
+       u32_t add;
+       union {
+               struct {
+                       u32_t uid;
+                       u32_t gid;
+                       u32_t nlinks;
+                       u32_t dev;
+                       u64_t inode;
+                       u32_t flags;
+                       u32_t gen;
+                       u32_t rdev;
+               };
+               struct {
+                       u64_t easize;
+               };
+       };
+} vboxfs_objattr_t;
+
+/* Thankfully, MINIX uses the universal UNIX mode values. */
+#define VBOXFS_GET_MODE(mode)          ((mode) & 0xffff)
+#define VBOXFS_SET_MODE(type, perm)    ((type) | ((perm) & ALLPERMS))
+
+typedef struct {
+       u64_t size;
+       u64_t disksize;
+       u64_t atime;
+       u64_t mtime;
+       u64_t ctime;
+       u64_t crtime;
+       vboxfs_objattr_t attr;
+} vboxfs_objinfo_t;
+
+#define VBOXFS_CRFLAG_LOOKUP           0x00000001
+#define VBOXFS_CRFLAG_DIRECTORY                0x00000004
+#define VBOXFS_CRFLAG_OPEN_IF_EXISTS   0x00000000
+#define VBOXFS_CRFLAG_FAIL_IF_EXISTS   0x00000010
+#define VBOXFS_CRFLAG_REPLACE_IF_EXISTS        0x00000020
+#define VBOXFS_CRFLAG_TRUNC_IF_EXISTS  0x00000030
+#define VBOXFS_CRFLAG_CREATE_IF_NEW    0x00000000
+#define VBOXFS_CRFLAG_FAIL_IF_NEW      0x00000100
+#define VBOXFS_CRFLAG_READ             0x00001000
+#define VBOXFS_CRFLAG_WRITE            0x00002000
+#define VBOXFS_CRFLAG_APPEND           0x00004000
+#define VBOXFS_CRFLAG_READ_ATTR                0x00010000
+#define VBOXFS_CRFLAG_WRITE_ATTR       0x00020000
+
+typedef struct {
+       vboxfs_handle_t handle;
+       u32_t result;
+       u32_t flags;
+       vboxfs_objinfo_t info;
+} vboxfs_crinfo_t;
+
+typedef struct {
+       vboxfs_objinfo_t info;
+       u16_t shortlen;
+       u16_t shortname[14];
+       vboxfs_path_t name;     /* WARNING: name data size is dynamic! */
+} vboxfs_dirinfo_t;
+
+#define VBOXFS_INFO_GET                0x00    /* get file information */
+#define VBOXFS_INFO_SET                0x01    /* set file information */
+
+#define VBOXFS_INFO_SIZE       0x04    /* get/set file size */
+#define VBOXFS_INFO_FILE       0x08    /* get/set file attributes */
+#define VBOXFS_INFO_VOLUME     0x10    /* get volume information */
+
+#define VBOXFS_REMOVE_FILE     0x01    /* remove file */
+#define VBOXFS_REMOVE_DIR      0x02    /* remove directory */
+#define VBOXFS_REMOVE_SYMLINK  0x04    /* remove symbolic link */
+
+#define VBOXFS_RENAME_FILE     0x01    /* rename file */
+#define VBOXFS_RENAME_DIR      0x02    /* rename directory */
+#define VBOXFS_RENAME_REPLACE  0x04    /* replace target if it exists */
+
+typedef struct {
+       u32_t namemax;
+       u8_t remote;
+       u8_t casesens;
+       u8_t readonly;
+       u8_t unicode;
+       u8_t fscomp;
+       u8_t filecomp;
+       u16_t reserved;
+} vboxfs_fsprops_t;
+
+typedef struct {
+       u64_t total;
+       u64_t free;
+       u32_t blocksize;
+       u32_t sectorsize;
+       u32_t serial;
+       vboxfs_fsprops_t props;
+} vboxfs_volinfo_t;
+
+#endif /* !_VBOXFS_VBOXFS_H */
index 103849697242ae73737ca8fd47d5e4dacb39bb52..82c14f83f4fee5b2a10ae427cfc815b1edff5450 100644 (file)
@@ -59,7 +59,7 @@ MKDEP_SUFFIXES?=      .o .ln
 #      rumpfs_tmpfs rumpfs_udf rumpfs_ufs
 .for _lib in \
        c curses blockdriver chardriver netdriver edit end m sys timers util \
-       bz2 l audiodriver exec ddekit devman usb elf bdev sffs hgfs
+       bz2 l audiodriver exec ddekit devman usb elf bdev sffs hgfs vboxfs
 .ifndef LIB${_lib:tu}
 LIB${_lib:tu}= ${DESTDIR}/usr/lib/lib${_lib}.a
 .MADE:         ${LIB${_lib:tu}}        # Note: ${DESTDIR} will be expanded