uid 0;
};
+service hgfs
+{
+ system
+ TIMES # 25
+ GETINFO # 26
+ SAFECOPYFROM # 31
+ SAFECOPYTO # 32
+ SETGRANT # 34
+ PROFBUF # 38
+ SYSCTL # 44
+ ;
+ ipc
+ SYSTEM PM VFS RS VM
+ ;
+};
+
service printer
{
io 378:4 # LPT1
--- /dev/null
+.TH HGFS 8
+.SH NAME
+hgfs \- Host/Guest File System server
+.SH SYNOPSIS
+\fBmount \-t hgfs \fR[\fB\-r\fR] [\fB\-o \fIoptions\fR] \fBnone \fImountpoint
+.SH DESCRIPTION
+The Host/Guest File System (HGFS) server allows one to mount
+VMware Shared Folders as a file system. This makes it possible to access
+selected portions of the VMware host file system when MINIX is run as a
+VMware guest.
+.PP
+The above mount command will mount the hgfs file system onto the directory
+\fImountpoint\fR. The \fB\-r\fR mount option makes the file system read-only;
+note that shared folders may independently have been configured as read-only
+on the VMware host. The \fIoptions\fR field is a string consisting of
+comma-delimited \fIkey\fR or \fIkey\fB=\fIvalue\fR options. The following
+options are supported.
+.TP 4
+\fBprefix=\fIpath\fR
+This option sets a path prefix that will be prepended to all file system
+operations on the host system. When mounted without a prefix (the default),
+the root directory of an HGFS mount will contain all the names of the
+available shares. The prefix option can be used to mount one of those shares,
+by specifying its name as the prefix. Multi-component path prefixes are
+supported as well.
+.TP
+\fBuid=\fInumber\fR
+This sets the user ID used for all the files and directories in the file
+system, allowing a non-root user to be the owner. The value must be specified
+as a decimal number.
+The default is root (the number \fB0\fR).
+.TP
+\fBgid=\fInumber\fR
+Likewise, sets the group ID for all files and directories.
+The default is operator (the number \fB0\fR).
+.TP
+\fBfmask=\fInumber\fR
+This option sets the file permission mask of regular files. It is specified as
+an octal number. For example, a value of \fB600\fR makes all files readable and
+writable by the owning user (see the "\fBuid\fR" option).
+The default is \fB755\fR.
+.TP
+\fBdmask=\fInumber\fR
+Likewise, sets the file permission mask of directories.
+The default is also \fB755\fR.
+.TP
+\fBicase\fR
+This option tells HGFS to treat names as case-insensitive.
+.TP
+\fBnoicase\fR
+This option, set by default, reverts the effect of an earlier specified
+"\fBicase\fR" option.
+.SH EXAMPLES
+.TP 20
+.B mount \-t hgfs none /mnt
+# Mount the entire shared folders tree on \fI/mnt\fR
+.TP 20
+.B mount \-t hgfs \-o prefix=shared,uid=20,fmask=644,icase none /usr/shared
+Mount the "\fIshared\fR" shared folder on \fI/usr/shared\fR
+.SH LIMITATIONS
+HGFS uses the first and original version of the VMware Shared Folders protocol
+to talk to the VMware host. That means that HGFS should work with all VMware
+products that support shared folders. However, this also imposes a number of
+limitations. For example, the first version of the protocol supports only
+regular files and directories (and not links), and does not have support for
+automatic case sensitivity handling.
+.PP
+Some file system operations may not behave as expected, because the behavior
+of HGFS is determined largely by the host. Other file system operations
+(in particular, using directories as mountpoints) are not implemented,
+because the file system structure as perceived by HGFS may change arbitrarily
+at any time, due to modifications on the host side.
+.SH "SEE ALSO"
+.BR mount (1)
+.SH AUTHOR
+David van Moolenbroek <dcvmoole@cs.vu.nl>
cd ./mfs && $(MAKE) $@
cd ./pfs && $(MAKE) $@
cd ./iso9660fs && $(MAKE) $@
+ cd ./hgfs && $(MAKE) $@
cd ./rs && $(MAKE) $@
cd ./ds && $(MAKE) $@
cd ./is && $(MAKE) $@
--- /dev/null
+# Makefile for VMware Host/Guest File System (HGFS) server
+SERVER=hgfs
+
+LIBHGFSDIR=./libhgfs
+LIBHGFS=$(LIBHGFSDIR)/libhgfs.a
+
+DEST=/sbin/$(SERVER)
+LIBS=-lsys -L$(LIBHGFSDIR) -lhgfs
+
+OBJ=dentry.o handle.o inode.o link.o lookup.o main.o \
+ misc.o mount.o name.o optset.o path.o read.o \
+ stat.o table.o util.o verify.o write.o
+
+all build: $(SERVER)
+
+$(SERVER): $(LIBHGFS) $(OBJ)
+ $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
+
+$(LIBHGFS):
+ cd $(LIBHGFSDIR) && $(MAKE)
+
+install: $(SERVER)
+ install -c $(SERVER) $(DEST)
+
+clean:
+ cd $(LIBHGFSDIR) && $(MAKE) $@
+ rm -f $(SERVER) $(OBJ)
+
+depend:
+ cd $(LIBHGFSDIR) && $(MAKE) $@
+ mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
+
+# Include generated dependencies.
+include .depend
--- /dev/null
+
+/* Number of inodes. */
+/* The following number must not exceed 16. The i_num field is only a short. */
+#define NUM_INODE_BITS 8
+#define NUM_INODES ((1 << NUM_INODE_BITS) - 1)
+
+/* Number of entries in the name hashtable. */
+#define NUM_HASH_SLOTS 1023
+
+/* Arbitrary block size constant returned by fstatfs. du(1) uses this.
+ * Also used by getdents. This is not the actual HGFS data transfer unit size.
+ */
+#define BLOCK_SIZE 4096
--- /dev/null
+/* This file contains directory entry management and the name lookup hashtable.
+ *
+ * The entry points into this file are:
+ * init_dentry initialize the directory entry name lookup hashtable
+ * lookup_dentry find an inode based on parent directory and name
+ * add_dentry add an inode as directory entry to a parent directory
+ * del_dentry delete an inode from its parent directory
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+PRIVATE LIST_HEAD(hash_head, inode) hash_table[NUM_HASH_SLOTS];
+
+FORWARD _PROTOTYPE( void del_one_dentry, (struct inode *ino) );
+FORWARD _PROTOTYPE( unsigned int hash_dentry, (struct inode *parent,
+ char *name) );
+
+/*===========================================================================*
+ * init_dentry *
+ *===========================================================================*/
+PUBLIC void init_dentry()
+{
+/* Initialize the names hashtable.
+ */
+ int i;
+
+ for (i = 0; i < NUM_HASH_SLOTS; i++)
+ LIST_INIT(&hash_table[i]);
+}
+
+/*===========================================================================*
+ * lookup_dentry *
+ *===========================================================================*/
+PUBLIC struct inode *lookup_dentry(parent, name)
+struct inode *parent;
+char *name;
+{
+/* Given a directory inode and a component name, look up the inode associated
+ * with that directory entry. Return the inode (with increased reference
+ * count) if found, or NIL_INODE otherwise.
+ */
+ struct inode *ino;
+ unsigned int slot;
+
+ assert(IS_DIR(parent));
+
+ slot = hash_dentry(parent, name);
+
+ LIST_FOREACH(ino, &hash_table[slot], i_hash) {
+ if (compare_name(ino->i_name, name) == TRUE)
+ break;
+ }
+
+ if (ino == NIL_INODE)
+ return NIL_INODE;
+
+ get_inode(ino);
+
+ return ino;
+}
+
+/*===========================================================================*
+ * add_dentry *
+ *===========================================================================*/
+PUBLIC void add_dentry(parent, name, ino)
+struct inode *parent;
+char *name;
+struct inode *ino;
+{
+/* Add an entry to a parent inode, in the form of a new inode, with the given
+ * name. An entry with this name must not already exist.
+ */
+ unsigned int slot;
+
+ assert(IS_DIR(parent));
+ assert(parent->i_ref > 0);
+ assert(ino->i_ref > 0);
+ assert(name[0]);
+ assert(strlen(name) <= NAME_MAX);
+
+ link_inode(parent, ino);
+
+ strcpy(ino->i_name, name);
+
+ /* hash_add(ino); */
+ slot = hash_dentry(parent, ino->i_name);
+ LIST_INSERT_HEAD(&hash_table[slot], ino, i_hash);
+}
+
+/*===========================================================================*
+ * del_one_dentry *
+ *===========================================================================*/
+PRIVATE void del_one_dentry(ino)
+struct inode *ino;
+{
+/* This inode has become inaccessible by name. Disassociate it from its parent
+ * and remove it from the names hash table.
+ */
+
+ /* There can and must be exactly one root inode, so don't delete it! */
+ if (IS_ROOT(ino))
+ return;
+
+ /* INUSE -> DELETED, CACHED -> FREE */
+
+ /* Remove the entry from the hashtable.
+ * Decrease parent's refcount, possibly adding it to the free list.
+ * Do not touch open handles. Do not add to the free list.
+ */
+
+ assert(ino->i_parent != NIL_INODE);
+
+ /* hash_del(ino); */
+ LIST_REMOVE(ino, i_hash);
+
+ ino->i_name[0] = 0;
+
+ unlink_inode(ino);
+}
+
+/*===========================================================================*
+ * del_dentry *
+ *===========================================================================*/
+PUBLIC void del_dentry(ino)
+struct inode *ino;
+{
+/* Disassociate an inode from its parent, effectively deleting it. Recursively
+ * delete all its children as well, fragmenting the deleted branch into single
+ * inodes.
+ */
+ LIST_HEAD(work_list, inode) work_list;
+ struct inode *child;
+
+ del_one_dentry(ino);
+
+ /* Quick way out: one directory entry that itself has no children. */
+ if (!HAS_CHILDREN(ino))
+ return;
+
+ /* Recursively delete all children of the inode as well.
+ * Iterative version: this is potentially 128 levels deep.
+ */
+
+ LIST_INIT(&work_list);
+ LIST_INSERT_HEAD(&work_list, ino, i_next);
+
+ do {
+ ino = LIST_FIRST(&work_list);
+ LIST_REMOVE(ino, i_next);
+
+ assert(IS_DIR(ino));
+
+ while (!LIST_EMPTY(&ino->i_child)) {
+ child = LIST_FIRST(&ino->i_child);
+ LIST_REMOVE(child, i_next);
+
+ del_one_dentry(child);
+
+ if (HAS_CHILDREN(child))
+ LIST_INSERT_HEAD(&work_list, child, i_next);
+ }
+ } while (!LIST_EMPTY(&work_list));
+}
+
+/*===========================================================================*
+ * hash_dentry *
+ *===========================================================================*/
+PRIVATE unsigned int hash_dentry(parent, name)
+struct inode *parent;
+char *name;
+{
+/* Generate a hash value for a given name. Normalize the name first, so that
+ * different variations of the name will result in the same hash value.
+ */
+ unsigned int val;
+ char buf[NAME_MAX+1], *p;
+
+ dprintf(("HGFS: hash_dentry for '%s'\n", name));
+
+ normalize_name(buf, name);
+
+ /* djb2 string hash algorithm, XOR variant */
+ val = 5381;
+ for (p = buf; *p; p++)
+ val = ((val << 5) + val) ^ *p;
+
+ /* Mix with inode number: typically, many file names occur in several
+ * different directories.
+ */
+ return (val ^ parent->i_num) % NUM_HASH_SLOTS;
+}
--- /dev/null
+
+#ifdef _TABLE
+#undef EXTERN
+#define EXTERN
+#endif
+
+EXTERN message m_in; /* request message */
+EXTERN message m_out; /* reply message */
+EXTERN struct state state; /* global state */
+EXTERN struct opt opt; /* global options */
+
+extern _PROTOTYPE( int (*call_vec[]), (void) );
--- /dev/null
+/* This file contains open file and directory handle management functions.
+ *
+ * The entry points into this file are:
+ * get_handle open a handle for an inode and store the handle
+ * put_handle close any handles associated with an inode
+ *
+ * Created:
+ * April 2007 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+#include <fcntl.h>
+
+/*===========================================================================*
+ * get_handle *
+ *===========================================================================*/
+PUBLIC int get_handle(ino)
+struct inode *ino;
+{
+/* Get an open file or directory handle for an inode.
+ */
+ char path[PATH_MAX];
+ int r;
+
+ /* If we don't have a file handle yet, try to open the file now. */
+ if (ino->i_flags & I_HANDLE)
+ return OK;
+
+ if ((r = verify_inode(ino, path, NULL)) != OK)
+ return r;
+
+ if (IS_DIR(ino)) {
+ r = hgfs_opendir(path, &ino->i_dir);
+ }
+ else {
+ if (!state.read_only)
+ r = hgfs_open(path, O_RDWR, 0, &ino->i_file);
+
+ /* Protection or mount status might prevent us from writing. With the
+ * information that we have available, this is the best we can do..
+ */
+ if (state.read_only || r != OK)
+ r = hgfs_open(path, O_RDONLY, 0, &ino->i_file);
+ }
+
+ if (r != OK)
+ return r;
+
+ ino->i_flags |= I_HANDLE;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * put_handle *
+ *===========================================================================*/
+PUBLIC void put_handle(ino)
+struct inode *ino;
+{
+/* Close an open file or directory handle associated with an inode.
+ */
+ int r;
+
+ if (!(ino->i_flags & I_HANDLE))
+ return;
+
+ /* We ignore any errors here, because we can't deal with them anyway. */
+ if (IS_DIR(ino))
+ r = hgfs_closedir(ino->i_dir);
+ else
+ r = hgfs_close(ino->i_file);
+
+ if (r != OK)
+ printf("HGFS: put_handle: handle close operation returned %d\n", r);
+
+ ino->i_flags &= ~I_HANDLE;
+}
--- /dev/null
+
+#define _POSIX_SOURCE 1 /* for signal handling */
+#define _SYSTEM 1 /* for negative error values */
+#define _MINIX 1
+
+#include <minix/config.h>
+#include <minix/const.h>
+#include <minix/type.h>
+#include <minix/ipc.h>
+#include <minix/com.h>
+#include <minix/callnr.h>
+#include <minix/safecopies.h>
+#include <minix/vfsif.h>
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+
+#if DEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+
+#include "libhgfs/hgfs.h"
+
+#include "type.h"
+#include "const.h"
+#include "proto.h"
+#include "glo.h"
+
+#include "inode.h"
--- /dev/null
+/* This file deals with inode management.
+ *
+ * The entry points into this file are:
+ * init_inode initialize the inode table, return the root inode
+ * find_inode find an inode based on its inode number
+ * get_inode increase the reference count of an inode
+ * put_inode decrease the reference count of an inode
+ * link_inode link an inode as a directory entry to another inode
+ * unlink_inode unlink an inode from its parent directory
+ * get_free_inode return a free inode object
+ * have_free_inode check whether there is a free inode available
+ * have_used_inode check whether any inode is still in use
+ * do_putnode perform the PUTNODE file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+PRIVATE TAILQ_HEAD(free_head, inode) free_list;
+
+/*===========================================================================*
+ * init_inode *
+ *===========================================================================*/
+PUBLIC struct inode *init_inode()
+{
+/* Initialize inode table. Return the root inode.
+ */
+ struct inode *ino;
+ unsigned int index;
+
+ TAILQ_INIT(&free_list);
+
+ dprintf(("HGFS: %d inodes, %d bytes each, equals %d bytes\n",
+ NUM_INODES, sizeof(struct inode), sizeof(inodes)));
+
+ /* Mark all inodes except the root inode as free. */
+ for (index = 1; index < NUM_INODES; index++) {
+ ino = &inodes[index];
+ ino->i_parent = NIL_INODE;
+ LIST_INIT(&ino->i_child);
+ ino->i_num = index + 1;
+ ino->i_gen = (unsigned short)-1; /* aesthetics */
+ ino->i_ref = 0;
+ ino->i_flags = 0;
+ TAILQ_INSERT_TAIL(&free_list, ino, i_free);
+ }
+
+ /* Initialize and return the root inode. */
+ ino = &inodes[0];
+ ino->i_parent = ino; /* root inode is its own parent */
+ LIST_INIT(&ino->i_child);
+ ino->i_num = ROOT_INODE_NR;
+ ino->i_gen = 0; /* unused by root node */
+ ino->i_ref = 1; /* root inode is hereby in use */
+ ino->i_flags = I_DIR; /* root inode is a directory */
+ ino->i_name[0] = 0; /* root inode has empty name */
+
+ return ino;
+}
+
+/*===========================================================================*
+ * find_inode *
+ *===========================================================================*/
+PUBLIC struct inode *find_inode(ino_nr)
+ino_t ino_nr;
+{
+/* Get an inode based on its inode number. Do not increase its reference count.
+ */
+ struct inode *ino;
+ unsigned int index;
+
+ /* Inode 0 (= index -1) is not a valid inode number. */
+ index = INODE_INDEX(ino_nr);
+ if (index < 0) {
+ printf("HGFS: VFS passed invalid inode number!\n");
+
+ return NIL_INODE;
+ }
+
+ assert(index < NUM_INODES);
+
+ ino = &inodes[index];
+
+ /* Make sure the generation number matches. */
+ if (INODE_GEN(ino_nr) != ino->i_gen) {
+ printf("HGFS: VFS passed outdated inode number!\n");
+
+ return NIL_INODE;
+ }
+
+ /* The VFS/FS protocol only uses referenced inodes. */
+ if (ino->i_ref == 0)
+ printf("HGFS: VFS passed unused inode!\n");
+
+ return ino;
+}
+
+/*===========================================================================*
+ * get_inode *
+ *===========================================================================*/
+PUBLIC void get_inode(ino)
+struct inode *ino;
+{
+/* Increase the given inode's reference count. If both reference and link
+ * count were zero before, remove the inode from the free list.
+ */
+
+ dprintf(("HGFS: get_inode(%p) ['%s']\n", ino, ino->i_name));
+
+ /* (INUSE, CACHED) -> INUSE */
+
+ /* If this is the first reference, remove the node from the free list. */
+ if (ino->i_ref == 0 && !HAS_CHILDREN(ino))
+ TAILQ_REMOVE(&free_list, ino, i_free);
+
+ ino->i_ref++;
+
+ if (ino->i_ref == 0)
+ panic("HGFS", "inode reference count wrapped", NO_NUM);
+}
+
+/*===========================================================================*
+ * put_inode *
+ *===========================================================================*/
+PUBLIC void put_inode(ino)
+struct inode *ino;
+{
+/* Decrease an inode's reference count. If this count has reached zero, close
+ * the inode's file handle, if any. If both reference and link count have
+ * reached zero, mark the inode as cached or free.
+ */
+
+ dprintf(("HGFS: put_inode(%p) ['%s']\n", ino, ino->i_name));
+
+ assert(ino != NIL_INODE);
+ assert(ino->i_ref > 0);
+
+ ino->i_ref--;
+
+ /* If there are still references to this inode, we're done here. */
+ if (ino->i_ref > 0)
+ return;
+
+ /* Close any file handle associated with this inode. */
+ put_handle(ino);
+
+ /* Only add the inode to the free list if there are also no links to it. */
+ if (HAS_CHILDREN(ino))
+ return;
+
+ /* INUSE -> CACHED, DELETED -> FREE */
+
+ /* Add the inode to the head or tail of the free list, depending on whether
+ * it is also deleted (and therefore can never be reused as is).
+ */
+ if (ino->i_parent == NIL_INODE)
+ TAILQ_INSERT_HEAD(&free_list, ino, i_free);
+ else
+ TAILQ_INSERT_TAIL(&free_list, ino, i_free);
+}
+
+/*===========================================================================*
+ * link_inode *
+ *===========================================================================*/
+PUBLIC void link_inode(parent, ino)
+struct inode *parent;
+struct inode *ino;
+{
+/* Link an inode to a parent. If both reference and link count were zero
+ * before, remove the inode from the free list. This function should only be
+ * called from add_dentry().
+ */
+
+ /* This can never happen, right? */
+ if (parent->i_ref == 0 && !HAS_CHILDREN(parent))
+ TAILQ_REMOVE(&free_list, parent, i_free);
+
+ LIST_INSERT_HEAD(&parent->i_child, ino, i_next);
+
+ ino->i_parent = parent;
+}
+
+/*===========================================================================*
+ * unlink_inode *
+ *===========================================================================*/
+PUBLIC void unlink_inode(ino)
+struct inode *ino;
+{
+/* Unlink an inode from its parent. If both reference and link count have
+ * reached zero, mark the inode as cached or free. This function should only
+ * be used from del_dentry().
+ */
+ struct inode *parent;
+
+ parent = ino->i_parent;
+
+ LIST_REMOVE(ino, i_next);
+
+ if (parent->i_ref == 0 && !HAS_CHILDREN(parent)) {
+ if (parent->i_parent == NIL_INODE)
+ TAILQ_INSERT_HEAD(&free_list, parent, i_free);
+ else
+ TAILQ_INSERT_TAIL(&free_list, parent, i_free);
+ }
+
+ ino->i_parent = NIL_INODE;
+}
+
+/*===========================================================================*
+ * get_free_inode *
+ *===========================================================================*/
+PUBLIC struct inode *get_free_inode()
+{
+/* Return a free inode object (with reference count 1), if available.
+ */
+ struct inode *ino;
+
+ /* [CACHED -> FREE,] FREE -> DELETED */
+
+ /* If there are no inodes on the free list, we cannot satisfy the request. */
+ if (TAILQ_EMPTY(&free_list)) {
+ printf("HGFS: out of inodes!\n");
+
+ return NIL_INODE;
+ }
+
+ ino = TAILQ_FIRST(&free_list);
+ TAILQ_REMOVE(&free_list, ino, i_free);
+
+ assert(ino->i_ref == 0);
+ assert(!HAS_CHILDREN(ino));
+
+ /* If this was a cached inode, free it first. */
+ if (ino->i_parent != NIL_INODE)
+ del_dentry(ino);
+
+ assert(ino->i_parent == NIL_INODE);
+
+ /* Initialize a subset of its fields */
+ ino->i_gen++;
+ ino->i_ref = 1;
+
+ return ino;
+}
+
+/*===========================================================================*
+ * have_free_inode *
+ *===========================================================================*/
+PUBLIC int have_free_inode()
+{
+/* Check whether there are any free inodes at the moment. Kind of lame, but
+ * this allows for easier error recovery in some places.
+ */
+
+ return !TAILQ_EMPTY(&free_list);
+}
+
+/*===========================================================================*
+ * have_used_inode *
+ *===========================================================================*/
+PUBLIC int have_used_inode()
+{
+/* Check whether any inodes are still in use, that is, any of the inodes have
+ * a reference count larger than zero.
+ */
+ unsigned int index;
+
+ for (index = 0; index < NUM_INODES; index++)
+ if (inodes[index].i_ref > 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*===========================================================================*
+ * do_putnode *
+ *===========================================================================*/
+PUBLIC int do_putnode()
+{
+/* Decrease an inode's reference count.
+ */
+ struct inode *ino;
+ int count;
+
+ if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ count = m_in.REQ_COUNT;
+
+ if (count <= 0 || count > ino->i_ref) return EINVAL;
+
+ ino->i_ref -= count - 1;
+
+ put_inode(ino);
+
+ return OK;
+}
--- /dev/null
+#ifndef _INODE_H
+#define _INODE_H
+
+/* We cannot use inode number 0, so to be able to use bitmasks to combine
+ * inode and generation numbers, we have to use one fewer than the maximum of
+ * inodes possible by using NUM_INODE_BITS bits.
+ */
+#define NUM_INODES ((1 << NUM_INODE_BITS) - 1)
+
+/* The main portion of the inode array forms a fully linked tree, providing a
+ * cached partial view of what the server believes is on the host system. Each
+ * inode contains only a pointer to its parent and its path component name, so
+ * a path for an inode is constructed by walking up to the root. Inodes that
+ * are in use as directory for a child node must not be recycled; in this case,
+ * the i_child list is not empty. Naturally, inodes for which VFS holds a
+ * reference must also not be recycled; the i_ref count takes care of that.
+ *
+ * Multiple hard links to a single file do not exist; that is why an inode is
+ * also a directory entry (when in IN USE or CACHED state). Notifications about
+ * modifications on the host system are not part of the protocol, so sometimes
+ * the server may discover that some files do not exist anymore. In that case,
+ * they are marked as DELETED in the inode table. Such files may still be used
+ * because of open file handles, but cannot be referenced by path anymore.
+ * Unfortunately the HGFS v1 protocol is largely path-oriented, so even
+ * truncating a deleted file is not possible. This has been fixed in v2/v3, but
+ * we currently use the v1 protocol for VMware backwards compatibility reasons.
+ *
+ * An inode is REFERENCED iff it has a reference count > 0 *or* has children.
+ * An inode is LINKED IN iff it has a parent.
+ *
+ * An inode is IN USE iff it is REFERENCED and LINKED IN.
+ * An inode is CACHED iff it is NOT REFERENCED and LINKED IN.
+ * An inode is DELETED iff it is REFERENCED and NOT LINKED IN.
+ * An inode is FREE iff it is NOT REFERENCED and NOT LINKED IN.
+ *
+ * An inode may have an open file handle if it is IN USE or DELETED.
+ * An inode may have children if it is IN USE (and is a directory).
+ * An inode is in the names hashtable iff it is IN USE or CACHED.
+ * An inode is on the free list iff it is CACHED or FREE.
+ *
+ * - An IN USE inode becomes DELETED when it is either deleted explicitly, or
+ * when it has been determined to have become unreachable by path name on the
+ * host system (the verify_* functions take care of this).
+ * - An IN USE inode may become CACHED when there are no VFS references to it
+ * anymore (i_ref == 0), and it is not a directory with children.
+ * - A DELETED inode cannot have children, but may become FREE when there are
+ * also no VFS references to it anymore.
+ * - A CACHED inode may become IN USE when either i_ref or i_link is increased
+ * from zero. Practically, it will always be i_ref that gets increased, since
+ * i_link cannot be increased by VFS without having a reference to the inode.
+ * - A CACHED or FREE inode may be reused for other purposes at any time.
+ */
+
+EXTERN struct inode {
+ struct inode *i_parent; /* parent inode pointer */
+ LIST_HEAD(child_head, inode) i_child; /* child inode anchor */
+ LIST_ENTRY(inode) i_next; /* sibling inode chain entry */
+ LIST_ENTRY(inode) i_hash; /* hashtable chain entry */
+ unsigned short i_num; /* inode number for quick reference */
+ unsigned short i_gen; /* inode generation number */
+ unsigned short i_ref; /* VFS reference count */
+ unsigned short i_flags; /* any combination of I_* flags */
+ union {
+ TAILQ_ENTRY(inode) u_free; /* free list chain entry */
+ hgfs_file_t u_file; /* handle to open HGFS file */
+ hgfs_dir_t u_dir; /* handle to open HGFS directory */
+ } i_u;
+ char i_name[NAME_MAX+1]; /* entry name in parent directory */
+} inodes[NUM_INODES];
+
+#define i_free i_u.u_free
+#define i_file i_u.u_file
+#define i_dir i_u.u_dir
+
+#define NIL_INODE ((struct inode *)NULL)
+
+#define I_DIR 0x01 /* this inode represents a directory */
+#define I_HANDLE 0x02 /* this inode has an open handle */
+
+/* warning: the following line is not a proper macro */
+#define INODE_NR(i) (((i)->i_gen << NUM_INODE_BITS) | (i)->i_num)
+#define INODE_INDEX(n) (((n) & ((1 << NUM_INODE_BITS) - 1)) - 1)
+#define INODE_GEN(n) (((n) >> NUM_INODE_BITS) & 0xffff)
+
+#define ROOT_INODE_NR 1
+
+#define IS_DIR(i) ((i)->i_flags & I_DIR)
+#define IS_ROOT(i) ((i)->i_num == ROOT_INODE_NR)
+#define HAS_CHILDREN(i) (!LIST_EMPTY(&(i)->i_child))
+
+#define MODE_TO_DIRFLAG(m) (S_ISDIR(m) ? I_DIR : 0)
+
+#endif /* _INODE_H */
--- /dev/null
+# Makefile for HGFS library
+LIBNAME=libhgfs.a
+
+OBJ=backdoor.o attr.o channel.o dir.o error.o file.o \
+ link.o misc.o path.o rpc.o time.o
+
+AR=ar
+GAS2ACK=gas2ack
+
+all build: $(LIBNAME)
+
+$(LIBNAME): $(OBJ)
+ $(AR) cr $@ $(OBJ)
+
+backdoor.o: backdoor.S
+ $(GAS2ACK) $< $@.s
+ $(CC) $(CFLAGS) -c -o $@ $@.s
+
+clean distclean:
+ rm -f $(LIBNAME) $(OBJ) *.o.s
+
+depend:
+ mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
+
+# Include generated dependencies.
+include .depend
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+#include <sys/stat.h>
+
+/*===========================================================================*
+ * attr_get *
+ *===========================================================================*/
+PUBLIC void attr_get(attr)
+struct hgfs_attr *attr;
+{
+/* Get attribute information from the RPC buffer, storing the requested parts
+ * in the given attr structure.
+ */
+ mode_t mode;
+ u32_t size_lo, size_hi;
+
+ mode = (RPC_NEXT32) ? S_IFDIR : S_IFREG;
+
+ size_lo = RPC_NEXT32;
+ size_hi = RPC_NEXT32;
+ if (attr->a_mask & HGFS_ATTR_SIZE)
+ attr->a_size = make64(size_lo, size_hi);
+
+ time_get((attr->a_mask & HGFS_ATTR_CRTIME) ? &attr->a_crtime : NULL);
+ time_get((attr->a_mask & HGFS_ATTR_ATIME) ? &attr->a_atime : NULL);
+ time_get((attr->a_mask & HGFS_ATTR_MTIME) ? &attr->a_mtime : NULL);
+ time_get((attr->a_mask & HGFS_ATTR_CTIME) ? &attr->a_ctime : NULL);
+
+ mode |= HGFS_PERM_TO_MODE(RPC_NEXT8);
+ if (attr->a_mask & HGFS_ATTR_MODE) attr->a_mode = mode;
+}
+
+/*===========================================================================*
+ * hgfs_getattr *
+ *===========================================================================*/
+PUBLIC int hgfs_getattr(path, attr)
+char *path;
+struct hgfs_attr *attr;
+{
+/* Get selected attributes of a file by path name.
+ */
+ int r;
+
+ RPC_REQUEST(HGFS_REQ_GETATTR);
+
+ path_put(path);
+
+ if ((r = rpc_query()) != OK)
+ return r;
+
+ attr_get(attr);
+
+ return OK;
+}
+
+/*===========================================================================*
+ * hgfs_setattr *
+ *===========================================================================*/
+PUBLIC int hgfs_setattr(path, attr)
+char *path;
+struct hgfs_attr *attr;
+{
+/* Set selected attributes of a file by path name.
+ */
+
+ RPC_REQUEST(HGFS_REQ_SETATTR);
+
+ RPC_NEXT8 = (attr->a_mask & HGFS_ATTR_ALL);
+
+ RPC_NEXT32 = !!(S_ISDIR(attr->a_mode));
+ RPC_NEXT32 = ex64lo(attr->a_size);
+ RPC_NEXT32 = ex64hi(attr->a_size);
+
+ time_put((attr->a_mask & HGFS_ATTR_CRTIME) ? &attr->a_crtime : NULL);
+ time_put((attr->a_mask & HGFS_ATTR_ATIME) ? &attr->a_atime : NULL);
+ time_put((attr->a_mask & HGFS_ATTR_MTIME) ? &attr->a_mtime : NULL);
+ time_put((attr->a_mask & HGFS_ATTR_CTIME) ? &attr->a_ctime : NULL);
+
+ RPC_NEXT8 = HGFS_MODE_TO_PERM(attr->a_mode);
+
+ path_put(path);
+
+ return rpc_query();
+}
--- /dev/null
+# Part of libhgfs - (c) 2009, D.C. van Moolenbroek
+
+.globl __libhgfs_backdoor
+.globl __libhgfs_backdoor_in
+.globl __libhgfs_backdoor_out
+
+.text
+
+ MAGIC = 0x564D5868
+ BD_PORT = 0x5658
+ IO_PORT = 0x5659
+
+.balign 16
+__libhgfs_backdoor:
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+ movl 4+16(%esp), %ebp
+ movl $MAGIC, %eax
+ movl 4(%ebp), %ebx
+ movl 8(%ebp), %ecx
+ movl 12(%ebp), %edx
+ movw $BD_PORT, %dx
+ movl 16(%ebp), %esi
+ movl 20(%ebp), %edi
+ inl %dx
+ movl %eax, (%ebp)
+ movl %ebx, 4(%ebp)
+ movl %ecx, 8(%ebp)
+ movl %edx, 12(%ebp)
+ movl %esi, 16(%ebp)
+ movl %edi, 20(%ebp)
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %ebx
+ ret
+
+.balign 16
+__libhgfs_backdoor_in:
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+ movl 4+16(%esp), %eax
+ movl 4(%eax), %ebx
+ movl 8(%eax), %ecx
+ movl 12(%eax), %edx
+ movw $IO_PORT, %dx
+ movl 16(%eax), %esi
+ movl 20(%eax), %edi
+ movl 24(%eax), %ebp
+ movl $MAGIC, %eax
+ cld
+ repe; insb
+ pushl %eax
+ movl 4+20(%esp), %eax
+ movl %ebx, 4(%eax)
+ movl %ecx, 8(%eax)
+ movl %edx, 12(%eax)
+ movl %esi, 16(%eax)
+ movl %edi, 20(%eax)
+ movl %ebp, 24(%eax)
+ popl %ebx
+ movl %ebx, (%eax)
+ movl (%eax), %eax
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %ebx
+ ret
+
+.balign 16
+__libhgfs_backdoor_out:
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+ movl 4+16(%esp), %eax
+ movl 4(%eax), %ebx
+ movl 8(%eax), %ecx
+ movl 12(%eax), %edx
+ movw $IO_PORT, %dx
+ movl 16(%eax), %esi
+ movl 20(%eax), %edi
+ movl 24(%eax), %ebp
+ movl $MAGIC, %eax
+ cld
+ repe; outsb
+ pushl %eax
+ movl 4+20(%esp), %eax
+ movl %ebx, 4(%eax)
+ movl %ecx, 8(%eax)
+ movl %edx, 12(%eax)
+ movl %esi, 16(%eax)
+ movl %edi, 20(%eax)
+ movl %ebp, 24(%eax)
+ popl %ebx
+ movl %ebx, (%eax)
+ movl (%eax), %eax
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %ebx
+ ret
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+#define CMD_XFER 0x1E /* vmware backdoor transfer command */
+
+enum {
+ XFER_OPEN, /* open transfer channel */
+ XFER_SENDLEN, /* specify length of data to send */
+ XFER_SEND, /* send data */
+ XFER_RECVLEN, /* get length of data to receive */
+ XFER_RECV, /* receive data */
+ XFER_RECVACK, /* acknowledge receipt of data */
+ XFER_CLOSE /* close transfer channel */
+};
+
+#define STATUS(p) (HIWORD((p)[2]) & 0xff)
+
+/*===========================================================================*
+ * channel_open *
+ *===========================================================================*/
+PUBLIC int channel_open(ch, type)
+struct channel *ch; /* struct describing the new channel */
+u32_t type; /* channel type: CH_IN or CH_OUT */
+{
+/* Open a new backdoor channel. Upon success, the given channel structure will
+ * be filled with information and can be used in subsequent channel calls.
+ * Return OK on success, or a negative error code on error.
+ */
+ u32_t ptr[6];
+
+ ptr[1] = type | 0x80000000;
+ ptr[2] = MAKELONG(CMD_XFER, XFER_OPEN);
+
+ backdoor(ptr);
+
+ if ((STATUS(ptr) & 1) == 0) return EIO;
+
+ ch->id = HIWORD(ptr[3]);
+ ch->cookie1 = ptr[4];
+ ch->cookie2 = ptr[5];
+
+ return OK;
+}
+
+/*===========================================================================*
+ * channel_close *
+ *===========================================================================*/
+PUBLIC void channel_close(ch)
+struct channel *ch; /* backdoor channel to close */
+{
+/* Close a previously opened backdoor channel.
+ */
+ u32_t ptr[6];
+
+ ptr[2] = MAKELONG(CMD_XFER, XFER_CLOSE);
+ ptr[3] = MAKELONG(0, ch->id);
+ ptr[4] = ch->cookie1;
+ ptr[5] = ch->cookie2;
+
+ backdoor(ptr);
+}
+
+/*===========================================================================*
+ * channel_send *
+ *===========================================================================*/
+PUBLIC int channel_send(ch, buf, len)
+struct channel *ch; /* backdoor channel to send to */
+char *buf; /* buffer to send data from */
+int len; /* size of the data to send */
+{
+/* Receive data over a backdoor channel. Return OK on success, or a negative
+ * error code on error.
+ */
+ u32_t ptr[7];
+
+ ptr[1] = len;
+ ptr[2] = MAKELONG(CMD_XFER, XFER_SENDLEN);
+ ptr[3] = MAKELONG(0, ch->id);
+ ptr[4] = ch->cookie1;
+ ptr[5] = ch->cookie2;
+
+ backdoor(ptr);
+
+ if ((STATUS(ptr) & 1) == 0) return EIO;
+
+ if (len == 0) return OK;
+
+ ptr[1] = MAKELONG(0, 1);
+ ptr[2] = len;
+ ptr[3] = MAKELONG(0, ch->id);
+ ptr[4] = (u32_t)buf;
+ ptr[5] = ch->cookie2;
+ ptr[6] = ch->cookie1;
+
+ backdoor_out(ptr);
+
+ return OK;
+}
+
+/*===========================================================================*
+ * channel_recv *
+ *===========================================================================*/
+PUBLIC int channel_recv(ch, buf, max)
+struct channel *ch; /* backdoor channel to receive from */
+char *buf; /* buffer to receive data into */
+int max; /* size of the buffer */
+{
+/* Receive data on a backdoor channel. Return the number of bytes received, or
+ * a negative error code on error.
+ */
+ u32_t ptr[7];
+ int len;
+
+ ptr[2] = MAKELONG(CMD_XFER, XFER_RECVLEN);
+ ptr[3] = MAKELONG(0, ch->id);
+ ptr[4] = ch->cookie1;
+ ptr[5] = ch->cookie2;
+
+ backdoor(ptr);
+
+ if ((STATUS(ptr) & 0x81) == 0) return EIO;
+
+ if ((len = ptr[1]) == 0 || (STATUS(ptr) & 3) == 1) return 0;
+
+ if (len > max) return E2BIG;
+
+ ptr[1] = MAKELONG(0, 1);
+ ptr[2] = len;
+ ptr[3] = MAKELONG(0, ch->id);
+ ptr[4] = ch->cookie1;
+ ptr[5] = (u32_t)buf;
+ ptr[6] = ch->cookie2;
+
+ backdoor_in(ptr);
+
+ ptr[1] = 1;
+ ptr[2] = MAKELONG(CMD_XFER, XFER_RECVACK);
+ ptr[3] = MAKELONG(0, ch->id);
+ ptr[4] = ch->cookie1;
+ ptr[5] = ch->cookie2;
+
+ backdoor(ptr);
+
+ return len;
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+/* Various macros used here and there */
+#define MAKELONG(a,b) ((a) | ((b) << 16))
+#define HIWORD(d) ((d) >> 16)
+#define LOWORD(d) ((d) & 0xffff)
+#define BYTES(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
+
+/* Valid channel types for channel_open() */
+#define CH_IN BYTES('T', 'C', 'L', 'O')
+#define CH_OUT BYTES('R', 'P', 'C', 'I')
+
+/* RPC constants */
+#define RPC_BUF_SIZE 6134 /* max size of RPC request */
+#define RPC_HDR_SIZE 10 /* RPC HGFS header size */
+
+/* RPC macros. These NEED NOT be portable. VMware only does x86(-64) anyway. */
+/* ..all this because ACK can't pack structures :( */
+#define RPC_NEXT8 *(((u8_t*)(++rpc_ptr))-1) /* get/set next byte */
+#define RPC_NEXT16 *(((u16_t*)(rpc_ptr+=2))-1) /* get/set next short */
+#define RPC_NEXT32 *(((u32_t*)(rpc_ptr+=4))-1) /* get/set next long */
+#define RPC_LEN (rpc_ptr - rpc_buf) /* request length thus far */
+#define RPC_ADVANCE(n) rpc_ptr += n /* skip n bytes in buffer */
+#define RPC_PTR (rpc_ptr) /* pointer to next data */
+#define RPC_RESET rpc_ptr = rpc_buf /* start at beginning */
+#define RPC_REQUEST(r) \
+ RPC_RESET; \
+ RPC_NEXT8 = 'f'; \
+ RPC_NEXT8 = ' '; \
+ RPC_NEXT32 = 0; \
+ RPC_NEXT32 = r; /* start a RPC request */
+
+/* HGFS requests */
+enum {
+ HGFS_REQ_OPEN,
+ HGFS_REQ_READ,
+ HGFS_REQ_WRITE,
+ HGFS_REQ_CLOSE,
+ HGFS_REQ_OPENDIR,
+ HGFS_REQ_READDIR,
+ HGFS_REQ_CLOSEDIR,
+ HGFS_REQ_GETATTR,
+ HGFS_REQ_SETATTR,
+ HGFS_REQ_MKDIR,
+ HGFS_REQ_UNLINK,
+ HGFS_REQ_RMDIR,
+ HGFS_REQ_RENAME,
+ HGFS_REQ_QUERYVOL
+};
+
+/* HGFS open types */
+enum {
+ HGFS_OPEN_TYPE_O,
+ HGFS_OPEN_TYPE_OT,
+ HGFS_OPEN_TYPE_CO,
+ HGFS_OPEN_TYPE_C,
+ HGFS_OPEN_TYPE_COT
+};
+
+/* HGFS mode/perms conversion macros */
+#define HGFS_MODE_TO_PERM(m) (((m) & S_IRWXU) >> 6)
+#define HGFS_PERM_TO_MODE(p) (((p) << 6) & S_IRWXU)
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*===========================================================================*
+ * hgfs_opendir *
+ *===========================================================================*/
+PUBLIC int hgfs_opendir(path, handle)
+char *path;
+hgfs_dir_t *handle;
+{
+/* Open a directory. Store a directory handle upon success.
+ */
+ int r;
+
+ RPC_REQUEST(HGFS_REQ_OPENDIR);
+
+ path_put(path);
+
+ if ((r = rpc_query()) != OK)
+ return r;
+
+ *handle = (hgfs_dir_t)RPC_NEXT32;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * hgfs_readdir *
+ *===========================================================================*/
+PUBLIC int hgfs_readdir(handle, index, buf, size, attr)
+hgfs_dir_t handle;
+unsigned int index;
+char *buf;
+size_t size;
+struct hgfs_attr *attr;
+{
+/* Read a directory entry from an open directory, using a zero-based index
+ * number. Upon success, the resulting path name is stored in the given buffer
+ * and the given attribute structure is filled selectively as requested. Upon
+ * error, the contents of the path buffer and attribute structure are
+ * undefined.
+ */
+ int r;
+
+ RPC_REQUEST(HGFS_REQ_READDIR);
+ RPC_NEXT32 = (u32_t)handle;
+ RPC_NEXT32 = index;
+
+ if ((r = rpc_query()) != OK)
+ return r;
+
+ attr_get(attr);
+
+ return path_get(buf, size);
+}
+
+/*===========================================================================*
+ * hgfs_closedir *
+ *===========================================================================*/
+PUBLIC int hgfs_closedir(handle)
+hgfs_dir_t handle;
+{
+/* Close an open directory.
+ */
+
+ RPC_REQUEST(HGFS_REQ_CLOSEDIR);
+ RPC_NEXT32 = (u32_t)handle;
+
+ return rpc_query();
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/* Not sure if all of these occur in the HGFS v1 protocol, but at least some
+ * that weren't in the original protocol, are being returned now.
+ */
+#define NERRS 16
+PRIVATE int error_map[NERRS] = {
+ OK, /* no error */
+ ENOENT, /* no such file/directory */
+ EBADF, /* invalid handle */
+ EPERM, /* operation not permitted */
+ EEXIST, /* file already exists */
+ ENOTDIR, /* not a directory */
+ ENOTEMPTY, /* directory not empty */
+ EIO, /* protocol error */
+ EACCES, /* access denied */
+ EINVAL, /* invalid name */
+ EIO, /* generic error */
+ EIO, /* sharing violation */
+ ENOSPC, /* no space */
+ ENOSYS, /* operation not supported */
+ ENAMETOOLONG, /* name too long */
+ EINVAL, /* invalid parameter */
+};
+
+/*===========================================================================*
+ * error_convert *
+ *===========================================================================*/
+PUBLIC int error_convert(err)
+int err;
+{
+/* Convert a HGFS error into an errno error code.
+ */
+
+ if (err < 0 || err >= NERRS) return EIO;
+
+ return error_map[err];
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+/*===========================================================================*
+ * hgfs_open *
+ *===========================================================================*/
+PUBLIC int hgfs_open(path, flags, mode, handle)
+char *path; /* path name to open */
+int flags; /* open flags to use */
+int mode; /* mode to create (user bits only) */
+hgfs_file_t *handle; /* place to store resulting handle */
+{
+/* Open a file. Store a file handle upon success.
+ */
+ int r, type;
+
+ /* We could implement this, but that means we would have to start tracking
+ * open files in order to associate data with them. Rather not.
+ */
+ if (flags & O_APPEND) return EINVAL;
+
+ if (flags & O_CREAT) {
+ if (flags & O_EXCL) type = HGFS_OPEN_TYPE_C;
+ else if (flags & O_TRUNC) type = HGFS_OPEN_TYPE_COT;
+ else type = HGFS_OPEN_TYPE_CO;
+ } else {
+ if (flags & O_TRUNC) type = HGFS_OPEN_TYPE_OT;
+ else type = HGFS_OPEN_TYPE_O;
+ }
+
+ RPC_REQUEST(HGFS_REQ_OPEN);
+ RPC_NEXT32 = (flags & O_ACCMODE);
+ RPC_NEXT32 = type;
+ RPC_NEXT8 = HGFS_MODE_TO_PERM(mode);
+
+ path_put(path);
+
+ if ((r = rpc_query()) != OK)
+ return r;
+
+ *handle = (hgfs_file_t)RPC_NEXT32;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * hgfs_read *
+ *===========================================================================*/
+PUBLIC int hgfs_read(handle, buf, size, off)
+hgfs_file_t handle; /* handle to open file */
+char *buf; /* data buffer or NULL */
+size_t size; /* maximum number of bytes to read */
+u64_t off; /* file offset */
+{
+/* Read from an open file. Upon success, return the number of bytes read.
+ */
+ int r, len, max;
+
+ RPC_REQUEST(HGFS_REQ_READ);
+ RPC_NEXT32 = (u32_t)handle;
+ RPC_NEXT32 = ex64lo(off);
+ RPC_NEXT32 = ex64hi(off);
+
+ max = RPC_BUF_SIZE - RPC_LEN - sizeof(u32_t);
+ RPC_NEXT32 = (size < max) ? size : max;
+
+ if ((r = rpc_query()) != OK)
+ return r;
+
+ len = RPC_NEXT32;
+ if (len > max) len = max; /* sanity check */
+
+ /* Only copy out data if we're requested to do so. */
+ if (buf != NULL)
+ memcpy(buf, RPC_PTR, len);
+
+ return len;
+}
+
+/*===========================================================================*
+ * hgfs_write *
+ *===========================================================================*/
+PUBLIC int hgfs_write(handle, buf, len, off, append)
+hgfs_file_t handle; /* handle to open file */
+const char *buf; /* data buffer or NULL */
+size_t len; /* number of bytes to write */
+u64_t off; /* file offset */
+int append; /* if set, append to file (ignore offset) */
+{
+/* Write to an open file. Upon success, return the number of bytes written.
+ */
+ int r;
+
+ RPC_REQUEST(HGFS_REQ_WRITE);
+ RPC_NEXT32 = (u32_t)handle;
+
+ if (append) {
+ RPC_NEXT8 = 1;
+ RPC_NEXT32 = 0;
+ RPC_NEXT32 = 0;
+ }
+ else {
+ RPC_NEXT8 = 0;
+ RPC_NEXT32 = ex64lo(off);
+ RPC_NEXT32 = ex64hi(off);
+ }
+
+ RPC_NEXT32 = len;
+
+ /* Only copy in data if we're requested to do so. */
+ if (buf != NULL)
+ memcpy(RPC_PTR, buf, len);
+ RPC_ADVANCE(len);
+
+ if ((r = rpc_query()) != OK)
+ return r;
+
+ return RPC_NEXT32;
+}
+
+/*===========================================================================*
+ * hgfs_close *
+ *===========================================================================*/
+PUBLIC int hgfs_close(handle)
+hgfs_file_t handle; /* handle to open file */
+{
+/* Close an open file.
+ */
+
+ RPC_REQUEST(HGFS_REQ_CLOSE);
+ RPC_NEXT32 = (u32_t)handle;
+
+ return rpc_query();
+}
+
+/*===========================================================================*
+ * hgfs_readbuf *
+ *===========================================================================*/
+PUBLIC size_t hgfs_readbuf(ptr)
+char **ptr;
+{
+/* Return information about the read buffer, for zero-copy purposes. Store a
+ * pointer to the first byte of the read buffer, and return the maximum data
+ * size. The results are static, but must only be used directly prior to a
+ * hgfs_read() call (with a NULL data buffer address).
+ */
+ u32_t off;
+
+ off = RPC_HDR_SIZE + sizeof(u32_t);
+
+ RPC_RESET;
+ RPC_ADVANCE(off);
+ *ptr = RPC_PTR;
+
+ return RPC_BUF_SIZE - off;
+}
+
+/*===========================================================================*
+ * hgfs_writebuf *
+ *===========================================================================*/
+PUBLIC size_t hgfs_writebuf(ptr)
+char **ptr;
+{
+/* Return information about the write buffer, for zero-copy purposes. Store a
+ * pointer to the first byte of the write buffer, and return the maximum data
+ * size. The results are static, but must only be used immediately after a
+ * hgfs_write() call (with a NULL data buffer address).
+ */
+ u32_t off;
+
+ off = RPC_HDR_SIZE + sizeof(u32_t) + sizeof(u8_t) + sizeof(u32_t) * 3;
+
+ RPC_RESET;
+ RPC_ADVANCE(off);
+ *ptr = RPC_PTR;
+
+ return RPC_BUF_SIZE - off;
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#define rpc_buf PREFIX(rpc_buf)
+#define rpc_ptr PREFIX(rpc_ptr)
+extern char rpc_buf[RPC_BUF_SIZE];
+extern char *rpc_ptr;
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#ifndef _HGFS_H
+#define _HGFS_H
+
+#include <sys/types.h>
+#include <minix/u64.h>
+
+typedef void *hgfs_file_t; /* handle to open file */
+typedef void *hgfs_dir_t; /* handle to directory search */
+
+struct hgfs_attr {
+ u32_t a_mask; /* which fields to retrieve/set */
+ mode_t a_mode; /* file type and permissions */
+ u64_t a_size; /* file size */
+ time_t a_crtime; /* file creation time */
+ time_t a_atime; /* file access time */
+ time_t a_mtime; /* file modification time */
+ time_t a_ctime; /* file change time */
+};
+
+#define HGFS_ATTR_SIZE 0x01 /* get/set file size */
+#define HGFS_ATTR_CRTIME 0x02 /* get/set file creation time */
+#define HGFS_ATTR_ATIME 0x04 /* get/set file access time */
+#define HGFS_ATTR_MTIME 0x08 /* get/set file modification time */
+#define HGFS_ATTR_CTIME 0x10 /* get/set file change time */
+#define HGFS_ATTR_MODE 0x20 /* get/set file mode */
+#define HGFS_ATTR_ALL \
+ (HGFS_ATTR_SIZE | HGFS_ATTR_CRTIME | HGFS_ATTR_ATIME | \
+ HGFS_ATTR_MTIME | HGFS_ATTR_CTIME | HGFS_ATTR_MODE)
+
+_PROTOTYPE( int hgfs_init, (void) );
+_PROTOTYPE( void hgfs_cleanup, (void) );
+
+_PROTOTYPE( int hgfs_enabled, (void) );
+
+_PROTOTYPE( int hgfs_open, (char *path, int flags, int mode,
+ hgfs_file_t *handle) );
+_PROTOTYPE( int hgfs_read, (hgfs_file_t handle, char *buf, size_t size,
+ u64_t offset) );
+_PROTOTYPE( int hgfs_write, (hgfs_file_t handle, const char *buf,
+ size_t len, u64_t offset, int append) );
+_PROTOTYPE( int hgfs_close, (hgfs_file_t handle) );
+
+_PROTOTYPE( size_t hgfs_readbuf, (char **ptr) );
+_PROTOTYPE( size_t hgfs_writebuf, (char **ptr) );
+
+_PROTOTYPE( int hgfs_opendir, (char *path, hgfs_dir_t *handle) );
+_PROTOTYPE( int hgfs_readdir, (hgfs_dir_t handle, unsigned int index,
+ char *buf, size_t size, struct hgfs_attr *attr) );
+_PROTOTYPE( int hgfs_closedir, (hgfs_dir_t handle) );
+
+_PROTOTYPE( int hgfs_getattr, (char *path, struct hgfs_attr *attr) );
+_PROTOTYPE( int hgfs_setattr, (char *path, struct hgfs_attr *attr) );
+
+_PROTOTYPE( int hgfs_mkdir, (char *path, int mode) );
+_PROTOTYPE( int hgfs_unlink, (char *path) );
+_PROTOTYPE( int hgfs_rmdir, (char *path) );
+_PROTOTYPE( int hgfs_rename, (char *opath, char *npath) );
+
+_PROTOTYPE( int hgfs_queryvol, (char *path, u64_t *free, u64_t *total) );
+
+#endif /* _HGFS_H */
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#define _POSIX_SOURCE 1 /* need PATH_MAX */
+#define _SYSTEM 1 /* need negative error codes */
+
+#include <minix/config.h>
+#include <minix/const.h>
+
+#include <string.h>
+#include <errno.h>
+
+#include "hgfs.h"
+
+#define PREFIX(x) __libhgfs_##x
+
+#include "type.h"
+#include "const.h"
+#include "proto.h"
+#include "glo.h"
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+#include <sys/stat.h>
+
+/*===========================================================================*
+ * hgfs_mkdir *
+ *===========================================================================*/
+PUBLIC int hgfs_mkdir(path, mode)
+char *path;
+int mode;
+{
+/* Create a new directory.
+ */
+
+ RPC_REQUEST(HGFS_REQ_MKDIR);
+ RPC_NEXT8 = HGFS_MODE_TO_PERM(mode);
+
+ path_put(path);
+
+ return rpc_query();
+}
+
+/*===========================================================================*
+ * hgfs_unlink *
+ *===========================================================================*/
+PUBLIC int hgfs_unlink(path)
+char *path;
+{
+/* Delete a file.
+ */
+
+ RPC_REQUEST(HGFS_REQ_UNLINK);
+
+ path_put(path);
+
+ return rpc_query();
+}
+
+/*===========================================================================*
+ * hgfs_rmdir *
+ *===========================================================================*/
+PUBLIC int hgfs_rmdir(path)
+char *path;
+{
+/* Remove an empty directory.
+ */
+
+ RPC_REQUEST(HGFS_REQ_RMDIR);
+
+ path_put(path);
+
+ return rpc_query();
+}
+
+/*===========================================================================*
+ * hgfs_rename *
+ *===========================================================================*/
+PUBLIC int hgfs_rename(opath, npath)
+char *opath;
+char *npath;
+{
+/* Rename a file or directory.
+ */
+
+ RPC_REQUEST(HGFS_REQ_RENAME);
+
+ path_put(opath);
+ path_put(npath);
+
+ return rpc_query();
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+/*===========================================================================*
+ * hgfs_init *
+ *===========================================================================*/
+PUBLIC int hgfs_init()
+{
+/* Initialize the library. Return OK on success, or a negative error code
+ * otherwise. If EAGAIN is returned, shared folders are disabled; in that
+ * case, other operations may be tried (and possibly succeed).
+ */
+
+ time_init();
+
+ return rpc_open();
+}
+
+/*===========================================================================*
+ * hgfs_cleanup *
+ *===========================================================================*/
+PUBLIC void hgfs_cleanup()
+{
+/* Clean up state.
+ */
+
+ rpc_close();
+}
+
+/*===========================================================================*
+ * hgfs_enabled *
+ *===========================================================================*/
+PUBLIC int hgfs_enabled()
+{
+/* Check if shared folders are enabled. Return OK if so, EAGAIN if not, and
+ * another negative error code on error.
+ */
+
+ return rpc_test();
+}
+
+/*===========================================================================*
+ * hgfs_queryvol *
+ *===========================================================================*/
+PUBLIC int hgfs_queryvol(path, free, total)
+char *path;
+u64_t *free;
+u64_t *total;
+{
+/* Retrieve information about available and total volume space associated with
+ * a given path.
+ */
+ u32_t lo, hi;
+ int r;
+
+ RPC_REQUEST(HGFS_REQ_QUERYVOL);
+
+ path_put(path);
+
+ if ((r = rpc_query()) != OK)
+ return r;
+
+ lo = RPC_NEXT32;
+ hi = RPC_NEXT32;
+ *free = make64(lo, hi);
+
+ lo = RPC_NEXT32;
+ hi = RPC_NEXT32;
+ *total = make64(lo, hi);
+
+ return OK;
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+#include <limits.h>
+
+/*===========================================================================*
+ * path_put *
+ *===========================================================================*/
+PUBLIC void path_put(path)
+char *path;
+{
+/* Append the given path name in HGFS format to the RPC buffer. Truncate it
+ * if it is longer than PATH_MAX bytes.
+ */
+ char *p, buf[PATH_MAX];
+ int len;
+
+ /* No leading slashes are allowed. */
+ for (p = path; *p == '/'; p++);
+
+ /* No double or tailing slashes, either. */
+ for (len = 0; *p && len < sizeof(buf) - 1; len++) {
+ if (*p == '/') {
+ for (p++; *p == '/'; p++);
+
+ if (!*p) break;
+
+ buf[len] = 0;
+ }
+ else buf[len] = *p++;
+ }
+
+ RPC_NEXT32 = len;
+
+ memcpy(RPC_PTR, buf, len);
+ RPC_ADVANCE(len);
+
+ RPC_NEXT8 = 0;
+}
+
+/*===========================================================================*
+ * path_get *
+ *===========================================================================*/
+PUBLIC int path_get(path, max)
+char *path;
+int max;
+{
+/* Retrieve a HGFS formatted path name from the RPC buffer. Returns EINVAL if
+ * the path name is invalid. Returns ENAMETOOLONG if the path name is too
+ * long. Returns OK on success.
+ */
+ char *p, *q;
+ int n, len;
+
+ n = len = RPC_NEXT32;
+
+ if (len >= max) return ENAMETOOLONG;
+
+ for (p = path, q = RPC_PTR; n--; p++, q++) {
+ /* We can not deal with a slash in a path component. */
+ if (*q == '/') return EINVAL;
+
+ if (*q == 0) *p = '/';
+ else *p = *q;
+ }
+
+ RPC_ADVANCE(len);
+
+ *p = 0;
+
+ return (RPC_NEXT8 != 0) ? EINVAL : OK;
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+/* attr.c */
+#define attr_get PREFIX(attr_get)
+_PROTOTYPE( void attr_get, (struct hgfs_attr *attr) );
+
+/* backdoor.s */
+#define backdoor PREFIX(backdoor)
+#define backdoor_in PREFIX(backdoor_in)
+#define backdoor_out PREFIX(backdoor_out)
+_PROTOTYPE( u32_t backdoor, (u32_t ptr[6]) );
+_PROTOTYPE( u32_t backdoor_in, (u32_t ptr[6]) );
+_PROTOTYPE( u32_t backdoor_out, (u32_t ptr[6]) );
+
+/* channel.c */
+#define channel_open PREFIX(channel_open)
+#define channel_close PREFIX(channel_close)
+#define channel_send PREFIX(channel_send)
+#define channel_recv PREFIX(channel_recv)
+_PROTOTYPE( int channel_open, (struct channel *ch, u32_t type) );
+_PROTOTYPE( void channel_close, (struct channel *ch) );
+_PROTOTYPE( int channel_send, (struct channel *ch, char *buf, int len) );
+_PROTOTYPE( int channel_recv, (struct channel *ch, char *buf, int max) );
+
+/* error.c */
+#define error_convert PREFIX(error_convert)
+_PROTOTYPE( int error_convert, (int err) );
+
+/* path.c */
+#define path_put PREFIX(path_put)
+#define path_get PREFIX(path_get)
+_PROTOTYPE( void path_put, (char *path) );
+_PROTOTYPE( int path_get, (char *path, int max) );
+
+/* rpc.c */
+#define rpc_open PREFIX(rpc_open)
+#define rpc_query PREFIX(rpc_query)
+#define rpc_test PREFIX(rpc_test)
+#define rpc_close PREFIX(rpc_close)
+_PROTOTYPE( int rpc_open, (void) );
+_PROTOTYPE( int rpc_query, (void) );
+_PROTOTYPE( int rpc_test, (void) );
+_PROTOTYPE( void rpc_close, (void) );
+
+/* time.c */
+#define time_init PREFIX(time_init)
+#define time_put PREFIX(time_put)
+#define time_get PREFIX(time_get)
+_PROTOTYPE( void time_init, (void) );
+_PROTOTYPE( void time_put, (time_t *timep) );
+_PROTOTYPE( void time_get, (time_t *timep) );
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+PUBLIC char rpc_buf[RPC_BUF_SIZE];
+PUBLIC char *rpc_ptr;
+
+PRIVATE struct channel rpc_chan;
+
+/*===========================================================================*
+ * rpc_open *
+ *===========================================================================*/
+PUBLIC int rpc_open()
+{
+/* Open a HGFS RPC backdoor channel to the VMware host, and make sure that it
+ * is working. Return OK upon success, or a negative error code otherwise.
+ */
+ int r;
+
+ if ((r = channel_open(&rpc_chan, CH_OUT)) != OK)
+ return r;
+
+ r = rpc_test();
+
+ if (r != OK && r != EAGAIN)
+ channel_close(&rpc_chan);
+
+ return r;
+}
+
+/*===========================================================================*
+ * rpc_query *
+ *===========================================================================*/
+PUBLIC int rpc_query()
+{
+/* Send a HGFS RPC query over the backdoor channel. Return OK upon success, or
+ * a negative error code otherwise; EAGAIN is returned if shared folders are
+ * disabled. In general, we make the assumption that the sender (= VMware)
+ * speaks the protocol correctly. Hence, the callers of this function do not
+ * check for lengths.
+ */
+ int r, len, id, err;
+
+ len = RPC_LEN;
+
+ /* A more robust version of this call could reopen the channel and
+ * retry the request upon low-level failure.
+ */
+ r = channel_send(&rpc_chan, rpc_buf, len);
+ if (r < 0) return r;
+
+ r = channel_recv(&rpc_chan, rpc_buf, sizeof(rpc_buf));
+ if (r < 0) return r;
+ if (r < 2 || (len > 2 && r < 10)) return EIO;
+
+ RPC_RESET;
+
+ if (RPC_NEXT8 != '1') return EAGAIN;
+ if (RPC_NEXT8 != ' ') return EAGAIN;
+
+ if (len <= 2) return OK;
+
+ id = RPC_NEXT32;
+ err = RPC_NEXT32;
+
+ return error_convert(err);
+}
+
+/*===========================================================================*
+ * rpc_test *
+ *===========================================================================*/
+PUBLIC int rpc_test()
+{
+/* Test whether HGFS communication is working. Return OK on success, EAGAIN if
+ * shared folders are disabled, or another negative error code upon error.
+ */
+
+ RPC_RESET;
+ RPC_NEXT8 = 'f';
+ RPC_NEXT8 = ' ';
+
+ return rpc_query();
+}
+
+/*===========================================================================*
+ * rpc_close *
+ *===========================================================================*/
+PUBLIC void rpc_close()
+{
+/* Close the HGFS RPC backdoor channel.
+ */
+
+ channel_close(&rpc_chan);
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+#include "inc.h"
+
+PRIVATE u64_t time_offset;
+
+/*===========================================================================*
+ * time_init *
+ *===========================================================================*/
+PUBLIC void time_init()
+{
+/* Initialize the time conversion module.
+ */
+
+ /* Generate a 64-bit value for the offset to use in time conversion. The
+ * HGFS time format uses Windows' FILETIME standard, expressing time in
+ * 100ns-units since Jan 1, 1601 UTC. The value that is generated is
+ * 116444736000000000.
+ */
+ /* FIXME: we currently do not take into account timezones. */
+ time_offset = make64(3577643008UL, 27111902UL);
+}
+
+/*===========================================================================*
+ * time_put *
+ *===========================================================================*/
+PUBLIC void time_put(timep)
+time_t *timep;
+{
+/* Store a UNIX timestamp pointed to by the given pointer onto the RPC buffer,
+ * in HGFS timestamp format. If a NULL pointer is given, store a timestamp of
+ * zero instead.
+ */
+ u64_t hgfstime;
+
+ if (timep != NULL) {
+ hgfstime = add64(mul64u(*timep, 10000000), time_offset);
+
+ RPC_NEXT32 = ex64lo(hgfstime);
+ RPC_NEXT32 = ex64hi(hgfstime);
+ } else {
+ RPC_NEXT32 = 0;
+ RPC_NEXT32 = 0;
+ }
+}
+
+/*===========================================================================*
+ * time_get *
+ *===========================================================================*/
+PUBLIC void time_get(timep)
+time_t *timep;
+{
+/* Get a HGFS timestamp from the RPC buffer, convert it into a UNIX timestamp,
+ * and store the result in the given time pointer. If the given pointer is
+ * NULL, however, simply skip over the timestamp in the RPC buffer.
+ */
+ u64_t hgfstime;
+ u32_t time_lo, time_hi;
+
+ if (timep != NULL) {
+ time_lo = RPC_NEXT32;
+ time_hi = RPC_NEXT32;
+
+ hgfstime = make64(time_lo, time_hi);
+
+ *timep = div64u(sub64(hgfstime, time_offset), 10000000);
+ }
+ else RPC_ADVANCE(sizeof(u32_t) * 2);
+}
--- /dev/null
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+struct channel {
+ u16_t id;
+ u32_t cookie1;
+ u32_t cookie2;
+};
--- /dev/null
+/* This file contains directory entry related file system call handlers.
+ *
+ * The entry points into this file are:
+ * do_create perform the CREATE file system call
+ * do_mkdir perform the MKDIR file system call
+ * do_unlink perform the UNLINK file system call
+ * do_rmdir perform the RMDIR file system call
+ * do_rename perform the RENAME file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+#include <fcntl.h>
+
+FORWARD _PROTOTYPE( int force_remove, (char *path, int dir) );
+
+/*===========================================================================*
+ * do_create *
+ *===========================================================================*/
+PUBLIC int do_create()
+{
+/* Create a new file.
+ */
+ char path[PATH_MAX], name[NAME_MAX+1];
+ struct inode *parent, *ino;
+ struct hgfs_attr attr;
+ hgfs_file_t handle;
+ int r;
+
+ /* We cannot create files on a read-only file system. */
+ if (state.read_only)
+ return EROFS;
+
+ /* Get path, name, parent inode and possibly inode for the given path. */
+ if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
+
+ if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if ((r = verify_dentry(parent, name, path, &ino)) != OK)
+ return r;
+
+ /* Are we going to need a new inode upon success?
+ * Then make sure there is one available before trying anything.
+ */
+ if (ino == NIL_INODE || ino->i_ref > 1 || HAS_CHILDREN(ino)) {
+ if (!have_free_inode()) {
+ if (ino != NIL_INODE)
+ put_inode(ino);
+
+ return ENFILE;
+ }
+ }
+
+ /* Perform the actual create call. */
+ r = hgfs_open(path, O_CREAT | O_EXCL | O_RDWR, m_in.REQ_MODE, &handle);
+
+ if (r != OK) {
+ /* Let's not try to be too clever with error codes here. If something
+ * is wrong with the directory, we'll find out later anyway.
+ */
+
+ if (ino != NIL_INODE)
+ put_inode(ino);
+
+ return r;
+ }
+
+ /* Get the created file's attributes. */
+ attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE;
+ r = hgfs_getattr(path, &attr);
+
+ /* If this fails, or returns a directory, we have a problem. This
+ * scenario is in fact possible with race conditions.
+ * Simulate a close and return a somewhat appropriate error.
+ */
+ if (r != OK || S_ISDIR(attr.a_mode)) {
+ printf("HGFS: lost file after creation!\n");
+
+ hgfs_close(handle);
+
+ if (ino != NIL_INODE) {
+ del_dentry(ino);
+
+ put_inode(ino);
+ }
+
+ return (r == OK) ? EEXIST : r;
+ }
+
+ /* We do assume that the HGFS open(O_CREAT|O_EXCL) did its job.
+ * If we previousy found an inode, get rid of it now. It's old.
+ */
+ if (ino != NIL_INODE) {
+ del_dentry(ino);
+
+ put_inode(ino);
+ }
+
+ /* Associate the open file handle with an inode, and reply with its details.
+ */
+ ino = get_free_inode();
+
+ assert(ino != NIL_INODE); /* we checked before whether we had a free one */
+
+ ino->i_file = handle;
+ ino->i_flags = I_HANDLE;
+
+ add_dentry(parent, name, ino);
+
+ m_out.RES_INODE_NR = INODE_NR(ino);
+ m_out.RES_MODE = get_mode(ino, attr.a_mode);
+ m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
+ m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
+ m_out.RES_UID = opt.uid;
+ m_out.RES_GID = opt.gid;
+ m_out.RES_DEV = NO_DEV;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_mkdir *
+ *===========================================================================*/
+PUBLIC int do_mkdir()
+{
+/* Make a new directory.
+ */
+ char path[PATH_MAX], name[NAME_MAX+1];
+ struct inode *parent, *ino;
+ int r;
+
+ /* We cannot create directories on a read-only file system. */
+ if (state.read_only)
+ return EROFS;
+
+ /* Get the path string and possibly an inode for the given path. */
+ if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
+
+ if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if ((r = verify_dentry(parent, name, path, &ino)) != OK)
+ return r;
+
+ /* Perform the actual mkdir call. */
+ r = hgfs_mkdir(path, m_in.REQ_MODE);
+
+ if (r != OK) {
+ if (ino != NIL_INODE)
+ put_inode(ino);
+
+ return r;
+ }
+
+ /* If we thought the new dentry already existed, it was apparently gone
+ * already. Delete it.
+ */
+ if (ino != NIL_INODE) {
+ del_dentry(ino);
+
+ put_inode(ino);
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * force_remove *
+ *===========================================================================*/
+PRIVATE int force_remove(path, dir)
+char *path; /* path to file or directory */
+int dir; /* TRUE iff directory */
+{
+/* Remove a file or directory. Wrapper around hgfs_unlink and hgfs_rmdir that
+ * makes the target temporarily writable if the operation fails with an access
+ * denied error. On Windows hosts, read-only files or directories cannot be
+ * removed (even though they can be renamed). In general, the HGFS server
+ * follows the behavior of the host file system, but this case just confuses
+ * the hell out of the MINIX userland..
+ */
+ struct hgfs_attr attr;
+ int r, r2;
+
+ /* First try to remove the target. */
+ if (dir)
+ r = hgfs_rmdir(path);
+ else
+ r = hgfs_unlink(path);
+
+ if (r != EACCES) return r;
+
+ /* If this fails with an access error, retrieve the target's mode. */
+ attr.a_mask = HGFS_ATTR_MODE;
+
+ r2 = hgfs_getattr(path, &attr);
+
+ if (r2 != OK || (attr.a_mode & S_IWUSR)) return r;
+
+ /* If the target is not writable, temporarily set it to writable. */
+ attr.a_mode |= S_IWUSR;
+
+ r2 = hgfs_setattr(path, &attr);
+
+ if (r2 != OK) return r;
+
+ /* Then try the original operation again. */
+ if (dir)
+ r = hgfs_rmdir(path);
+ else
+ r = hgfs_unlink(path);
+
+ if (r == OK) return r;
+
+ /* If the operation still fails, unset the writable bit again. */
+ attr.a_mode &= ~S_IWUSR;
+
+ hgfs_setattr(path, &attr);
+
+ return r;
+}
+
+/*===========================================================================*
+ * do_unlink *
+ *===========================================================================*/
+PUBLIC int do_unlink()
+{
+/* Delete a file.
+ */
+ char path[PATH_MAX], name[NAME_MAX+1];
+ struct inode *parent, *ino;
+ int r;
+
+ /* We cannot delete files on a read-only file system. */
+ if (state.read_only)
+ return EROFS;
+
+ /* Get the path string and possibly preexisting inode for the given path. */
+ if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
+ return r;
+
+ if (!strcmp(name, ".") || !strcmp(name, "..")) return EPERM;
+
+ if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if ((r = verify_dentry(parent, name, path, &ino)) != OK)
+ return r;
+
+ /* Perform the unlink call. */
+ r = force_remove(path, FALSE /*dir*/);
+
+ if (r != OK) {
+ if (ino != NIL_INODE)
+ put_inode(ino);
+
+ return r;
+ }
+
+ /* If a dentry existed for this name, it is gone now. */
+ if (ino != NIL_INODE) {
+ del_dentry(ino);
+
+ put_inode(ino);
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_rmdir *
+ *===========================================================================*/
+PUBLIC int do_rmdir()
+{
+/* Remove an empty directory.
+ */
+ char path[PATH_MAX], name[NAME_MAX+1];
+ struct inode *parent, *ino;
+ int r;
+
+ /* We cannot remove directories on a read-only file system. */
+ if (state.read_only)
+ return EROFS;
+
+ /* Get the path string and possibly preexisting inode for the given path. */
+ if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
+ return r;
+
+ if (!strcmp(name, ".")) return EINVAL;
+ if (!strcmp(name, "..")) return ENOTEMPTY;
+
+ if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if ((r = verify_dentry(parent, name, path, &ino)) != OK)
+ return r;
+
+ /* Perform the rmdir call. */
+ r = force_remove(path, TRUE /*dir*/);
+
+ if (r != OK) {
+ if (ino != NIL_INODE)
+ put_inode(ino);
+
+ return r;
+ }
+
+ /* If a dentry existed for this name, it is gone now. */
+ if (ino != NIL_INODE) {
+ del_dentry(ino);
+
+ put_inode(ino);
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_rename *
+ *===========================================================================*/
+PUBLIC int do_rename()
+{
+/* Rename a file or directory.
+ */
+ char old_path[PATH_MAX], new_path[PATH_MAX];
+ char old_name[NAME_MAX+1], new_name[NAME_MAX+1];
+ struct inode *old_parent, *new_parent;
+ struct inode *old_ino, *new_ino;
+ int r;
+
+ /* We cannot do rename on a read-only file system. */
+ if (state.read_only)
+ return EROFS;
+
+ /* Get path strings, names, directory inodes and possibly preexisting inodes
+ * for the old and new paths.
+ */
+ if ((r = get_name(m_in.REQ_REN_GRANT_OLD, m_in.REQ_REN_LEN_OLD,
+ old_name)) != OK) return r;
+
+ if ((r = get_name(m_in.REQ_REN_GRANT_NEW, m_in.REQ_REN_LEN_NEW,
+ new_name)) != OK) return r;
+
+ if (!strcmp(old_name, ".") || !strcmp(old_name, "..") ||
+ !strcmp(new_name, ".") || !strcmp(new_name, "..")) return EINVAL;
+
+ if ((old_parent = find_inode(m_in.REQ_REN_OLD_DIR)) == NIL_INODE ||
+ (new_parent = find_inode(m_in.REQ_REN_NEW_DIR)) == NIL_INODE)
+ return EINVAL;
+
+ if ((r = verify_dentry(old_parent, old_name, old_path, &old_ino)) != OK)
+ return r;
+
+ if ((r = verify_dentry(new_parent, new_name, new_path, &new_ino)) != OK) {
+ if (old_ino != NIL_INODE)
+ put_inode(old_ino);
+
+ return r;
+ }
+
+ /* Perform the actual rename call. */
+ r = hgfs_rename(old_path, new_path);
+
+ /* If we failed, or if we have nothing further to do: both inodes are
+ * NULL, or they both refer to the same file.
+ */
+ if (r != OK || old_ino == new_ino) {
+ if (old_ino != NIL_INODE) put_inode(old_ino);
+
+ if (new_ino != NIL_INODE) put_inode(new_ino);
+
+ return r;
+ }
+
+ /* If the new dentry already existed, it has now been overwritten.
+ * Delete the associated inode if we had found one.
+ */
+ if (new_ino != NIL_INODE) {
+ del_dentry(new_ino);
+
+ put_inode(new_ino);
+ }
+
+ /* If the old dentry existed, rename it accordingly. */
+ if (old_ino != NIL_INODE) {
+ del_dentry(old_ino);
+
+ add_dentry(new_parent, new_name, old_ino);
+
+ put_inode(old_ino);
+ }
+
+ return OK;
+}
--- /dev/null
+/* This file provides path-to-inode lookup functionality.
+ *
+ * The entry points into this file are:
+ * do_lookup perform the LOOKUP file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+FORWARD _PROTOTYPE( int get_mask, (vfs_ucred_t *ucred) );
+FORWARD _PROTOTYPE( int access_as_dir, (struct inode *ino,
+ struct hgfs_attr *attr, int uid, int mask) );
+FORWARD _PROTOTYPE( int next_name, (char **ptr, char **start,
+ char name[NAME_MAX+1]) );
+FORWARD _PROTOTYPE( int go_up, (char path[PATH_MAX], struct inode *ino,
+ struct inode **res_ino, struct hgfs_attr *attr) );
+FORWARD _PROTOTYPE( int go_down, (char path[PATH_MAX],
+ struct inode *ino, char *name,
+ struct inode **res_ino, struct hgfs_attr *attr) );
+
+/*===========================================================================*
+ * get_mask *
+ *===========================================================================*/
+PRIVATE int get_mask(ucred)
+vfs_ucred_t *ucred; /* credentials of the caller */
+{
+ /* Given the caller's credentials, precompute a search access mask to test
+ * against directory modes.
+ */
+ int i;
+
+ if (ucred->vu_uid == opt.uid) return S_IXUSR;
+
+ if (ucred->vu_gid == opt.gid) return S_IXGRP;
+
+ for (i = 0; i < ucred->vu_ngroups; i++)
+ if (ucred->vu_sgroups[i] == opt.gid) return S_IXGRP;
+
+ return S_IXOTH;
+}
+
+/*===========================================================================*
+ * access_as_dir *
+ *===========================================================================*/
+PRIVATE int access_as_dir(ino, attr, uid, mask)
+struct inode *ino; /* the inode to test */
+struct hgfs_attr *attr; /* attributes of the inode */
+int uid; /* UID of the caller */
+int mask; /* search access mask of the caller */
+{
+/* Check whether the given inode may be accessed as directory.
+ * Return OK or an appropriate error code.
+ */
+ mode_t mode;
+
+ assert(attr->a_mask & HGFS_ATTR_MODE);
+
+ /* The inode must be a directory to begin with. */
+ if (!IS_DIR(ino)) return ENOTDIR;
+
+ /* The caller must have search access to the directory. Root always does. */
+ if (uid == 0) return OK;
+
+ mode = get_mode(ino, attr->a_mode);
+
+ return (mode & mask) ? OK : EACCES;
+}
+
+/*===========================================================================*
+ * next_name *
+ *===========================================================================*/
+PRIVATE int next_name(ptr, start, name)
+char **ptr; /* cursor pointer into path (in, out) */
+char **start; /* place to store start of name */
+char name[NAME_MAX+1]; /* place to store name */
+{
+/* Get the next path component from a path.
+ */
+ char *p;
+ int i;
+
+ for (p = *ptr; *p == '/'; p++);
+
+ *start = p;
+
+ if (*p) {
+ for (i = 0; *p && *p != '/' && i <= NAME_MAX; p++, i++)
+ name[i] = *p;
+
+ if (i > NAME_MAX)
+ return ENAMETOOLONG;
+
+ name[i] = 0;
+ } else {
+ strcpy(name, ".");
+ }
+
+ *ptr = p;
+ return OK;
+}
+
+/*===========================================================================*
+ * go_up *
+ *===========================================================================*/
+PRIVATE int go_up(path, ino, res_ino, attr)
+char path[PATH_MAX]; /* path to take the last part from */
+struct inode *ino; /* inode of the current directory */
+struct inode **res_ino; /* place to store resulting inode */
+struct hgfs_attr *attr; /* place to store inode attributes */
+{
+/* Given an inode, progress into the parent directory.
+ */
+ struct inode *parent;
+ int r;
+
+ pop_path(path);
+
+ parent = ino->i_parent;
+ assert(parent != NIL_INODE);
+
+ if ((r = verify_path(path, parent, attr, NULL)) != OK)
+ return r;
+
+ get_inode(parent);
+
+ *res_ino = parent;
+
+ return r;
+}
+
+/*===========================================================================*
+ * go_down *
+ *===========================================================================*/
+PRIVATE int go_down(path, parent, name, res_ino, attr)
+char path[PATH_MAX]; /* path to add the name to */
+struct inode *parent; /* inode of the current directory */
+char *name; /* name of the directory entry */
+struct inode **res_ino; /* place to store resulting inode */
+struct hgfs_attr *attr; /* place to store inode attributes */
+{
+/* Given a directory inode and a name, progress into a directory entry.
+ */
+ struct inode *ino;
+ int r, stale = 0;
+
+ if ((r = push_path(path, name)) != OK)
+ return r;
+
+ dprintf(("HGFS: go_down: name '%s', path now '%s'\n", name, path));
+
+ ino = lookup_dentry(parent, name);
+
+ dprintf(("HGFS: lookup_dentry('%s') returned %p\n", name, ino));
+
+ if (ino != NIL_INODE)
+ r = verify_path(path, ino, attr, &stale);
+ else
+ r = hgfs_getattr(path, attr);
+
+ dprintf(("HGFS: path query returned %d\n", r));
+
+ if (r != OK) {
+ if (ino != NIL_INODE) {
+ put_inode(ino);
+
+ ino = NIL_INODE;
+ }
+
+ if (!stale)
+ return r;
+ }
+
+ dprintf(("HGFS: name '%s'\n", name));
+
+ if (ino == NIL_INODE) {
+ if ((ino = get_free_inode()) == NIL_INODE)
+ return ENFILE;
+
+ dprintf(("HGFS: inode %p ref %d\n", ino, ino->i_ref));
+
+ ino->i_flags = MODE_TO_DIRFLAG(attr->a_mode);
+
+ add_dentry(parent, name, ino);
+ }
+
+ *res_ino = ino;
+ return OK;
+}
+
+/*===========================================================================*
+ * do_lookup *
+ *===========================================================================*/
+PUBLIC int do_lookup()
+{
+/* Resolve a path string to an inode.
+ */
+ ino_t dir_ino_nr, root_ino_nr;
+ struct inode *cur_ino, *next_ino, *root_ino;
+ struct hgfs_attr attr;
+ char buf[PATH_MAX], path[PATH_MAX];
+ char name[NAME_MAX+1];
+ char *ptr, *last;
+ vfs_ucred_t ucred;
+ mode_t mask;
+ size_t len;
+ int r;
+
+ dir_ino_nr = m_in.REQ_DIR_INO;
+ root_ino_nr = m_in.REQ_ROOT_INO;
+ len = m_in.REQ_PATH_LEN;
+
+ /* Fetch the path name. */
+ if (len < 1 || len > PATH_MAX)
+ return EINVAL;
+
+ r = sys_safecopyfrom(m_in.m_source, m_in.REQ_GRANT, 0,
+ (vir_bytes) buf, len, D);
+
+ if (r != OK)
+ return r;
+
+ if (buf[len-1] != 0) {
+ printf("HGFS: VFS did not zero-terminate path!\n");
+
+ return EINVAL;
+ }
+
+ /* Fetch the credentials, and generate a search access mask to test against
+ * directory modes.
+ */
+ if (m_in.REQ_FLAGS & PATH_GET_UCRED) {
+ if (m_in.REQ_UCRED_SIZE != sizeof(ucred)) {
+ printf("HGFS: bad credential structure size\n");
+
+ return EINVAL;
+ }
+
+ r = sys_safecopyfrom(m_in.m_source, m_in.REQ_GRANT2, 0,
+ (vir_bytes) &ucred, m_in.REQ_UCRED_SIZE, D);
+
+ if (r != OK)
+ return r;
+ }
+ else {
+ ucred.vu_uid = m_in.REQ_UID;
+ ucred.vu_gid = m_in.REQ_GID;
+ ucred.vu_ngroups = 0;
+ }
+
+ mask = get_mask(&ucred);
+
+ /* Start the actual lookup. */
+ dprintf(("HGFS: lookup: got query '%s'\n", buf));
+
+ if ((cur_ino = find_inode(dir_ino_nr)) == NIL_INODE)
+ return EINVAL;
+
+ attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE;
+
+ if ((r = verify_inode(cur_ino, path, &attr)) != OK)
+ return r;
+
+ get_inode(cur_ino);
+
+ if (root_ino_nr > 0)
+ root_ino = find_inode(root_ino_nr);
+ else
+ root_ino = NIL_INODE;
+
+ /* One possible optimization would be to check a path only right before the
+ * first ".." in a row, and at the very end (if still necessary). This would
+ * have consequences for inode validation, though.
+ */
+ for (ptr = last = buf; *ptr != 0; ) {
+ if ((r = access_as_dir(cur_ino, &attr, ucred.vu_uid, mask)) != OK)
+ break;
+
+ if ((r = next_name(&ptr, &last, name)) != OK)
+ break;
+
+ dprintf(("HGFS: lookup: next name '%s'\n", name));
+
+ if (!strcmp(name, ".") ||
+ (cur_ino == root_ino && !strcmp(name, "..")))
+ continue;
+
+ if (!strcmp(name, "..")) {
+ if (IS_ROOT(cur_ino))
+ r = ELEAVEMOUNT;
+ else
+ r = go_up(path, cur_ino, &next_ino, &attr);
+ } else {
+ r = go_down(path, cur_ino, name, &next_ino, &attr);
+ }
+
+ if (r != OK)
+ break;
+
+ assert(next_ino != NIL_INODE);
+
+ put_inode(cur_ino);
+
+ cur_ino = next_ino;
+ }
+
+ dprintf(("HGFS: lookup: result %d\n", r));
+
+ if (r != OK) {
+ put_inode(cur_ino);
+
+ /* We'd need support for these here. We don't have such support. */
+ assert(r != EENTERMOUNT && r != ESYMLINK);
+
+ if (r == ELEAVEMOUNT) {
+ m_out.RES_OFFSET = (int)(last - buf);
+ m_out.RES_SYMLOOP = 0;
+ }
+
+ return r;
+ }
+
+ m_out.RES_INODE_NR = INODE_NR(cur_ino);
+ m_out.RES_MODE = get_mode(cur_ino, attr.a_mode);
+ m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
+ m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
+ m_out.RES_UID = opt.uid;
+ m_out.RES_GID = opt.gid;
+ m_out.RES_DEV = NO_DEV;
+
+ return OK;
+}
--- /dev/null
+/* This file contains the main message loop of the HGFS file system server.
+ *
+ * The entry points into this file are:
+ * main main program function
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+#include "optset.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+FORWARD _PROTOTYPE( int init, (int type, sef_init_info_t *info) );
+FORWARD _PROTOTYPE( void sef_local_startup, (void) );
+FORWARD _PROTOTYPE( void cleanup, (void) );
+FORWARD _PROTOTYPE( int get_work, (endpoint_t *who_e) );
+FORWARD _PROTOTYPE( void send_reply, (int err) );
+FORWARD _PROTOTYPE( int proc_event, (void) );
+
+PRIVATE struct optset optset_table[] = {
+ { "prefix", OPT_STRING, opt.prefix, sizeof(opt.prefix) },
+ { "uid", OPT_INT, &opt.uid, 10 },
+ { "gid", OPT_INT, &opt.gid, 10 },
+ { "fmask", OPT_INT, &opt.file_mask, 8 },
+ { "dmask", OPT_INT, &opt.dir_mask, 8 },
+ { "icase", OPT_BOOL, &opt.case_insens, TRUE },
+ { "noicase", OPT_BOOL, &opt.case_insens, FALSE },
+ { NULL }
+};
+
+EXTERN int env_argc;
+EXTERN char **env_argv;
+
+/*===========================================================================*
+ * init *
+ *===========================================================================*/
+PRIVATE int init(type, info)
+int type;
+sef_init_info_t *info;
+{
+/* Initialize this file server. Called at startup time.
+ */
+ message m;
+ int i, r;
+
+ /* Defaults */
+ opt.prefix[0] = 0;
+ opt.uid = 0;
+ opt.gid = 0;
+ opt.file_mask = 0755;
+ opt.dir_mask = 0755;
+ opt.case_insens = FALSE;
+
+ /* If we have been given an options string, parse options from there. */
+ for (i = 1; i < env_argc - 1; i++)
+ if (!strcmp(env_argv[i], "-o"))
+ optset_parse(optset_table, env_argv[++i]);
+
+ /* Make sure that the given path prefix doesn't end with a slash. */
+ for (i = strlen(opt.prefix); i > 0 && opt.prefix[i - 1] == '/'; i--);
+ opt.prefix[i] = 0;
+
+ /* Initialize the HGFS library. If this fails, exit immediately. */
+ r = hgfs_init();
+ if (r != OK && r != EAGAIN) {
+ printf("HGFS: unable to initialize HGFS library (%d)\n", r);
+
+ return r;
+ }
+
+ state.mounted = FALSE;
+
+ /* Announce our presence to VFS. */
+ m.m_type = FS_READY;
+
+ if ((r = send(FS_PROC_NR, &m)) != OK) {
+ printf("HGFS: unable to login to VFS (%d)\n", r);
+
+ return r;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * sef_local_startup *
+ *===========================================================================*/
+PRIVATE void sef_local_startup()
+{
+/* Specify initialization routines and start the SEF framework.
+ */
+
+ sef_setcb_init_fresh(init);
+ sef_setcb_init_restart(init);
+
+ /* No live update support yet. */
+
+ sef_startup();
+}
+
+/*===========================================================================*
+ * cleanup *
+ *===========================================================================*/
+PRIVATE void cleanup()
+{
+/* Clean up any resources in use by this file server. Called at shutdown time.
+ */
+
+ /* Pass on the cleanup request to the HGFS library. */
+ hgfs_cleanup();
+}
+
+/*===========================================================================*
+ * main *
+ *===========================================================================*/
+PUBLIC int main(argc, argv)
+int argc;
+char *argv[];
+{
+/* The main function of this file server. After initializing, loop forever
+ * receiving one request from VFS at a time, processing it, and sending a
+ * reply back to VFS.
+ */
+ endpoint_t who_e;
+ int call_nr, err;
+
+ env_setargs(argc, argv);
+ sef_local_startup();
+
+ for (;;) {
+ call_nr = get_work(&who_e);
+
+ if (who_e != FS_PROC_NR) {
+ /* Is this PM telling us to shut down? */
+ if (who_e == PM_PROC_NR && is_notify(call_nr))
+ if (proc_event()) break;
+
+ continue;
+ }
+
+ if (state.mounted || call_nr == REQ_READSUPER) {
+ call_nr -= VFS_BASE;
+
+ dprintf(("HGFS: call %d\n", call_nr));
+
+ if (call_nr >= 0 && call_nr < NREQS) {
+ err = (*call_vec[call_nr])();
+ } else {
+ err = ENOSYS;
+ }
+
+ dprintf(("HGFS: call %d result %d\n", call_nr, err));
+ }
+ else err = EINVAL;
+
+ send_reply(err);
+ }
+
+ cleanup();
+
+ return 0;
+}
+
+/*===========================================================================*
+ * get_work *
+ *===========================================================================*/
+PRIVATE int get_work(who_e)
+endpoint_t *who_e;
+{
+/* Receive a request message from VFS. Return the request call number.
+ */
+ int r;
+
+ if ((r = sef_receive(ANY, &m_in)) != OK)
+ panic("HGFS", "receive failed", r);
+
+ *who_e = m_in.m_source;
+
+ return m_in.m_type;
+}
+
+/*===========================================================================*
+ * send_reply *
+ *===========================================================================*/
+PRIVATE void send_reply(err)
+int err; /* resulting error code */
+{
+/* Send a reply message to the requesting party, with the given error code.
+ */
+ int r;
+
+ m_out.m_type = err;
+
+ if ((r = send(m_in.m_source, &m_out)) != OK)
+ printf("HGFS: send failed (%d)\n", r);
+}
+
+/*===========================================================================*
+ * proc_event *
+ *===========================================================================*/
+PRIVATE int proc_event()
+{
+/* We got a notification from PM; see what it's about. Return TRUE if this
+ * server has been told to shut down.
+ */
+ sigset_t set;
+ int r;
+
+ if ((r = getsigset(&set)) != OK) {
+ printf("HGFS: unable to get pending signals from PM (%d)\n", r);
+
+ return FALSE;
+ }
+
+ if (sigismember(&set, SIGTERM)) {
+ if (state.mounted) {
+ dprintf(("HGFS: got SIGTERM, still mounted\n"));
+
+ return FALSE;
+ }
+
+ dprintf(("HGFS: got SIGTERM, shutting down\n"));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
--- /dev/null
+/* This file contains miscellaneous file system call handlers.
+ *
+ * The entry points into this file are:
+ * do_fstatfs perform the FSTATFS file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+#include <sys/statfs.h>
+
+/*===========================================================================*
+ * do_fstatfs *
+ *===========================================================================*/
+PUBLIC int do_fstatfs()
+{
+/* Retrieve file system statistics.
+ */
+ struct statfs statfs;
+
+ statfs.f_bsize = BLOCK_SIZE; /* arbitrary block size constant */
+
+ return sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, 0,
+ (vir_bytes) &statfs, sizeof(statfs), D);
+}
--- /dev/null
+/* This file contains mount and unmount functionality.
+ *
+ * The entry points into this file are:
+ * do_readsuper perform the READSUPER file system call
+ * do_unmount perform the UNMOUNT file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+/*===========================================================================*
+ * do_readsuper *
+ *===========================================================================*/
+PUBLIC int do_readsuper()
+{
+/* Mount the file system.
+ */
+ char path[PATH_MAX];
+ struct inode *ino;
+ struct hgfs_attr attr;
+ int r;
+
+ dprintf(("HGFS: readsuper (dev %x, flags %x)\n",
+ m_in.REQ_DEV, m_in.REQ_FLAGS));
+
+ if (m_in.REQ_FLAGS & REQ_ISROOT) {
+ printf("HGFS: attempt to mount as root device\n");
+
+ return EINVAL;
+ }
+
+ state.read_only = !!(m_in.REQ_FLAGS & REQ_RDONLY);
+ state.dev = m_in.REQ_DEV;
+
+ init_dentry();
+ ino = init_inode();
+
+ attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE;
+
+ /* We cannot continue if we fail to get the properties of the root inode at
+ * all, because we cannot guess the details of the root node to return to
+ * VFS. Print a (hopefully) helpful error message, and abort the mount.
+ */
+ if ((r = verify_inode(ino, path, &attr)) != OK) {
+ if (r == EAGAIN)
+ printf("HGFS: shared folders disabled\n");
+ else if (opt.prefix[0] && (r == ENOENT || r == EACCES))
+ printf("HGFS: unable to access the given prefix directory\n");
+ else
+ printf("HGFS: unable to access shared folders\n");
+
+ return r;
+ }
+
+ m_out.RES_INODE_NR = INODE_NR(ino);
+ m_out.RES_MODE = get_mode(ino, attr.a_mode);
+ m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
+ m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
+ m_out.RES_UID = opt.uid;
+ m_out.RES_GID = opt.gid;
+ m_out.RES_DEV = NO_DEV;
+
+ state.mounted = TRUE;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_unmount *
+ *===========================================================================*/
+PUBLIC int do_unmount()
+{
+/* Unmount the file system.
+ */
+ struct inode *ino;
+
+ dprintf(("HGFS: do_unmount\n"));
+
+ /* Decrease the reference count of the root inode. */
+ if ((ino = find_inode(ROOT_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ put_inode(ino);
+
+ /* There should not be any referenced inodes anymore now. */
+ if (have_used_inode())
+ printf("HGFS: in-use inodes left at unmount time!\n");
+
+ state.mounted = FALSE;
+
+ return OK;
+}
--- /dev/null
+/* This file contains path component name utility functions.
+ *
+ * The entry points into this file are:
+ * normalize_name normalize a path component name for hashing purposes
+ * compare_name check whether two path component names are equivalent
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+#include <ctype.h>
+
+/*===========================================================================*
+ * normalize_name *
+ *===========================================================================*/
+PUBLIC void normalize_name(dst, src)
+char dst[NAME_MAX+1];
+char *src;
+{
+/* Normalize the given path component name, storing the result in the given
+ * buffer.
+ */
+ size_t i, size;
+
+ size = strlen(src) + 1;
+
+ assert(size <= NAME_MAX+1);
+
+ if (opt.case_insens) {
+ for (i = 0; i < size; i++)
+ *dst++ = tolower(*src++);
+ }
+ else memcpy(dst, src, size);
+}
+
+/*===========================================================================*
+ * compare_name *
+ *===========================================================================*/
+PUBLIC int compare_name(name1, name2)
+char *name1;
+char *name2;
+{
+/* Return TRUE if the given path component names are equivalent, FALSE
+ * otherwise.
+ */
+ int r;
+
+ if (opt.case_insens)
+ r = strcasecmp(name1, name2);
+ else
+ r = strcmp(name1, name2);
+
+ return r ? FALSE : TRUE;
+}
--- /dev/null
+/* This file provides functionality to parse strings of comma-separated
+ * options, each being either a single key name or a key=value pair, where the
+ * value may be enclosed in quotes. A table of optset entries is provided to
+ * determine which options are recognized, how to parse their values, and where
+ * to store those. Unrecognized options are silently ignored; improperly
+ * formatted options are silently set to reasonably acceptable values.
+ *
+ * The entry points into this file are:
+ * optset_parse parse the given options string using the given table
+ *
+ * Created:
+ * May 2009 (D.C. van Moolenbroek)
+ */
+
+#define _MINIX 1
+#include <stdlib.h>
+#include <string.h>
+#include <minix/config.h>
+#include <minix/const.h>
+
+#include "optset.h"
+
+FORWARD _PROTOTYPE( void optset_parse_entry, (struct optset *entry,
+ char *ptr, int len) );
+
+/*===========================================================================*
+ * optset_parse_entry *
+ *===========================================================================*/
+PRIVATE void optset_parse_entry(entry, ptr, len)
+struct optset *entry;
+char *ptr;
+int len;
+{
+/* Parse and store the value of a single option.
+ */
+ char *dst;
+ int val;
+
+ switch (entry->os_type) {
+ case OPT_BOOL:
+ *((int *) entry->os_ptr) = entry->os_val;
+
+ break;
+
+ case OPT_STRING:
+ if (len >= entry->os_val)
+ len = entry->os_val - 1;
+
+ dst = (char *) entry->os_ptr;
+
+ if (len > 0)
+ memcpy(dst, ptr, len);
+ dst[len] = 0;
+
+ break;
+
+ case OPT_INT:
+ if (len > 0)
+ val = strtol(ptr, NULL, entry->os_val);
+ else
+ val = 0;
+
+ *((int *) entry->os_ptr) = val;
+
+ break;
+ }
+}
+
+/*===========================================================================*
+ * optset_parse *
+ *===========================================================================*/
+PUBLIC void optset_parse(table, string)
+struct optset *table;
+char *string;
+{
+/* Parse a string of options, using the provided table of optset entries.
+ */
+ char *p, *kptr, *vptr;
+ int i, klen, vlen;
+
+ for (p = string; *p; ) {
+ /* Get the key name for the field. */
+ for (kptr = p, klen = 0; *p && *p != '=' && *p != ','; p++, klen++);
+
+ if (*p == '=') {
+ /* The field has an associated value. */
+ vptr = ++p;
+
+ /* If the first character after the '=' is a quote character,
+ * find a matching quote character followed by either a comma
+ * or the terminating null character, and use the string in
+ * between. Otherwise, use the string up to the next comma or
+ * the terminating null character.
+ */
+ if (*p == '\'' || *p == '"') {
+ p++;
+
+ for (vlen = 0; *p && (*p != *vptr ||
+ (p[1] && p[1] != ',')); p++, vlen++);
+
+ if (*p) p++;
+ vptr++;
+ }
+ else
+ for (vlen = 0; *p && *p != ','; p++, vlen++);
+ }
+ else {
+ vptr = NULL;
+ vlen = 0;
+ }
+
+ if (*p == ',') p++;
+
+ /* Find a matching entry for this key in the given table. If found,
+ * call optset_parse_entry() on it. Silently ignore the option
+ * otherwise.
+ */
+ for (i = 0; table[i].os_name != NULL; i++) {
+ if (strlen(table[i].os_name) == klen &&
+ !strncasecmp(table[i].os_name, kptr, klen)) {
+
+ optset_parse_entry(&table[i], vptr, vlen);
+
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+#ifndef _OPTSET_H
+#define _OPTSET_H
+
+enum {
+ OPT_BOOL,
+ OPT_STRING,
+ OPT_INT
+};
+
+/* An entry for the parser of an options set. The 'os_name' field must point
+ * to a string, which is treated case-insensitively; the last entry of a table
+ * must have NULL name. The 'os_type' field must be set to one of the OPT_
+ * values defined above. The 'os_ptr' field must point to the field that is to
+ * receive the value of a recognized option. For OPT_STRING, it must point to a
+ * string of a size set in 'os_val'; the resulting string may be truncated, but
+ * will always be null-terminated. For OPT_BOOL, it must point to an int which
+ * will be set to the value in 'os_val' if the option is present. For OPT_INT,
+ * it must point to an int which will be set to the provided option value;
+ * 'os_val' is then a base passed to strtol().
+ */
+struct optset {
+ char *os_name;
+ int os_type;
+ void *os_ptr;
+ int os_val;
+};
+
+_PROTOTYPE( void optset_parse, (struct optset *table, char *string) );
+
+#endif /* _OPTSET_H */
--- /dev/null
+/* This file contains routines for creating and manipulating path strings.
+ *
+ * The entry points into this file are:
+ * make_path construct a path string for an inode
+ * push_path add a path component to the end of a path string
+ * pop_path remove the last path component from a path string
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+/*===========================================================================*
+ * make_path *
+ *===========================================================================*/
+PUBLIC int make_path(path, ino)
+char path[PATH_MAX];
+struct inode *ino;
+{
+/* Given an inode, construct the path identifying that inode.
+ */
+ char buf[PATH_MAX], *p, *prefix;
+ size_t len, plen, total;
+
+ p = &buf[sizeof(buf) - 1];
+ p[0] = 0;
+
+ dprintf(("HGFS: make_path: constructing path for inode %d\n", ino->i_num));
+
+ /* Get the length of the prefix, skipping any leading slashes. */
+ for (prefix = opt.prefix; prefix[0] == '/'; prefix++);
+ plen = strlen(prefix);
+
+ /* Construct the path right-to-left in a temporary buffer first. */
+ for (total = plen; ino != NIL_INODE && !IS_ROOT(ino); ino = ino->i_parent) {
+ len = strlen(ino->i_name);
+
+ total += len + 1;
+ p -= len + 1;
+
+ if (total >= sizeof(buf))
+ return ENAMETOOLONG;
+
+ p[0] = '/';
+ memcpy(p + 1, ino->i_name, len);
+ }
+
+ /* If any of the intermediate inodes has no parent, the final inode is no
+ * longer addressable by name.
+ */
+ if (ino == NIL_INODE)
+ return ENOENT;
+
+ /* Put the result in the actual buffer. We need the leading slash in the
+ * temporary buffer only when the prefix is not empty.
+ */
+ if (!prefix[0] && p[0] == '/') p++;
+
+ strcpy(path, prefix);
+ strcpy(&path[plen], p);
+
+ dprintf(("HGFS: make_path: resulting path is '%s'\n", path));
+
+ return OK;
+}
+
+/*===========================================================================*
+ * push_path *
+ *===========================================================================*/
+PUBLIC int push_path(path, name)
+char path[PATH_MAX];
+char *name;
+{
+/* Add a component to the end of a path.
+ */
+ size_t len, add;
+
+ len = strlen(path);
+ add = strlen(name);
+ if (len > 0) add++;
+
+ if (len + add >= PATH_MAX)
+ return ENAMETOOLONG;
+
+ if (len > 0) path[len++] = '/';
+ strcpy(&path[len], name);
+
+ return OK;
+}
+
+/*===========================================================================*
+ * pop_path *
+ *===========================================================================*/
+PUBLIC void pop_path(path)
+char path[PATH_MAX];
+{
+/* Remove the last component from a path.
+ */
+ char *p;
+
+ p = strrchr(path, '/');
+
+ if (p == NULL) {
+ p = path;
+
+ /* Can't pop the root component */
+ assert(p[0] != 0);
+ }
+
+ p[0] = 0;
+}
--- /dev/null
+
+/* dentry.c */
+_PROTOTYPE( void init_dentry, (void) );
+_PROTOTYPE( struct inode *lookup_dentry, (struct inode *parent,
+ char *name) );
+_PROTOTYPE( void add_dentry, (struct inode *parent, char *name,
+ struct inode *ino) );
+_PROTOTYPE( void del_dentry, (struct inode *ino) );
+
+/* handle.c */
+_PROTOTYPE( int get_handle, (struct inode *ino) );
+_PROTOTYPE( void put_handle, (struct inode *ino) );
+
+/* inode.c */
+_PROTOTYPE( struct inode *init_inode, (void) );
+_PROTOTYPE( struct inode *find_inode, (ino_t ino_nr) );
+_PROTOTYPE( void get_inode, (struct inode *ino) );
+_PROTOTYPE( void put_inode, (struct inode *ino) );
+_PROTOTYPE( void link_inode, (struct inode *parent, struct inode *ino) );
+_PROTOTYPE( void unlink_inode, (struct inode *ino) );
+_PROTOTYPE( struct inode *get_free_inode, (void) );
+_PROTOTYPE( int have_free_inode, (void) );
+_PROTOTYPE( int have_used_inode, (void) );
+_PROTOTYPE( int do_putnode, (void) );
+
+/* link.c */
+_PROTOTYPE( int do_create, (void) );
+_PROTOTYPE( int do_mkdir, (void) );
+_PROTOTYPE( int do_unlink, (void) );
+_PROTOTYPE( int do_rmdir, (void) );
+_PROTOTYPE( int do_rename, (void) );
+
+/* lookup.c */
+_PROTOTYPE( int do_lookup, (void) );
+
+/* main.c */
+_PROTOTYPE( int main, (int argc, char *argv[]) );
+
+/* misc.c */
+_PROTOTYPE( int do_fstatfs, (void) );
+
+/* mount.c */
+_PROTOTYPE( int do_readsuper, (void) );
+_PROTOTYPE( int do_unmount, (void) );
+
+/* name.c */
+_PROTOTYPE( void normalize_name, (char dst[NAME_MAX+1], char *src) );
+_PROTOTYPE( int compare_name, (char *name1, char *name2) );
+
+/* path.c */
+_PROTOTYPE( int make_path, (char path[PATH_MAX], struct inode *ino) );
+_PROTOTYPE( int push_path, (char path[PATH_MAX], char *name) );
+_PROTOTYPE( void pop_path, (char path[PATH_MAX]) );
+
+/* read.c */
+_PROTOTYPE( int do_read, (void) );
+_PROTOTYPE( int do_getdents, (void) );
+
+/* stat.c */
+_PROTOTYPE( mode_t get_mode, (struct inode *ino, int mode) );
+_PROTOTYPE( int do_stat, (void) );
+_PROTOTYPE( int do_chmod, (void) );
+_PROTOTYPE( int do_utime, (void) );
+
+/* util.c */
+_PROTOTYPE( int get_name, (cp_grant_id_t grant, size_t len,
+ char name[NAME_MAX+1]) );
+_PROTOTYPE( int do_noop, (void) );
+_PROTOTYPE( int no_sys, (void) );
+
+/* verify.c */
+_PROTOTYPE( int verify_path, (char *path, struct inode *ino,
+ struct hgfs_attr *attr, int *stale) );
+_PROTOTYPE( int verify_inode, (struct inode *ino, char path[PATH_MAX],
+ struct hgfs_attr *attr) );
+_PROTOTYPE( int verify_dentry, (struct inode *parent,
+ char name[NAME_MAX+1], char path[PATH_MAX],
+ struct inode **res_ino) );
+
+/* write.c */
+_PROTOTYPE( int do_write, (void) );
+_PROTOTYPE( int do_ftrunc, (void) );
--- /dev/null
+/* This file contains file and directory reading file system call handlers.
+ *
+ * The entry points into this file are:
+ * do_read perform the READ file system call
+ * do_getdents perform the GETDENTS file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+#include <dirent.h>
+
+#define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+
+/*===========================================================================*
+ * do_read *
+ *===========================================================================*/
+PUBLIC int do_read()
+{
+/* Read data from a file.
+ */
+ struct inode *ino;
+ u64_t pos;
+ size_t count, size;
+ vir_bytes off;
+ char *ptr;
+ int r, chunk;
+
+ if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if (IS_DIR(ino)) return EISDIR;
+
+ if ((r = get_handle(ino)) != OK)
+ return r;
+
+ pos = make64(m_in.REQ_SEEK_POS_LO, m_in.REQ_SEEK_POS_HI);
+ count = m_in.REQ_NBYTES;
+
+ assert(count > 0);
+
+ /* Use the buffer from libhgfs to eliminate extra copying. */
+ size = hgfs_readbuf(&ptr);
+ off = 0;
+
+ while (count > 0) {
+ chunk = MIN(count, size);
+
+ if ((r = hgfs_read(ino->i_file, NULL, chunk, pos)) <= 0)
+ break;
+
+ chunk = r;
+
+ r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, off,
+ (vir_bytes) ptr, chunk, D);
+
+ if (r != OK)
+ break;
+
+ count -= chunk;
+ off += chunk;
+ pos = add64u(pos, chunk);
+ }
+
+ if (r < 0)
+ return r;
+
+ m_out.RES_SEEK_POS_HI = ex64hi(pos);
+ m_out.RES_SEEK_POS_LO = ex64lo(pos);
+ m_out.RES_NBYTES = off;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_getdents *
+ *===========================================================================*/
+PUBLIC int do_getdents()
+{
+/* Retrieve directory entries.
+ */
+ char name[NAME_MAX+1];
+ struct inode *ino, *child;
+ struct dirent *dent;
+ struct hgfs_attr attr;
+ size_t len, off, user_off, user_left;
+ off_t pos;
+ int r;
+ /* must be at least sizeof(struct dirent) + NAME_MAX */
+ static char buf[BLOCK_SIZE];
+
+ attr.a_mask = HGFS_ATTR_MODE;
+
+ if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if (m_in.REQ_SEEK_POS_HI != 0) return EINVAL;
+
+ if (!IS_DIR(ino)) return ENOTDIR;
+
+ /* We are going to need at least one free inode to store children in. */
+ if (!have_free_inode()) return ENFILE;
+
+ /* If we don't have a directory handle yet, get one now. */
+ if ((r = get_handle(ino)) != OK)
+ return r;
+
+ off = 0;
+ user_off = 0;
+ user_left = m_in.REQ_MEM_SIZE;
+
+ /* We use the seek position as file index number. The first position is for
+ * the "." entry, the second position is for the ".." entry, and the next
+ * position numbers each represent a file in the directory.
+ */
+ for (pos = m_in.REQ_SEEK_POS_LO; ; pos++) {
+ /* Determine which inode and name to use for this entry.
+ * We have no idea whether the HGFS host will give us "." and/or "..",
+ * so generate our own and skip those from the host.
+ */
+ if (pos == 0) {
+ /* Entry for ".". */
+ child = ino;
+
+ strcpy(name, ".");
+
+ get_inode(child);
+ }
+ else if (pos == 1) {
+ /* Entry for "..", but only when there is a parent. */
+ if (ino->i_parent == NIL_INODE)
+ continue;
+
+ child = ino->i_parent;
+
+ strcpy(name, "..");
+
+ get_inode(child);
+ }
+ else {
+ /* Any other entry, not being "." or "..". */
+ r = hgfs_readdir(ino->i_dir, pos - 2, name, sizeof(name),
+ &attr);
+
+ if (r != OK || !name[0]) {
+ /* No more entries? Then close the handle and stop. */
+ /* VMware Player 3 returns an empty name, instead of
+ * EINVAL, when reading from an EOF position right
+ * after opening the directory handle. Seems to be a
+ * newly introduced bug..
+ */
+ if (r == EINVAL || !name[0]) {
+ put_handle(ino);
+
+ break;
+ }
+
+ /* FIXME: what if the error is ENAMETOOLONG? */
+ return r;
+ }
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ if ((child = lookup_dentry(ino, name)) == NIL_INODE) {
+ child = get_free_inode();
+
+ /* We were promised a free inode! */
+ assert(child != NIL_INODE);
+
+ child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
+
+ add_dentry(ino, name, child);
+ }
+ }
+
+ len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name));
+
+ /* Is the user buffer too small to store another record?
+ * Note that we will be rerequesting the same dentry upon a subsequent
+ * getdents call this way, but we really need the name length for this.
+ */
+ if (user_off + off + len > user_left) {
+ put_inode(child);
+
+ /* Is the user buffer too small for even a single record? */
+ if (user_off == 0 && off == 0)
+ return EINVAL;
+
+ break;
+ }
+
+ /* If our own buffer cannot contain the new record, copy out first. */
+ if (off + len > sizeof(buf)) {
+ r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT,
+ user_off, (vir_bytes) buf, off, D);
+
+ if (r != OK) {
+ put_inode(child);
+
+ return r;
+ }
+
+ user_off += off;
+ user_left -= off;
+ off = 0;
+ }
+
+ /* Fill in the actual directory entry. */
+ dent = (struct dirent *) &buf[off];
+ dent->d_ino = INODE_NR(child);
+ dent->d_off = pos;
+ dent->d_reclen = len;
+ strcpy(dent->d_name, name);
+
+ off += len;
+
+ put_inode(child);
+ }
+
+ /* If there is anything left in our own buffer, copy that out now. */
+ if (off > 0) {
+ r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, user_off,
+ (vir_bytes) buf, off, D);
+
+ if (r != OK)
+ return r;
+
+ user_off += off;
+ }
+
+ m_out.RES_SEEK_POS_HI = 0;
+ m_out.RES_SEEK_POS_LO = pos;
+ m_out.RES_NBYTES = user_off;
+
+ return OK;
+}
--- /dev/null
+/* This file contains file metadata retrieval and manipulation routines.
+ *
+ * The entry points into this file are:
+ * get_mode return a file's mode
+ * do_stat perform the STAT file system call
+ * do_chmod perform the CHMOD file system call
+ * do_utime perform the UTIME file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+/*===========================================================================*
+ * get_mode *
+ *===========================================================================*/
+PUBLIC mode_t get_mode(ino, mode)
+struct inode *ino;
+int mode;
+{
+/* Return the mode for an inode, given the inode and the HGFS retrieved mode.
+ */
+
+ mode &= S_IRWXU;
+ mode = mode | (mode >> 3) | (mode >> 6);
+
+ if (IS_DIR(ino))
+ mode = S_IFDIR | (mode & opt.dir_mask);
+ else
+ mode = S_IFREG | (mode & opt.file_mask);
+
+ if (state.read_only)
+ mode &= ~0222;
+
+ return mode;
+}
+
+/*===========================================================================*
+ * do_stat *
+ *===========================================================================*/
+PUBLIC int do_stat()
+{
+/* Retrieve inode statistics.
+ */
+ struct inode *ino;
+ struct hgfs_attr attr;
+ struct stat stat;
+ char path[PATH_MAX];
+ ino_t ino_nr;
+ int r;
+
+ ino_nr = m_in.REQ_INODE_NR;
+
+ /* Don't increase the inode refcount: it's already open anyway */
+ if ((ino = find_inode(ino_nr)) == NIL_INODE)
+ return EINVAL;
+
+ attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE | HGFS_ATTR_ATIME |
+ HGFS_ATTR_MTIME | HGFS_ATTR_CTIME;
+
+ if ((r = verify_inode(ino, path, &attr)) != OK)
+ return r;
+
+ stat.st_dev = state.dev;
+ stat.st_ino = ino_nr;
+ stat.st_mode = get_mode(ino, attr.a_mode);
+ stat.st_uid = opt.uid;
+ stat.st_gid = opt.gid;
+ stat.st_rdev = NO_DEV;
+ stat.st_size = ex64hi(attr.a_size) ? ULONG_MAX : ex64lo(attr.a_size);
+ stat.st_atime = attr.a_atime;
+ stat.st_mtime = attr.a_mtime;
+ stat.st_ctime = attr.a_ctime;
+
+ /* We could make this more accurate by iterating over directory inodes'
+ * children, counting how many of those are directories as well.
+ * It's just not worth it.
+ */
+ stat.st_nlink = 0;
+ if (ino->i_parent != NIL_INODE) stat.st_nlink++;
+ if (IS_DIR(ino)) {
+ stat.st_nlink++;
+ if (HAS_CHILDREN(ino)) stat.st_nlink++;
+ }
+
+ return sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, 0,
+ (vir_bytes) &stat, sizeof(stat), D);
+}
+
+/*===========================================================================*
+ * do_chmod *
+ *===========================================================================*/
+PUBLIC int do_chmod()
+{
+/* Change file mode.
+ */
+ struct inode *ino;
+ char path[PATH_MAX];
+ struct hgfs_attr attr;
+ int r;
+
+ if (state.read_only)
+ return EROFS;
+
+ if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if ((r = verify_inode(ino, path, NULL)) != OK)
+ return r;
+
+ /* Set the new file mode. */
+ attr.a_mask = HGFS_ATTR_MODE;
+ attr.a_mode = m_in.REQ_MODE; /* no need to convert in this direction */
+
+ if ((r = hgfs_setattr(path, &attr)) != OK)
+ return r;
+
+ /* We have no idea what really happened. Query for the mode again. */
+ if ((r = verify_path(path, ino, &attr, NULL)) != OK)
+ return r;
+
+ m_out.RES_MODE = get_mode(ino, attr.a_mode);
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_utime *
+ *===========================================================================*/
+PUBLIC int do_utime()
+{
+/* Set file times.
+ */
+ struct inode *ino;
+ char path[PATH_MAX];
+ struct hgfs_attr attr;
+ int r;
+
+ if (state.read_only)
+ return EROFS;
+
+ if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if ((r = verify_inode(ino, path, NULL)) != OK)
+ return r;
+
+ attr.a_mask = HGFS_ATTR_ATIME | HGFS_ATTR_MTIME;
+ attr.a_atime = m_in.REQ_ACTIME;
+ attr.a_mtime = m_in.REQ_MODTIME;
+
+ return hgfs_setattr(path, &attr);
+}
--- /dev/null
+/* This file contains the file system call table.
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#define _TABLE
+#include "inc.h"
+
+PUBLIC _PROTOTYPE( int (*call_vec[]), (void) ) = {
+ no_sys, /* 0 */
+ no_sys, /* 1 getnode */
+ do_putnode, /* 2 putnode */
+ no_sys, /* 3 slink */
+ do_ftrunc, /* 4 ftrunc */
+ no_sys, /* 5 chown */
+ do_chmod, /* 6 chmod */
+ do_noop, /* 7 inhibread */
+ do_stat, /* 8 stat */
+ do_utime, /* 9 utime */
+ do_fstatfs, /* 10 fstatfs */
+ no_sys, /* 11 bread */
+ no_sys, /* 12 bwrite */
+ do_unlink, /* 13 unlink */
+ do_rmdir, /* 14 rmdir */
+ do_unmount, /* 15 unmount */
+ do_noop, /* 16 sync */
+ do_noop, /* 17 new_driver */
+ do_noop, /* 18 flush */
+ do_read, /* 19 read */
+ do_write, /* 20 write */
+ no_sys, /* 21 mknod */
+ do_mkdir, /* 22 mkdir */
+ do_create, /* 23 create */
+ no_sys, /* 24 link */
+ do_rename, /* 25 rename */
+ do_lookup, /* 26 lookup */
+ no_sys, /* 27 mountpoint */
+ do_readsuper, /* 28 readsuper */
+ no_sys, /* 29 newnode */
+ no_sys, /* 30 rdlink */
+ do_getdents, /* 31 getdents */
+};
+
+/* This should not fail with "array size is negative": */
+extern int dummy[sizeof(call_vec) == NREQS * sizeof(call_vec[0]) ? 1 : -1];
--- /dev/null
+
+/* Structure with global file system state. */
+struct state {
+ int mounted; /* is the file system mounted? */
+ int read_only; /* is the file system mounted read-only? note,
+ * has no relation to the shared folder mode */
+ dev_t dev; /* device the file system is mounted on */
+};
+
+/* Structure with options affecting global behavior. */
+struct opt {
+ char prefix[PATH_MAX]; /* prefix for all paths used */
+ uid_t uid; /* UID that owns all files */
+ gid_t gid; /* GID that owns all files */
+ unsigned int file_mask; /* AND-mask to apply to file permissions */
+ unsigned int dir_mask; /* AND-mask to apply to directory perm's */
+ int case_insens; /* case insensitivity flag; has no relation
+ * to the hosts's shared folder naming */
+};
--- /dev/null
+/* This file contains various utility functions.
+ *
+ * The entry points into this file are:
+ * get_name retrieve a path component string from VFS
+ * do_noop handle file system calls that do nothing and succeed
+ * no_sys handle file system calls that are not implemented
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+/*===========================================================================*
+ * get_name *
+ *===========================================================================*/
+PUBLIC int get_name(grant, len, name)
+cp_grant_id_t grant; /* memory grant for the path component */
+size_t len; /* length of the name, including '\0' */
+char name[NAME_MAX+1]; /* buffer in which store the result */
+{
+/* Retrieve a path component from the caller, using a given grant.
+ */
+ int r;
+
+ /* Copy in the name of the directory entry. */
+ if (len <= 1) return EINVAL;
+ if (len > NAME_MAX+1) return ENAMETOOLONG;
+
+ r = sys_safecopyfrom(m_in.m_source, grant, 0, (vir_bytes) name, len, D);
+
+ if (r != OK) return r;
+
+ if (name[len-1] != 0) {
+ printf("HGFS: VFS did not zero-terminate path component!\n");
+
+ return EINVAL;
+ }
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_noop *
+ *===========================================================================*/
+PUBLIC int do_noop()
+{
+/* Generic handler for no-op system calls.
+ */
+
+ return OK;
+}
+
+/*===========================================================================*
+ * no_sys *
+ *===========================================================================*/
+PUBLIC int no_sys()
+{
+/* Generic handler for unimplemented system calls.
+ */
+
+ return ENOSYS;
+}
--- /dev/null
+/* This file contains routines that verify inodes and paths against the host.
+ *
+ * The entry points into this file are:
+ * verify_path check whether a path,inode pair is still valid
+ * verify_inode construct a path for an inode and verify the inode
+ * verify_dentry check a directory inode and look for a directory entry
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+/*===========================================================================*
+ * verify_path *
+ *===========================================================================*/
+PUBLIC int verify_path(path, ino, attr, stale)
+char path[PATH_MAX];
+struct inode *ino;
+struct hgfs_attr *attr;
+int *stale;
+{
+/* Given a path, and the inode associated with that path, verify if the inode
+ * still matches the real world. Obtain the attributes of the file identified
+ * by the given path, and see if they match. If not, possibly mark the inode
+ * as deleted and return an error. Only upon success is the inode guaranteed
+ * to be usable.
+ *
+ * The caller must set the a_mask field of the passed attr struct.
+ * If 'stale' is not NULL, the value it points to must be initialized to 0,
+ * and will be set to 1 if the path was valid but the inode wasn't.
+ */
+ int r;
+
+ attr->a_mask |= HGFS_ATTR_MODE;
+
+ r = hgfs_getattr(path, attr);
+
+ dprintf(("HGFS: verify_path: getattr('%s') returned %d\n", path, r));
+
+ if (r != OK) {
+ /* If we are told that the path does not exist, delete the inode */
+ if (r == ENOENT || r == ENOTDIR)
+ del_dentry(ino);
+
+ return r; /* path isn't valid */
+ }
+
+ /* If the file type (reg, dir) isn't what we thought, delete the inode */
+ if ((ino->i_flags & I_DIR) != MODE_TO_DIRFLAG(attr->a_mode)) {
+ del_dentry(ino);
+
+ if (stale != NULL) *stale = 1;
+ return ENOENT; /* path is valid, inode wasn't */
+ }
+
+ return OK; /* path and inode are valid */
+}
+
+/*===========================================================================*
+ * verify_inode *
+ *===========================================================================*/
+PUBLIC int verify_inode(ino, path, attr)
+struct inode *ino; /* inode to verify */
+char path[PATH_MAX]; /* buffer in which to store the path */
+struct hgfs_attr *attr; /* buffer for attributes, or NULL */
+{
+/* Given an inode, construct a path identifying the inode, and check whether
+ * that path is still valid for that inode (as far as we can tell). As a side
+ * effect, store attributes in the given attribute structure if not NULL (its
+ * a_mask member must then be set).
+ */
+ struct hgfs_attr attr2;
+ int r;
+
+ if ((r = make_path(path, ino)) != OK) return r;
+
+ if (attr == NULL) {
+ attr2.a_mask = 0;
+
+ attr = &attr2;
+ }
+
+ return verify_path(path, ino, attr, NULL);
+}
+
+/*===========================================================================*
+ * verify_dentry *
+ *===========================================================================*/
+PUBLIC int verify_dentry(parent, name, path, res_ino)
+struct inode *parent; /* parent inode: the inode to verify */
+char name[NAME_MAX+1]; /* the given directory entry path component */
+char path[PATH_MAX]; /* buffer to store the resulting path in */
+struct inode **res_ino; /* pointer for addressed inode (or NULL) */
+{
+/* Given a directory inode and a name, construct a path identifying that
+ * directory entry, check whether the path to the parent is still valid, and
+ * check whether there is an inode pointed to by the full path. Upon success,
+ * res_ino will contain either the inode for the full path, with increased
+ * refcount, or NIL_INODE if no such inode exists.
+ */
+ int r;
+
+ if ((r = verify_inode(parent, path, NULL)) != OK)
+ return r;
+
+ dprintf(("HGFS: verify_dentry: given path is '%s', name '%s'\n", path,
+ name));
+
+ if ((r = push_path(path, name)) != OK)
+ return r;
+
+ dprintf(("HGFS: verify_dentry: path now '%s'\n", path));
+
+ *res_ino = lookup_dentry(parent, name);
+
+ return OK;
+}
--- /dev/null
+/* This file contains file writing system call handlers.
+ *
+ * The entry points into this file are:
+ * do_write perform the WRITE file system call
+ * do_ftrunc perform the FTRUNC file system call
+ *
+ * Created:
+ * April 2009 (D.C. van Moolenbroek)
+ */
+
+#include "inc.h"
+
+FORWARD _PROTOTYPE( int write_file, (struct inode *ino, u64_t *posp,
+ size_t *countp, cp_grant_id_t *grantp) );
+
+/*===========================================================================*
+ * write_file *
+ *===========================================================================*/
+PRIVATE int write_file(ino, posp, countp, grantp)
+struct inode *ino;
+u64_t *posp;
+size_t *countp;
+cp_grant_id_t *grantp;
+{
+/* Write data or zeroes to a file, depending on whether a valid pointer to
+ * a data grant was provided.
+ */
+ u64_t pos;
+ size_t count, size;
+ vir_bytes off;
+ char *ptr;
+ int r, chunk;
+
+ assert(!IS_DIR(ino));
+
+ if ((r = get_handle(ino)) != OK)
+ return r;
+
+ pos = *posp;
+ count = *countp;
+
+ assert(count > 0);
+
+ /* Use the buffer from libhgfs to eliminate extra copying. */
+ size = hgfs_writebuf(&ptr);
+ off = 0;
+
+ while (count > 0) {
+ chunk = MIN(count, size);
+
+ if (grantp != NULL) {
+ r = sys_safecopyfrom(m_in.m_source, *grantp,
+ off, (vir_bytes) ptr, chunk, D);
+
+ if (r != OK)
+ break;
+ } else {
+ /* Do this every time. We don't know what happens below. */
+ memset(ptr, 0, chunk);
+ }
+
+ if ((r = hgfs_write(ino->i_file, NULL, chunk, pos, FALSE)) <= 0)
+ break;
+
+ count -= r;
+ off += r;
+ pos = add64u(pos, r);
+ }
+
+ if (r < 0)
+ return r;
+
+ *posp = pos;
+ *countp = off;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_write *
+ *===========================================================================*/
+PUBLIC int do_write()
+{
+/* Write data to a file.
+ */
+ struct inode *ino;
+ u64_t pos;
+ size_t count;
+ cp_grant_id_t grant;
+ int r;
+
+ if (state.read_only)
+ return EROFS;
+
+ if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if (IS_DIR(ino)) return EISDIR;
+
+ pos = make64(m_in.REQ_SEEK_POS_LO, m_in.REQ_SEEK_POS_HI);
+ count = m_in.REQ_NBYTES;
+ grant = m_in.REQ_GRANT;
+
+ if (count <= 0) return EINVAL;
+
+ if ((r = write_file(ino, &pos, &count, &grant)) != OK)
+ return r;
+
+ m_out.RES_SEEK_POS_HI = ex64hi(pos);
+ m_out.RES_SEEK_POS_LO = ex64lo(pos);
+ m_out.RES_NBYTES = count;
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_ftrunc *
+ *===========================================================================*/
+PUBLIC int do_ftrunc()
+{
+/* Change file size or create file holes.
+ */
+ char path[PATH_MAX];
+ struct inode *ino;
+ struct hgfs_attr attr;
+ u64_t start, end, delta;
+ size_t count;
+ int r;
+
+ if (state.read_only)
+ return EROFS;
+
+ if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
+ return EINVAL;
+
+ if (IS_DIR(ino)) return EISDIR;
+
+ start = make64(m_in.REQ_TRC_START_LO, m_in.REQ_TRC_START_HI);
+ end = make64(m_in.REQ_TRC_END_LO, m_in.REQ_TRC_END_HI);
+
+ if (cmp64u(end, 0) == 0) {
+ /* Truncate or expand the file. */
+ if ((r = verify_inode(ino, path, NULL)) != OK)
+ return r;
+
+ attr.a_mask = HGFS_ATTR_SIZE;
+ attr.a_size = start;
+
+ r = hgfs_setattr(path, &attr);
+ } else {
+ /* Write zeroes to the file. We can't create holes. */
+ if (cmp64(end, start) <= 0) return EINVAL;
+
+ delta = sub64(end, start);
+
+ if (ex64hi(delta) != 0) return EINVAL;
+
+ count = ex64lo(delta);
+
+ r = write_file(ino, &start, &count, NULL);
+ }
+
+ return r;
+}