[vbox-dev] [PATCH] symlink support for shared folders in solaris guests

Life is hard, and then you die ronald at innovation.ch
Thu Jan 13 11:26:58 GMT 2011


Atached are two patches to shared folders to provide support for
symlinks on Solaris guests. The first is just some small refactoring
in preparation for the second patch, which is the real symlink work.

As noted in the comments, renaming symlinks is currently a bit hacky
(as tested on a Linux host) - it looks like vbsfRename (in vbsf.cpp)
could possibly use a SHFL_RENAME_LINK flag.

Also, for creation of symlinks to work properly either the patch in
http://www.virtualbox.org/attachment/ticket/818/symlink-fix-4.0.0.patch
or the patch I sent in my Jan 06 email
(http://article.gmane.org/gmane.comp.emulators.virtualbox.devel/3494)
needs to be applied too (the two are equivalent).

Again, the patches are under MIT license / public-domain.


  Cheers,

  Ronald

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

Removed duplicate obj-info copying code and simplified getting file attrs.
There are no functional changes.

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index 7e12d9b..d37c41d 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -524,6 +524,16 @@ sfprov_ftime_from_timespec(timestruc_t *time, RTTIMESPEC *ts)
 	time->tv_nsec = nanosec % UINT64_C(1000000000);
 }
 
+static void
+sfprov_stat_from_info(sffs_stat_t *stat, SHFLFSOBJINFO *info)
+{
+	sfprov_mode_from_fmode(&stat->sf_mode, info->Attr.fMode);
+	stat->sf_size = info->cbObject;
+	sfprov_ftime_from_timespec(&stat->sf_atime, &info->AccessTime);
+	sfprov_ftime_from_timespec(&stat->sf_mtime, &info->ModificationTime);
+	sfprov_ftime_from_timespec(&stat->sf_ctime, &info->ChangeTime);
+}
+
 int
 sfprov_get_atime(sfp_mount_t *mnt, char *path, timestruc_t *time)
 {
@@ -564,14 +574,7 @@ sfprov_get_ctime(sfp_mount_t *mnt, char *path, timestruc_t *time)
 }
 
 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)
+sfprov_get_attr(sfp_mount_t *mnt, char *path, sffs_stat_t *attr)
 {
 	int rc;
 	SHFLFSOBJINFO info;
@@ -580,16 +583,7 @@ sfprov_get_attr(
 	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);
+	sfprov_stat_from_info(attr, &info);
 
 	return (0);
 }
