[vbox-dev] [PATCH] fixes and improvements to solaris (guest) shared folders

Life is hard, and then you die ronald at innovation.ch
Mon Jun 21 15:35:43 PDT 2010


I've worked on some fixes and related improvements for a couple issues
with the shared-folders in Solaris guests, specifically
http://www.virtualbox.org/ticket/6512 and
http://www.virtualbox.org/ticket/4154 . Attached are 10 patches, with
the commit message/explanation at the top of each (you'll also find the
first 7 patches attached to the above tickets). I'm releasing into the
public domain (or if you prefer, under the MIT license).

Patches 07 - 09 are performance improvements for directory listing -
together they speed up the time to list a directory with 5000 files by
between 3 and 10 times (depending on what options are given to 'ls').
The other patches are all bug fixes or enhancements; patch 03
specifically fixes #6512 and patch 06 fixes #4154. All patches are
against svn rev 30317, but to apply cleanly they need to applied in the
numbered order.

These patches have been tested on Solaris 10u8 and OpenSolaris 2009.06
using VirtualBox 3.2.2 .

As an aside, I've tried to figure out what the prevailing coding style
is and follow it, but especially when it comes to tabs/spaces and
indentation it wasn't always clear; it would be helpful if there were a
basic style guide on the wiki or something (and if the code consistently
followed it).

It would be wondeful if these patches could be incorporated; if
there's anything I can do to help with that (do you prefer the patches
formatted differently?), please let me know.


  Cheers,

  Ronald

-------------- next part --------------

Add missing parameters to panic() calls.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 2b3585a..7b18dca 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -242,7 +242,7 @@ top:
 	ASSERT(node->sf_path != NULL);
     LogFlowFunc(("sffs_destroy(%s)%s\n", node->sf_path, node->sf_is_stale ? " stale": ""));
 	if (node->sf_children != 0)
-		panic("sfnode_destroy(%s) has %d children", node->sf_children);
+		panic("sfnode_destroy(%s) has %d children", node->sf_path, node->sf_children);
 	if (node->sf_vnode != NULL)
 		panic("sfnode_destroy(%s) has active vnode", node->sf_path);
 
