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

Life is hard, and then you die ronald at innovation.ch
Wed Jun 23 11:51:58 GMT 2010


On Wed, Jun 23, 2010 at 10:54:43AM +0200, Ramshankar wrote:
> On Tue, 2010-06-22 at 02:17 -0700, Life is hard, and then you die wrote:
> > On Tue, Jun 22, 2010 at 09:29:24AM +0200, Christian Pötzsch wrote:
> > > On 06/22/10 00:35, Life is hard, and then you die wrote:
> > > > 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).
> > > The coding style guide is located in doc/VBox-CodingGuidelines.cpp.
[snip]
> > > No tabs, 4 spaces indentation. Not all the source code follow this strictly
> > > yet, but it is improving.
> > 
> > Ok. The vast majority of lines in the source files I edited were of
> > the tab variety, and hence that's what I used - I can easily redo the
> > patches to use spaces on all the affected lines, if you'd like. Would
> > you like a patch that first fixes all the tabs, indentation, and
> > bracing? (just for .../solaris/SharedFolders/).
> 
> The coding style followed in the solaris Shared Folders is actually the
> Solaris kernel coding style from the original developer wrote it this
> way, and back then it was written this way as it might end up being part
> of Solaris itself.
> 
> So for the moment, please preserve this coding style for solaris shared
> folders, i.e. tabs and braces.

Ok, makes sense. I took a closer look at the Solaris coding standards
guide and realized I got a couple things wrong (mainly
line-continuations and switch statements); so I reworked the patches
to follows those better.

Also, while looking at the latest linux shared-folders changes today I
realized that I had fallen into the same trap with the readdir fix
(patch 06) as the linux code (http://www.virtualbox.org/ticket/5251),
i.e. it wasn't properly resetting the directory listing for apps doing
a seekdir. I've fixed that now and tested it, both using the 'svnadmin
load' mentioned in the above ticket as well as with a small test-suite
I wrote for this.

Attached therefore you'll find a new set of patches which include both
the style fixes and the readdir fix, but are otherwise identical to
the previous ones.

> I'll take a look at the patches you provided and get back to you
> regarding them. Thanks a lot for the effort and time on this.

Thanks.


  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..edacb7a 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -242,7 +242,8 @@ 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 +260,8 @@ 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 parent(%s) has no child",
+			    parent->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 edacb7a..62aba13 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -728,6 +728,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..76b598e 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -519,6 +519,170 @@ 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..d254f20 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 62aba13..a90d65b 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>
@@ -781,6 +782,73 @@ 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(
@@ -1513,6 +1581,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
@@ -1533,6 +1603,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 a90d65b..303a14e 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -1180,6 +1180,15 @@ 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);
@@ -1233,6 +1242,15 @@ 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 303a14e..c526453 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -1479,7 +1479,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 76b598e..59c6186 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
@@ -774,23 +775,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;
@@ -798,45 +791,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;
@@ -858,24 +855,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 d254f20..a4c6386 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 c526453..2e0b259 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -142,6 +142,21 @@ sfnode_construct_path(sfnode_t *node, char *tail)
 }
 
 /*
+ * Clears the (cached) directory listing for the node.
+ */
+static void
+sfnode_clear_dir_list(sfnode_t *node)
+{
+	ASSERT(MUTEX_HELD(&sffs_lock));
+
+	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;
+	}
+}
+
+/*
  * Open the provider file associated with a vnode. Holding the file open is
  * the only way we have of trying to have a vnode continue to refer to the
  * same host file in the host in light of the possibility of host side renames.
@@ -218,6 +233,7 @@ sfnode_make(
 	node->sf_parent = parent;
 	if (parent)
 		++parent->sf_children;
+	node->sf_dir_list = NULL;
 
 	/*
 	 * add the new node to our cache
@@ -257,9 +273,11 @@ top:
 	avl_remove(tree, node);
 
 	VFS_RELE(node->sf_sffs->sf_vfsp);
+	sfnode_clear_dir_list(node);
 	kmem_free(node->sf_path, strlen(node->sf_path) + 1);
 	kmem_free(node, sizeof (*node));
 	if (parent != NULL) {
+		sfnode_clear_dir_list(parent);
 		if (parent->sf_children == 0)
 			panic("sfnode_destroy parent(%s) has no child",
 			    parent->sf_path);
@@ -313,6 +331,7 @@ sfnode_make_stale(sfnode_t *node)
 				sfnode_destroy(n);
 			} else {
                 LogFlowFunc(("sffs_make_stale(%s) sub\n", n->sf_path));
+				sfnode_clear_dir_list(n);
 				if (avl_find(&sfnodes, n, &where) == NULL)
 					panic("sfnode_make_stale(%s)"
 					    " not in sfnodes", n->sf_path);
@@ -333,6 +352,9 @@ sfnode_make_stale(sfnode_t *node)
 		sfnode_destroy(node);
 	} else if (!node->sf_is_stale) {
         LogFlowFunc(("sffs_make_stale(%s)\n", node->sf_path));
+		sfnode_clear_dir_list(node);
+		if (node->sf_parent)
+			sfnode_clear_dir_list(node->sf_parent);
 		if (avl_find(&sfnodes, node, &where) == NULL)
 			panic("sfnode_make_stale(%s) not in sfnodes",
 			    node->sf_path);
@@ -433,6 +455,8 @@ sfnode_rename(sfnode_t *node, sfnode_t *newparent, char *path)
 		panic("sfnode_rename(%s) no parent", node->sf_path);
 	if (node->sf_parent->sf_children == 0)
 		panic("sfnode_rename(%s) parent has no child", node->sf_path);
+	sfnode_clear_dir_list(node->sf_parent);
+	sfnode_clear_dir_list(newparent);
 	--node->sf_parent->sf_children;
 	node->sf_parent = newparent;
 	++newparent->sf_children;
@@ -584,14 +608,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);
@@ -608,63 +628,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);
 }
 
@@ -1189,6 +1206,9 @@ sffs_create(
 			    " rc=%d", node->sf_path, vap->va_mode, error);
 	}
 
+	if (node->sf_parent)
+		sfnode_clear_dir_list(node->sf_parent);
+
 	mutex_exit(&sffs_lock);
 	if (node == NULL)
 		return (EINVAL);
@@ -1251,6 +1271,9 @@ sffs_mkdir(
 			    " rc=%d", node->sf_path, va->va_mode, error);
 	}
 
+	if (node->sf_parent)
+		sfnode_clear_dir_list(node->sf_parent);
+
 	mutex_exit(&sffs_lock);
 	if (node == NULL)
 		return (EACCES);
@@ -1329,6 +1352,9 @@ sffs_rmdir(
 	error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
 	if (error == ENOENT || error == 0)
 		sfnode_make_stale(node);
+
+	if (node->sf_parent)
+		sfnode_clear_dir_list(node->sf_parent);
 done:
 	mutex_exit(&sffs_lock);
 	VN_RELE(vp);
@@ -1388,6 +1414,9 @@ sffs_remove(
 	error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
 	if (error == ENOENT || error == 0)
 		sfnode_make_stale(node);
+
+	if (node->sf_parent)
+		sfnode_clear_dir_list(node->sf_parent);
 done:
 	mutex_exit(&sffs_lock);
 	VN_RELE(vp);
@@ -1524,6 +1553,13 @@ 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.
+	 */
+	sfnode_clear_dir_list(node);
+
+	/*
 	 * If the node is stale, we can also destroy it.
 	 */
 	if (node->sf_is_stale && node->sf_children == 0)
