]> Zhao Yanbai Git Server - minix.git/commitdiff
HGFS - VMware Shared Folders file system server
authorDavid van Moolenbroek <david@minix3.org>
Mon, 25 Jan 2010 23:18:02 +0000 (23:18 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Mon, 25 Jan 2010 23:18:02 +0000 (23:18 +0000)
46 files changed:
etc/system.conf
man/man8/hgfs.8 [new file with mode: 0644]
servers/Makefile
servers/hgfs/Makefile [new file with mode: 0644]
servers/hgfs/const.h [new file with mode: 0644]
servers/hgfs/dentry.c [new file with mode: 0644]
servers/hgfs/glo.h [new file with mode: 0644]
servers/hgfs/handle.c [new file with mode: 0644]
servers/hgfs/inc.h [new file with mode: 0644]
servers/hgfs/inode.c [new file with mode: 0644]
servers/hgfs/inode.h [new file with mode: 0644]
servers/hgfs/libhgfs/Makefile [new file with mode: 0644]
servers/hgfs/libhgfs/attr.c [new file with mode: 0644]
servers/hgfs/libhgfs/backdoor.S [new file with mode: 0644]
servers/hgfs/libhgfs/channel.c [new file with mode: 0644]
servers/hgfs/libhgfs/const.h [new file with mode: 0644]
servers/hgfs/libhgfs/dir.c [new file with mode: 0644]
servers/hgfs/libhgfs/error.c [new file with mode: 0644]
servers/hgfs/libhgfs/file.c [new file with mode: 0644]
servers/hgfs/libhgfs/glo.h [new file with mode: 0644]
servers/hgfs/libhgfs/hgfs.h [new file with mode: 0644]
servers/hgfs/libhgfs/inc.h [new file with mode: 0644]
servers/hgfs/libhgfs/link.c [new file with mode: 0644]
servers/hgfs/libhgfs/misc.c [new file with mode: 0644]
servers/hgfs/libhgfs/path.c [new file with mode: 0644]
servers/hgfs/libhgfs/proto.h [new file with mode: 0644]
servers/hgfs/libhgfs/rpc.c [new file with mode: 0644]
servers/hgfs/libhgfs/time.c [new file with mode: 0644]
servers/hgfs/libhgfs/type.h [new file with mode: 0644]
servers/hgfs/link.c [new file with mode: 0644]
servers/hgfs/lookup.c [new file with mode: 0644]
servers/hgfs/main.c [new file with mode: 0644]
servers/hgfs/misc.c [new file with mode: 0644]
servers/hgfs/mount.c [new file with mode: 0644]
servers/hgfs/name.c [new file with mode: 0644]
servers/hgfs/optset.c [new file with mode: 0644]
servers/hgfs/optset.h [new file with mode: 0644]
servers/hgfs/path.c [new file with mode: 0644]
servers/hgfs/proto.h [new file with mode: 0644]
servers/hgfs/read.c [new file with mode: 0644]
servers/hgfs/stat.c [new file with mode: 0644]
servers/hgfs/table.c [new file with mode: 0644]
servers/hgfs/type.h [new file with mode: 0644]
servers/hgfs/util.c [new file with mode: 0644]
servers/hgfs/verify.c [new file with mode: 0644]
servers/hgfs/write.c [new file with mode: 0644]

index a8567c11b5dfd496e8b02ccde286c9afac935456..5abdc954cfa1bcfd2b63b586eaba59848ba9b4bd 100644 (file)
@@ -302,6 +302,22 @@ service isofs
        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
diff --git a/man/man8/hgfs.8 b/man/man8/hgfs.8
new file mode 100644 (file)
index 0000000..59cbbe0
--- /dev/null
@@ -0,0 +1,76 @@
+.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>
index 2b5a979b80b9265fdf91e2da8c7021aadb1776c8..3da04d36c6ae15569c1f534e7e7aea8dbc74330e 100644 (file)
@@ -20,6 +20,7 @@ all install depend clean:
        cd ./mfs && $(MAKE) $@
        cd ./pfs && $(MAKE) $@
        cd ./iso9660fs && $(MAKE) $@
+       cd ./hgfs && $(MAKE) $@
        cd ./rs && $(MAKE) $@
        cd ./ds && $(MAKE) $@
        cd ./is && $(MAKE) $@
diff --git a/servers/hgfs/Makefile b/servers/hgfs/Makefile
new file mode 100644 (file)
index 0000000..109fdf6
--- /dev/null
@@ -0,0 +1,34 @@
+# 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
diff --git a/servers/hgfs/const.h b/servers/hgfs/const.h
new file mode 100644 (file)
index 0000000..9d0e767
--- /dev/null
@@ -0,0 +1,13 @@
+
+/* 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
diff --git a/servers/hgfs/dentry.c b/servers/hgfs/dentry.c
new file mode 100644 (file)
index 0000000..6926498
--- /dev/null
@@ -0,0 +1,194 @@
+/* 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;
+}
diff --git a/servers/hgfs/glo.h b/servers/hgfs/glo.h
new file mode 100644 (file)
index 0000000..23aa094
--- /dev/null
@@ -0,0 +1,12 @@
+
+#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) );
diff --git a/servers/hgfs/handle.c b/servers/hgfs/handle.c
new file mode 100644 (file)
index 0000000..41115d3
--- /dev/null
@@ -0,0 +1,78 @@
+/* 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;
+}
diff --git a/servers/hgfs/inc.h b/servers/hgfs/inc.h
new file mode 100644 (file)
index 0000000..01b77c6
--- /dev/null
@@ -0,0 +1,37 @@
+
+#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"
diff --git a/servers/hgfs/inode.c b/servers/hgfs/inode.c
new file mode 100644 (file)
index 0000000..ee3b0ed
--- /dev/null
@@ -0,0 +1,299 @@
+/* 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;
+}
diff --git a/servers/hgfs/inode.h b/servers/hgfs/inode.h
new file mode 100644 (file)
index 0000000..37c0978
--- /dev/null
@@ -0,0 +1,93 @@
+#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 */
diff --git a/servers/hgfs/libhgfs/Makefile b/servers/hgfs/libhgfs/Makefile
new file mode 100644 (file)
index 0000000..302d767
--- /dev/null
@@ -0,0 +1,26 @@
+# 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
diff --git a/servers/hgfs/libhgfs/attr.c b/servers/hgfs/libhgfs/attr.c
new file mode 100644 (file)
index 0000000..12fe0e7
--- /dev/null
@@ -0,0 +1,86 @@
+/* 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();
+}
diff --git a/servers/hgfs/libhgfs/backdoor.S b/servers/hgfs/libhgfs/backdoor.S
new file mode 100644 (file)
index 0000000..870419d
--- /dev/null
@@ -0,0 +1,106 @@
+# 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
diff --git a/servers/hgfs/libhgfs/channel.c b/servers/hgfs/libhgfs/channel.c
new file mode 100644 (file)
index 0000000..8ed97f4
--- /dev/null
@@ -0,0 +1,146 @@
+/* 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;
+}
diff --git a/servers/hgfs/libhgfs/const.h b/servers/hgfs/libhgfs/const.h
new file mode 100644 (file)
index 0000000..7e47724
--- /dev/null
@@ -0,0 +1,62 @@
+/* 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)
diff --git a/servers/hgfs/libhgfs/dir.c b/servers/hgfs/libhgfs/dir.c
new file mode 100644 (file)
index 0000000..1f0081b
--- /dev/null
@@ -0,0 +1,71 @@
+/* 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();
+}
diff --git a/servers/hgfs/libhgfs/error.c b/servers/hgfs/libhgfs/error.c
new file mode 100644 (file)
index 0000000..af38fd9
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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];
+}
diff --git a/servers/hgfs/libhgfs/file.c b/servers/hgfs/libhgfs/file.c
new file mode 100644 (file)
index 0000000..24bdbd1
--- /dev/null
@@ -0,0 +1,182 @@
+/* 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;
+}
diff --git a/servers/hgfs/libhgfs/glo.h b/servers/hgfs/libhgfs/glo.h
new file mode 100644 (file)
index 0000000..aa1ae7f
--- /dev/null
@@ -0,0 +1,6 @@
+/* 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;
diff --git a/servers/hgfs/libhgfs/hgfs.h b/servers/hgfs/libhgfs/hgfs.h
new file mode 100644 (file)
index 0000000..1d5a0dd
--- /dev/null
@@ -0,0 +1,63 @@
+/* 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 */
diff --git a/servers/hgfs/libhgfs/inc.h b/servers/hgfs/libhgfs/inc.h
new file mode 100644 (file)
index 0000000..5471cd5
--- /dev/null
@@ -0,0 +1,19 @@
+/* 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"
diff --git a/servers/hgfs/libhgfs/link.c b/servers/hgfs/libhgfs/link.c
new file mode 100644 (file)
index 0000000..274dbb4
--- /dev/null
@@ -0,0 +1,73 @@
+/* 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();
+}
diff --git a/servers/hgfs/libhgfs/misc.c b/servers/hgfs/libhgfs/misc.c
new file mode 100644 (file)
index 0000000..1acda1a
--- /dev/null
@@ -0,0 +1,73 @@
+/* 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;
+}
diff --git a/servers/hgfs/libhgfs/path.c b/servers/hgfs/libhgfs/path.c
new file mode 100644 (file)
index 0000000..6a2a2a1
--- /dev/null
@@ -0,0 +1,73 @@
+/* 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;
+}
diff --git a/servers/hgfs/libhgfs/proto.h b/servers/hgfs/libhgfs/proto.h
new file mode 100644 (file)
index 0000000..513a935
--- /dev/null
@@ -0,0 +1,51 @@
+/* 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)                             );
diff --git a/servers/hgfs/libhgfs/rpc.c b/servers/hgfs/libhgfs/rpc.c
new file mode 100644 (file)
index 0000000..3345ea3
--- /dev/null
@@ -0,0 +1,94 @@
+/* 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);
+}
diff --git a/servers/hgfs/libhgfs/time.c b/servers/hgfs/libhgfs/time.c
new file mode 100644 (file)
index 0000000..5daab92
--- /dev/null
@@ -0,0 +1,69 @@
+/* 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);
+}
diff --git a/servers/hgfs/libhgfs/type.h b/servers/hgfs/libhgfs/type.h
new file mode 100644 (file)
index 0000000..a8ade07
--- /dev/null
@@ -0,0 +1,7 @@
+/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
+
+struct channel {
+  u16_t id;
+  u32_t cookie1;
+  u32_t cookie2;
+};
diff --git a/servers/hgfs/link.c b/servers/hgfs/link.c
new file mode 100644 (file)
index 0000000..0d3e468
--- /dev/null
@@ -0,0 +1,403 @@
+/* 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;
+}
diff --git a/servers/hgfs/lookup.c b/servers/hgfs/lookup.c
new file mode 100644 (file)
index 0000000..8de9b73
--- /dev/null
@@ -0,0 +1,333 @@
+/* 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;
+}
diff --git a/servers/hgfs/main.c b/servers/hgfs/main.c
new file mode 100644 (file)
index 0000000..9f0924a
--- /dev/null
@@ -0,0 +1,232 @@
+/* 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;
+}
diff --git a/servers/hgfs/misc.c b/servers/hgfs/misc.c
new file mode 100644 (file)
index 0000000..b7c4c60
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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);
+}
diff --git a/servers/hgfs/mount.c b/servers/hgfs/mount.c
new file mode 100644 (file)
index 0000000..9cc3a08
--- /dev/null
@@ -0,0 +1,94 @@
+/* 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;
+}
diff --git a/servers/hgfs/name.c b/servers/hgfs/name.c
new file mode 100644 (file)
index 0000000..39aa540
--- /dev/null
@@ -0,0 +1,56 @@
+/* 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;
+}
diff --git a/servers/hgfs/optset.c b/servers/hgfs/optset.c
new file mode 100644 (file)
index 0000000..a338a47
--- /dev/null
@@ -0,0 +1,128 @@
+/* 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;
+               }
+       }
+  }
+}
diff --git a/servers/hgfs/optset.h b/servers/hgfs/optset.h
new file mode 100644 (file)
index 0000000..87ea4ce
--- /dev/null
@@ -0,0 +1,30 @@
+#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 */
diff --git a/servers/hgfs/path.c b/servers/hgfs/path.c
new file mode 100644 (file)
index 0000000..654b651
--- /dev/null
@@ -0,0 +1,112 @@
+/* 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;
+}
diff --git a/servers/hgfs/proto.h b/servers/hgfs/proto.h
new file mode 100644 (file)
index 0000000..93575c9
--- /dev/null
@@ -0,0 +1,82 @@
+
+/* 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)                                      );
diff --git a/servers/hgfs/read.c b/servers/hgfs/read.c
new file mode 100644 (file)
index 0000000..5a80209
--- /dev/null
@@ -0,0 +1,239 @@
+/* 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;
+}
diff --git a/servers/hgfs/stat.c b/servers/hgfs/stat.c
new file mode 100644 (file)
index 0000000..92f0910
--- /dev/null
@@ -0,0 +1,154 @@
+/* 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);
+}
diff --git a/servers/hgfs/table.c b/servers/hgfs/table.c
new file mode 100644 (file)
index 0000000..17f0c3f
--- /dev/null
@@ -0,0 +1,46 @@
+/* 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];
diff --git a/servers/hgfs/type.h b/servers/hgfs/type.h
new file mode 100644 (file)
index 0000000..590ec1f
--- /dev/null
@@ -0,0 +1,19 @@
+
+/* 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 */
+};
diff --git a/servers/hgfs/util.c b/servers/hgfs/util.c
new file mode 100644 (file)
index 0000000..af463c3
--- /dev/null
@@ -0,0 +1,63 @@
+/* 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;
+}
diff --git a/servers/hgfs/verify.c b/servers/hgfs/verify.c
new file mode 100644 (file)
index 0000000..681bd1b
--- /dev/null
@@ -0,0 +1,118 @@
+/* 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;
+}
diff --git a/servers/hgfs/write.c b/servers/hgfs/write.c
new file mode 100644 (file)
index 0000000..5e3088d
--- /dev/null
@@ -0,0 +1,164 @@
+/* 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;
+}