@@ -259,7 +259,7 @@ top:
 	kmem_free(node, sizeof (*node));
 	if (parent != NULL) {
 		if (parent->sf_children == 0)
-			panic("sfnode_destroy(%s) parent has no child");
+			panic("sfnode_destroy(%s) parent has no child", node->sf_path);
 		--parent->sf_children;
 		if (parent->sf_children == 0 &&
 		    parent->sf_is_stale &&
-------------- next part --------------

Fix/improve getattr. This adds missing gid and txt mode bits, and makes
getattr report the file type properly.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index 473ecb1..b46ec7a 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -420,7 +420,7 @@ sfprov_get_mode(sfp_mount_t *mnt, char *path, mode_t *mode)
 	else if (RTFS_IS_FILE(info.Attr.fMode))
 		m |= S_IFREG;
 	else if (RTFS_IS_FIFO(info.Attr.fMode))
-		m |= S_IFDIR;
+		m |= S_IFIFO;
 	else if (RTFS_IS_DEV_CHAR(info.Attr.fMode))
 		m |= S_IFCHR;
 	else if (RTFS_IS_DEV_BLOCK(info.Attr.fMode))
@@ -450,6 +450,10 @@ sfprov_get_mode(sfp_mount_t *mnt, char *path, mode_t *mode)
 		m |= S_IXOTH;
 	if (info.Attr.fMode & RTFS_UNIX_ISUID)
 		m |= S_ISUID;
+	if (info.Attr.fMode & RTFS_UNIX_ISGID)
+		m |= S_ISGID;
+	if (info.Attr.fMode & RTFS_UNIX_ISTXT)
+		m |= S_ISVTX;
 	*mode = m;
 	return (0);
 }
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 7b18dca..66a7744 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -726,6 +726,20 @@ sffs_getattr(
 	if (error != 0)
 		goto done;
 	vap->va_mode = mode & MODEMASK;
+	if (S_ISDIR(mode))
+		vap->va_type = VDIR;
+	else if (S_ISREG(mode))
+		vap->va_type = VREG;
+	else if (S_ISFIFO(mode))
+		vap->va_type = VFIFO;
+	else if (S_ISCHR(mode))
+		vap->va_type = VCHR;
+	else if (S_ISBLK(mode))
+		vap->va_type = VBLK;
+	else if (S_ISLNK(mode))
+		vap->va_type = VLNK;
+	else if (S_ISSOCK(mode))
+		vap->va_type = VSOCK;
 
 	error = sfprov_get_size(node->sf_sffs->sf_handle, node->sf_path, &x);
 	if (error == ENOENT)
-------------- next part --------------

Added support for setting file attributes (times and mode) and file-length.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index b46ec7a..c3d14f7 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -519,6 +519,175 @@ sfprov_get_ctime(sfp_mount_t *mnt, char *path, timestruc_t *time)
 	return (0);
 }
 
+static void
+sfprov_timespec_from_ftime(RTTIMESPEC *ts, timestruc_t time)
+{
+	uint64_t nanosec = 1000000000 * time.tv_sec + time.tv_nsec;
+	RTTimeSpecSetNano(ts, nanosec);
+}
+
+int
+sfprov_set_attr(
+	sfp_mount_t *mnt,
+	char *path,
+	uint_t mask,
+	mode_t mode,
+	timestruc_t atime,
+	timestruc_t mtime,
+	timestruc_t ctime)
+{
+	int rc, err;
+	SHFLCREATEPARMS parms;
+	SHFLSTRING *str;
+	RTFSOBJINFO info;
+	uint32_t bytes;
+	int str_size;
+
+	str = sfprov_string(path, &str_size);
+	parms.Handle = 0;
+	parms.Info.cbObject = 0;
+	parms.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
+			  | SHFL_CF_ACT_FAIL_IF_NEW
+			  | SHFL_CF_ACCESS_ATTR_WRITE;
+
+	rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
+
+	if (RT_FAILURE(rc)) {
+		cmn_err(CE_WARN, "sfprov_set_attr: vboxCallCreate(%s) failed rc=%d",
+			path, rc);
+		err = EINVAL;
+		goto fail2;
+	}
+	if (parms.Result != SHFL_FILE_EXISTS) {
+		err = ENOENT;
+		goto fail1;
+	}
+
+	RT_ZERO(info);
+	if (mask & AT_MODE)
+	{
+#define mode_set(r) ((mode & (S_##r)) ? RTFS_UNIX_##r : 0)
+
+		info.Attr.fMode  = mode_set (ISUID);
+		info.Attr.fMode |= mode_set (ISGID);
+		info.Attr.fMode |= (mode & S_ISVTX) ? RTFS_UNIX_ISTXT : 0;
+		info.Attr.fMode |= mode_set (IRUSR);
+		info.Attr.fMode |= mode_set (IWUSR);
+		info.Attr.fMode |= mode_set (IXUSR);
+		info.Attr.fMode |= mode_set (IRGRP);
+		info.Attr.fMode |= mode_set (IWGRP);
+		info.Attr.fMode |= mode_set (IXGRP);
+		info.Attr.fMode |= mode_set (IROTH);
+		info.Attr.fMode |= mode_set (IWOTH);
+		info.Attr.fMode |= mode_set (IXOTH);
+
+		if (S_ISDIR(mode))
+			info.Attr.fMode |= RTFS_TYPE_DIRECTORY;
+		else if (S_ISREG(mode))
+			info.Attr.fMode |= RTFS_TYPE_FILE;
+		else if (S_ISFIFO(mode))
+			info.Attr.fMode |= RTFS_TYPE_FIFO;
+		else if (S_ISCHR(mode))
+			info.Attr.fMode |= RTFS_TYPE_DEV_CHAR;
+		else if (S_ISBLK(mode))
+			info.Attr.fMode |= RTFS_TYPE_DEV_BLOCK;
+		else if (S_ISLNK(mode))
+			info.Attr.fMode |= RTFS_TYPE_SYMLINK;
+		else if (S_ISSOCK(mode))
+			info.Attr.fMode |= RTFS_TYPE_SOCKET;
+		else
+			info.Attr.fMode |= RTFS_TYPE_FILE;
+	}
+
+	if (mask & AT_ATIME)
+		sfprov_timespec_from_ftime(&info.AccessTime, atime);
+	if (mask & AT_MTIME)
+		sfprov_timespec_from_ftime(&info.ModificationTime, mtime);
+	if (mask & AT_CTIME)
+		sfprov_timespec_from_ftime(&info.ChangeTime, ctime);
+
+	bytes = sizeof(info);
+	rc = vboxCallFSInfo(&vbox_client, &mnt->map, parms.Handle,
+			    (SHFL_INFO_SET | SHFL_INFO_FILE), &bytes,
+			    (SHFLDIRINFO *)&info);
+	if (RT_FAILURE (rc)) {
+		cmn_err(CE_WARN, "sfprov_set_attr: vboxCallFSInfo(%s, FILE) failed rc=%d",
+			path, rc);
+		err = RTErrConvertToErrno(rc);
+		goto fail1;
+	}
+
+	err = 0;
+
+fail1:
+	rc = vboxCallClose(&vbox_client, &mnt->map, parms.Handle);
+	if (RT_FAILURE (rc))
+	{
+		cmn_err(CE_WARN, "sfprov_set_attr: vboxCallClose(%s) failed rc=%d",
+			path, rc);
+	}
+fail2:
+	kmem_free(str, str_size);
+	return err;
+}
+
+int
+sfprov_set_size(sfp_mount_t *mnt, char *path, uint64_t size)
+{
+	int rc, err;
+	SHFLCREATEPARMS parms;
+	SHFLSTRING *str;
+	RTFSOBJINFO info;
+	uint32_t bytes;
+	int str_size;
+
+	str = sfprov_string(path, &str_size);
+	parms.Handle = 0;
+	parms.Info.cbObject = 0;
+	parms.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
+			  | SHFL_CF_ACT_FAIL_IF_NEW
+			  | SHFL_CF_ACCESS_WRITE;
+
+	rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
+
+	if (RT_FAILURE(rc)) {
+		cmn_err(CE_WARN, "sfprov_set_size: vboxCallCreate(%s) failed rc=%d",
+			path, rc);
+		err = EINVAL;
+		goto fail2;
+	}
+	if (parms.Result != SHFL_FILE_EXISTS) {
+		err = ENOENT;
+		goto fail1;
+	}
+
+	RT_ZERO(info);
+	info.cbObject = size;
+	bytes = sizeof(info);
+	rc = vboxCallFSInfo(&vbox_client, &mnt->map, parms.Handle,
+			    (SHFL_INFO_SET | SHFL_INFO_SIZE), &bytes,
+			    (SHFLDIRINFO *)&info);
+	if (RT_FAILURE (rc)) {
+		cmn_err(CE_WARN, "sfprov_set_size: vboxCallFSInfo(%s, SIZE) failed rc=%d",
+			path, rc);
+		err = RTErrConvertToErrno(rc);
+		goto fail1;
+	}
+
+	err = 0;
+
+fail1:
+	rc = vboxCallClose(&vbox_client, &mnt->map, parms.Handle);
+	if (RT_FAILURE (rc))
+	{
+		cmn_err(CE_WARN, "sfprov_set_size: vboxCallClose(%s) failed rc=%d",
+			path, rc);
+	}
+fail2:
+	kmem_free(str, str_size);
+	return err;
+}
+
 /*
  * Directory operations
  */
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
index 435a2b5..8693ad1 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -85,13 +85,16 @@ extern int sfprov_write(sfp_file_t *, char * buffer, uint64_t offset,
 
 
 /*
- * get information about a file (or directory) using pathname
+ * get/set information about a file (or directory) using pathname
  */
 extern int sfprov_get_mode(sfp_mount_t *, char *, mode_t *);
 extern int sfprov_get_size(sfp_mount_t *, char *, uint64_t *);
 extern int sfprov_get_atime(sfp_mount_t *, char *, timestruc_t *);
 extern int sfprov_get_mtime(sfp_mount_t *, char *, timestruc_t *);
 extern int sfprov_get_ctime(sfp_mount_t *, char *, timestruc_t *);
+extern int sfprov_set_attr(sfp_mount_t *, char *, uint_t, mode_t,
+			   timestruc_t, timestruc_t, timestruc_t);
+extern int sfprov_set_size(sfp_mount_t *, char *, uint64_t);
 
 
 /*
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 66a7744..9a10b4e 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -58,6 +58,7 @@
 
 #include <VBox/log.h>
 
+#include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mntent.h>
@@ -779,6 +780,74 @@ done:
 	return (error);
 }
 
+static int
+sffs_setattr(
+	vnode_t		*vp,
+	vattr_t		*vap,
+	int		flags,
+	cred_t		*cred,
+	caller_context_t *ct)
+{
+	sfnode_t	*node = VN2SFN(vp);
+	int		error;
+	mode_t		mode;
+
+	mode = vap->va_mode;
+	if (vp->v_type == VREG)
+		mode |= S_IFREG;
+	else if (vp->v_type == VDIR)
+		mode |= S_IFDIR;
+	else if (vp->v_type == VBLK)
+		mode |= S_IFBLK;
+	else if (vp->v_type == VCHR)
+		mode |= S_IFCHR;
+	else if (vp->v_type == VLNK)
+		mode |= S_IFLNK;
+	else if (vp->v_type == VFIFO)
+		mode |= S_IFIFO;
+	else if (vp->v_type == VSOCK)
+		mode |= S_IFSOCK;
+
+	mutex_enter(&sffs_lock);
+
+	error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
+				vap->va_mask, mode,
+				vap->va_atime, vap->va_mtime, vap->va_ctime);
+	if (error == ENOENT)
+		sfnode_make_stale(node);
+
+	mutex_exit(&sffs_lock);
+	return (error);
+}
+
+static int
+sffs_space(
+	vnode_t		*vp,
+	int		cmd,
+	struct flock64	*bfp,
+	int		flags,
+	offset_t	off,
+	cred_t		*cred,
+	caller_context_t *ct)
+{
+	sfnode_t	*node = VN2SFN(vp);
+	int		error;
+
+	/* we only support changing the length of the file */
+	if (bfp->l_whence != SEEK_SET || bfp->l_len != 0)
+	    return ENOSYS;
+
+	mutex_enter(&sffs_lock);
+
+	error = sfprov_set_size(node->sf_sffs->sf_handle, node->sf_path,
+				bfp->l_start);
+	if (error == ENOENT)
+		sfnode_make_stale(node);
+
+	mutex_exit(&sffs_lock);
+	return (error);
+}
+
 /*ARGSUSED*/
 static int
 sffs_read(
@@ -1511,6 +1580,8 @@ const fs_operation_def_t sffs_ops_template[] = {
 	VOPNAME_RENAME,		sffs_rename,
 	VOPNAME_RMDIR,		sffs_rmdir,
 	VOPNAME_SEEK,		sffs_seek,
+	VOPNAME_SETATTR,	sffs_setattr,
+	VOPNAME_SPACE,		sffs_space,
 	VOPNAME_WRITE,		sffs_write,
 	NULL,			NULL
 #else
@@ -1531,6 +1602,8 @@ const fs_operation_def_t sffs_ops_template[] = {
 	VOPNAME_RENAME,		{ .vop_rename = sffs_rename },
 	VOPNAME_RMDIR,		{ .vop_rmdir = sffs_rmdir },
 	VOPNAME_SEEK,		{ .vop_seek = sffs_seek },
+	VOPNAME_SETATTR,	{ .vop_setattr = sffs_setattr },
+	VOPNAME_SPACE,		{ .vop_space = sffs_space },
 	VOPNAME_WRITE,		{ .vop_write = sffs_write },
 	NULL,			NULL
 #endif
-------------- next part --------------

Honor the requested file mode when creating files and directories.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 9a10b4e..00fb370 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -1179,6 +1179,16 @@ sffs_create(
 	 * Doesn't exist yet and we have the lock, so create it.
 	 */
 	node = sfnode_lookup(VN2SFN(dvp), name, VREG);
+	if (node && (vap->va_mask & AT_MODE)) {
+		timestruc_t dummy;
+		error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
+					AT_MODE, vap->va_mode, dummy, dummy,
+					dummy);
+		if (error)
+			cmn_err(CE_WARN, "sffs_create: set_mode(%s, %o) failed"
+				" rc=%d", node->sf_path, vap->va_mode, error);
+	}
+
 	mutex_exit(&sffs_lock);
 	if (node == NULL)
 		return (EINVAL);
@@ -1232,6 +1242,16 @@ sffs_mkdir(
 	}
 
 	node = sfnode_lookup(VN2SFN(dvp), nm, VDIR);
+	if (node && (va->va_mask & AT_MODE)) {
+		timestruc_t dummy;
+		error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
+					AT_MODE, va->va_mode, dummy, dummy,
+					dummy);
+		if (error)
+			cmn_err(CE_WARN, "sffs_mkdir: set_mode(%s, %o) failed"
+				" rc=%d", node->sf_path, va->va_mode, error);
+	}
+
 	mutex_exit(&sffs_lock);
 	if (node == NULL)
 		return (EACCES);
-------------- next part --------------

Use correct declaration on Solaris 10 for fop_inactive function.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 00fb370..aad0078 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -1480,7 +1480,11 @@ sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
  */
 /*ARGSUSED*/
 static void
+#if defined(VBOX_VFS_SOLARIS_10U6)
+sffs_inactive(vnode_t *vp, cred_t *cr)
+#else
 sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+#endif
 {
 	sfnode_t *node;
 
-------------- next part --------------

Fix out-of-space errors when listing large directories.

The existing code would fail if the full directory listing could not fit
in the buffer passed to the first readdir call. The change here implements
the same idea as the linux guest code does: on the first readdir call the
full directory list is read into a dynamically allocated buffer (actually
a list of buffers), and the readdir calls then copy from this buffer; the
buffer is freed when the vnode is closed. This buffer contains dirent_t
entries, rather than some slightly more compact list-of-names, because
readdir provides the offset as a byte offset in a conceptual array of
dirent_t's, and hence we can find our place much easier and faster if we
keep actual dirent_t entries around.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index c3d14f7..e393ff8 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -28,6 +28,7 @@
 #include <sys/sysmacros.h>
 #include <sys/ddi.h>
 #include <sys/sunddi.h>
+#include <sys/dirent.h>
 #include "vboxfs_prov.h"
 #ifdef u
 #undef u
@@ -779,23 +780,15 @@ sfprov_rename(sfp_mount_t *mnt, char *from, char *to, uint_t is_dir)
  * - ENOENT - Couldn't open the directory for reading
  * - EINVAL - Internal error of some kind
  *
- * On successful return, buffer[0] is the start of an array of "char *"
- * pointers to the filenames. The array ends with a NULL pointer.
- * The remaining storage in buffer after that NULL pointer is where the
- * filename strings actually are.
- *
- * On input nents is the max number of filenames the requestor can handle.
- * On output nents is the number of entries at buff[0]
- *
- * The caller is responsible for freeing the returned buffer.
+ * On successful return, *dirents points to a list of sffs_dirents_t;
+ * for each dirent, all fields except the d_ino will be set appropriately.
+ * The caller is responsible for freeing the dirents buffer.
  */
 int
 sfprov_readdir(
 	sfp_mount_t *mnt,
 	char *path,
-	void **buffer,
-	size_t *buffersize,
-	uint32_t *nents)
+	sffs_dirents_t **dirents)
 {
 	int error;
 	char *cp;
@@ -803,45 +796,49 @@ sfprov_readdir(
 	SHFLSTRING *mask_str = NULL;	/* must be path with "/*" appended */
 	int mask_size;
 	sfp_file_t *fp;
-	void *buff_start = NULL;
-	size_t buff_size;
 	static char infobuff[2 * MAXNAMELEN];	/* not on stack!! */
 	SHFLDIRINFO *info = (SHFLDIRINFO *)&infobuff;
-	uint32_t numbytes = sizeof (infobuff);
+	uint32_t numbytes;
 	uint32_t justone;
 	uint32_t cnt;
-	char **name_ptrs;
+	sffs_dirents_t *cur_buf;
+	struct dirent64 *dirent;
+	unsigned short reclen;
+
+	*dirents = NULL;
 
-	*buffer = NULL;
-	*buffersize = 0;
-	if (*nents == 0)
-		return (EINVAL);
 	error = sfprov_open(mnt, path, &fp);
 	if (error != 0)
 		return (ENOENT);
 
 	/*
+	 * Allocate the first dirents buffer.
+	 */
+	*dirents = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
+	if (*dirents == NULL) {
+		error = (ENOSPC);
+		goto done;
+	}
+	cur_buf = *dirents;
+	cur_buf->sf_next = NULL;
+	cur_buf->sf_len = 0;
+
+	/*
 	 * Create mask that VBox expects. This needs to be the directory path,
 	 * plus a "*" wildcard to get all files.
 	 */
 	len = strlen(path) + 3;
 	cp = kmem_alloc(len, KM_SLEEP);
+	if (cp == NULL) {
+		error = (ENOSPC);
+		goto done;
+	}
 	strcpy(cp, path);
 	strcat(cp, "/*");
 	mask_str = sfprov_string(cp, &mask_size);
 	kmem_free(cp, len);
 
 	/*
-	 * Allocate the buffer to use for return values. Each entry
-	 * in the buffer will have a pointer and the string itself.
-	 * The pointers go in the front of the buffer, the strings
-	 * at the end.
-	 */
-	buff_size = *nents * (sizeof(char *) + MAXNAMELEN);
-	name_ptrs = buff_start = kmem_alloc(buff_size, KM_SLEEP);
-	cp = (char *)buff_start + buff_size;
-
-	/*
 	 * Now loop using vboxCallDirInfo to get one file name at a time
 	 */
 	cnt = 0;
@@ -863,24 +860,41 @@ sfprov_readdir(
 		}
 
 		/*
-		 * Put this name in the buffer, stop if we run out of room.
+		 * Put this name in the buffer, expand if we run out of room.
 		 */
-		cp -= strlen(info->name.String.utf8) + 1;
-		if (cp < (char *)(&name_ptrs[cnt + 2]))
-			break;
-		strcpy(cp, info->name.String.utf8);
-		name_ptrs[cnt] = cp;
+		reclen = DIRENT64_RECLEN(strlen(info->name.String.utf8));
+		if (SFFS_DIRENTS_OFF + cur_buf->sf_len + reclen > SFFS_DIRENTS_SIZE) {
+			cur_buf->sf_next = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
+			if (cur_buf->sf_next == NULL) {
+				error = ENOSPC;
+				goto done;
+			}
+			cur_buf = cur_buf->sf_next;
+			cur_buf->sf_next = NULL;
+			cur_buf->sf_len = 0;
+		}
+
+		dirent = (dirent64_t *)
+			(((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len);
+		strcpy(&dirent->d_name[0], info->name.String.utf8);
+		dirent->d_reclen = reclen;
+		dirent->d_off = cnt;
+
+		cur_buf->sf_len += reclen;
 		++cnt;
 	}
 	error = 0;
-	name_ptrs[cnt] = NULL;
-	*nents = cnt;
-	*buffer = buff_start;
-	*buffersize = buff_size;
+
 done:
-	if (error != 0)
-		kmem_free(buff_start, buff_size);
-	kmem_free(mask_str, mask_size);
+	if (error != 0) {
+		while (*dirents) {
+			cur_buf = (*dirents)->sf_next;
+			kmem_free(*dirents, SFFS_DIRENTS_SIZE);
+			*dirents = cur_buf;
+		}
+	}
+	if (mask_str != NULL)
+		kmem_free(mask_str, mask_size);
 	sfprov_close(fp);
 	return (error);
 }
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
index 8693ad1..f08b09f 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -109,8 +109,20 @@ extern int sfprov_rename(sfp_mount_t *, char *from, char *to, uint_t is_dir);
 /*
  * Read directory entries.
  */
-extern int sfprov_readdir(sfp_mount_t *mnt, char *path, void **buffer,
-	size_t *buffersize, uint32_t *nents);
+/*
+ * a singly linked list of buffers, each containing an array of dirent's.
+ * sf_len is length of the sf_entries array, in bytes.
+ */
+typedef struct sffs_dirents {
+	struct sffs_dirents	*sf_next;
+	len_t			sf_len;
+	dirent64_t		sf_entries[1];
+} sffs_dirents_t;
+
+extern int sfprov_readdir(sfp_mount_t *mnt, char *path, sffs_dirents_t **dirents);
+
+#define SFFS_DIRENTS_SIZE	8192
+#define SFFS_DIRENTS_OFF	(offsetof(sffs_dirents_t, sf_entries[0]))
 
 #ifdef	__cplusplus
 }
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index aad0078..56d0213 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -218,6 +218,7 @@ sfnode_make(
 	node->sf_parent = parent;
 	if (parent)
 		++parent->sf_children;
+	node->sf_dir_list = NULL;
 
 	/*
 	 * add the new node to our cache
@@ -582,14 +583,10 @@ sffs_readdir(
 	sfnode_t *dir = VN2SFN(vp);
 	sfnode_t *node;
 	struct dirent64 *dirent;
+	sffs_dirents_t *cur_buf;
+	offset_t offset;
 	int dummy_eof;
 	int error = 0;
-	int namelen;
-	void *prov_buff = NULL;
-	size_t prov_buff_size;
-	char **names;
-	uint32_t nents;
-	uint32_t index;
 
 	if (uiop->uio_iovcnt != 1)
 		return (EINVAL);
@@ -606,63 +603,60 @@ sffs_readdir(
 		return (0);
 	}
 
-	dirent = kmem_zalloc(DIRENT64_RECLEN(MAXNAMELEN), KM_SLEEP);
-
 	/*
 	 * Get the directory entry names from the host. This gets all
-	 * entries, so add in starting offset. Max the caller can expect
-	 * would be the size of the UIO buffer / sizeof of a dirent for
-	 * file with name of length 1
+	 * entries. These are stored in a linked list of sffs_dirents_t
+	 * buffers, each of which contains a list of dirent64_t's.
 	 */
 	mutex_enter(&sffs_lock);
-	index = uiop->uio_loffset;
-	nents = index + (uiop->uio_resid / DIRENT64_RECLEN(1));
-	error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
-	    &prov_buff, &prov_buff_size, &nents);
-	if (error != 0)
-		goto done;
-	if (nents <= index) {
-		*eofp = 1;
-		goto done;
+
+	if (dir->sf_dir_list == NULL) {
+		error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
+					&dir->sf_dir_list);
+		if (error != 0)
+			goto done;
 	}
-	names = (void *)prov_buff;
 
 	/*
-	 * Lookup each of the names, so that we have ino's.
+	 * Lookup each of the names, so that we have ino's, and copy to
+	 * result buffer.
 	 */
-	for (; index < nents; ++index) {
-		if (strcmp(names[index], ".") == 0) {
+	offset = 0;
+	cur_buf = dir->sf_dir_list;
+	while (cur_buf != NULL) {
+		if (offset + cur_buf->sf_len <= uiop->uio_loffset) {
+			offset += cur_buf->sf_len;
+			cur_buf = cur_buf->sf_next;
+			continue;
+		}
+
+		dirent = (dirent64_t *)
+			(((char *) &cur_buf->sf_entries[0]) +
+			 (uiop->uio_loffset - offset));
+		if (dirent->d_reclen > uiop->uio_resid)
+			break;
+
+		if (strcmp(dirent->d_name, ".") == 0) {
 			node = dir;
-		} else if (strcmp(names[index], "..") == 0) {
+		} else if (strcmp(dirent->d_name, "..") == 0) {
 			node = dir->sf_parent;
 			if (node == NULL)
 				node = dir;
 		} else {
-			node = sfnode_lookup(dir, names[index], VNON);
+			node = sfnode_lookup(dir, dirent->d_name, VNON);
 			if (node == NULL)
 				panic("sffs_readdir() lookup failed");
 		}
-		namelen = strlen(names[index]);
-		strcpy(&dirent->d_name[0], names[index]);
-		dirent->d_reclen = DIRENT64_RECLEN(namelen);
-		dirent->d_off = index;
 		dirent->d_ino = node->sf_ino;
-		if (dirent->d_reclen > uiop->uio_resid) {
-			error = ENOSPC;
-			break;
-		}
+
 		error = uiomove(dirent, dirent->d_reclen, UIO_READ, uiop);
 		if (error != 0)
 			break;
-		bzero(&dirent->d_name[0], namelen);
 	}
-	if (error == 0 && index >= nents)
+	if (error == 0 && cur_buf == NULL)
 		*eofp = 1;
 done:
 	mutex_exit(&sffs_lock);
-	if (prov_buff != NULL)
-		kmem_free(prov_buff, prov_buff_size);
-	kmem_free(dirent, DIRENT64_RECLEN(MAXNAMELEN));
 	return (error);
 }
 
@@ -1525,6 +1519,17 @@ sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
 	}
 
 	/*
+	 * Free the directory entries for the node. This should normally
+	 * have been taken care of in sffs_close(), but better safe than
+	 * sorry.
+	 */
+	while (node->sf_dir_list != NULL) {
+		sffs_dirents_t *next = node->sf_dir_list->sf_next;
+		kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
+		node->sf_dir_list = next;
+	}
+
+	/*
 	 * If the node is stale, we can also destroy it.
 	 */
 	if (node->sf_is_stale && node->sf_children == 0)
@@ -1557,6 +1562,27 @@ sffs_close(
 	cred_t *cr,
 	caller_context_t *ct)
 {
+	sfnode_t *node;
+
+	mutex_enter(&sffs_lock);
+	node = VN2SFN(vp);
+
+	/*
+	 * Free the directory entries for the node. We do this on this call
+	 * here because the directory node may not become inactive for a long
+	 * time after the readdir is over. Case in point, if somebody cd's into
+	 * the directory then it won't become inactive until they cd away again.
+	 * In such a case we would end up with the directory listing not getting
+	 * updated (i.e. the result of 'ls' always being the same) until they
+	 * change the working directory.
+	 */
+	while (node->sf_dir_list != NULL) {
+		sffs_dirents_t *next = node->sf_dir_list->sf_next;
+		kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
+		node->sf_dir_list = next;
+	}
+
+	mutex_exit(&sffs_lock);
 	return (0);
 }
 
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
index dbdbdcf..0eda1c3 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
@@ -47,6 +47,7 @@ typedef struct sfnode {
 	uint16_t	sf_children;	/* number of children sfnodes */
 	uint8_t		sf_type;	/* VDIR or VREG */
 	uint8_t		sf_is_stale;	/* this is stale and should be purged */
+	sffs_dirents_t	*sf_dir_list;	/* list of entries for this directory */
 } sfnode_t;
 
 #define VN2SFN(vp) ((sfnode_t *)(vp)->v_data)
-------------- next part --------------

Improve performance of readdir. Instead of reading one directory entry at
a time (i.e. making one guest call per entry) we read in whole chunks at
a time. This speeds up the operation by 10 to 20 times.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index e393ff8..c14436b 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -796,10 +796,11 @@ sfprov_readdir(
 	SHFLSTRING *mask_str = NULL;	/* must be path with "/*" appended */
 	int mask_size;
 	sfp_file_t *fp;
-	static char infobuff[2 * MAXNAMELEN];	/* not on stack!! */
-	SHFLDIRINFO *info = (SHFLDIRINFO *)&infobuff;
+	uint32_t infobuff_alloc = 16384;
+	SHFLDIRINFO *infobuff = NULL, *info;
 	uint32_t numbytes;
-	uint32_t justone;
+	uint32_t nents;
+	uint32_t size;
 	uint32_t cnt;
 	sffs_dirents_t *cur_buf;
 	struct dirent64 *dirent;
@@ -839,49 +840,69 @@ sfprov_readdir(
 	kmem_free(cp, len);
 
 	/*
-	 * Now loop using vboxCallDirInfo to get one file name at a time
+	 * Now loop using vboxCallDirInfo
 	 */
+	infobuff = kmem_alloc(infobuff_alloc, KM_SLEEP);
+	if (infobuff == NULL) {
+		error = (ENOSPC);
+		goto done;
+	}
+
 	cnt = 0;
 	for (;;) {
-		justone = 1;
-		numbytes = sizeof (infobuff);
+		numbytes = infobuff_alloc;
 		error = vboxCallDirInfo(&vbox_client, &fp->map, fp->handle,
-		    mask_str, SHFL_LIST_RETURN_ONE, 0, &numbytes, info,
-		    &justone);
-		if (error == VERR_NO_MORE_FILES) {
-			break;
-		}
-		else if (error == VERR_NO_TRANSLATION) {
-			continue;	/* ?? just skip this one */
-		}
-		else if (error != VINF_SUCCESS || justone != 1) {
-			error = EINVAL;
-	 		goto done;
+		    mask_str, 0, 0, &numbytes, infobuff, &nents);
+		switch (error) {
+			case VINF_SUCCESS:
+				/* fallthrough */
+			case VERR_NO_MORE_FILES:
+				break;
+
+			case VERR_NO_TRANSLATION:
+				/* XXX ??? */
+				break;
+
+			default:
+				error = RTErrConvertToErrno(error);
+				goto done;
 		}
 
 		/*
-		 * Put this name in the buffer, expand if we run out of room.
+		 * Create the dirent_t's for each name
 		 */
-		reclen = DIRENT64_RECLEN(strlen(info->name.String.utf8));
-		if (SFFS_DIRENTS_OFF + cur_buf->sf_len + reclen > SFFS_DIRENTS_SIZE) {
-			cur_buf->sf_next = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
-			if (cur_buf->sf_next == NULL) {
-				error = ENOSPC;
-				goto done;
+		for (info = infobuff; (char *) info < (char *) infobuff + numbytes; nents--) {
+			/* expand buffer if we need more space */
+			reclen = DIRENT64_RECLEN(strlen(info->name.String.utf8));
+			if (SFFS_DIRENTS_OFF + cur_buf->sf_len + reclen > SFFS_DIRENTS_SIZE) {
+				cur_buf->sf_next = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
+				if (cur_buf->sf_next == NULL) {
+					error = ENOSPC;
+					goto done;
+				}
+				cur_buf = cur_buf->sf_next;
+				cur_buf->sf_next = NULL;
+				cur_buf->sf_len = 0;
 			}
-			cur_buf = cur_buf->sf_next;
-			cur_buf->sf_next = NULL;
-			cur_buf->sf_len = 0;
-		}
 
-		dirent = (dirent64_t *)
-			(((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len);
-		strcpy(&dirent->d_name[0], info->name.String.utf8);
-		dirent->d_reclen = reclen;
-		dirent->d_off = cnt;
+			/* create the dirent with the name, offset, and len */
+			dirent = (dirent64_t *)
+				(((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len);
+			strcpy(&dirent->d_name[0], info->name.String.utf8);
+			dirent->d_reclen = reclen;
+			dirent->d_off = cnt;
+
+			cur_buf->sf_len += reclen;
+			++cnt;
 
-		cur_buf->sf_len += reclen;
-		++cnt;
+			size = offsetof (SHFLDIRINFO, name.String) + info->name.u16Size;
+			info = (SHFLDIRINFO *) ((uintptr_t) info + size);
+		}
+		ASSERT(nents == 0);
+		ASSERT((char *) info == (char *) infobuff + numbytes);
+
+		if (error == VERR_NO_MORE_FILES)
+			break;
 	}
 	error = 0;
 
@@ -893,6 +914,8 @@ done:
 			*dirents = cur_buf;
 		}
 	}
+	if (infobuff != NULL)
+		kmem_free(infobuff, infobuff_alloc);
 	if (mask_str != NULL)
 		kmem_free(mask_str, mask_size);
 	sfprov_close(fp);
-------------- next part --------------

Improved performance of getattr by making 1 instead of 5 separate guest
(and hence stat) calls.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index c14436b..9db1d27 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -406,56 +406,66 @@ sfprov_getinfo(sfp_mount_t *mnt, char *path, RTFSOBJINFO *info)
 /*
  * get information about a file (or directory)
  */
-int
-sfprov_get_mode(sfp_mount_t *mnt, char *path, mode_t *mode)
+static void
+sfprov_mode_from_fmode(mode_t *mode, RTFMODE fMode)
 {
-	int rc;
-	RTFSOBJINFO info;
 	mode_t m = 0;
 
-	rc = sfprov_getinfo(mnt, path, &info);
-	if (rc)
-		return (rc);
-	if (RTFS_IS_DIRECTORY(info.Attr.fMode))
+	if (RTFS_IS_DIRECTORY(fMode))
 		m |= S_IFDIR;
-	else if (RTFS_IS_FILE(info.Attr.fMode))
+	else if (RTFS_IS_FILE(fMode))
 		m |= S_IFREG;
-	else if (RTFS_IS_FIFO(info.Attr.fMode))
+	else if (RTFS_IS_FIFO(fMode))
 		m |= S_IFIFO;
-	else if (RTFS_IS_DEV_CHAR(info.Attr.fMode))
+	else if (RTFS_IS_DEV_CHAR(fMode))
 		m |= S_IFCHR;
-	else if (RTFS_IS_DEV_BLOCK(info.Attr.fMode))
+	else if (RTFS_IS_DEV_BLOCK(fMode))
 		m |= S_IFBLK;
-	else if (RTFS_IS_SYMLINK(info.Attr.fMode))
+	else if (RTFS_IS_SYMLINK(fMode))
 		m |= S_IFLNK;
-	else if (RTFS_IS_SOCKET(info.Attr.fMode))
+	else if (RTFS_IS_SOCKET(fMode))
 		m |= S_IFSOCK;
 
-	if (info.Attr.fMode & RTFS_UNIX_IRUSR)
+	if (fMode & RTFS_UNIX_IRUSR)
 		m |= S_IRUSR;
-	if (info.Attr.fMode & RTFS_UNIX_IWUSR)
+	if (fMode & RTFS_UNIX_IWUSR)
 		m |= S_IWUSR;
-	if (info.Attr.fMode & RTFS_UNIX_IXUSR)
+	if (fMode & RTFS_UNIX_IXUSR)
 		m |= S_IXUSR;
-	if (info.Attr.fMode & RTFS_UNIX_IRGRP)
+	if (fMode & RTFS_UNIX_IRGRP)
 		m |= S_IRGRP;
-	if (info.Attr.fMode & RTFS_UNIX_IWGRP)
+	if (fMode & RTFS_UNIX_IWGRP)
 		m |= S_IWGRP;
-	if (info.Attr.fMode & RTFS_UNIX_IXGRP)
+	if (fMode & RTFS_UNIX_IXGRP)
 		m |= S_IXGRP;
-	if (info.Attr.fMode & RTFS_UNIX_IROTH)
+	if (fMode & RTFS_UNIX_IROTH)
 		m |= S_IROTH;
-	if (info.Attr.fMode & RTFS_UNIX_IWOTH)
+	if (fMode & RTFS_UNIX_IWOTH)
 		m |= S_IWOTH;
-	if (info.Attr.fMode & RTFS_UNIX_IXOTH)
+	if (fMode & RTFS_UNIX_IXOTH)
 		m |= S_IXOTH;
-	if (info.Attr.fMode & RTFS_UNIX_ISUID)
+	if (fMode & RTFS_UNIX_ISUID)
 		m |= S_ISUID;
-	if (info.Attr.fMode & RTFS_UNIX_ISGID)
+	if (fMode & RTFS_UNIX_ISGID)
 		m |= S_ISGID;
-	if (info.Attr.fMode & RTFS_UNIX_ISTXT)
+	if (fMode & RTFS_UNIX_ISTXT)
 		m |= S_ISVTX;
 	*mode = m;
+}
+
+/*
+ * get information about a file (or directory)
+ */
+int
+sfprov_get_mode(sfp_mount_t *mnt, char *path, mode_t *mode)
+{
+	int rc;
+	RTFSOBJINFO info;
+
+	rc = sfprov_getinfo(mnt, path, &info);
+	if (rc)
+		return (rc);
+	sfprov_mode_from_fmode(mode, info.Attr.fMode);
 	return (0);
 }
 
@@ -472,19 +482,24 @@ sfprov_get_size(sfp_mount_t *mnt, char *path, uint64_t *size)
 	return (0);
 }
 
+static void
+sfprov_ftime_from_timespec(timestruc_t *time, RTTIMESPEC *ts)
+{
+	uint64_t nanosec = RTTimeSpecGetNano(ts);
+	time->tv_sec = nanosec / 1000000000;
+	time->tv_nsec = nanosec % 1000000000;
+}
+
 int
 sfprov_get_atime(sfp_mount_t *mnt, char *path, timestruc_t *time)
 {
 	int rc;
 	RTFSOBJINFO info;
-	uint64_t nanosec;
 
 	rc = sfprov_getinfo(mnt, path, &info);
 	if (rc)
 		return (rc);
-	nanosec = RTTimeSpecGetNano(&info.AccessTime);
-	time->tv_sec = nanosec / 1000000000;
-	time->tv_nsec = nanosec % 1000000000;
+	sfprov_ftime_from_timespec(time, &info.AccessTime);
 	return (0);
 }
 
@@ -493,14 +508,11 @@ sfprov_get_mtime(sfp_mount_t *mnt, char *path, timestruc_t *time)
 {
 	int rc;
 	RTFSOBJINFO info;
-	uint64_t nanosec;
 
 	rc = sfprov_getinfo(mnt, path, &info);
 	if (rc)
 		return (rc);
-	nanosec = RTTimeSpecGetNano(&info.ModificationTime);
-	time->tv_sec = nanosec / 1000000000;
-	time->tv_nsec = nanosec % 1000000000;
+	sfprov_ftime_from_timespec(time, &info.ModificationTime);
 	return (0);
 }
 
@@ -509,14 +521,42 @@ sfprov_get_ctime(sfp_mount_t *mnt, char *path, timestruc_t *time)
 {
 	int rc;
 	RTFSOBJINFO info;
-	uint64_t nanosec;
 
 	rc = sfprov_getinfo(mnt, path, &info);
 	if (rc)
 		return (rc);
-	nanosec = RTTimeSpecGetNano(&info.ChangeTime);
-	time->tv_sec = nanosec / 1000000000;
-	time->tv_nsec = nanosec % 1000000000;
+	sfprov_ftime_from_timespec(time, &info.ChangeTime);
+	return (0);
+}
+
+int
+sfprov_get_attr(
+	sfp_mount_t *mnt,
+	char *path,
+	mode_t *mode,
+	uint64_t *size,
+	timestruc_t *atime,
+	timestruc_t *mtime,
+	timestruc_t *ctime)
+{
+	int rc;
+	RTFSOBJINFO info;
+
+	rc = sfprov_getinfo(mnt, path, &info);
+	if (rc)
+		return (rc);
+
+	if (mode)
+		sfprov_mode_from_fmode(mode, info.Attr.fMode);
+	if (size != NULL)
+		*size = info.cbObject;
+	if (atime != NULL)
+		sfprov_ftime_from_timespec(atime, &info.AccessTime);
+	if (mtime != NULL)
+		sfprov_ftime_from_timespec(mtime, &info.ModificationTime);
+	if (ctime != NULL)
+		sfprov_ftime_from_timespec(ctime, &info.ChangeTime);
+
 	return (0);
 }
 
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
index f08b09f..5948e4b 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -92,6 +92,8 @@ extern int sfprov_get_size(sfp_mount_t *, char *, uint64_t *);
 extern int sfprov_get_atime(sfp_mount_t *, char *, timestruc_t *);
 extern int sfprov_get_mtime(sfp_mount_t *, char *, timestruc_t *);
 extern int sfprov_get_ctime(sfp_mount_t *, char *, timestruc_t *);
+extern int sfprov_get_attr(sfp_mount_t *, char *, mode_t *, uint64_t *,
+			   timestruc_t *, timestruc_t *, timestruc_t *);
 extern int sfprov_set_attr(sfp_mount_t *, char *, uint_t, mode_t,
 			   timestruc_t, timestruc_t, timestruc_t);
 extern int sfprov_set_size(sfp_mount_t *, char *, uint64_t);
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 56d0213..37c527d 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -701,7 +701,6 @@ sffs_getattr(
 	sfnode_t	*node = VN2SFN(vp);
 	sffs_data_t	*sffs = node->sf_sffs;
 	mode_t		mode;
-	timestruc_t	time;
 	uint64_t	x;
 	int		error;
 
@@ -715,7 +714,9 @@ sffs_getattr(
 	vap->va_rdev =  sffs->sf_vfsp->vfs_dev;
 	vap->va_seq = 0;
 
-	error = sfprov_get_mode(node->sf_sffs->sf_handle, node->sf_path, &mode);
+	error = sfprov_get_attr(node->sf_sffs->sf_handle, node->sf_path, &mode,
+			        &x, &vap->va_atime, &vap->va_mtime,
+				&vap->va_ctime);
 	if (error == ENOENT)
 		sfnode_make_stale(node);
 	if (error != 0)
@@ -736,39 +737,10 @@ sffs_getattr(
 	else if (S_ISSOCK(mode))
 		vap->va_type = VSOCK;
 
-	error = sfprov_get_size(node->sf_sffs->sf_handle, node->sf_path, &x);
-	if (error == ENOENT)
-		sfnode_make_stale(node);
-	if (error != 0)
-		goto done;
 	vap->va_size = x;
 	vap->va_blksize = 512;
 	vap->va_nblocks = (x + 511) / 512;
 
-	error =
-	    sfprov_get_atime(node->sf_sffs->sf_handle, node->sf_path, &time);
-	if (error == ENOENT)
-		sfnode_make_stale(node);
-	if (error != 0)
-		goto done;
-	vap->va_atime = time;
-
-	error =
-	    sfprov_get_mtime(node->sf_sffs->sf_handle, node->sf_path, &time);
-	if (error == ENOENT)
-		sfnode_make_stale(node);
-	if (error != 0)
-		goto done;
-	vap->va_mtime = time;
-
-	error =
-	    sfprov_get_ctime(node->sf_sffs->sf_handle, node->sf_path, &time);
-	if (error == ENOENT)
-		sfnode_make_stale(node);
-	if (error != 0)
-		goto done;
-	vap->va_ctime = time;
-
 done:
 	mutex_exit(&sffs_lock);
 	return (error);
-------------- next part --------------

Cache the file attributes to reduce the number of host/stat calls.

Operations like 'ls -F' or 'ls -l' end up making multiple stat calls
for a every file, so caching the attributes for a short time can be
useful. In particular, caching for 200ms speeds up the listing of a
directory with 5000 entries by between 150% to 250%. A new mount
option, 'stat_ttl', controls how long attributes are cached, and
defaults to 200ms; setting it to 0 effectively disables caching.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
index 2236832..0cf732b 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
@@ -52,6 +52,7 @@ static void Usage(char *pszName)
            "     ro                 mount read only\n"
            "     uid=UID            set the default file owner user id to UID\n"
            "     gid=GID            set the default file owner group id to GID\n"
+           "     stat_ttl=TTL       set the \"time to live\" (in ms) for the stat caches\n"
            "     ttl=TTL            set the \"time to live\" to TID for the dentry\n"
            "     iocharset CHARSET  use the character set CHARSET for i/o operations (default utf8)\n"
            "     convertcp CHARSET  convert the shared folder name from the character set CHARSET to utf8\n\n");
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index 9db1d27..6fd00d3 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -828,7 +828,8 @@ int
 sfprov_readdir(
 	sfp_mount_t *mnt,
 	char *path,
-	sffs_dirents_t **dirents)
+	sffs_dirents_t **dirents,
+	sffs_stats_t **stats)
 {
 	int error;
 	char *cp;
@@ -843,17 +844,20 @@ sfprov_readdir(
 	uint32_t size;
 	uint32_t cnt;
 	sffs_dirents_t *cur_buf;
+	sffs_stats_t *cur_stats;
 	struct dirent64 *dirent;
+	sffs_stat_t *stat;
 	unsigned short reclen;
 
 	*dirents = NULL;
+	*stats = NULL;
 
 	error = sfprov_open(mnt, path, &fp);
 	if (error != 0)
 		return (ENOENT);
 
 	/*
-	 * Allocate the first dirents buffer.
+	 * Allocate the first dirents and stats buffers.
 	 */
 	*dirents = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
 	if (*dirents == NULL) {
@@ -864,6 +868,15 @@ sfprov_readdir(
 	cur_buf->sf_next = NULL;
 	cur_buf->sf_len = 0;
 
+	*stats = kmem_alloc(sizeof(**stats), KM_SLEEP);
+	if (*stats == NULL) {
+		error = (ENOSPC);
+		goto done;
+	}
+	cur_stats = *stats;
+	cur_stats->sf_next = NULL;
+	cur_stats->sf_num = 0;
+
 	/*
 	 * Create mask that VBox expects. This needs to be the directory path,
 	 * plus a "*" wildcard to get all files.
@@ -909,10 +922,10 @@ sfprov_readdir(
 		}
 
 		/*
-		 * Create the dirent_t's for each name
+		 * Create the dirent_t's and save the stats for each name
 		 */
 		for (info = infobuff; (char *) info < (char *) infobuff + numbytes; nents--) {
-			/* expand buffer if we need more space */
+			/* expand buffers if we need more space */
 			reclen = DIRENT64_RECLEN(strlen(info->name.String.utf8));
 			if (SFFS_DIRENTS_OFF + cur_buf->sf_len + reclen > SFFS_DIRENTS_SIZE) {
 				cur_buf->sf_next = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
@@ -925,6 +938,17 @@ sfprov_readdir(
 				cur_buf->sf_len = 0;
 			}
 
+			if (cur_stats->sf_num >= SFFS_STATS_LEN) {
+				cur_stats->sf_next = kmem_alloc(sizeof(**stats), KM_SLEEP);
+				if (cur_stats->sf_next == NULL) {
+					error = (ENOSPC);
+					goto done;
+				}
+				cur_stats = cur_stats->sf_next;
+				cur_stats->sf_next = NULL;
+				cur_stats->sf_num = 0;
+			}
+
 			/* create the dirent with the name, offset, and len */
 			dirent = (dirent64_t *)
 				(((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len);
@@ -935,6 +959,17 @@ sfprov_readdir(
 			cur_buf->sf_len += reclen;
 			++cnt;
 
+			/* save the stats */
+			stat = &cur_stats->sf_stats[cur_stats->sf_num];
+			++cur_stats->sf_num;
+
+			sfprov_mode_from_fmode(&stat->sf_mode, info->Info.Attr.fMode);
+			stat->sf_size = info->Info.cbObject;
+			sfprov_ftime_from_timespec(&stat->sf_atime, &info->Info.AccessTime);
+			sfprov_ftime_from_timespec(&stat->sf_mtime, &info->Info.ModificationTime);
+			sfprov_ftime_from_timespec(&stat->sf_ctime, &info->Info.ChangeTime);
+
+			/* next info */
 			size = offsetof (SHFLDIRINFO, name.String) + info->name.u16Size;
 			info = (SHFLDIRINFO *) ((uintptr_t) info + size);
 		}
@@ -953,6 +988,11 @@ done:
 			kmem_free(*dirents, SFFS_DIRENTS_SIZE);
 			*dirents = cur_buf;
 		}
+		while (*stats) {
+			cur_stats = (*stats)->sf_next;
+			kmem_free(*stats, sizeof(**stats));
+			*stats = cur_stats;
+		}
 	}
 	if (infobuff != NULL)
 		kmem_free(infobuff, infobuff_alloc);
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
index 5948e4b..d045bae 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -121,10 +121,26 @@ typedef struct sffs_dirents {
 	dirent64_t		sf_entries[1];
 } sffs_dirents_t;
 
-extern int sfprov_readdir(sfp_mount_t *mnt, char *path, sffs_dirents_t **dirents);
-
 #define SFFS_DIRENTS_SIZE	8192
 #define SFFS_DIRENTS_OFF	(offsetof(sffs_dirents_t, sf_entries[0]))
+#define SFFS_STATS_LEN		100
+
+typedef struct sffs_stat {
+	mode_t		sf_mode;
+	off_t		sf_size;
+	timestruc_t	sf_atime;
+	timestruc_t	sf_mtime;
+	timestruc_t	sf_ctime;
+} sffs_stat_t;
+
+typedef struct sffs_stats {
+	struct sffs_stats	*sf_next;
+	len_t			sf_num;
+	sffs_stat_t		sf_stats[SFFS_STATS_LEN];
+} sffs_stats_t;
+
+extern int sfprov_readdir(sfp_mount_t *mnt, char *path, sffs_dirents_t **dirents,
+			sffs_stats_t **stats);
 
 #ifdef	__cplusplus
 }
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
index 70a1750..d195f79 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
@@ -47,6 +47,8 @@
 /** The module description as seen in 'modinfo'. */
 #define DEVICE_DESC                     "VirtualBox ShrdFS"
 
+#define DEF_STAT_TTL_MS                 200
+
 
 /*
  * Shared Folders filesystem implementation of the Solaris VFS interfaces.
@@ -63,7 +65,8 @@ static int sffs_statvfs(vfs_t *vfsp, statvfs64_t *sbp);
 static mntopt_t sffs_options[] = {
 	/* Option	Cancels Opt	Arg	Flags		Data */
 	{"uid",		NULL,		NULL,	MO_HASVALUE,	NULL},
-	{"gid",		NULL,		NULL,	MO_HASVALUE,	NULL}
+	{"gid",		NULL,		NULL,	MO_HASVALUE,	NULL},
+	{"stat_ttl",	NULL,		NULL,	MO_HASVALUE,	NULL}
 };
 
 static mntopts_t sffs_options_table = {
@@ -226,6 +229,7 @@ sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 	dev_t dev;
 	uid_t uid = 0;
 	gid_t gid = 0;
+	int stat_ttl = DEF_STAT_TTL_MS;
 	char *optval;
 	long val;
 	char *path;
@@ -288,6 +292,14 @@ sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 		gid = val;
 
 	/*
+	 * ttl to use for stat caches
+	 */
+	if (vfs_optionisset(vfsp, "stat_ttl", &optval) &&
+	    ddi_strtol(optval, NULL, 10, &val) == 0 &&
+	    (int)val == val)
+		stat_ttl = val;
+
+	/*
 	 * Any unknown options are an error
 	 */
 	if ((uap->flags & MS_DATA) && uap->datalen > 0) {
@@ -338,6 +350,7 @@ sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 	sffs->sf_vfsp = vfsp;
 	sffs->sf_uid = uid;
 	sffs->sf_gid = gid;
+	sffs->sf_stat_ttl = stat_ttl;
 	sffs->sf_share_name = share_name;
 	sffs->sf_mntpath = mount_point;
 	sffs->sf_handle = handle;
@@ -361,7 +374,7 @@ sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 	path = kmem_alloc(2, KM_SLEEP);
 	strcpy(path, ".");
 	mutex_enter(&sffs_lock);
-	sfnode = sfnode_make(sffs, path, VDIR, NULL, NULL);
+	sfnode = sfnode_make(sffs, path, VDIR, NULL, NULL, NULL, 0);
 	sffs->sf_rootnode = sfnode_get_vnode(sfnode);
 	sffs->sf_rootnode->v_flag |= VROOT;
 	sffs->sf_rootnode->v_vfsp = vfsp;
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
index 66fe842..3e9c9ad 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
@@ -30,6 +30,7 @@ typedef struct sffs_data {
 	vnode_t		*sf_rootnode;	/* of vnode of the root directory */
 	uid_t		sf_uid;		/* owner of all shared folders */
 	gid_t		sf_gid;		/* group of all shared folders */
+	int  		sf_stat_ttl;	/* ttl for stat caches (in ms) */
 	char		*sf_share_name;
 	char 		*sf_mntpath;	/* name of mount point */
 	sfp_mount_t	*sf_handle;
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 37c527d..36bbef0 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -77,6 +77,7 @@
 #include <sys/pathname.h>
 #include <sys/dirent.h>
 #include <sys/fs_subr.h>
+#include <sys/time.h>
 #include "vboxfs_prov.h"
 #include "vboxfs_vnode.h"
 #include "vboxfs_vfs.h"
@@ -194,7 +195,9 @@ sfnode_make(
 	char		*path,
 	vtype_t		type,
 	sfp_file_t	*fp,
-	sfnode_t	*parent)	/* can be NULL for root */
+	sfnode_t	*parent,	/* can be NULL for root */
+	sffs_stat_t	*stat,
+	uint64_t	stat_time)
 {
 	sfnode_t	*node;
 	avl_index_t	where;
@@ -219,6 +222,13 @@ sfnode_make(
 	if (parent)
 		++parent->sf_children;
 	node->sf_dir_list = NULL;
+	node->sf_dir_stats = NULL;
+	if (stat != NULL) {
+		node->sf_stat = *stat;
+		node->sf_stat_time = stat_time;
+	} else {
+		node->sf_stat_time = 0;
+	}
 
 	/*
 	 * add the new node to our cache
@@ -343,6 +353,49 @@ sfnode_make_stale(sfnode_t *node)
 	}
 }
 
+static uint64_t
+sfnode_cur_time_usec(void)
+{
+	timestruc_t now = hrestime;
+	return (now.tv_sec * 1000000L + now.tv_nsec / 1000L);
+}
+
+static int
+sfnode_stat_cached(sfnode_t *node)
+{
+	return (sfnode_cur_time_usec() - node->sf_stat_time) <
+			node->sf_sffs->sf_stat_ttl * 1000L;
+}
+
+static int
+sfnode_get_stat(sfp_mount_t *mnt, char *path, sffs_stat_t *stat)
+{
+	return sfprov_get_attr(mnt, path, &stat->sf_mode, &stat->sf_size,
+				&stat->sf_atime, &stat->sf_mtime,
+				&stat->sf_ctime);
+}
+
+static void
+sfnode_invalidate_stat_cache(sfnode_t *node)
+{
+	node->sf_stat_time = 0;
+}
+
+static int
+sfnode_update_stat_cache(sfnode_t *node)
+{
+	int error;
+
+	error = sfnode_get_stat(node->sf_sffs->sf_handle, node->sf_path,
+				&node->sf_stat);
+	if (error == ENOENT)
+		sfnode_make_stale(node);
+	if (error == 0)
+		node->sf_stat_time = sfnode_cur_time_usec();
+
+	return (error);
+}
+
 /*
  * Rename a file or a directory
  */
@@ -446,7 +499,8 @@ sfnode_rename(sfnode_t *node, sfnode_t *newparent, char *path)
  * bumped by 1.
  */
 static sfnode_t *
-sfnode_lookup(sfnode_t *dir, char *name, vtype_t create)
+sfnode_lookup(sfnode_t *dir, char *name, vtype_t create, sffs_stat_t *stat,
+		uint64_t stat_time)
 {
 	avl_index_t	where;
 	sfnode_t	template;
@@ -455,6 +509,7 @@ sfnode_lookup(sfnode_t *dir, char *name, vtype_t create)
 	int		type;
 	char		*fullpath;
 	sfp_file_t	*fp;
+	sffs_stat_t	tmp_stat;
 
 	ASSERT(MUTEX_HELD(&sffs_lock));
 
@@ -500,8 +555,15 @@ sfnode_lookup(sfnode_t *dir, char *name, vtype_t create)
 		mode_t m;
 		fp = NULL;
 		type = VNON;
-		error =
-		    sfprov_get_mode(dir->sf_sffs->sf_handle, fullpath, &m);
+		if (stat == NULL) {
+			stat = &tmp_stat;
+			error =
+			    sfnode_get_stat(dir->sf_sffs->sf_handle, fullpath, stat);
+			stat_time = sfnode_cur_time_usec();
+		} else {
+			error = 0;
+		}
+		m = stat->sf_mode;
 		if (error != 0)
 			error = ENOENT;
 		else if (S_ISDIR(m))
@@ -517,7 +579,8 @@ sfnode_lookup(sfnode_t *dir, char *name, vtype_t create)
 		kmem_free(fullpath, strlen(fullpath) + 1);
 		return (NULL);
 	}
-	node = sfnode_make(dir->sf_sffs, fullpath, type, fp, dir);
+	node = sfnode_make(dir->sf_sffs, fullpath, type, fp, dir, stat,
+			stat_time);
 	return (node);
 }
 
@@ -537,14 +600,13 @@ sfnode_access(sfnode_t *node, mode_t mode, cred_t *cr)
 	ASSERT(MUTEX_HELD(&sffs_lock));
 
 	/*
-	 * get the mode from the provider
+	 * get the mode from the cache or provider
 	 */
-	error = sfprov_get_mode(node->sf_sffs->sf_handle, node->sf_path, &m);
-	if (error != 0) {
-		m = 0;
-		if (error == ENOENT)
-			sfnode_make_stale(node);
-	}
+	if (sfnode_stat_cached(node))
+		error = 0;
+	else
+		error = sfnode_update_stat_cache(node);
+	m = (error == 0) ? node->sf_stat.sf_mode : 0;
 
 	/*
 	 * mask off the permissions based on uid/gid
@@ -584,6 +646,8 @@ sffs_readdir(
 	sfnode_t *node;
 	struct dirent64 *dirent;
 	sffs_dirents_t *cur_buf;
+	sffs_stats_t *cur_stats;
+	int cur_snum;
 	offset_t offset;
 	int dummy_eof;
 	int error = 0;
@@ -612,7 +676,7 @@ sffs_readdir(
 
 	if (dir->sf_dir_list == NULL) {
 		error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
-					&dir->sf_dir_list);
+					&dir->sf_dir_list, &dir->sf_dir_stats);
 		if (error != 0)
 			goto done;
 	}
@@ -623,6 +687,8 @@ sffs_readdir(
 	 */
 	offset = 0;
 	cur_buf = dir->sf_dir_list;
+	cur_stats = dir->sf_dir_stats;
+	cur_snum = 0;
 	while (cur_buf != NULL) {
 		if (offset + cur_buf->sf_len <= uiop->uio_loffset) {
 			offset += cur_buf->sf_len;
@@ -630,6 +696,11 @@ sffs_readdir(
 			continue;
 		}
 
+		if (cur_snum >= SFFS_STATS_LEN) {
+			cur_stats = cur_stats->sf_next;
+			cur_snum = 0;
+		}
+
 		dirent = (dirent64_t *)
 			(((char *) &cur_buf->sf_entries[0]) +
 			 (uiop->uio_loffset - offset));
@@ -643,13 +714,16 @@ sffs_readdir(
 			if (node == NULL)
 				node = dir;
 		} else {
-			node = sfnode_lookup(dir, dirent->d_name, VNON);
+			node = sfnode_lookup(dir, dirent->d_name, VNON,
+					&cur_stats->sf_stats[cur_snum],
+					sfnode_cur_time_usec());
 			if (node == NULL)
 				panic("sffs_readdir() lookup failed");
 		}
 		dirent->d_ino = node->sf_ino;
 
 		error = uiomove(dirent, dirent->d_reclen, UIO_READ, uiop);
+		++cur_snum;
 		if (error != 0)
 			break;
 	}
@@ -701,8 +775,7 @@ sffs_getattr(
 	sfnode_t	*node = VN2SFN(vp);
 	sffs_data_t	*sffs = node->sf_sffs;
 	mode_t		mode;
-	uint64_t	x;
-	int		error;
+	int		error = 0;
 
 	mutex_enter(&sffs_lock);
 	vap->va_type = vp->v_type;
@@ -714,13 +787,17 @@ sffs_getattr(
 	vap->va_rdev =  sffs->sf_vfsp->vfs_dev;
 	vap->va_seq = 0;
 
-	error = sfprov_get_attr(node->sf_sffs->sf_handle, node->sf_path, &mode,
-			        &x, &vap->va_atime, &vap->va_mtime,
-				&vap->va_ctime);
-	if (error == ENOENT)
-		sfnode_make_stale(node);
-	if (error != 0)
-		goto done;
+	if (!sfnode_stat_cached(node)) {
+		error = sfnode_update_stat_cache(node);
+		if (error != 0)
+			goto done;
+	}
+
+	vap->va_atime = node->sf_stat.sf_atime;
+	vap->va_mtime = node->sf_stat.sf_mtime;
+	vap->va_ctime = node->sf_stat.sf_ctime;
+
+	mode = node->sf_stat.sf_mode;
 	vap->va_mode = mode & MODEMASK;
 	if (S_ISDIR(mode))
 		vap->va_type = VDIR;
@@ -737,9 +814,9 @@ sffs_getattr(
 	else if (S_ISSOCK(mode))
 		vap->va_type = VSOCK;
 
-	vap->va_size = x;
+	vap->va_size = node->sf_stat.sf_size;
 	vap->va_blksize = 512;
-	vap->va_nblocks = (x + 511) / 512;
+	vap->va_nblocks = (vap->va_size + 511) / 512;
 
 done:
 	mutex_exit(&sffs_lock);
@@ -776,6 +853,7 @@ sffs_setattr(
 
 	mutex_enter(&sffs_lock);
 
+	sfnode_invalidate_stat_cache(node);
 	error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
 				vap->va_mask, mode,
 				vap->va_atime, vap->va_mtime, vap->va_ctime);
@@ -805,6 +883,8 @@ sffs_space(
 
 	mutex_enter(&sffs_lock);
 
+	sfnode_invalidate_stat_cache(node);
+
 	error = sfprov_set_size(node->sf_sffs->sf_handle, node->sf_path,
 				bfp->l_start);
 	if (error == ENOENT)
@@ -899,6 +979,9 @@ sffs_write(
 		mutex_exit(&sffs_lock);
 		return (EINVAL);
 	}
+
+	sfnode_invalidate_stat_cache(node);
+
 	if (ioflag & FAPPEND) {
 		uint64_t endoffile;
 
@@ -1042,7 +1125,7 @@ sffs_lookup(
 	/*
 	 * Lookup the node.
 	 */
-	node = sfnode_lookup(VN2SFN(dvp), name, VNON);
+	node = sfnode_lookup(VN2SFN(dvp), name, VNON, NULL, 0);
 	if (node != NULL)
 		*vpp = sfnode_get_vnode(node);
 	mutex_exit(&sffs_lock);
@@ -1109,6 +1192,8 @@ sffs_create(
 			return (error);
 		}
 
+		sfnode_invalidate_stat_cache(VN2SFN(dvp));
+
 		/*
 		 * handle truncating an existing file
 		 */
@@ -1135,7 +1220,7 @@ sffs_create(
 	 * Create a new node. First check for a race creating it.
 	 */
 	mutex_enter(&sffs_lock);
-	node = sfnode_lookup(VN2SFN(dvp), name, VNON);
+	node = sfnode_lookup(VN2SFN(dvp), name, VNON, NULL, 0);
 	if (node != NULL) {
 		mutex_exit(&sffs_lock);
 		return (EEXIST);
@@ -1144,7 +1229,8 @@ sffs_create(
 	/*
 	 * Doesn't exist yet and we have the lock, so create it.
 	 */
-	node = sfnode_lookup(VN2SFN(dvp), name, VREG);
+	sfnode_invalidate_stat_cache(VN2SFN(dvp));
+	node = sfnode_lookup(VN2SFN(dvp), name, VREG, NULL, 0);
 	if (node && (vap->va_mask & AT_MODE)) {
 		timestruc_t dummy;
 		error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
@@ -1207,7 +1293,9 @@ sffs_mkdir(
 		return (error);
 	}
 
-	node = sfnode_lookup(VN2SFN(dvp), nm, VDIR);
+	sfnode_invalidate_stat_cache(VN2SFN(dvp));
+
+	node = sfnode_lookup(VN2SFN(dvp), nm, VDIR, NULL, 0);
 	if (node && (va->va_mask & AT_MODE)) {
 		timestruc_t dummy;
 		error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
@@ -1293,6 +1381,7 @@ sffs_rmdir(
 	/*
 	 * Remove the directory on the host and mark the node as stale.
 	 */
+	sfnode_invalidate_stat_cache(VN2SFN(dvp));
 	error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
 	if (error == ENOENT || error == 0)
 		sfnode_make_stale(node);
@@ -1352,6 +1441,8 @@ sffs_remove(
 	/*
 	 * Remove the file on the host and mark the node as stale.
 	 */
+	sfnode_invalidate_stat_cache(VN2SFN(dvp));
+
 	error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
 	if (error == ENOENT || error == 0)
 		sfnode_make_stale(node);
@@ -1394,16 +1485,19 @@ sffs_rename(
 	if (error)
 		goto done;
 
-	node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON);
+	node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON, NULL, 0);
 	if (node == NULL) {
 		error = ENOENT;
 		goto done;
 	}
 
-
 	/*
 	 * Rename the file on the host and in our caches.
 	 */
+	sfnode_invalidate_stat_cache(node);
+	sfnode_invalidate_stat_cache(VN2SFN(old_dir));
+	sfnode_invalidate_stat_cache(VN2SFN(new_dir));
+
 	newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
 	error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
 	    node->sf_type == VDIR);
@@ -1501,6 +1595,12 @@ sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
 		node->sf_dir_list = next;
 	}
 
+	while (node->sf_dir_stats != NULL) {
+		sffs_stats_t *next = node->sf_dir_stats->sf_next;
+		kmem_free(node->sf_dir_stats, sizeof(*node->sf_dir_stats));
+		node->sf_dir_stats = next;
+	}
+
 	/*
 	 * If the node is stale, we can also destroy it.
 	 */
@@ -1554,6 +1654,8 @@ sffs_close(
 		node->sf_dir_list = next;
 	}
 
+	sfnode_invalidate_stat_cache(node);
+
 	mutex_exit(&sffs_lock);
 	return (0);
 }
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
index 0eda1c3..f4f9e33 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
@@ -47,7 +47,10 @@ typedef struct sfnode {
 	uint16_t	sf_children;	/* number of children sfnodes */
 	uint8_t		sf_type;	/* VDIR or VREG */
 	uint8_t		sf_is_stale;	/* this is stale and should be purged */
+	sffs_stat_t	sf_stat;	/* cached file attrs for this node */
+	uint64_t	sf_stat_time;	/* last-modified time of sf_stat */
 	sffs_dirents_t	*sf_dir_list;	/* list of entries for this directory */
+	sffs_stats_t	*sf_dir_stats;	/* file attrs for the above entries */
 } sfnode_t;
 
 #define VN2SFN(vp) ((sfnode_t *)(vp)->v_data)
@@ -56,7 +59,7 @@ typedef struct sfnode {
 extern int sffs_vnode_init(void);
 extern void sffs_vnode_fini(void);
 extern sfnode_t *sfnode_make(struct sffs_data *, char *, vtype_t, sfp_file_t *,
- sfnode_t *parent);
+ sfnode_t *parent, sffs_stat_t *, uint64_t stat_time);
 extern vnode_t *sfnode_get_vnode(sfnode_t *);
 
 /*
-------------- next part --------------

Added support for fsync. A mount option, 'fsync', determines whether the
fsync is actually performed or not, with the default being to ignore it
(this preserves the previous behaviour).

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
index 0cf732b..b765671 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
@@ -53,6 +53,7 @@ static void Usage(char *pszName)
            "     uid=UID            set the default file owner user id to UID\n"
            "     gid=GID            set the default file owner group id to GID\n"
            "     stat_ttl=TTL       set the \"time to live\" (in ms) for the stat caches\n"
+           "     fsync              honor fsync calls instead of ignoring them\n"
            "     ttl=TTL            set the \"time to live\" to TID for the dentry\n"
            "     iocharset CHARSET  use the character set CHARSET for i/o operations (default utf8)\n"
            "     convertcp CHARSET  convert the shared folder name from the character set CHARSET to utf8\n\n");
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index 6fd00d3..39f92c7 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -379,6 +379,17 @@ sfprov_write(sfp_file_t *fp, char *buffer, uint64_t offset, uint32_t *numbytes)
 	return (0);
 }
 
+int
+sfprov_fsync(sfp_file_t *fp)
+{
+	int rc;
+
+	rc = vboxCallFlush(&vbox_client, &fp->map, fp->handle);
+	if (RT_FAILURE(rc))
+		return (EIO);
+	return (0);
+}
+
 
 static int
 sfprov_getinfo(sfp_mount_t *mnt, char *path, RTFSOBJINFO *info)
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
index d045bae..1be2031 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -82,6 +82,7 @@ extern int sfprov_read(sfp_file_t *, char * buffer, uint64_t offset,
     uint32_t *numbytes);
 extern int sfprov_write(sfp_file_t *, char * buffer, uint64_t offset,
     uint32_t *numbytes);
+extern int sfprov_fsync(sfp_file_t *fp);
 
 
 /*
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
index d195f79..2dd0965 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
@@ -66,7 +66,8 @@ static mntopt_t sffs_options[] = {
 	/* Option	Cancels Opt	Arg	Flags		Data */
 	{"uid",		NULL,		NULL,	MO_HASVALUE,	NULL},
 	{"gid",		NULL,		NULL,	MO_HASVALUE,	NULL},
-	{"stat_ttl",	NULL,		NULL,	MO_HASVALUE,	NULL}
+	{"stat_ttl",	NULL,		NULL,	MO_HASVALUE,	NULL},
+	{"fsync",	NULL,		NULL,	0,	        NULL}
 };
 
 static mntopts_t sffs_options_table = {
@@ -230,6 +231,7 @@ sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 	uid_t uid = 0;
 	gid_t gid = 0;
 	int stat_ttl = DEF_STAT_TTL_MS;
+	int fsync = 0;
 	char *optval;
 	long val;
 	char *path;
@@ -300,6 +302,12 @@ sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 		stat_ttl = val;
 
 	/*
+	 * whether to honor fsync
+	 */
+	if (vfs_optionisset(vfsp, "fsync", &optval))
+            fsync = 1;
+
+	/*
 	 * Any unknown options are an error
 	 */
 	if ((uap->flags & MS_DATA) && uap->datalen > 0) {
@@ -351,6 +359,7 @@ sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 	sffs->sf_uid = uid;
 	sffs->sf_gid = gid;
 	sffs->sf_stat_ttl = stat_ttl;
+	sffs->sf_fsync = fsync;
 	sffs->sf_share_name = share_name;
 	sffs->sf_mntpath = mount_point;
 	sffs->sf_handle = handle;
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
index 3e9c9ad..414d735 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
@@ -31,6 +31,7 @@ typedef struct sffs_data {
 	uid_t		sf_uid;		/* owner of all shared folders */
 	gid_t		sf_gid;		/* group of all shared folders */
 	int  		sf_stat_ttl;	/* ttl for stat caches (in ms) */
+	int  		sf_fsync;	/* whether to honor fsync or not */
 	char		*sf_share_name;
 	char 		*sf_mntpath;	/* name of mount point */
 	sfp_mount_t	*sf_handle;
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 36bbef0..9ec5879 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -1518,20 +1518,22 @@ done:
 static int
 sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
 {
-#if 0
 	sfnode_t *node;
+	int		error;
 
 	/*
 	 * Ask the host to sync any data it may have cached for open files.
-	 * I don't think we care about errors.
 	 */
 	mutex_enter(&sffs_lock);
 	node = VN2SFN(vp);
-	if (node->sf_file != NULL)
-		(void) sfprov_fsync(node->sf_file);
+	if (node->sf_file == NULL)
+		error = EBADF;
+	else if (node->sf_sffs->sf_fsync)
+		error = sfprov_fsync(node->sf_file);
+	else
+		error = 0;
 	mutex_exit(&sffs_lock);
-#endif
-	return (0);
+	return (error);
 }
 
 /*


More information about the vbox-dev mailing list