Index: /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
===================================================================
--- /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c	(revision 30445)
+++ /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c	(revision 30446)
@@ -30,4 +30,5 @@
 #include <sys/ddi.h>
 #include <sys/sunddi.h>
+#include <sys/dirent.h>
 #include "vboxfs_prov.h"
 #ifdef u
@@ -776,13 +777,7 @@
  * - 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
@@ -790,7 +785,5 @@
 	sfp_mount_t *mnt,
 	char *path,
-	void **buffer,
-	size_t *buffersize,
-	uint32_t *nents)
+	sffs_dirents_t **dirents)
 {
 	int error;
@@ -800,20 +793,30 @@
 	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;
-
-	*buffer = NULL;
-	*buffersize = 0;
-	if (*nents == 0)
-		return (EINVAL);
+	sffs_dirents_t *cur_buf;
+	struct dirent64 *dirent;
+	unsigned short reclen;
+
+	*dirents = NULL;
+
 	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;
 
 	/*
@@ -823,18 +826,12 @@
 	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;
 
 	/*
@@ -860,22 +857,39 @@
 
 		/*
-		 * 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);
Index: /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
===================================================================
--- /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h	(revision 30445)
+++ /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h	(revision 30446)
@@ -111,6 +111,18 @@
  * 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
Index: /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
===================================================================
--- /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c	(revision 30445)
+++ /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c	(revision 30446)
@@ -144,4 +144,19 @@
 
 /*
+ * 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
@@ -220,4 +235,5 @@
 	if (parent)
 		++parent->sf_children;
+	node->sf_dir_list = NULL;
 
 	/*
@@ -258,7 +274,9 @@
 
 	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(%s) parent has no child", node->sf_path);
@@ -313,4 +331,5 @@
 			} 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)"
@@ -333,4 +352,7 @@
 	} 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",
@@ -433,4 +455,6 @@
 	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;
@@ -584,12 +608,8 @@
 	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)
@@ -608,61 +628,58 @@
 	}
 
-	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;
-	}
-	names = (void *)prov_buff;
-
-	/*
-	 * Lookup each of the names, so that we have ino's.
-	 */
-	for (; index < nents; ++index) {
-		if (strcmp(names[index], ".") == 0) {
+
+	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;
+	}
+
+	/*
+	 * Lookup each of the names, so that we have ino's, and copy to
+	 * result buffer.
+	 */
+	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);
 }
@@ -1188,4 +1205,7 @@
 	}
 
+	if (node->sf_parent)
+		sfnode_clear_dir_list(node->sf_parent);
+
 	mutex_exit(&sffs_lock);
 	if (node == NULL)
@@ -1250,4 +1270,7 @@
 	}
 
+	if (node->sf_parent)
+		sfnode_clear_dir_list(node->sf_parent);
+
 	mutex_exit(&sffs_lock);
 	if (node == NULL)
@@ -1328,4 +1351,7 @@
 	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);
@@ -1387,4 +1413,7 @@
 	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);
@@ -1523,4 +1552,11 @@
 
 	/*
+	 * 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.
 	 */
@@ -1555,4 +1591,21 @@
 	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);
 }
@@ -1716,5 +1769,4 @@
 		}
 	}
-done:
 	mutex_exit(&sffs_lock);
 	return (0);
Index: /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
===================================================================
--- /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h	(revision 30445)
+++ /trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h	(revision 30446)
@@ -48,4 +48,5 @@
 	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;
 