@@ -1556,6 +1592,23 @@ 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.
+	 */
+	sfnode_clear_dir_list(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 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 59c6186..25e813a 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -791,10 +791,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;
@@ -834,49 +835,70 @@ 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) {
+		    mask_str, 0, 0, &numbytes, infobuff, &nents);
+		switch (error) {
+
+		case VINF_SUCCESS:
+			/* fallthrough */
+		case 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;
+
+		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;
 
@@ -888,6 +910,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 25e813a..c41cb7b 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 a4c6386..8d63c24 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 2e0b259..561e6e4 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -726,7 +726,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;
 
@@ -740,7 +739,8 @@ 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)
@@ -761,39 +761,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 c41cb7b..db1851e 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -823,7 +823,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;
@@ -838,17 +839,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) {
@@ -859,6 +863,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.
@@ -905,10 +918,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);
@@ -921,6 +934,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);
@@ -931,6 +955,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);
 		}
@@ -949,6 +984,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 8d63c24..6aacaf4 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 561e6e4..9525bef 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"
@@ -154,6 +155,12 @@ sfnode_clear_dir_list(sfnode_t *node)
 		kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
 		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;
+	}
 }
 
 /*
@@ -209,7 +216,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;
@@ -234,6 +243,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
@@ -366,6 +382,48 @@ 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
  */
@@ -471,7 +529,12 @@ 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;
@@ -480,6 +543,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));
 
@@ -525,8 +589,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))
@@ -542,7 +613,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);
 }
 
@@ -562,14 +634,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
@@ -609,6 +680,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;
@@ -637,7 +710,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;
 	}
@@ -648,6 +721,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;
@@ -655,6 +730,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));
@@ -668,13 +748,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;
 	}
@@ -726,8 +809,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;
@@ -739,12 +821,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;
@@ -761,9 +848,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);
@@ -800,6 +887,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);
 	if (error == ENOENT)
@@ -828,6 +916,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)
@@ -922,6 +1012,9 @@ sffs_write(
 		mutex_exit(&sffs_lock);
 		return (EINVAL);
 	}
+
+	sfnode_invalidate_stat_cache(node);
+
 	if (ioflag & FAPPEND) {
 		uint64_t endoffile;
 
@@ -1065,7 +1158,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);
@@ -1132,6 +1225,8 @@ sffs_create(
 			return (error);
 		}
 
+		sfnode_invalidate_stat_cache(VN2SFN(dvp));
+
 		/*
 		 * handle truncating an existing file
 		 */
@@ -1158,7 +1253,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);
@@ -1167,7 +1262,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,
@@ -1232,7 +1328,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,
@@ -1320,6 +1418,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);
@@ -1382,6 +1481,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);
@@ -1427,16 +1528,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);
@@ -1579,6 +1683,8 @@ sffs_close(
 	 */
 	sfnode_clear_dir_list(node);
 
+	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..f21f8fc 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 db1851e..a9c12f0 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 6aacaf4..c52654b 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..169fa10 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 9525bef..80848cf 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -1561,20 +1561,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