@@ -875,7 +869,6 @@ sfprov_readdir(
 	uint32_t size;
 	sffs_dirents_t *cur_buf;
 	struct sffs_dirent *dirent;
-	sffs_stat_t *stat;
 	unsigned short reclen;
 	unsigned short entlen;
 	off_t offset;
@@ -971,13 +964,7 @@ sfprov_readdir(
 			dirent->sf_entry.d_off = offset;
 
 			/* save the stats */
-			stat = &dirent->sf_stat;
-
-			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);
+			sfprov_stat_from_info(&dirent->sf_stat, &info->Info);
 
 			/* next info */
 			cur_buf->sf_len += entlen;
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
index c04f1f9..ed37d79 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -98,13 +98,20 @@ extern int sfprov_fsync(sfp_file_t *fp);
 /*
  * get/set information about a file (or directory) using pathname
  */
+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;
+
 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_get_attr(sfp_mount_t *, char *, mode_t *, uint64_t *,
-   timestruc_t *, timestruc_t *, timestruc_t *);
+extern int sfprov_get_attr(sfp_mount_t *, char *, sffs_stat_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);
@@ -126,14 +133,6 @@ extern int sfprov_rename(sfp_mount_t *, char *from, char *to, uint_t is_dir);
  * a singly linked list of buffers, each containing an array of stat's+dirent's.
  * sf_len is length of the sf_entries array, in bytes.
  */
-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_dirents {
 	struct sffs_dirents	*sf_next;
 	len_t			sf_len;
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index d54f3c3..036022d 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -398,13 +398,6 @@ sfnode_stat_cached(sfnode_t *node)
 	    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)
 {
@@ -416,7 +409,7 @@ sfnode_update_stat_cache(sfnode_t *node)
 {
 	int error;
 
-	error = sfnode_get_stat(node->sf_sffs->sf_handle, node->sf_path,
+	error = sfprov_get_attr(node->sf_sffs->sf_handle, node->sf_path,
 	    &node->sf_stat);
 	if (error == ENOENT)
 		sfnode_make_stale(node);
@@ -597,7 +590,7 @@ sfnode_lookup(
 		type = VNON;
 		if (stat == NULL) {
 			stat = &tmp_stat;
-			error = sfnode_get_stat(dir->sf_sffs->sf_handle,
+			error = sfprov_get_attr(dir->sf_sffs->sf_handle,
 			    fullpath, stat);
 			stat_time = sfnode_cur_time_usec();
 		} else {

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

Add symlink support.
    
Note that renaming symlinks currently only works if the symlink points to an
existing object - this is an issue with the underlying vboxCallRename().

diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
index d37c41d..52da908 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -119,6 +119,18 @@ sfprov_disconnect(sfp_connection_t *conn)
 	vboxUninit();
 }
 
+int
+sfprov_set_show_symlinks(void)
+{
+	int rc;
+
+	rc = vboxCallSetSymlinks(&vbox_client);
+	if (RT_FAILURE(rc))
+		return (sfprov_vbox2errno(rc));
+
+	return (0);
+}
+
 
 /*
  * representation of an active mount point
@@ -790,14 +802,15 @@ sfprov_mkdir(sfp_mount_t *mnt, char *path, sfp_file_t **fp)
 }
 
 int
-sfprov_remove(sfp_mount_t *mnt, char *path)
+sfprov_remove(sfp_mount_t *mnt, char *path, uint_t is_link)
 {
 	int rc;
 	SHFLSTRING *str;
 	int size;
 
 	str = sfprov_string(path, &size);
-	rc = vboxCallRemove(&vbox_client, &mnt->map, str, SHFL_REMOVE_FILE);
+	rc = vboxCallRemove(&vbox_client, &mnt->map, str,
+	    SHFL_REMOVE_FILE | (is_link ? SHFL_REMOVE_SYMLINK : 0));
 	kmem_free(str, size);
 	if (RT_FAILURE(rc))
 		return (sfprov_vbox2errno(rc));
@@ -995,3 +1008,56 @@ done:
 	sfprov_close(fp);
 	return (error);
 }
+
+int
+sfprov_readlink(
+	sfp_mount_t *mnt,
+	char *path,
+	char *target,
+	size_t tgt_size)
+{
+	int rc;
+	SHFLSTRING *str;
+	int size;
+
+	str = sfprov_string(path, &size);
+
+	rc = vboxReadLink(&vbox_client, &mnt->map, str, (uint32_t) tgt_size,
+	    target);
+	if (RT_FAILURE(rc))
+		rc = sfprov_vbox2errno(rc);
+
+	kmem_free(str, size);
+	return (rc);
+}
+
+int
+sfprov_symlink(
+	sfp_mount_t *mnt,
+	char *linkname,
+	char *target,
+	sffs_stat_t *stat)
+{
+	int rc;
+	SHFLSTRING *lnk, *tgt;
+	int lnk_size, tgt_size;
+	SHFLFSOBJINFO info;
+
+	lnk = sfprov_string(linkname, &lnk_size);
+	tgt = sfprov_string(target, &tgt_size);
+
+	rc = vboxCallSymlink(&vbox_client, &mnt->map, lnk, tgt, &info);
+	if (RT_FAILURE(rc)) {
+		rc = sfprov_vbox2errno(rc);
+		goto done;
+	}
+
+	if (stat != NULL)
+		sfprov_stat_from_info(stat, &info);
+
+done:
+	kmem_free(lnk, lnk_size);
+	kmem_free(tgt, tgt_size);
+
+	return (rc);
+}
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
index ed37d79..e073ddb 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -121,7 +121,7 @@ extern int sfprov_set_size(sfp_mount_t *, char *, uint64_t);
  * File/Directory operations
  */
 extern int sfprov_trunc(sfp_mount_t *, char *);
-extern int sfprov_remove(sfp_mount_t *, char *path);
+extern int sfprov_remove(sfp_mount_t *, char *path, uint_t is_link);
 extern int sfprov_mkdir(sfp_mount_t *, char *path, sfp_file_t **fp);
 extern int sfprov_rmdir(sfp_mount_t *, char *path);
 extern int sfprov_rename(sfp_mount_t *, char *from, char *to, uint_t is_dir);
@@ -148,6 +148,16 @@ typedef struct sffs_dirents {
 extern int sfprov_readdir(sfp_mount_t *mnt, char *path,
     sffs_dirents_t **dirents);
 
+
+/*
+ * Symbolic link operations
+ */
+extern int sfprov_set_show_symlinks(void);
+extern int sfprov_readlink(sfp_mount_t *, char *path, char *target,
+    size_t tgt_size);
+extern int sfprov_symlink(sfp_mount_t *, char *linkname, char *target,
+    sffs_stat_t *stat);
+
 #ifdef	__cplusplus
 }
 #endif
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
index a72cb60..198131c 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
@@ -186,6 +186,12 @@ sffs_init(int fstype, char *name)
 		return (ENODEV);
 	}
 
+	error = sfprov_set_show_symlinks();
+	if (error != 0) {
+		cmn_err(CE_WARN, "sffs_init: Host unable to show symlinks, "
+		    "rc=%d", error);
+	}
+
 	error = vfs_setfsops(fstype, sffs_vfsops_template, NULL);
 	if (error != 0) {
 		cmn_err(CE_WARN, "sffs_init: bad vfs ops template");
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
index 036022d..b7c5b44 100644
--- a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -603,6 +603,8 @@ sfnode_lookup(
 			type = VDIR;
 		else if (S_ISREG(m))
 			type = VREG;
+		else if (S_ISLNK(m))
+			type = VLNK;
 	}
 
 	if (err)
@@ -1471,6 +1473,153 @@ done:
 
 /*ARGSUSED*/
 static int
+sffs_readlink(
+	vnode_t		*vp,
+	uio_t		*uiop,
+	cred_t		*cred
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+	,
+	caller_context_t *ct
+#endif
+	)
+{
+	sfnode_t	*node;
+	int		error = 0;
+	char		*target = NULL;
+
+	if (uiop->uio_iovcnt != 1)
+		return (EINVAL);
+
+	if (vp->v_type != VLNK)
+		return (EINVAL);
+
+	mutex_enter(&sffs_lock);
+	node = VN2SFN(vp);
+
+	target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+	error = sfprov_readlink(node->sf_sffs->sf_handle, node->sf_path, target,
+	    MAXPATHLEN);
+	if (error)
+		goto done;
+
+	error = uiomove(target, strlen(target), UIO_READ, uiop);
+
+done:
+	mutex_exit(&sffs_lock);
+	if (target)
+		kmem_free(target, MAXPATHLEN);
+	return (error);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_symlink(
+	vnode_t		*dvp,
+	char		*linkname,
+	vattr_t		*vap,
+	char		*target,
+	cred_t		*cred
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+	,
+	caller_context_t *ct,
+	int		flags
+#endif
+	)
+{
+	sfnode_t	 *dir;
+	sfnode_t	 *node;
+	sffs_stat_t	 stat;
+	int		 error = 0;
+	char		 *fullpath;
+
+	/*
+	 * These should never happen
+	 */
+	ASSERT(linkname != NULL);
+	ASSERT(strcmp(linkname, "") != 0);
+	ASSERT(strcmp(linkname, ".") != 0);
+	ASSERT(strcmp(linkname, "..") != 0);
+
+	/*
+	 * Basic checks.
+	 */
+	if (vap->va_type != VLNK)
+		return (EINVAL);
+
+	mutex_enter(&sffs_lock);
+
+	if (sfnode_lookup(VN2SFN(dvp), linkname, VNON, NULL, 0, NULL) != NULL) {
+		error = EEXIST;
+		goto done;
+	}
+
+	dir = VN2SFN(dvp);
+	error = sfnode_access(dir, VWRITE, cred);
+	if (error)
+		goto done;
+
+	/*
+	 * Create symlink. Note that we ignore vap->va_mode because generally
+	 * we can't change the attributes of the symlink itself.
+	 */
+	fullpath = sfnode_construct_path(dir, linkname);
+	error = sfprov_symlink(dir->sf_sffs->sf_handle, fullpath, target,
+	    &stat);
+	kmem_free(fullpath, strlen(fullpath) + 1);
+	if (error)
+		goto done;
+
+	node = sfnode_lookup(dir, linkname, VLNK, &stat, sfnode_cur_time_usec(),
+	    NULL);
+
+	sfnode_invalidate_stat_cache(dir);
+	sfnode_clear_dir_list(dir);
+
+done:
+	mutex_exit(&sffs_lock);
+	return (error);
+}
+
+static sfnode_t *
+sfnode_resolve_link(sfnode_t *node, int *err)
+{
+	int	error = 0;
+	char	*target = NULL;
+
+	ASSERT(MUTEX_HELD(&sffs_lock));
+
+	if (err == NULL)
+		err = &error;
+
+	target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+	/*
+	 * Follow symlinks until we reach a non-symlink.
+	 */
+	while (node->sf_type == VLNK) {
+		*err = sfprov_readlink(node->sf_sffs->sf_handle, node->sf_path,
+		    target, MAXPATHLEN);
+		if (*err) {
+			node = NULL;
+			goto done;
+		}
+
+		ASSERT(node->sf_parent != NULL);
+		node = sfnode_lookup(node->sf_parent, target, VNON, NULL, 0, err);
+		if (node == NULL)
+			goto done;
+	}
+
+done:
+	if (target)
+		kmem_free(target, MAXPATHLEN);
+	return (node);
+}
+
+/*ARGSUSED*/
+static int
 sffs_remove(
 	vnode_t		*dvp,
 	char		*name,
@@ -1520,7 +1669,8 @@ sffs_remove(
 	 */
 	sfnode_invalidate_stat_cache(VN2SFN(dvp));
 
-	error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
+	error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path,
+	    node->sf_type == VLNK);
 	if (error == ENOENT || error == 0)
 		sfnode_make_stale(node);
 
@@ -1546,6 +1696,7 @@ sffs_rename(
 	char		*newpath;
 	int		error;
 	sfnode_t	*node;
+	sfnode_t	*res_node;
 
 	if (strcmp(new_nm, "") == 0 ||
 	    strcmp(new_nm, ".") == 0 ||
@@ -1579,8 +1730,21 @@ sffs_rename(
 	sfnode_invalidate_stat_cache(VN2SFN(new_dir));
 
 	newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
+
+	/*
+	 * Hack because vboxCallRename doesn't support renaming symlinks
+	 * properly. By getting node type of what the symlink points to we
+	 * can work around things, but this still fails if the symlink points
+	 * to a non-existent object (with ENOENT).
+	 *
+	 * Remove sfnode_resolve_link when vboxCallRename is fixed.
+	 */
+	res_node = sfnode_resolve_link(node, &error);
+	if (res_node == NULL)
+		res_node = node;
+
 	error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
-	    node->sf_type == VDIR);
+	    res_node->sf_type == VDIR);
 	if (error == 0)
 		sfnode_rename(node, VN2SFN(new_dir), newpath);
 	else {
@@ -1785,12 +1949,14 @@ const fs_operation_def_t sffs_ops_template[] = {
 	VOPNAME_PATHCONF,	sffs_pathconf,
 	VOPNAME_READ,		sffs_read,
 	VOPNAME_READDIR,	sffs_readdir,
+	VOPNAME_READLINK,	sffs_readlink,
 	VOPNAME_REMOVE,		sffs_remove,
 	VOPNAME_RENAME,		sffs_rename,
 	VOPNAME_RMDIR,		sffs_rmdir,
 	VOPNAME_SEEK,		sffs_seek,
 	VOPNAME_SETATTR,	sffs_setattr,
 	VOPNAME_SPACE,		sffs_space,
+	VOPNAME_SYMLINK,	sffs_symlink,
 	VOPNAME_WRITE,		sffs_write,
 	NULL,			NULL
 #else
@@ -1807,12 +1973,14 @@ const fs_operation_def_t sffs_ops_template[] = {
 	VOPNAME_PATHCONF,	{ .vop_pathconf = sffs_pathconf },
 	VOPNAME_READ,		{ .vop_read = sffs_read },
 	VOPNAME_READDIR,	{ .vop_readdir = sffs_readdir },
+	VOPNAME_READLINK,	{ .vop_readlink = sffs_readlink },
 	VOPNAME_REMOVE,		{ .vop_remove = sffs_remove },
 	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_SYMLINK,	{ .vop_symlink = sffs_symlink },
 	VOPNAME_WRITE,		{ .vop_write = sffs_write },
 	NULL,			NULL
 #endif
@@ -1914,6 +2082,7 @@ sfnode_print(sfnode_t *node)
 	Log((" type=%s (%d)",
 	    node->sf_type == VDIR ? "VDIR" :
 	    node->sf_type == VNON ? "VNON" :
+	    node->sf_type == VLNK ? "VLNK" :
 	    node->sf_type == VREG ? "VREG" : "other", node->sf_type));
 	Log((" ino=%d", (uint_t)node->sf_ino));
 	Log((" path=%s", node->sf_path));

diff --git a/doc/manual/en_US/user_GuestAdditions.xml b/doc/manual/en_US/user_GuestAdditions.xml
index c0e4180..fd3b135 100644
--- a/doc/manual/en_US/user_GuestAdditions.xml
+++ b/doc/manual/en_US/user_GuestAdditions.xml
@@ -1026,7 +1026,7 @@ EndSection</screen>
         </listitem>
 
         <listitem>
-          <para>Currently only Linux Guest Additions support symlinks.</para>
+          <para>Currently only Linux and Solaris Guest Additions support symlinks.</para>
         </listitem>
       </orderedlist></para>
 


More information about the vbox-dev mailing list