+2009-01-21 Marcelo Vanzin <mvanzin@vmware.com>
+ * Resync with internal trunk (2009.01.19)
+
+ * configure.ac: detect the presence of FUSE libraries.
+
+ * configure.ac, Makefile.am: compile kernel modules for Solaris, and
+ vmblock-fuse module if FUSE is available.
+
+ * lib/ghIntegration/ghIntegrationX11.c: retrieved localized application
+ names.
+
+ * lib/guestInfo/guestInfo.c, lib/guestInfo/guestInfoPosix.c,
+ lib/include/hostinfo.h, lib/misc/hostinfo_misc.c, lib/misc/hostinfoPosic.c,
+ lib/vixTools/vixTools.c: refactoring to move code shared with other VMware
+ products to a common library.
+
+ * lib/guestRpc/guestlibV3.x, lib/guestRpc/Makefile.am,
+ lib/include/vmGuestLib.h, libguestlib/*: add new iteration of the
+ "guestlib" protocol. This is a more extensible solution than the current
+ protocol, and should make it easier to add new information when needed.
+
+ * lib/include/dynarray.h, lib/include/dynbuf.h, lib/misc/dynarray.c,
+ lib/misc/dynbuf.c: make some DynArray/DynBuf functions inline for speed.
+
+ * lib/include/unityCommon.h: more documentation about the Unity protocol.
+
+ * lib/unity/unityPlatformX11.c: fix Unity<->guest desktop ID mappings.
+
+ * libvmtools/vmtoolsLog.c: change the way log domains are configured; now
+ sub-domains inherit the default handler for the app if no handler is
+ specified.
+
+ * modules/freebsd/vmhgfs/state.c, modules/freebsd/vmhgfs/state.h,
+ modules/freebsd/vmhgfs/vnopscommon.{c,h}: implement support for mmap on
+ the Mac OS driver, which allows running executables from an HGFS
+ share. The FreeBSD module still does not support this functionality.
+
+ * modules/freebsd/vmhgfs/transport.{c,h}: refactoring for sharing structure
+ definitions.
+
+ * modules/linux/pvscsi/pvscsi.c, modules/linux/vmblock/linux/module.c,
+ modules/linux/vmci/vmci_drv.c, modules/linux/hgfs/module.c,
+ modules/linux/vmmemctl/os.c, modules/linux/vmsync/sync.c,
+ modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet3/vmxnet3.c: add support
+ for Novell's proprietary module info tag ("supported").
+
+ * modules/linux/vmci/vmciKernelIf.c: add support for VMCI queue pairs on
+ the host. This does not affect the driver when it runs inside virtual
+ machines.
+
+ * modules/linux/vmci/*.h, modules/linux/vsock/*.h: some changes in the common
+ code to support Mac OS X, and also queue pairs on the Solaris VMCI module.
+
+ * modules/linux/vsock/af_vsock.{c,h}: add functions for registering with
+ the vsock driver from within the kernel.
+
+ * modules/linux/*/Makefile: add $(LINUXINCLUDE) to the compiler flags; this
+ allows compiling the modules against recent kernels which removed that
+ from $(KBUILD_CPPFLAGS).
+
+ * modules/Makefile.am: add support for compiling Solaris kernel modules.
+
+ * modules/solaris/vmhgfs/*: initial release of the Solaris HGFS driver in
+ open-vm-tools. Driver is licensed under the CDDL 1.0.
+
+ * vmblock-fuse/*: add the user-level implementation of the vmblock driver,
+ which is build on top of FUSE, to open-vm-tools. vmware-user hasn't yet
+ been modified to use this version of vmblock.
+
+ * vmware-user/copyPaseV3.h: new header introduced during development of the
+ next version of copy paste / DnD for X11 platforms.
+
+
2008-11-23 Marcelo Vanzin <mvanzin@vmware.com>
* Resync with internal trunk (2008.12.19)
if ENABLE_CORE_SERVICES
SUBDIRS += libvmtools
endif
+if HAVE_FUSE
+ SUBDIRS += vmblock-fuse
+endif
SUBDIRS += xferlogs
SUBDIRS += scripts
SUBDIRS += toolbox
-if !SOLARIS
if WITH_KERNEL_MODULES
-SUBDIRS += modules
-endif
+ SUBDIRS += modules
endif
-open-vm-tools 2009.12.23 changes:
+open-vm-tools 2009.01.21 changes:
+ * First open source release of the HGFS module for Solaris, under the CDDL.
+ Other modules are expected to be added in the upcoming releases.
+
+ * Added an implementation of vmblock on top of FUSE; vmware-user still
+ doesn't use this module even if it is available, though.
+
+ * Linux modules now add the "supported" tag used by Novell in their
+ SLES 10 SP2 release when loading modules.
+
+ * Fix compilation of modules in newer Linux kernels which don't include
+ $(LINUXINCLUDE) in the compiler flags anymore.
+
+open-vm-tools 2008.12.23 changes:
* Lots of makefile cleanup with the switch to using libtool archives.
* Automatically disable Unity if multimon support is disabled.
# In addition to the usual environment variables and command line
# arguments that a configure script usually takes (CFLAGS, LDFLAGS,
# etc.), this script also accepts:
+# $CUSTOM_FUSE_CPPFLAGS - compile-time flags to find FUSE headers.
+# $CUSTOM_FUSE_LIBS - link-time flags to find FUSE libraries.
# $CUSTOM_GLIB2_CPPFLAGS - compile-time flags to find Glib headers.
# $CUSTOM_GLIB2_LIBS - link-time flags to find Glib libraries.
# $CUSTOM_GTK_CPPFLAGS - compile-time flags to find Gtk headers.
AC_INIT(
[open-vm-tools],
- [2008.12.23-137496],
+ [2009.01.21-142982],
[open-vm-tools-devel@lists.sourceforge.net])
# In order to make this configure script auto-detect situations where
fi
fi
+#
+# Check for fuse.
+#
+if test "$HAVE_PKG_CONFIG" = "yes"; then
+ AC_MSG_CHECKING([for fuse (via pkg-config)])
+ if pkg-config --exists 'fuse'; then
+ FUSE_CPPFLAGS="`pkg-config --cflags fuse`"
+ FUSE_LIBS="`pkg-config --libs fuse`"
+ have_fuse="yes"
+ AC_MSG_RESULT([yes])
+ else
+ have_fuse="no"
+ AC_MSG_RESULT([no])
+ fi
+else
+ ORIGINAL_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$CUSTOM_FUSE_LIBS $LDFLAGS"
+ AC_CHECK_LIB(
+ [fuse],
+ [fuse_main],
+ [have_fuse="yes";
+ if test -n "$CUSTOM_FUSE_LIBS"; then
+ FUSE_LIBS="$CUSTOM_FUSE_LIBS"
+ else
+ FUSE_LIBS="-lfuse"
+ fi],
+ [have_fuse="no"; AC_MSG_WARN([Fuse is missing, vmblock-fuse will be disabled.])])
+ LDFLAGS="$ORIGINAL_LDFLAGS"
+
+ if test "$have_fuse" = "yes"; then
+ ORIGINAL_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CUSTOM_FUSE_CPPFLAGS $CPPFLAGS"
+ AC_CHECK_HEADER(
+ [fuse.h],
+ [if test -n "$CUSTOM_FUSE_CPPFLAGS"; then
+ FUSE_CPPFLAGS="$CUSTOM_FUSE_CPPFLAGS"
+ else
+ FUSE_CPPFLAGS="-I/usr/include"
+ fi],
+ [have_fuse="no"; AC_MSG_WARN([Can't locate fuse headers, vmblock-fuse will be disabled.])])
+ CPPFLAGS="$ORIGINAL_CPPFLAGS"
+ fi
+fi
# If the user explicitly disables X11, then don't try to detect the X-related libraries
if test "$have_x" = "disabled"; then
# uname -r on Solaris 10 reports "5.10"
if test "$osVersion" -ge 510000; then
CPPFLAGS="$CPPFLAGS -DSOL10"
+ MODULES="$MODULES vmhgfs"
fi
fi
AM_CONDITIONAL(ENABLE_UNITY, test "$enable_unity" = "yes")
AM_CONDITIONAL(WITH_ROOT_PRIVILEGES, test "$with_root_privileges" = "yes")
AM_CONDITIONAL(ENABLE_CORE_SERVICES, test "$have_glib_26" = "yes")
+AM_CONDITIONAL(HAVE_FUSE, test "$have_fuse" = "yes")
+AM_CONDITIONAL(HAVE_GNU_LD, test "$with_gnu_ld" = "yes")
### Feature-specific flags / actions
# Combine where possible
AC_SUBST([VMTOOLS_LIBS])
AC_SUBST([ZLIB_CPPFLAGS])
AC_SUBST([ZLIB_LIBS])
+AC_SUBST([FUSE_CPPFLAGS])
+AC_SUBST([FUSE_LIBS])
### Lib substs
libvmtools/Makefile \
xferlogs/Makefile \
modules/Makefile \
+ vmblock-fuse/Makefile \
scripts/Makefile \
])
# include <windows.h>
# include "win32u.h"
# include "hgfsUsabilityLib.h"
+# include "rescaps.h"
# include "ServiceHelpers.h"
#endif
*/
if (status || data->lastFailedStateChg == data->stateChgInProgress) {
status = TRUE;
+#ifdef _WIN32
+ if (data->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT || data->stateChgInProgress == GUESTOS_STATECHANGE_HALT) {
+ if (System_GetOSType() >= OS_VISTA) {
+ DISABLE_RES_CAPS();
+ }
+ }
+#endif
if (data->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT) {
Debug("Initiating reboot\n");
status = data->rebootCB(data->rebootCBData);
}
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileIO_GetAllocSize --
+ *
+ * Get allocated size of file.
+ *
+ * Results:
+ * Size of file or -1.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int64
+FileIO_GetAllocSize(const FileIODescriptor *fd) // IN
+{
+ struct stat s;
+
+ ASSERT(fd);
+
+#if __linux__ && defined(N_PLAT_NLM)
+ /* Netware doesn't have st_blocks. Just fall back to GetSize. */
+ return FileIO_GetSize(fd);
+#else
+ /*
+ * If you don't like the magic number 512, yell at the people
+ * who wrote sys/stat.h and tell them to add a #define for it.
+ */
+ return (fstat(fd->posix, &s) == -1) ? -1 : s.st_blocks * 512;
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileIO_SetAllocSize --
+ *
+ * Set allocated size of file, allocating new blocks if needed.
+ * It is an error for size to be less than the current size.
+ *
+ * Results:
+ * TRUE on success. Sets errno on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+Bool
+FileIO_SetAllocSize(const FileIODescriptor *fd, // IN
+ uint64 size) // IN
+{
+#ifdef __APPLE__
+ fstore_t prealloc;
+ uint64 curSize;
+
+ curSize = FileIO_GetAllocSize(fd);
+
+ if (curSize > size) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ prealloc.fst_flags = 0;
+ prealloc.fst_posmode = F_PEOFPOSMODE;
+ prealloc.fst_offset = 0;
+ prealloc.fst_length = size - curSize;
+ prealloc.fst_bytesalloc = 0;
+
+ return fcntl(fd->posix, F_PREALLOCATE, &prealloc) != -1;
+#else
+ errno = EINVAL;
+ return FALSE;
+#endif
+}
+
+
/*
*----------------------------------------------------------------------
*
*
* FileGetStats --
*
- * Calls statfs on a full path (eg. something returned from File_FullPath)
+ * Calls statfs on a full path (eg. something returned from File_FullPath).
+ * If doNotAscend is FALSE, climb up the directory chain and call statfs
+ * on each level until it succeeds.
*
* Results:
* TRUE statfs succeeded
static Bool
FileGetStats(ConstUnicode pathName, // IN:
+ Bool doNotAscend, // IN:
struct statfs *pstatfsbuf) // OUT:
{
Bool retval = TRUE;
while (Posix_Statfs(dupPath ? dupPath : pathName,
pstatfsbuf) == -1) {
- if (errno != ENOENT) {
+ if (errno != ENOENT || doNotAscend) {
retval = FALSE;
break;
}
* File_GetFreeSpace --
*
* Return the free space (in bytes) available to the user on a disk where
- * a file is or would be
+ * a file is or would be. If doNotAscend is FALSE, the helper function
+ * ascends the directory chain on system call errors in order to obtain
+ * the file system information.
*
* Results:
* -1 if error (reported to the user)
*/
uint64
-File_GetFreeSpace(ConstUnicode pathName) // IN: File name
+File_GetFreeSpace(ConstUnicode pathName, // IN: File name
+ Bool doNotAscend) // IN: Do not ascend dir chain
{
uint64 ret;
Unicode fullPath;
goto end;
}
- if (!FileGetStats(fullPath, &statfsbuf)) {
- Warning("%s: Couldn't statfs\n", __func__);
+ if (!FileGetStats(fullPath, doNotAscend, &statfsbuf)) {
+ Warning("%s: Couldn't statfs %s\n", __func__, fullPath);
ret = -1;
goto end;
}
goto end;
}
- err = FileGetStats(fullPath, &statfsbuf);
+ err = FileGetStats(fullPath, FALSE, &statfsbuf);
Unicode_Free(fullPath);
goto end;
}
- if (!FileGetStats(fullPath, &statfsbuf)) {
+ if (!FileGetStats(fullPath, FALSE, &statfsbuf)) {
Warning(LGPFX" %s: Couldn't statfs\n", __func__);
ret = -1;
goto end;
#include <sys/stat.h>
#include <unistd.h>
+#ifndef GTK2
+#error "Gtk 2.0 is required"
+#endif
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixbuf-core.h>
+
+// gdkx.h includes Xlib.h, which #defines Bool.
+#include <gdk/gdkx.h>
+#undef Bool
+
#include "vmware.h"
#include "base64.h"
#include "rpcin.h"
(((textrange).afterLast - (textrange).first) == strlen((str)) \
&& !strncmp((textrange).first, (str), (textrange).afterLast - (textrange).first))
-#ifndef GTK2
-#error "Gtk 2.0 is required"
-#endif
-
#include "appUtil.h"
-#include <glib.h>
-#include <gdk-pixbuf/gdk-pixbuf-core.h>
-#include <gdk/gdkx.h>
-
/*
* The following defines appear in newer versions of glib 2.x, so
* we define them for backwards compat.
* Stick the app name into 'buf'.
*/
if (ghm) {
- ctmp = g_key_file_get_string(ghm->keyfile, G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NAME, NULL);
+ ctmp = g_key_file_get_locale_string(ghm->keyfile, G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
if (!ctmp) {
ctmp = g_path_get_basename(realCmd);
}
gmi = g_ptr_array_index(gmh->gmd->items, itemIndex);
- /*
- * XXX I should get_locale_string here for the localizedItemName, if there's a
- * way to do it in the guest user's current language.
- */
- localizedItemName = g_key_file_get_string(gmi->keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NAME,
- NULL);
+ localizedItemName = g_key_file_get_locale_string(gmi->keyfile,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_NAME,
+ NULL, NULL);
freeLocalItemName = TRUE;
itemName = g_strdup_printf("%s/%s/%s", UNITY_START_MENU_LAUNCH_FOLDER,
gmh->gmd->dirname, localizedItemName);
switch (thisarg[1]) {
case 'c': // %c expands to the .desktop's Name=
{
- // XXX Should use the translated version
char *ctmp =
- g_key_file_get_string(ghm->keyfile,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NAME,
- NULL);
+ g_key_file_get_locale_string(ghm->keyfile,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_NAME,
+ NULL, NULL);
if (ctmp) {
g_ptr_array_add(fullargs, ctmp);
}
}
-#if defined(N_PLAT_NLM)
-/*
- *----------------------------------------------------------------------------
- *
- * GuestInfo_GetSystemBitness --
- *
- * Determines the operating system's bitness.
- *
- * Return value:
- * 32 or 64 on success, negative value on failure. Check errno for more
- * details of error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-GuestInfo_GetSystemBitness(void)
-{
- return 32;
-}
-#endif // defined(N_PLAT_NLM)
-
-
/**
* Add a NIC into the given list. The macAddress of the new GuestNic is
* initialized with the given address.
#include "sys/utsname.h"
#include "sys/ioctl.h"
#include "vmware.h"
+#include "hostinfo.h"
#include "guestInfoInt.h"
#include "debug.h"
#include "str.h"
#define DISTRO_BUF_SIZE 255
-#define SYSINFO_STRING_32 "i386"
-#define SYSINFO_STRING_64 "amd64"
-#define MAX_ARCH_NAME_LEN sizeof SYSINFO_STRING_32 > sizeof SYSINFO_STRING_64 ? \
- sizeof SYSINFO_STRING_32 : \
- sizeof SYSINFO_STRING_64
-
typedef struct lsb_distro_info {
char *name;
char *scanstring;
}
}
- if (GuestInfo_GetSystemBitness() == 64) {
+ if (Hostinfo_GetSystemBitness() == 64) {
if (strlen(osName) + sizeof STR_OS_64BIT_SUFFIX > outBufLen) {
Debug("GuestInfoGetOSName: Error: buffer too small\n");
return FALSE;
return TRUE;
}
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * GuestInfo_GetSystemBitness --
- *
- * Determines the operating system's bitness.
- *
- * Return value:
- * 32 or 64 on success, negative value on failure. Check errno for more
- * details of error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-GuestInfo_GetSystemBitness(void)
-{
- char buf[MAX_ARCH_NAME_LEN] = { 0 };
-#if defined(__FreeBSD__) || defined(__APPLE__)
- int mib[2];
- size_t len;
-
- len = sizeof buf;
- mib[0] = CTL_HW;
- mib[1] = HW_MACHINE;
-
- if (sysctl(mib, ARRAYSIZE(mib), buf, &len, NULL, 0) < 0) {
- return -1;
- }
-#elif defined(sun)
-# if !defined(SOL10)
- /*
- * XXX: This is bad. We define SI_ARCHITECTURE_K to what it is on Solaris
- * 10 so that we can use a single guestd build for Solaris 9 and 10. In the
- * future we should have the Solaris 9 build just return 32 -- since it did
- * not support 64-bit x86 -- and let the Solaris 10 headers define
- * SI_ARCHITECTURE_K, then have the installer symlink to the correct binary.
- * For now, though, we'll share a single build for both versions.
- */
-# define SI_ARCHITECTURE_K 518
-# endif
- if (sysinfo(SI_ARCHITECTURE_K, buf, sizeof buf) < 0) {
- return -1;
- }
-#elif defined(linux)
- struct utsname u;
-
- if (uname(&u) < 0) {
- return -1;
- }
- if (strstr(u.machine, "x86_64")) {
- return 64;
- } else {
- return 32;
- }
-#endif
-
- if (strcmp(buf, SYSINFO_STRING_32) == 0) {
- return 32;
- } else if (strcmp(buf, SYSINFO_STRING_64) == 0) {
- return 64;
- }
-
- return -1;
-}
libGuestRpc_la_SOURCES =
libGuestRpc_la_SOURCES += ghiGetBinaryHandlers_xdr.c
libGuestRpc_la_SOURCES += ghiProtocolHandler_xdr.c
+libGuestRpc_la_SOURCES += guestlibV3_xdr.c
libGuestRpc_la_SOURCES += nicinfo_xdr.c
libGuestRpc_la_SOURCES += unityActive_xdr.c
CLEANFILES += ghiGetBinaryHandlers_xdr.c
CLEANFILES += ghiProtocolHandler.h
CLEANFILES += ghiProtocolHandler_xdr.c
+CLEANFILES += guestlibV3.h
+CLEANFILES += guestlibV3_xdr.c
CLEANFILES += nicinfo.h
CLEANFILES += nicinfo_xdr.c
CLEANFILES += unityActive.h
EXTRA_DIST =
EXTRA_DIST += ghiGetBinaryHandlers.x
EXTRA_DIST += ghiProtocolHandler.x
+EXTRA_DIST += guestlibV3.x
EXTRA_DIST += nicinfo.x
EXTRA_DIST += unityActive.x
if [ $(top_builddir) != $(top_srcdir) ]; then \
cp $(top_srcdir)/lib/guestRpc/nicinfo.x $(builddir); \
fi
- $(RPCGEN) -h -o $@ nicinfo.x
+ $(RPCGEN) -h -o $@ $<
+ $(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp
+ mv $@.tmp $@
+ $(MKDIR_P) $(top_builddir)/lib/include/guestrpc
+ cp $@ $(top_builddir)/lib/include/guestrpc
+
+guestlibV3.h: guestlibV3.x
+ if [ $(top_builddir) != $(top_srcdir) ]; then \
+ cp $(top_srcdir)/lib/guestRpc/guestlibV3.x $(builddir); \
+ fi
+ $(RPCGEN) -h -o $@ $<
$(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp
mv $@.tmp $@
$(MKDIR_P) $(top_builddir)/lib/include/guestrpc
nicinfo_xdr.c: nicinfo.x nicinfo.h
$(RPCGEN) -c -o $@ nicinfo.x
+guestlibV3_xdr.c: guestlibV3.x guestlibV3.h
+ $(RPCGEN) -c -o $@ guestlibV3.x
+
unityActive.h: unityActive.x
if [ $(top_builddir) != $(top_srcdir) ]; then \
cp $(top_srcdir)/lib/guestRpc/unityActive.x $(builddir); \
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+
+/*
+ * guestlibV3.x --
+ *
+ * Data structures that encode the information sent from VMX to Guest upon a
+ * Guestlib protocol v3 request.
+ */
+
+struct GuestLibV3StatUint32 {
+ Bool valid;
+ uint32 value;
+};
+
+struct GuestLibV3StatUint64 {
+ Bool valid;
+ uint64 value;
+};
+
+struct GuestLibV3String {
+ Bool valid;
+ string value<512>;
+};
+
+typedef uint32 GuestLibV3StatCount;
+
+enum GuestLibV3TypeIds {
+ /* V2 statistics */
+ GUESTLIB_TYPE_RESERVED = 0,
+ GUESTLIB_CPU_RESERVATION_MHZ = 1,
+ GUESTLIB_CPU_LIMIT_MHZ = 2,
+ GUESTLIB_CPU_SHARES = 3,
+ GUESTLIB_CPU_USED_MS = 4,
+
+ GUESTLIB_HOST_MHZ = 5,
+
+ GUESTLIB_MEM_RESERVATION_MB = 6,
+ GUESTLIB_MEM_LIMIT_MB = 7,
+ GUESTLIB_MEM_SHARES = 8,
+ GUESTLIB_MEM_MAPPED_MB = 9,
+ GUESTLIB_MEM_ACTIVE_MB = 10,
+ GUESTLIB_MEM_OVERHEAD_MB = 11,
+ GUESTLIB_MEM_BALLOONED_MB = 12,
+ GUESTLIB_MEM_SWAPPED_MB = 13,
+ GUESTLIB_MEM_SHARED_MB = 14,
+ GUESTLIB_MEM_SHARED_SAVED_MB = 15,
+ GUESTLIB_MEM_USED_MB = 16,
+
+ GUESTLIB_ELAPSED_MS = 17,
+ GUESTLIB_RESOURCE_POOL_PATH = 18,
+
+ /*------ Add any new statistics above this line. ------- */
+
+ /*------ Bump this when adding to this list. -------*/
+ GUESTLIB_MAX_STATISTIC_ID = 19
+};
+
+union GuestLibV3Stat switch (GuestLibV3TypeIds d) {
+ case GUESTLIB_CPU_RESERVATION_MHZ:
+ struct GuestLibV3StatUint32 cpuReservationMHz;
+ case GUESTLIB_CPU_LIMIT_MHZ:
+ struct GuestLibV3StatUint32 cpuLimitMHz;
+ case GUESTLIB_CPU_SHARES:
+ struct GuestLibV3StatUint32 cpuShares;
+ case GUESTLIB_CPU_USED_MS:
+ struct GuestLibV3StatUint64 cpuUsedMs;
+
+ case GUESTLIB_HOST_MHZ:
+ struct GuestLibV3StatUint32 hostMHz;
+
+ case GUESTLIB_MEM_RESERVATION_MB:
+ struct GuestLibV3StatUint32 memReservationMB;
+ case GUESTLIB_MEM_LIMIT_MB:
+ struct GuestLibV3StatUint32 memLimitMB;
+ case GUESTLIB_MEM_SHARES:
+ struct GuestLibV3StatUint32 memShares;
+ case GUESTLIB_MEM_MAPPED_MB:
+ struct GuestLibV3StatUint32 memMappedMB;
+ case GUESTLIB_MEM_ACTIVE_MB:
+ struct GuestLibV3StatUint32 memActiveMB;
+ case GUESTLIB_MEM_OVERHEAD_MB:
+ struct GuestLibV3StatUint32 memOverheadMB;
+ case GUESTLIB_MEM_BALLOONED_MB:
+ struct GuestLibV3StatUint32 memBalloonedMB;
+ case GUESTLIB_MEM_SWAPPED_MB:
+ struct GuestLibV3StatUint32 memSwappedMB;
+ case GUESTLIB_MEM_SHARED_MB:
+ struct GuestLibV3StatUint32 memSharedMB;
+ case GUESTLIB_MEM_SHARED_SAVED_MB:
+ struct GuestLibV3StatUint32 memSharedSavedMB;
+ case GUESTLIB_MEM_USED_MB:
+ struct GuestLibV3StatUint32 memUsedMB;
+
+ case GUESTLIB_ELAPSED_MS:
+ struct GuestLibV3StatUint64 elapsedMs;
+
+ case GUESTLIB_RESOURCE_POOL_PATH:
+ struct GuestLibV3String resourcePoolPath;
+};
+
#define BUILD_NUMBER \
- "build-137496"
+ "build-142982"
#define BUILD_NUMBER_NUMERIC \
- 137496
+ 142982
#define BUILD_NUMBER_NUMERIC_STRING \
- "137496"
+ "142982"
#define PRODUCT_BUILD_NUMBER \
- "product-build-416"
+ "product-build-444"
#define PRODUCT_BUILD_NUMBER_NUMERIC \
- 416
+ 444
#define PRODUCT_BUILD_NUMBER_NUMERIC_STRING \
- "416"
+ "444"
CPFORMAT_RTF,
CPFORMAT_FILELIST_URI,
CPFORMAT_FILECONTENTS,
+ CPFORMAT_IMG_PNG,
CPFORMAT_MAX,
} DND_CPFORMAT;
utf16_t *bufIn);
EXTERN Bool DnD_SetCPClipboardFromLocalRtf(CPClipboard *clip,
char *bufIn);
+EXTERN Bool DnD_SetCPClipboardFromBMPInfo(CPClipboard *clip,
+ const LPBITMAPINFOHEADER bmi,
+ DND_CPFORMAT fmt);
+EXTERN Bool DnD_SetCPClipboardFromHBITMAP(CPClipboard *clip,
+ HBITMAP hBitmap,
+ DND_CPFORMAT fmt);
+EXTERN Bool DnD_PNGToLocalFormat(const unsigned char *pngData,
+ unsigned int pngDataLen,
+ int pngReadFlags,
+ DynBuf *bmpData,
+ HBITMAP *hBitmap);
EXTERN Bool DnD_FakeMouseEvent(DWORD flag);
EXTERN Bool DnD_FakeMouseState(DWORD key, Bool isDown);
EXTERN Bool DnD_FakeEscapeKey(void);
#define _DYNARRAY_H_
#include "dynbuf.h"
+#include "vm_basic_types.h"
+#include "vm_assert.h"
+
typedef struct DynArray {
DynBuf buf;
void
DynArray_Destroy(DynArray *a);
-void *
-DynArray_AddressOf(const DynArray *a, unsigned int i);
-
-unsigned int
-DynArray_Count(const DynArray *a);
-
Bool
DynArray_SetCount(DynArray *a, unsigned int c);
+void
+DynArray_QSort(DynArray *a, DynArrayCmp compare);
+
unsigned int
DynArray_AllocCount(const DynArray *a);
-Bool
-DynArray_Trim(DynArray *a);
-void
-DynArray_QSort(DynArray *a, DynArrayCmp compare);
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynArray_Trim --
+ *
+ * Resize the array to fit exactly DynArray_Count() elements.
+ *
+ * Results:
+ * TRUE on success
+ * FALSE on failure (why? who knows...)
+ *
+ * Side effects:
+ * Resizes the array
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE Bool
+DynArray_Trim(DynArray *a) // IN/OUT
+{
+ ASSERT(a);
+
+ return DynBuf_Trim(&a->buf);
+}
+
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynArray_AddressOf --
+ *
+ * Fetch a pointer to the address of the ith element.
+ *
+ * Results:
+ * The pointer to the ith element or NULL if the index is out of
+ * bounds.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void *
+DynArray_AddressOf(const DynArray *a, // IN
+ unsigned int i) // IN
+{
+ uintptr_t offset = i * a->width;
+
+ ASSERT(a);
+
+ if (offset + a->width <= DynBuf_GetSize(&a->buf)) {
+ return offset + (uint8 *)DynBuf_Get(&a->buf);
+ }
+
+ return NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynArray_AddressOfUnsafe --
+ *
+ * Fetch a pointer to the address of the ith element. Only call
+ * this if you already know that 'i' is valid. The index is not
+ * checked against the size of the array.
+ *
+ * Results:
+ * Always returns a pointer to the ith element.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void *
+DynArray_AddressOfUnsafe(const DynArray *a, // IN
+ unsigned int i) // IN
+{
+ uintptr_t offset = i * a->width;
+
+ ASSERT(a);
+ ASSERT(offset + a->width <= DynBuf_GetSize(&a->buf));
+
+ return offset + (uint8 *)DynBuf_Get(&a->buf);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynArray_Count --
+ *
+ * Returns the number of elements in the array.
+ *
+ * XXX: This is relatively slow, since we do an integer division.
+ * Avoid calling this in inner loops.
+ *
+ * Results:
+ * See above.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE unsigned int
+DynArray_Count(const DynArray *a) // IN
+{
+ ASSERT(a);
+
+ return (unsigned int) (DynBuf_GetSize(&a->buf) / a->width);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynArray_Copy --
+ *
+ * Copies all data and metadata from src Dynarray to dest DynArray.
+ *
+ * Dest should be an initialized DynArray of size zero.
+ *
+ * Results:
+ * TRUE on success, FALSE on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE Bool
+DynArray_Copy(DynArray *src, // IN
+ DynArray *dest) // OUT
+{
+ ASSERT(src);
+ ASSERT(dest);
+ ASSERT(dest->width);
+ ASSERT(dest->width == src->width);
+ ASSERT(DynArray_AllocCount(dest) == 0);
+
+ return DynBuf_Copy(&src->buf, &dest->buf);
+}
-Bool
-DynArray_Copy(DynArray *src, DynArray *dest);
/*
* Use the following macros to define your own DynArray type to
#include <string.h>
#include "vm_basic_types.h"
+#include "vm_assert.h"
typedef struct DynBuf {
void
DynBuf_Destroy(DynBuf *b); // IN
-void *
-DynBuf_Get(DynBuf const *b); // IN
-
void *
DynBuf_AllocGet(DynBuf const *b); // IN
Bool
DynBuf_Trim(DynBuf *b); // IN
-size_t
-DynBuf_GetSize(DynBuf const *b); // IN
-
-void
-DynBuf_SetSize(DynBuf *b, // IN
- size_t size); // IN
-
-size_t
-DynBuf_GetAllocatedSize(DynBuf const *b); // IN
-
Bool
DynBuf_Copy(DynBuf *src, // IN
DynBuf *dest); // OUT
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynBuf_Get --
+ *
+ * Retrieve a pointer to the data contained in a dynamic buffer --hpreg
+ *
+ * Results:
+ * The pointer to the data
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void *
+DynBuf_Get(DynBuf const *b) // IN
+{
+ ASSERT(b);
+
+ return b->data;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynBuf_GetSize --
+ *
+ * Returns the current size of the dynamic buffer --hpreg
+ *
+ * Results:
+ * The current size of the dynamic buffer
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE size_t
+DynBuf_GetSize(DynBuf const *b) // IN
+{
+ ASSERT(b);
+
+ return b->size;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynBuf_SetSize --
+ *
+ * Set the current size of a dynamic buffer --hpreg
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+DynBuf_SetSize(DynBuf *b, // IN
+ size_t size) // IN
+{
+ ASSERT(b);
+ ASSERT(size <= b->allocated);
+
+ b->size = size;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DynBuf_GetAllocatedSize --
+ *
+ * Returns the current allocated size of the dynamic buffer --hpreg
+ *
+ * Results:
+ * The current allocated size of the dynamic buffer
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE size_t
+DynBuf_GetAllocatedSize(DynBuf const *b) // IN
+{
+ ASSERT(b);
+
+ return b->allocated;
+}
+
+
/*
*----------------------------------------------------------------------------
*
EXTERN Bool File_IsFullPath(ConstUnicode pathName);
-EXTERN uint64 File_GetFreeSpace(ConstUnicode pathName);
+EXTERN uint64 File_GetFreeSpace(ConstUnicode pathName,
+ Bool doNotAscend);
EXTERN uint64 File_GetCapacity(ConstUnicode pathName);
int64 FileIO_GetSize(const FileIODescriptor *fd);
+int64 FileIO_GetAllocSize(const FileIODescriptor *fd);
+
+Bool FileIO_SetAllocSize(const FileIODescriptor *fd, uint64 size);
+
int64 FileIO_GetSizeByPath(ConstUnicode pathName);
int FileIO_Close(FileIODescriptor *file);
#endif
extern Bool Hostinfo_GetRatedCpuMhz(int32 cpuNumber,
uint32 *mHz);
-extern char* Hostinfo_GetCpuDescription(uint32 cpuNumber);
+extern char *Hostinfo_GetCpuDescription(uint32 cpuNumber);
extern void Hostinfo_GetTimeOfDay(VmTimeType *time);
extern VmTimeType Hostinfo_SystemUpTime(void);
extern VmTimeType Hostinfo_RawSystemTimerUS(void);
extern VmTimeType Hostinfo_SystemTimerUS(void);
extern int Hostinfo_OSVersion(int i);
+extern int Hostinfo_GetSystemBitness(void);
extern const char *Hostinfo_OSVersionString(void);
extern Bool Hostinfo_OSIsSMP(void);
#if defined(_WIN32)
/*
* imageUtil.h --
*
+ * Image manipulation routines.
*/
#ifndef _IMAGEUTIL_H_
#define _IMAGEUTIL_H_
+#include "vmware.h"
#include "imageUtilTypes.h"
#include "dynbuf.h"
#include "unicodeTypes.h"
#ifdef _WIN32
-
-typedef char Bool;
-
-typedef struct tagDIBINFOHEADER {
- BITMAPINFOHEADER bmiHeader;
- RGBQUAD bmiColors[256];
- LPBYTE bitmapBits;
-} DIBINFOHEADER, FAR *LPDIBINFOHEADER, *PDIBINFOHEADER;
-
-BOOL ImageUtil_SavePNG(LPDIBINFOHEADER image, ConstUnicode fileName);
-EXTERN HBITMAP ImageUtil_LoadPNG(ConstUnicode fileName, int pngReadFlags);
-EXTERN HBITMAP ImageUtil_LoadPNGFromBuffer(const unsigned char *imageData,
- unsigned int dataLen, int pngReadFlags);
-EXTERN HBITMAP ImageUtil_LoadImage(ConstUnicode filename, unsigned int width,
- unsigned int height);
-
-#else
-#ifndef NO_X11_XLIB_H /* XXX Carbon and X both define 'Cursor'
- * but we don't use ImgUtil_SavePNG in mksQuartz.h
- * Remove after we have implemeted imageUtil with
- * Quartz.
- */
-#include <X11/Xlib.h>
-#undef Bool // XXX conflict with vm_basic_types.h
-#define XBool int
-#undef Cursor
-
-Bool ImageUtil_SavePNG(const XImage *image,
- unsigned int numColors, unsigned short colors[256][3],
- const char *fileName);
-#endif //USE_IMAGE_UTIL_H
+#include "imageUtilWin32.h"
#endif
-#define DWORD_ALIGN(x) ((((x)+3) >> 2) << 2)
-
EXTERN Bool ImageUtil_ReadPNG(ImageInfo *image,
ConstUnicode pathName,
int pngReadFlags);
EXTERN Msg_List *Msg_GetMsgList(void);
EXTERN Msg_List *Msg_GetMsgListAndReset(void);
EXTERN void Msg_FreeMsgList(Msg_List *messages);
-EXTERN char *Msg_LocalizeList(Msg_List *messages);
+EXTERN char *Msg_LocalizeList(const Msg_List *messages);
EXTERN void Msg_Reset(Bool log);
EXTERN Bool Msg_Present(void);
EXTERN void Msg_Exit(void);
RECT_SETBOX((r), (rx1), (ry1), ((rx2) - (rx1)), ((ry2) - (ry1))); \
} while (FALSE)
+#define RECT_SETVMRECT(r, vmr) \
+ RECT_SETRECT((r), (vmr)->left, (vmr)->top, (vmr)->right, (vmr)->bottom)
+
/*
* This will only work if the intersection is not empty.
*/
const char *delimiters);
Bool StrUtil_GetNextInt64Token(int64 *out, unsigned int *index, const char *str,
const char *delimiters);
+Bool StrUtil_DecimalStrToUint(unsigned int *out, const char **str);
Bool StrUtil_StrToInt(int32 *out, const char *str);
Bool StrUtil_StrToUint(uint32 *out, const char *str);
Bool StrUtil_StrToInt64(int64 *out, const char *str);
Bool StrUtil_StrToSizet(size_t *out, const char *str);
-Bool StrUtil_DecimalStrToUint(unsigned int *out, const char **str);
+Bool StrUtil_CapacityToSectorType(SectorType *out, const char *str,
+ unsigned int bytes);
char * StrUtil_FormatSizeInBytesUnlocalized(uint64 size);
size_t StrUtil_GetLongestLineLength(const char *buf, size_t bufLength);
* Unity window attributes are boolean flags that can be set in combination on a window.
* If they are not set by the guest, it is up to the host to decide on a reasonable
* default.
- *
- * @attention
- * If you add attributes here, please also update unityAttributeNames in
- * unityWindowTracker.c.
*/
typedef enum {
- UNITY_WINDOW_ATTR_BORDERLESS = 0,
- UNITY_WINDOW_ATTR_MINIMIZABLE = 1,
- UNITY_WINDOW_ATTR_MAXIMIZABLE = 2,
- UNITY_WINDOW_ATTR_MAXIMIZED = 3,
- UNITY_WINDOW_ATTR_CLOSABLE = 5,
- UNITY_WINDOW_ATTR_HAS_TITLEBAR = 6,
- UNITY_WINDOW_ATTR_VISIBLE = 7,
- UNITY_WINDOW_ATTR_CHILD_WINDOW = 8,
- UNITY_WINDOW_ATTR_HAS_TOOLBAR_BTN = 9,
- UNITY_WINDOW_ATTR_BELONGS_TO_APP = 10,
- UNITY_WINDOW_ATTR_DROPSHADOWED = 11,
- UNITY_WINDOW_ATTR_ALWAYS_ABOVE = 12,
- UNITY_WINDOW_ATTR_ALWAYS_BELOW = 13,
- UNITY_WINDOW_ATTR_DISABLED = 14,
- UNITY_WINDOW_ATTR_NOACTIVATE = 15,
- UNITY_WINDOW_ATTR_SYSMENU = 16,
+ UNITY_WINDOW_ATTR_BORDERLESS = 0, ///< @deprecated
+ UNITY_WINDOW_ATTR_MINIMIZABLE = 1, ///< Can be minimized.
+ UNITY_WINDOW_ATTR_MAXIMIZABLE = 2, ///< Can be maximized.
+ UNITY_WINDOW_ATTR_MAXIMIZED = 3, /**< Is maximized. @note Not mutually exclusive
+ with @ref UNITY_WINDOW_STATE_MINIMIZED. */
+ UNITY_WINDOW_ATTR_CLOSABLE = 5, ///< Supports closing.
+ UNITY_WINDOW_ATTR_HAS_TITLEBAR = 6, ///< @deprecated
+ UNITY_WINDOW_ATTR_VISIBLE = 7, ///< @deprecated
+ UNITY_WINDOW_ATTR_CHILD_WINDOW = 8, ///< @deprecated
+ UNITY_WINDOW_ATTR_HAS_TASKBAR_BTN = 9, /**< Should appear in the taskbar.
+ @todo Consider deprecation? */
+ UNITY_WINDOW_ATTR_MOVABLE = 10, ///< Can be moved around the desktop.
+ UNITY_WINDOW_ATTR_RESIZABLE = 11, ///< Can be resized.
+ UNITY_WINDOW_ATTR_ALWAYS_ABOVE = 12, ///< Should stay on top of stack.
+ UNITY_WINDOW_ATTR_ALWAYS_BELOW = 13, ///< Should stay at bottom of stack.
+ UNITY_WINDOW_ATTR_DISABLED = 14, ///< Keyboard, mouse input is disabled.
+ UNITY_WINDOW_ATTR_NOACTIVATE = 15, /**< Does not raise to foreground via mouse
+ click, alt-tab, etc. */
+ UNITY_WINDOW_ATTR_SYSMENU = 16, /**< Window includes system menu (e.g., on
+ Windows, right-click the taskbar
+ button). */
UNITY_WINDOW_ATTR_TOOLWINDOW = 17,
- UNITY_WINDOW_ATTR_APPWINDOW = 18,
- UNITY_WINDOW_ATTR_FULLSCREENABLE = 19,
- UNITY_WINDOW_ATTR_FULLSCREENED = 20,
- UNITY_WINDOW_ATTR_ATTN_WANTED = 21,
- UNITY_WINDOW_ATTR_SHADEABLE = 22,
- UNITY_WINDOW_ATTR_SHADED = 23,
- UNITY_WINDOW_ATTR_STICKABLE = 24,
- UNITY_WINDOW_ATTR_STICKY = 25,
- UNITY_WINDOW_ATTR_MODAL = 26,
-
- UNITY_MAX_ATTRIBUTES // Not a valid attribute
+ UNITY_WINDOW_ATTR_APPWINDOW = 18, /**< Application window. Should appear in
+ task switchers, etc. */
+ UNITY_WINDOW_ATTR_FULLSCREENABLE = 19, ///< @deprecated
+ UNITY_WINDOW_ATTR_FULLSCREENED = 20, ///< @deprecated
+ UNITY_WINDOW_ATTR_ATTN_WANTED = 21, ///< Application wants user's attention.
+ UNITY_WINDOW_ATTR_SHADEABLE = 22, ///< @deprecated
+ UNITY_WINDOW_ATTR_SHADED = 23, ///< @deprecated
+ UNITY_WINDOW_ATTR_STICKABLE = 24, ///< Can be made sticky.
+ UNITY_WINDOW_ATTR_STICKY = 25, ///< Window should appear on all desktops.
+ UNITY_WINDOW_ATTR_MODAL = 26, /**< Modal window.
+ @todo But relative to which app? */
+
+ UNITY_MAX_ATTRIBUTES ///< Final, sentinel attribute entry.
} UnityWindowAttribute;
typedef enum {
#define UUID_SIZE 16
#define UUID_STRSIZE (2*UUID_SIZE + 1)
+#define UUID_MAXLEN 48
Bool UUID_ConvertToBin(uint8 dest_id[UUID_SIZE], const char *text);
char *UUID_ConvertToText(const uint8 id[UUID_SIZE]);
VMGUESTLIB_ERROR_MEMORY, // Not enough memory
VMGUESTLIB_ERROR_BUFFER_TOO_SMALL, // Buffer too small
VMGUESTLIB_ERROR_INVALID_HANDLE, // Handle is invalid
- VMGUESTLIB_ERROR_INVALID_ARG // One or more arguments were invalid
+ VMGUESTLIB_ERROR_INVALID_ARG, // One or more arguments were invalid
+ VMGUESTLIB_ERROR_UNSUPPORTED_VERSION // The host doesn't support this request
} VMGuestLibError;
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
* Use PATENTS_STRING for showing the patents string in plaintext form.
* PATENTS_FMT_STRING can be used with PATENTS_URL for creating hyperlinks.
*/
-#define PATENTS_STRING_BASE "This product is protected by U.S. and international copyright and\nintellectual property laws. This product is covered by one or more\npatents listed at "
+#define PATENTS_STRING_BASE "This product is protected by U.S. and international copyright and\nintellectual property laws. VMware products are covered by one or\nmore patents listed at "
#define PATENTS_STRING PATENTS_STRING_BASE "<" PATENTS_URL ">."
#define PATENTS_FMT_STRING PATENTS_STRING_BASE "%s."
#define PATENTS_URL "http://www.vmware.com/go/patents"
#define PRODUCT_VDM_SHORT_NAME "VDM"
#define PRODUCT_VDM_NAME MAKE_NAME("Virtual Desktop Manager")
+#define PRODUCT_VDDK_SHORT_NAME "VDDK"
+#define PRODUCT_VDDK_NAME MAKE_NAME("VMware Virtual Disk Development Kit")
+
#define PRODUCT_VDM_CLIENT_NAME MAKE_NAME("VDM Client")
#define PRODUCT_VDM_CLIENT_NAME_FOR_LICENSE PRODUCT_VDM_CLIENT_NAME
|| defined(VMX86_VMLS) \
|| defined(VMX86_VLICENSE) \
|| defined(VMX86_P2V) \
- || defined(VMX86_DDK))
+ || defined(VMX86_DDK) \
+ || defined(VMX86_VDDK))
# if defined(_WIN32)
/*
* XXX Make the product be Workstation by default if none of the defines
# define PRODUCT_SHORT_NAME PRODUCT_VLICENSE_NAME
#elif defined(VMX86_DDK)
# define PRODUCT_SHORT_NAME PRODUCT_DDK_NAME
+#elif defined(VMX86_VDDK)
+# define PRODUCT_SHORT_NAME PRODUCT_VDDK_NAME
#endif
#define TOOLS_VERSION_FUSION15_UPDATE1_V_MNR 9
#define TOOLS_VERSION_FUSION15_UPDATE1_V_BASE 4
+#ifndef RC_INVOKED
+#define TOOLS_VERSION_FUSION15_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION15_UPDATE2_V)
+#endif /* RC_INVOKED */
+#define TOOLS_VERSION_FUSION15_UPDATE2_V_MJR 7
+#define TOOLS_VERSION_FUSION15_UPDATE2_V_MNR 9
+#define TOOLS_VERSION_FUSION15_UPDATE2_V_BASE 5
+
/*
* At the request of CPD, ESX 4.0 receives its own major version (8.0.0).
*/
#define VIPERL_VERSION "1.1.0"
#define RCLI_VERSION "4.0.0"
#define VDM_VERSION "e.x.p"
+#define VMSAFE_VERSION "e.x.p"
+#define VDDK_VERSION "1.1.0"
+#define VDDK_FILE_VERSION 1,1,0,PRODUCT_BUILD_NUMBER_NUMERIC
+#define OVFTOOL_VERSION "1.0.0"
#define VDM_CLIENT_VERSION "e.x.p"
+#define OVFTOOL_VERSION "1.0.0"
// VMRC_PLUGIN_VERSION should match PLAYER_VERSION but can't be e.x.p
#ifndef MAKESTR
/*
* The current Tools version, derived from vm_tools_version.h. Do not modify this.
*/
-#define TOOLS_VERSION "2008.12.23"
+#define TOOLS_VERSION "2009.01.21"
#ifdef VMX86_VPX
#define VIM_API_TYPE "VirtualCenter"
# define PRODUCT_VERSION_NUMBER DDK_VERSION
#elif defined(VMX86_TOOLS)
# define PRODUCT_VERSION_NUMBER TOOLS_VERSION
+#elif defined(VMX86_VDDK)
+# define PRODUCT_VERSION_NUMBER VDDK_VERSION
#endif
/*
#define VMBACKUP_PROTOCOL_ABORT VMBACKUP_PROTOCOL_PREFIX"abort"
#define VMBACKUP_PROTOCOL_SNAPSHOT_DONE VMBACKUP_PROTOCOL_PREFIX"snapshotDone"
#define VMBACKUP_PROTOCOL_EVENT_SET VMBACKUP_PROTOCOL_PREFIX"eventSet"
-#define VMBACKUP_PROTOCOL_EVENT_GET VMBACKUP_PROTOCOL_PREFIX"eventGet"
/* These are responses to messages sent to the guest. */
#define VMBACKUP_PROTOCOL_ERROR "protocol.error"
libMisc_la_SOURCES += dynbuf.c
libMisc_la_SOURCES += escape.c
libMisc_la_SOURCES += hashTable.c
-libMisc_la_SOURCES += hostinfo_misc.c
+libMisc_la_SOURCES += hostinfoPosix.c
libMisc_la_SOURCES += hostname.c
libMisc_la_SOURCES += idLinux.c
libMisc_la_SOURCES += iovector.c
}
-/*
- *-----------------------------------------------------------------------------
- *
- * DynArray_AddressOf --
- *
- * Fetch a pointer to the address of the ith element.
- *
- * Results:
- * The pointer to the ith element or NULL if the index is out of
- * bounds.
- *
- * Side effects:
- * None
- *
- *-----------------------------------------------------------------------------
- */
-
-void *
-DynArray_AddressOf(const DynArray *a, // IN
- unsigned int i) // IN
-{
- uint8 *result = NULL;
-
- ASSERT(a);
-
- if (i < DynArray_Count(a)) {
- result = ((uint8 *)DynBuf_Get(&a->buf)) + (i * a->width);
- }
- return result;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * DynArray_Count --
- *
- * Returns the number of elements in the array.
- *
- * Results:
- * See above.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-unsigned int
-DynArray_Count(const DynArray *a) // IN
-{
- ASSERT(a);
-
- return (unsigned int) (DynBuf_GetSize(&a->buf) / a->width);
-}
-
-
/*
*-----------------------------------------------------------------------------
*
* DynArray_AllocCount is within some threshold of DynArray_Count.
* It won't buy you much).
*
+ * XXX: This is relatively slow, since we do an integer division.
+ * Avoid calling this in inner loops.
+ *
* Results:
* See above.
*
}
-/*
- *-----------------------------------------------------------------------------
- *
- * DynArray_Trim --
- *
- * Resize the array to fit exactly DynArray_Count() elements.
- *
- * Results:
- * TRUE on success
- * FALSE on failure (why? who knows...)
- *
- * Side effects:
- * Resizes the array
- *
- *-----------------------------------------------------------------------------
- */
-
-Bool
-DynArray_Trim(DynArray *a) // IN/OUT
-{
- ASSERT(a);
-
- return DynBuf_Trim(&a->buf);
-}
-
-
/*
*-----------------------------------------------------------------------------
*
arrayBuf = DynBuf_Get(&a->buf);
qsort(arrayBuf, DynArray_Count(a), a->width, compare);
}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * DynArray_Copy --
- *
- * Copies all data and metadata from src Dynarray to dest DynArray.
- *
- * Dest should be an initialized DynArray of size zero.
- *
- * Results:
- * TRUE on success, FALSE on failure.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-Bool
-DynArray_Copy(DynArray *src, // IN
- DynArray *dest) // OUT
-{
- ASSERT(src);
- ASSERT(dest);
- ASSERT(dest->width);
- ASSERT(dest->width == src->width);
- ASSERT(DynArray_AllocCount(dest) == 0);
-
- return DynBuf_Copy(&src->buf, &dest->buf);
-}
-
}
-/*
- *-----------------------------------------------------------------------------
- *
- * DynBuf_Get --
- *
- * Retrieve a pointer to the data contained in a dynamic buffer --hpreg
- *
- * Results:
- * The pointer to the data
- *
- * Side effects:
- * None
- *
- *-----------------------------------------------------------------------------
- */
-
-void *
-DynBuf_Get(DynBuf const *b) // IN
-{
- ASSERT(b);
-
- return b->data;
-}
-
-
/*
*-----------------------------------------------------------------------------
*
size_t size) // IN
{
size_t new_size;
-
+
ASSERT(b);
if (size <= 0) {
}
-/*
- *-----------------------------------------------------------------------------
- *
- * DynBuf_GetSize --
- *
- * Returns the current size of the dynamic buffer --hpreg
- *
- * Results:
- * The current size of the dynamic buffer
- *
- * Side effects:
- * None
- *
- *-----------------------------------------------------------------------------
- */
-
-size_t
-DynBuf_GetSize(DynBuf const *b) // IN
-{
- ASSERT(b);
-
- return b->size;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * DynBuf_SetSize --
- *
- * Set the current size of a dynamic buffer --hpreg
- *
- * Results:
- * None
- *
- * Side effects:
- * None
- *
- *-----------------------------------------------------------------------------
- */
-
-void
-DynBuf_SetSize(DynBuf *b, // IN
- size_t size) // IN
-{
- ASSERT(b);
- ASSERT(size <= b->allocated);
-
- b->size = size;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * DynBuf_GetAllocatedSize --
- *
- * Returns the current allocated size of the dynamic buffer --hpreg
- *
- * Results:
- * The current allocated size of the dynamic buffer
- *
- * Side effects:
- * None
- *
- *-----------------------------------------------------------------------------
- */
-
-size_t
-DynBuf_GetAllocatedSize(DynBuf const *b) // IN
-{
- ASSERT(b);
-
- return b->allocated;
-}
-
-
/*
*-----------------------------------------------------------------------------
*
}
memcpy(dest->data, src->data, src->size);
-
+
return TRUE;
}
#include <stdio.h>
#include <stdlib.h>
-
-#if defined(_WIN32)
-#include <windows.h>
-#include <winbase.h>
-#else
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/utsname.h>
+#include <fcntl.h>
+#if defined(sun)
+#include <sys/systeminfo.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <errno.h>
+#if defined(__FreeBSD__) || defined(__APPLE__)
+# include <sys/sysctl.h>
#endif
#include "vmware.h"
#include "safetime.h"
#include "str.h"
+#define SYSINFO_STRING_32 "i386"
+#define SYSINFO_STRING_64 "amd64"
+#define MAX_ARCH_NAME_LEN sizeof SYSINFO_STRING_32 > sizeof SYSINFO_STRING_64 ? \
+ sizeof SYSINFO_STRING_32 : \
+ sizeof SYSINFO_STRING_64
+
static Bool hostinfoOSVersionInitialized;
-#if defined(_WIN32)
-static int hostinfoOSVersion[4];
-static DWORD hostinfoOSPlatform;
-#else
#if defined(__APPLE__)
#define SYS_NMLN _SYS_NAMELEN
#endif
static int hostinfoOSVersion[3];
static char hostinfoOSVersionString[SYS_NMLN];
-#endif
-
-
-#if defined(_WIN32)
-/*
- *----------------------------------------------------------------------
- *
- * HostinfoOSVersionInit --
- *
- * Compute the OS version information
- *
- * Results:
- * None.
- *
- * Side effects:
- * hostinfoOS* variables are filled in.
- *
- *----------------------------------------------------------------------
- */
-
-void
-HostinfoOSVersionInit(void)
-{
- OSVERSIONINFO info;
- OSVERSIONINFOEX infoEx;
-
- if (hostinfoOSVersionInitialized) {
- return;
- }
-
- info.dwOSVersionInfoSize = sizeof (info);
- if (!GetVersionEx(&info)) {
- Warning("Unable to get OS version.\n");
- NOT_IMPLEMENTED();
- }
- ASSERT(ARRAYSIZE(hostinfoOSVersion) >= 4);
- hostinfoOSVersion[0] = info.dwMajorVersion;
- hostinfoOSVersion[1] = info.dwMinorVersion;
- hostinfoOSVersion[2] = info.dwBuildNumber & 0xffff;
- /*
- * Get the service pack number. We don't care much about NT4 hosts
- * so we can use OSVERSIONINFOEX without checking for Windows NT 4.0 SP6
- * or later versions.
- */
- infoEx.dwOSVersionInfoSize = sizeof infoEx;
- if (GetVersionEx((OSVERSIONINFO*)&infoEx)) {
- hostinfoOSVersion[3] = infoEx.wServicePackMajor;
- }
- hostinfoOSPlatform = info.dwPlatformId;
-
- hostinfoOSVersionInitialized = TRUE;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * Hostinfo_OSIsWinNT --
- *
- * This is Windows NT or descendant.
- *
- * Results:
- * TRUE if Windows NT or descendant.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-Bool
-Hostinfo_OSIsWinNT(void)
-{
- HostinfoOSVersionInit();
- return hostinfoOSPlatform == VER_PLATFORM_WIN32_NT;
-}
-#else
/*
Hostinfo_OSVersionString(void)
{
HostinfoOSVersionInit();
+
return hostinfoOSVersionString;
}
-#endif
/*
Hostinfo_OSVersion(int i)
{
HostinfoOSVersionInit();
+
return i < ARRAYSIZE(hostinfoOSVersion) ? hostinfoOSVersion[i] : 0;
}
void
Hostinfo_GetTimeOfDay(VmTimeType *time)
{
-#if defined(_WIN32)
- struct _timeb t;
-
- _ftime(&t);
-
- *time = (t.time * 1000000) + t.millitm * 1000;
-#else // assume POSIX
struct timeval tv;
gettimeofday(&tv, NULL);
*time = ((int64)tv.tv_sec * 1000000) + tv.tv_usec;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Hostinfo_GetSystemBitness --
+ *
+ * Determines the operating system's bitness.
+ *
+ * Return value:
+ * 32 or 64 on success, negative value on failure. Check errno for more
+ * details of error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+Hostinfo_GetSystemBitness(void)
+{
+#if defined(linux)
+ struct utsname u;
+
+ if (uname(&u) < 0) {
+ return -1;
+ }
+ if (strstr(u.machine, "x86_64")) {
+ return 64;
+ } else {
+ return 32;
+ }
+#elif defined(N_PLAT_NLM)
+ return 32;
+#else
+ char buf[MAX_ARCH_NAME_LEN] = { 0 };
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ int mib[2];
+ size_t len;
+
+ len = sizeof buf;
+ mib[0] = CTL_HW;
+ mib[1] = HW_MACHINE;
+
+ if (sysctl(mib, ARRAYSIZE(mib), buf, &len, NULL, 0) < 0) {
+ return -1;
+ }
+#elif defined(sun)
+#if !defined(SOL10)
+ /*
+ * XXX: This is bad. We define SI_ARCHITECTURE_K to what it is on Solaris
+ * 10 so that we can use a single guestd build for Solaris 9 and 10. In the
+ * future we should have the Solaris 9 build just return 32 -- since it did
+ * not support 64-bit x86 -- and let the Solaris 10 headers define
+ * SI_ARCHITECTURE_K, then have the installer symlink to the correct binary.
+ * For now, though, we'll share a single build for both versions.
+ */
+# define SI_ARCHITECTURE_K 518
+# endif
+
+ if (sysinfo(SI_ARCHITECTURE_K, buf, sizeof buf) < 0) {
+ return -1;
+ }
+
+ if (strcmp(buf, SYSINFO_STRING_32) == 0) {
+ return 32;
+ } else if (strcmp(buf, SYSINFO_STRING_64) == 0) {
+ return 64;
+ }
+#endif
+
+ return -1;
#endif
}
}
+#ifndef N_PLAT_NLM // NetWare Tools ask for unresolved _GLOBAL_OFFSET_TABLE...
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StrUtil_CapacityToSectorType --
+ *
+ * Converts a string containing a measure of disk capacity (such as
+ * "100MB" or "1.5k") into an unadorned and primitive quantity of sector
+ * capacity. The comment before the switch statement describes the kinds of
+ * disk capacity expressible.
+ *
+ * Results:
+ * TRUE if conversion was successful, FALSE otherwise.
+ * Value is stored in 'out', which is left undefined in the FALSE case.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+StrUtil_CapacityToSectorType(SectorType *out, // OUT: The output value
+ const char *str, // IN: String to parse
+ unsigned int bytes) // IN: Bytes per unit in an
+ // unadorned string
+
+{
+ double quantity;
+ char *rest;
+
+ ASSERT(out);
+ ASSERT(str);
+
+ errno = 0;
+ quantity = strtod(str, &rest);
+ if (errno == ERANGE) {
+ return FALSE;
+ }
+
+ /* Skip over any whitespace in the suffix. */
+ while (*rest == ' ' || *rest == '\t') {
+ rest++;
+ }
+ if (*rest != '\0') {
+ uint64 shift;
+
+ /*
+ * [kK], [mM], [gG], and [tT] represent kilo, mega, giga, and tera
+ * byte quantities respectively. [bB] represents a singular byte
+ * quantity. [sS] represents a sector quantity.
+ *
+ * All other suffixes are ignored, which also means a suffix like
+ * "MB" will be treated as 'M'.
+ */
+ switch (*rest) {
+ case 's': case 'S': shift = 9; break;
+ case 'k': case 'K': shift = 10; break;
+ case 'm': case 'M': shift = 20; break;
+ case 'g': case 'G': shift = 30; break;
+ case 't': case 'T': shift = 40; break;
+ case 'b': case 'B': default: shift = 0; break;
+ }
+ quantity *= (double)(1 << shift);
+ } else {
+ /*
+ * No suffix, so multiply by the number of bytes per unit as specified by
+ * the caller.
+ */
+ quantity *= (double)bytes;
+ }
+
+ /*
+ * Convert from "number of bytes" to "number of sectors", rounding up or down
+ * appropriately.
+ *
+ * XXX: We should use DISKLIB_SECTOR_SIZE, but do we really want the disklib
+ * header dependencies in this file?
+ *
+ */
+ *out = (SectorType)((quantity + 256) / 512);
+ return TRUE;
+}
+#endif
+
+
/*
*-----------------------------------------------------------------------------
*
*/
static int useTid = 1;
+#if defined(VMX86_SERVER) && defined(VMX86_VMX) // ESX with userworld VMX
+ static __thread pid_t tid = -1;
+#else
pid_t tid;
-
- // ESX with userworld VMX
-#if defined(VMX86_SERVER)
- if (HostType_OSIsVMK()) {
- return User_GetTid();
- }
#endif
+
if (useTid) {
+#if defined(VMX86_SERVER) && defined(VMX86_VMX) // ESX with userworld VMX
+ if (tid != -1) {
+ return tid;
+ }
+#endif
tid = gettid();
if (tid != (pid_t)-1) {
return tid;
fputs(buf, stderr);
#ifdef _WIN32
- Win32U_OutputDebugString(buf);
+ /*
+ * This would nominally be Win32U_OutputDebugString. However,
+ * OutputDebugString is unusual in that the W version converts
+ * to local encoding and calls the A version.
+ *
+ * Since any such conversion is risky (read: can Panic) and
+ * we haven't yet hit the loop detection, we will conservatively
+ * dump UTF-8 via the A version.
+ */
+ OutputDebugStringA(buf);
#endif
/*
NOT_REACHED();
}
+#ifdef _WIN32
+ /*
+ * Output again, in a way that we hope localizes correctly. Since
+ * we are converting, this can Panic, so it must run after loop
+ * detection.
+ */
+ Win32U_OutputDebugString(buf);
+#endif
+
/*
* Log panic information, and make sure we don't remove
* the log file on exit.
*/
i = 0;
memset(guestDesktopLayout, 0xFF, sizeof guestDesktopLayout); // Set all entries to -1
- if (up->desktopInfo.layoutData[0] == _NET_WM_ORIENTATION_VERT) {
+ if (up->desktopInfo.layoutData[0] == _NET_WM_ORIENTATION_HORZ) {
for (y = 0; y <= desktopSpread.y; y++) {
for (x = 0; x <= desktopSpread.x; x++) {
if (unityDesktopLayout[x][y] >= 0) {
#include "vixCommands.h"
#include "base64.h"
#include "guestInfo.h"
+#include "hostinfo.h"
#include "hgfsServer.h"
#include "hgfs.h"
#include "system.h"
osNameFull[0] = 0;
osName[0] = 0;
}
- wordSize = GuestInfo_GetSystemBitness();
+ wordSize = Hostinfo_GetSystemBitness();
if (wordSize <= 0) {
wordSize = 32;
}
lib_LTLIBRARIES = libguestlib.la
+AM_CFLAGS =
+AM_CFLAGS += -I$(top_builddir)/include
+
libguestlib_la_LIBADD =
libguestlib_la_LIBADD += ../lib/rpcOut/libRpcOut.la
libguestlib_la_LIBADD += ../lib/string/libString.la
# order so their symbols are properly resolved.
libguestlib_la_LIBADD += ../lib/message/libMessage.la
libguestlib_la_LIBADD += ../lib/backdoor/libBackdoor.la
+libguestlib_la_LIBADD += ../lib/guestRpc/libGuestRpc.la
libguestlib_la_SOURCES =
libguestlib_la_SOURCES += vmGuestLib.c
#include "rpcout.h"
#include "vmcheck.h"
#include "guestApp.h" // for ALLOW_TOOLS_IN_FOREIGN_VM
-
-#define GUESTLIB_NAME "VMware Guest API"
-
-
-// XXX For testing only. As a real "library", this should not be here.
+#include "util.h"
#include "debug.h"
+#include "strutil.h"
+#include "guestrpc/guestlibV3.h"
+#define GUESTLIB_NAME "VMware Guest API"
-
-#if 0
-/*
- *-----------------------------------------------------------------------------
- *
- * VMGuestLibDumpData --
- *
- * Debugging routine to print a data struct.
- *
- * Results:
- * None
- *
- * Side effects:
- * None
- *
- *-----------------------------------------------------------------------------
+/*
+ * These are client side data structures, separate from the wire data formats
+ * (VMGuestLibDataV[23]).
*/
-static void
-VMGuestLibDumpData(VMGuestLibDataV2 *data) // IN
-{
- ASSERT(data);
+/* Layout of the buffer holding the variable length array of V3 statistics. */
+typedef struct {
+ GuestLibV3StatCount numStats;
+ GuestLibV3Stat stats[0];
+} VMGuestLibStatisticsV3;
- printf("version: %u\n"
- "sessionId: %"FMT64"u\n"
- "cpuReservationMHz: %u\n"
- "cpuLimitMHz: %u\n"
- "cpuShares: %u\n"
- "cpuUsedMs: %"FMT64"u\n"
- "hostMHz: %u\n"
- "memReservationMB: %u\n"
- "memLimitMB: %u\n"
- "memShares: %u\n"
- "memMappedMB: %u\n"
- "memActiveMB: %u\n"
- "memOverheadMB: %u\n"
- "memBalloonedMB: %u\n"
- "memSwappedMB: %u\n"
- "memSharedMB: %u\n"
- "memSharedSavedMB: %u\n"
- "memUsedMB: %u\n"
- "elapsedMs: %"FMT64"u\n"
- "resourcePoolPath: '%s'\n"
- "last character: '%c'\n",
- data->version, data->sessionId,
- data->cpuReservationMHz.value, data->cpuLimitMHz.value,
- data->cpuShares.value, data->cpuUsedMs.value,
- data->hostMHz.value, data->memReservationMB.value,
- data->memLimitMB.value, data->memShares.value,
- data->memMappedMB.value, data->memActiveMB.value,
- data->memOverheadMB.value, data->memBalloonedMB.value,
- data->memSwappedMB.value, data->memSharedMB.value,
- data->memSharedSavedMB.value, data->memUsedMB.value,
- data->elapsedMs.value, data->resourcePoolPath.value,
- data->resourcePoolPath.value[sizeof data->resourcePoolPath.value - 2]);
-}
-#endif
+/* Layout of the handle that holds information about the statistics. */
+typedef struct {
+ uint32 version;
+ VMSessionId sessionId;
+
+ /*
+ * Statistics.
+ *
+ * dataSize is the size of the buffer pointed to by 'data'.
+ * For v2 protocol:
+ * - 'data' points to VMGuestLibDataV2 struct,
+ * For v3 protocol:
+ * - 'data' points to VMGuestLibStatisticsV3 struct.
+ */
+ size_t dataSize;
+ void *data;
+} VMGuestLibHandleType;
+
+#define HANDLE_VERSION(h) (((VMGuestLibHandleType *)(h))->version)
+#define HANDLE_SESSIONID(h) (((VMGuestLibHandleType *)(h))->sessionId)
+#define HANDLE_DATA(h) (((VMGuestLibHandleType *)(h))->data)
+#define HANDLE_DATASIZE(h) (((VMGuestLibHandleType *)(h))->dataSize)
+
+#define VMGUESTLIB_GETSTAT_V2(HANDLE, ERROR, OUTPTR, FIELDNAME) \
+ do { \
+ VMGuestLibDataV2 *_dataV2 = HANDLE_DATA(HANDLE); \
+ ASSERT(HANDLE_VERSION(HANDLE) == 2); \
+ if (!_dataV2->FIELDNAME.valid) { \
+ (ERROR) = VMGUESTLIB_ERROR_NOT_AVAILABLE; \
+ break; \
+ } \
+ *(OUTPTR) = _dataV2->FIELDNAME.value; \
+ (ERROR) = VMGUESTLIB_ERROR_SUCCESS; \
+ } while (0)
+
+#define VMGUESTLIB_GETSTAT_V3(HANDLE, ERROR, OUTPTR, FIELDNAME, STATID) \
+ do { \
+ void *_data; \
+ GuestLibV3Stat _stat; \
+ \
+ ASSERT(HANDLE_VERSION(HANDLE) == 3); \
+ (ERROR) = VMGuestLibCheckArgs((HANDLE), (OUTPTR), &_data); \
+ if (VMGUESTLIB_ERROR_SUCCESS != (ERROR)) { \
+ break; \
+ } \
+ (ERROR) = VMGuestLibGetStatisticsV3((HANDLE), (STATID), &_stat); \
+ if ((ERROR) != VMGUESTLIB_ERROR_SUCCESS) { \
+ break; \
+ } \
+ if (!_stat.GuestLibV3Stat_u.FIELDNAME.valid) { \
+ (ERROR) = VMGUESTLIB_ERROR_NOT_AVAILABLE; \
+ break; \
+ } \
+ ASSERT(_stat.d == (STATID)); \
+ if (sizeof *(OUTPTR) < sizeof _stat.GuestLibV3Stat_u.FIELDNAME.value) { \
+ (ERROR) = VMGUESTLIB_ERROR_BUFFER_TOO_SMALL; \
+ } \
+ *(OUTPTR) = _stat.GuestLibV3Stat_u.FIELDNAME.value; \
+ (ERROR) = VMGUESTLIB_ERROR_SUCCESS; \
+ } while (0)
+
+/* Handle extraction of integral type statistics. */
+#define VMGUESTLIB_GETFN_BODY(HANDLE, ERROR, OUTPTR, FIELDNAME, STATID) \
+ do { \
+ void *_data; \
+ \
+ (ERROR) = VMGuestLibCheckArgs((HANDLE), (OUTPTR), &_data); \
+ if (VMGUESTLIB_ERROR_SUCCESS != (ERROR)) { \
+ break; \
+ } \
+ if (HANDLE_VERSION(HANDLE) == 2) { \
+ VMGUESTLIB_GETSTAT_V2(HANDLE, ERROR, OUTPTR, FIELDNAME); \
+ } else if (HANDLE_VERSION(HANDLE) == 3) { \
+ VMGUESTLIB_GETSTAT_V3(HANDLE, ERROR, OUTPTR, FIELDNAME, STATID); \
+ } \
+ } while (0)
/*
case VMGUESTLIB_ERROR_OTHER:
return "Other error";
+ case VMGUESTLIB_ERROR_UNSUPPORTED_VERSION:
+ return "Host does not support this request.";
+
default:
ASSERT(FALSE); // We want to catch this case in debug builds.
return "Other error";
static VMGuestLibError
VMGuestLibCheckArgs(VMGuestLibHandle handle, // IN
void *outArg, // IN
- VMGuestLibDataV2 **data) // OUT
+ void **data) // OUT
{
ASSERT(data);
return VMGUESTLIB_ERROR_INVALID_ARG;
}
- *data = (VMGuestLibDataV2 *)handle;
+ *data = HANDLE_DATA(handle);
- if (0 == (*data)->sessionId) {
+ if (0 == HANDLE_SESSIONID(handle)) {
return VMGUESTLIB_ERROR_NO_INFO;
}
VMGuestLibError
VMGuestLib_OpenHandle(VMGuestLibHandle *handle) // OUT
{
- VMGuestLibDataV2 *data;
+ VMGuestLibHandleType *data;
#ifndef ALLOW_TOOLS_IN_FOREIGN_VM
if (!VmCheck_IsVirtualWorld()) {
return VMGUESTLIB_ERROR_INVALID_ARG;
}
- data = calloc(1, sizeof *data);
+ data = Util_SafeCalloc(1, sizeof *data);
if (!data) {
Debug("VMGuestLib_OpenHandle: Unable to allocate memory\n");
return VMGUESTLIB_ERROR_MEMORY;
* VMGuestLib_CloseHandle --
*
* Release resources associated with a handle obtained from
- * VMGuestLib_OpenHandle().
+ * VMGuestLib_OpenHandle(). Handle is invalid after return
+ * from this call.
*
* Results:
* VMGuestLibError
VMGuestLibError
VMGuestLib_CloseHandle(VMGuestLibHandle handle) // IN
{
- VMGuestLibDataV2 *data;
+ void *data;
if (NULL == handle) {
return VMGUESTLIB_ERROR_INVALID_HANDLE;
}
- data = (VMGuestLibDataV2 *)handle;
+ data = HANDLE_DATA(handle);
+ if (data != NULL &&
+ HANDLE_SESSIONID(handle) != 0 &&
+ HANDLE_VERSION(handle) == 3) {
+ VMGuestLibStatisticsV3 *v3stats = data;
+ GuestLibV3StatCount count;
+
+ for (count = 0; count < v3stats->numStats; count++) {
+ VMX_XDR_FREE(xdr_GuestLibV3Stat, &v3stats->stats[count]);
+ }
+ }
free(data);
+ /* Be paranoid. */
+ HANDLE_DATA(handle) = NULL;
+ free(handle);
+
return VMGUESTLIB_ERROR_SUCCESS;
}
*
* VMGuestLibUpdateInfo --
*
- * Retrieve the bundle of stats over the backdoor.
+ * Retrieve the bundle of stats over the backdoor and update the pointer to
+ * the Guestlib info in the handle.
*
* Results:
* TRUE on success
*/
static VMGuestLibError
-VMGuestLibUpdateInfo(VMGuestLibDataV2 *data) // IN
+VMGuestLibUpdateInfo(VMGuestLibHandle handle) // IN
{
- char commandBuf[64];
- char *reply;
+ char *reply = NULL;
size_t replyLen;
- VMGuestLibDataV2 *newData;
+ VMGuestLibError ret = VMGUESTLIB_ERROR_INVALID_ARG;
+ uint32 hostVersion = HANDLE_VERSION(handle);
- /*
- * Construct command string with the command name and the version
- * of the data struct that we want.
+ /*
+ * Starting with the highest supported protocol (major) version, negotiate
+ * down to the highest host supported version. Host supports minimum version
+ * 2.
*/
- Str_Sprintf(commandBuf, sizeof commandBuf, "%s %d",
- VMGUESTLIB_BACKDOOR_COMMAND_STRING,
- VMGUESTLIB_DATA_VERSION);
+ if (hostVersion == 0) {
+ hostVersion = VMGUESTLIB_DATA_VERSION;
+ }
- /* Actually send the request. */
- if (!RpcOut_sendOne(&reply, &replyLen, commandBuf)) {
- Debug("Failed to retrieve info: %s\n", reply ? reply : "NULL");
+ do {
+ char commandBuf[64];
+ unsigned int index = 0;
+
+ /* Free the last reply when retrying. */
free(reply);
- return VMGUESTLIB_ERROR_NOT_ENABLED;
+ reply = NULL;
+
+ /*
+ * Construct command string with the command name and the version
+ * of the data struct that we want.
+ */
+ Str_Sprintf(commandBuf, sizeof commandBuf, "%s %d",
+ VMGUESTLIB_BACKDOOR_COMMAND_STRING,
+ hostVersion);
+
+ /* Send the request. */
+ if (RpcOut_sendOne(&reply, &replyLen, commandBuf)) {
+ VMGuestLibDataV2 *v2reply = (VMGuestLibDataV2 *)reply;
+ VMSessionId sessionId = HANDLE_SESSIONID(handle);
+
+ ASSERT(hostVersion == v2reply->hdr.version);
+
+ if (sessionId != 0 && sessionId != v2reply->hdr.sessionId) {
+ /* Renegotiate protocol if sessionId changed. */
+ hostVersion = VMGUESTLIB_DATA_VERSION;
+ HANDLE_SESSIONID(handle) = 0;
+ continue;
+ }
+ ret = VMGUESTLIB_ERROR_SUCCESS;
+ break;
+ }
+
+ /*
+ * Host is older and doesn't support the requested protocol version.
+ * Request the highest version the host supports.
+ */
+ Debug("Failed to retrieve info: %s\n", reply ? reply : "NULL");
+
+ if (hostVersion == 2 ||
+ Str_Strncmp(reply, "Unknown command", sizeof "Unknown command") == 0) {
+ /*
+ * Host does not support this feature. Older (v2) host would return
+ * "Unsupported version" if it doesn't recognize the requested version.
+ *
+ * XXX: Maybe use another error code for this case where the host
+ * product doesn't support this feature?
+ */
+ ret = VMGUESTLIB_ERROR_UNSUPPORTED_VERSION;
+ break;
+ } else if (hostVersion == 3) {
+ /*
+ * Host supports v2 at a minimum. If request for v3 fails, then just use
+ * v2, since v2 host does not send the highest supported version in the
+ * reply.
+ */
+ hostVersion = 2;
+ HANDLE_SESSIONID(handle) = 0;
+ continue;
+ } else if (!StrUtil_GetNextUintToken(&hostVersion, &index, reply, ":")) {
+ /*
+ * v3 and onwards, the host returns the highest major version it supports,
+ * if the requested version is not supported. So parse out the host
+ * version from the reply and return error if it didn't.
+ */
+ Debug("Bad reply received from host.\n");
+ ret = VMGUESTLIB_ERROR_OTHER;
+ break;
+ }
+ ASSERT(hostVersion < VMGUESTLIB_DATA_VERSION);
+ } while (ret != VMGUESTLIB_ERROR_SUCCESS);
+
+ if (ret != VMGUESTLIB_ERROR_SUCCESS) {
+ goto done;
}
/* Sanity check the results. */
- if (replyLen < sizeof data->version) {
+ if (replyLen < sizeof hostVersion) {
Debug("Unable to retrieve version\n");
- return VMGUESTLIB_ERROR_OTHER;
- }
- newData = (VMGuestLibDataV2 *)reply;
- if (newData->version != VMGUESTLIB_DATA_VERSION) {
- Debug("Incorrect version returned\n");
- return VMGUESTLIB_ERROR_OTHER;
+ ret = VMGUESTLIB_ERROR_OTHER;
+ goto done;
}
- if (replyLen != sizeof *data) {
- Debug("Incorrect data size returned\n");
- return VMGUESTLIB_ERROR_OTHER;
+
+ if (hostVersion == 2) {
+ VMGuestLibDataV2 *v2reply = (VMGuestLibDataV2 *)reply;
+ size_t dataSize = sizeof *v2reply;
+
+ /* More sanity checks. */
+ if (v2reply->hdr.version != hostVersion) {
+ Debug("Incorrect data version returned\n");
+ ret = VMGUESTLIB_ERROR_OTHER;
+ goto done;
+ }
+ if (replyLen != dataSize) {
+ Debug("Incorrect data size returned\n");
+ ret = VMGUESTLIB_ERROR_OTHER;
+ goto done;
+ }
+
+ /* Store the reply in the handle. */
+ HANDLE_VERSION(handle) = v2reply->hdr.version;
+ HANDLE_SESSIONID(handle) = v2reply->hdr.sessionId;
+ if (HANDLE_DATASIZE(handle) < dataSize) {
+ /* [Re]alloc if the local handle buffer is not big enough. */
+ free(HANDLE_DATA(handle));
+ HANDLE_DATA(handle) = Util_SafeCalloc(1, dataSize);
+ HANDLE_DATASIZE(handle) = dataSize;
+ }
+ memcpy(HANDLE_DATA(handle), reply, replyLen);
+
+ /* Make sure resourcePoolPath is NUL terminated. */
+ v2reply = HANDLE_DATA(handle);
+ v2reply->resourcePoolPath.value[sizeof v2reply->resourcePoolPath.value - 1] = '\0';
+ ret = VMGUESTLIB_ERROR_SUCCESS;
+ } else if (hostVersion == 3) {
+ VMGuestLibDataV3 *v3reply = (VMGuestLibDataV3 *)reply;
+ size_t dataSize;
+ XDR xdrs;
+ GuestLibV3StatCount count;
+ VMGuestLibStatisticsV3 *v3stats;
+
+ /* More sanity checks. */
+ if (v3reply->hdr.version != hostVersion) {
+ Debug("Incorrect data version returned\n");
+ ret = VMGUESTLIB_ERROR_OTHER;
+ goto done;
+ }
+ if (replyLen < sizeof *v3reply) {
+ Debug("Incorrect data size returned\n");
+ ret = VMGUESTLIB_ERROR_OTHER;
+ goto done;
+ }
+
+ /* 0. Copy the reply version and sessionId to the handle. */
+ HANDLE_VERSION(handle) = v3reply->hdr.version;
+ HANDLE_SESSIONID(handle) = v3reply->hdr.sessionId;
+
+ /*
+ * 1. Retrieve the length of the statistics array from the XDR encoded
+ * part of the reply.
+ */
+ xdrmem_create(&xdrs, v3reply->data, v3reply->dataSize, XDR_DECODE);
+
+ if (!xdr_GuestLibV3StatCount(&xdrs, &count)) {
+ xdr_destroy(&xdrs);
+ goto done;
+ }
+ if (count >= GUESTLIB_MAX_STATISTIC_ID) {
+ /*
+ * Host has more than we can process. So process only what this side
+ * can.
+ */
+ count = GUESTLIB_MAX_STATISTIC_ID - 1;
+ }
+
+ /* 2. [Re]alloc if the local handle buffer is not big enough. */
+ dataSize = sizeof *v3stats + (count * sizeof (GuestLibV3Stat));
+ if (HANDLE_DATASIZE(handle) < dataSize) {
+ free(HANDLE_DATA(handle));
+ HANDLE_DATA(handle) = Util_SafeCalloc(1, dataSize);
+ HANDLE_DATASIZE(handle) = dataSize;
+ }
+
+ /* 3. Unmarshal the array of statistics. */
+ v3stats = HANDLE_DATA(handle);
+ v3stats->numStats = count;
+ for (count = 0; count < v3stats->numStats; count++) {
+ GuestLibV3TypeIds statId = count + 1;
+
+ /* Unmarshal the statistic. */
+ if (!xdr_GuestLibV3Stat(&xdrs, &v3stats->stats[count])) {
+ break;
+ }
+
+ /* Host sends all the V3 statistics it supports, in order. */
+ if (v3stats->stats[count].d != statId) {
+ break;
+ }
+ }
+ if (count >= v3stats->numStats) {
+ ret = VMGUESTLIB_ERROR_SUCCESS;
+ } else {
+ /*
+ * Error while unmarshalling. Deep-free already unmarshalled
+ * statistics and invalidate the data in the handle.
+ */
+ GuestLibV3StatCount c;
+
+ for (c = 0; c < count; c++) {
+ VMX_XDR_FREE(xdr_GuestLibV3Stat, &v3stats->stats[c]);
+ }
+ HANDLE_SESSIONID(handle) = 0;
+ }
+
+ /* 4. Free resources. */
+ xdr_destroy(&xdrs);
+ } else {
+ /*
+ * Host should never reply with a higher version protocol than requested.
+ */
+ ret = VMGUESTLIB_ERROR_OTHER;
}
- /* Copy stats to struct, free buffer. */
- memcpy(data, newData, sizeof *data);
+done:
free(reply);
-
- /* Make sure resourcePoolPath is NUL terminated. */
- data->resourcePoolPath.value[sizeof data->resourcePoolPath.value - 1] = '\0';
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ return ret;
}
* need to do the test again here.
*/
- error = VMGuestLibUpdateInfo((VMGuestLibDataV2 *)handle);
+ error = VMGuestLibUpdateInfo(handle);
if (error != VMGUESTLIB_ERROR_SUCCESS) {
Debug("VMGuestLibUpdateInfo failed: %d\n", error);
return error;
VMGuestLib_GetSessionId(VMGuestLibHandle handle, // IN
VMSessionId *id) // OUT
{
- VMGuestLibDataV2 *data;
+ void *data;
VMGuestLibError error;
error = VMGuestLibCheckArgs(handle, id, &data);
return error;
}
- *id = data->sessionId;
+ *id = HANDLE_SESSIONID(handle);
+
+ return VMGUESTLIB_ERROR_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMGuestLibGetStatisticsV3 --
+ *
+ * Accessor helper to retrieve the requested V3 statistic.
+ *
+ * Results:
+ * VMGuestLibError
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static VMGuestLibError
+VMGuestLibGetStatisticsV3(VMGuestLibHandle handle, // IN
+ GuestLibV3TypeIds statId, // IN
+ GuestLibV3Stat *outStat) // OUT
+{
+ VMGuestLibStatisticsV3 *stats = HANDLE_DATA(handle);
+ uint32 statIdx = statId - 1;
+
+ /*
+ * Check that the requested statistic is supported by the host. V3 host sends
+ * all the available statistics, in order. So any statistics that were added
+ * to this version of guestlib, but unsupported by the host, would not be
+ * sent by the host.
+ */
+ if (statIdx >= stats->numStats) {
+ return VMGUESTLIB_ERROR_UNSUPPORTED_VERSION;
+ }
+ memcpy(outStat, &stats->stats[statIdx], sizeof *outStat);
return VMGUESTLIB_ERROR_SUCCESS;
}
VMGuestLib_GetCpuReservationMHz(VMGuestLibHandle handle, // IN
uint32 *cpuReservationMHz) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, cpuReservationMHz, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->cpuReservationMHz.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *cpuReservationMHz = data->cpuReservationMHz.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ cpuReservationMHz, cpuReservationMHz,
+ GUESTLIB_CPU_RESERVATION_MHZ);
+ return error;
}
VMGuestLib_GetCpuLimitMHz(VMGuestLibHandle handle, // IN
uint32 *cpuLimitMHz) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, cpuLimitMHz, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->cpuLimitMHz.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *cpuLimitMHz = data->cpuLimitMHz.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ cpuLimitMHz, cpuLimitMHz,
+ GUESTLIB_CPU_LIMIT_MHZ);
+ return error;
}
VMGuestLib_GetCpuShares(VMGuestLibHandle handle, // IN
uint32 *cpuShares) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, cpuShares, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->cpuShares.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *cpuShares = data->cpuShares.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ cpuShares, cpuShares,
+ GUESTLIB_CPU_SHARES);
+ return error;
}
VMGuestLib_GetCpuUsedMs(VMGuestLibHandle handle, // IN
uint64 *cpuUsedMs) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, cpuUsedMs, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->cpuUsedMs.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *cpuUsedMs = data->cpuUsedMs.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ cpuUsedMs, cpuUsedMs,
+ GUESTLIB_CPU_USED_MS);
+ return error;
}
VMGuestLib_GetHostProcessorSpeed(VMGuestLibHandle handle, // IN
uint32 *mhz) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, mhz, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->hostMHz.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *mhz = data->hostMHz.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ mhz, hostMHz,
+ GUESTLIB_HOST_MHZ);
+ return error;
}
VMGuestLib_GetMemReservationMB(VMGuestLibHandle handle, // IN
uint32 *memReservationMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memReservationMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memReservationMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memReservationMB = data->memReservationMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memReservationMB, memReservationMB,
+ GUESTLIB_MEM_RESERVATION_MB);
+ return error;
}
VMGuestLib_GetMemLimitMB(VMGuestLibHandle handle, // IN
uint32 *memLimitMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memLimitMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memLimitMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memLimitMB = data->memLimitMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memLimitMB, memLimitMB,
+ GUESTLIB_MEM_LIMIT_MB);
+ return error;
}
VMGuestLib_GetMemShares(VMGuestLibHandle handle, // IN
uint32 *memShares) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memShares, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memShares.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memShares = data->memShares.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memShares, memShares,
+ GUESTLIB_MEM_SHARES);
+ return error;
}
VMGuestLib_GetMemMappedMB(VMGuestLibHandle handle, // IN
uint32 *memMappedMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memMappedMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memMappedMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memMappedMB = data->memMappedMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memMappedMB, memMappedMB,
+ GUESTLIB_MEM_MAPPED_MB);
+ return error;
}
VMGuestLib_GetMemActiveMB(VMGuestLibHandle handle, // IN
uint32 *memActiveMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memActiveMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memActiveMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memActiveMB = data->memActiveMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memActiveMB, memActiveMB,
+ GUESTLIB_MEM_ACTIVE_MB);
+ return error;
}
VMGuestLib_GetMemOverheadMB(VMGuestLibHandle handle, // IN
uint32 *memOverheadMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memOverheadMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memOverheadMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memOverheadMB = data->memOverheadMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memOverheadMB, memOverheadMB,
+ GUESTLIB_MEM_OVERHEAD_MB);
+ return error;
}
VMGuestLib_GetMemBalloonedMB(VMGuestLibHandle handle, // IN
uint32 *memBalloonedMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memBalloonedMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memBalloonedMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memBalloonedMB = data->memBalloonedMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memBalloonedMB, memBalloonedMB,
+ GUESTLIB_MEM_BALLOONED_MB);
+ return error;
}
VMGuestLib_GetMemSwappedMB(VMGuestLibHandle handle, // IN
uint32 *memSwappedMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memSwappedMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memSwappedMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memSwappedMB = data->memSwappedMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memSwappedMB, memSwappedMB,
+ GUESTLIB_MEM_SWAPPED_MB);
+ return error;
}
VMGuestLib_GetMemSharedMB(VMGuestLibHandle handle, // IN
uint32 *memSharedMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memSharedMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memSharedMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memSharedMB = data->memSharedMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memSharedMB, memSharedMB,
+ GUESTLIB_MEM_SHARED_MB);
+ return error;
}
VMGuestLib_GetMemSharedSavedMB(VMGuestLibHandle handle, // IN
uint32 *memSharedSavedMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memSharedSavedMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memSharedSavedMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memSharedSavedMB = data->memSharedSavedMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memSharedSavedMB, memSharedSavedMB,
+ GUESTLIB_MEM_SHARED_SAVED_MB);
+ return error;
}
VMGuestLib_GetMemUsedMB(VMGuestLibHandle handle, // IN
uint32 *memUsedMB) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, memUsedMB, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->memUsedMB.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *memUsedMB = data->memUsedMB.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ memUsedMB, memUsedMB,
+ GUESTLIB_MEM_USED_MB);
+ return error;
}
VMGuestLib_GetElapsedMs(VMGuestLibHandle handle, // IN
uint64 *elapsedMs) // OUT
{
- VMGuestLibDataV2 *data;
- VMGuestLibError error;
-
- error = VMGuestLibCheckArgs(handle, elapsedMs, &data);
- if (VMGUESTLIB_ERROR_SUCCESS != error) {
- return error;
- }
-
- if (!data->elapsedMs.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- *elapsedMs = data->elapsedMs.value;
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+ VMGUESTLIB_GETFN_BODY(handle, error,
+ elapsedMs, elapsedMs,
+ GUESTLIB_ELAPSED_MS);
+ return error;
}
size_t *bufferSize, // IN/OUT
char *pathBuffer) // OUT
{
- size_t size;
- VMGuestLibDataV2 *data;
-
- if (NULL == handle) {
- return VMGUESTLIB_ERROR_INVALID_HANDLE;
- }
-
- if (NULL == bufferSize || NULL == pathBuffer) {
- return VMGUESTLIB_ERROR_INVALID_ARG;
- }
-
- data = (VMGuestLibDataV2 *)handle;
-
- if (0 == data->sessionId) {
- return VMGUESTLIB_ERROR_NO_INFO;
- }
-
- if (!data->resourcePoolPath.valid) {
- return VMGUESTLIB_ERROR_NOT_AVAILABLE;
- }
-
- /*
- * Check buffer size. It's safe to use strlen because we squash the
- * final byte of resourcePoolPath in VMGuestLibUpdateInfo.
- */
- size = strlen(data->resourcePoolPath.value) + 1;
- if (*bufferSize < size) {
- *bufferSize = size;
- return VMGUESTLIB_ERROR_BUFFER_TOO_SMALL;
- }
-
- memcpy(pathBuffer, data->resourcePoolPath.value, *bufferSize);
-
- return VMGUESTLIB_ERROR_SUCCESS;
+ VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
+
+ do {
+ size_t size;
+ if (NULL == handle) {
+ error = VMGUESTLIB_ERROR_INVALID_HANDLE;
+ break;
+ }
+
+ if (NULL == bufferSize || NULL == pathBuffer) {
+ error = VMGUESTLIB_ERROR_INVALID_ARG;
+ break;
+ }
+
+ if (0 == HANDLE_SESSIONID(handle)) {
+ error = VMGUESTLIB_ERROR_NO_INFO;
+ break;
+ }
+
+ if (HANDLE_VERSION(handle) == 2) {
+ VMGuestLibDataV2 *dataV2;
+
+ dataV2 = (VMGuestLibDataV2 *)HANDLE_DATA(handle);
+
+ if (!dataV2->resourcePoolPath.valid) {
+ error = VMGUESTLIB_ERROR_NOT_AVAILABLE;
+ break;
+ }
+
+ /*
+ * Check buffer size. It's safe to use strlen because we squash the
+ * final byte of resourcePoolPath in VMGuestLibUpdateInfo.
+ */
+ size = strlen(dataV2->resourcePoolPath.value) + 1;
+ if (*bufferSize < size) {
+ *bufferSize = size;
+ error = VMGUESTLIB_ERROR_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ memcpy(pathBuffer, dataV2->resourcePoolPath.value, size);
+ error = VMGUESTLIB_ERROR_SUCCESS;
+ } else if (HANDLE_VERSION(handle) == 3) {
+ VMGuestLibStatisticsV3 *stats = HANDLE_DATA(handle);
+ GuestLibV3Stat *stat = &stats->stats[GUESTLIB_RESOURCE_POOL_PATH - 1];
+
+ if (!stat->GuestLibV3Stat_u.resourcePoolPath.valid) {
+ error = VMGUESTLIB_ERROR_NOT_AVAILABLE;
+ break;
+ }
+
+ size = strlen(stat->GuestLibV3Stat_u.resourcePoolPath.value) + 1;
+ if (*bufferSize < size) {
+ *bufferSize = size;
+ error = VMGUESTLIB_ERROR_BUFFER_TOO_SMALL;
+ break;
+ }
+ memcpy(pathBuffer, stat->GuestLibV3Stat_u.resourcePoolPath.value, size);
+ error = VMGUESTLIB_ERROR_SUCCESS;
+ }
+ } while (0);
+
+ return error;
}
* Current version of the VMGuestLibData structure
*/
-#define VMGUESTLIB_DATA_VERSION 2
+#define VMGUESTLIB_DATA_VERSION 3
/* Stat types with a valid bit per stat. */
#endif // #if 0
+typedef
+#include "vmware_pack_begin.h"
+struct {
+ uint32 version;
+ VMSessionId sessionId;
+}
+#include "vmware_pack_end.h"
+VMGuestLibHeader;
/*
* This is version 2 of the data structure GuestLib uses to obtain
typedef
#include "vmware_pack_begin.h"
struct VMGuestLibDataV2 {
- uint32 version;
- VMSessionId sessionId;
+ /* Header */
+ VMGuestLibHeader hdr;
/* Statistics */
VMGuestLibDataV2;
+/*
+ * This is version 3 of the data structure GuestLib uses to obtain stats over
+ * the backdoor from the VMX/VMKernel. It is not exposed to users of the
+ * GuestLib API. This struct is sent on the wire.
+ *
+ * The buffer contains a variable length array of statistics, that are
+ * marshalled from XDR spec generated code. Each data field has a discriminant
+ * preceding the payload, which enables the client to detect fields that it
+ * doesn't recognize.
+ *
+ * V3 is a superset of V2 and a major protocol change. Any extensions to the
+ * wire protocol that just add statistics, can do so within V3. Just update the
+ * .x file to add a new discriminant for the union, at the end of the list. V3
+ * clients may assume that the fields are ordered on the wire in increasing order
+ * of the discriminant list. So, a client may stop processing at the first
+ * unrecognized field. V3 payload contains all available guestlib statistics
+ * supported by the host.
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct VMGuestLibDataV3 {
+ /* Header */
+ VMGuestLibHeader hdr;
+
+ /* Statistics */
+ uint32 dataSize;
+ char data[0];
+}
+#include "vmware_pack_end.h"
+VMGuestLibDataV3;
+
#endif /* _VM_GUEST_LIB_INT_H_ */
# define DEFAULT_HANDLER VMToolsLogFile
#endif
+#define LOGGING_GROUP "logging"
#define MAX_DOMAIN_LEN 64
/*
(gLogEnabled && ((data)->mask & (level))))
+static void
+VMToolsLogFile(const gchar *domain,
+ GLogLevelFlags level,
+ const gchar *message,
+ gpointer _data);
+
+#if defined(G_PLATFORM_WIN32)
+static void
+VMToolsLogOutputDebugString(const gchar *domain,
+ GLogLevelFlags level,
+ const gchar *message,
+ gpointer _data);
+#endif
+
+
typedef struct LogHandlerData {
GLogLevelFlags mask;
FILE *file;
static gboolean gLogEnabled = FALSE;
static guint gPanicCount = 0;
static LogHandlerData *gDefaultData = NULL;
+static GLogFunc gDefaultLogFunc = DEFAULT_HANDLER;
/* Internal functions. */
}
+/**
+ * Configures the given log domain based on the data provided in the given
+ * dictionary. If the log domain being configured doesn't match the default
+ * (@see VMTools_GetDefaultLogDomain()), and no specific handler is defined
+ * for the domain, the handler is inherited from the default domain, instead
+ * of using the default handler. This allows reusing the same log file, for
+ * example, while maintaining the ability to enable different log levels
+ * for different domains.
+ *
+ * For the above to properly work, the default log domain has to be configured
+ * before any other domains.
+ *
+ * @param[in] domain Name of domain being configured.
+ * @param[in] cfg Dictionary with config data.
+ */
+
+static void
+VMToolsConfigLogDomain(const gchar *domain,
+ GKeyFile *cfg)
+{
+ gchar *handler = NULL;
+ gchar *level = NULL;
+ gchar *logpath = NULL;
+ gchar key[128];
+
+ GLogFunc handlerFn = NULL;
+ GLogLevelFlags levelsMask;
+ LogHandlerData *data;
+ FILE *logfile = NULL;
+
+ /* Arbitrary limit. */
+ if (strlen(domain) > MAX_DOMAIN_LEN) {
+ g_warning("Domain name too long: %s\n", domain);
+ goto exit;
+ } else if (strlen(domain) == 0) {
+ g_warning("Invalid domain declaration, missing name.\n");
+ goto exit;
+ }
+
+ Str_Sprintf(key, sizeof key, "%s.level", domain);
+ level = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
+ if (level == NULL) {
+#ifdef VMX86_DEBUG
+ level = g_strdup("message");
+#else
+ level = g_strdup("warning");
+#endif
+ }
+
+ /* Parse the handler information. */
+ Str_Sprintf(key, sizeof key, "%s.handler", domain);
+ handler = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
+
+ if (handler == NULL) {
+ if (strcmp(domain, VMTools_GetDefaultLogDomain()) == 0) {
+ handlerFn = DEFAULT_HANDLER;
+ } else {
+ handlerFn = gDefaultLogFunc;
+ }
+ } else if (strcmp(handler, "std") == 0) {
+ handlerFn = VMToolsLogFile;
+ } else if (strcmp(handler, "file") == 0) {
+ /* Don't set up the file sink if logging is disabled. */
+ if (strcmp(level, "none") != 0) {
+ handlerFn = VMToolsLogFile;
+ Str_Sprintf(key, sizeof key, "%s.data", domain);
+ logpath = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
+ if (logpath == NULL) {
+ g_warning("Missing log path for file handler (%s).\n", domain);
+ goto exit;
+ }
+ }
+#if defined(G_PLATFORM_WIN32)
+ } else if (strcmp(handler, "outputdebugstring") == 0) {
+ handlerFn = VMToolsLogOutputDebugString;
+#endif
+ } else {
+ g_warning("Unknown log handler: %s\n", handler);
+ goto exit;
+ }
+
+ /* Parse the log level configuration, and build the mask. */
+ if (strcmp(level, "error") == 0) {
+ levelsMask = G_LOG_LEVEL_ERROR;
+ } else if (strcmp(level, "critical") == 0) {
+ levelsMask = G_LOG_LEVEL_ERROR |
+ G_LOG_LEVEL_CRITICAL;
+ } else if (strcmp(level, "warning") == 0) {
+ levelsMask = G_LOG_LEVEL_ERROR |
+ G_LOG_LEVEL_CRITICAL |
+ G_LOG_LEVEL_WARNING;
+ } else if (strcmp(level, "message") == 0) {
+ levelsMask = G_LOG_LEVEL_ERROR |
+ G_LOG_LEVEL_CRITICAL |
+ G_LOG_LEVEL_WARNING |
+ G_LOG_LEVEL_MESSAGE;
+ } else if (strcmp(level, "info") == 0) {
+ levelsMask = G_LOG_LEVEL_ERROR |
+ G_LOG_LEVEL_CRITICAL |
+ G_LOG_LEVEL_WARNING |
+ G_LOG_LEVEL_MESSAGE |
+ G_LOG_LEVEL_INFO;
+ } else if (strcmp(level, "debug") == 0) {
+ levelsMask = ALL_LOG_LEVELS;
+ } else if (strcmp(level, "none") == 0) {
+ levelsMask = 0;
+ } else {
+ g_warning("Unknown log level (%s): %s\n", domain, level);
+ goto exit;
+ }
+
+ /* Initialize the log file, if using the "file" handler. */
+ if (logpath != NULL) {
+ logfile = VMToolsLogOpenFile(logpath);
+ if (logfile == NULL) {
+ g_warning("Couldn't open log file (%s): %s\n", domain, logpath);
+ goto exit;
+ }
+ }
+
+ data = g_malloc0(sizeof *data);
+ data->mask = levelsMask;
+ data->file = logfile;
+
+ if (strcmp(domain, VMTools_GetDefaultLogDomain()) == 0) {
+ g_free(gDefaultData);
+ gDefaultData = data;
+ gDefaultLogFunc = handlerFn;
+ g_log_set_default_handler(handlerFn, data);
+ } else if (handler == NULL) {
+ data->file = gDefaultData->file;
+ }
+
+ g_log_set_handler(domain, ALL_LOG_LEVELS, handlerFn, data);
+
+exit:
+ g_free(handler);
+ g_free(logpath);
+ g_free(level);
+}
+
/* Public API. */
void
VMTools_ConfigLogging(GKeyFile *cfg)
{
- const gchar *group = "logging";
gchar **list;
gchar **curr;
- if (!g_key_file_has_group(cfg, group)) {
+ if (!g_key_file_has_group(cfg, LOGGING_GROUP)) {
return;
}
- list = g_key_file_get_keys(cfg, group, NULL, NULL);
+ /*
+ * Configure the default domain first. See function documentation for
+ * VMToolsConfigLogDomain() for the reason.
+ */
+ VMToolsConfigLogDomain(VMTools_GetDefaultLogDomain(), cfg);
+
+ list = g_key_file_get_keys(cfg, LOGGING_GROUP, NULL, NULL);
for (curr = list; curr != NULL && *curr != NULL; curr++) {
gchar *domain = *curr;
- gchar *handler;
- gchar *level = NULL;
- gchar *logpath = NULL;
- gchar key[128];
- guint handlerId;
-
- GLogFunc handlerFn = NULL;
- GLogLevelFlags levelsMask;
- LogHandlerData *data;
- FILE *logfile = NULL;
/* Check whether it's a domain "declaration". */
if (!g_str_has_suffix(domain, ".level")) {
continue;
}
- level = g_key_file_get_string(cfg, group, domain, NULL);
-
/* Trims ".level" from the key to get the domain name. */
domain[strlen(domain) - 6] = '\0';
- /* Arbitrary limit. */
- if (strlen(domain) > MAX_DOMAIN_LEN) {
- g_warning("Domain name too long: %s\n", domain);
- continue;
- } else if (strlen(domain) == 0) {
- g_warning("Invalid domain declaration, missing name.\n");
- continue;
- }
-
- /* Parse the handler information. */
- Str_Sprintf(key, sizeof key, "%s.handler", domain);
- handler = g_key_file_get_string(cfg, group, key, NULL);
-
- if (handler == NULL) {
- handlerFn = DEFAULT_HANDLER;
- } else if (strcmp(handler, "std") == 0) {
- handlerFn = VMToolsLogFile;
- } else if (strcmp(handler, "file") == 0) {
- /* Don't set up the file sink if logging is disabled. */
- if (strcmp(level, "none") != 0) {
- handlerFn = VMToolsLogFile;
- Str_Sprintf(key, sizeof key, "%s.data", domain);
- logpath = g_key_file_get_string(cfg, group, key, NULL);
- if (logpath == NULL) {
- g_warning("Missing log path for file handler (%s).\n", domain);
- goto next;
- }
- }
-#if defined(G_PLATFORM_WIN32)
- } else if (strcmp(handler, "outputdebugstring") == 0) {
- handlerFn = VMToolsLogOutputDebugString;
-#endif
- } else {
- g_warning("Unknown log handler: %s\n", handler);
- goto next;
- }
-
- /* Parse the log level configuration, and build the mask. */
- if (strcmp(level, "error") == 0) {
- levelsMask = G_LOG_LEVEL_ERROR;
- } else if (strcmp(level, "critical") == 0) {
- levelsMask = G_LOG_LEVEL_ERROR |
- G_LOG_LEVEL_CRITICAL;
- } else if (strcmp(level, "warning") == 0) {
- levelsMask = G_LOG_LEVEL_ERROR |
- G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING;
- } else if (strcmp(level, "message") == 0) {
- levelsMask = G_LOG_LEVEL_ERROR |
- G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING |
- G_LOG_LEVEL_MESSAGE;
- } else if (strcmp(level, "info") == 0) {
- levelsMask = G_LOG_LEVEL_ERROR |
- G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING |
- G_LOG_LEVEL_MESSAGE |
- G_LOG_LEVEL_INFO;
- } else if (strcmp(level, "debug") == 0) {
- levelsMask = ALL_LOG_LEVELS;
- } else if (strcmp(level, "none") == 0) {
- levelsMask = 0;
- } else {
- g_warning("Unknown log level (%s): %s\n", domain, level);
- goto next;
- }
-
- /* Initialize the log file, if using the "file" handler. */
- if (logpath != NULL) {
- logfile = VMToolsLogOpenFile(logpath);
- if (logfile == NULL) {
- g_warning("Couldn't open log file (%s): %s\n", domain, logpath);
- goto next;
- }
- }
-
- data = g_malloc0(sizeof *data);
- data->mask = levelsMask;
- data->file = logfile;
-
+ /* Skip the default domain. */
if (strcmp(domain, VMTools_GetDefaultLogDomain()) == 0) {
- g_free(gDefaultData);
- gDefaultData = NULL;
- g_log_set_default_handler(handlerFn, data);
+ continue;
}
- handlerId = g_log_set_handler(domain, ALL_LOG_LEVELS, handlerFn, data);
- next:
- g_free(handler);
- g_free(logpath);
- g_free(level);
+ VMToolsConfigLogDomain(domain, cfg);
}
g_strfreev(list);
- gLogEnabled = g_key_file_get_boolean(cfg, group, "log", NULL);
- if (g_key_file_has_key(cfg, group, "enableCoreDump", NULL)) {
- gEnableCoreDump = g_key_file_get_boolean(cfg, group, "enableCoreDump", NULL);
+ gLogEnabled = g_key_file_get_boolean(cfg, LOGGING_GROUP, "log", NULL);
+ if (g_key_file_has_key(cfg, LOGGING_GROUP, "enableCoreDump", NULL)) {
+ gEnableCoreDump = g_key_file_get_boolean(cfg, LOGGING_GROUP,
+ "enableCoreDump", NULL);
}
}
modulesrc = $(top_srcdir)/modules
$(MODULES):
- $(MAKE) VM_UNAME=$(KERNEL_RELEASE) -C "$(modulesrc)/$(MODULES_OS)/$@"
+ $(MAKE) VM_UNAME=$(KERNEL_RELEASE) $(EXTRA_ARGS) -C "$(modulesrc)/$(MODULES_OS)/$@"
if LINUX
export pvscsidir := $(MODULES_DIR)/drivers/scsi
export vmxnetdir := $(MODULES_DIR)
endif FREEBSD
+if SOLARIS
+export vmhgfsdir := $(MODULES_DIR)
+# Solaris does not have Kbuild-like system so we need to supply
+# compiler and linker and other items discovered by configure
+# script.
+EXTRA_ARGS =
+EXTRA_ARGS += "CC=$(CC)"
+EXTRA_ARGS += "CC_WARNINGS=$(CC_WARNINGS)"
+EXTRA_ARGS += "GLOBAL_DEFS=$(GLOBAL_DEFS)"
+EXTRA_ARGS += "LD=$(LD)"
+EXTRA_ARGS += "HAVE_GNU_LD=$(HAVE_GNU_LD)"
+endif
+
## Automake will supplement its own "clean" targets with this.
clean-local:
- for MOD in $(MODULES); do \
- if [ -d "$(modulesrc)/$(MODULES_OS)/$$MOD" ]; then \
- $(MAKE) -C "$(modulesrc)/$(MODULES_OS)/$$MOD" clean || exit 1; \
- fi \
+ for MOD in $(MODULES); do \
+ if [ -d "$(modulesrc)/$(MODULES_OS)/$$MOD" ]; then \
+ $(MAKE) VM_UNAME=$(KERNEL_RELEASE) \
+ -C "$(modulesrc)/$(MODULES_OS)/$$MOD" clean || exit 1; \
+ fi \
done
$(RM) -f $(modulesrc)/$(MODULES_OS)/*.o $(modulesrc)/$(MODULES_OS)/*.ko
install-exec-hook:
- for MOD in $(MODULES); do \
- $(INSTALL) -d $(DESTDIR)`eval echo '$$'$${MOD}dir`; \
- $(INSTALL) -m644 $(modulesrc)/$(MODULES_OS)/$$MOD/$$MOD.ko \
- $(DESTDIR)`eval echo '$$'$${MOD}dir`; \
+if SOLARIS
+ for MOD in $(MODULES); do \
+ $(MAKE) VM_UNAME=$(KERNEL_RELEASE) \
+ -C "$(modulesrc)/$(MODULES_OS)/$$MOD" install || exit 1; \
+ done
+else
+ for MOD in $(MODULES); do \
+ $(INSTALL) -d $(DESTDIR)`eval echo '$$'$${MOD}dir`; \
+ $(INSTALL) -m644 $(modulesrc)/$(MODULES_OS)/$$MOD/$$MOD.ko \
+ $(DESTDIR)`eval echo '$$'$${MOD}dir`; \
done
eval "$$DEPMOD"
+endif
+
uninstall-hook:
- for MOD in $(MODULES); do \
- $(RM) -f $(DESTDIR)`eval echo '$$'$${MOD}dir`/$$MOD.ko &> /dev/null; \
+ for MOD in $(MODULES); do \
+ $(RM) -f $(DESTDIR)`eval echo '$$'$${MOD}dir`/$$MOD.ko &> /dev/null;\
done
eval "$$DEPMOD"
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
HEADERS += compat_freebsd.h
HEADERS += compat_vop.h
HEADERS += vfsopscommon.h
+HEADERS += transport.h
COMMON_SRCS := cpName.c
COMMON_SRCS += cpNameLinux.c
COMMON_HGFS_SRCS += vnopscommon.c
COMMON_HGFS_SRCS += state.c
COMMON_HGFS_SRCS += vfsopscommon.c
+COMMON_HGFS_SRCS += transport.c
MODULE_SRCS := vnops.c
MODULE_SRCS += vfsops.c
/*
* state.c --
*
- * Vnode, HgfsOpenFile, and HgfsFile state manipulation routines.
+ * Vnode and HgfsFile state manipulation routines.
*/
#include <sys/param.h>
# include <sys/malloc.h>
# include "sha1.h"
# include "compat_freebsd.h"
+#define vnode_get(vnode) vget(vnode, LK_SHARED, curthread)
+#define vnode_rele(vnode) vrele(vnode)
+#define vnode_ref(vnode) vref(vnode)
#elif defined(__APPLE__)
# include <string.h>
/*
#define HGFS_FILE_HT_BUCKET(ht, index) (&ht->hashTable[index])
#define HGFS_IS_ROOT_FILE(sip, file) (HGFS_VP_TO_FP(sip->rootVnode) == file)
+#define LCK_MTX_ASSERT(mutex)
#if defined(__APPLE__)
# define SHA1_HASH_LEN SHA_DIGEST_LENGTH
+
+#if defined(VMX86_DEVEL)
+#undef LCK_MTX_ASSERT
+#define LCK_MTX_ASSERT(mutex) lck_mtx_assert(mutex, LCK_MTX_ASSERT_OWNED)
+#endif
+
#endif
/*
*/
static int HgfsVnodeGetInt(struct vnode **vpp,
- struct HgfsSuperInfo *sip,
- struct mount *vfsp,
- const char *fileName,
- HgfsFileType fileType,
- HgfsFileHashTable *htp,
- Bool rootVnode);
+ struct vnode *dvp,
+ struct HgfsSuperInfo *sip,
+ struct mount *vfsp,
+ const char *fileName,
+ HgfsFileType fileType,
+ HgfsFileHashTable *htp,
+ Bool rootVnode,
+ Bool createFile,
+ int permissions);
/* Allocation/initialization/free of open file state */
-static HgfsOpenFile *HgfsAllocOpenFile(const char *fileName, HgfsFileType fileType,
- HgfsFileHashTable *htp);
-static void HgfsFreeOpenFile(HgfsOpenFile *ofp, HgfsFileHashTable *htp);
+static HgfsFile *HgfsAllocFile(const char *fileName, HgfsFileType fileType,
+ struct vnode *dvp, HgfsFileHashTable *htp,
+ int permissions);
/* Acquiring/releasing file state */
-static HgfsFile *HgfsGetFile(const char *fileName, HgfsFileType fileType,
- HgfsFileHashTable *htp);
+static HgfsFile *HgfsInsertFile(const char *fileName,
+ HgfsFile *fp,
+ HgfsFileHashTable *htp);
static void HgfsReleaseFile(HgfsFile *fp, HgfsFileHashTable *htp);
-static int HgfsInitFile(HgfsFile *fp, const char *fileName, HgfsFileType fileType);
+static int HgfsInitFile(HgfsFile *fp, struct vnode *dvp, const char *fileName,
+ HgfsFileType fileType, int permissions);
+static void HgfsFreeFile(HgfsFile *fp);
/* Adding/finding/removing file state from hash table */
static void HgfsAddFile(HgfsFile *fp, HgfsFileHashTable *htp);
static unsigned int HgfsFileNameHash(const char *fileName);
static void HgfsNodeIdHash(const char *fileName, uint32_t fileNameLength,
ino_t *outHash);
+static Bool HgfsIsModeCompatible(HgfsMode requestedMode, HgfsMode existingMode);
/*
* Global functions
*/
*
* Creates a vnode for the provided filename.
*
- * This will always allocate a vnode and HgfsOpenFile. If a HgfsFile
+ * This will always allocate a vnode and HgfsFile. If a HgfsFile
* already exists for this filename then that is used, if a HgfsFile doesn't
* exist, one is created.
*
* vnode is returned locked.
*
* Side effects:
- * If the HgfsFile already exists, its reference count is incremented;
- * otherwise a HgfsFile is created.
+ * If the HgfsFile already exists and createFile is TRUE then the EEXIST error
+ * is returned. Otherwise if the HgfsFile already exists its reference count
+ * is incremented.
+ * If HgfsFile with the given name does not exist then HgfsFile is created.
*
*----------------------------------------------------------------------------
*/
int
HgfsVnodeGet(struct vnode **vpp, // OUT: Filled with address of created vnode
+ struct vnode *dvp, // IN: Parent directory vnode
HgfsSuperInfo *sip, // IN: Superinfo
struct mount *vfsp, // IN: Filesystem structure
const char *fileName, // IN: Name of this file
HgfsFileType fileType, // IN: Type of file
- HgfsFileHashTable *htp) // IN: File hash table
+ HgfsFileHashTable *htp, // IN: File hash table
+ Bool createFile, // IN: Creating a new file or open existing?
+ int permissions) // IN: Permissions for the created file
{
- return HgfsVnodeGetInt(vpp, sip, vfsp, fileName, fileType, htp, FALSE);
+ return HgfsVnodeGetInt(vpp, dvp, sip, vfsp, fileName, fileType, htp, FALSE,
+ createFile, permissions);
}
HgfsFileType fileType, // IN: Type of file
HgfsFileHashTable *htp) // IN: File hash table
{
- return HgfsVnodeGetInt(vpp, sip, vfsp, fileName, fileType, htp, TRUE);
+ return HgfsVnodeGetInt(vpp, NULL, sip, vfsp, fileName, fileType, htp, TRUE, FALSE, 0);
}
/*
*----------------------------------------------------------------------------
*
- * HgfsVnodePut --
+ * HgfsReleaseVnodeContext --
*
- * Releases the provided vnode.
+ * Releases context for the provided vnode.
*
- * This will free the associated vnode.
- * The HgfsFile's reference count is decremented and, if 0, freed.
+ * This will free the context information associated vnode.
*
* Results:
* Returns 0 on success and a non-zero error code on failure.
*/
int
-HgfsVnodePut(struct vnode *vp, // IN: Vnode to release
- HgfsFileHashTable *htp) // IN: Hash table pointer
+HgfsReleaseVnodeContext(struct vnode *vp, // IN: Vnode to release
+ HgfsFileHashTable *htp) // IN: Hash table pointer
{
- HgfsOpenFile *ofp;
+ HgfsFile *fp;
ASSERT(vp);
ASSERT(htp);
DEBUG(VM_DEBUG_ENTRY, "Entering HgfsVnodePut\n");
/* Get our private open-file state. */
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
+ fp = HGFS_VP_TO_FP(vp);
+ ASSERT(fp);
- /*
- * We need to free the open file structure. This takes care of releasing
- * our reference on the underlying file structure (and freeing it if
- * necessary).
- */
- HgfsFreeOpenFile(ofp, htp);
+ /* We need to release private HGFS information asosiated with the vnode. */
+ HgfsReleaseFile(fp, htp);
return 0;
}
* file. (This situation exists on a readdir since dentries are filled in
* rather than creating vnodes.)
*
- * In Solaris, node ids are provided in vnodes and inode numbers are
- * provided in dentries. For applications to work correctly, we must make
- * sure that the inode number of a file's dentry and the node id in a file's
- * vnode match one another. This poses a problem since vnodes typically do
- * not exist when dentries need to be created, and once a dentry is created
- * we have no reference to it since it is copied to the user and freed from
- * kernel space. An example of a program that breaks when these values
- * don't match is /usr/bin/pwd. This program first acquires the node id of
- * "." from its vnode, then traverses backwards to ".." and looks for the
- * dentry in that directory with the inode number matching the node id.
- * (This is how it obtains the name of the directory it was just in.)
- * /usr/bin/pwd repeats this until it reaches the root directory, at which
- * point it concatenates the filenames it acquired along the way and
- * displays them to the user. When inode numbers don't match the node id,
- * /usr/bin/pwd displays an error saying it cannot determine the directory.
- *
* The Hgfs protocol does not provide us with unique identifiers for files
* since it must support filesystems that do not have the concept of inode
* numbers. Therefore, we must maintain a mapping from filename to node id/
}
DEBUG(VM_DEBUG_FAIL, "HgfsFileHashTableIsEmpty: %s is in use.\n",
- currFile->fileName);
+ currFile->fileName);
HGFS_VP_VI_UNLOCK(sip->rootVnode);
/* Fall through to failure case */
/* Fail if a file is found. */
os_mutex_unlock(htp->mutex);
- DEBUG(VM_DEBUG_FAIL, "HgfsFileHashTableIsEmpty: %s "
- "still in use (file count=%d).\n",
- currFile->fileName, currFile->refCount);
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileHashTableIsEmpty: %s still in use.\n",
+ currFile->fileName);
return FALSE;
}
}
/*
*----------------------------------------------------------------------------
*
- * HgfsHandleIsSet --
+ * HgfsCheckAndReferenceHandle --
*
* Determines whether one of vnode's open file handles is currently set.
+ * If the handle is set the function increments its reference count.
+ * The function must be called while holding handleLock from the correspondent
+ * HgfsFile structure.
*
* Results:
- * Returns TRUE if the handle is set, FALSE if the handle is not set.
- * HGFS_ERR is returned on error.
+ * Returns 0 if the handle is set and had been referenced,
+ * EACCES if the handle is set but has an incompatible open mode,
+ * ENOENT if no hadnle is set for the vnode
*
* Side effects:
* None.
*----------------------------------------------------------------------------
*/
-Bool
-HgfsHandleIsSet(struct vnode *vp) // IN: Vnode to check handle of
+int
+HgfsCheckAndReferenceHandle(struct vnode *vp, // IN: Vnode to check handle of
+ int requestedOpenMode) // IN: Requested open mode
{
- HgfsOpenFile *ofp;
+ HgfsFile *fp;
Bool ret;
ASSERT(vp);
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
-
- os_mutex_lock(ofp->handleMutex);
-
- ret = ofp->handleRefCount ? TRUE : FALSE;
-
- os_mutex_unlock(ofp->handleMutex);
-
- return ret;
- }
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsHandleIncrementRefCount --
- *
- * Increments the reference count of the specified handle associated with
- * the vnode (vp).
- *
- * Results:
- * Returns 0 on success and HGFS_ERR on error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-HgfsHandleIncrementRefCount(struct vnode *vp) // IN
-{
- int ret = 0;
- HgfsOpenFile *ofp;
-
- ASSERT(vp);
+ fp = HGFS_VP_TO_FP(vp);
+ ASSERT(fp);
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
+ ret = fp->handleRefCount ? 0 : ENOENT;
+ if (ret == 0) {
+ if (HgfsIsModeCompatible(requestedOpenMode, fp->mode)) {
+ fp->handleRefCount++;
+ } else {
+ ret = EACCES;
+ }
}
- os_mutex_lock(ofp->handleMutex);
-
- ++ofp->handleRefCount;
-
- os_mutex_unlock(ofp->handleMutex);
-
return ret;
-}
+ }
/*
*
* Sets the file handle for the provided vnode if it has reference count
* equal to zero. The reference count of the handle must be increased when
- * the handle is set. This is done with HgfsHandleIsSet.
+ * the handle is set. This is done with HgfsCheckAndReferenceHandle.
+ * Caller must hold handleLock when invoking the function.
*
* Results:
- * Returns 0 on success and a non-zero error code on failure.
+ * None.
*
* Side effects:
* The handle may not be set again until it is cleared.
*----------------------------------------------------------------------------
*/
-int
+void
HgfsSetOpenFileHandle(struct vnode *vp, // IN: Vnode to set handle for
- HgfsHandle handle) // IN: Value of handle
+ HgfsHandle handle, // IN: Value of handle
+ HgfsMode openMode) // IN: Mode assosiated with the handle
{
- int ret = 0;
- HgfsOpenFile *ofp;
+ HgfsFile *fp;
ASSERT(vp);
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
-
- os_mutex_lock(ofp->handleMutex);
+ fp = HGFS_VP_TO_FP(vp);
+ ASSERT(fp);
- if (ofp->handleRefCount != 0) {
- ret = HGFS_ERR;
- goto out;
- }
- ++ofp->handleRefCount;
- ofp->handle = handle;
+ fp->handle = handle;
+ fp->mode = openMode;
+ fp->handleRefCount = 1;
DEBUG(VM_DEBUG_STATE, "HgfsSetOpenFileHandle: set handle for %s to %d\n",
- HGFS_VP_TO_FILENAME(vp), ofp->handle);
-
-out:
- os_mutex_unlock(ofp->handleMutex);
-
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "could not set file handle for %s\n",
- HGFS_VP_TO_FILENAME(vp));
- }
-
- return ret;
+ HGFS_VP_TO_FILENAME(vp), fp->handle);
}
int
HgfsGetOpenFileHandle(struct vnode *vp, // IN: Vnode to get handle for
- HgfsHandle *outHandle) // OUT: Filled with value of handle
+ HgfsHandle *outHandle) // OUT: Filled with value of handle
{
- HgfsOpenFile *ofp;
+ HgfsFile *fp;
int ret = 0;
ASSERT(vp);
ASSERT(outHandle);
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
+ fp = HGFS_VP_TO_FP(vp);
+ ASSERT(fp);
- os_mutex_lock(ofp->handleMutex);
+ os_rw_lock_lock_shared(fp->handleLock);
- if (ofp->handleRefCount == 0) {
- os_mutex_unlock(ofp->handleMutex);
+ if (fp->handleRefCount == 0) {
ret = HGFS_ERR;
+ } else {
+ *outHandle = fp->handle;
}
- *outHandle = ofp->handle;
-
- os_mutex_unlock(ofp->handleMutex);
+ os_rw_lock_unlock_shared(fp->handleLock);
return ret;
}
* HgfsReleaseOpenFileHandle --
*
* Decrements the reference count of one of the handles for the provided
- * vnode. If the reference count becomes zero, then the handle is cleared.
+ * vnode. If the reference count becomes zero, then the handle is cleared and
+ * the original handle is retruned to the caller.
*
* Results:
- * Returns 0 on success and a non-zero error code on failure.
+ * Returns new handle reference count.
+ * When the returned value is 0 returns the file handle which need to be closed
+ * on the host.
*
* Side effects:
- * The handle may be cleared.
+ * None.
*
*----------------------------------------------------------------------------
*/
int
-HgfsReleaseOpenFileHandle(struct vnode *vp, // IN
- Bool *closed) // OUT
+HgfsReleaseOpenFileHandle(struct vnode *vp, // IN
+ HgfsHandle *handleToClose) // OUT
{
- int ret = 0;
- HgfsOpenFile *ofp;
+ int ret;
+ HgfsFile *fp;
ASSERT(vp);
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
-
- *closed = FALSE;
+ fp = HGFS_VP_TO_FP(vp);
+ ASSERT(fp);
- os_mutex_lock(ofp->handleMutex);
+ os_rw_lock_lock_exclusive(fp->handleLock);
/* Make sure the reference count is not going negative! */
- ASSERT(ofp->handleRefCount > 0);
+ ASSERT(fp->handleRefCount > 0);
- --ofp->handleRefCount;
+ --fp->handleRefCount;
+ ret = fp->handleRefCount;
/* If the reference count has gone to zero, clear the handle. */
- if (ofp->handleRefCount == 0) {
+ if (ret == 0) {
DEBUG(VM_DEBUG_LOG, "closing directory handle\n");
- ofp->handle = 0;
- *closed = TRUE;
+ *handleToClose = fp->handle;
+ fp->handle = 0;
} else {
DEBUG(VM_DEBUG_LOG, "ReleaseOpenFileHandle with a refcount of: %d\n",
- ofp->handleRefCount);
+ fp->handleRefCount);
}
- os_mutex_unlock(ofp->handleMutex);
+ os_rw_lock_unlock_exclusive(fp->handleLock);
return ret;
}
/*
*----------------------------------------------------------------------------
*
- * HgfsShouldCloseOpenFileHandle --
+ * HgfsLookupExistingVnode --
*
- * Checks to see if the next call to HgfsReleaseOpenFileHandle will result in
- * the file handle being cleared.
+ * Locates existing vnode in the hash table that matches given file name.
+ * If a vnode that corresponds the given name does not exists then the function
+ * returns ENOENT.
+ * If the vnode exists the function behavior depends on failIfExist parameter.
+ * When failIfExist is true then the function return EEXIST, otherwise
+ * function references the vnode, assigns vnode pointer to vpp and return 0.
*
* Results:
- * Returns TRUE if the file handle will be cleared on the next
- * HgfsReleaseOpenFileHandle call and FALSE otherwise.
- * Returns HGFS_ERR if an error was encountered.
+ *
+ * Returns 0 if existing vnode is found and its address is returned in vpp or
+ * an error code otherwise.
*
* Side effects:
* None.
*----------------------------------------------------------------------------
*/
-Bool
-HgfsShouldCloseOpenFileHandle(struct vnode *vp) // IN: Vnode to clear handle for
+static int
+HgfsLookupExistingVnode(const char* fileName,
+ HgfsFileHashTable *htp,
+ Bool failIfExist,
+ struct vnode **vpp)
{
- HgfsOpenFile *ofp;
-
- ASSERT(vp);
-
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
-
- os_mutex_lock(ofp->handleMutex);
-
- if (ofp->handleRefCount == 1) {
- os_mutex_unlock(ofp->handleMutex);
- return TRUE;
+ HgfsFile* existingFp;
+ int err = ENOENT;
+ os_mutex_lock(htp->mutex);
+ /* First verify if a vnode for the filename is already allocated. */
+ existingFp = HgfsFindFile(fileName, htp);
+ if (existingFp != NULL) {
+ if (failIfExist) {
+ err = EEXIST;
+ } else {
+ err = vnode_get(existingFp->vnodep);
+ if (err == 0) {
+ *vpp = existingFp->vnodep;
+ } else {
+ /* vnode exists but unusable, remove HGFS context assosiated with it. */
+ DEBUG(VM_DEBUG_FAIL, "Removing HgfsFile assosiated with an unusable vnode\n");
+ HgfsRemoveFile(existingFp, htp);
+ err = ENOENT;
+ }
+ }
}
-
- os_mutex_unlock(ofp->handleMutex);
-
- return FALSE;
+ os_mutex_unlock(htp->mutex);
+ return err;
}
+/*
+ * Local functions (definitions)
+ */
+
+/* Internal versions of public functions to allow bypassing htp locking */
/*
*----------------------------------------------------------------------------
*
- * HgfsSetOpenFileMode --
+ * HgfsVnodeGetInt --
+ *
+ * Creates a vnode for the provided filename.
*
- * Sets the mode of the open file for the provided vnode.
+ * If a HgfsFile already exists for this filename then it is used and the associated
+ * vnode is referenced and returned.
+ * if a HgfsFile doesn't exist, a new vnode and HgfsFile structure is created.
*
* Results:
- * Returns 0 on success and a non-zero error code on failure.
+ * Returns 0 on success and a non-zero error code on failure. The new
+ * vnode is returned locked.
*
* Side effects:
- * The mode may not be set again until cleared.
+ * None.
*
*----------------------------------------------------------------------------
*/
-int
-HgfsSetOpenFileMode(struct vnode *vp, // IN: Vnode to set mode for
- HgfsMode mode) // IN: Mode to set to
+#if defined(__FreeBSD__)
+static int
+HgfsVnodeGetInt(struct vnode **vpp, // OUT: Filled with address of created vnode
+ struct vnode *dvp, // IN: Parent directory vnode
+ HgfsSuperInfo *sip, // IN: Superinfo
+ struct mount *vfsp, // IN: Filesystem structure
+ const char *fileName, // IN: Name of this file
+ HgfsFileType fileType, // IN: Tyoe of file
+ HgfsFileHashTable *htp, // IN: File hash
+ Bool rootVnode, // IN: Is this a root vnode?
+ Bool fileCreate, // IN: Is it a new file creation?
+ int permissions) // IN: Permissions for new files
{
- HgfsOpenFile *ofp;
-
- ASSERT(vp);
+ struct vnode *vp;
+ int ret;
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
+ HgfsFile *fp;
+ HgfsFile *existingFp;
- os_mutex_lock(ofp->modeMutex);
+ ASSERT(vpp);
+ ASSERT(sip);
+ ASSERT(vfsp);
+ ASSERT(fileName);
+ ASSERT(htp);
+ ASSERT(dvp != NULL || rootVnode);
- if (ofp->modeIsSet) {
- DEBUG(VM_DEBUG_FAIL, "**HgfsSetOpenFileMode: mode for %s already set to %d; "
- "cannot set to %d\n", HGFS_VP_TO_FILENAME(vp), ofp->mode, mode);
- os_mutex_unlock(ofp->modeMutex);
- return HGFS_ERR;
+ /* First verify if a vnode for the filename is already allocated. */
+ ret = HgfsLookupExistingVnode(fileName, htp, fileCreate, vpp);
+ if (ret != ENOENT) {
+ return ret;
}
- ofp->mode = mode;
- ofp->modeIsSet = TRUE;
-
- DEBUG(VM_DEBUG_STATE, "HgfsSetOpenFileMode: set mode for %s to %d\n",
- HGFS_VP_TO_FILENAME(vp), ofp->mode);
+ /*
+ * Here we need to construct the vnode for the kernel as well as our
+ * internal file system state. Our internal state described by
+ * HgfsFile structure which is kept per-file. There is no state information assosiated
+ * with file descriptor. The reason is that when OS invokes vnode methods
+ * it does not provide information about file descriptor that was used to initiate the
+ * IO. We have a one-to-one mapping between vnodes and HgfsFiles.
+ */
+ if ((ret = getnewvnode(HGFS_FS_NAME, vfsp, &HgfsVnodeOps, &vp)) != 0) {
+ return ret;
+ }
- os_mutex_unlock(ofp->modeMutex);
+ /*
+ * Return a locked vnode to the caller.
+ */
+ ret = compat_lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL, curthread);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "Fatal: could not acquire lock on vnode\n");
+ goto destroyVnode;
+ }
- return 0;
-}
+ /*
+ * Now we'll initialize the vnode. We need to set the file type, vnode
+ * operations, flags, filesystem pointer, reference count, and device.
+ * After that we'll create our private structures and hang them from the
+ * vnode's v_data pointer.
+ */
+ switch (fileType) {
+ case HGFS_FILE_TYPE_REGULAR:
+ vp->v_type = VREG;
+ break;
+ case HGFS_FILE_TYPE_DIRECTORY:
+ vp->v_type = VDIR;
+ break;
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsGetOpenFileMode --
- *
- * Gets the mode of the file for the provided vnode.
- *
- * Results:
- * Returns 0 on success and a non-zero error code on failure.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
+ case HGFS_FILE_TYPE_SYMLINK:
+ vp->v_type = VLNK;
+ break;
-int
-HgfsGetOpenFileMode(struct vnode *vp, // IN: Vnode to get mode for
- HgfsMode *outMode) // OUT: Filled with mode
-{
- HgfsOpenFile *ofp;
+ default:
+ /* Hgfs only supports directories and regular files */
+ ret = EPERM;
+ goto destroyOut;
+ }
- ASSERT(vp);
- ASSERT(outMode);
+ /* We now allocate our private open file structure. */
+ fp = (void *)HgfsAllocFile(fileName, fileType, dvp, htp, permissions);
+ if (fp == NULL) {
+ ret = ENOMEM;
+ goto destroyOut;
+ }
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
+ fp->vnodep = vp;
+ vp->v_data = fp;
+ /* If this is going to be the root vnode, we have to mark it as such. */
+ if (rootVnode) {
+ vp->v_vflag |= VV_ROOT;
}
- os_mutex_lock(ofp->modeMutex);
+ existingFp = HgfsInsertFile(fileName, fp, htp);
- if (!ofp->modeIsSet) {
- os_mutex_unlock(ofp->modeMutex);
- return HGFS_ERR;
+ if (existingFp != NULL) { // Race occured, another thread inserted a node ahead of us
+ if (fileCreate) {
+ ret = EEXIST;
+ goto destroyOut;
+ }
+ compat_lockmgr(vp->v_vnlock, LK_RELEASE, NULL, curthread);
+ vput(vp);
+ vp = existingFp->vnodep;
+ /*
+ * Return a locked vnode to the caller.
+ */
+ ret = compat_lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL, curthread);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "Fatal: could not acquire lock on vnode\n");
+ goto destroyVnode;
+ }
+ HgfsFreeFile(fp);
}
- *outMode = ofp->mode;
-
- os_mutex_unlock(ofp->modeMutex);
+ /* Fill in the provided address with the new vnode. */
+ *vpp = vp;
+ /* Return success */
return 0;
-}
+ /* Cleanup points for errors. */
+destroyOut:
+ compat_lockmgr(vp->v_vnlock, LK_RELEASE, NULL, curthread);
+destroyVnode:
+ vput(vp);
+ return ret;
+}
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsClearOpenFileMode --
- *
- * Clears the mode of the file for the provided vnode.
- *
- * Results:
- * Returns 0 on success and a non-zero error code on failure.
- *
- * Side effects:
- * The mode may be set again.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-HgfsClearOpenFileMode(struct vnode *vp) // IN: Vnode to clear mode for
+#elif defined(__APPLE__)
+static int
+HgfsVnodeGetInt(struct vnode **vpp, // OUT
+ struct vnode *dvp, // IN
+ HgfsSuperInfo *sip, // IN
+ struct mount *vfsp, // IN
+ const char *fileName, // IN
+ HgfsFileType fileType, // IN
+ HgfsFileHashTable *htp, // IN
+ Bool rootVnode, // IN
+ Bool fileCreate, // IN
+ int permissions) // IN
{
- HgfsOpenFile *ofp;
+ struct vnode *vp;
+ struct vnode_fsparam params;
+ int ret;
+ HgfsFile *fp = NULL;
+ HgfsFile *existingFp;
- ASSERT(vp);
+ ASSERT(vpp);
+ ASSERT(sip);
+ ASSERT(vfsp);
+ ASSERT(fileName);
+ ASSERT(htp);
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
+ /* First verify if a vnode for the filename is already allocated. */
+ ret = HgfsLookupExistingVnode(fileName, htp, fileCreate, vpp);
+ if (ret != ENOENT) {
+ return ret;
}
- os_mutex_lock(ofp->modeMutex);
-
- ofp->mode = 0;
- ofp->modeIsSet = FALSE;
-
- DEBUG(VM_DEBUG_STATE, "HgfsClearOpenOpenFileMode: cleared %s's mode\n",
- HGFS_VP_TO_FILENAME(vp));
-
- os_mutex_unlock(ofp->modeMutex);
+ params.vnfs_mp = vfsp;
+ params.vnfs_str = NULL;
+ params.vnfs_dvp = NULL;
+ params.vnfs_fsnode = NULL;
+ params.vnfs_vops = HgfsVnodeOps;
+ params.vnfs_marksystem = FALSE;
+ params.vnfs_rdev = 0;
+ params.vnfs_filesize = 0;
+ params.vnfs_cnp = NULL;
+ /* Do not let OS X cache vnodes for us. */
+ params.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
- return 0;
-}
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsFileLock --
- *
- * Locks the HgfsFile associated with the vnode (vp). The type specifies is
- * we are locking for reads or writes. We only lock the HgfsFile on OS X
- * because FreeBSD vnodes are locked when handed to the VFS layer and there
- * is a 1:1 mapping between vnodes and HgfsFile objects so no extra locking
- * is required.
- *
- * Results:
- * Returns 0 on success and an error code on error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-HgfsFileLock(struct vnode *vp, // IN: Vnode to lock
- HgfsLockType type) // IN: Reader or Writer lock?
-{
-#if defined(__APPLE__)
- HgfsOpenFile *ofp;
-
- ASSERT(vp);
-
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
-
- return HgfsFileLockOfp(ofp, type);
-#else
- NOT_IMPLEMENTED();
-#endif
-}
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsFileUnlock --
- *
- * Unlocks the HgfsFile associated with the vnode (vp). Results are
- * undefined the type of lock specified is different than the one that the
- * vnode was locked with originally.
- *
- * Results:
- * Returns 0 on success and an error code on error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-HgfsFileUnlock(struct vnode *vp, // IN: Vnode to unlock
- HgfsLockType type) // IN: Reader or Writer lock?
-{
-#if defined(__APPLE__)
- HgfsOpenFile *ofp;
-
- ASSERT(vp);
-
- ofp = HGFS_VP_TO_OFP(vp);
- if (!ofp) {
- return HGFS_ERR;
- }
-
- return HgfsFileUnlockOfp(ofp, type);
-#else
- NOT_IMPLEMENTED();
-#endif
-}
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsFileLockOfp --
- *
- * Locks the HgfsFile associated with the HgfsOpenFile. This funciton should
- * only be called by HgfsFileLock
- *
- * Results:
- * Returns 0 on success and an error code on error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-HgfsFileLockOfp(HgfsOpenFile *ofp, // IN: HgfsOpenFile to lcok
- HgfsLockType type) // IN: Reader or Writer lock?
-{
-#if defined(__APPLE__)
- ASSERT(ofp);
-
- if (type == HGFS_READER_LOCK) {
- os_rw_lock_lock_shared(ofp->rwFileLock);
- } else if (type == HGFS_WRITER_LOCK) {
- os_rw_lock_lock_exclusive(ofp->rwFileLock);
- } else {
- return HGFS_ERR;
- }
- return 0;
-#else
- NOT_IMPLEMENTED();
-#endif
-}
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsFileUnlockOfp --
- *
- * Unlocks the HgfsFile associated with the HgfsOpenFile. This funciton should
- * only be called by inactive, reclaim and the HgfsFileUnlock routine.
- *
- * Results:
- * Returns 0 on success and an error code on error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-int
-HgfsFileUnlockOfp(HgfsOpenFile *ofp, // IN: HgfsOpenFile to unlcok
- HgfsLockType type) // IN: Reader or Writer lock?
-{
-#if defined(__APPLE__)
- ASSERT(ofp);
-
- if (type == HGFS_READER_LOCK) {
- os_rw_lock_unlock_shared(ofp->rwFileLock);
- } else if (type == HGFS_WRITER_LOCK) {
- os_rw_lock_unlock_exclusive(ofp->rwFileLock);
- } else {
- return HGFS_ERR;
- }
- return 0;
-#else
- NOT_IMPLEMENTED();
-#endif
-}
-
-
-/*
- * Local functions (definitions)
- */
-
-/* Internal versions of public functions to allow bypassing htp locking */
-
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsVnodeGetInt --
- *
- * Creates a vnode for the provided filename.
- *
- * This will always allocate a vnode and HgfsOpenFile. If a HgfsFile
- * already exists for this filename then that is used, if a HgfsFile doesn't
- * exist, one is created.
- *
- * Results:
- * Returns 0 on success and a non-zero error code on failure. The new
- * vnode is returned locked.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-#if defined(__FreeBSD__)
-static int
-HgfsVnodeGetInt(struct vnode **vpp, // OUT: Filled with address of created vnode
- HgfsSuperInfo *sip, // IN: Superinfo
- struct mount *vfsp, // IN: Filesystem structure
- const char *fileName, // IN: Name of this file
- HgfsFileType fileType, // IN: Tyoe of file
- HgfsFileHashTable *htp, // IN: File hash
- Bool rootVnode) // IN: Is this a root vnode?
-{
- struct vnode *vp;
- int ret = 0;
-
- ASSERT(vpp);
- ASSERT(sip);
- ASSERT(vfsp);
- ASSERT(fileName);
- ASSERT(htp);
-
- /*
- * Here we need to construct the vnode for the kernel as well as our
- * internal file system state. Our internal state consists of
- * a HgfsOpenFile and a HgfsFile. The HgfsOpenFile is state kept per-open
- * file; the HgfsFile state is kept per-file. We have a one-to-one mapping
- * between vnodes and HgfsOpenFiles, and a many-to-one mapping from each of
- * those to a HgfsFile.
- *
- * Note that it appears the vnode is intended to be used as a per-file
- * structure, but we are using it as a per-open-file. The sole exception
- * for this is the root vnode because it is returned by HgfsRoot(). This
- * also means that reference counts for all vnodes except the root should
- * be one; the reference count in our HgfsFile takes on the role of the
- * vnode reference count.
- */
- if ((ret = getnewvnode(HGFS_FS_NAME, vfsp, &HgfsVnodeOps, &vp)) != 0) {
- return ret;
- }
-
- /*
- * Return a locked vnode to the caller.
- */
- ret = compat_lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL, curthread);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "Fatal: could not acquire lock on vnode\n");
- goto destroyVnode;
- }
-
- /*
- * Now we'll initialize the vnode. We need to set the file type, vnode
- * operations, flags, filesystem pointer, reference count, and device.
- * After that we'll create our private structures and hang them from the
- * vnode's v_data pointer.
- */
- switch (fileType) {
- case HGFS_FILE_TYPE_REGULAR:
- vp->v_type = VREG;
- break;
-
- case HGFS_FILE_TYPE_DIRECTORY:
- vp->v_type = VDIR;
- break;
-
- case HGFS_FILE_TYPE_SYMLINK:
- vp->v_type = VLNK;
- break;
-
- default:
- /* Hgfs only supports directories and regular files */
- ret = EPERM;
- goto destroyOut;
- }
-
- /*
- * We now allocate our private open file structure. This will correctly
- * initialize the per-open-file state, as well as locate (or create if
- * necessary) the per-file state.
- */
- vp->v_data = (void *)HgfsAllocOpenFile(fileName, fileType, htp);
- if (vp->v_data == NULL) {
- ret = ENOMEM;
- goto destroyOut;
- }
-
- /* If this is going to be the root vnode, we have to mark it as such. */
- if (rootVnode) {
- vp->v_vflag |= VV_ROOT;
- }
-
- /* Fill in the provided address with the new vnode. */
- *vpp = vp;
-
- /* Return success */
- return 0;
-
- /* Cleanup points for errors. */
-destroyOut:
- compat_lockmgr(vp->v_vnlock, LK_RELEASE, NULL, curthread);
-destroyVnode:
- vrele(vp);
- return ret;
-}
-
-#elif defined(__APPLE__)
-static int
-HgfsVnodeGetInt(struct vnode **vpp, // OUT
- HgfsSuperInfo *sip, // IN
- struct mount *vfsp, // IN
- const char *fileName, // IN
- HgfsFileType fileType, // IN
- HgfsFileHashTable *htp, // IN
- Bool rootVnode) // IN
-{
- struct vnode *vp;
- struct vnode_fsparam params;
- int ret = 0;
- HgfsOpenFile *ofp;
-
- ASSERT(vpp);
- ASSERT(sip);
- ASSERT(vfsp);
- ASSERT(fileName);
- ASSERT(htp);
-
- params.vnfs_mp = vfsp;
- params.vnfs_str = NULL;
- params.vnfs_dvp = NULL;
- params.vnfs_fsnode = NULL;
- params.vnfs_vops = HgfsVnodeOps;
- params.vnfs_marksystem = FALSE;
- params.vnfs_rdev = 0;
- params.vnfs_filesize = 0;
- params.vnfs_cnp = NULL;
- /* Do not let OS X cache vnodes for us. */
- params.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
-
- if (rootVnode) {
- params.vnfs_markroot = TRUE;
- } else {
- params.vnfs_markroot = FALSE;
- }
+ if (rootVnode) {
+ params.vnfs_markroot = TRUE;
+ } else {
+ params.vnfs_markroot = FALSE;
+ }
/*
* Now we'll initialize the vnode. We need to set the file type, vnode
* operations, flags, filesystem pointer, reference count, and device.
* After that we'll create our private structures and hang them from the
- * vnode's v_data pointer.
- */
- switch (fileType) {
- case HGFS_FILE_TYPE_REGULAR:
- params.vnfs_vtype = VREG;
- break;
-
- case HGFS_FILE_TYPE_DIRECTORY:
- params.vnfs_vtype = VDIR;
- break;
-
- case HGFS_FILE_TYPE_SYMLINK:
- params.vnfs_vtype = VLNK;
- break;
-
- default:
- /* Hgfs only supports directories and regular files */
- ret = EINVAL;
- goto out;
- }
-
- /*
- * We now allocate our private open file structure. This will correctly
- * initialize the per-open-file state, as well as locate (or create if
- * necessary) the per-file state.
- */
-
- ofp = HgfsAllocOpenFile(fileName, fileType, htp);
-
- params.vnfs_fsnode = (void *)ofp;
- if (params.vnfs_fsnode == NULL) {
- ret = ENOMEM;
- goto out;
- }
-
-
- ret = vnode_create(VNCREATE_FLAVOR, sizeof(params), ¶ms, &vp);
- ofp->vnodep = vp;
-
- /* Get a soft FS reference to the vnode. This tells the system that the vnode
- * has data associated with it. It is considered a weak reference though, in that
- * it does not prevent the system from reusing the vnode.
- */
- vnode_addfsref(vp);
-
- if (ret != 0) {
- DEBUG(VM_DEBUG_FAIL, "Failed to create vnode");
- ret = EINVAL;
- goto destroyVnode;
- }
-
- /* Fill in the provided address with the new vnode. */
- *vpp = vp;
-
- /* Return success */
- return 0;
-
- /* Cleanup points for errors. */
-destroyVnode:
- vnode_put(vp);
-out:
- return ret;
-}
-#else
- NOT_IMPLEMENTED();
-#endif
-
-/* Allocation/initialization/free of open file state */
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * HgfsAllocOpenFile --
- *
- * Allocates and initializes an open file structure. Also finds or, if
- * necessary, creates the underlying HgfsFile per-file state.
- *
- * Results:
- * Returns a pointer to the open file on success, NULL on error.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-static HgfsOpenFile *
-HgfsAllocOpenFile(const char *fileName, // IN: Name of file
- HgfsFileType fileType, // IN: Type of file
- HgfsFileHashTable *htp) // IN: Hash table
-{
- HgfsOpenFile *ofp;
-
- ASSERT(fileName);
- ASSERT(htp);
-
- /*
- * We allocate and initialize our open-file state.
+ * vnode's v_data pointer.
*/
- ofp = os_malloc(sizeof *ofp, M_ZERO | M_WAITOK);
- if (!ofp) {
- DEBUG(VM_DEBUG_FAIL, "Failed to allocate memory");
- return NULL;
- }
+ switch (fileType) {
+ case HGFS_FILE_TYPE_REGULAR:
+ params.vnfs_vtype = VREG;
+ break;
- ofp->mode = 0;
- ofp->modeIsSet = FALSE;
+ case HGFS_FILE_TYPE_DIRECTORY:
+ params.vnfs_vtype = VDIR;
+ break;
- ofp->handleRefCount = 0;
- ofp->handle = 0;
+ case HGFS_FILE_TYPE_SYMLINK:
+ params.vnfs_vtype = VLNK;
+ break;
- ofp->handleMutex = os_mutex_alloc_init("hgfs_mtx_handle");
- if (!ofp->handleMutex) {
- goto destroyOut;
+ default:
+ /* Hgfs only supports directories and regular files */
+ ret = EINVAL;
+ goto out;
}
- ofp->modeMutex = os_mutex_alloc_init("hgfs_mtx_mode");
- if (!ofp->modeMutex) {
- goto destroyOut;
- }
+ fp = HgfsAllocFile(fileName, fileType, dvp, htp, permissions);
-#if defined(__APPLE__)
- ofp->rwFileLock = os_rw_lock_alloc_init("hgfs_rw_file_lock");
- if (!ofp->rwFileLock) {
- goto destroyOut;
+ params.vnfs_fsnode = (void *)fp;
+ if (params.vnfs_fsnode == NULL) {
+ ret = ENOMEM;
+ goto out;
}
-#endif
- /*
- * Now we get a reference to the underlying per-file state.
- */
- ofp->hgfsFile = HgfsGetFile(fileName, fileType, htp);
- if (!ofp->hgfsFile) {
- goto destroyOut;
+ ret = vnode_create(VNCREATE_FLAVOR, sizeof(params), ¶ms, &vp);
+ if (ret != 0) {
+ DEBUG(VM_DEBUG_FAIL, "Failed to create vnode");
+ goto out;
}
- /* Success */
- return ofp;
+ fp->vnodep = vp;
-destroyOut:
- ASSERT(ofp);
+ existingFp = HgfsInsertFile(fileName, fp, htp);
- if (ofp->handleMutex) {
- os_mutex_free(ofp->handleMutex);
+ if (existingFp != NULL) { // Race occured, another thread inserted a node ahead of us
+ vnode_put(vp);
+ if (fileCreate) {
+ ret = EEXIST;
+ goto out;
+ }
+ vp = existingFp->vnodep;
+ HgfsFreeFile(fp);
+ } else {
+ /* Get a soft FS reference to the vnode. This tells the system that the vnode
+ * has data associated with it. It is considered a weak reference though, in that
+ * it does not prevent the system from reusing the vnode.
+ */
+ vnode_addfsref(vp);
}
- if (ofp->modeMutex) {
- os_mutex_free(ofp->modeMutex);
- }
+ /* Fill in the provided address with the new vnode. */
+ *vpp = vp;
-#if defined(__APPLE__)
- if (ofp->rwFileLock) {
- os_rw_lock_free(ofp->rwFileLock);
+ /* Return success */
+ return 0;
+
+out:
+ if (fp) {
+ HgfsFreeFile(fp);
}
+ return ret;
+}
+#else
+ NOT_IMPLEMENTED();
#endif
- os_free(ofp, sizeof *ofp);
- return NULL;
-
-}
+/* Allocation/initialization/free of open file state */
/*
*----------------------------------------------------------------------------
*
- * HgfsFreeOpenFile --
+ * HgfsAllocFile --
*
- * Frees the provided open file.
+ * Allocates and initializes a file structure.
*
* Results:
- * Returns 0 on success and a non-zero error code on failure.
+ * Returns a pointer to the open file on success, NULL on error.
*
* Side effects:
* None.
*----------------------------------------------------------------------------
*/
-static void
-HgfsFreeOpenFile(HgfsOpenFile *ofp, // IN: Open file to free
- HgfsFileHashTable *htp) // IN: File hash table
+static HgfsFile *
+HgfsAllocFile(const char *fileName, // IN: Name of file
+ HgfsFileType fileType, // IN: Type of file
+ struct vnode *dvp, // IN: Parent directory vnode
+ HgfsFileHashTable *htp, // IN: Hash table
+ int permissions) // IN: permissions for creating new files
{
- ASSERT(ofp);
- ASSERT(htp);
-
- /*
- * First we release our reference to the underlying per-file state.
- */
- HgfsReleaseFile(ofp->hgfsFile, htp);
+ HgfsFile *fp;
+ fp = os_malloc(sizeof *fp, M_ZERO | M_WAITOK);
- /*
- * Then we destroy anything initialized and free the open file.
- */
-#if defined(__APPLE__)
- os_rw_lock_free(ofp->rwFileLock);
-#endif
- os_mutex_free(ofp->handleMutex);
- os_mutex_free(ofp->modeMutex);
+ if (fp != NULL) {
+ DEBUG(VM_DEBUG_INFO, "HgfsGetFile: allocated HgfsFile for %s.\n", fileName);
- os_free(ofp, sizeof *ofp);
+ if (HgfsInitFile(fp, dvp, fileName, fileType, permissions) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "Failed to initialize HgfsFile");
+ os_free(fp, sizeof(*fp));
+ fp = NULL;
+ }
+ } else {
+ DEBUG(VM_DEBUG_FAIL, "Failed to allocate memory");
+ }
+ return fp;
}
-
/* Acquiring/releasing file state */
/*
*----------------------------------------------------------------------------
*
- * HgfsGetFile --
+ * HgfsInsertFile --
*
- * Gets the file for the provided filename.
+ * Inserts a HgfsFile object into the hash table if the table does not
+ * contain an object with the same name.
+ * If an object with the same name already exists in the hash table
+ * then does nothing and just returns pointer to the existing object.
*
* Results:
- * Returns a pointer to the file on success, NULL on error.
+ * Returns a pointer to the file if there is a name collision, NULL otherwise.
*
* Side effects:
- * None.
+ * If there is a name collision adds reference to vnode IOrefcount.
*
*----------------------------------------------------------------------------
*/
static HgfsFile *
-HgfsGetFile(const char *fileName, // IN: Filename to get file for
- HgfsFileType fileType, // IN: Type of file
- HgfsFileHashTable *htp) // IN: Hash table to look in
+HgfsInsertFile(const char *fileName, // IN: Filename to get file for
+ HgfsFile *fp, // IN: HgfsFile object to insert
+ HgfsFileHashTable *htp) // IN: Hash table to look in
{
- HgfsFile *fp;
- HgfsFile *newfp;
- int err;
+ HgfsFile *existingFp = NULL;
ASSERT(fileName);
ASSERT(htp);
*/
os_mutex_lock(htp->mutex);
- fp = HgfsFindFile(fileName, htp);
- if (fp) {
- /* Signify our reference to this file. */
- os_mutex_lock(fp->mutex);
- fp->refCount++;
- os_mutex_unlock(fp->mutex);
- goto out;
- }
-
- /* Drop the lock here, since we can block on os_malloc */
- os_mutex_unlock(htp->mutex);
-
- /*
- * If it doesn't exist we create one. Ideally this should never fail, since
- * we are ready to block till we get memory. Note that while we are creating
- * HgfsFile, other thread(s) could also be creating HgfsFile at the same time.
- * Thus once we get memory, we acquire the lock and check hash table to detect
- * any race condition.
- */
- newfp = os_malloc(sizeof *newfp, M_ZERO | M_WAITOK);
-
- if (!newfp) {
- /* newfp is NULL already */
- DEBUG(VM_DEBUG_FAIL, "Failed to allocate memory");
- return NULL;
- }
-
- /* Acquire the lock and check for races */
- os_mutex_lock(htp->mutex);
-
- fp = HgfsFindFile(fileName, htp);
- if (fp) {
- /*
- * Some other thread allocated HgfsFile before us. Free newfp
- * and get the reference on this file.
- */
- os_free(newfp, sizeof(*newfp));
- os_mutex_lock(fp->mutex);
- fp->refCount++;
- os_mutex_unlock(fp->mutex);
- goto out;
+ existingFp = HgfsFindFile(fileName, htp);
+ if (existingFp) { // HgfsFile with this name alrady exists
+ int ret = vnode_get(existingFp->vnodep);
+ if (ret != 0) {
+ /*
+ * It is not clear why vnode_get may fail while there is HgfsFile in
+ * our hash table. Most likely it will never happen.
+ * However if this ever occur the safest approach is to remove
+ * the HgsfFle structure from the hash table but do dont free it.
+ * It should be freed later on when the vnode is recycled.
+ */
+ DblLnkLst_Unlink1(&existingFp->listNode);
+ existingFp = NULL;
+ }
}
-
- DEBUG(VM_DEBUG_INFO, "HgfsGetFile: allocated HgfsFile for %s.\n", fileName);
-
- err = HgfsInitFile(newfp, fileName, fileType);
- if (err) {
- os_free(newfp, sizeof(*newfp));
- newfp = NULL;
- goto out;
+ if (existingFp) {
+ HgfsAddFile(fp, htp);
}
- /*
- * This is guaranteed to not add a duplicate since after acquiring the lock on the
- * hash table, we rechecked above to detect if the file was present and have held
- * the lock until now.
- */
- HgfsAddFile(newfp, htp);
- fp = newfp;
-
-out:
os_mutex_unlock(htp->mutex);
-
- DEBUG(VM_DEBUG_DONE, "HgfsGetFile: done\n");
- return fp;
+ return existingFp;
}
*
* HgfsReleaseFile --
*
- * Releases a reference to the provided file. If the reference count of
- * this file becomes zero, the file structure is removed from the hash table
- * and freed.
+ * Removes HgfsFile structure from the hash table and releases it.
*
* Results:
- * Returns 0 on success and a non-zero error code on failure.
+ * None.
*
* Side effects:
* None.
ASSERT(fp);
ASSERT(htp);
- /*
- * Decrement this file's reference count. If it becomes zero, then we
- * remove it from the hash table and free it.
- */
- os_mutex_lock(fp->mutex);
-
- if ( !(--fp->refCount) ) {
- os_mutex_unlock(fp->mutex);
-
- /* Remove file from hash table, then clean up. */
- HgfsRemoveFile(fp, htp);
-
- DEBUG(VM_DEBUG_INFO, "HgfsReleaseFile: freeing HgfsFile for %s.\n",
- fp->fileName);
-
- os_mutex_free(fp->mutex);
- os_free(fp, sizeof *fp);
- return;
- }
+ os_mutex_lock(htp->mutex);
- DEBUG(VM_DEBUG_INFO, "HgfsReleaseFile: %s has %d references.\n",
- fp->fileName, fp->refCount);
+ DEBUG(VM_DEBUG_INFO, "HgfsReleaseFile: freeing HgfsFile for %s.\n",
+ fp->fileName);
+ /* Take this file off its list */
+ DblLnkLst_Unlink1(&fp->listNode);
+ HgfsFreeFile(fp);
- os_mutex_unlock(fp->mutex);
+ os_mutex_unlock(htp->mutex);
}
static int
HgfsInitFile(HgfsFile *fp, // IN: File to initialize
+ struct vnode *dvp, // IN: Paretn directory vnode
const char *fileName, // IN: Name of file
- HgfsFileType fileType) // IN: Type of file
+ HgfsFileType fileType, // IN: Type of file
+ int permissions) // IN: Permissions for new files
{
int len;
return HGFS_ERR;
}
- fp->mutex = os_mutex_alloc_init("hgfs_file_mutex_lock");
- if (!fp->mutex) {
- return HGFS_ERR;
- }
-
fp->fileNameLength = len;
memcpy(fp->fileName, fileName, len + 1);
fp->fileName[fp->fileNameLength] = '\0';
* We save the file type so we can recreate a vnode for the HgfsFile without
* sending a request to the Hgfs Server.
*/
- fp->fileType = fileType;
+ fp->permissions = permissions;
/* Initialize the links to place this file in our hash table. */
DblLnkLst_Init(&fp->listNode);
*/
HgfsNodeIdHash(fp->fileName, fp->fileNameLength, &fp->nodeId);
- /* The caller is the single reference. */
- fp->refCount = 1;
+ fp->mode = 0;
+ fp->modeIsSet = FALSE;
+
+ fp->handleRefCount = 0;
+ fp->handle = 0;
+
+ fp->handleLock = os_rw_lock_alloc_init("hgfs_rw_handle_lock");
+ if (!fp->handleLock) {
+ goto destroyOut;
+ }
+
+ fp->modeMutex = os_mutex_alloc_init("hgfs_mtx_mode");
+ if (!fp->modeMutex) {
+ goto destroyOut;
+ }
+
+#if defined(__APPLE__)
+ fp->rwFileLock = os_rw_lock_alloc_init("hgfs_rw_file_lock");
+ if (!fp->rwFileLock) {
+ goto destroyOut;
+ }
+ DEBUG(VM_DEBUG_LOG, "fp = %p, Lock = %p .\n", fp, fp->rwFileLock);
+
+#endif
+
+ fp->parent = dvp;
+ if (dvp != NULL) {
+ vnode_ref(dvp);
+ }
+ /* Success */
return 0;
-}
+destroyOut:
+ ASSERT(fp);
+
+ if (fp->handleLock) {
+ os_rw_lock_free(fp->handleLock);
+ }
-/* Adding/finding/removing file state from hash table */
+ if (fp->modeMutex) {
+ os_mutex_free(fp->modeMutex);
+ }
+
+#if defined(__APPLE__)
+ if (fp->rwFileLock) {
+ os_rw_lock_free(fp->rwFileLock);
+ }
+ DEBUG(VM_DEBUG_LOG, "Destroying fp = %p, Lock = %p .\n", fp, fp->rwFileLock);
+#endif
+
+ os_free(fp, sizeof *fp);
+ return HGFS_ERR;
+}
/*
*----------------------------------------------------------------------------
*
- * HgfsAddFile --
- *
- * Adds the file to the hash table.
+ * HgfsFreeFile --
*
- * This function must be called with the hash table lock held. This is done
- * so adding the file in the hash table can be made with any other
- * operations (such as previously finding out that this file wasn't in the
- * hash table).
+ * Preforms necessary cleanup and frees the memory allocated for HgfsFile.
*
* Results:
- * Returns 0 on success and a non-zero error code on failure.
+ * None.
*
* Side effects:
* None.
*----------------------------------------------------------------------------
*/
-static void
-HgfsAddFile(HgfsFile *fp, // IN: File to add
- HgfsFileHashTable *htp) // IN: Hash table to add to
+void
+HgfsFreeFile(HgfsFile *fp)
{
- unsigned int index;
-
ASSERT(fp);
- ASSERT(htp);
-
- index = HgfsFileNameHash(fp->fileName);
-
- /* Add this file to the end of the bucket's list */
- DblLnkLst_LinkLast(HGFS_FILE_HT_HEAD(htp, index), &fp->listNode);
+ os_rw_lock_free(fp->handleLock);
+ os_mutex_free(fp->modeMutex);
+#if defined(__APPLE__)
+ DEBUG(VM_DEBUG_LOG, "Trace enter, fp = %p, Lock = %p .\n", fp, fp->rwFileLock);
+ os_rw_lock_free(fp->rwFileLock);
+#endif
+ if (fp->parent != NULL) {
+ vnode_rele(fp->parent);
+ }
+ os_free(fp, sizeof *fp);
}
+/* Adding/finding/removing file state from hash table */
+
/*
*----------------------------------------------------------------------------
*
- * HgfsRemoveFile --
+ * HgfsAddFile --
*
- * Removes file from the hash table.
+ * Adds the file to the hash table.
*
- * Note that unlike the other two hash functions, this one performs its own
- * locking since the removal doesn't need to be atomic with other
- * operations. (This could change in the future if the functions that use
- * this one are reorganized.)
+ * This function must be called with the hash table lock held. This is done
+ * so adding the file in the hash table can be made with any other
+ * operations (such as previously finding out that this file wasn't in the
+ * hash table).
*
* Results:
* Returns 0 on success and a non-zero error code on failure.
*/
static void
-HgfsRemoveFile(HgfsFile *fp, // IN: File to remove
- HgfsFileHashTable *htp) // IN: Hash table to remove from
+HgfsAddFile(HgfsFile *fp, // IN: File to add
+ HgfsFileHashTable *htp) // IN: Hash table to add to
{
+ unsigned int index;
+
ASSERT(fp);
ASSERT(htp);
- os_mutex_lock(htp->mutex);
-
- /* Take this file off its list */
- DblLnkLst_Unlink1(&fp->listNode);
+ LCK_MTX_ASSERT(htp->mutex);
+ index = HgfsFileNameHash(fp->fileName);
- os_mutex_unlock(htp->mutex);
+ /* Add this file to the end of the bucket's list */
+ DblLnkLst_LinkLast(HGFS_FILE_HT_HEAD(htp, index), &fp->listNode);
}
ASSERT(fileName);
ASSERT(htp);
+ LCK_MTX_ASSERT(htp->mutex);
/* Determine which bucket. */
index = HgfsFileNameHash(fileName);
}
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRemoveFile --
+ *
+ * Removes file from the hash table.
+ *
+ * Note that unlike the other two hash functions, this one performs its own
+ * locking since the removal doesn't need to be atomic with other
+ * operations. (This could change in the future if the functions that use
+ * this one are reorganized.)
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+HgfsRemoveFile(HgfsFile *fp, // IN: File to remove
+ HgfsFileHashTable *htp) // IN: Hash table to remove from
+{
+ ASSERT(fp);
+ ASSERT(htp);
+
+ os_mutex_lock(htp->mutex);
+
+ /* Take this file off its list */
+ DblLnkLst_Unlink1(&fp->listNode);
+
+ os_mutex_unlock(htp->mutex);
+}
+
+
/* Other utility functions */
return;
}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsIsModeCompatible --
+ *
+ * Verifies if the requested open mode for the file is compatible
+ * with already assigned open mode.
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsModeCompatible(HgfsMode requestedMode, // IN: Requested open mode
+ HgfsMode existingMode) // IN: Existing open mode
+{
+ DEBUG(VM_DEBUG_LOG, "Compare mode %d with %d.\n", requestedMode, existingMode);
+ return (existingMode == HGFS_OPEN_MODE_READ_WRITE ||
+ requestedMode == existingMode);
+}
+
/* Conversion between different state structures */
#if defined(__FreeBSD__)
-# define HGFS_VP_TO_OFP(vp) \
- ((HgfsOpenFile *)(vp)->v_data)
+# define HGFS_VP_TO_FP(vp) \
+ ((HgfsFile *)(vp)->v_data)
#elif defined(__APPLE__)
-# define HGFS_VP_TO_OFP(vp) \
- ((HgfsOpenFile *)vnode_fsnode(vp))
+# define HGFS_VP_TO_FP(vp) \
+ ((HgfsFile *)vnode_fsnode(vp))
#endif
-#define HGFS_VP_TO_FP(vp) \
- ((HgfsFile *)(HGFS_VP_TO_OFP(vp))->hgfsFile)
-
#define HGFS_FP_TO_VP(fp) \
(fp)->vnodep
#define HGFS_VP_TO_RWLOCKP(vp) \
&(HGFS_VP_TO_RWLOCK(vp))
-#define HGFS_VP_TO_HGFSFILETYPE(vp) \
- HGFS_VP_TO_FP(vp)->fileType
+#define HGFS_VP_TO_PERMISSIONS(vp) \
+ HGFS_VP_TO_FP(vp)->permissions
-#define HGFS_OFP_TO_FP(ofp) \
- (ofp)->hgfsFile
/*
* Types
char fileName[MAXPATHLEN + 1];
uint32_t fileNameLength;
ino_t nodeId;
- /*
- * The file type is saved so additional per-open-file vnodes can be
- * recreated from a Hgfs file without sending a request to the Hgfs server.
- */
- HgfsFileType fileType;
-
- OS_MUTEX_T *mutex;
- uint32_t refCount;
-} HgfsFile;
-/*
- * State kept per vnode, which implies per open file within a process.
- *
- * Once created, the hgfsFile and vnode/vnodep values are read-only. The
- * handle and mode will change throughout this structure's existence.
- */
-typedef struct HgfsOpenFile {
/*
- * Pointer to the single Hgfs file state structure shared amongst all open
- * instances of this file.
+ * A pointer back to the vnode this HgfsFile is for.
*/
- HgfsFile *hgfsFile;
+ struct vnode *vnodep;
/*
- * A pointer back to the vnode this open-file state is for.
+ * A pointer back to the parent directory vnode.
*/
- struct vnode *vnodep;
+ struct vnode *parent;
/*
- * Mode specified for this file to be created with. This is necessary
+ * Mode (permissions) specified for this file to be created with. This is necessary
* because create is called with the mode then open is called without it.
* We save this value in create and access it in open.
+ * This field is set during initialization of HGFS and is never changed.
*/
+ int permissions;
+
+ /* Mode that was used to open the handle on the host */
HgfsMode mode;
Bool modeIsSet;
OS_MUTEX_T *modeMutex;
*/
uint32_t handleRefCount;
HgfsHandle handle;
- OS_MUTEX_T *handleMutex;
+ OS_RWLOCK_T *handleLock;
/*
* One big difference between the OS X and FreeBSD VFS layers is that the
OS_RWLOCK_T *rwFileLock;
#endif
-} HgfsOpenFile;
+} HgfsFile;
/* The hash table for file state. */
typedef struct HgfsFileHashTable {
/* Forward declaration to prevent circular dependency between this and hgfsbsd.h. */
struct HgfsSuperInfo;
-int HgfsVnodeGet(struct vnode **vpp, struct HgfsSuperInfo *sip, struct mount *vfsp,
- const char *fileName, HgfsFileType fileType, HgfsFileHashTable *htp);
+int HgfsVnodeGet(struct vnode **vpp, struct vnode *dp, struct HgfsSuperInfo *sip,
+ struct mount *vfsp, const char *fileName, HgfsFileType fileType,
+ HgfsFileHashTable *htp, Bool createFile, int permissions);
int HgfsVnodeGetRoot(struct vnode **vpp, struct HgfsSuperInfo *sip, struct mount *vfsp,
const char *fileName, HgfsFileType fileType, HgfsFileHashTable *htp);
-int HgfsVnodePut(struct vnode *vp, HgfsFileHashTable *htp);
+int HgfsReleaseVnodeContext(struct vnode *vp, HgfsFileHashTable *htp);
void HgfsNodeIdGet(HgfsFileHashTable *ht, const char *fileName,
uint32_t fileNameLength, ino_t *outNodeId);
int HgfsInitFileHashTable(HgfsFileHashTable *htp);
Bool HgfsFileHashTableIsEmpty(struct HgfsSuperInfo *sip, HgfsFileHashTable *htp);
/* Handle get/set/clear functions */
-int HgfsSetOpenFileHandle(struct vnode *vp, HgfsHandle handle);
+void HgfsSetOpenFileHandle(struct vnode *vp, HgfsHandle handle, HgfsMode openMode);
int HgfsGetOpenFileHandle(struct vnode *vp, HgfsHandle *outHandle);
-int HgfsReleaseOpenFileHandle(struct vnode *vp, Bool *closed);
-Bool HgfsShouldCloseOpenFileHandle(struct vnode *vp);
-Bool HgfsHandleIsSet(struct vnode *vp);
+int HgfsReleaseOpenFileHandle(struct vnode *vp, HgfsHandle *handleToClose);
+int HgfsCheckAndReferenceHandle(struct vnode *vp, int requestedOpenMode);
int HgfsHandleIncrementRefCount(struct vnode *vp);
-/* Mode get/set/clear functions */
-int HgfsSetOpenFileMode(struct vnode *vp, HgfsMode mode);
-int HgfsGetOpenFileMode(struct vnode *vp, HgfsMode *outMode);
-int HgfsClearOpenFileMode(struct vnode *);
-
-/* HgfsFile locking functions */
-int HgfsFileLock(struct vnode *vp, HgfsLockType type);
-int HgfsFileLockOfp(HgfsOpenFile *ofp, HgfsLockType type);
-int HgfsFileUnlock(struct vnode *vp, HgfsLockType type);
-int HgfsFileUnlockOfp(HgfsOpenFile *ofp, HgfsLockType type);
-
#endif /* _STATE_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * transport.c --
+ *
+ * Functions that prepare HGFS packages and send them to the host.
+ * Implementations are shared between both OS X and FreeBSD.
+ */
+
+#include <sys/param.h> // for everything
+#include <sys/vnode.h> // for struct vnode
+#include <sys/dirent.h> // for struct dirent
+
+#include "fsutil.h"
+#include "debug.h"
+#include "transport.h"
+#include "cpName.h"
+#include "os.h"
+
+#define HGFS_FILE_OPEN_MASK (HGFS_OPEN_VALID_MODE | \
+ HGFS_OPEN_VALID_FLAGS | \
+ HGFS_OPEN_VALID_SPECIAL_PERMS | \
+ HGFS_OPEN_VALID_OWNER_PERMS | \
+ HGFS_OPEN_VALID_GROUP_PERMS | \
+ HGFS_OPEN_VALID_OTHER_PERMS | \
+ HGFS_OPEN_VALID_FILE_NAME)
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSendOpenDirRequest --
+ *
+ * Sends a SEARCH_OPEN request to the Hgfs server.
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsSendOpenDirRequest(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ char *fullPath, // IN: Full path for the file
+ uint32 fullPathLen, // IN: Length of the full path
+ HgfsHandle *handle) // OUT: HGFS handle for the opened file
+{
+ HgfsKReqHandle req;
+ HgfsRequest *requestHeader;
+ HgfsReply *replyHeader;
+ HgfsRequestSearchOpenV3 *request;
+ HgfsReplySearchOpenV3 *reply;
+ uint32 reqSize;
+ uint32 repSize;
+ uint32 reqBufferSize;
+ int ret;
+
+ req = HgfsKReq_AllocateRequest(sip->reqs);
+ if (!req) {
+ return ENOMEM;
+ }
+
+ /* Set the correct header values */
+ requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
+ request = (HgfsRequestSearchOpenV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
+
+ HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_SEARCH_OPEN_V3);
+
+ request->dirName.flags = 0;
+ request->dirName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
+ request->dirName.fid = HGFS_INVALID_HANDLE;
+ request->reserved = 0;
+
+ reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
+ reqBufferSize = HGFS_NAME_BUFFER_SIZET(reqSize);
+
+ /*
+ * Convert an input string to utf8 precomposed form, convert it to
+ * the cross platform name format and finally unescape any illegal
+ * filesystem characters.
+ */
+ ret = HgfsNameToWireEncoding(fullPath, fullPathLen + 1,
+ request->dirName.name,
+ reqBufferSize);
+
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "Could not encode to wire format");
+ HgfsKReq_ReleaseRequest(sip->reqs, req);
+ return -ret;
+ }
+
+ request->dirName.length = ret;
+ reqSize += ret;
+
+ HgfsKReq_SetPayloadSize(req, reqSize);
+
+ /* Submit the request to the Hgfs server */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret == 0) {
+
+ /* Our reply is in the request packet */
+ replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req);
+ reply = (HgfsReplySearchOpenV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader);
+
+ repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
+
+ ret = HgfsGetStatus(req, repSize);
+ if (ret == 0) {
+ *handle = reply->search;
+ } else {
+ DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret);
+ }
+
+ DEBUG(VM_DEBUG_COMM, "received reply for ID %d\n", replyHeader->id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", replyHeader->status);
+ DEBUG(VM_DEBUG_COMM, " handle: %d\n", reply->search);
+
+ HgfsKReq_ReleaseRequest(sip->reqs, req);
+ }
+ return ret;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSendOpenRequest --
+ *
+ * Sends an OPEN request to the Hgfs server to open an existing file.
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsSendOpenRequest(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ int openMode, // IN: HGFS mode of open
+ int openFlags, // IN: HGFS open flags
+ int permissions, // IN: Permissions of open (only when creating)
+ char *fullPath, // IN: Full path for the file
+ uint32 fullPathLen, // IN: Length of the full path
+ HgfsHandle *handle) // OUT: HGFS handle for the opened file
+{
+ HgfsKReqHandle req;
+ HgfsRequest *requestHeader;
+ HgfsReply *replyHeader;
+ HgfsRequestOpenV3 *request;
+ HgfsReplyOpenV3 *reply;
+ int ret;
+ uint32 reqSize;
+ uint32 repSize;
+ uint32 reqBufferSize;
+
+ DEBUG(VM_DEBUG_LOG, "Trace enter.\n");
+ req = HgfsKReq_AllocateRequest(sip->reqs);
+ if (!req) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsKReq_AllocateRequest failed.\n");
+ return ENOMEM;
+ }
+
+ requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
+ request = (HgfsRequestOpenV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
+
+ HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_OPEN_V3);
+
+ request->mask = HGFS_FILE_OPEN_MASK;
+ request->reserved1 = 0;
+ request->reserved2 = 0;
+
+ reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
+ reqBufferSize = HGFS_NAME_BUFFER_SIZET(reqSize);
+ request->mode = openMode;
+ request->flags = openFlags;
+ DEBUG(VM_DEBUG_COMM, "open flags are %x\n", request->flags);
+
+ request->specialPerms = (permissions & (S_ISUID | S_ISGID | S_ISVTX)) >>
+ HGFS_ATTR_SPECIAL_PERM_SHIFT;
+ request->ownerPerms = (permissions & S_IRWXU) >> HGFS_ATTR_OWNER_PERM_SHIFT;
+ request->groupPerms = (permissions & S_IRWXG) >> HGFS_ATTR_GROUP_PERM_SHIFT;
+ request->otherPerms = permissions & S_IRWXO;
+ DEBUG(VM_DEBUG_COMM, "permissions are %o\n", permissions);
+
+ request->fileName.flags = 0;
+ request->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
+ request->fileName.fid = HGFS_INVALID_HANDLE;
+
+ /*
+ * Convert an input string to utf8 precomposed form, convert it to
+ * the cross platform name format and finally unescape any illegal
+ * filesystem characters.
+ */
+ ret = HgfsNameToWireEncoding(fullPath, fullPathLen + 1,
+ request->fileName.name,
+ reqBufferSize);
+
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "Could not encode to wire format");
+ ret = -ret;
+ goto out;
+ }
+ request->fileName.length = ret;
+ reqSize += ret;
+
+ /* Packet size includes the request and its payload. */
+ HgfsKReq_SetPayloadSize(req, reqSize);
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest will destroy the request if necessary. */
+ DEBUG(VM_DEBUG_FAIL, "could not submit request.\n");
+ req = NULL; // Request has been deallocated by SubmitRequest
+ } else {
+ replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req);
+ reply = (HgfsReplyOpenV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader);
+ repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
+ ret = HgfsGetStatus(req, repSize);
+ if (ret == 0) {
+ *handle = reply->file;
+ }
+ }
+out:
+ if (req != NULL) {
+ HgfsKReq_ReleaseRequest(sip->reqs, req);
+ }
+ return ret;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsCloseServerDirHandle --
+ *
+ * Prepares close handle request and sends it to the HGFS server
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsCloseServerDirHandle(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ HgfsHandle handle) // IN: Handle to close
+{
+ HgfsKReqHandle req;
+ HgfsRequest *requestHeader;
+ HgfsReply *replyHeader;
+ HgfsRequestSearchCloseV3 *request;
+ HgfsReplySearchCloseV3 *reply;
+ uint32 reqSize;
+ uint32 repSize;
+ int ret;
+
+ req = HgfsKReq_AllocateRequest(sip->reqs);
+ if (!req) {
+ return ENOMEM;
+ }
+
+ /*
+ * Prepare the request structure. Of note here is that the request is
+ * always the same size so we just set the packetSize to that.
+ */
+ requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
+ request = (HgfsRequestSearchCloseV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
+
+ HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_SEARCH_CLOSE_V3);
+
+ request->search = handle;
+ request->reserved = 0;
+ reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
+
+ HgfsKReq_SetPayloadSize(req, reqSize);
+
+ /* Submit the request to the Hgfs server */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret == 0) {
+ replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req);
+ repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
+ DEBUG(VM_DEBUG_COMM, "received reply for ID %d\n", replyHeader->id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", replyHeader->status);
+
+ ret = HgfsGetStatus(req, repSize);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret);
+ if (ret != EPROTO) {
+ ret = EFAULT;
+ }
+ }
+
+ HgfsKReq_ReleaseRequest(sip->reqs, req);
+ }
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsCloseServerFileHandle --
+ *
+ * Prepares close handle request and sends it to the HGFS server
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsCloseServerFileHandle(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ HgfsHandle handle) // IN: Handle to close
+{
+ HgfsKReqHandle req;
+ HgfsRequest *requestHeader;
+ HgfsReply *replyHeader;
+ HgfsRequestCloseV3 *request;
+ HgfsReplyCloseV3 *reply;
+ uint32 reqSize;
+ uint32 repSize;
+ int ret;
+
+ req = HgfsKReq_AllocateRequest(sip->reqs);
+ if (!req) {
+ return ENOMEM;
+ }
+
+ /*
+ * Prepare the request structure. Of note here is that the request is
+ * always the same size so we just set the packetSize to that.
+ */
+ requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
+ request = (HgfsRequestCloseV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
+
+ HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_CLOSE_V3);
+
+ request->file = handle;
+ request->reserved = 0;
+ reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
+
+ HgfsKReq_SetPayloadSize(req, reqSize);
+
+ /* Submit the request to the Hgfs server */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret == 0) {
+ replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req);
+ repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
+ DEBUG(VM_DEBUG_COMM, "received reply for ID %d\n", replyHeader->id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", replyHeader->status);
+
+ ret = HgfsGetStatus(req, repSize);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret);
+ if (ret != EPROTO) {
+ ret = EFAULT;
+ }
+ }
+
+ HgfsKReq_ReleaseRequest(sip->reqs, req);
+ }
+ return ret;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * transport.h --
+ *
+ * Communication with HGFS server.
+ */
+
+#ifndef _HGFS_TRANSPORT_COMMON_H_
+#define _HGFS_TRANSPORT_COMMON_H_
+
+#include "hgfs_kernel.h"
+
+/* Internal transport functions used by both FreeBSD and Mac OS */
+int HgfsSendOpenRequest(HgfsSuperInfo *sip, int openMode, int openFlags,
+ int permissions, char *fullPath,
+ uint32 fullPathLen, HgfsHandle *handle);
+int HgfsSendOpenDirRequest(HgfsSuperInfo *sip, char *fullPath,
+ uint32 fullPathLen, HgfsHandle *handle);
+int HgfsCloseServerDirHandle(HgfsSuperInfo *sip, HgfsHandle handleToClose);
+int HgfsCloseServerFileHandle(HgfsSuperInfo *sip, HgfsHandle handleToClose);
+
+#endif // _HGFS_TRANSPORT_COMMON_H_
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
};
*/
{
- /*
- * On FreeBSD we can always call vgone because there is no possibility that the
- * vnode is being shared by more than one open file.
- */
- vgone(ap->a_vp);
-
return 0;
}
HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp);
- HgfsVnodePut(vp, &sip->fileHashTable);
+ HgfsReleaseVnodeContext(vp, &sip->fileHashTable);
vp->v_data = NULL;
return 0;
*********************************************************/
/*
- * vnopscommon.h --
+ * vnopscommon.c --
*
* Common VFS vnop implementations that are shared between both OS X and FreeBSD.
*/
#include "fsutil.h"
#include "debug.h"
#include "vnopscommon.h"
+#include "transport.h"
#include "cpName.h"
#include "os.h"
static int HgfsDoGetattrByHandle(HgfsHandle handle, HgfsSuperInfo *sip, HgfsAttrV2 *hgfsAttrV2);
#endif
-#define HGFS_FILE_OPEN_MASK (HGFS_OPEN_VALID_MODE | \
- HGFS_OPEN_VALID_FLAGS | \
- HGFS_OPEN_VALID_SPECIAL_PERMS | \
- HGFS_OPEN_VALID_OWNER_PERMS | \
- HGFS_OPEN_VALID_GROUP_PERMS | \
- HGFS_OPEN_VALID_OTHER_PERMS | \
- HGFS_OPEN_VALID_FILE_NAME)
-
#define HGFS_CREATE_DIR_MASK (HGFS_CREATE_DIR_VALID_FILE_NAME | \
HGFS_CREATE_DIR_VALID_SPECIAL_PERMS | \
HGFS_CREATE_DIR_VALID_OWNER_PERMS | \
uint32 repSize;
int ret;
+ DEBUG(VM_DEBUG_LOG, "Trace enter.\n");
/* No cross-device renaming. */
if (HGFS_VP_TO_MP(fvp) != HGFS_VP_TO_MP(tdvp)) {
return EXDEV;
char *fullName = NULL; /* Hashed to generate inode number */
int ret = 0;
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
/* uio_offset is a signed quantity. */
if (HGFS_UIOP_TO_OFFSET(uiop) < 0) {
DEBUG(VM_DEBUG_FAIL, "fed negative offset.\n");
if (fullName != NULL) {
os_free(fullName, MAXPATHLEN);
}
- DEBUG(VM_DEBUG_ENTRY, "exiting.\n");
+ DEBUG(VM_DEBUG_ENTRY, "exiting %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
HgfsAttrV2 hgfsAttrV2;
int ret = 0;
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
/* XXX It would be nice to do a GetattrByHandle when possible here. */
ret = HgfsDoGetattrByName(HGFS_VP_TO_FILENAME(vp), sip, &hgfsAttrV2);
HgfsAttrToBSD(vp, &hgfsAttrV2, vap);
}
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
uint32 fullPathLen;
int ret;
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
ASSERT(vp);
ASSERT(vap);
DEBUG(VM_DEBUG_DONE, "done with ret = %d\n", ret);
HgfsKReq_ReleaseRequest(sip->reqs, req);
out:
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
int ret = 0;
HgfsSuperInfo *sip = HGFS_VP_TO_SIP(dvp);
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
DEBUG(VM_DEBUG_ENTRY, "HgfsRmdir().\n");
(HGFS_VP_TO_FP(vp)) ? HGFS_VP_TO_FILENAME(vp) : "vp->v_data null");
ret = HgfsDelete(sip, HGFS_VP_TO_FILENAME(vp), HGFS_OP_DELETE_DIR_V3);
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp);
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
DEBUG(VM_DEBUG_ENTRY, "HgfsRemove().\n");
/* Removing directories is a no-no; save that for VNOP_RMDIR. */
ret = HgfsDelete(sip, HGFS_VP_TO_FILENAME(vp), HGFS_OP_DELETE_FILE_V3);
out:
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
{
int ret = 0;
HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp);
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
/*
* If we are closing a directory we need to send a SEARCH_CLOSE request,
ret = EINVAL;
break;
}
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return 0;
}
int mode) // IN: Mode of vnode being opened.
{
HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp);
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
switch(HGFS_VP_TO_VTYPE(vp)) {
case VDIR:
case VREG:
{
- HgfsMode hmode = 0;
-
/*
- * If HgfsCreate() was called prior to this, this fills in the mode we
- * saved there. It's okay if this fails since often HgfsCreate()
- * won't have been called.
+ * If HgfsCreate() was called prior to this then is would set permissions
+ * in HgfsFile that we need to pass to HgfsFileOpen.
+ * If HgfsCreate has not been called then file already exists and permissions
+ * are ignored by HgfsFileOpen.
*/
- HgfsGetOpenFileMode(vp, &hmode);
-
DEBUG(VM_DEBUG_COMM, "opening a file with flag %x\n", mode);
- return HgfsFileOpen(sip, vp, mode, hmode);
+ return HgfsFileOpen(sip, vp, mode, HGFS_VP_TO_PERMISSIONS(vp));
}
default:
return EINVAL;
}
- return 0;
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
+ return 0;
}
int ret = 0;
int len = 0;
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(dvp));
+
ASSERT(dvp);
ASSERT(vpp);
ASSERT(cnp);
HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp),
(int)cnp->cn_namelen, cnp->cn_nameptr);
+ if (cnp->cn_flags & ISDOTDOT) {
+ HgfsFile *fp = HGFS_VP_TO_FP(dvp);
+ ASSERT(fp);
+ if (fp->parent == NULL) {
+ return EIO; // dvp is root directory
+ } else {
+#if defined(__FreeBSD__)
+ vref(fp->parent);
+#else
+ vnode_get(fp->parent);
+#endif
+ *vpp = fp->parent;
+ return 0;
+ }
+ }
+ if (cnp->cn_namelen == 1 && *cnp->cn_nameptr == '.') {
+#if defined(__FreeBSD__)
+ vref(dvp);
+#else
+ vnode_get(dvp);
+#endif
+ *vpp = dvp;
+ return 0;
+ }
+
/*
* Get pointer to the superinfo. If the device is not attached,
* hgfsInstance will not be valid and we immediately return an error.
goto out;
}
- ret = HgfsVnodeGet(vpp, // Location to write vnode's address
- sip, // Superinfo
- HGFS_VP_TO_MP(dvp), // VFS for our filesystem
- path, // Full name of the file
- attrV2.type, // Type of file
- &sip->fileHashTable); // File hash table
+ ret = HgfsVnodeGet(vpp, // Location to write vnode's address
+ dvp, // Parent vnode
+ sip, // Superinfo
+ HGFS_VP_TO_MP(dvp), // VFS for our filesystem
+ path, // Full name of the file
+ attrV2.type, // Type of file
+ &sip->fileHashTable, // File hash table
+ FALSE, // Not a new file creation
+ 0); // No permissions - not a new file
if (ret) {
DEBUG(VM_DEBUG_FAIL, "couldn't create vnode for \"%s\".\n", path);
if (path != NULL) {
os_free(path, MAXPATHLEN);
}
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(dvp));
return ret;
}
char *fullname = NULL; // allocated from M_TEMP; free when done.
int ret = 0;
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(dvp));
+
if (*vpp != NULL) {
DEBUG(VM_DEBUG_ALWAYS, "vpp (%p) not null\n", vpp);
return EEXIST;
fullname, // Buffer to write full name
MAXPATHLEN); // Size of this buffer
- if (ret < 0) {
+ if (ret >= 0) {
+ /* Create the vnode for this file. */
+ ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullname,
+ HGFS_FILE_TYPE_REGULAR, &sip->fileHashTable, FALSE, mode);
+ /* HgfsVnodeGet() guarantees this. */
+ ASSERT(ret != 0 || *vpp);
+ } else {
DEBUG(VM_DEBUG_FAIL, "couldn't create full path name.\n");
ret = ENAMETOOLONG;
- goto out;
- }
-
- /* Create the vnode for this file. */
- ret = HgfsVnodeGet(vpp, sip, HGFS_VP_TO_MP(dvp), fullname,
- HGFS_FILE_TYPE_REGULAR, &sip->fileHashTable);
- if (ret) {
- goto out;
}
- /* HgfsVnodeGet() guarantees this. */
- ASSERT(*vpp);
-
- /* Save the mode so when open is called we can reference it. */
- HgfsSetOpenFileMode(*vpp, mode);
-
-out:
if (fullname != NULL) {
os_free(fullname, MAXPATHLEN);
}
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(dvp));
return ret;
}
uint64_t offset;
int ret;
- DEBUG(VM_DEBUG_ENTRY, "entry.\n");
+ DEBUG(VM_DEBUG_ENTRY, "entry %s.\n", HGFS_VP_TO_FILENAME(vp));
/* We can't read from directories, that's what readdir() is for. */
if (HGFS_VP_TO_VTYPE(vp) == VDIR) {
} while (HGFS_UIOP_TO_RESID(uiop));
/* We fulfilled the user's read request, so return success. */
- DEBUG(VM_DEBUG_DONE, "done.\n");
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return 0;
}
} while (HGFS_UIOP_TO_RESID(uiop));
/* We have completed the user's write request, so return success. */
- DEBUG(VM_DEBUG_DONE, "done.\n");
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return 0;
}
goto destroyOut;
}
- ret = HgfsVnodeGet(vpp, sip, HGFS_VP_TO_MP(dvp), fullName,
- HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable);
+ ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullName,
+ HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable, TRUE, mode);
if (ret) {
ret = EIO;
goto destroyOut;
if (fullName != NULL) {
os_free(fullName, MAXPATHLEN);
}
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(dvp));
return ret;
}
HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer
struct vnode *vp) // IN: Vnode of directory to open
{
- HgfsKReqHandle req;
- HgfsRequest *requestHeader;
- HgfsReply *replyHeader;
- HgfsRequestSearchOpenV3 *request;
- HgfsReplySearchOpenV3 *reply;
- uint32 reqSize;
- uint32 repSize;
- uint32 reqBufferSize;
char *fullPath;
uint32 fullPathLen;
int ret;
+ HgfsFile *fp;
+ HgfsHandle handle;
ASSERT(sip);
ASSERT(vp);
- DEBUG(VM_DEBUG_ENTRY, "opening \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
+ fp = HGFS_VP_TO_FP(vp);
+ ASSERT(fp);
- if (HgfsHandleIsSet(vp)) {
- /*
- * We already have a handle. Idealy this would never happen, but it
- * does on OS X when trying to open "." or ".." (because HgfsLookupInt
- * is never called by the VFS layer in that specific case.). If that
- * happens, then the "per-open-file" HgfsOpenFile associated with this
- * vnode will now be shared between two separate opens. At the open
- * level, there is no way to insert a new vnode so the exist handle
- * is shared. Since this can only happen for directories and directory
- * entries are read off of the server at the time of the
- * HGFS_OP_SEARCH_OPEN in HgfsServerScandir, there is nothing to do
- * here. The worst that could happen is that a process gets stale
- * entries and there isn't really any way to avoid that.
- * The root vnode directory handle is also shared on both FreeBSD and
- * OS X.
- */
-
-#if defined(__FreeBSD__)
- /*
- * XXX It looks like the FreeBSD open call asks for a shared vnode lock.
- * It would be nice to allow handle sharing for the root vnode. However,
- * until the following is answered, I'm going to leave the behavior as is
- * (strict one vnode per open file) for FreeBSD until the implications of
- * I have time to think through the behavior of two racing open calls (If this
- * can really happen then the open call could try to acquire two handles and
- * one would be lost (the HgfsSetOpenFileHandle call would fail) and never be
- * closed. I don't think this can actually happen because each open syscall first
- * calls namei and then lookup. I think the right solution is to make the open call
- * send a close call on the handle if it cannot set the handle later on. This would
- * make it so that its fine to share the root vnode and would protect us against
- * future changes to how open is called by the FreeBSD kernel. Then again, this
- * might be overkill.
- * This does not happen on OS X becasue the macos function that called HgfsOpenInt
- * first acquired a writer lock on the HgfsOpenFile.
- */
- return EIO;
-#endif
-
- DEBUG(VM_DEBUG_LOG, "Not doing an open because we have a handle\n");
- HgfsHandleIncrementRefCount(vp);
- return 0;
- }
-
- req = HgfsKReq_AllocateRequest(sip->reqs);
- if (!req) {
- return ENOMEM;
- }
-
- /* Set the correct header values */
- requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
- request = (HgfsRequestSearchOpenV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
-
- HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_SEARCH_OPEN_V3);
-
- request->dirName.flags = 0;
- request->dirName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
- request->dirName.fid = HGFS_INVALID_HANDLE;
- request->reserved = 0;
-
- reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
- reqBufferSize = HGFS_NAME_BUFFER_SIZET(reqSize);
-
- if (HGFS_IS_ROOT_VNODE(sip, vp)) {
- fullPath = "";
- fullPathLen = 0;
- } else {
- fullPath = HGFS_VP_TO_FILENAME(vp);
- fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp);
- }
+ DEBUG(VM_DEBUG_ENTRY, "opening \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
/*
- * Convert an input string to utf8 precomposed form, convert it to
- * the cross platform name format and finally unescape any illegal
- * filesystem characters.
+ * If the directory is already opened then we are done.
+ * There is no different open modes for directories thus the handle is compatible.
*/
- ret = HgfsNameToWireEncoding(fullPath, fullPathLen + 1,
- request->dirName.name,
- reqBufferSize);
-
- if (ret < 0) {
- DEBUG(VM_DEBUG_FAIL, "Could not encode to wire format");
- ret = -ret;
- goto destroyOut;
- }
-
- request->dirName.length = ret;
- reqSize += ret;
+ os_rw_lock_lock_exclusive(fp->handleLock);
+ ret = HgfsCheckAndReferenceHandle(vp, 0);
+ if (ret == ENOENT) { // Handle is not set, need to get one from the host
- HgfsKReq_SetPayloadSize(req, reqSize);
-
- /* Submit the request to the Hgfs server */
- ret = HgfsSubmitRequest(sip, req);
- if (ret) {
- /* HgfsSubmitRequest destroys the request if necessary */
-
- goto out;
- }
-
- /* Our reply is in the request packet */
- replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req);
- reply = (HgfsReplySearchOpenV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader);
-
- repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
-
- ret = HgfsGetStatus(req, repSize);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret);
- goto destroyOut;
- }
-
- DEBUG(VM_DEBUG_COMM, "received reply for ID %d\n", replyHeader->id);
- DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", replyHeader->status);
- DEBUG(VM_DEBUG_COMM, " handle: %d\n", reply->search);
+ if (HGFS_IS_ROOT_VNODE(sip, vp)) {
+ fullPath = "";
+ fullPathLen = 0;
+ } else {
+ fullPath = HGFS_VP_TO_FILENAME(vp);
+ fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp);
+ }
- /* Set the search open handle for use in HgfsReaddir() */
- ret = HgfsSetOpenFileHandle(vp, reply->search);
- if (ret) {
- ret = EINVAL;
- goto destroyOut;
+ ret = HgfsSendOpenDirRequest(sip, fullPath, fullPathLen, &handle);
+ if (ret == 0) {
+ /*
+ * We successfully received a reply, so we need to save the handle in
+ * this file's HgfsOpenFile and return success.
+ */
+ HgfsSetOpenFileHandle(vp, handle, 0);
+ }
}
+ os_rw_lock_unlock_exclusive(fp->handleLock);
- ret = 0; /* Return success */
-
-destroyOut:
- /* Make sure we put the request back on the list */
- HgfsKReq_ReleaseRequest(sip->reqs, req);
-
-out:
- DEBUG(VM_DEBUG_DONE, "done\n");
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
int flag, // IN: Flags of open
int permissions) // IN: Permissions of open (only when creating)
{
- HgfsKReqHandle req;
- HgfsRequest *requestHeader;
- HgfsReply *replyHeader;
- HgfsRequestOpenV3 *request;
- HgfsReplyOpenV3 *reply;
- uint32 reqSize;
- uint32 repSize;
- uint32 reqBufferSize;
char *fullPath = NULL;
uint32 fullPathLen;
int ret;
+ int openMode;
+ int openFlags;
+ HgfsHandle handle;
+ HgfsFile *fp;
ASSERT(sip);
ASSERT(vp);
+ fp = HGFS_VP_TO_FP(vp);
+ ASSERT(fp);
+
DEBUG(VM_DEBUG_ENTRY, "opening \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
/*
return EPERM;
}
- if (HgfsHandleIsSet(vp)) {
- DEBUG(VM_DEBUG_FAIL, "Trying to share a file handle for an already open !file! handle\n");
- return EIO;
- }
-
- req = HgfsKReq_AllocateRequest(sip->reqs);
- if (!req) {
- DEBUG(VM_DEBUG_FAIL, "HgfsKReq_AllocateRequest failed.\n");
- return ENOMEM;
- }
-
- requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
- request = (HgfsRequestOpenV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
-
- HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_OPEN_V3);
-
- request->mask = HGFS_FILE_OPEN_MASK;
- request->reserved1 = 0;
- request->reserved2 = 0;
-
- reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
- reqBufferSize = HGFS_NAME_BUFFER_SIZET(reqSize);
-
/* Convert FreeBSD modes to Hgfs modes */
- ret = HgfsGetOpenMode((uint32_t)flag);
- if (ret < 0) {
+ openMode = HgfsGetOpenMode((uint32_t)flag);
+ if (openMode < 0) {
DEBUG(VM_DEBUG_FAIL, "HgfsGetOpenMode failed.\n");
ret = EINVAL;
- goto destroyOut;
+ goto out;
}
-
- request->mode = ret;
- DEBUG(VM_DEBUG_COMM, "open mode is %x\n", request->mode);
+ DEBUG(VM_DEBUG_COMM, "open mode is %x\n", openMode);
/* Convert FreeBSD flags to Hgfs flags */
- ret = HgfsGetOpenFlags((uint32_t)flag);
- if (ret < 0) {
+ openFlags = HgfsGetOpenFlags((uint32_t)flag);
+ if (openFlags < 0) {
DEBUG(VM_DEBUG_FAIL, "HgfsGetOpenFlags failed.\n");
ret = EINVAL;
- goto destroyOut;
+ goto out;
}
- request->flags = ret;
- DEBUG(VM_DEBUG_COMM, "open flags are %x\n", request->flags);
-
- request->specialPerms = (permissions & (S_ISUID | S_ISGID | S_ISVTX)) >>
- HGFS_ATTR_SPECIAL_PERM_SHIFT;
- request->ownerPerms = (permissions & S_IRWXU) >> HGFS_ATTR_OWNER_PERM_SHIFT;
- request->groupPerms = (permissions & S_IRWXG) >> HGFS_ATTR_GROUP_PERM_SHIFT;
- request->otherPerms = permissions & S_IRWXO;
-
- fullPath = HGFS_VP_TO_FILENAME(vp);
- fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp);
-
- DEBUG(VM_DEBUG_COMM, "permissions are %o\n", permissions);
-
- request->fileName.flags = 0;
- request->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
- request->fileName.fid = HGFS_INVALID_HANDLE;
-
+ os_rw_lock_lock_exclusive(fp->handleLock);
/*
- * Convert an input string to utf8 precomposed form, convert it to
- * the cross platform name format and finally unescape any illegal
- * filesystem characters.
+ * If the file is already opened, verify that it is opened in a compatible mode.
+ * If it is true then add reference to vnode and grant the access, otherwise
+ * deny the access.
*/
- ret = HgfsNameToWireEncoding(fullPath, fullPathLen + 1,
- request->fileName.name,
- reqBufferSize);
-
- if (ret < 0) {
- DEBUG(VM_DEBUG_FAIL, "Could not encode to wire format");
- ret = -ret;
- goto destroyOut;
- }
- request->fileName.length = ret;
- reqSize += ret;
-
- /* Packet size includes the request and its payload. */
- HgfsKReq_SetPayloadSize(req, reqSize);
-
- ret = HgfsSubmitRequest(sip, req);
- if (ret) {
- /* HgfsSubmitRequest will destroy the request if necessary. */
- DEBUG(VM_DEBUG_FAIL, "could not submit request.\n");
- goto out;
- }
+ ret = HgfsCheckAndReferenceHandle(vp, openMode);
+ if (ret == ENOENT) { // Handle is not set, need to get one from the host
- replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req);
- reply = (HgfsReplyOpenV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader);
+ int requestedMode = HGFS_OPEN_MODE_READ_WRITE;
+ fullPath = HGFS_VP_TO_FILENAME(vp);
+ fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp);
- repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
+ /* First see if we can get the most permissive read/write open mode */
+ ret = HgfsSendOpenRequest(sip, requestedMode, openFlags,
+ permissions, fullPath, fullPathLen, &handle);
+ if (ret) {
+ if (ret == EACCES && HGFS_OPEN_MODE_READ_WRITE != openMode) {
+ /*
+ * Failed to open in read/write open mode because of denied access.
+ * It means file's permissions do not allow opening for read/write.
+ * However caller does not need this mode and may be satisfied with
+ * less permissive mode.
+ * Try exact open mode now.
+ */
+ DEBUG(VM_DEBUG_FAIL, "RW mode failed, re-submitting original mode = %d.\n",
+ ret);
+ requestedMode = openMode;
+ ret = HgfsSendOpenRequest(sip, requestedMode, openFlags,
+ permissions, fullPath, fullPathLen, &handle);
+ }
+ }
- ret = HgfsGetStatus(req, repSize);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d.\n", ret);
- goto destroyOut;
+ /*
+ * We successfully received a reply, so we need to save the handle in
+ * this file's HgfsOpenFile and return success.
+ */
+ if (ret == 0) {
+ HgfsSetOpenFileHandle(vp, handle, requestedMode);
+ }
}
- /*
- * We successfully received a reply, so we need to save the handle in
- * this file's HgfsOpenFile and return success.
- */
- ret = HgfsSetOpenFileHandle(vp, reply->file);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "couldn't assign handle %d (%s)\n",
- reply->file, HGFS_VP_TO_FILENAME(vp));
- ret = EINVAL;
- goto destroyOut;
- }
-
- ret = 0;
-
-destroyOut:
- HgfsKReq_ReleaseRequest(sip->reqs, req);
+ os_rw_lock_unlock_exclusive(fp->handleLock);
out:
- DEBUG(VM_DEBUG_DONE, "returning %d\n", ret);
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer
struct vnode *vp) // IN: Vnode of directory to close
{
- HgfsKReqHandle req;
- HgfsRequest *requestHeader;
- HgfsReply *replyHeader;
- HgfsRequestSearchCloseV3 *request;
- HgfsReplySearchCloseV3 *reply;
- uint32 reqSize;
- uint32 repSize;
- int ret;
- Bool closed;
+ int ret = 0;
+ HgfsHandle handleToClose;
ASSERT(sip);
ASSERT(vp);
DEBUG(VM_DEBUG_ENTRY, "closing \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
/*
- * Check to see if we should close the file handle ( which will happen when
- * the reference count of the current handle is 1 and will become 0 after
- * this close.
+ * Check to see if we should close the file handle on the host ( which happen when
+ * the reference count of the current handle become 0.
*/
- if (!HgfsShouldCloseOpenFileHandle(vp)) {
- ret = HgfsReleaseOpenFileHandle(vp, &closed);
- if (ret || (closed == TRUE)) {
- DEBUG(VM_DEBUG_FAIL, "Dirclose\n");
- return EINVAL;
- }
- return 0;
- }
-
- req = HgfsKReq_AllocateRequest(sip->reqs);
- if (!req) {
- return ENOMEM;
+ if (HgfsReleaseOpenFileHandle(vp, &handleToClose) == 0) {
+ ret = HgfsCloseServerDirHandle(sip, handleToClose);
}
-
- /*
- * Prepare the request structure. Of note here is that the request is
- * always the same size so we just set the packetSize to that.
- */
- requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
- request = (HgfsRequestSearchCloseV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
-
- HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_SEARCH_CLOSE_V3);
-
- request->reserved = 0;
- reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
-
- /* Get this open file's handle, since that is what we want to close. */
- ret = HgfsGetOpenFileHandle(vp, &request->search);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "couldn't get handle for %s\n",
- HGFS_VP_TO_FILENAME(vp));
- ret = EINVAL;
- goto destroyOut;
- }
-
- HgfsKReq_SetPayloadSize(req, reqSize);
-
- /* Submit the request to the Hgfs server */
- ret = HgfsSubmitRequest(sip, req);
- if (ret) {
- /* HgfsSubmitRequest destroys the request if necessary */
- goto out;
- }
-
- replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req);
- repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
-
- ret = HgfsGetStatus(req, repSize);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret);
- if (ret != EPROTO) {
- ret = EFAULT;
- }
- goto destroyOut;
- }
-
- DEBUG(VM_DEBUG_COMM, "received reply for ID %d\n", replyHeader->id);
- DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", replyHeader->status);
-
- /* Now clear this open file's handle for future use. */
- ret = HgfsReleaseOpenFileHandle(vp, &closed);
- if (ret || (closed == FALSE)) {
- DEBUG(VM_DEBUG_FAIL, "couldn't clear handle.\n");
- ret = EINVAL;
- goto destroyOut;
- }
-
- DEBUG(VM_DEBUG_LOG, "cleared file handle\n");
-
- /* The directory was closed successfully so we return success. */
- ret = 0;
-
-destroyOut:
- HgfsKReq_ReleaseRequest(sip->reqs, req);
-out:
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
struct vnode *vp, // IN: Vnode of file to close
int flags) // IN: The mode flags for the close
{
- HgfsKReqHandle req;
- HgfsRequest *requestHeader;
- HgfsRequestCloseV3 *request;
- HgfsReplyCloseV3 *reply;
- uint32 reqSize;
- uint32 repSize;
- int ret;
- Bool closed;
+ int ret = 0;
+ HgfsHandle handleToClose;
ASSERT(sip);
ASSERT(vp);
- DEBUG(VM_DEBUG_ENTRY, "closing \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
-
- /*
- * Check to see if we should close the file handle ( which will happen when
- * the reference count of the current handle is 1 and will become 0 after
- * this close.
- */
- if (!HgfsShouldCloseOpenFileHandle(vp)) {
- ret = HgfsReleaseOpenFileHandle(vp, &closed);
- if (ret || (closed == TRUE)) {
- DEBUG(VM_DEBUG_FAIL, " HgfsFileClose: The handle is closed!\n");
- return EINVAL;
- }
- return 0;
- }
-
- req = HgfsKReq_AllocateRequest(sip->reqs);
- if (!req) {
- ret = ENOMEM;
- goto out;
- }
-
- requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
- request = (HgfsRequestCloseV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
-
- HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_CLOSE_V3);
-
- request->reserved = 0;
- reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request);
-
- /* Tell the Hgfs server which handle to close */
- ret = HgfsGetOpenFileHandle(vp, &request->file);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "couldn't get handle.\n");
- ret = EINVAL;
- goto destroyOut;
- }
-
- HgfsKReq_SetPayloadSize(req, reqSize);
-
- ret = HgfsSubmitRequest(sip, req);
- if (ret) {
- /* HgfsSubmitRequest will destroy the request if necessary. */
- DEBUG(VM_DEBUG_FAIL, "submit request failed.\n");
- goto out;
- }
-
- repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
-
- ret = HgfsGetStatus(req, repSize);
- if (ret) {
- DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret);
- goto destroyOut;
- }
+ DEBUG(VM_DEBUG_ENTRY, "closing file \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
/*
- * We already verified the size of the reply above since this reply type
- * only contains a header, so we just clear the handle and return success.
+ * Check to see if we should close the file handle on the host ( which happen when
+ * the reference count of the current handle become 0.
*/
- ret = HgfsReleaseOpenFileHandle(vp, &closed);
- if (ret || (closed == FALSE)) {
- DEBUG(VM_DEBUG_FAIL, "couldn't clear handle.\n");
- ret = EINVAL;
- goto destroyOut;
+ if (HgfsReleaseOpenFileHandle(vp, &handleToClose) == 0) {
+ ret = HgfsCloseServerFileHandle(sip, handleToClose);
}
-
-destroyOut:
- HgfsKReq_ReleaseRequest(sip->reqs, req);
-out:
- DEBUG(VM_DEBUG_DONE, "returning %d\n", ret);
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
uint32 repSize;
int ret;
+ DEBUG(VM_DEBUG_LOG, "Trace enter.\n");
ASSERT(sip);
ASSERT(uiop);
ASSERT(size <= HGFS_IO_MAX); // HgfsWrite() guarantees this
HgfsKReqHandle req;
int ret = 0;
HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp);
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
/* This operation is valid only for symbolic links. */
if (HGFS_VP_TO_VTYPE(vp) != VLNK) {
DEBUG(VM_DEBUG_FAIL, "Must be a symbolic link.\n");
} else {
DEBUG(VM_DEBUG_FAIL, "Error %d reading symlink name.\n", ret);
}
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp));
return ret;
}
HgfsFileNameV3 *fileNameP;
int nameOffset;
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(dvp));
DEBUG(VM_DEBUG_ENTRY, "dvp=%p (%s), dirname=%s, vpp=%p\n",
dvp, HGFS_VP_TO_FILENAME(dvp), cnp->cn_nameptr,
*vpp);
repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply);
ret = HgfsGetStatus(req, repSize);
if (ret == 0) {
- ret = HgfsVnodeGet(vpp, sip, HGFS_VP_TO_MP(dvp), fullName,
- HGFS_FILE_TYPE_SYMLINK, &sip->fileHashTable);
+ ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullName,
+ HGFS_FILE_TYPE_SYMLINK, &sip->fileHashTable, TRUE, 0);
if (ret) {
ret = EIO;
}
if (fullName != NULL) {
os_free(fullName, MAXPATHLEN);
}
+ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(dvp));
return ret;
}
HgfsSuperInfo *sip, // IN : SuperInfo block of hgfs mount.
HgfsAttrV2 *hgfsAttrV2) // OUT: Attributes from hgfs server
{
+ DEBUG(VM_DEBUG_LOG, "Trace enter.\n");
return HgfsDoGetattrInt(path, 0, sip, hgfsAttrV2);
}
HgfsKReqHandle req;
int ret = 0;
+ DEBUG(VM_DEBUG_LOG, "Trace enter, %s.\n", path);
ASSERT(hgfsAttrV2);
req = HgfsKReq_AllocateRequest(sip->reqs);
uint32 reqBufferSize;
int ret = 0;
+ DEBUG(VM_DEBUG_LOG, "Trace enter.\n");
requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req);
request = (HgfsRequestGetattrV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader);
HgfsVnodeAttr va;
int ret = 0;
- DEBUG(VM_DEBUG_FAIL, "HgfsAccessInt is called\n");
+ DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp));
/* Get the attributes for this file from the Hgfs server. */
ret = HgfsGetattrInt(vp, &va);
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define NET_SG_SIZE(len) (sizeof(NetSG_Array) + (len - NET_SG_DEFAULT_LENGTH) * sizeof(NetSG_Elem))
-#define NET_SG_MAKE_PA(elem) (((PA)elem.addrHi << 32) | (PA)elem.addrLow)
-#define NET_SG_MAKE_PTR(elem) \
- ((char*)(uintptr_t)(((uint64)elem.addrHi << 32) | elem.addrLow))
+#define NET_SG_MAKE_PA(elem) (PA)QWORD(elem.addrHi, elem.addrLow)
+#define NET_SG_MAKE_PTR(elem) (char *)(uintptr_t)QWORD(elem.addrHi, elem.addrLow)
#endif
PACKAGE_NAME=open-vm-tools
-PACKAGE_VERSION=2008.12.23
+PACKAGE_VERSION=2009.01.21
MAKE[0]="make -C pvscsi VM_UNAME=\$kernelver; \
make -C vmblock VM_UNAME=\$kernelver; \
make -C vmhgfs VM_UNAME=\$kernelver; \
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
MODULE_AUTHOR("VMware, Inc.");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(PVSCSI_DRIVER_VERSION_STRING);
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
#define PVSCSI_DRIVER_VECTORS_USED 1
#define DEFAULT_PAGES_PER_RING 8
*/
static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
{
- unsigned i, max;
- struct pvscsi_ctx *ctx = adapter->cmd_map;
+ struct pvscsi_ctx *ctx;
+ unsigned max;
+ int i;
+ ctx = adapter->cmd_map;
max = adapter->req_depth;
ASSERT_ON_COMPILE(sizeof(struct PVSCSISGList) <= PAGE_SIZE);
+
for (i = 0; i < max; ++i, ++ctx) {
ctx->sgl = kmalloc(PAGE_SIZE, GFP_KERNEL);
BUG_ON((long)ctx->sgl & ~PAGE_MASK);
PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100,
PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104,
PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108,
- PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10C,
+ PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c,
PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c,
PVSCSI_REG_OFFSET_INTR_MASK = 0x2010,
PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
uint32 _pad;
};
+/*
+ * Note:
+ * - reqRingNumPages and cmpRingNumPages need to be power of two.
+ * - reqRingNumPages and cmpRingNumPages need to be different from 0,
+ * - reqRingNumPages and cmpRingNumPages need to be inferior to
+ * PVSCSI_SETUP_RINGS_MAX_NUM_PAGES.
+ */
#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32
struct CmdDescSetupRings {
#include "vm_basic_defs.h" // for offsetof()
+#define SCSI_MAX_CMD_LEN 16
+
/*
* Non-exhaustive list of SCSI operation codes. Note that
* some codes are defined differently according to the target
#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x6 // the LUN has been reset (bus reset of medium change)
#define SCSI_SENSE_KEY_DATA_PROTECT 0x7 // access to the data is blocked
#define SCSI_SENSE_KEY_BLANK_CHECK 0x8 // reached an unexpected written or unwritten region of the medium
+#define SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x9 // report vendor specific condition
#define SCSI_SENSE_KEY_COPY_ABORTED 0xa // COPY, COMPARE, or COPY AND VERIFY was aborted
#define SCSI_SENSE_KEY_ABORTED_CMD 0xb // the target aborted the command
#define SCSI_SENSE_KEY_EQUAL 0xc // comparison for SEARCH DATA was unsuccessful
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
MODULE_DESCRIPTION("VMware Blocking File System");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(VMBLOCK_DRIVER_VERSION_STRING);
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
/* Functions */
static int VMBlockInit(void);
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/*
*-----------------------------------------------------------------------------
*
- * VMCIDatagram_CreateHnd --
+ * VMCIDatagramCreateHndInt --
*
* Creates a datagram endpoint and returns a handle to it.
*
*-----------------------------------------------------------------------------
*/
-#ifdef __linux__
-EXPORT_SYMBOL(VMCIDatagram_CreateHnd);
-#endif
-
int
-VMCIDatagram_CreateHnd(VMCIId resourceID, // IN:
- uint32 flags, // IN:
- VMCIDatagramRecvCB recvCB, // IN:
- void *clientData, // IN:
- VMCIHandle *outHandle) // OUT:
-
+VMCIDatagramCreateHndInt(VMCIId resourceID, // IN:
+ uint32 flags, // IN:
+ VMCIDatagramRecvCB recvCB, // IN:
+ void *clientData, // IN:
+ VMCIHandle *outHandle) // OUT:
{
int result;
DatagramHashEntry *entry;
}
if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) {
- VMCIDatagramWellKnownMapMsg wkMsg;
+ VMCIDatagramWellKnownMapMsg wkMsg;
if (resourceID == VMCI_INVALID_ID) {
return VMCI_ERROR_INVALID_ARGS;
}
if (entry == NULL) {
return VMCI_ERROR_NO_MEM;
}
-
+
entry->handle = handle;
entry->flags = flags;
entry->recvCB = recvCB;
return VMCI_SUCCESS;
}
-
+
/*
*-----------------------------------------------------------------------------
*
- * VMCIDatagram_DestroyHnd --
+ * VMCIDatagram_CreateHnd --
*
- * Destroys a handle.
+ * Creates a datagram endpoint and returns a handle to it.
*
* Results:
- * VMCI_SUCCESS or error code.
+ * Returns handle if success, negative errno value otherwise.
*
* Side effects:
- * Host and guest state is cleaned up.
+ * Datagram endpoint is created both in guest and on host.
*
*-----------------------------------------------------------------------------
*/
#ifdef __linux__
-EXPORT_SYMBOL(VMCIDatagram_DestroyHnd);
+EXPORT_SYMBOL(VMCIDatagram_CreateHnd);
#endif
int
-VMCIDatagram_DestroyHnd(VMCIHandle handle) // IN
+VMCIDatagram_CreateHnd(VMCIId resourceID, // IN:
+ uint32 flags, // IN:
+ VMCIDatagramRecvCB recvCB, // IN:
+ void *clientData, // IN:
+ VMCIHandle *outHandle) // OUT:
+{
+ return VMCIDatagramCreateHndInt(resourceID, flags, recvCB, clientData, outHandle);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIDatagramDestroyHndInt --
+ *
+ * Destroys a handle.
+ *
+ * Results:
+ * VMCI_SUCCESS or error code.
+ *
+ * Side effects:
+ * Host and guest state is cleaned up.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMCIDatagramDestroyHndInt(VMCIHandle handle) // IN
{
DatagramHashEntry *entry = DatagramHashGetEntry(handle);
if (entry == NULL) {
return VMCI_ERROR_NOT_FOUND;
}
-
+
DatagramHashRemoveEntry(entry->handle);
/*
*/
VMCI_WaitOnEvent(&entry->destroyEvent, DatagramReleaseCB, entry);
-
+
if ((entry->flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) {
int result;
VMCIDatagramWellKnownMapMsg wkMsg;
entry->handle.resource));
}
}
-
+
/* We know we are now holding the last reference so we can free the entry. */
VMCI_DestroyEvent(&entry->destroyEvent);
VMCI_FreeKernelMem(entry, sizeof *entry);
}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIDatagram_DestroyHnd --
+ *
+ * Destroys a handle.
+ *
+ * Results:
+ * VMCI_SUCCESS or error code.
+ *
+ * Side effects:
+ * Host and guest state is cleaned up.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#ifdef __linux__
+EXPORT_SYMBOL(VMCIDatagram_DestroyHnd);
+#endif
+
+int
+VMCIDatagram_DestroyHnd(VMCIHandle handle) // IN
+{
+ return VMCIDatagramDestroyHndInt(handle);
+}
+
+
/*
*-----------------------------------------------------------------------------
*
Bool VMCIDatagram_CheckHostCapabilities(void);
int VMCIDatagram_Dispatch(VMCIId contextID, VMCIDatagram *msg);
+int VMCIDatagramCreateHndInt(VMCIId resourceID,
+ uint32 flags,
+ VMCIDatagramRecvCB recvCB,
+ void *clientData,
+ VMCIHandle *outHandle);
+int VMCIDatagramDestroyHndInt(VMCIHandle handle);
+
int VMCIDatagramProcess_Create(VMCIDatagramProcess **outDgmProc,
VMCIDatagramCreateInfo *createInfo);
void VMCIDatagramProcess_Destroy(VMCIDatagramProcess *dgmProc);
int VMCIDatagramProcess_ReadCall(VMCIDatagramProcess *dgmProc,
size_t maxSize, VMCIDatagram **dg);
-
+
#endif //__VMCI_DATAGRAM_H__
/*
*----------------------------------------------------------------------
*
- * VMCIEvent_Subscribe --
+ * VMCIEventSubscribe --
*
* Subscribe to given event.
*
*----------------------------------------------------------------------
*/
-#if defined(__linux__) && !defined(VMKERNEL)
-EXPORT_SYMBOL(VMCIEvent_Subscribe);
-#endif
-
int
-VMCIEvent_Subscribe(VMCI_Event event, // IN
- VMCI_EventCB callback, // IN
- void *callbackData, // IN
- VMCIId *subscriptionID) // OUT
+VMCIEventSubscribe(VMCI_Event event, // IN
+ VMCI_EventCB callback, // IN
+ void *callbackData, // IN
+ VMCIId *subscriptionID) // OUT
{
int retval;
- VMCISubscription *s = NULL;
-
+ VMCISubscription *s = NULL;
+
if (subscriptionID == NULL) {
VMCI_LOG(("VMCIEvent: Invalid arguments.\n"));
return VMCI_ERROR_INVALID_ARGS;
if (s == NULL) {
return VMCI_ERROR_NO_MEM;
}
-
+
retval = VMCIEventRegisterSubscription(s, event, callback, callbackData);
if (retval < VMCI_SUCCESS) {
VMCI_FreeKernelMem(s, sizeof *s);
/*
*----------------------------------------------------------------------
*
- * VMCIEvent_Unsubscribe --
+ * VMCIEvent_Subscribe --
*
- * Unsubscribe to given event. Removes it from list and frees it.
- * Will return callbackData if requested by caller.
+ * Subscribe to given event.
*
* Results:
* VMCI_SUCCESS on success, error code otherwise.
*/
#if defined(__linux__) && !defined(VMKERNEL)
-EXPORT_SYMBOL(VMCIEvent_Unsubscribe);
+EXPORT_SYMBOL(VMCIEvent_Subscribe);
#endif
int
-VMCIEvent_Unsubscribe(VMCIId subID) // IN
+VMCIEvent_Subscribe(VMCI_Event event, // IN
+ VMCI_EventCB callback, // IN
+ void *callbackData, // IN
+ VMCIId *subscriptionID) // OUT
+{
+ return VMCIEventSubscribe(event, callback, callbackData, subscriptionID);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIEventUnsubscribe --
+ *
+ * Unsubscribe to given event. Removes it from list and frees it.
+ * Will return callbackData if requested by caller.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+VMCIEventUnsubscribe(VMCIId subID) // IN
{
VMCISubscription *s;
- /*
+ /*
* Return subscription. At this point we know noone else is accessing
- * the subscription so we can free it.
+ * the subscription so we can free it.
*/
s = VMCIEventUnregisterSubscription(subID);
if (s == NULL) {
return VMCI_SUCCESS;
}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIEvent_Unsubscribe --
+ *
+ * Unsubscribe to given event. Removes it from list and frees it.
+ * Will return callbackData if requested by caller.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#if defined(__linux__) && !defined(VMKERNEL)
+EXPORT_SYMBOL(VMCIEvent_Unsubscribe);
+#endif
+
+int
+VMCIEvent_Unsubscribe(VMCIId subID) // IN
+{
+ return VMCIEventUnsubscribe(subID);
+}
#include "vmci_defs.h"
#include "vmci_call_defs.h"
+#include "vmciGuestKernelAPI.h"
void VMCIEvent_Init(void);
void VMCIEvent_Exit(void);
int VMCIEvent_Dispatch(VMCIDatagram *msg);
#ifdef VMX86_TOOLS
Bool VMCIEvent_CheckHostCapabilities(void);
-#else
+#endif
-/*
- * Public VMCI Event API for host kernel.
- */
-typedef void (*VMCI_EventCB)(VMCIId subID, VMCI_EventData *ed,
- void *clientData);
+/*
+ * Non-public VMCI Event API for guest kernel.
+ */
-int VMCIEvent_Subscribe(VMCI_Event event, VMCI_EventCB callback,
+int VMCIEventSubscribe(VMCI_Event event, VMCI_EventCB callback,
void *callbackData, VMCIId *subID);
-int VMCIEvent_Unsubscribe(VMCIId subID);
-#endif
+int VMCIEventUnsubscribe(VMCIId subID);
+
#endif //__VMCI_EVENT_H__
#elif defined(SOLARIS)
# include <sys/ddi.h>
# include <sys/sunddi.h>
+#elif defined(__APPLE__)
+# include <IOKit/IOLib.h>
#else
# error "Platform not support by VMCI datagram API."
#endif // linux
#include "vmci_defs.h"
#include "vmci_call_defs.h"
-#if defined(__linux__) || defined(_WIN32)
+#if defined(__linux__) || defined(_WIN32) || defined(SOLARIS)
/* XXX TODO for other guests. */
# include "vmci_queue_pair.h"
#endif
/* VMCI Discovery Service API. */
int VMCIDs_Lookup(const char *name, VMCIHandle *out);
-#if defined(__linux__) || defined(_WIN32)
+#if defined(__linux__) || defined(_WIN32) || defined(SOLARIS)
/* VMCI QueuePair API. XXX TODO for other guests. */
int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ,
uint64 produceSize, VMCIQueue **consumeQ,
#ifndef _VMCI_GUEST_KERNEL_IF_H_
#define _VMCI_GUEST_KERNEL_IF_H_
-#if !defined(linux) && !defined(_WIN32) && !defined(SOLARIS)
-#error "Platform not supported."
-#endif
-
#if defined(_WIN32)
#include <ntddk.h>
#endif
#elif defined(SOLARIS)
typedef uint8_t * VMCIIoPort;
typedef ddi_acc_handle_t VMCIIoHandle;
+#elif defined(__APPLE__)
+ typedef unsigned short int VMCIIoPort;
+ typedef void *VMCIIoHandle;
#endif // VMKERNEL
void VMCI_ReadPortBytes(VMCIIoHandle handle, VMCIIoPort port, uint8 *buffer,
#include "pgtbl.h"
#include <linux/vmalloc.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
-# include <linux/mm.h> /* For vmalloc_to_page() */
+# include <linux/mm.h> /* For vmalloc_to_page() and get_user_pages()*/
+#else
+# include <linux/iobuf.h> /* For map_user_kiobuf() and unmap_kiobuf() */
#endif
#include <linux/socket.h> /* For memcpy_{to,from}iovec(). */
+#include <linux/pagemap.h> /* For page_cache_release() */
#include "vm_assert.h"
#include "vmci_kernel_if.h"
+#ifndef VMX86_TOOLS
+# include "vmciQueuePair.h"
+#endif
+
#include "vmci_queue_pair.h"
+#include "vmci_iocontrols.h"
/*
* In Linux 2.6.25 kernels and onwards, the symbol init_mm is no
VMCI_WaitOnEvent(VMCIEvent *event, // IN:
VMCIEventReleaseCB releaseCB, // IN:
void *clientData) // IN:
+{
+ /*
+ * XXX Should this be a TASK_UNINTERRUPTIBLE wait? I'm leaving it
+ * as it was for now.
+ */
+ VMCI_WaitOnEventInterruptible(event, releaseCB, clientData);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCI_WaitOnEventInterruptible --
+ *
+ * Results:
+ * True if the wait was interrupted by a signal, false otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+VMCI_WaitOnEventInterruptible(VMCIEvent *event, // IN:
+ VMCIEventReleaseCB releaseCB, // IN:
+ void *clientData) // IN:
{
DECLARE_WAITQUEUE(wait, current);
if (event == NULL || releaseCB == NULL) {
- return;
+ return FALSE;
}
add_wait_queue(event, &wait);
schedule();
current->state = TASK_RUNNING;
remove_wait_queue(event, &wait);
+
+ return signal_pending(current);
}
}
+#ifdef VMX86_TOOLS
/*
*-----------------------------------------------------------------------------
*
return VMCI_SUCCESS;
}
+#endif
#ifdef __KERNEL__
}
return TRUE;
}
+
+
+
+#ifndef VMX86_TOOLS
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIHost_FinishAttach --
+
+ * Finish the "attach" operation by update the queues to be
+ * backed by the shared memory represented by the "attach"
+ * structure.
+ *
+ * Results:
+ * VMCI_SUCCESS on success or negative error on failure.
+ *
+ * Side Effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMCIHost_FinishAttach(PageStoreAttachInfo *attach, // IN
+ VMCIQueue *produceQ, // OUT
+ VMCIQueue *consumeQ) // OUT
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ produceQ->queueHeaderPtr = kmap(attach->producePages[0]);
+ produceQ->page = &attach->producePages[1];
+ consumeQ->queueHeaderPtr = kmap(attach->consumePages[0]);
+ consumeQ->page = &attach->consumePages[1];
+#else
+ produceQ->queueHeaderPtr = kmap(attach->produceIoBuf->maplist[0]);
+ produceQ->page = &attach->produceIoBuf->maplist[1];
+ consumeQ->queueHeaderPtr = kmap(attach->consumeIoBuf->maplist[0]);
+ consumeQ->page = &attach->consumeIoBuf->maplist[1];
+#endif
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIHost_DetachMappings --
+ * Remove any local mappings of memory referenced in 'attach' in
+ * preparation for releasing the shared memory region entirely.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VMCIHost_DetachMappings(PageStoreAttachInfo *attach) // IN
+{
+ /*
+ * Unmap the header pages
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ kunmap(attach->producePages[0]);
+ kunmap(attach->consumePages[0]);
+#else
+ kunmap(attach->produceIoBuf->maplist[0]);
+ kunmap(attach->consumeIoBuf->maplist[0]);
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIHost_GetUserMemory --
+ * Lock the user pages referenced by the {produce,consume}Buffer
+ * struct into memory and populate the {produce,consume}Pages
+ * arrays in the attach structure with them.
+ *
+ * Results:
+ * VMCI_SUCCESS on sucess, negative error code on failure.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMCIHost_GetUserMemory(PageStoreAttachInfo *attach) // IN/OUT
+{
+ int retval;
+ int err = VMCI_SUCCESS;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+
+ attach->producePages =
+ VMCI_AllocKernelMem(attach->numProducePages * sizeof attach->producePages[0],
+ VMCI_MEMORY_NORMAL);
+ if (attach->producePages == NULL) {
+ return VMCI_ERROR_NO_MEM;
+ }
+ attach->consumePages =
+ VMCI_AllocKernelMem(attach->numConsumePages * sizeof attach->consumePages[0],
+ VMCI_MEMORY_NORMAL);
+ if (attach->consumePages == NULL) {
+ err = VMCI_ERROR_NO_MEM;
+ goto errorDealloc;
+ }
+
+ down_write(¤t->mm->mmap_sem);
+ retval = get_user_pages(current,
+ current->mm,
+ (VA)attach->produceBuffer,
+ attach->numProducePages,
+ 1, 0,
+ attach->producePages,
+ NULL);
+ if (retval < attach->numProducePages) {
+ Log("get_user_pages(produce) failed: %d\n", retval);
+ if (retval > 0) {
+ int i;
+ for (i = 0; i < retval; i++) {
+ page_cache_release(attach->producePages[i]);
+ }
+ }
+ err = VMCI_ERROR_NO_MEM;
+ goto out;
+ }
+
+ retval = get_user_pages(current,
+ current->mm,
+ (VA)attach->consumeBuffer,
+ attach->numConsumePages,
+ 1, 0,
+ attach->consumePages,
+ NULL);
+ if (retval < attach->numConsumePages) {
+ int i;
+ Log("get_user_pages(consume) failed: %d\n", retval);
+ if (retval > 0) {
+ for (i = 0; i < retval; i++) {
+ page_cache_release(attach->consumePages[i]);
+ }
+ }
+ for (i = 0; i < attach->numProducePages; i++) {
+ page_cache_release(attach->producePages[i]);
+ }
+ err = VMCI_ERROR_NO_MEM;
+ }
+
+out:
+ up_write(¤t->mm->mmap_sem);
+
+errorDealloc:
+ if (err < VMCI_SUCCESS) {
+ if (attach->producePages != NULL) {
+ VMCI_FreeKernelMem(attach->producePages,
+ attach->numProducePages *
+ sizeof attach->producePages[0]);
+ }
+ if (attach->consumePages != NULL) {
+ VMCI_FreeKernelMem(attach->consumePages,
+ attach->numConsumePages *
+ sizeof attach->consumePages[0]);
+ }
+ }
+
+ return err;
+
+#else
+
+ attach->produceIoBuf = VMCI_AllocKernelMem(sizeof *attach->produceIoBuf,
+ VMCI_MEMORY_NORMAL);
+ if (attach->produceIoBuf == NULL) {
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ attach->consumeIoBuf = VMCI_AllocKernelMem(sizeof *attach->consumeIoBuf,
+ VMCI_MEMORY_NORMAL);
+ if (attach->consumeIoBuf == NULL) {
+ VMCI_FreeKernelMem(attach->produceIoBuf,
+ sizeof *attach->produceIoBuf);
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ retval = map_user_kiobuf(WRITE, attach->produceIoBuf,
+ (VA)attach->produceBuffer,
+ attach->numProducePages * PAGE_SIZE);
+ if (retval < 0) {
+ err = VMCI_ERROR_NO_ACCESS;
+ goto out;
+ }
+
+ retval = map_user_kiobuf(WRITE, attach->consumeIoBuf,
+ (VA)attach->consumeBuffer,
+ attach->numConsumePages * PAGE_SIZE);
+ if (retval < 0) {
+ unmap_kiobuf(attach->produceIoBuf);
+ err = VMCI_ERROR_NO_ACCESS;
+ }
+
+out:
+
+ if (err < VMCI_SUCCESS) {
+ if (attach->produceIoBuf != NULL) {
+ VMCI_FreeKernelMem(attach->produceIoBuf,
+ sizeof *attach->produceIoBuf);
+ }
+ if (attach->consumeIoBuf != NULL) {
+ VMCI_FreeKernelMem(attach->consumeIoBuf,
+ sizeof *attach->consumeIoBuf);
+ }
+ }
+ return err;
+
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIHost_ReleaseUserMemory --
+ * Release the reference to user pages stored in the attach
+ * struct
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Pages are released from the page cache and may become
+ * swappable again.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VMCIHost_ReleaseUserMemory(PageStoreAttachInfo *attach) // IN
+{
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ int i;
+
+ ASSERT(attach->producePages);
+ ASSERT(attach->consumePages);
+ for (i = 0; i < attach->numProducePages; i++) {
+ ASSERT(attach->producePages[i]);
+
+ set_page_dirty(attach->producePages[i]);
+ page_cache_release(attach->producePages[i]);
+ }
+
+ for (i = 0; i < attach->numConsumePages; i++) {
+ ASSERT(attach->consumePages[i]);
+
+ set_page_dirty(attach->consumePages[i]);
+ page_cache_release(attach->consumePages[i]);
+ }
+
+ VMCI_FreeKernelMem(attach->producePages,
+ attach->numProducePages *
+ sizeof attach->producePages[0]);
+ VMCI_FreeKernelMem(attach->consumePages,
+ attach->numConsumePages *
+ sizeof attach->consumePages[0]);
+#else
+ mark_dirty_kiobuf(attach->produceIoBuf,
+ attach->numProducePages * PAGE_SIZE);
+ unmap_kiobuf(attach->produceIoBuf);
+
+ mark_dirty_kiobuf(attach->consumeIoBuf,
+ attach->numConsumePages * PAGE_SIZE);
+ unmap_kiobuf(attach->consumeIoBuf);
+
+ VMCI_FreeKernelMem(attach->produceIoBuf,
+ sizeof *attach->produceIoBuf);
+ VMCI_FreeKernelMem(attach->consumeIoBuf,
+ sizeof *attach->consumeIoBuf);
+#endif
+}
+
+#endif
# include <linux/module.h>
#elif defined(_WIN32)
# include <wdm.h>
+#elif defined(__APPLE__)
+# include <IOKit/IOLib.h>
#endif /* __linux__ */
#include "vm_assert.h"
# define VMCIQP_OFFSET_OF(Struct, field) ((uintptr_t)&(((Struct *)0)->field))
#ifdef __linux__
ASSERT_ON_COMPILE(VMCIQP_OFFSET_OF(VMCIQueue, page) == PAGE_SIZE);
-#else
+#elif !defined(SOLARIS)
ASSERT_ON_COMPILE(VMCIQP_OFFSET_OF(VMCIQueue, buffer) == PAGE_SIZE);
#endif
# undef VMCIQP_OFFSET_OF
# include <sys/ddi.h>
# include <sys/sunddi.h>
# include <sys/disp.h>
+#elif defined(__APPLE__)
+# include <IOKit/IOLib.h>
#else
#error "platform not supported."
#endif //linux
return in_interrupt();
#elif defined(SOLARIS)
return servicing_interrupt(); /* servicing_interrupt is not part of DDI. */
+#elif defined(__APPLE__)
+ /*
+ * All interrupt servicing is handled by IOKit functions, by the time the IOService
+ * interrupt handler is called we're no longer in an interrupt dispatch level.
+ */
+ return false;
#endif //
}
MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface");
MODULE_VERSION(VMCI_DRIVER_VERSION_STRING);
MODULE_LICENSE("GPL v2");
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
/* reallocate. */
uint32 arraySize = sizeof array->capacity + sizeof array->size +
array->capacity * sizeof(VMCIHandle);
- VMCIHandleArray *newArray =
+ VMCIHandleArray *newArray = (VMCIHandleArray *)
VMCI_AllocKernelMem(arraySize + array->capacity * sizeof(VMCIHandle),
VMCI_MEMORY_NONPAGED);
if (newArray == NULL) {
VMCIHandleArray_RemoveEntry(VMCIHandleArray *array,
VMCIHandle entryHandle)
{
- int i;
+ uint32 i;
VMCIHandle handle = VMCI_INVALID_HANDLE;
ASSERT(array);
VMCIHandleArray_HasEntry(const VMCIHandleArray *array,
VMCIHandle entryHandle)
{
- int i;
+ uint32 i;
ASSERT(array);
for (i = 0; i < array->size; i++) {
IOCTLCMD(SOCKETS_GET_SOCK_NAME),
IOCTLCMD(SOCKETS_GET_SOCK_OPT),
IOCTLCMD(SOCKETS_GET_VM_BY_NAME),
+ IOCTLCMD(SOCKETS_IOCTL),
IOCTLCMD(SOCKETS_LISTEN),
IOCTLCMD(SOCKETS_RECV),
IOCTLCMD(SOCKETS_RECV_FROM),
IOCTLCMD(SOCKETS_SEND_TO),
IOCTLCMD(SOCKETS_SET_SOCK_OPT),
IOCTLCMD(SOCKETS_SHUTDOWN),
- IOCTLCMD(SOCKETS_SOCKET), /* 1989 on Linux. */
+ IOCTLCMD(SOCKETS_SOCKET), /* 1990 on Linux. */
/* END VMCI SOCKETS */
/*
- * We reserve a range of 5 ioctls for VMCI Sockets to grow. We cannot
+ * We reserve a range of 4 ioctls for VMCI Sockets to grow. We cannot
* reserve many ioctls here since we are close to overlapping with vmmon
* ioctls. Define a meta-ioctl if running out of this binary space.
*/
// Must be last.
- IOCTLCMD(SOCKETS_LAST) = IOCTLCMD(SOCKETS_SOCKET) + 5, /* 1994 on Linux. */
+ IOCTLCMD(SOCKETS_LAST) = IOCTLCMD(SOCKETS_SOCKET) + 4, /* 1994 on Linux. */
/*
* The VSockets ioctls occupy the block above. We define a new range of
* VMCI ioctls to maintain binary compatibility between the user land and
* the kernel driver. Careful, vmmon ioctls start from 2001, so this means
- * we can add only 5 new VMCI ioctls. Define a meta-ioctl if running out of
+ * we can add only 4 new VMCI ioctls. Define a meta-ioctl if running out of
* this binary space.
*/
IOCTLCMD(FIRST2),
IOCTLCMD(SET_NOTIFY) = IOCTLCMD(FIRST2), /* 1995 on Linux. */
+ IOCTLCMD(VMCID_RPC),
IOCTLCMD(LAST2),
};
* Windows VMCI ioctl definitions.
*/
+/*
+ * The first is the device name in user-mode. The next two are for registering
+ * or opening the device in kernel-mode, and are always in UNICODE.
+ */
+#define VMCI_DEVICE_NAME TEXT("\\\\.\\VMCI")
+#define VMCI_DEVICE_NAME_PATH L"\\Device\\vmci"
+#define VMCI_DEVICE_LINK_PATH L"\\DosDevices\\vmci"
+
/* These values cannot be changed since some of the ioctl values are public. */
#define FILE_DEVICE_VMCI 0x8103
#define VMCI_IOCTL_BASE_INDEX 0x801
VMCIIOCTL_BUFFERED(SOCKETS_GET_SOCK_OPT)
#define IOCTL_VMCI_SOCKETS_GET_VM_BY_NAME \
VMCIIOCTL_BUFFERED(SOCKETS_GET_VM_BY_NAME)
+#define IOCTL_VMCI_SOCKETS_IOCTL \
+ VMCIIOCTL_BUFFERED(SOCKETS_IOCTL)
#define IOCTL_VMCI_SOCKETS_LISTEN \
VMCIIOCTL_BUFFERED(SOCKETS_LISTEN)
#define IOCTL_VMCI_SOCKETS_RECV \
VMCIIOCTL_BUFFERED(SOCKETS_SOCKET)
/* END VMCI SOCKETS */
+/* BEGIN VMCI USER SPACE DAEMON */
+#define IOCTL_VMCI_VMCID_RPC \
+ VMCIIOCTL_BUFFERED(VMCID_RPC)
+/* END VMCI USER SPACE DAEMON */
+
#endif // _WIN32
uint32 _pad;
} VMCISetNotifyInfo;
+/* User space daemon command numbers. */
+typedef enum VMCIDRequestType {
+ VMCID_REQ_NEW_PAGE_STORE,
+ VMCID_REQ_FREE_PAGE_STORE,
+ VMCID_REQ_ATTACH_PAGE_STORE,
+ VMCID_REQ_DETACH_PAGE_STORE,
+} VMCIDRequestType;
+
+#define VMCI_VMCID_INVALID_REQ CONST64U(-1)
+
+/* Used to pass requests/responses to the user space daemon. */
+typedef struct VMCIDRpc {
+ uint64 reqId;
+ uint32 reqType;
+ uint32 reqResult;
+ /* Passing page file names */
+ VA64 producePageFile; /* User VA. */
+ VA64 consumePageFile; /* User VA. */
+ uint64 producePageFileSize; /* Size of the file name array. */
+ uint64 consumePageFileSize; /* Size of the file name array. */
+ /* Used for attach/detach */
+ VA64 produceVA;
+ VA64 consumeVA;
+ uint64 numProducePages;
+ uint64 numConsumePages;
+} VMCIDRpc;
#ifdef __APPLE__
/*
# include <sys/mutex.h>
# include <sys/poll.h>
# include <sys/semaphore.h>
+# include <sys/kmem.h>
#endif
#include "vm_basic_types.h"
typedef unsigned long VMCILockFlags;
typedef semaphore_t VMCIEvent;
typedef IOLock *VMCIMutex;
+ typedef void *VMCIPpnList; /* Actually a pointer to the C++ Object IOMemoryDescriptor */
#elif defined(_WIN32)
typedef KSPIN_LOCK VMCILock;
typedef KIRQL VMCILockFlags;
typedef kmutex_t VMCILock;
typedef unsigned long VMCILockFlags;
typedef ksema_t VMCIEvent;
+ typedef kmutex_t VMCIMutex;
+ typedef PPN *VMCIPpnList; /* List of PPNs in produce/consume queue. */
#endif // VMKERNEL
/* Callback needed for correctly waiting on events. */
void VMCI_SignalEvent(VMCIEvent *event);
void VMCI_WaitOnEvent(VMCIEvent *event, VMCIEventReleaseCB releaseCB,
void *clientData);
+#if (defined(__linux__) || defined(_WIN32)) && !defined(VMKERNEL)
+Bool VMCI_WaitOnEventInterruptible(VMCIEvent *event,
+ VMCIEventReleaseCB releaseCB,
+ void *clientData);
+#endif
-/* XXX TODO for VMKERNEL (host) and Solaris (guest). */
#if !defined(VMKERNEL) && (defined(__linux__) || defined(_WIN32) || \
- defined(__APPLE__))
+ defined(__APPLE__) || defined(SOLARIS))
int VMCI_CopyFromUser(void *dst, const void *src, size_t len);
#endif
-#if !defined(SOLARIS)
int VMCIMutex_Init(VMCIMutex *mutex);
void VMCIMutex_Destroy(VMCIMutex *mutex);
void VMCIMutex_Acquire(VMCIMutex *mutex);
void VMCIMutex_Release(VMCIMutex *mutex);
-#endif
-/* XXX TODO for Solaris (guest). */
-#if !defined(VMKERNEL) && (defined(__linux__) || defined(_WIN32))
+#if defined(SOLARIS)
+int VMCIKernelIf_Init(void);
+void VMCIKernelIf_Exit(void);
+#endif /* SOLARIS */
+
+#if !defined(VMKERNEL) && (defined(__linux__) || defined(_WIN32) || \
+ defined(SOLARIS) || defined(__APPLE__))
VA VMCI_AllocQueueKVA(uint64 size);
void VMCI_FreeQueueKVA(VA va, uint64 size);
typedef struct PPNSet {
int VMCI_PopulatePPNList(uint8 *callBuf, const PPNSet *ppnSet);
#endif
+#if (defined(_WIN32) || defined(__linux__)) && !defined(VMKERNEL) && \
+ !defined(VMX86_TOOLS)
+struct PageStoreAttachInfo;
+struct VMCIQueue;
+
+int VMCIHost_GetUserMemory(struct PageStoreAttachInfo *attach);
+void VMCIHost_ReleaseUserMemory(struct PageStoreAttachInfo *attach);
+int VMCIHost_FinishAttach(struct PageStoreAttachInfo *attach,
+ struct VMCIQueue *produceQ,
+ struct VMCIQueue *detachQ);
+void VMCIHost_DetachMappings(struct PageStoreAttachInfo *attach);
+#endif
+
#endif // _VMCI_KERNEL_IF_H_
*
*********************************************************/
-#ifndef _VMCI_QUEUE_PAIR_H_
-#define _VMCI_QUEUE_PAIR_H_
+#ifndef _PUBLIC_VMCI_QUEUE_PAIR_H_
+#define _PUBLIC_VMCI_QUEUE_PAIR_H_
/*
*
#define INCLUDE_ALLOW_VMX
#include "includeCheck.h"
+#if defined(__APPLE__) && defined(KERNEL)
+# include "vmci_kernel_if.h"
+#endif // __APPLE__
+
#include "vm_basic_defs.h"
#include "vm_basic_types.h"
#include "vm_atomic.h"
#include "vmci_defs.h"
#include "vm_assert.h"
-#if defined(__linux__) && defined(__KERNEL__)
-# include "vmci_kernel_if.h"
+#if defined(SOLARIS)
+#include <sys/uio.h>
+#endif
+
+#if (defined(__linux__) && defined(__KERNEL__)) || defined(SOLARIS)
+#include "vmci_kernel_if.h"
#endif
Atomic_uint64 consumerHead; /* Offset in peer queue. */
} VMCIQueueHeader;
-typedef struct VMCIQueue {
- VMCIQueueHeader queueHeader;
- uint8 _padding[PAGE_SIZE - sizeof(VMCIQueueHeader)];
-#if defined(__linux__) && defined(__KERNEL__)
- struct page *page[0]; /* List of pages containing queue data. */
+
+/*
+ * If one client of a QueuePair is a 32bit entity, we restrict the QueuePair
+ * size to be less than 4GB, and use 32bit atomic operations on the head and
+ * tail pointers. 64bit atomic read on a 32bit entity involves cmpxchg8b which
+ * is an atomic read-modify-write. This will cause traces to fire when a 32bit
+ * consumer tries to read the producer's tail pointer, for example, because the
+ * consumer has read-only access to the producer's tail pointer.
+ *
+ * We provide the following macros to invoke 32bit or 64bit atomic operations
+ * based on the architecture the code is being compiled on.
+ */
+
+/* Architecture independent maximum queue size. */
+#define QP_MAX_QUEUE_SIZE_ARCH_ANY CONST64U(0xffffffff)
+
+#ifdef __x86_64__
+# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffffffffffff)
+# define QPAtomic_ReadOffset(x) Atomic_Read64(x)
+# define QPAtomic_WriteOffset(x, y) Atomic_Write64(x, y)
#else
- uint8 buffer[0]; /* Buffer containing data. */
+ /*
+ * Wrappers below are being used to call Atomic_Read32 because of the
+ * 'type punned' compilation warning received when Atomic_Read32 is
+ * called with a Atomic_uint64 pointer typecasted to Atomic_uint32
+ * pointer from QPAtomic_ReadOffset. Ditto with QPAtomic_WriteOffset.
+ */
+
+ static INLINE uint32
+ TypeSafe_Atomic_Read32(void *var) // IN:
+ {
+ return Atomic_Read32((Atomic_uint32 *)(var));
+ }
+
+ static INLINE void
+ TypeSafe_Atomic_Write32(void *var, uint32 val) // IN:
+ {
+ Atomic_Write32((Atomic_uint32 *)(var), (uint32)(val));
+ }
+
+# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffff)
+# define QPAtomic_ReadOffset(x) TypeSafe_Atomic_Read32((void *)(x))
+# define QPAtomic_WriteOffset(x, y) \
+ TypeSafe_Atomic_Write32((void *)(x), (uint32)(y))
+#endif /* __x86_64__ */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * AddPointer --
+ *
+ * Helper to add a given offset to a head or tail pointer. Wraps the value
+ * of the pointer around the max size of the queue.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+AddPointer(Atomic_uint64 *var, // IN:
+ size_t add, // IN:
+ uint64 size) // IN:
+{
+ uint64 newVal = QPAtomic_ReadOffset(var);
+
+ if (newVal >= size - add) {
+ newVal -= size;
+ }
+ newVal += add;
+
+ QPAtomic_WriteOffset(var, newVal);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue
+ *
+ * This data type contains the information about a queue.
+ *
+ * There are two queues (hence, queue pairs) per transaction model
+ * between a pair of end points, A & B. One queue is used by end
+ * point A to transmit commands and responses to B. The other queue
+ * is used by B to transmit commands and responses.
+ *
+ * There are six distinct definitions of the VMCIQueue structure.
+ * Five are found below along with descriptions of under what build
+ * type the VMCIQueue structure type is compiled. The sixth is
+ * located in a separate file and is used in the VMKernel context.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(VMX86_TOOLS) || defined(VMX86_VMX)
+# if defined(__linux__) && defined(__KERNEL__)
+ /*
+ * Linux Kernel Guest.
+ *
+ * The VMCIQueue is two or more machine pages where the first
+ * contains the queue header and the second and subsequent pages
+ * contain an array of struct page pointers to the actual pages
+ * that contain the queue data.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader queueHeader;
+ uint8 _padding[PAGE_SIZE - sizeof(VMCIQueueHeader)];
+ struct page *page[0];
+ } VMCIQueue;
+# elif defined(SOLARIS)
+ /*
+ * Solaris Kernel Guest.
+ *
+ * kmem_cache_alloc with object size of one page is being used to get
+ * page aligned memory for queueHeader. VMCIQueue stores a pointer to
+ * the queueHeader and the array of virtual addresses of actual pages
+ * that contain the queue data.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader *queueHeaderPtr;
+ void *vaddr[0];
+ } VMCIQueue;
+
+# else
+ /*
+ * VMX App and Windows Kernel Guest
+ *
+ * The VMCIQueue is two or more machine pages where the first
+ * contains the queue header and the second and subsequent pages
+ * contain the actual queue buffer data.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader queueHeader;
+ uint8 _padding[PAGE_SIZE - sizeof(VMCIQueueHeader)];
+ uint8 buffer[0]; // VMX doesn't use this today
+ } VMCIQueue;
+# endif
+#else
+# if defined(__linux__) && defined(__KERNEL__)
+ /*
+ * Linux Kernel Host
+ *
+ * The VMCIQueueHeader has been created elsewhere and this
+ * structure (VMCIQueue) needs a pointer to that
+ * VMCIQueueHeader.
+ *
+ * Also, the queue contents are managed by an array of struct
+ * pages which are managed elsewhere. So, this structure simply
+ * contains the address of that array of struct pages.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader *queueHeaderPtr;
+ struct page **page;
+ } VMCIQueue;
+# else
+ /*
+ * Windows Host
+ *
+ * The VMCIQueueHeader has been created elsewhere and this
+ * structure (VMCIQueue) needs a pointer to that
+ * VMCIQueueHeader.
+ *
+ * Also, the queue contents are managed by an array of bytes
+ * which are managed elsewhere. So, this structure simply
+ * contains the address of that array of bytes.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader *queueHeaderPtr;
+ uint8 *buffer;
+ } VMCIQueue;
+# endif
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_GetHeader() --
+ *
+ * Helper routine to get the address of the VMCIQueueHeader.
+ * Note that this routine is useful because there are two ways
+ * queues are declared (at least as far as the queue header is
+ * concerned, anyway). In one way, the queue contains the header
+ * in the other the queue contains a pointer to the header. This
+ * helper hides that ambiguity from users of queues who need
+ * access to the header.
+ *
+ * Results:
+ * The address of the queue header.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE VMCIQueueHeader *
+VMCIQueue_GetHeader(const VMCIQueue *q)
+{
+#if defined(VMX86_TOOLS) || defined(VMX86_VMX)
+ /*
+ * All guest variants except Solaris have a complete queue header
+ */
+#if defined (SOLARIS)
+ return (VMCIQueueHeader *)q->queueHeaderPtr;
+#else
+ return (VMCIQueueHeader *)&q->queueHeader;
+#endif /* SOLARIS */
+#else
+ /*
+ * All host variants have a pointer to the queue header
+ */
+ return (VMCIQueueHeader *)q->queueHeaderPtr;
#endif
-} VMCIQueue;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_ProducerTail() --
+ *
+ * Helper routine to get the Producer Tail from the supplied queue.
+ *
+ * Results:
+ * The contents of the queue's producer tail.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+static INLINE uint64
+VMCIQueue_ProducerTail(const VMCIQueue *queue)
+{
+ return QPAtomic_ReadOffset(&VMCIQueue_GetHeader(queue)->producerTail);
+}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_ConsumerHead() --
+ *
+ * Helper routine to get the Consumer Head from the supplied queue.
+ *
+ * Results:
+ * The contents of the queue's consumer tail.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE uint64
+VMCIQueue_ConsumerHead(const VMCIQueue *queue)
+{
+ return QPAtomic_ReadOffset(&VMCIQueue_GetHeader(queue)->consumerHead);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_AddProducerTail() --
+ *
+ * Helper routine to increment the Producer Tail. Fundamentally,
+ * AddPointer() is used to manipulate the tail itself.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+VMCIQueue_AddProducerTail(VMCIQueue *queue,
+ size_t add,
+ uint64 queueSize)
+{
+ AddPointer(&VMCIQueue_GetHeader(queue)->producerTail, add, queueSize);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_AddConsumerHead() --
+ *
+ * Helper routine to increment the Consumer Head. Fundamentally,
+ * AddPointer() is used to manipulate the head itself.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+VMCIQueue_AddConsumerHead(VMCIQueue *queue,
+ size_t add,
+ uint64 queueSize)
+{
+ AddPointer(&VMCIQueue_GetHeader(queue)->consumerHead, add, queueSize);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIMemcpy{To,From}QueueFunc() prototypes. Functions of these
+ * types are passed around to enqueue and dequeue routines. Note that
+ * often the functions passed are simply wrappers around memcpy
+ * itself.
+ *
+ *-----------------------------------------------------------------------------
+ */
typedef int VMCIMemcpyToQueueFunc(VMCIQueue *queue, uint64 queueOffset,
const void *src, size_t srcOffset,
size_t size);
const VMCIQueue *queue, uint64 queueOffset,
size_t size);
-#if defined(__linux__) && defined(__KERNEL__)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIMemcpy{To,From}Queue[v]() prototypes (and, in some cases, an
+ * inline version).
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined (SOLARIS) || (defined(__linux__) && defined(__KERNEL__))
int VMCIMemcpyToQueue(VMCIQueue *queue, uint64 queueOffset, const void *src,
size_t srcOffset, size_t size);
int VMCIMemcpyFromQueue(void *dest, size_t destOffset, const VMCIQueue *queue,
#endif /* __linux__ && __KERNEL__ */
-/*
- * If one client of a QueuePair is a 32bit entity, we restrict the QueuePair
- * size to be less than 4GB, and use 32bit atomic operations on the head and
- * tail pointers. 64bit atomic read on a 32bit entity involves cmpxchg8b which
- * is an atomic read-modify-write. This will cause traces to fire when a 32bit
- * consumer tries to read the producer's tail pointer, for example, because the
- * consumer has read-only access to the producer's tail pointer.
- *
- * We provide the following macros to invoke 32bit or 64bit atomic operations
- * based on the architecture the code is being compiled on.
- */
-
-/* Architecture independent maximum queue size. */
-#define QP_MAX_QUEUE_SIZE_ARCH_ANY CONST64U(0xffffffff)
-
-#ifdef __x86_64__
-# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffffffffffff)
-# define QPAtomic_ReadOffset(x) Atomic_Read64(x)
-# define QPAtomic_WriteOffset(x, y) Atomic_Write64(x, y)
-#else
-# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffff)
-# define QPAtomic_ReadOffset(x) Atomic_Read32((Atomic_uint32 *)(x))
-# define QPAtomic_WriteOffset(x, y) \
- Atomic_Write32((Atomic_uint32 *)(x), (uint32)(y))
-#endif
-
-
/*
*-----------------------------------------------------------------------------
*
* VMCIQueue_CheckAlignment --
*
- * Checks if the given queue is aligned to page boundary.
+ * Checks if the given queue is aligned to page boundary. Returns TRUE if
+ * the alignment is good.
*
* Results:
* TRUE or FALSE.
static INLINE Bool
VMCIQueue_CheckAlignment(const VMCIQueue *queue) // IN:
{
- return ((uintptr_t)queue & (PAGE_SIZE - 1)) == 0;
+ /*
+ * Checks if header of the queue is page aligned.
+ */
+ return ((uintptr_t)VMCIQueue_GetHeader(queue) & (PAGE_SIZE - 1)) == 0;
}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_GetPointers --
+ *
+ * Helper routine for getting the head and the tail pointer for a queue.
+ * Both the VMCIQueues are needed to get both the pointers for one queue.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
static INLINE void
VMCIQueue_GetPointers(const VMCIQueue *produceQ,
const VMCIQueue *consumeQ,
uint64 *producerTail,
uint64 *consumerHead)
{
- *producerTail = QPAtomic_ReadOffset(&produceQ->queueHeader.producerTail);
- *consumerHead = QPAtomic_ReadOffset(&consumeQ->queueHeader.consumerHead);
+ *producerTail = VMCIQueue_ProducerTail(produceQ);
+ *consumerHead = VMCIQueue_ConsumerHead(consumeQ);
}
static INLINE void
VMCIQueue_ResetPointers(VMCIQueue *queue) // IN:
{
- QPAtomic_WriteOffset(&queue->queueHeader.producerTail, CONST64U(0));
- QPAtomic_WriteOffset(&queue->queueHeader.consumerHead, CONST64U(0));
+ QPAtomic_WriteOffset(&VMCIQueue_GetHeader(queue)->producerTail, CONST64U(0));
+ QPAtomic_WriteOffset(&VMCIQueue_GetHeader(queue)->consumerHead, CONST64U(0));
}
VMCIQueue *queue) // IN:
{
ASSERT_NOT_IMPLEMENTED(VMCIQueue_CheckAlignment(queue));
- queue->queueHeader.handle = handle;
+ VMCIQueue_GetHeader(queue)->handle = handle;
VMCIQueue_ResetPointers(queue);
}
const uint64 produceQSize, // IN:
uint64 *freeSpace) // OUT:
{
- const uint64 tail =
- QPAtomic_ReadOffset(&produceQueue->queueHeader.producerTail);
- const uint64 head =
- QPAtomic_ReadOffset(&consumeQueue->queueHeader.consumerHead);
+ const uint64 tail = VMCIQueue_ProducerTail(produceQueue);
+ const uint64 head = VMCIQueue_ConsumerHead(consumeQueue);
ASSERT(freeSpace);
}
-/*
- *-----------------------------------------------------------------------------
- *
- * AddPointer --
- *
- * Helper to add a given offset to a head or tail pointer. Wraps the value
- * of the pointer around the max size of the queue.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static INLINE void
-AddPointer(Atomic_uint64 *var, // IN:
- size_t add, // IN:
- uint64 max) // IN:
-{
- uint64 newVal = QPAtomic_ReadOffset(var);
-
- if (newVal >= max - add) {
- newVal -= max;
- }
- newVal += add;
-
- QPAtomic_WriteOffset(var, newVal);
-}
-
-
/*
*-----------------------------------------------------------------------------
*
{
const int64 freeSpace = VMCIQueue_FreeSpace(produceQueue, consumeQueue,
produceQSize);
- const uint64 tail =
- QPAtomic_ReadOffset(&produceQueue->queueHeader.producerTail);
+ const uint64 tail = VMCIQueue_ProducerTail(produceQueue);
size_t written;
if (!freeSpace) {
memcpyToQueue(produceQueue, tail, buf, 0, tmp);
memcpyToQueue(produceQueue, 0, buf, tmp, written - tmp);
}
- AddPointer(&produceQueue->queueHeader.producerTail, written, produceQSize);
+ AddPointer(&VMCIQueue_GetHeader(produceQueue)->producerTail, written, produceQSize);
return written;
}
}
-#if defined(__linux__) && defined(__KERNEL__)
+#if defined (SOLARIS) || (defined(__linux__) && defined(__KERNEL__))
/*
*-----------------------------------------------------------------------------
*
{
const int64 bufReady = VMCIQueue_BufReady(consumeQueue, produceQueue,
consumeQSize);
- const uint64 head =
- QPAtomic_ReadOffset(&produceQueue->queueHeader.consumerHead);
+ const uint64 head = VMCIQueue_ConsumerHead(produceQueue);
size_t written;
if (!bufReady) {
memcpyFromQueue(buf, 0, consumeQueue, head, tmp);
memcpyFromQueue(buf, tmp, consumeQueue, 0, written - tmp);
}
- AddPointer(&produceQueue->queueHeader.consumerHead, written, consumeQSize);
+ AddPointer(&VMCIQueue_GetHeader(produceQueue)->consumerHead, written, consumeQSize);
return written;
}
}
-#if defined(__linux__) && defined(__KERNEL__)
+#if defined(SOLARIS) || (defined(__linux__) && defined(__KERNEL__))
/*
*-----------------------------------------------------------------------------
*
}
#endif
-#endif /* !_VMCI_QUEUE_PAIR_H_ */
+#endif /* !_PUBLIC_VMCI_QUEUE_PAIR_H_ */
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
MODULE_DESCRIPTION("VMware Host/Guest File System");
MODULE_VERSION(VMHGFS_DRIVER_VERSION_STRING);
MODULE_LICENSE("GPL v2");
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
/*
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
MODULE_DESCRIPTION("VMware Memory Control Driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(VMMEMCTL_DRIVER_VERSION_STRING);
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
MODULE_DESCRIPTION("VMware Sync Driver");
MODULE_VERSION(VMSYNC_DRIVER_VERSION_STRING);
MODULE_LICENSE("GPL v2");
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
static int VmSyncRelease(struct inode* inode,
struct file *file);
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
#define NET_SG_SIZE(len) (sizeof(NetSG_Array) + (len - NET_SG_DEFAULT_LENGTH) * sizeof(NetSG_Elem))
-#define NET_SG_MAKE_PA(elem) (((PA)elem.addrHi << 32) | (PA)elem.addrLow)
-#define NET_SG_MAKE_PTR(elem) \
- ((char*)(uintptr_t)(((uint64)elem.addrHi << 32) | elem.addrLow))
+#define NET_SG_MAKE_PA(elem) (PA)QWORD(elem.addrHi, elem.addrLow)
+#define NET_SG_MAKE_PTR(elem) (char *)(uintptr_t)QWORD(elem.addrHi, elem.addrLow)
#endif
MODULE_DESCRIPTION("VMware Virtual Ethernet driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(VMXNET_DRIVER_VERSION_STRING);
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
MODULE_DESCRIPTION(VMXNET3_DRIVER_DESC);
MODULE_LICENSE("GPL v2");
MODULE_VERSION(VMXNET3_DRIVER_VERSION_STRING);
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
endif
vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) \
+ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \
$(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \
-DKBUILD_BASENAME=\"$(DRIVER)\" \
-Werror -S -o /dev/null -xc $(1) \
(!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
# include <stddef.h>
#else
-// XXX the __cplusplus one matches that of VC++, to prevent redefinition warning
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
#ifndef offsetof
-#ifdef __cplusplus
+#ifdef _WIN32
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#include "vmci_defs.h"
#include "vmci_call_defs.h"
-#if defined(__linux__) || defined(_WIN32)
+#if defined(__linux__) || defined(_WIN32) || defined(SOLARIS)
/* XXX TODO for other guests. */
# include "vmci_queue_pair.h"
#endif
/* VMCI Discovery Service API. */
int VMCIDs_Lookup(const char *name, VMCIHandle *out);
-#if defined(__linux__) || defined(_WIN32)
+#if defined(__linux__) || defined(_WIN32) || defined(SOLARIS)
/* VMCI QueuePair API. XXX TODO for other guests. */
int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ,
uint64 produceSize, VMCIQueue **consumeQ,
IOCTLCMD(SOCKETS_GET_SOCK_NAME),
IOCTLCMD(SOCKETS_GET_SOCK_OPT),
IOCTLCMD(SOCKETS_GET_VM_BY_NAME),
+ IOCTLCMD(SOCKETS_IOCTL),
IOCTLCMD(SOCKETS_LISTEN),
IOCTLCMD(SOCKETS_RECV),
IOCTLCMD(SOCKETS_RECV_FROM),
IOCTLCMD(SOCKETS_SEND_TO),
IOCTLCMD(SOCKETS_SET_SOCK_OPT),
IOCTLCMD(SOCKETS_SHUTDOWN),
- IOCTLCMD(SOCKETS_SOCKET), /* 1989 on Linux. */
+ IOCTLCMD(SOCKETS_SOCKET), /* 1990 on Linux. */
/* END VMCI SOCKETS */
/*
- * We reserve a range of 5 ioctls for VMCI Sockets to grow. We cannot
+ * We reserve a range of 4 ioctls for VMCI Sockets to grow. We cannot
* reserve many ioctls here since we are close to overlapping with vmmon
* ioctls. Define a meta-ioctl if running out of this binary space.
*/
// Must be last.
- IOCTLCMD(SOCKETS_LAST) = IOCTLCMD(SOCKETS_SOCKET) + 5, /* 1994 on Linux. */
+ IOCTLCMD(SOCKETS_LAST) = IOCTLCMD(SOCKETS_SOCKET) + 4, /* 1994 on Linux. */
/*
* The VSockets ioctls occupy the block above. We define a new range of
* VMCI ioctls to maintain binary compatibility between the user land and
* the kernel driver. Careful, vmmon ioctls start from 2001, so this means
- * we can add only 5 new VMCI ioctls. Define a meta-ioctl if running out of
+ * we can add only 4 new VMCI ioctls. Define a meta-ioctl if running out of
* this binary space.
*/
IOCTLCMD(FIRST2),
IOCTLCMD(SET_NOTIFY) = IOCTLCMD(FIRST2), /* 1995 on Linux. */
+ IOCTLCMD(VMCID_RPC),
IOCTLCMD(LAST2),
};
* Windows VMCI ioctl definitions.
*/
+/*
+ * The first is the device name in user-mode. The next two are for registering
+ * or opening the device in kernel-mode, and are always in UNICODE.
+ */
+#define VMCI_DEVICE_NAME TEXT("\\\\.\\VMCI")
+#define VMCI_DEVICE_NAME_PATH L"\\Device\\vmci"
+#define VMCI_DEVICE_LINK_PATH L"\\DosDevices\\vmci"
+
/* These values cannot be changed since some of the ioctl values are public. */
#define FILE_DEVICE_VMCI 0x8103
#define VMCI_IOCTL_BASE_INDEX 0x801
VMCIIOCTL_BUFFERED(SOCKETS_GET_SOCK_OPT)
#define IOCTL_VMCI_SOCKETS_GET_VM_BY_NAME \
VMCIIOCTL_BUFFERED(SOCKETS_GET_VM_BY_NAME)
+#define IOCTL_VMCI_SOCKETS_IOCTL \
+ VMCIIOCTL_BUFFERED(SOCKETS_IOCTL)
#define IOCTL_VMCI_SOCKETS_LISTEN \
VMCIIOCTL_BUFFERED(SOCKETS_LISTEN)
#define IOCTL_VMCI_SOCKETS_RECV \
VMCIIOCTL_BUFFERED(SOCKETS_SOCKET)
/* END VMCI SOCKETS */
+/* BEGIN VMCI USER SPACE DAEMON */
+#define IOCTL_VMCI_VMCID_RPC \
+ VMCIIOCTL_BUFFERED(VMCID_RPC)
+/* END VMCI USER SPACE DAEMON */
+
#endif // _WIN32
uint32 _pad;
} VMCISetNotifyInfo;
+/* User space daemon command numbers. */
+typedef enum VMCIDRequestType {
+ VMCID_REQ_NEW_PAGE_STORE,
+ VMCID_REQ_FREE_PAGE_STORE,
+ VMCID_REQ_ATTACH_PAGE_STORE,
+ VMCID_REQ_DETACH_PAGE_STORE,
+} VMCIDRequestType;
+
+#define VMCI_VMCID_INVALID_REQ CONST64U(-1)
+
+/* Used to pass requests/responses to the user space daemon. */
+typedef struct VMCIDRpc {
+ uint64 reqId;
+ uint32 reqType;
+ uint32 reqResult;
+ /* Passing page file names */
+ VA64 producePageFile; /* User VA. */
+ VA64 consumePageFile; /* User VA. */
+ uint64 producePageFileSize; /* Size of the file name array. */
+ uint64 consumePageFileSize; /* Size of the file name array. */
+ /* Used for attach/detach */
+ VA64 produceVA;
+ VA64 consumeVA;
+ uint64 numProducePages;
+ uint64 numConsumePages;
+} VMCIDRpc;
#ifdef __APPLE__
/*
# include <sys/mutex.h>
# include <sys/poll.h>
# include <sys/semaphore.h>
+# include <sys/kmem.h>
#endif
#include "vm_basic_types.h"
typedef unsigned long VMCILockFlags;
typedef semaphore_t VMCIEvent;
typedef IOLock *VMCIMutex;
+ typedef void *VMCIPpnList; /* Actually a pointer to the C++ Object IOMemoryDescriptor */
#elif defined(_WIN32)
typedef KSPIN_LOCK VMCILock;
typedef KIRQL VMCILockFlags;
typedef kmutex_t VMCILock;
typedef unsigned long VMCILockFlags;
typedef ksema_t VMCIEvent;
+ typedef kmutex_t VMCIMutex;
+ typedef PPN *VMCIPpnList; /* List of PPNs in produce/consume queue. */
#endif // VMKERNEL
/* Callback needed for correctly waiting on events. */
void VMCI_SignalEvent(VMCIEvent *event);
void VMCI_WaitOnEvent(VMCIEvent *event, VMCIEventReleaseCB releaseCB,
void *clientData);
+#if (defined(__linux__) || defined(_WIN32)) && !defined(VMKERNEL)
+Bool VMCI_WaitOnEventInterruptible(VMCIEvent *event,
+ VMCIEventReleaseCB releaseCB,
+ void *clientData);
+#endif
-/* XXX TODO for VMKERNEL (host) and Solaris (guest). */
#if !defined(VMKERNEL) && (defined(__linux__) || defined(_WIN32) || \
- defined(__APPLE__))
+ defined(__APPLE__) || defined(SOLARIS))
int VMCI_CopyFromUser(void *dst, const void *src, size_t len);
#endif
-#if !defined(SOLARIS)
int VMCIMutex_Init(VMCIMutex *mutex);
void VMCIMutex_Destroy(VMCIMutex *mutex);
void VMCIMutex_Acquire(VMCIMutex *mutex);
void VMCIMutex_Release(VMCIMutex *mutex);
-#endif
-/* XXX TODO for Solaris (guest). */
-#if !defined(VMKERNEL) && (defined(__linux__) || defined(_WIN32))
+#if defined(SOLARIS)
+int VMCIKernelIf_Init(void);
+void VMCIKernelIf_Exit(void);
+#endif /* SOLARIS */
+
+#if !defined(VMKERNEL) && (defined(__linux__) || defined(_WIN32) || \
+ defined(SOLARIS) || defined(__APPLE__))
VA VMCI_AllocQueueKVA(uint64 size);
void VMCI_FreeQueueKVA(VA va, uint64 size);
typedef struct PPNSet {
int VMCI_PopulatePPNList(uint8 *callBuf, const PPNSet *ppnSet);
#endif
+#if (defined(_WIN32) || defined(__linux__)) && !defined(VMKERNEL) && \
+ !defined(VMX86_TOOLS)
+struct PageStoreAttachInfo;
+struct VMCIQueue;
+
+int VMCIHost_GetUserMemory(struct PageStoreAttachInfo *attach);
+void VMCIHost_ReleaseUserMemory(struct PageStoreAttachInfo *attach);
+int VMCIHost_FinishAttach(struct PageStoreAttachInfo *attach,
+ struct VMCIQueue *produceQ,
+ struct VMCIQueue *detachQ);
+void VMCIHost_DetachMappings(struct PageStoreAttachInfo *attach);
+#endif
+
#endif // _VMCI_KERNEL_IF_H_
*
*********************************************************/
-#ifndef _VMCI_QUEUE_PAIR_H_
-#define _VMCI_QUEUE_PAIR_H_
+#ifndef _PUBLIC_VMCI_QUEUE_PAIR_H_
+#define _PUBLIC_VMCI_QUEUE_PAIR_H_
/*
*
#define INCLUDE_ALLOW_VMX
#include "includeCheck.h"
+#if defined(__APPLE__) && defined(KERNEL)
+# include "vmci_kernel_if.h"
+#endif // __APPLE__
+
#include "vm_basic_defs.h"
#include "vm_basic_types.h"
#include "vm_atomic.h"
#include "vmci_defs.h"
#include "vm_assert.h"
-#if defined(__linux__) && defined(__KERNEL__)
-# include "vmci_kernel_if.h"
+#if defined(SOLARIS)
+#include <sys/uio.h>
+#endif
+
+#if (defined(__linux__) && defined(__KERNEL__)) || defined(SOLARIS)
+#include "vmci_kernel_if.h"
#endif
Atomic_uint64 consumerHead; /* Offset in peer queue. */
} VMCIQueueHeader;
-typedef struct VMCIQueue {
- VMCIQueueHeader queueHeader;
- uint8 _padding[PAGE_SIZE - sizeof(VMCIQueueHeader)];
-#if defined(__linux__) && defined(__KERNEL__)
- struct page *page[0]; /* List of pages containing queue data. */
+
+/*
+ * If one client of a QueuePair is a 32bit entity, we restrict the QueuePair
+ * size to be less than 4GB, and use 32bit atomic operations on the head and
+ * tail pointers. 64bit atomic read on a 32bit entity involves cmpxchg8b which
+ * is an atomic read-modify-write. This will cause traces to fire when a 32bit
+ * consumer tries to read the producer's tail pointer, for example, because the
+ * consumer has read-only access to the producer's tail pointer.
+ *
+ * We provide the following macros to invoke 32bit or 64bit atomic operations
+ * based on the architecture the code is being compiled on.
+ */
+
+/* Architecture independent maximum queue size. */
+#define QP_MAX_QUEUE_SIZE_ARCH_ANY CONST64U(0xffffffff)
+
+#ifdef __x86_64__
+# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffffffffffff)
+# define QPAtomic_ReadOffset(x) Atomic_Read64(x)
+# define QPAtomic_WriteOffset(x, y) Atomic_Write64(x, y)
#else
- uint8 buffer[0]; /* Buffer containing data. */
+ /*
+ * Wrappers below are being used to call Atomic_Read32 because of the
+ * 'type punned' compilation warning received when Atomic_Read32 is
+ * called with a Atomic_uint64 pointer typecasted to Atomic_uint32
+ * pointer from QPAtomic_ReadOffset. Ditto with QPAtomic_WriteOffset.
+ */
+
+ static INLINE uint32
+ TypeSafe_Atomic_Read32(void *var) // IN:
+ {
+ return Atomic_Read32((Atomic_uint32 *)(var));
+ }
+
+ static INLINE void
+ TypeSafe_Atomic_Write32(void *var, uint32 val) // IN:
+ {
+ Atomic_Write32((Atomic_uint32 *)(var), (uint32)(val));
+ }
+
+# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffff)
+# define QPAtomic_ReadOffset(x) TypeSafe_Atomic_Read32((void *)(x))
+# define QPAtomic_WriteOffset(x, y) \
+ TypeSafe_Atomic_Write32((void *)(x), (uint32)(y))
+#endif /* __x86_64__ */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * AddPointer --
+ *
+ * Helper to add a given offset to a head or tail pointer. Wraps the value
+ * of the pointer around the max size of the queue.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+AddPointer(Atomic_uint64 *var, // IN:
+ size_t add, // IN:
+ uint64 size) // IN:
+{
+ uint64 newVal = QPAtomic_ReadOffset(var);
+
+ if (newVal >= size - add) {
+ newVal -= size;
+ }
+ newVal += add;
+
+ QPAtomic_WriteOffset(var, newVal);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue
+ *
+ * This data type contains the information about a queue.
+ *
+ * There are two queues (hence, queue pairs) per transaction model
+ * between a pair of end points, A & B. One queue is used by end
+ * point A to transmit commands and responses to B. The other queue
+ * is used by B to transmit commands and responses.
+ *
+ * There are six distinct definitions of the VMCIQueue structure.
+ * Five are found below along with descriptions of under what build
+ * type the VMCIQueue structure type is compiled. The sixth is
+ * located in a separate file and is used in the VMKernel context.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(VMX86_TOOLS) || defined(VMX86_VMX)
+# if defined(__linux__) && defined(__KERNEL__)
+ /*
+ * Linux Kernel Guest.
+ *
+ * The VMCIQueue is two or more machine pages where the first
+ * contains the queue header and the second and subsequent pages
+ * contain an array of struct page pointers to the actual pages
+ * that contain the queue data.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader queueHeader;
+ uint8 _padding[PAGE_SIZE - sizeof(VMCIQueueHeader)];
+ struct page *page[0];
+ } VMCIQueue;
+# elif defined(SOLARIS)
+ /*
+ * Solaris Kernel Guest.
+ *
+ * kmem_cache_alloc with object size of one page is being used to get
+ * page aligned memory for queueHeader. VMCIQueue stores a pointer to
+ * the queueHeader and the array of virtual addresses of actual pages
+ * that contain the queue data.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader *queueHeaderPtr;
+ void *vaddr[0];
+ } VMCIQueue;
+
+# else
+ /*
+ * VMX App and Windows Kernel Guest
+ *
+ * The VMCIQueue is two or more machine pages where the first
+ * contains the queue header and the second and subsequent pages
+ * contain the actual queue buffer data.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader queueHeader;
+ uint8 _padding[PAGE_SIZE - sizeof(VMCIQueueHeader)];
+ uint8 buffer[0]; // VMX doesn't use this today
+ } VMCIQueue;
+# endif
+#else
+# if defined(__linux__) && defined(__KERNEL__)
+ /*
+ * Linux Kernel Host
+ *
+ * The VMCIQueueHeader has been created elsewhere and this
+ * structure (VMCIQueue) needs a pointer to that
+ * VMCIQueueHeader.
+ *
+ * Also, the queue contents are managed by an array of struct
+ * pages which are managed elsewhere. So, this structure simply
+ * contains the address of that array of struct pages.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader *queueHeaderPtr;
+ struct page **page;
+ } VMCIQueue;
+# else
+ /*
+ * Windows Host
+ *
+ * The VMCIQueueHeader has been created elsewhere and this
+ * structure (VMCIQueue) needs a pointer to that
+ * VMCIQueueHeader.
+ *
+ * Also, the queue contents are managed by an array of bytes
+ * which are managed elsewhere. So, this structure simply
+ * contains the address of that array of bytes.
+ */
+ typedef struct VMCIQueue {
+ VMCIQueueHeader *queueHeaderPtr;
+ uint8 *buffer;
+ } VMCIQueue;
+# endif
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_GetHeader() --
+ *
+ * Helper routine to get the address of the VMCIQueueHeader.
+ * Note that this routine is useful because there are two ways
+ * queues are declared (at least as far as the queue header is
+ * concerned, anyway). In one way, the queue contains the header
+ * in the other the queue contains a pointer to the header. This
+ * helper hides that ambiguity from users of queues who need
+ * access to the header.
+ *
+ * Results:
+ * The address of the queue header.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE VMCIQueueHeader *
+VMCIQueue_GetHeader(const VMCIQueue *q)
+{
+#if defined(VMX86_TOOLS) || defined(VMX86_VMX)
+ /*
+ * All guest variants except Solaris have a complete queue header
+ */
+#if defined (SOLARIS)
+ return (VMCIQueueHeader *)q->queueHeaderPtr;
+#else
+ return (VMCIQueueHeader *)&q->queueHeader;
+#endif /* SOLARIS */
+#else
+ /*
+ * All host variants have a pointer to the queue header
+ */
+ return (VMCIQueueHeader *)q->queueHeaderPtr;
#endif
-} VMCIQueue;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_ProducerTail() --
+ *
+ * Helper routine to get the Producer Tail from the supplied queue.
+ *
+ * Results:
+ * The contents of the queue's producer tail.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+static INLINE uint64
+VMCIQueue_ProducerTail(const VMCIQueue *queue)
+{
+ return QPAtomic_ReadOffset(&VMCIQueue_GetHeader(queue)->producerTail);
+}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_ConsumerHead() --
+ *
+ * Helper routine to get the Consumer Head from the supplied queue.
+ *
+ * Results:
+ * The contents of the queue's consumer tail.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE uint64
+VMCIQueue_ConsumerHead(const VMCIQueue *queue)
+{
+ return QPAtomic_ReadOffset(&VMCIQueue_GetHeader(queue)->consumerHead);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_AddProducerTail() --
+ *
+ * Helper routine to increment the Producer Tail. Fundamentally,
+ * AddPointer() is used to manipulate the tail itself.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+VMCIQueue_AddProducerTail(VMCIQueue *queue,
+ size_t add,
+ uint64 queueSize)
+{
+ AddPointer(&VMCIQueue_GetHeader(queue)->producerTail, add, queueSize);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_AddConsumerHead() --
+ *
+ * Helper routine to increment the Consumer Head. Fundamentally,
+ * AddPointer() is used to manipulate the head itself.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+VMCIQueue_AddConsumerHead(VMCIQueue *queue,
+ size_t add,
+ uint64 queueSize)
+{
+ AddPointer(&VMCIQueue_GetHeader(queue)->consumerHead, add, queueSize);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIMemcpy{To,From}QueueFunc() prototypes. Functions of these
+ * types are passed around to enqueue and dequeue routines. Note that
+ * often the functions passed are simply wrappers around memcpy
+ * itself.
+ *
+ *-----------------------------------------------------------------------------
+ */
typedef int VMCIMemcpyToQueueFunc(VMCIQueue *queue, uint64 queueOffset,
const void *src, size_t srcOffset,
size_t size);
const VMCIQueue *queue, uint64 queueOffset,
size_t size);
-#if defined(__linux__) && defined(__KERNEL__)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIMemcpy{To,From}Queue[v]() prototypes (and, in some cases, an
+ * inline version).
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined (SOLARIS) || (defined(__linux__) && defined(__KERNEL__))
int VMCIMemcpyToQueue(VMCIQueue *queue, uint64 queueOffset, const void *src,
size_t srcOffset, size_t size);
int VMCIMemcpyFromQueue(void *dest, size_t destOffset, const VMCIQueue *queue,
#endif /* __linux__ && __KERNEL__ */
-/*
- * If one client of a QueuePair is a 32bit entity, we restrict the QueuePair
- * size to be less than 4GB, and use 32bit atomic operations on the head and
- * tail pointers. 64bit atomic read on a 32bit entity involves cmpxchg8b which
- * is an atomic read-modify-write. This will cause traces to fire when a 32bit
- * consumer tries to read the producer's tail pointer, for example, because the
- * consumer has read-only access to the producer's tail pointer.
- *
- * We provide the following macros to invoke 32bit or 64bit atomic operations
- * based on the architecture the code is being compiled on.
- */
-
-/* Architecture independent maximum queue size. */
-#define QP_MAX_QUEUE_SIZE_ARCH_ANY CONST64U(0xffffffff)
-
-#ifdef __x86_64__
-# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffffffffffff)
-# define QPAtomic_ReadOffset(x) Atomic_Read64(x)
-# define QPAtomic_WriteOffset(x, y) Atomic_Write64(x, y)
-#else
-# define QP_MAX_QUEUE_SIZE_ARCH CONST64U(0xffffffff)
-# define QPAtomic_ReadOffset(x) Atomic_Read32((Atomic_uint32 *)(x))
-# define QPAtomic_WriteOffset(x, y) \
- Atomic_Write32((Atomic_uint32 *)(x), (uint32)(y))
-#endif
-
-
/*
*-----------------------------------------------------------------------------
*
* VMCIQueue_CheckAlignment --
*
- * Checks if the given queue is aligned to page boundary.
+ * Checks if the given queue is aligned to page boundary. Returns TRUE if
+ * the alignment is good.
*
* Results:
* TRUE or FALSE.
static INLINE Bool
VMCIQueue_CheckAlignment(const VMCIQueue *queue) // IN:
{
- return ((uintptr_t)queue & (PAGE_SIZE - 1)) == 0;
+ /*
+ * Checks if header of the queue is page aligned.
+ */
+ return ((uintptr_t)VMCIQueue_GetHeader(queue) & (PAGE_SIZE - 1)) == 0;
}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQueue_GetPointers --
+ *
+ * Helper routine for getting the head and the tail pointer for a queue.
+ * Both the VMCIQueues are needed to get both the pointers for one queue.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
static INLINE void
VMCIQueue_GetPointers(const VMCIQueue *produceQ,
const VMCIQueue *consumeQ,
uint64 *producerTail,
uint64 *consumerHead)
{
- *producerTail = QPAtomic_ReadOffset(&produceQ->queueHeader.producerTail);
- *consumerHead = QPAtomic_ReadOffset(&consumeQ->queueHeader.consumerHead);
+ *producerTail = VMCIQueue_ProducerTail(produceQ);
+ *consumerHead = VMCIQueue_ConsumerHead(consumeQ);
}
static INLINE void
VMCIQueue_ResetPointers(VMCIQueue *queue) // IN:
{
- QPAtomic_WriteOffset(&queue->queueHeader.producerTail, CONST64U(0));
- QPAtomic_WriteOffset(&queue->queueHeader.consumerHead, CONST64U(0));
+ QPAtomic_WriteOffset(&VMCIQueue_GetHeader(queue)->producerTail, CONST64U(0));
+ QPAtomic_WriteOffset(&VMCIQueue_GetHeader(queue)->consumerHead, CONST64U(0));
}
VMCIQueue *queue) // IN:
{
ASSERT_NOT_IMPLEMENTED(VMCIQueue_CheckAlignment(queue));
- queue->queueHeader.handle = handle;
+ VMCIQueue_GetHeader(queue)->handle = handle;
VMCIQueue_ResetPointers(queue);
}
const uint64 produceQSize, // IN:
uint64 *freeSpace) // OUT:
{
- const uint64 tail =
- QPAtomic_ReadOffset(&produceQueue->queueHeader.producerTail);
- const uint64 head =
- QPAtomic_ReadOffset(&consumeQueue->queueHeader.consumerHead);
+ const uint64 tail = VMCIQueue_ProducerTail(produceQueue);
+ const uint64 head = VMCIQueue_ConsumerHead(consumeQueue);
ASSERT(freeSpace);
}
-/*
- *-----------------------------------------------------------------------------
- *
- * AddPointer --
- *
- * Helper to add a given offset to a head or tail pointer. Wraps the value
- * of the pointer around the max size of the queue.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static INLINE void
-AddPointer(Atomic_uint64 *var, // IN:
- size_t add, // IN:
- uint64 max) // IN:
-{
- uint64 newVal = QPAtomic_ReadOffset(var);
-
- if (newVal >= max - add) {
- newVal -= max;
- }
- newVal += add;
-
- QPAtomic_WriteOffset(var, newVal);
-}
-
-
/*
*-----------------------------------------------------------------------------
*
{
const int64 freeSpace = VMCIQueue_FreeSpace(produceQueue, consumeQueue,
produceQSize);
- const uint64 tail =
- QPAtomic_ReadOffset(&produceQueue->queueHeader.producerTail);
+ const uint64 tail = VMCIQueue_ProducerTail(produceQueue);
size_t written;
if (!freeSpace) {
memcpyToQueue(produceQueue, tail, buf, 0, tmp);
memcpyToQueue(produceQueue, 0, buf, tmp, written - tmp);
}
- AddPointer(&produceQueue->queueHeader.producerTail, written, produceQSize);
+ AddPointer(&VMCIQueue_GetHeader(produceQueue)->producerTail, written, produceQSize);
return written;
}
}
-#if defined(__linux__) && defined(__KERNEL__)
+#if defined (SOLARIS) || (defined(__linux__) && defined(__KERNEL__))
/*
*-----------------------------------------------------------------------------
*
{
const int64 bufReady = VMCIQueue_BufReady(consumeQueue, produceQueue,
consumeQSize);
- const uint64 head =
- QPAtomic_ReadOffset(&produceQueue->queueHeader.consumerHead);
+ const uint64 head = VMCIQueue_ConsumerHead(produceQueue);
size_t written;
if (!bufReady) {
memcpyFromQueue(buf, 0, consumeQueue, head, tmp);
memcpyFromQueue(buf, tmp, consumeQueue, 0, written - tmp);
}
- AddPointer(&produceQueue->queueHeader.consumerHead, written, consumeQSize);
+ AddPointer(&VMCIQueue_GetHeader(produceQueue)->consumerHead, written, consumeQSize);
return written;
}
}
-#if defined(__linux__) && defined(__KERNEL__)
+#if defined(SOLARIS) || (defined(__linux__) && defined(__KERNEL__))
/*
*-----------------------------------------------------------------------------
*
}
#endif
-#endif /* !_VMCI_QUEUE_PAIR_H_ */
+#endif /* !_PUBLIC_VMCI_QUEUE_PAIR_H_ */
/*
* Prototypes
*/
-
int VSockVmci_GetAFValue(void);
/* Internal functions. */
+static int VSockVmciGetAFValue(void);
static int VSockVmciRecvDgramCB(void *data, VMCIDatagram *dg);
#ifdef VMX86_TOOLS
static int VSockVmciRecvStreamCB(void *data, VMCIDatagram *dg);
struct socket *sock, gfp_t priority,
unsigned short type);
#endif
+static inline void VSockVmciTestUnregister(void);
static int VSockVmciRegisterAddressFamily(void);
static void VSockVmciUnregisterAddressFamily(void);
static DECLARE_MUTEX(registrationMutex);
static int devOpenCount = 0;
static int vsockVmciSocketCount = 0;
+static int vsockVmciKernClientCount = 0;
#ifdef VMX86_TOOLS
static VMCIHandle vmciStreamHandle = { VMCI_INVALID_ID, VMCI_INVALID_ID };
static Bool vmciDevicePresent = FALSE;
/*
*----------------------------------------------------------------------------
*
- * VSockVmci_GetAFValue --
+ * VMCISock_GetAFValue --
*
- * Returns the address family value being used.
+ * Kernel interface that allows external kernel modules to get the current
+ * VMCI Sockets address family.
+ * This version of the function is exported to kernel clients and should not
+ * change.
*
* Results:
* The address family on success, a negative error on failure.
*/
int
-VSockVmci_GetAFValue(void)
+VMCISock_GetAFValue(void)
{
int afvalue;
down(®istrationMutex);
+ /*
+ * Kernel clients are required to explicitly register themselves before they
+ * can use VMCI Sockets.
+ */
+ if (vsockVmciKernClientCount <= 0) {
+ afvalue = -1;
+ goto exit;
+ }
+
+ afvalue = VSockVmciGetAFValue();
+
+exit:
+ up(®istrationMutex);
+ return afvalue;
+
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * VMCISock_KernelRegister --
+ *
+ * Allows a kernel client to register with VMCI Sockets. Must be called
+ * before VMCISock_GetAFValue within a kernel module. Note that we don't
+ * actually register the address family until the first time the module
+ * needs to use it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+VMCISock_KernelRegister(void)
+{
+ down(®istrationMutex);
+ vsockVmciKernClientCount++;
+ up(®istrationMutex);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * VMCISock_KernelDeregister --
+ *
+ * Allows a kernel client to unregister with VMCI Sockets. Every call
+ * to VMCISock_KernRegister must be matched with a call to
+ * VMCISock_KernUnregister.
+ *
+ * Results:
+ None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+VMCISock_KernelDeregister(void)
+{
+ down(®istrationMutex);
+ vsockVmciKernClientCount--;
+ VSockVmciTestUnregister();
+ up(®istrationMutex);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * VSockVmciGetAFValue --
+ *
+ * Returns the address family value being used.
+ * Note: The registration mutex must be held when calling this function.
+ *
+ * Results:
+ * The address family on success, a negative error on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+VSockVmciGetAFValue(void)
+{
+ int afvalue;
+
afvalue = vsockVmciFamilyOps.family;
if (!VSOCK_AF_IS_REGISTERED(afvalue)) {
afvalue = VSockVmciRegisterAddressFamily();
}
+ return afvalue;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * VSockVmci_GetAFValue --
+ *
+ * Returns the address family value being used.
+ *
+ * Results:
+ * The address family on success, a negative error on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+VSockVmci_GetAFValue(void)
+{
+ int afvalue;
+
+ down(®istrationMutex);
+ afvalue = VSockVmciGetAFValue();
up(®istrationMutex);
+
return afvalue;
}
static inline void
VSockVmciTestUnregister(void)
{
- if (devOpenCount <= 0 && vsockVmciSocketCount <= 0) {
+ if (devOpenCount <= 0 && vsockVmciSocketCount <= 0 &&
+ vsockVmciKernClientCount <= 0) {
if (VSOCK_AF_IS_REGISTERED(vsockVmciFamilyOps.family)) {
VSockVmciUnregisterAddressFamily();
}
MODULE_DESCRIPTION("VMware Virtual Socket Family");
MODULE_VERSION(VSOCK_DRIVER_VERSION_STRING);
MODULE_LICENSE("GPL v2");
+/*
+ * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement
+ * with them and mark their kernel modules as externally supported via a
+ * change to the module header. If this isn't done, the module will not load
+ * by default (i.e., neither mkinitrd nor modprobe will accept it).
+ */
+MODULE_INFO(supported, "external");
/*
* Address structure for VSockets VMCI sockets. The address family should be
- * set to AF_VMCI.
+ * set to AF_VMCI. The structure members should all align on their natural
+ * boundaries without resorting to compiler packing directives.
*/
+
struct sockaddr_vm {
+#if defined(__APPLE__)
+ unsigned char svm_len; // Mac OS has the length first.
+#endif // __APPLE__
sa_family_t svm_family; // AF_VMCI.
unsigned short svm_reserved1; // Reserved.
unsigned int svm_port; // Port.
unsigned int svm_cid; // Context id.
unsigned char svm_zero[sizeof(struct sockaddr) - // Same size as sockaddr.
- sizeof(sa_family_t) -
- sizeof(unsigned short) -
- sizeof(unsigned int) -
- sizeof(unsigned int)];
+#if defined(__APPLE__)
+ sizeof(unsigned char) -
+#endif // __APPLE__
+ sizeof(sa_family_t) -
+ sizeof(unsigned short) -
+ sizeof(unsigned int) -
+ sizeof(unsigned int)];
};
# endif // WINNT_DDK
#else // _WIN32
#if defined(linux) && !defined(VMKERNEL)
-# ifndef __KERNEL__
+# ifdef __KERNEL__
+ void VMCISock_KernelRegister(void);
+ void VMCISock_KernelDeregister(void);
+ int VMCISock_GetAFValue(void);
+# else // __KERNEL__
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <stdio.h>
# define VMCI_SOCKETS_DEFAULT_DEVICE "/dev/vsock"
+# define VMCI_SOCKETS_CLASSIC_ESX_DEVICE "/vmfs/devices/char/vsock/vsock"
# define IOCTL_VMCI_SOCKETS_GET_AF_VALUE 1976
# define IOCTL_VMCI_SOCKETS_GET_LOCAL_CID 1977
fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDWR);
if (fd < 0) {
- return -1;
+ fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDWR);
+ if (fd < 0) {
+ return -1;
+ }
}
if (ioctl(fd, IOCTL_VMCI_SOCKETS_GET_AF_VALUE, &family) < 0) {
fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDWR);
if (fd < 0) {
- return VMADDR_CID_ANY;
+ fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDWR);
+ if (fd < 0) {
+ return VMADDR_CID_ANY;
+ }
}
if (ioctl(fd, IOCTL_VMCI_SOCKETS_GET_LOCAL_CID, &contextId) < 0) {
ASSERT(addr);
memset(addr, 0, sizeof *addr);
VSockAddr_InitNoFamily(addr, cid, port);
- addr->svm_family = VMCISock_GetAFValue();
+ addr->svm_family = VMCISockGetAFValueInt();
VSOCK_ADDR_ASSERT(addr);
}
goto exit;
}
- if (VMCISock_GetAFValue() != addr->svm_family) {
+ if (VMCISockGetAFValueInt() != addr->svm_family) {
err = EAFNOSUPPORT;
goto exit;
}
#ifndef _VSOCK_COMMON_H_
#define _VSOCK_COMMON_H_
+/*
+ * VMCISockGetAFValueInt is defined separately from VMCISock_GetAFValue because
+ * it is used in several different contexts. In particular it is called from
+ * vsockAddr.c which gets compiled into both our kernel modules as well as
+ * the user level vsock library. In the linux kernel we need different behavior
+ * than external kernel modules using VMCI Sockets api inside the kernel.
+ */
#if defined(_WIN32)
# define VMCI_SOCKETS_AF_VALUE 28
# define _INC_WINDOWS
# include "vmci_queue_pair.h"
/* In the kernel we can't call into the provider. */
-# define VMCISock_GetAFValue() VMCI_SOCKETS_AF_VALUE
+# define VMCISockGetAFValueInt() VMCI_SOCKETS_AF_VALUE
# else // WINNT_DDK
+ /* In userland, just use the normal exported userlevel api. */
+# define VMCISockGetAFValueInt() VMCISock_GetAFValue()
# include <windows.h>
# endif // WINNT_DDK
# define Uint64ToPtr(_ui) ((void *)(uint64)(_ui))
# include "uwvmkAPI.h"
# define VMCI_SOCKETS_AF_VALUE AF_VMCI /* Defined in uwvmkAPI.h. */
/* The address family is fixed in the vmkernel. */
-# define VMCISock_GetAFValue() VMCI_SOCKETS_AF_VALUE
+# define VMCISockGetAFValueInt() VMCI_SOCKETS_AF_VALUE
# include "vmciHostKernelAPI.h"
# define Uint64ToPtr(_ui) ((void *)(uint64)(_ui))
# define PtrToUint64(_p) ((uint64)(_p))
# if defined(VMX86_TOOLS)
# include "vmci_queue_pair.h"
# endif
- /*
- * In the kernel we call back into af_vsock.c to get the address family
- * being used. Otherwise an ioctl(2) is performed (see vmci_sockets.h).
- */
+ /*
+ * In the kernel we call back into af_vsock.c to get the address family
+ * being used. Otherwise an ioctl(2) is performed (see vmci_sockets.h).
+ */
extern int VSockVmci_GetAFValue(void);
-# define VMCISock_GetAFValue() VSockVmci_GetAFValue()
+# define VMCISockGetAFValueInt() VSockVmci_GetAFValue()
+# else // __KERNEL__
+ /* In userland, just use the normal exported userlevel api. */
+# define VMCISockGetAFValueInt() VMCISock_GetAFValue()
# endif
#endif // linux
#endif // VMKERNEL
/*
- * Socket states and flags. Note that MSG_WAITALL is only supported on 2K3,
+ * Socket states and flags. Note that MSG_WAITALL is only defined on 2K3,
* XP-SP2 and above. Since we currently build for 2K to maintain backwards
- * compatibility, it will always be 0.
+ * compatibility, we pull the value from the newer header.
*/
#if defined(_WIN32)
# define MSG_DONTWAIT 0
# define MSG_NOSIGNAL 0
# if (_WIN32_WINNT < 0x0502)
-# define MSG_WAITALL 0
+# define MSG_WAITALL 0x8
# endif
#endif
--- /dev/null
+
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+
+1. Definitions.
+
+ 1.1. "Contributor" means each individual or entity that creates
+ or contributes to the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Software, prior Modifications used by a Contributor (if any),
+ and the Modifications made by that particular Contributor.
+
+ 1.3. "Covered Software" means (a) the Original Software, or (b)
+ Modifications, or (c) the combination of files containing
+ Original Software with files containing Modifications, in
+ each case including portions thereof.
+
+ 1.4. "Executable" means the Covered Software in any form other
+ than Source Code.
+
+ 1.5. "Initial Developer" means the individual or entity that first
+ makes Original Software available under this License.
+
+ 1.6. "Larger Work" means a work which combines Covered Software or
+ portions thereof with code not governed by the terms of this
+ License.
+
+ 1.7. "License" means this document.
+
+ 1.8. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant or
+ subsequently acquired, any and all of the rights conveyed
+ herein.
+
+ 1.9. "Modifications" means the Source Code and Executable form of
+ any of the following:
+
+ A. Any file that results from an addition to, deletion from or
+ modification of the contents of a file containing Original
+ Software or previous Modifications;
+
+ B. Any new file that contains any part of the Original
+ Software or previous Modifications; or
+
+ C. Any new file that is contributed or otherwise made
+ available under the terms of this License.
+
+ 1.10. "Original Software" means the Source Code and Executable
+ form of computer software code that is originally released
+ under this License.
+
+ 1.11. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by
+ grantor.
+
+ 1.12. "Source Code" means (a) the common form of computer software
+ code in which modifications are made and (b) associated
+ documentation included in or with such code.
+
+ 1.13. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms
+ of, this License. For legal entities, "You" includes any
+ entity which controls, is controlled by, or is under common
+ control with You. For purposes of this definition,
+ "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty
+ percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants.
+
+ 2.1. The Initial Developer Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and
+ subject to third party intellectual property claims, the Initial
+ Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer, to use,
+ reproduce, modify, display, perform, sublicense and
+ distribute the Original Software (or portions thereof),
+ with or without Modifications, and/or as part of a Larger
+ Work; and
+
+ (b) under Patent Claims infringed by the making, using or
+ selling of Original Software, to make, have made, use,
+ practice, sell, and offer for sale, and/or otherwise
+ dispose of the Original Software (or portions thereof).
+
+ (c) The licenses granted in Sections 2.1(a) and (b) are
+ effective on the date Initial Developer first distributes
+ or otherwise makes the Original Software available to a
+ third party under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is
+ granted: (1) for code that You delete from the Original
+ Software, or (2) for infringements caused by: (i) the
+ modification of the Original Software, or (ii) the
+ combination of the Original Software with other software
+ or devices.
+
+ 2.2. Contributor Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and
+ subject to third party intellectual property claims, each
+ Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor to use, reproduce,
+ modify, display, perform, sublicense and distribute the
+ Modifications created by such Contributor (or portions
+ thereof), either on an unmodified basis, with other
+ Modifications, as Covered Software and/or as part of a
+ Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or
+ selling of Modifications made by that Contributor either
+ alone and/or in combination with its Contributor Version
+ (or portions of such combination), to make, use, sell,
+ offer for sale, have made, and/or otherwise dispose of:
+ (1) Modifications made by that Contributor (or portions
+ thereof); and (2) the combination of Modifications made by
+ that Contributor with its Contributor Version (or portions
+ of such combination).
+
+ (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
+ effective on the date Contributor first distributes or
+ otherwise makes the Modifications available to a third
+ party.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is
+ granted: (1) for any code that Contributor has deleted
+ from the Contributor Version; (2) for infringements caused
+ by: (i) third party modifications of Contributor Version,
+ or (ii) the combination of Modifications made by that
+ Contributor with other software (except as part of the
+ Contributor Version) or other devices; or (3) under Patent
+ Claims infringed by Covered Software in the absence of
+ Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Availability of Source Code.
+
+ Any Covered Software that You distribute or otherwise make
+ available in Executable form must also be made available in Source
+ Code form and that Source Code form must be distributed only under
+ the terms of this License. You must include a copy of this
+ License with every copy of the Source Code form of the Covered
+ Software You distribute or otherwise make available. You must
+ inform recipients of any such Covered Software in Executable form
+ as to how they can obtain such Covered Software in Source Code
+ form in a reasonable manner on or through a medium customarily
+ used for software exchange.
+
+ 3.2. Modifications.
+
+ The Modifications that You create or to which You contribute are
+ governed by the terms of this License. You represent that You
+ believe Your Modifications are Your original creation(s) and/or
+ You have sufficient rights to grant the rights conveyed by this
+ License.
+
+ 3.3. Required Notices.
+
+ You must include a notice in each of Your Modifications that
+ identifies You as the Contributor of the Modification. You may
+ not remove or alter any copyright, patent or trademark notices
+ contained within the Covered Software, or any notices of licensing
+ or any descriptive text giving attribution to any Contributor or
+ the Initial Developer.
+
+ 3.4. Application of Additional Terms.
+
+ You may not offer or impose any terms on any Covered Software in
+ Source Code form that alters or restricts the applicable version
+ of this License or the recipients' rights hereunder. You may
+ choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of
+ Covered Software. However, you may do so only on Your own behalf,
+ and not on behalf of the Initial Developer or any Contributor.
+ You must make it absolutely clear that any such warranty, support,
+ indemnity or liability obligation is offered by You alone, and You
+ hereby agree to indemnify the Initial Developer and every
+ Contributor for any liability incurred by the Initial Developer or
+ such Contributor as a result of warranty, support, indemnity or
+ liability terms You offer.
+
+ 3.5. Distribution of Executable Versions.
+
+ You may distribute the Executable form of the Covered Software
+ under the terms of this License or under the terms of a license of
+ Your choice, which may contain terms different from this License,
+ provided that You are in compliance with the terms of this License
+ and that the license for the Executable form does not attempt to
+ limit or alter the recipient's rights in the Source Code form from
+ the rights set forth in this License. If You distribute the
+ Covered Software in Executable form under a different license, You
+ must make it absolutely clear that any terms which differ from
+ this License are offered by You alone, not by the Initial
+ Developer or Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred
+ by the Initial Developer or such Contributor as a result of any
+ such terms You offer.
+
+ 3.6. Larger Works.
+
+ You may create a Larger Work by combining Covered Software with
+ other code not governed by the terms of this License and
+ distribute the Larger Work as a single product. In such a case,
+ You must make sure the requirements of this License are fulfilled
+ for the Covered Software.
+
+4. Versions of the License.
+
+ 4.1. New Versions.
+
+ Sun Microsystems, Inc. is the initial license steward and may
+ publish revised and/or new versions of this License from time to
+ time. Each version will be given a distinguishing version number.
+ Except as provided in Section 4.3, no one other than the license
+ steward has the right to modify this License.
+
+ 4.2. Effect of New Versions.
+
+ You may always continue to use, distribute or otherwise make the
+ Covered Software available under the terms of the version of the
+ License under which You originally received the Covered Software.
+ If the Initial Developer includes a notice in the Original
+ Software prohibiting it from being distributed or otherwise made
+ available under any subsequent version of the License, You must
+ distribute and make the Covered Software available under the terms
+ of the version of the License under which You originally received
+ the Covered Software. Otherwise, You may also choose to use,
+ distribute or otherwise make the Covered Software available under
+ the terms of any subsequent version of the License published by
+ the license steward.
+
+ 4.3. Modified Versions.
+
+ When You are an Initial Developer and You want to create a new
+ license for Your Original Software, You may create and use a
+ modified version of this License if You: (a) rename the license
+ and remove any references to the name of the license steward
+ (except to note that the license differs from this License); and
+ (b) otherwise make it clear that the license contains terms which
+ differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+ COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
+ BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+ INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
+ SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
+ PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
+ PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY
+ COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
+ INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
+ NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
+ WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+ ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
+ DISCLAIMER.
+
+6. TERMINATION.
+
+ 6.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to
+ cure such breach within 30 days of becoming aware of the breach.
+ Provisions which, by their nature, must remain in effect beyond
+ the termination of this License shall survive.
+
+ 6.2. If You assert a patent infringement claim (excluding
+ declaratory judgment actions) against Initial Developer or a
+ Contributor (the Initial Developer or Contributor against whom You
+ assert such claim is referred to as "Participant") alleging that
+ the Participant Software (meaning the Contributor Version where
+ the Participant is a Contributor or the Original Software where
+ the Participant is the Initial Developer) directly or indirectly
+ infringes any patent, then any and all rights granted directly or
+ indirectly to You by such Participant, the Initial Developer (if
+ the Initial Developer is not the Participant) and all Contributors
+ under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
+ notice from Participant terminate prospectively and automatically
+ at the expiration of such 60 day notice period, unless if within
+ such 60 day period You withdraw Your claim with respect to the
+ Participant Software against such Participant either unilaterally
+ or pursuant to a written agreement with Participant.
+
+ 6.3. In the event of termination under Sections 6.1 or 6.2 above,
+ all end user licenses that have been validly granted by You or any
+ distributor hereunder prior to termination (excluding licenses
+ granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
+ INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
+ COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
+ LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
+ LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
+ STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+ COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
+ INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
+ APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO
+ NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
+ APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+ The Covered Software is a "commercial item," as that term is
+ defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
+ computer software" (as that term is defined at 48
+ C.F.R. 252.227-7014(a)(1)) and "commercial computer software
+ documentation" as such terms are used in 48 C.F.R. 12.212
+ (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48
+ C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
+ U.S. Government End Users acquire Covered Software with only those
+ rights set forth herein. This U.S. Government Rights clause is in
+ lieu of, and supersedes, any other FAR, DFAR, or other clause or
+ provision that addresses Government rights in computer software
+ under this License.
+
+9. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed
+ by the law of the jurisdiction specified in a notice contained
+ within the Original Software (except to the extent applicable law,
+ if any, provides otherwise), excluding such jurisdiction's
+ conflict-of-law provisions. Any litigation relating to this
+ License shall be subject to the jurisdiction of the courts located
+ in the jurisdiction and venue specified in a notice contained
+ within the Original Software, with the losing party responsible
+ for costs, including, without limitation, court costs and
+ reasonable attorneys' fees and expenses. The application of the
+ United Nations Convention on Contracts for the International Sale
+ of Goods is expressly excluded. Any law or regulation which
+ provides that the language of a contract shall be construed
+ against the drafter shall not apply to this License. You agree
+ that You alone are responsible for compliance with the United
+ States export administration regulations (and the export control
+ laws and regulation of any other countries) when You use,
+ distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or
+ indirectly, out of its utilization of rights under this License
+ and You agree to work with Initial Developer and Contributors to
+ distribute such responsibility on an equitable basis. Nothing
+ herein is intended or shall be deemed to constitute any admission
+ of liability.
+
--- /dev/null
+#!/usr/bin/make -f
+##########################################################
+# Copyright (C) 2008 VMware, Inc. All rights reserved.
+#
+# The contents of this file are subject to the terms of the Common
+# Development and Distribution License (the "License") version 1.0
+# and no later version. You may not use this file except in
+# compliance with the License.
+#
+# You can obtain a copy of the License at
+# http://www.opensource.org/licenses/cddl1.php
+#
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+##########################################################
+
+##
+## General build locations and variables
+##
+
+MODULE := vmhgfs
+CFLAGS :=
+LDFLAGS :=
+
+# Solaris version defines for in-code #ifdefs (default to Solaris 9)
+ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.11)
+CFLAGS += -DSOL11
+else
+ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.10)
+CFLAGS += -DSOL10
+else
+CFLAGS += -DSOL9
+# we don't support Solaris 9 (we could but we use memcpy/memset
+# and we'd need stubs to get the module to load into the kernel).
+$(error "This makefile is only used for Solaris 10 and 11")
+endif
+endif
+
+CFLAGS += -ffreestanding
+CFLAGS += -nodefaultlibs
+
+CFLAGS += -O2
+CFLAGS += -Wall -Werror
+CFLAGS += $(GLOBAL_DEFS)
+CFLAGS += $(CC_WARNINGS)
+CFLAGS += -DVMX86_TOOLS
+CFLAGS += -D_KERNEL
+
+ifdef VMX86_DEVEL
+CFLAGS += -DVMX86_DEVEL
+endif
+ifdef VMX86_DEBUG # For assertions
+CFLAGS += -DVMX86_DEBUG
+endif
+ifdef VMX86_LOG # For debug logging
+CFLAGS += -DVM_DEBUGGING_ON
+endif
+
+##
+## Objects needed to build the HGFS kernel module
+##
+SOLHGFS_OBJS := debug.o
+SOLHGFS_OBJS += device.o
+SOLHGFS_OBJS += filesystem.o
+SOLHGFS_OBJS += hgfsState.o
+SOLHGFS_OBJS += module.o
+SOLHGFS_OBJS += request.o
+SOLHGFS_OBJS += vnode.o
+SOLHGFS_OBJS += cpName.o
+SOLHGFS_OBJS += cpNameLinux.o
+SOLHGFS_OBJS += dbllnklst.o
+SOLHGFS_OBJS += hgfsEscape.o
+SOLHGFS_OBJS += hgfsUtil.o
+SOLHGFS_OBJS += sha1.o
+
+SOLHGFS_32_OBJS := $(addprefix i386/, $(SOLHGFS_OBJS))
+SOLHGFS_64_OBJS := $(addprefix amd64/, $(SOLHGFS_OBJS))
+
+MODULE_32 := i386/$(MODULE)
+MODULE_64 := amd64/$(MODULE)
+
+CFLAGS_32 := $(CFLAGS)
+CFLAGS_32 += -m32
+LDFLAGS_32 := $(LDFLAGS)
+
+CFLAGS_64 := $(CFLAGS)
+CFLAGS_64 += -m64
+CFLAGS_64 += -mcmodel=kernel
+CFLAGS_64 += -mno-red-zone
+LDFLAGS_64 := $(LDFLAGS)
+ifdef HAVE_GNU_LD
+LDFLAGS_64 += -m elf_x86_64
+else
+LDFLAGS_64 += -64
+endif
+
+##
+## Building targets
+##
+.PHONY: all prepare_dirs clean install
+
+all: prepare_dirs $(MODULE_32) $(MODULE_64)
+
+prepare_dirs:
+ @echo "Creating build directories"
+ mkdir -p i386
+ mkdir -p amd64
+
+# Build just the module for 32 bits kernel
+$(MODULE_32): $(SOLHGFS_32_OBJS)
+ @echo "Linking $(MODULE_32)"
+ $(LD) $(LDFLAGS_32) -r -o $(MODULE_32) $(SOLHGFS_32_OBJS)
+
+$(SOLHGFS_32_OBJS): i386/%.o: %.c
+ @echo "Compiling $(<F)"
+ $(CC) $(CFLAGS_32) -c $< -o $@
+
+# Now the same for 64 bits kernel
+$(MODULE_64): $(SOLHGFS_64_OBJS)
+ @echo "Linking $(MODULE_64)"
+ $(LD) $(LDFLAGS_64) -r -o $(MODULE_64) $(SOLHGFS_64_OBJS)
+
+$(SOLHGFS_64_OBJS): amd64/%.o: %.c
+ @echo "Compiling $(<F)"
+ $(CC) $(CFLAGS_64) -c $< -o $@
+
+clean:
+ @echo "Cleaning directories"
+ @rm -f $(MODULE_32) $(SOLHGFS_32_OBJS) i386
+ @rm -r $(MODULE_64) $(SOLHGFS_32_OBJS) amd64
+
+install:
+ @echo "Installing modules"
+ cp $(MODULE_32) /kernel/drv/
+ chmod 755 /kernel/drv/$(MODULE)
+ cp $(MODULE_64) /kernel/drv/amd64
+ chmod 755 /kernel/drv/amd64/$(MODULE)
+ cp docs/hgfs.conf /kernel/drv/vmhgfs.conf
+ chmod 644 /kernel/drv/vmhgfs.conf
+ grep $(MODULE) /etc/name_to_major > /dev/null 2>&1; \
+ if [ ! $$? = 0 ]; then \
+ cat docs/append-to-devlink.tab >> /etc/devlink.tab; \
+ /usr/sbin/add_drv -m "* 0644 root sys" vmhgfs; \
+ fi
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * cpName.c --
+ *
+ * Shared portions of cross-platform name conversion routines used
+ * by hgfs. [bac]
+ *
+ */
+
+#ifdef sun
+#include <string.h>
+#endif
+
+#include "cpName.h"
+#include "cpNameInt.h"
+#include "vm_assert.h"
+#include "hgfsEscape.h"
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPName_GetComponent --
+ *
+ * Get the next component of the CP name.
+ *
+ * Returns the length of the component starting with the begin
+ * pointer, and a pointer to the next component in the buffer, if
+ * any. The "next" pointer is set to "end" if there is no next
+ * component.
+ *
+ * Results:
+ * length (not including NUL termination) >= 0 of next
+ * component on success.
+ * error < 0 on failure (invalid component).
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CPName_GetComponent(char const *begin, // IN: Beginning of buffer
+ char const *end, // IN: End of buffer
+ char const **next) // OUT: Start of next component
+{
+ char const *walk;
+ char const *myNext;
+ size_t len;
+
+ ASSERT(begin);
+ ASSERT(end);
+ ASSERT(next);
+ ASSERT(begin <= end);
+
+ for (walk = begin; ; walk++) {
+ if (walk == end) {
+ /* End of buffer. No NUL was found */
+
+ myNext = end;
+ break;
+ }
+
+ if (*walk == '\0') {
+ /* Found a NUL */
+
+ if (walk == begin) {
+ Log("CPName_GetComponent: error: first char can't be NUL\n");
+ return -1;
+ }
+
+ myNext = walk + 1;
+ if (myNext == end) {
+ /* Last character in the buffer is not allowed to be NUL */
+ return -1;
+ }
+
+ break;
+ }
+ }
+
+ len = walk - begin;
+
+ *next = myNext;
+ return ((int) len);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPNameEscapeAndConvertFrom --
+ *
+ * Converts a cross-platform name representation into a string for
+ * use in the local filesystem.
+ * Escapes illegal characters as a part of convertion.
+ * This is a cross-platform implementation and takes the path separator
+ * argument as an argument. The path separator is prepended before each
+ * additional path component, so this function never adds a trailing path
+ * separator.
+ *
+ * Results:
+ * 0 on success.
+ * error < 0 on failure (the converted string did not fit in
+ * the buffer provided or the input was invalid).
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CPNameEscapeAndConvertFrom(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input
+ size_t *outSize, // IN/OUT: Size of output buffer
+ char **bufOut, // IN/OUT: Output buffer
+ char pathSep) // IN: Path separator character
+{
+ int result;
+ size_t inputSize;
+ inputSize = HgfsEscape_GetSize(*bufIn, *inSize);
+ if (inputSize != 0) {
+ char *savedBufOut = *bufOut;
+ char const *savedOutConst = savedBufOut;
+ size_t savedOutSize = *outSize;
+ if (inputSize > *outSize) {
+ Log("CPNameEscapeAndConvertFrom: error: not enough room for escaping\n");
+ return -1;
+ }
+
+ /* Leaving space for the leading path separator, thus output to savedBufOut + 1. */
+ *inSize = HgfsEscape_Do(*bufIn, *inSize, savedOutSize, savedBufOut + 1);
+ result = CPNameConvertFrom(&savedOutConst, inSize, outSize, bufOut, pathSep);
+ *bufIn += *inSize;
+ *inSize = 0;
+ } else {
+ result = CPNameConvertFrom(bufIn, inSize, outSize, bufOut, pathSep);
+ }
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPNameConvertFrom --
+ *
+ * Converts a cross-platform name representation into a string for
+ * use in the local filesystem. This is a cross-platform
+ * implementation and takes the path separator argument as an
+ * argument. The path separator is prepended before each additional
+ * path component, so this function never adds a trailing path
+ * separator.
+ *
+ * Results:
+ * 0 on success.
+ * error < 0 on failure (the converted string did not fit in
+ * the buffer provided or the input was invalid).
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CPNameConvertFrom(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input
+ size_t *outSize, // IN/OUT: Size of output buffer
+ char **bufOut, // IN/OUT: Output buffer
+ char pathSep) // IN: Path separator character
+{
+ char const *in;
+ char const *inEnd;
+ size_t myOutSize;
+ char *out;
+ Bool inPlaceConvertion = (*bufIn == *bufOut);
+
+ ASSERT(bufIn);
+ ASSERT(inSize);
+ ASSERT(outSize);
+ ASSERT(bufOut);
+
+ in = *bufIn;
+ if (inPlaceConvertion) {
+ in++; // Skip place for the leading path separator.
+ }
+ inEnd = in + *inSize;
+ myOutSize = *outSize;
+ out = *bufOut;
+
+ for (;;) {
+ char const *next;
+ int len;
+ int newLen;
+
+ len = CPName_GetComponent(in, inEnd, &next);
+ if (len < 0) {
+ Log("CPNameConvertFrom: error: get next component failed\n");
+ return len;
+ }
+
+ /* Bug 27926 - preventing escaping from shared folder. */
+ if ((len == 1 && *in == '.') ||
+ (len == 2 && in[0] == '.' && in[1] == '.')) {
+ Log("CPNameConvertFrom: error: found dot/dotdot\n");
+ return -1;
+ }
+
+ if (len == 0) {
+ /* No more component */
+ break;
+ }
+
+ newLen = ((int) myOutSize) - len - 1;
+ if (newLen < 0) {
+ Log("CPNameConvertFrom: error: not enough room\n");
+ return -1;
+ }
+ myOutSize = (size_t) newLen;
+
+ *out++ = pathSep;
+ if (!inPlaceConvertion) {
+ memcpy(out, in, len);
+ }
+ out += len;
+
+ in = next;
+ }
+
+ /* NUL terminate */
+ if (myOutSize < 1) {
+ Log("CPNameConvertFrom: error: not enough room\n");
+ return -1;
+ }
+ *out = '\0';
+
+ /* Path name size should not require more than 4 bytes. */
+ ASSERT((in - *bufIn) <= 0xFFFFFFFF);
+
+ /* Update pointers. */
+ *inSize -= (in - *bufIn);
+ *outSize = myOutSize;
+ *bufIn = in;
+ *bufOut = out;
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * CPName_Print --
+ *
+ * Converts a CPName formatted string to a valid, NUL-terminated string by
+ * replacing all embedded NUL characters with '|'.
+ *
+ * Results:
+ * Pointer to a static buffer containing the converted string.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+char const *
+CPName_Print(char const *in, // IN: Name to print
+ size_t size) // IN: Size of name
+{
+ /* Static so it does not go on a kernel stack --hpreg */
+ static char out[128];
+ size_t i;
+
+ ASSERT(in);
+
+ ASSERT(sizeof out >= 4);
+ if (size > sizeof out - 1) {
+ size = sizeof out - 4;
+ out[size] = '.';
+ out[size + 1] = '.';
+ out[size + 2] = '.';
+ out[size + 3] = '\0';
+ } else {
+ out[size] = '\0';
+ }
+
+ for (i = 0; i < size; i++) {
+ out[i] = in[i] != '\0' ? in[i] : '|';
+ }
+
+ return out;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * CPName_LinuxConvertTo --
+ *
+ * Wrapper function that calls CPNameConvertTo() with the correct arguments
+ * for Linux path conversions.
+ *
+ * Makes a cross-platform name representation from the Linux path input
+ * string and writes it into the output buffer.
+ *
+ * Results:
+ * On success, returns the number of bytes used in the cross-platform name,
+ * NOT including the final terminating NUL character. On failure, returns
+ * a negative error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+CPName_LinuxConvertTo(char const *nameIn, // IN: Buf to convert
+ size_t bufOutSize, // IN: Size of the output buffer
+ char *bufOut) // OUT: Output buffer
+{
+ return CPNameConvertTo(nameIn, bufOutSize, bufOut, '/');
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * CPName_WindowsConvertTo --
+ *
+ * Wrapper function that calls CPNameConvertTo() with the correct arguments
+ * for Windows path conversions.
+ *
+ * Makes a cross-platform name representation from the Linux path input
+ * string and writes it into the output buffer.
+ *
+ * Results:
+ * On success, returns the number of bytes used in the cross-platform name,
+ * NOT including the final terminating NUL character. On failure, returns
+ * a negative error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+CPName_WindowsConvertTo(char const *nameIn, // IN: Buf to convert
+ size_t bufOutSize, // IN: Size of the output buffer
+ char *bufOut) // OUT: Output buffer
+{
+ return CPNameConvertTo(nameIn, bufOutSize, bufOut, '\\');
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPNameConvertTo --
+ *
+ * Makes a cross-platform name representation from the input string
+ * and writes it into the output buffer.
+ * HGFS convention is to echange names between guest and host in uescaped form.
+ * Both ends perform necessary name escaping according to its own rules
+ * to avoid presenitng invalid file names to OS. Thus the name needs to be unescaped
+ * as a part of conversion to host-independent format.
+ *
+ * Results:
+ * On success, returns the number of bytes used in the
+ * cross-platform name, NOT including the final terminating NUL
+ * character. On failure, returns a negative error.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CPNameConvertTo(char const *nameIn, // IN: Buf to convert
+ size_t bufOutSize, // IN: Size of the output buffer
+ char *bufOut, // OUT: Output buffer
+ char pathSep) // IN: path separator to use
+{
+ char *origOut = bufOut;
+ char const *endOut = bufOut + bufOutSize;
+ size_t cpNameLength = 0;
+
+ ASSERT(nameIn);
+ ASSERT(bufOut);
+
+ /* Skip any path separators at the beginning of the input string */
+ while (*nameIn == pathSep) {
+ nameIn++;
+ }
+
+ /* Copy the string to the output buf, converting all path separators into '\0'. */
+ for (; *nameIn != '\0' && bufOut < endOut; nameIn++) {
+ *bufOut = (*nameIn == pathSep) ? '\0' : *nameIn;
+ bufOut++;
+ }
+
+ /*
+ * NUL terminate. XXX This should go away.
+ *
+ * When we get rid of NUL termination here, this test should
+ * also change to "if (*nameIn != '\0')".
+ */
+ if (bufOut == endOut) {
+ return -1;
+ }
+ *bufOut = '\0';
+
+ /* Path name size should not require more than 4 bytes. */
+ ASSERT((bufOut - origOut) <= 0xFFFFFFFF);
+
+ /* If there were any trailing path separators, dont count them [krishnan] */
+ cpNameLength = bufOut - origOut;
+ while ((cpNameLength >= 1) && (origOut[cpNameLength - 1] == 0)) {
+ cpNameLength--;
+ }
+ cpNameLength = HgfsEscape_Undo(origOut, cpNameLength);
+
+ /* Return number of bytes used */
+ return (int) cpNameLength;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * cpName.h --
+ *
+ * Cross-platform name format used by hgfs.
+ *
+ */
+
+#ifndef __CP_NAME_H__
+#define __CP_NAME_H__
+
+
+#ifdef __KERNEL__
+# include "driver-config.h"
+# include <linux/string.h>
+#elif defined(__FreeBSD__)
+# if defined(_KERNEL)
+# include <sys/libkern.h>
+# define strchr(s,c) index(s,c)
+# else
+# include <string.h>
+# endif
+#elif defined(__APPLE__) && defined(KERNEL)
+# include <string.h>
+#elif !defined(sun)
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#include "vm_basic_types.h"
+
+
+/* Status codes for processing share names */
+typedef enum {
+ HGFS_NAME_STATUS_COMPLETE, /* Name is complete */
+ HGFS_NAME_STATUS_FAILURE, /* Name processing failed */
+ HGFS_NAME_STATUS_INCOMPLETE_BASE, /* Name is base of namespace */
+ HGFS_NAME_STATUS_INCOMPLETE_ROOT, /* Name is "root" only */
+ HGFS_NAME_STATUS_INCOMPLETE_DRIVE, /* Name is "root drive" only */
+ HGFS_NAME_STATUS_INCOMPLETE_UNC, /* Name is "root unc" only */
+ HGFS_NAME_STATUS_INCOMPLETE_UNC_MACH, /* Name is "root unc <x>" only */
+ HGFS_NAME_STATUS_DOES_NOT_EXIST, /* Name does not exist */
+ HGFS_NAME_STATUS_ACCESS_DENIED, /* Desired access to share denied */
+ HGFS_NAME_STATUS_SYMBOLIC_LINK, /* Name contains a symbolic link */
+ HGFS_NAME_STATUS_OUT_OF_MEMORY, /* Out of memory while processing */
+ HGFS_NAME_STATUS_TOO_LONG, /* Name has overly long component */
+} HgfsNameStatus;
+
+
+int
+CPName_ConvertTo(char const *nameIn, // IN: The buf to convert
+ size_t bufOutSize, // IN: The size of the output buffer
+ char *bufOut); // OUT: The output buffer
+
+int
+CPName_LinuxConvertTo(char const *nameIn, // IN: buf to convert
+ size_t bufOutSize, // IN: size of the output buffer
+ char *bufOut); // OUT: output buffer
+
+int
+CPName_WindowsConvertTo(char const *nameIn, // IN: buf to convert
+ size_t bufOutSize, // IN: size of the output buffer
+ char *bufOut); // OUT: output buffer
+
+int
+CPName_ConvertFrom(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input buffer
+ size_t *outSize, // IN/OUT: Size of output buffer
+ char **bufOut); // IN/OUT: Output buffer
+
+HgfsNameStatus
+CPName_ConvertFromRoot(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input
+ size_t *outSize, // IN/OUT: Size of output buf
+ char **bufOut); // IN/OUT: Output buffer
+
+int
+CPName_GetComponent(char const *begin, // IN: Beginning of buffer
+ char const *end, // IN: End of buffer
+ char const **next); // OUT: Next component
+
+char const *
+CPName_Print(char const *in, // IN: Name to print
+ size_t size); // IN: Size of name
+
+
+#endif /* __CP_NAME_H__ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * cpNameInt.h --
+ *
+ * Cross-platform name format used by hgfs.
+ *
+ */
+
+
+#ifndef __CP_NAME_INT_H__
+#define __CP_NAME_INT_H__
+
+
+#include "vm_basic_types.h"
+
+/*
+ * Used by CPName_ConvertFrom
+ */
+int
+CPNameConvertFrom(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input
+ size_t *outSize, // IN/OUT: Size of output buffer
+ char **bufOut, // IN/OUT: Output buffer
+ char pathSep); // IN: Path separator character
+
+int
+CPNameEscapeAndConvertFrom(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input
+ size_t *outSize, // IN/OUT: Size of output buffer
+ char **bufOut, // IN/OUT: Output buffer
+ char pathSep); // IN: Path separator character
+
+/*
+ * Common code for CPName_ConvertTo
+ */
+int
+CPNameConvertTo(char const *nameIn, // IN: Buf to convert
+ size_t bufOutSize, // IN: Size of the output buffer
+ char *bufOut, // OUT: Output buffer
+ char pathSep); // IN: path separator to use
+
+#endif /* __CP_NAME_INT_H__ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * cpNameLinux.c --
+ *
+ * Linux implementation of cross-platform name conversion
+ * routines used by hgfs. [bac]
+ *
+ */
+
+#if defined(sun) && !defined(SOL9)
+#include <memory.h>
+#endif
+
+#include "cpName.h"
+#include "cpNameInt.h"
+#include "vm_assert.h"
+#include "hgfsEscape.h"
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPName_ConvertFrom --
+ *
+ * Converts a cross-platform name representation into a string for
+ * use in the local filesystem.
+ *
+ * Results:
+ * Length (not including NUL termination) >= 0 of resulting
+ * string on success.
+ * Negative error on failure (the converted string did not fit in
+ * the buffer provided or the input was invalid).
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CPName_ConvertFrom(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input
+ size_t *outSize, // IN/OUT: Size of output buffer
+ char **bufOut) // IN/OUT: Output buffer
+{
+ ASSERT(bufIn);
+ ASSERT(inSize);
+ ASSERT(outSize);
+ ASSERT(bufOut);
+
+ return CPNameEscapeAndConvertFrom(bufIn, inSize, outSize, bufOut, '/');
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPName_ConvertFromRoot --
+ *
+ * Append the appropriate prefix to the output buffer for accessing
+ * the root of the local filesystem. CPName_ConvertFrom prepends
+ * leading path separators before each path component, but only
+ * when the next component has nonzero length, so we still need to
+ * special case this for Linux.
+ *
+ * The pointers and sizes are updated appropriately.
+ *
+ * Results:
+ * Status of name conversion
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+HgfsNameStatus
+CPName_ConvertFromRoot(char const **bufIn, // IN/OUT: Input to convert
+ size_t *inSize, // IN/OUT: Size of input
+ size_t *outSize, // IN/OUT: Size of output buffer
+ char **bufOut) // IN/OUT: Output buffer
+{
+ char const *next;
+ char *out;
+ int len;
+
+ ASSERT(bufIn);
+ ASSERT(inSize);
+ ASSERT(outSize);
+ ASSERT(bufOut);
+
+ out = *bufOut;
+
+ /*
+ * Get first component
+ */
+ len = CPName_GetComponent(*bufIn, *bufIn + *inSize, &next);
+ if (len < 0) {
+ Log("CPName_ConvertFromRoot: get first component failed\n");
+ return HGFS_NAME_STATUS_FAILURE;
+ }
+
+ /* Space for leading '/' plus NUL termination */
+ if (*outSize < len + 2) {
+ return HGFS_NAME_STATUS_FAILURE;
+ }
+
+ /* Put a leading '/' in the output buffer either way */
+ *out++ = '/';
+
+ memcpy(out, *bufIn, len);
+ out += len;
+
+ /* NUL terminate */
+ *out = '\0';
+
+ *inSize -= next - *bufIn;
+ *outSize -= out - *bufOut;
+ *bufIn = next;
+ *bufOut = out;
+
+ return HGFS_NAME_STATUS_COMPLETE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * CPName_ConvertTo --
+ *
+ * Wrapper function that calls the Linux implementation of _ConvertTo().
+ *
+ * Makes a cross-platform name representation from the Linux path input
+ * string and writes it into the output buffer.
+ * If the name being converter may be a result a of name escaping the
+ * function performs unescaping. HGFS convention is to always exchange
+ * unescaped names between guest and host and to perform necessary
+ * name escaping on both ends.
+ *
+ * Results:
+ * On success, returns the number of bytes used in the
+ * cross-platform name, NOT including the final terminating NUL
+ * character. On failure, returns a negative error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+CPName_ConvertTo(char const *nameIn, // IN: Buf to convert
+ size_t bufOutSize, // IN: Size of the output buffer
+ char *bufOut) // OUT: Output buffer
+{
+ int result;
+ result = CPName_LinuxConvertTo(nameIn, bufOutSize, bufOut);
+ if (result > 0) {
+ result = HgfsEscape_Undo(bufOut, result);
+ }
+ return result;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#include "vmware.h"
+#include "dbllnklst.h"
+
+/*
+ * dbllnklst.c --
+ *
+ * Light (but nonetheless powerful) implementation of doubly linked lists
+ */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DblLnkLst_Init --
+ *
+ * Initialize a member of a doubly linked list
+ *
+ * Result
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DblLnkLst_Init(DblLnkLst_Links *l) // IN
+{
+ ASSERT(l);
+
+ l->prev = l->next = l;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DblLnkLst_Link --
+ *
+ * Merge two doubly linked lists into one
+ *
+ * The operation is commutative
+ * The operation is inversible (its inverse is DblLnkLst_Unlink)
+ *
+ * Result
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DblLnkLst_Link(DblLnkLst_Links *l1, // IN
+ DblLnkLst_Links *l2) // IN
+{
+ DblLnkLst_Links *tmp;
+
+ ASSERT(l1);
+ ASSERT(l2);
+
+ (tmp = l1->prev)->next = l2;
+ (l1->prev = l2->prev)->next = l1;
+ l2->prev = tmp ;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DblLnkLst_Unlink --
+ *
+ * Split one doubly linked list into two
+ *
+ * No check is performed: the caller must ensure that both members
+ * belong to the same doubly linked list
+ *
+ * The operation is commutative
+ * The operation is inversible (its inverse is DblLnkLst_Link)
+ *
+ * Result
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DblLnkLst_Unlink(DblLnkLst_Links *l1, // IN
+ DblLnkLst_Links *l2) // IN
+{
+ DblLnkLst_Links *tmp;
+
+ ASSERT(l1);
+ ASSERT(l2);
+
+ tmp = l1->prev ;
+ (l1->prev = l2->prev)->next = l1;
+ (l2->prev = tmp )->next = l2;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DblLnkLst_Unlink1 --
+ *
+ * Unlink an element from its list.
+ *
+ * Result
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DblLnkLst_Unlink1(DblLnkLst_Links *l) // IN
+{
+ ASSERT(l);
+
+ DblLnkLst_Unlink(l, l->next);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * DblLnkLst_IsLinked --
+ *
+ * Determines whether an element is linked with any other elements.
+ *
+ * Results:
+ * TRUE if link is linked, FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+Bool
+DblLnkLst_IsLinked(DblLnkLst_Links const *l) // IN
+{
+ ASSERT(l);
+
+ ASSERT((l->prev == l && l->next == l) ||
+ (l->prev != l && l->next != l));
+
+ /*
+ * A DblLnkLst_Links is either linked to itself (not linked) or linked to
+ * other elements in a list (linked).
+ */
+ return l->prev != l;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DblLnkLst_LinkFirst --
+ *
+ * Insert 'l' at the beginning of the list anchored at 'head'
+ *
+ * Result
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DblLnkLst_LinkFirst(DblLnkLst_Links *head, // IN
+ DblLnkLst_Links *l) // IN
+{
+ ASSERT(head);
+ ASSERT(l);
+
+ DblLnkLst_Link(head->next, l);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DblLnkLst_LinkLast --
+ *
+ * Insert 'l' at the end of the list anchored at 'head'
+ *
+ * Result
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DblLnkLst_LinkLast(DblLnkLst_Links *head, // IN
+ DblLnkLst_Links *l) // IN
+{
+ ASSERT(head);
+ ASSERT(l);
+
+ DblLnkLst_Link(head, l);
+}
+
+
+#if 0
+/*
+ * Test code (which also demonstrates how to use this library)
+ */
+
+/*
+ * Add the double linked list capability to any of your data structure just by
+ * adding a DblLnkLst_Links field inside it. It is not required that the field
+ * comes first, but if it does, the execution will be slighly faster.
+ *
+ * Here we create a doubly linked list of integers
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef struct member {
+ int i;
+ DblLnkLst_Links l;
+} member;
+
+
+/* Member constructor */
+member *
+make_member(int i)
+{
+ member *m;
+
+ m = malloc(sizeof(*m));
+ DblLnkLst_Init(&m->l);
+ m->i = i;
+
+ return m;
+}
+
+
+/* Dump a circular list */
+void
+dump_circular(const member *c) // IN
+{
+ const member *current;
+
+ printf("forward: ");
+ current = c;
+ do {
+ printf("%d ", current->i);
+ current = DblLnkLst_Container(current->l.next, member, l);
+ } while (current != c);
+ printf("backward: ");
+ do {
+ printf("%d ", current->i);
+ current = DblLnkLst_Container(current->l.prev, member, l);
+ } while (current != c);
+ printf("\n");
+}
+
+
+/* Dump an anchored list */
+void
+dump_anchored(const DblLnkLst_Links *h) // IN
+{
+ DblLnkLst_Links *cur_l;
+
+ printf("forward: ");
+ for (cur_l = h->next; cur_l != h; cur_l = cur_l->next) {
+ member *current;
+
+ current = DblLnkLst_Container(cur_l, member, l);
+ printf("%d ", current->i);
+ }
+ printf("backward: ");
+ for (cur_l = h->prev; cur_l != h; cur_l = cur_l->prev) {
+ member *current;
+
+ current = DblLnkLst_Container(cur_l, member, l);
+ printf("%d ", current->i);
+ }
+ printf("\n");
+}
+
+
+/* Test code entry point */
+int
+main(int argc, // IN
+ char **argv) // IN
+{
+ member *c1;
+ member *c2;
+ member *c3;
+ member *c4;
+
+ DblLnkLst_Links h;
+ member *a1;
+ member *a2;
+ member *a3;
+
+ printf("Circular list: there is no origin\n");
+
+ /* Create the 1st member */
+ c1 = make_member(1);
+ /* Special case: there is no list to merge with, initially */
+
+ /* Add the 2nd member _after_ the 1st one */
+ c2 = make_member(2);
+ DblLnkLst_Link(&c1->l, &c2->l);
+
+ /* Add the 3rd member _after_ the 2nd one */
+ c3 = make_member(3);
+ DblLnkLst_Link(&c1->l, &c3->l);
+
+ /* Add the 4th member _before_ the 3rd one */
+ c4 = make_member(4);
+ DblLnkLst_Link(&c3->l, &c4->l);
+
+ printf("See it from this member...\n");
+ dump_circular(c1);
+ printf("...Or from this one\n");
+ dump_circular(c4);
+
+ printf("\n");
+ printf("Anchored (linear) list: it has a beginning and an end\n");
+
+ /* Create the 'head' of the list */
+ DblLnkLst_Init(&h);
+
+ /* Add the 1st member at the _end_ */
+ a1 = make_member(5);
+ DblLnkLst_LinkLast(&h, &a1->l);
+
+ /* Add the 2nd member at the _beginning_ */
+ a2 = make_member(6);
+ DblLnkLst_LinkFirst(&h, &a2->l);
+
+ /* Add the 3rd member _before_ the 1st one */
+ a3 = make_member(7);
+ DblLnkLst_Link(&a1->l, &a3->l);
+
+ dump_anchored(&h);
+
+ printf("\n");
+ printf("Merge both lists: the result is an anchored list\n");
+
+ DblLnkLst_Link(&h, &c4->l);
+
+ dump_anchored(&h);
+
+ printf("\n");
+ printf("Remove a member\n");
+
+ DblLnkLst_Unlink1(&c3->l);
+
+ dump_anchored(&h);
+
+ printf("\n");
+ printf("Split the result in two lists: an anchored one and a circular "
+ "one\n");
+ DblLnkLst_Unlink(&h, &a1->l);
+
+ dump_anchored(&h);
+ dump_circular(a1);
+
+ return 0;
+}
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * dbllnklst.h --
+ *
+ * Double linked lists
+ */
+
+#ifndef _DBLLNKLST_H_
+#define _DBLLNKLST_H_
+
+#include "vm_basic_types.h"
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_USERLEVEL
+#include "includeCheck.h"
+
+
+#define DblLnkLst_OffsetOf(type, field) ((intptr_t)&((type *)0)->field)
+
+#define DblLnkLst_Container(addr, type, field) \
+ ((type *)((char *)addr - DblLnkLst_OffsetOf(type, field)))
+
+#define DblLnkLst_ForEach(curr, head) \
+ for (curr = (head)->next; curr != (head); curr = (curr)->next)
+
+/* Safe from list element removal within loop body. */
+#define DblLnkLst_ForEachSafe(curr, nextElem, head) \
+ for (curr = (head)->next, nextElem = (curr)->next; \
+ curr != (head); \
+ curr = nextElem, nextElem = (curr)->next)
+
+typedef struct DblLnkLst_Links {
+ struct DblLnkLst_Links *prev;
+ struct DblLnkLst_Links *next;
+} DblLnkLst_Links;
+
+
+/* Functions for both circular and anchored lists. --hpreg */
+
+void DblLnkLst_Init(DblLnkLst_Links *l);
+void DblLnkLst_Link(DblLnkLst_Links *l1, DblLnkLst_Links *l2);
+void DblLnkLst_Unlink(DblLnkLst_Links *l1, DblLnkLst_Links *l2);
+void DblLnkLst_Unlink1(DblLnkLst_Links *l);
+Bool DblLnkLst_IsLinked(DblLnkLst_Links const *l);
+
+/* Functions specific to anchored lists. --hpreg */
+
+void DblLnkLst_LinkFirst(DblLnkLst_Links *head, DblLnkLst_Links *l);
+void DblLnkLst_LinkLast(DblLnkLst_Links *head, DblLnkLst_Links *l);
+
+
+#endif /* _DBLLNKLST_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * debug.c --
+ *
+ * Routines for debugging Solaris kernel module.
+ *
+ */
+
+
+#include "debug.h"
+#include "filesystem.h"
+#ifndef SOL9
+#include <sys/cred_impl.h>
+#endif
+
+
+/*
+ * Functions
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintVfssw --
+ *
+ * Prints the provided VFS Switch structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsDebugPrintVfssw(char *str, struct vfssw *vfsswp)
+{
+ ASSERT(str);
+ ASSERT(vfsswp);
+
+ DEBUG(VM_DEBUG_STRUCT, "struct vfssw from %s\n", str);
+ DEBUG(VM_DEBUG_STRUCT, " vsw_name : %s\n",
+ (vfsswp->vsw_name) ? vfsswp->vsw_name : "NULL");
+ DEBUG(VM_DEBUG_STRUCT, " vsw_init : %p\n", vfsswp->vsw_init);
+ DEBUG(VM_DEBUG_STRUCT, " vsw_flag : %x\n", vfsswp->vsw_flag);
+# ifdef SOL9
+ DEBUG(VM_DEBUG_STRUCT, " vsw_vfsops : %x\n", vfsswp->vsw_vfsops);
+ DEBUG(VM_DEBUG_STRUCT, " vsw_optproto: %x\n", vfsswp->vsw_optproto);
+# endif
+ DEBUG(VM_DEBUG_STRUCT, " vsw_count : %d\n", vfsswp->vsw_count);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintVfs --
+ *
+ * Prints the provided VFS structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsDebugPrintVfs(char *str, struct vfs *vfsp)
+{
+ ASSERT(str);
+ ASSERT(vfsp);
+
+ DEBUG(VM_DEBUG_STRUCT, "struct vfs from %s\n", str);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_next : %p\n", vfsp->vfs_next);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_op : %p\n", vfsp->vfs_op);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_vnodecovered: %p\n", vfsp->vfs_vnodecovered);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_flag : %d\n", vfsp->vfs_flag);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_bsize : %d\n", vfsp->vfs_bsize);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_fstype : %d\n", vfsp->vfs_fstype);
+# ifdef SOL9
+ DEBUG(VM_DEBUG_STRUCT, " vfs_fsid : %d\n", vfsp->vfs_fsid);
+# else
+ DEBUG(VM_DEBUG_STRUCT, " vfs_fsid.val[0] : %d\n", vfsp->vfs_fsid.val[0]);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_fsid.val[1] : %d\n", vfsp->vfs_fsid.val[1]);
+# endif
+ DEBUG(VM_DEBUG_STRUCT, " vfs_vadata : %p\n", vfsp->vfs_data);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_dev : %lu\n", vfsp->vfs_dev);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_bcount : %lu\n", vfsp->vfs_bcount);
+# ifdef SOL9
+ DEBUG(VM_DEBUG_STRUCT, " vfs_nsubmounts : %d\n", vfsp->vfs_nsubmounts);
+# endif
+ DEBUG(VM_DEBUG_STRUCT, " vfs_list : %p\n", vfsp->vfs_list);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_hash : %p\n", vfsp->vfs_hash);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_reflock : %p\n", &vfsp->vfs_reflock);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_count : %d\n", vfsp->vfs_count);
+# ifdef SOL9
+ DEBUG(VM_DEBUG_STRUCT, " vfs_mntopts : %x\n", vfsp->vfs_mntopts);
+ DEBUG(VM_DEBUG_STRUCT, " vfs_resource : %s\n",
+ (vfsp->vfs_resource) ? vfsp->vfs_resource : "NULL");
+# endif
+ DEBUG(VM_DEBUG_STRUCT, " vfs_mtime : %ld\n", vfsp->vfs_mtime);
+
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintVnode --
+ *
+ * Prints the provided vnode structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsDebugPrintVnode(uint32 level, char *str, struct vnode *vnodep)
+{
+ ASSERT(str);
+ ASSERT(vnodep);
+
+ DEBUG(level, "struct vnode from %s located at %p\n", str, vnodep);
+ DEBUG(level, " v_lock : %p\n", &vnodep->v_lock);
+ DEBUG(level, " v_flag : %d\n", vnodep->v_flag);
+ DEBUG(level, " v_count : %d\n", vnodep->v_count);
+ DEBUG(level, " v_vfsmountedhere: %p\n", vnodep->v_vfsmountedhere);
+ DEBUG(level, " v_op : %p\n", vnodep->v_op);
+ DEBUG(level, " v_vfsp : %p\n", vnodep->v_vfsp);
+ DEBUG(level, " v_stream : %p\n", vnodep->v_stream);
+ DEBUG(level, " v_pages : %p\n", vnodep->v_pages);
+# ifdef SOL9
+ DEBUG(level, " v_next : %p\n", vnodep->v_next);
+ DEBUG(level, " v_prev : %p\n", vnodep->v_prev);
+# endif
+ DEBUG(level, " v_type : %d\n", vnodep->v_type);
+ DEBUG(level, " v_rdev : %lu\n", vnodep->v_rdev);
+ DEBUG(level, " v_data : %p\n", vnodep->v_data);
+ DEBUG(level, " v_filocks : %p\n", vnodep->v_filocks);
+ DEBUG(level, " v_shrlocks : %p\n", vnodep->v_shrlocks);
+ DEBUG(level, " v_cv : %p\n", &vnodep->v_cv);
+ DEBUG(level, " v_locality : %p\n", vnodep->v_locality);
+ DEBUG(level, " v_nbllock : %p\n", &vnodep->v_nbllock);
+
+ if (HGFS_VP_TO_OFP(vnodep) && HGFS_VP_TO_FP(vnodep)) {
+ DEBUG(level, " filename : %s\n", HGFS_VP_TO_FILENAME(vnodep));
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintCred --
+ *
+ * Prints the provided cred structure the describes the credentials of the
+ * caller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsDebugPrintCred(char *str, struct cred *credp)
+{
+ DEBUG(VM_DEBUG_STRUCT, "struct cred from %s\n", str);
+ DEBUG(VM_DEBUG_STRUCT, " cr_ref : %d\n", credp->cr_ref);
+ DEBUG(VM_DEBUG_STRUCT, " cr_uid : %d\n", credp->cr_uid);
+ DEBUG(VM_DEBUG_STRUCT, " cr_gid : %d\n", credp->cr_gid);
+ DEBUG(VM_DEBUG_STRUCT, " cr_ruid : %d\n", credp->cr_ruid);
+ DEBUG(VM_DEBUG_STRUCT, " cr_rgid : %d\n", credp->cr_rgid);
+ DEBUG(VM_DEBUG_STRUCT, " cr_suid : %d\n", credp->cr_suid);
+ DEBUG(VM_DEBUG_STRUCT, " cr_sgid : %d\n", credp->cr_sgid);
+ DEBUG(VM_DEBUG_STRUCT, " cr_ngroups: %d\n", credp->cr_ngroups);
+ DEBUG(VM_DEBUG_STRUCT, " cr_groups : %p\n", credp->cr_groups);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugMounta --
+ *
+ * Prints the provided mounta structure that describes the arguments
+ * provided to users.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsDebugPrintMounta(char *str, struct mounta *mntp)
+{
+ ASSERT(str);
+ ASSERT(mntp);
+
+ DEBUG(VM_DEBUG_STRUCT, "struct mounta from %s\n", str);
+ DEBUG(VM_DEBUG_STRUCT, " spec : %s\n",
+ (mntp->spec) ? mntp->spec : "NULL");
+ DEBUG(VM_DEBUG_STRUCT, " dir : %s\n",
+ (mntp->dir) ? mntp->dir : "NULL");
+ DEBUG(VM_DEBUG_STRUCT, " flags : %x\n", mntp->flags);
+ DEBUG(VM_DEBUG_STRUCT, " fstype : %s\n",
+ (mntp->fstype) ? mntp->fstype : "NULL");
+ DEBUG(VM_DEBUG_STRUCT, " dataptr : %p\n", mntp->dataptr);
+ DEBUG(VM_DEBUG_STRUCT, " datalen : %d\n", mntp->datalen);
+ DEBUG(VM_DEBUG_STRUCT, " optptr : %p\n", mntp->optptr);
+ DEBUG(VM_DEBUG_STRUCT, " optlen : %d\n", mntp->optlen);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintVattr --
+ *
+ * Prints the contents of an attributes structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsDebugPrintVattr(const struct vattr *vap)
+{
+ DEBUG(VM_DEBUG_STRUCT, " va_mask: %x\n", vap->va_mask);
+ DEBUG(VM_DEBUG_STRUCT, " va_type: %d\n", vap->va_type);
+ DEBUG(VM_DEBUG_STRUCT, " va_mode: %x\n", vap->va_mode);
+ DEBUG(VM_DEBUG_STRUCT, " va_uid: %u\n", vap->va_uid);
+ DEBUG(VM_DEBUG_STRUCT, " va_gid: %u\n", vap->va_gid);
+ DEBUG(VM_DEBUG_STRUCT, " va_fsid: %lu\n", vap->va_fsid);
+ DEBUG(VM_DEBUG_STRUCT, " va_nodeid: %llu\n", vap->va_nodeid);
+ DEBUG(VM_DEBUG_STRUCT, " va_nlink: %x\n", vap->va_nlink);
+ DEBUG(VM_DEBUG_STRUCT, " va_size: %llu\n", vap->va_size);
+ DEBUG(VM_DEBUG_STRUCT, " va_atime.tv_sec: %ld\n", vap->va_atime.tv_sec);
+ DEBUG(VM_DEBUG_STRUCT, " va_atime.tv_nsec: %ld\n", vap->va_atime.tv_nsec);
+ DEBUG(VM_DEBUG_STRUCT, " va_mtime.tv_sec: %ld\n", vap->va_mtime.tv_sec);
+ DEBUG(VM_DEBUG_STRUCT, " va_mtime.tv_nsec: %ld\n", vap->va_mtime.tv_nsec);
+ DEBUG(VM_DEBUG_STRUCT, " va_ctime.tv_sec: %ld\n", vap->va_ctime.tv_sec);
+ DEBUG(VM_DEBUG_STRUCT, " va_ctime.tv_nsec: %ld\n", vap->va_ctime.tv_nsec);
+ DEBUG(VM_DEBUG_STRUCT, " va_rdev: %lu\n", vap->va_rdev);
+ DEBUG(VM_DEBUG_STRUCT, " va_blksize: %u\n", vap->va_blksize);
+ DEBUG(VM_DEBUG_STRUCT, " va_nblocks: %llu\n", vap->va_nblocks);
+#ifdef SOL9
+ DEBUG(VM_DEBUG_STRUCT, " va_vcode: %u\n", vap->va_vcode);
+#else
+ DEBUG(VM_DEBUG_STRUCT, " va_seq: %u\n", vap->va_seq);
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintReqList --
+ *
+ * For debugging. Prints out the request list for the provided list
+ * anchor.
+ * Note: Assumes called with the list lock held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+HgfsDebugPrintReqList(DblLnkLst_Links *listAnchor) // IN: Anchor of list to print
+{
+ DblLnkLst_Links *currNode;
+ HgfsReq *currReq;
+
+ ASSERT(listAnchor);
+
+ DEBUG(VM_DEBUG_STRUCT, "Request List:\n");
+ DEBUG(VM_DEBUG_STRUCT, " anchor: %p\n", listAnchor);
+
+ for (currNode = listAnchor->next; currNode != listAnchor; currNode = currNode->next)
+ {
+ currReq = DblLnkLst_Container(currNode, HgfsReq, listNode);
+ DEBUG(VM_DEBUG_STRUCT, " address: %p (id=%d)\n",
+ currReq, currReq->id);
+ }
+
+ DEBUG(VM_DEBUG_STRUCT, "--DONE--\n");
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintReq --
+ *
+ * Prints the relevant portions of the provided HgfsReq structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+HgfsDebugPrintReq(const char *str,
+ HgfsReq *req)
+{
+ ASSERT(str);
+ ASSERT(req);
+
+ DEBUG(VM_DEBUG_STRUCT, "struct HgfsReq from %s\n", str);
+ DEBUG(VM_DEBUG_STRUCT, " id: %d\n", req->id);
+ DEBUG(VM_DEBUG_STRUCT, " listNode: %p\n", &req->listNode);
+ DEBUG(VM_DEBUG_STRUCT, " next=%p\n", req->listNode.next);
+ DEBUG(VM_DEBUG_STRUCT, " prev=%p\n", req->listNode.prev);
+ DEBUG(VM_DEBUG_STRUCT, " packetSize: %d\n", req->packetSize);
+ DEBUG(VM_DEBUG_STRUCT, " state: %d (see hgfsSolaris.h)\n", req->state);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintReqPool --
+ *
+ * Prints the contents if the request pool.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+HgfsDebugPrintReqPool(const char *str)
+{
+ int i;
+
+ ASSERT(str);
+
+ DEBUG(VM_DEBUG_STRUCT, "Request pool from %s\n", str);
+
+ for (i = 0; i < ARRAYSIZE(requestPool); i++) {
+ DEBUG(VM_DEBUG_STRUCT, " Index: %d, ID: %d\n", i, requestPool[i].id);
+ DEBUG(VM_DEBUG_STRUCT, " listNode: %p\n", &requestPool[i].listNode);
+ DEBUG(VM_DEBUG_STRUCT, " next=%p\n", requestPool[i].listNode.next);
+ DEBUG(VM_DEBUG_STRUCT, " prev=%p\n", requestPool[i].listNode.prev);
+ DEBUG(VM_DEBUG_STRUCT, " packetSize: %d\n", requestPool[i].packetSize);
+ DEBUG(VM_DEBUG_STRUCT, " state: %d (see hgfsSolaris.h)\n", requestPool[i].state);
+ }
+
+ DEBUG(VM_DEBUG_STRUCT, "--request pool done--\n");
+}
+
+
+/*
+ * Gotta have Panic. Stolen from vmnix.
+ * (Then stolen from hgfs/driver/linux/main.c and tweaked for Solaris)
+ */
+
+/*
+ * There is a problem in Solaris 9's header files when using the va_start
+ * and va_end macros, so we manually do what the preprocessor would have
+ * done here.
+ *
+ * Note, the line using __builtin_next_arg is equivalent to:
+ * args = ((char *)(&fmt) + sizeof (char *));
+ *
+ * That is, it just provides a pointer to the unnamed first variable
+ * argument.
+ */
+#ifdef SOL9
+# define compat_va_list char *
+# define compat_va_start(arg, fmt) arg = ((char *)__builtin_next_arg(fmt))
+# define compat_va_end(arg)
+#else
+# define compat_va_list va_list
+# define compat_va_start(arg, fmt) va_start(arg, fmt)
+# define compat_va_end(arg) va_end(arg)
+#endif
+
+void
+Panic(const char *fmt, ...) // IN
+{
+ compat_va_list args;
+ char buffer[1024];
+ volatile char *p = NULL;
+
+ compat_va_start(args, fmt);
+ vsprintf(buffer, fmt, args);
+ compat_va_end(args);
+
+ cmn_err(HGFS_ERROR, "%s", buffer);
+
+ /* force segfault */
+ buffer[0] = *p;
+ while (1) ; // avoid compiler warning
+}
+
+
+/*
+ * For compatibility with existing code.
+ */
+void
+Log(const char *fmt, ...)
+{
+#ifdef VM_DEBUG_LEV
+ compat_va_list args;
+ char buffer[1024];
+
+ /*
+ * We check this here to avoid unnecessarily manipulating buffer if we
+ * aren't even going to print the log.
+ */
+ if (VM_DEBUG_LOG & VM_DEBUG_LEV) {
+ compat_va_start(args, fmt);
+ vsprintf(buffer, fmt, args);
+ compat_va_end(args);
+
+ cmn_err(HGFS_DEBUG, "%s", buffer);
+ }
+#endif
+}
+
+#undef compat_va_list
+#undef compat_va_start
+#undef compat_va_end
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * debug.h --
+ *
+ * Macros and includes for debugging.
+ *
+ */
+
+#ifndef __DEBUG_H_
+#define __DEBUG_H_
+
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+
+#include "hgfsSolaris.h"
+#include "filesystem.h"
+
+
+/*
+ * Debugging
+ */
+#define HGFS_DEBUG (CE_NOTE)
+
+#define VM_DEBUG_ALWAYS (1)
+#define VM_DEBUG_FAIL VM_DEBUG_ALWAYS
+#define VM_DEBUG_NOTSUP VM_DEBUG_ALWAYS
+#define VM_DEBUG_ENTRY (1 << 1)
+#define VM_DEBUG_DONE (1 << 2)
+#define VM_DEBUG_LOAD (1 << 3)
+#define VM_DEBUG_INFO (1 << 4)
+#define VM_DEBUG_STRUCT (1 << 5)
+#define VM_DEBUG_LIST (1 << 6)
+#define VM_DEBUG_CHPOLL (1 << 7)
+#define VM_DEBUG_RARE (1 << 8)
+#define VM_DEBUG_COMM (1 << 9)
+#define VM_DEBUG_REQUEST (1 << 10)
+#define VM_DEBUG_LOG (1 << 11)
+#define VM_DEBUG_ATTR (1 << 12)
+#define VM_DEBUG_DEVENTRY (1 << 13)
+#define VM_DEBUG_DEVDONE (1 << 14)
+#define VM_DEBUG_SIG (1 << 15)
+#define VM_DEBUG_ERROR (1 << 16)
+#define VM_DEBUG_HSHTBL (1 << 17)
+#define VM_DEBUG_HANDLE (1 << 18)
+#define VM_DEBUG_STATE (1 << 19)
+
+#ifdef VM_DEBUGGING_ON
+/*#define VM_DEBUG_LEV (VM_DEBUG_ALWAYS | VM_DEBUG_ENTRY | VM_DEBUG_DONE | \
+ VM_DEBUG_LOAD | VM_DEBUG_COMM | \
+ VM_DEBUG_LOG | VM_DEBUG_ATTR)
+*/
+/*#define VM_DEBUG_LEV (VM_DEBUG_ALWAYS | VM_DEBUG_FAIL | VM_DEBUG_ERROR | \
+ VM_DEBUG_COMM | VM_DEBUG_DONE)
+*/
+#define VM_DEBUG_LEV (VM_DEBUG_ALWAYS | VM_DEBUG_FAIL)
+#endif
+
+
+#ifdef VM_DEBUG_LEV
+#define DEBUG(type, args...) \
+ ((type & VM_DEBUG_LEV) ? (cmn_err(HGFS_DEBUG, args)) : 0)
+#else
+#define DEBUG(type, args...)
+#endif
+
+
+/*
+ * Prototypes
+ */
+INLINE void HgfsDebugPrintVfssw(char *str, struct vfssw *vfsswp);
+INLINE void HgfsDebugPrintVfs(char *str, struct vfs *vfsp);
+INLINE void HgfsDebugPrintVnode(uint32 level, char *str, struct vnode *vnodep);
+INLINE void HgfsDebugPrintCred(char *str, struct cred *credp);
+INLINE void HgfsDebugPrintMounta(char *str, struct mounta *mntp);
+INLINE void HgfsDebugPrintVattr(const struct vattr *vap);
+void HgfsDebugPrintReqList(DblLnkLst_Links *listAnchor);
+void HgfsDebugPrintReq(const char *str, HgfsReq *req);
+void HgfsDebugPrintReqPool(const char *str);
+void Panic(const char *fmt, ...);
+void Log(const char *fmt, ...);
+
+
+
+#endif /* __DEBUG_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * device.c --
+ *
+ * HGFS device driver for Solaris.
+ *
+ * Implements a device driver that creates a device in /dev that can be
+ * interacted with through standard file IO syscalls. Implements open(),
+ * close(), read(), write(), and poll().
+ *
+ * read() provides requests from the filesystem to the caller and is
+ * synchronized with the HgfsSubmitRequest() function in the filesystem.
+ * write() receives replies to those requests and provides them to the
+ * filesystem by waking up processes waiting in HgfsSubmitRequest().
+ *
+ */
+
+#include "hgfsSolaris.h"
+#include "device.h"
+#include "request.h"
+#include "filesystem.h"
+
+/*
+ * Local variables
+ */
+static Bool hgfsAttached; /* Flag to tell whether device is attached */
+
+/*
+ * Function Prototypes
+ */
+static int HgfsSendRequestToUser(HgfsReq *req, uio_t *uiop);
+static INLINE int HgfsGetReplyHeaderFromUser(HgfsReplyHeader *reply, uio_t *uiop);
+static int HgfsGetReplyPacketFromUser(HgfsReq *req, ssize_t offset, uio_t *uiop);
+
+/* Local utility functions */
+static INLINE HgfsSuperInfo *HgfsDevToSuperInfo(dev_t dev);
+static INLINE HgfsSuperInfo *HgfsDevinfoToSuperInfo(dev_info_t *dip);
+static INLINE HgfsReq *HgfsReplyToRequest(HgfsReplyHeader *reply);
+
+/*
+ * Driver "configuration" functions
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevGetinfo --
+ *
+ * The entry point for getinfo(9E). Carries out commands requested by the
+ * kernel that either return the device information structure or the
+ * instance number.
+ *
+ * Results:
+ * Returns DDI_SUCCESS on success and DDI_FAILURE on failure.
+ * When infocmd is DDI_INFO_DEVT2DEVINFO, a pointer to the device's
+ * information structure is returned in result.
+ * When infocmd is DDI_INFO_DEVT2INSTANCE, the device's instance number is
+ * returned in result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevGetinfo(dev_info_t *dip, // IN: Device info for this device
+ ddi_info_cmd_t infocmd, // IN: Command to carry out
+ void *arg, // IN: Device number (as dev_t) for this device
+ void **result) // OUT: Result of command
+{
+ minor_t instance; /* getminor() returns minor_t */
+ HgfsSuperInfo *sip;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevGetinfo().\n");
+
+ ASSERT(arg);
+ ASSERT(result);
+
+ /* These indicate a kernel error */
+ if ((!arg) || (!result)) {
+ cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevGetinfo().\n");
+ return EINVAL;
+ }
+
+
+ switch (infocmd) {
+
+ /* This wants a dev_info_t from the dev_t: get it from the superinfo */
+ case DDI_INFO_DEVT2DEVINFO:
+ sip = HgfsDevToSuperInfo((dev_t)arg);
+ if (!sip) {
+ goto error;
+ }
+
+ *result = sip->dip;
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevGetinfo() done.\n");
+ return DDI_SUCCESS;
+
+ /*
+ * This wants the instance number, which is the minor number since we set
+ * it to be that in HgfsDevAttach()'s call to ddi_create_minor_node().
+ */
+ case DDI_INFO_DEVT2INSTANCE:
+ instance = getminor((dev_t)arg);
+ *result = (void *)((uintptr_t)instance);
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevGetinfo() done.\n");
+ return DDI_SUCCESS;
+
+ /* Unrecognized command */
+ default:
+ goto error;
+ }
+
+error:
+ *result = NULL;
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevGetinfo() done (FAIL).\n");
+ return DDI_FAILURE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevAttach --
+ *
+ * The entry point for attach(9E) that is invoked when the driver is loaded
+ * into the kernel. This allocates and initializes a super info structure
+ * and creates the device entry ('minor node') in the file system.
+ * XXX: do we need to handle suspend and resume commands also?
+ *
+ * Results:
+ * Returns zero on success, and an appropriate error code on failure.
+ *
+ * Side effects:
+ * The device entry appears in the filesystem (/devices).
+ * The hgfsAttaches flag is set.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevAttach(dev_info_t *dip, // IN: Device info for this device
+ ddi_attach_cmd_t cmd) // IN: Command to carry out
+{
+ int instance; /* ddi_get_instance() returns int */
+ HgfsSuperInfo *sip;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevAttach().\n");
+
+ ASSERT(dip);
+
+ /* This indicates a kernel error */
+ if (!dip) {
+ cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevAttach().\n");
+ return EINVAL;
+ }
+
+ if (hgfsAttached) {
+ cmn_err(HGFS_ERROR, "Device already attached.\n");
+ return EIO;
+ }
+
+ switch (cmd) {
+
+ /* Attach this device */
+ case DDI_ATTACH:
+ /* hgfsInstance is used by the filesystem side to get superinfo pointer */
+ instance = hgfsInstance = ddi_get_instance(dip);
+
+ /*
+ * Here we allocate state for this instance of the driver (see comment
+ * in _init() for more information on Solaris' DDI Soft State interface).
+ *
+ * Once we have the pointer to the state we just allocated, we'll
+ * initialize its fields. In particular:
+ * - a pointer to the dev_info_t is stored because HgfsGetInfo() will
+ * need it.
+ * - clear the flag indicating whether the device is open.
+ * - the request list, along with its mutex and condition variable, are
+ * initialized.
+ */
+ if (ddi_soft_state_zalloc(superInfoHead, instance) != DDI_SUCCESS) {
+ cmn_err(HGFS_ERROR,
+ "could not zalloc state for this instance (%d).\n", instance);
+ goto error;
+ }
+
+ sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, instance);
+ if (!sip) {
+ goto error_free;
+ }
+
+ sip->dip = dip;
+ sip->devOpen = 0;
+
+ HgfsInitRequestList(sip);
+
+ /*
+ * Create the minor node (the /devices entry)
+ */
+ if (ddi_create_minor_node(dip, /* device info ptr */
+ HGFS_DEV_NAME, /* name of device */
+ S_IFCHR, /* type of device (character) */
+ instance, /* minor number */
+ DDI_PSEUDO, /* pseudo device b/c not backed
+ by physical device */
+ 0 /* flag: not a clone device */
+ ) != DDI_SUCCESS) {
+
+ cmn_err(HGFS_ERROR, "could not create minor node (/devices entry).\n");
+ goto error_free;
+ }
+
+ /* Report presence of device to system log (syslog/dmesg) */
+ ddi_report_dev(dip);
+
+ /*
+ * We only want one instance of this driver to ever be running, so set a
+ * flag stating the device is attached
+ */
+ hgfsAttached = TRUE;
+
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevAttach() done.\n");
+ return DDI_SUCCESS;
+
+ /* Unsupported commands: DDI_PM_RESUME and DDI_RESUME */
+ default:
+ goto error;
+ }
+
+error_free:
+ ddi_soft_state_free(sip, instance);
+error:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevAttach() done (FAIL).\n");
+ return DDI_FAILURE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevDetach --
+ *
+ * The detach(9E) entry point that is invoked when the driver is
+ * unloaded from the kernel. This cleans up all state allocated in
+ * HgfsDevAttach() and removes the device entry ('minor node').
+ *
+ * Results:
+ * Returns zero on success, and an appropriate error code on failure.
+ *
+ * Side effects:
+ * The super info structure for this driver is deallocated.
+ * The device entry (/devices) is no longer valid.
+ * The hgfsAttached flag is cleared.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevDetach(dev_info_t *dip, // IN: Device info for this device
+ ddi_detach_cmd_t cmd) // IN: Command to carry out
+{
+ int instance;
+ HgfsSuperInfo *sip;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevDetach().\n");
+
+ ASSERT(dip);
+
+ /* This indicates a kernel error */
+ if (!dip) {
+ cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevDetach().\n");
+ return EINVAL;
+ }
+
+ switch(cmd) {
+
+ /* Detach this device */
+ case DDI_DETACH:
+ /*
+ * Clean up the state allocated for this instance of the driver:
+ * - Get a pointer to the super info structure,
+ * - Remove the device entry (minor node) from the system,
+ * - Free the super info structure.
+ */
+ instance = ddi_get_instance(dip);
+ sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, instance);
+ if (!sip) {
+ cmn_err(HGFS_ERROR, "could not find HgfsSuperInfo on detach.\n");
+ goto error;
+ }
+
+ ddi_remove_minor_node(dip, NULL); /* NULL means remove all for this dev_info_t */
+ ddi_soft_state_free(superInfoHead, instance);
+
+ /* Indicate driver is no longer attached */
+ hgfsAttached = 0;
+
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevDetach() done.\n");
+ return DDI_SUCCESS;
+
+ /* Unsupported commands: DDI_PM_SUSPEND and DDI_SUSPEND */
+ default:
+ goto error;
+ }
+
+error:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevDetach() done (FAIL).\n");
+ return DDI_FAILURE;
+}
+
+
+
+/*
+ * Device functions
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevOpen --
+ * The entry point for open(9E) that is invoked when a user process calls
+ * open(2) on the device. This checks that the specified device and open
+ * type are valid.
+ *
+ * Results:
+ * Returns zero on success, and an appropriate error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevOpen(dev_t *devp, // IN: Pointer to device number
+ int flag, // IN: Indicates open mode
+ // (FREAD, FWRITE, FEXCL, FNDELAY)
+ int otyp, // IN: Type of open (OTYP_CHR, OTYP_BLK, OTYP_LYR)
+ cred_t *credp) // IN: Pointer to credentials of caller
+{
+ HgfsSuperInfo *sip;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevOpen().\n");
+
+ ASSERT(devp);
+ ASSERT(credp);
+
+ /* These indicate a kernel error */
+ if ((!devp) || (!credp)) {
+ cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevOpen().\n");
+ return EINVAL;
+ }
+
+ /*
+ * Here we do a few checks to ensure that this open is valid. First, our
+ * device is a character device so we ensure that the type of open
+ * indicates this. Next we ensure that the caller is root. Finally, we
+ * make sure that the device number specified is valid by attempting to
+ * retrieve its state that was allocated in HgfsDevDetach().
+ */
+
+ if (otyp != OTYP_CHR) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevOpen() done (FAIL).\n");
+ return EINVAL;
+ }
+
+ if (!HgfsSuser(credp)) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevOpen() done (FAIL).\n");
+ return EINVAL;
+ }
+
+ sip = HgfsDevToSuperInfo(*devp);
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevOpen() done (FAIL).\n");
+ return ENXIO;
+ }
+
+ /* Make sure device isn't opened more than once */
+ if (sip->devOpen) {
+ return ENXIO;
+ }
+
+ sip->devOpen = TRUE;
+
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevOpen() done.\n");
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevClose --
+ * The entry point for close(9E), which is invoked when a user process
+ * calls close(2) on the device. This ensures that the specified device
+ * number is valid (by trying to find its state structure).
+ *
+ * Results:
+ * Returns zero on success, and an appropriate error code on failure.
+ *
+ * Side effects:
+ * All pending requests are cancelled.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevClose(dev_t dev, // IN: Device number
+ int flag, // IN: Mode opened with (see HgfsOpen() comment)
+ int otyp, // IN: Type of open (see HgfsOpen() comment)
+ cred_t *credp) // IN: Pointer to credentials of caller
+{
+ HgfsSuperInfo *sip;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevClose().\n");
+
+ ASSERT(credp);
+
+ /* This indicates a kernel error */
+ if (!credp) {
+ cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevClose().\n");
+ return EINVAL;
+ }
+
+ /*
+ * We only allow root to open and close the device.
+ */
+ if (!HgfsSuser(credp)) {
+ return EINVAL;
+ }
+
+
+ /* Just make sure this instance is valid, state is freed in HgfsDevDetach() */
+ sip = HgfsDevToSuperInfo(dev);
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevClose() done (FAIL).\n");
+ return ENXIO;
+ }
+
+ /*
+ * This will signify to the filesystem that the device half is no longer
+ * present.
+ */
+ sip->devOpen = 0;
+
+ /*
+ * Each submitted request must be told an error has occurred.
+ */
+ HgfsCancelAllRequests(sip);
+
+
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevClose() done.\n");
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevRead --
+ *
+ * Entry point for read(9E) that is invoked when a user process calls
+ * read(2) on the device.
+ *
+ * This checks the pending request list for any outstanding requests. If
+ * the list is empty, this blocks on a condition variable that is signaled
+ * each time a request is enqueued. The request is removed from the list
+ * and copied to the user's buffer.
+ *
+ * Results:
+ * Returns zero on success, an appropriate error code on failure.
+ *
+ * Side effects:
+ * Blocks if the list is empty.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevRead(dev_t dev, // IN: Device number
+ uio_t *uiop, // IN: Ptr to structure defining read request
+ cred_t *credp) // IN: Ptr to caller's credentials
+{
+ int ret;
+ HgfsSuperInfo *sip;
+ HgfsReq *nextReq = NULL;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevRead().\n");
+
+ ASSERT(uiop);
+ ASSERT(credp);
+
+ /* These indicate a kernel error */
+ if ((!uiop) || (!credp)) {
+ cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevRead().\n");
+ return EINVAL;
+ }
+
+ sip = HgfsDevToSuperInfo(dev);
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevRead() done (FAIL).\n");
+ return ENXIO;
+ }
+
+ /*
+ * We need to check the request list to see if there is anything to read.
+ * If not, wait on the list's condition variable so we will be
+ * cv_signal()'ed when a request is placed onto the request list (by the
+ * filesystem half). Once a request is there we can remove it from
+ * the list and copy it to user space.
+ */
+ mutex_enter(&sip->reqMutex);
+
+ for (;;) {
+ /*
+ * The cv_wait_sig(9F) man page says the cv_wait*() functions can return
+ * prematurely in certain situations, typically when job control or
+ * debugging is used, so it is required that we loop around cv_wait_sig().
+ */
+ while (HgfsListIsEmpty(&sip->reqList)) {
+ DEBUG(VM_DEBUG_COMM, "HgfsDevRead: blocking ...\n");
+ if (cv_wait_sig(&sip->reqCondVar, &sip->reqMutex) == 0) {
+ /*
+ * We received a system signal (e.g., SIGKILL) while waiting for the
+ * cv_signal(). Release mutex and return appropriate error code.
+ */
+ mutex_exit(&sip->reqMutex);
+ DEBUG(VM_DEBUG_FAIL,
+ "cv_wait_sig() interrupted by signal in HgfsDevRead().\n");
+ return EINTR;
+ }
+ }
+
+ /*
+ * We hold the lock so the next request is guaranteed to be there. If
+ * the next request is still in the submitted state then we process it;
+ * otherwise the request must be in the abandoned state and we destroy it
+ * and wait for the next one.
+ */
+ nextReq = HgfsDequeueRequest(sip);
+ ASSERT(nextReq);
+ if (HgfsReqGetState(nextReq) == HGFS_REQ_SUBMITTED) {
+ break;
+ }
+
+ ASSERT(HgfsReqGetState(nextReq) == HGFS_REQ_ABANDONED);
+ HgfsDestroyReq(sip, nextReq);
+ }
+
+ DEBUG(VM_DEBUG_LIST, "HgfsDevRead received request for ID %d", nextReq->id);
+ mutex_exit(&sip->reqMutex);
+
+ /*
+ * Filesystem ensures that requests are small enough to fit in a packet sent
+ * through the backdoor.
+ */
+ ret = HgfsSendRequestToUser(nextReq, uiop);
+ /*
+ * If we couldn't copy the request to guestd, set the request's state to
+ * error and wake up the client. The filesystem will have to clean up.
+ */
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevRead: could not copy request to user (FAIL).\n");
+ HgfsReqSetState(nextReq, HGFS_REQ_ERROR);
+ HgfsWakeWaitingClient(sip, nextReq);
+ }
+
+ DEBUG(VM_DEBUG_INFO, "resid=%ld\n", uiop->uio_resid);
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevRead() done.\n");
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevWrite --
+ *
+ * Entry point for write(9E), which is invoked when a user process calls
+ * write(2) on the device.
+ *
+ * This copies in a reply header, finds the associated request, then copies
+ * the reply packet into the request packet. It handles cleaning up
+ * requests that have been abandoned.
+ *
+ * Results:
+ * Returns zero on success, an appropriate error code on failure.
+ *
+ * Side effects:
+ * The state of the request corresponding to this reply is modified.
+ * The process waiting for this reply is woken up (if it is still waiting).
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevWrite(dev_t dev, // IN: Device number
+ uio_t *uiop, // IN: Ptr to structure defining write request
+ cred_t *credp) // IN: Ptr to caller's credentials
+{
+ int ret;
+ uint32_t writeSize;
+ HgfsSuperInfo *sip;
+ HgfsReq *request;
+ HgfsReplyHeader reply;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevWrite().\n");
+
+ ASSERT(uiop);
+ ASSERT(credp);
+
+ /* These indicate a kernel error */
+ if (!uiop || !credp) {
+ cmn_err(HGFS_ERROR, "NULL input from kernel in HgfsDevWrite().\n");
+ return EINVAL;
+ }
+
+ sip = HgfsDevToSuperInfo(dev);
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): couldn't get superinfo (FAIL)\n");
+ return ENXIO;
+ }
+
+ /* We need at least the reply structure to do anything useful. */
+ if (uiop->uio_resid < sizeof reply) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): too little data written (FAIL).\n");
+ return ERANGE;
+ }
+
+ /*
+ * If this fails there is a problem with guestd or the filesystem isn't
+ * splitting requests properly. This also allows writeSize to be a uint32
+ * despite uiop->uio_resid being a ssize_t (which can be a long).
+ */
+ if (uiop->uio_resid > HGFS_PACKET_MAX) {
+ return EINVAL;
+ }
+
+ /* Needed to set packet size upon successful receipt of packet. */
+ writeSize = uiop->uio_resid;
+
+ /*
+ * Copy in just the reply header that will tell us what request this is
+ * for and what the status of the reply is.
+ */
+ ret = HgfsGetReplyHeaderFromUser(&reply, uiop);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsDevWrite(): couldn't copy header from user (FAIL).\n");
+ return EIO;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsDevWrite: Reply for %d with status %d.\n",
+ reply.id, reply.status);
+
+ /*
+ * Here we need to determine which request this reply is for, then determine
+ * if that requester is still waiting. If so we copy the contents of the
+ * reply into the request's packet and wake up the client; otherwise we
+ * destroy the abandoned request and return an error.
+ */
+ request = HgfsReplyToRequest(&reply);
+ if (!request) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): invalid id in reply (FAIL).\n");
+ return EINVAL;
+ }
+
+ /*
+ * Acquiring this mutex makes the checking of whether the request has been
+ * ABANDONED and setting the request to COMPLETED or ERROR atomic. It is
+ * also acquired in HgfsSubmitRequest().
+ */
+ mutex_enter(&sip->reqMutex);
+
+ if (HgfsReqGetState(request) == HGFS_REQ_ABANDONED) {
+ /* The requesting process is gone so we don't need to wake it up */
+ HgfsDestroyReq(sip, request);
+ mutex_exit(&sip->reqMutex);
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): request was abandoned (FAIL).\n");
+ return EINTR; /* XXX: Does guestd need to know this? */
+ }
+
+ /*
+ * Now that we know which request this reply is for, zero out its packet and
+ * copy in the reply header.
+ */
+ bzero(request->packet, sizeof request->packet);
+ memcpy(request->packet, &reply, sizeof reply);
+
+ /* Copy rest of reply into request packet after the header */
+ ret = HgfsGetReplyPacketFromUser(request, sizeof reply, uiop);
+ if (ret < 0) {
+ /*
+ * If there was an error while copying the reply packet, we set the state
+ * to error and return an error code (in case guestd cares).
+ */
+ HgfsReqSetState(request, HGFS_REQ_ERROR);
+ ret = EIO;
+ } else {
+ /* ... otherwise we set the packet size and set the state to completed. */
+ request->packetSize = writeSize - uiop->uio_resid;
+ HgfsReqSetState(request, HGFS_REQ_COMPLETED);
+ ret = 0;
+ }
+
+ /*
+ * Wake up the client waiting on the request (Note we don't call
+ * HgfsWakeWaitingClient() since we already hold the lock.)
+ */
+ cv_signal(&request->condVar);
+ mutex_exit(&sip->reqMutex);
+
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevWrite() done.\n");
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevChpoll --
+ *
+ * Entry point for chpoll(9E), which is invoked (potentially more than
+ * once) when either poll(2) or select(3C) is called by a user process.
+ * This examines the state of the device and returns information indicating
+ * which operations are ready to be performed.
+ *
+ * Results:
+ * Returns 0 on success, or an error code if an error occurs.
+ * If any of the events polled have occurred, this is specified by filling
+ * in reventsp with the appropriate bits.
+ * If none of the events polled have occurred, reventsp is cleared. phpp
+ * is also set to point to a pollhead structure if no other file
+ * descriptors polled have had events.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsDevChpoll(dev_t dev, // IN: Device number
+ short int events, // IN: Events that could occur
+ // (see man -s 9E chpoll for all
+ // events)
+ int anyyet, // IN: Flag (set if other fds have events pending)
+ short int *reventsp, // OUT: Ptr to bitmask of events satisfied
+ struct pollhead **phpp) // OUT: Ptr to a pollhead structure
+{
+ short revents;
+ HgfsSuperInfo *sip;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevChPoll(). (events=%x)\n", events);
+
+ ASSERT(reventsp);
+ ASSERT(phpp);
+
+ /* These indicate a kernel error */
+ if ((!reventsp) || (!phpp)) {
+ cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevChpoll().\n");
+ return EINVAL;
+ }
+
+ sip = HgfsDevToSuperInfo(dev);
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDevChPoll() done (FAIL).\n");
+ return ENXIO;
+ }
+
+ /*
+ * This comment is for reference, can remove in future:
+ *
+ * Algorithm to implement (chpoll(9E) and p195 DDK docs):
+ *
+ * if (events are satisfied now) {
+ * *reventsp = (mask of satisfied events);
+ * } else {
+ * *reventsp = 0;
+ * if (!anyyet) {
+ * *phpp = &(local pollhead structure);
+ * }
+ * }
+ * return 0;
+ *
+ * The pollhead structure should not be referenced by the driver.
+ */
+
+ revents = 0;
+ /* Clear flag indicating the need to call pollwakeup() on write() */
+ sip->pollwakeupOnWrite = FALSE;
+
+ /*
+ * Caller asked to read: if the request list is not empty then they can
+ * read without blocking.
+ */
+ if (events & HGFS_POLL_READ) {
+ mutex_enter(&sip->reqMutex);
+
+ if ( !HgfsListIsEmpty(&sip->reqList) ) {
+ /* Set just those bits the caller asked for */
+ revents |= (events & HGFS_POLL_READ);
+ }
+ DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): HGFS_POLL_READ, revents=%d", revents);
+
+ mutex_exit(&sip->reqMutex);
+ }
+
+ /*
+ * Caller asked to write: they can always write.
+ */
+ if (events & HGFS_POLL_WRITE) {
+ revents |= (events & HGFS_POLL_WRITE);
+ DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): HGFS_POLL_WRITE, revent=%d\n", revents);
+ }
+
+ /*
+ * No events have occurred
+ */
+ if (revents == 0) {
+ DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): no events\n");
+ *reventsp = 0;
+ if (anyyet == 0) {
+ /*
+ * No other file descriptors have had events, so we set the pollhead
+ * structure and a flag indicating the need to call pollwakeup() on
+ * a successful completion of a write().
+ */
+ *phpp = &sip->hgfsPollhead;
+ DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): setting pollwakeupOnWrite.\n");
+ sip->pollwakeupOnWrite = TRUE;
+ }
+ }
+
+ *reventsp = revents;
+
+ DEBUG(VM_DEBUG_DEVDONE, "HgfsDevChPoll() done. (revents=%x)\n", revents);
+ return 0;
+}
+
+
+/*
+ * Functions to copy to and from user space.
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSendRequestToUser --
+ *
+ * Sends a requst to the user.
+ *
+ * Results:
+ * Returns zero on success, and non-zero on error.
+ *
+ * Side effects:
+ * The uio_resid field of uiop will be decremented by the number of bytes
+ * read.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsSendRequestToUser(HgfsReq *req, // IN: Request to send
+ uio_t *uiop) // IN: User's read request
+{
+ int ret;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsSendRequestToUser().\n");
+
+ ASSERT(req);
+ ASSERT(uiop);
+
+ /*
+ * If the buffer provided by guestd is not large enough to hold the largest
+ * packet, then either the filesystem or guestd is doing something wrong.
+ * (uio_resid is the number of bytes we can write to the user's buffer.)
+ */
+ ASSERT(req->packetSize <= HGFS_PACKET_MAX);
+ ASSERT(uiop->uio_resid >= req->packetSize);
+
+ DEBUG(VM_DEBUG_INFO, "HgfsSendRequestToUser: uiomove(%p, %d, UIO_READ, %p)\n",
+ req->packet, req->packetSize, uiop);
+
+ /*
+ * uiomove(9F) will handle copying of data from kernel buffer to user buffer
+ * for us. It understands the format of the uio_t that describes the
+ * user's read request and verifies user address is valid. It returns zero
+ * on success and an error code otherwise.
+ */
+ ret = uiomove(req->packet, // Kernel src address
+ req->packetSize, // Number of bytes to transfer
+ UIO_READ, // rwflag: READ
+ uiop); // Pointer to user's read request
+
+ DEBUG(VM_DEBUG_DEVDONE,
+ "HgfsSendRequestToUser() sent %d bytes in request (ret=%d).\n",
+ req->packetSize, ret);
+
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetReplyHeaderFromUser --
+ *
+ * Copies the reply header from the user into the provided reply
+ *
+ * Results:
+ * Returns zero on success, non-zero on error.
+ *
+ * Side effects:
+ * uiop's uio_resid is decremented by the number of bytes that were copied.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static INLINE int
+HgfsGetReplyHeaderFromUser(HgfsReplyHeader *header, // OUT: Header to copy into
+ uio_t *uiop) // IN: User's write request
+{
+ ASSERT(header);
+ ASSERT(uiop);
+
+ DEBUG(VM_DEBUG_COMM,
+ "HgfsGetReplyHeaderFromUser(): writing %"FMTSZ"u bytes"
+ " into request packet's header.\n", sizeof *header);
+
+ /* Only copy in the header, uio_resid decremented for us by uiomove() */
+ return uiomove(header, sizeof *header, UIO_WRITE, uiop);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetReplyPacketFromUser --
+ *
+ * Copies the rest of the user's reply into the offset of the request's
+ * packet.
+ *
+ * Results:
+ * Returns zero on success, non-zero on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsGetReplyPacketFromUser(HgfsReq *req, // IN/OUT: Request to copy into
+ ssize_t offset, // IN: Offset in packet to
+ // start copying at
+ uio_t *uiop) // IN: User's write request
+{
+ ASSERT(req);
+ ASSERT(uiop);
+
+
+ /*
+ * If guestd has more to write than the max room left in the packet then it
+ * is doing something wrong.
+ */
+ ASSERT(uiop->uio_resid <= HGFS_PACKET_MAX - offset);
+
+ DEBUG(VM_DEBUG_COMM,
+ "HgfsGetReplyPacketFromUser(): writing %ld bytes into request packet.\n",
+ uiop->uio_resid);
+
+ /* Write the reply into the specified buffer */
+ return uiomove(req->packet + offset, uiop->uio_resid, UIO_WRITE, uiop);
+}
+
+
+
+/*
+ * Utility functions
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevToSuperInfo --
+ *
+ * Gets a pointer to the super info structure from a device number (dev_t).
+ *
+ * Results:
+ * Returns a pointer to the super info struct, or NULL if it cannot be
+ * found.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static INLINE HgfsSuperInfo *
+HgfsDevToSuperInfo(dev_t dev) // IN: Device number
+{
+ minor_t instance;
+
+ DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevToSuperInfo().\n");
+
+ instance = getminor(dev);
+
+ DEBUG(VM_DEBUG_RARE,
+ "HgfsDevToSuperInfo: getting ptr to instance %d's state.\n", instance);
+
+ return (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, (int)instance);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDevinfoToSuperInfo --
+ *
+ * Gets a pointer to the super info from a device information struct.
+ * XXX: currently unused since HgfsDevDetach needs value of instance
+ * itself.
+ *
+ * Results:
+ * Returns a pointer to the super info structure, or NULL if it cannot be
+ * found.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static INLINE HgfsSuperInfo *
+HgfsDevinfoToSuperInfo(dev_info_t *dip) // IN: Device information structure
+{
+ int instance;
+
+ ASSERT(dip);
+
+ instance = ddi_get_instance(dip);
+
+ DEBUG(VM_DEBUG_RARE,
+ "HgfsDevinfoToSuperInfo: getting ptr to instance %d's state.\n", instance);
+
+ return (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, instance);
+}
+
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsReplyToRequest --
+ *
+ * Determines the request that corresponds with the provided reply.
+ *
+ * Results:
+ * Returns a pointer to the request, or NULL if it cannot be located.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static INLINE HgfsReq *
+HgfsReplyToRequest(HgfsReplyHeader *reply) // IN: Reply to find request of
+{
+ ASSERT(reply);
+
+ if (reply->id > HGFS_MAX_OUTSTANDING_REQS) {
+ return NULL;
+ }
+
+ /*
+ * When this function is called, the request should only be in the
+ * SUBMITTED, ABANDONED, or ERROR states.
+ */
+ ASSERT((HgfsReqGetState(&requestPool[reply->id]) == HGFS_REQ_SUBMITTED) ||
+ (HgfsReqGetState(&requestPool[reply->id]) == HGFS_REQ_ABANDONED) ||
+ (HgfsReqGetState(&requestPool[reply->id]) == HGFS_REQ_ERROR));
+
+ DEBUG(VM_DEBUG_INFO, "HgfsRequestToReply: reply for request id %d\n", reply->id);
+
+ return &requestPool[reply->id];
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * device.h --
+ *
+ * Definitions and includes for device driver code.
+ *
+ */
+
+
+#ifndef __DEVICE_H_
+#define __DEVICE_H_
+
+#include <sys/open.h> /* OTYP_CHR */
+#include <sys/stat.h> /* S_IFCHR */
+#include <sys/ddi.h> /* Device Driver Interface */
+#include <sys/sunddi.h> /* Sun DDI defines (DDI_PSEUDO) */
+#include <sys/ksynch.h> /* mutex and condition variables */
+
+#include "debug.h"
+
+/*
+ * Macros
+ */
+
+/* Flags for chpoll(): we don't distinguish between these priorities of data */
+#define HGFS_POLL_READ (POLLIN | POLLRDNORM | POLLRDBAND)
+#define HGFS_POLL_WRITE (POLLOUT | POLLWRNORM | POLLWRBAND)
+
+
+#endif /* __DEVICE_H_ */
--- /dev/null
+Solaris Hgfs Build Instructions
+===============================
+
+Last Updated: 11/22/2005 (biggadike@vmware.com)
+
+Hgfs Kernel Module
+------------------
+0. One-time Guest Operations
+1. Building with the Linux cross-compiler (Solaris 9)
+2. Building from the Guest (Solaris 10)
+
+Tools
+-----
+0. Building guestd and the toolbox
+1. Running guestd
+2. Running the toolbox
+
+
+Hgfs Kernel Module
+------------------
+
+ 0. One-time Guest Operations
+ -------------------------
+ Requirements: just a Solaris 9 or 10 guest
+
+ o First, to build you'll need to make sure gmake, gcc, and binutils are
+ installed. The easiest way to install these is to use the pkg-get script
+ from http://www.blastwave.org/pkg-get.php. Follow the instructions there
+ then do:
+
+ # pkg-get -U
+ # pkg-get -i gmake gcc2 binutils
+ # export PATH=$PATH:/opt/csw/bin (for Bash)
+ # alias make='gmake' (for Bash)
+
+ o You'll need to add an entry to the device link table so that when the driver
+ is loaded a symlink called /dev/vmware-hgfs is created that points to the
+ entry in the Kernel's device tree (/devices).
+
+ # cat docs/append-to-devlink.tab >> /etc/devlink.tab
+
+ o You'll also need a copy of the driver's configuration file where the kernel
+ looks for it.
+
+ # cp docs/hgfs.conf /usr/kernel/drv
+
+ o You also need to create the directory where guestd will mount the
+ filesystem:
+
+ # mkdir -p /mnt/hgfs
+
+ These only need to be done once.
+
+
+ 1. Building with the Linux cross-compiler (Solaris 9)
+ -----------------------------------------------------
+
+ Note that the cross-compiler is currently only for Solaris 9.
+
+ o From top of bora-vmsoft:
+
+ # make tools-for-solaris
+
+ o Respected build variables:
+ VMX86_DEVEL : gets passed on to code
+ VMX86_DEBUG : enables assertion checking
+ VMX86_LOG : enables logging
+ SOL_VERSION : defaults to 9 because the Solaris 10 cross-compiler doesn't
+ exist yet
+
+ o Installing Targets (run these from the guest):
+ rem : removes the driver from the system
+ install : installs the driver and driver config file to the proper
+ directories
+ add : adds the driver to the system
+ rotate : invokes rem, install, then add.
+
+ Note that most of the time rotate will take care of everything for you, but if
+ guestd is running this may cause problems because the driver will be added
+ even if the module cannot be removed. Just kill guestd then make rotate.
+
+
+ 2. Building from the Guest (Solaris 10)
+ ---------------------------------------
+
+ Note that this is currently the only way to build in Solaris 10.
+
+ o Make sure the BORA, BUILD, and BUILDROOT variables are set properly in the
+ Makefile.guest.
+
+ It's likely all you need know is:
+ make -f Makefile.guest module
+ make -f Makefile.guest install add
+
+ If you want to disable debugging, use:
+ make -f Makefile.guest module DEBUG=OFF
+
+ If you get a "module already loaded" error, try:
+ make -f Makefile.guest rotate
+
+ o Building targets:
+ module : builds only the Kernel module (current default)
+ clean : cleans build files
+
+ o Building variables:
+ DEBUG=OFF : turns off all debugging output from the module (faster!)
+ ASSERT=OFF : turns off ASSERT()s
+ SOL_VERSION : defaults to 10 and automatically determines which build number
+ this is with uname -v (52 and 58 are the only builds tested)
+
+ o Installing Targets (run these from the guest):
+ rem : removes the driver from the system
+ install : installs the driver and driver config file to the proper
+ directories
+ add : adds the driver to the system
+ rotate : invokes rem, install, then add.
+ once : appends necessary lines to /etc/devlink.tab (only needed once)
+ initbuilddir: creates the build directory
+
+
+Tools
+-----
+
+ 0. Building guestd and the toolbox
+ ------------------------------
+
+ o guestd has been ported to Solaris and is built using the Solaris 9 cross
+ compiler mentioned above. The toolbox has not been ported to Solaris and is
+ run using lxrun. Building both is handled by the Makefiles in bora-vmsoft:
+
+ # cd /path/to/bora-vmsoft
+ # make tools-for-solaris
+
+ The binaries will be located at $(BUILD)/obj/ws/tools-for-solaris/guestd/guestd
+ and $(BUILD)/obj/ws/tools-for-solaris/toolbox/toolbox.
+
+ 1. Running guestd
+ -----------------
+
+ o guestd runs natively on Solaris 9. It will mount Hgfs at /mnt/hgfs and your
+ "Shared Folders" will be visible there.
+
+ o I have not been able to use the bora-vmsoft Makefile infrastructure from
+ within a Solaris 10 guest, so it is not currently possible to build guestd
+ for Solaris 10. Porting the Makefiles to run inside the guest should not be
+ too difficult and a Solaris 10 cross compiler would allow guestd to be built
+ for Solaris 10.
+
+ 2. Running the toolbox
+ ----------------------
+
+ o It is first necessary to set up lxrun on the guestd
+
+ Setting up lxrun
+ ----------------
+
+ o lxrun enables you to run Linux binaries in Solaris by catching and
+ translating all of their system calls. This works relatively well for
+ running guestd and toolbox, but you must install lxrun and build versions of
+ the programs that have been "ported" to lxrun.
+
+ o Install lxrun on the guest:
+
+ The easiest way is to see if ~biggadike/public_html/lxrun/lxrun-environ.tgz
+ exists. If so, extract this to the root of your guest's filesystem:
+
+ # cd /
+ # gunzip /path/to/lxrun-environ.tgz
+ # tar xvf /path/to/lxrun-environ.tar
+
+ This will create two directories: /lx and /usr/local/lxrun. /lx contains
+ a linux environment (circa Red Hat 6.2) and the lxrun executable.
+ /usr/local/lxrun contains a file called PATHMAP which tells lxrun where to
+ get its environment files from (/lx).
+
+ If the precompiled environment doesn't work, you'll need to get lxrun
+ 0.9.6pre1, compile it, and then use a Red Hat 6.2 Installation CD with the
+ INSTALL-RH6 script in the lxrun directory.
+
+ o Try ~biggadike/public_html/lxrun/lxrun-0.9.6pre1.tar.gz or
+ http://www.ugcs.caltech.edu/~steven/lxrun/ for the lxrun source
+ o Try exit15:/home/ISO-Images/vol1/redhat/RedHat6.2.iso for the Red Hat disc
+
+ **Note that it is not necessary to patch lxrun for the toolbox. The patch
+ in the directory mentioned above is to enable lxrun to run guestd, which
+ is no longer necessary since it has been ported. The lxrun binary in
+ the lxrun-environ.tgz file is a patched version, but this does also work
+ for running the toolbox.
+
+ o Set necessary environment variables:
+
+ You will also need a few environment variables set. For bash, add these
+ lines to your .bashrc:
+
+ export LXRUN_ROOT=/lx
+ export PATH=$PATH:/lx
+
+ o The following steps outline running the toolbox with lxrun
+
+ Running the toolbox
+ -------------------
+
+ o Just copy (or make available) the above binaries on your guest and invoke
+ them with lxrun:
+
+ # lxrun /path/to/toolbox &
+
+ (It is usually best to background this process.)
+
+ o lxrun can also log the system calls it translates using its trace feature:
+
+ # lxrun -t all /path/to/toolbox
+
+ This will trace all the system calls guestd makes. The output file is always
+ placed in /tmp/lxrun.<pid>. You can also restrict the system calls that are
+ traced to a particular subset, run lxrun without any options to see the usage.
+
+ It also appears possible to chroot the running process with -r, to specify the
+ linux root directory with -R, and specify the uname of the emulated Linux
+ system with -U. I haven't ever needed to use these options.
+
+ o Mouse grab/ungrab and device connection/disconnection are both known to work.
+
+
+
+
+Notes from the old BUILD file that may still be helpful
+-------------------------------------------------------
+
+ o You can verify the module is installed with:
+
+ # modinfo | grep hgfs
+
+ This should output text of this format:
+ <id#> <loadaddr> <size> <info> <revision#> hgfs (HGFS Device Interface)
+ <id#> <loadaddr> <size> <info> <revision#> hgfs (Host/Guest Filesystem)
+
+ There are two entries for the same ID number because we've placed both the
+ device driver and the filesystem in a single loadable kernel module.
+
+ o You can load the driver into the Kernel by hand with:
+
+ # /usr/sbin/add_drv -v -m '* 0644 root sys' hgfs
+
+ This should output something similar to:
+ devfsadm[1032]: verbose: mknod /devices/pseudo/hgfs@0:vmware-hgfs 0l/3l/20644
+ devfsadm[1032]: verbose: symlink /dev/vmware-hgfs -> ../devices/pseudo/hgfs@0:vmware-hgfs
+
+ o You can remove the driver from the system by hand with:
+
+ # /usr/sbin/rem_drv hgfs
--- /dev/null
+Files
+=====
+
+Driver (hgfs/solaris/)
+----------------------------------
+hgfsSolaris.h - definitions global to all parts of module
+module.{c,h} - module initialization routines (_init(), _fini()) and
+ structures (struct modlinkage, struct modlfs, ...)
+filesystem.{c,h} - filesystem level functions (mount(), unmount())
+hgfsState.{c,h} - maintains internal state of filesystem (HgfsOpenFile, HgfsFile)
+vnode.c - file level functions (open(), read(), close())
+device.{c,h} - device interface (attach(), detach(), open(), read(),
+ close())
+request.{c,h} - functions that initialize, allocate, and manipulate requests
+ and the lists that they are on
+debug.{c,h} - debugging routines and macros
+
+Misc (hgfs/solaris/docs)
+------------------------------------
+hgfs.conf - must be place in /usr/kernel/drv/
+append-to-devlink.tab - must append this text to /etc/devlink.tab to generate
+ symlink /dev/vmware-hgfs
+
+Docs (hgfs/solaris/docs)
+------------------------------------
+BUILD - build instructions
+synchronization.txt - outline of how components are synchronized
+request-lifecycle.txt - outline of how requests travel through driver
+kadb.txt - cheat sheet for Solaris' kernel debugger
+hgfs-9-to-10.txt - notes for porting from Solaris 9 to 10
--- /dev/null
+type=ddi_pseudo;name=vmhgfs;addr=0;minor=vmware-hgfs \M0
--- /dev/null
+Notes for porting Hgfs from Solaris 9 to 10
+===========================================
+
+Note that Solaris 10 is a beta and still in development, so these notes may
+become outdated. They are based on the headers in build 52.
+
+vnode.h
+-------
+o Now use vn_make_ops() to create vnodeops
+ int vn_make_ops(const char *, const fs_operation_def_t *, vnodeops_t **);
+ void vn_freevnodeops(vnodeops_t *);
+
+o Now use vfs_makefsops()/vfs_setfsops() to create vfsops
+ (see vfs.h notes)
+
+o There is a new structure struct fs_operation_def_t:
+
+ typedef int (*fs_generic_func_p) ();
+
+ typedef struct fs_operation_def {
+ char *name; /* name of operation (NUL terminated) */
+ fs_generic_func_p func; /* function implementing operation */
+ } fs_operation_def_t;
+
+ The name is likely one of the VOPNAME_* macros.
+
+o vnodes should only be created by calls to vn_alloc(); they may not be embedded
+ in fs-specific structures; vnodes may change size. Definitions for related
+ vnode allocation/freeing functions are:
+ vnode_t *vn_alloc(int);
+ void vn_reinit(vnode_t *);
+ void vn_free(vnode_t *);
+
+ Note that the vn_free() function destroys the vnode's mutex so doing so
+ yourself will crash the system.
+
+o There are a quite a few new vnode manipulation functions; see vnode.h.
+
+o v_vfsmountedhere member of struct vnode is private, perhaps we shouldn't touch
+ it; it is also protected by vn_vfswlock in vnode.c
+
+o A number of private members were added to struct vnode -- shouldn't affect us
+
+o New structure struct caller_context contains PID of caller, system ID, and
+ a caller identifier. This structure is now passed in as the last argument to:
+ o vop_read
+ o vop_write
+ o vop_setattr
+ o vop_rwlock
+ o vop_rwunlock
+ o vop_space
+
+o vop_shrlock now takes an additional cred_t * argument as its last argument
+
+o A new vnodeop was added:
+ int (*vop_vnevent)(vnode_t *, vnevent_t);
+
+ This likely has to do "something" to the vnode depending on the value of
+ vnevent_t. It is likely that this is required since the vnode now stores the
+ cached path of the file in its private char *v_path member. Can we use
+ vnevent_*() for help, or do these call us? How about the new vn_setpath*(),
+ vn_path(), and vn_copypath() functions? (We should use the VN_SETPATH() macro
+ instead of calling vn_setpath() directly.)
+
+o vnevent_t is a new enum with the following definition:
+ typedef enum vnevent {
+ VE_SUPPORT = 0, /* Query */
+ VE_RENAME_SRC = 1, /* Rename, with vnode as source */
+ VE_RENAME_DEST = 2, /* Rename, with vnode as target/dest */
+ VE_REMOVE = 3, /* Remove of vnode's name */
+ VE_RMDIR = 4 /* Remove of directory vnode's name */
+ } vnevent_t;
+
+o A new macro VN_SET_VFS_TYPE_DEV(vp, vfsp, type, dev) was created, but it is
+ trivial.
+
+o VN_CMP() now calls vn_getops() rather than dereferencing the vnode's vn_ops
+ member. (We should use this to access that variable too, it seems.)
+
+o There is no more VN_INIT() macro, but it was trivial anyhow.
+
+
+vfs.h
+-----
+o Filesystems now must supply their list of vfs operations using:
+ int vfs_setfsops(int, const fs_operation_def_t *, vfsops_t **);
+
+o There are also functions for filesystems to make, free, etc vfs operation
+ structures:
+ int vfs_makefsops(const fs_operation_def_t *, vfsops_t **);
+ void vfs_freevfsops(vfsops_t *);
+ int vfs_freevfsops_by_type(int);
+ void vfs_setops(vfs_t *, vfsops_t *);
+ vfsops_t *vfs_getops(vfs_t *vfsp);
+ <there are some more: see vfs.h>
+
+ The names placed in fs_operation_def_t->name are likely one of the VFSNAME_*
+ macros.
+
+o This isn't relevant to our layer, but the vfs now keeps filesystems in
+ a double linked circular list rather than a singly linked list. There are
+ also issues with zones: each zone has a list of its own mounts.
+
+o The vfs_op member of struct vfs should never be directly accessed; use the
+ accessor functions described above.
+
+o There is a new function in struct vfsops:
+ int (*vfs_vnstate)(vfs_t *, vnode_t *, vntrans_t);
+
+o vntrans_t is a new enum that is either VNTRANS_EXISTS, VNTRANS_IDLED,
+ VNTRANS_RECLAIMED, or VNTRANS_DESTROYED.
+
+o The function pointer vsw_init member of struct vfssw now has the definition:
+ int (*vsw_init)(int, char *)
+
+ Also, the vsw_optproto and vsw_vfsops members are no longer pointers.
+
+ Since we will not be directly including this structure in our modlfs anymore
+ (see modctl.h notes), these differences probably don't matter.
+
+o There is a new structure (that will be used in our modlfs now instead of
+ struct vfssw).
+
+ typedef struct vfsdef_v2 {
+ int def_version; /* structure version, must be first */
+ char *name; /* filesystem type name */
+ int (*init) (int, char *); /* init routine */
+ int flags; /* fs flags */
+ mntopts_t *optproto; /* mount options table prototype */
+ } vfsdef_v2;
+
+ Note that build 58 contains the same structure but it is called vfsdef_v3.
+
+o There are a few other additional macros and functions that likely won't matter
+ to us.
+
+modctl.h
+--------
+o struct modlfs now has a struct vfsdef_v3 instead of a struct vfssw
+o other changes don't seem relevant
+
+devops.h
+--------
+o There are no changes to struct cb_ops, struct dev_ops, or any of the command
+ enums taken by these functions.
--- /dev/null
+# name=<driver name> parent="pseudo" instance=0;
+name="vmhgfs" parent="pseudo" instance=0;
+
--- /dev/null
+Solaris kadb cheat sheet
+=========================
+
+Setting up
+----------
+ See Chapter 18 of Solaris' 9 "Writing Device Drivers"
+ (http://docs.sun.com/db/doc/806-5222)
+
+ If using a tip connection, boot with 'b kadb -d' and at the break for __start,
+ create a deferred breakpoint with this command: hgfs# HgfsDevAttach:b
+
+Breakpoints
+-----------
+ Deferred: <module># <symbol>:b
+ Regular : <symbol>:b
+
+ - Regular will complain if symbol isn't found.
+
+ Examples:
+ hgfs# HgfsDevAttach:b --> deferred breakpoint for HgfsDevAttach
+ takes effect when hgfs module is loaded
+ HgfsGetNewReq:b --> breakpoint for HgfsGetNewReq
+
+ Commands at breakpoints:
+ :c : continue
+ :s : single step
+ :e : single step over function calls
+ :u : stop after return to caller of current function
+ :d : delete breakpoint
+ :z : delete allbreakpoints
+
+
+Viewing
+-------
+
+ <symbol>,<count>/<modifier>
+
+ <symbol> : symbol name, as in HgfsDevAttach, or address
+ <count> : number of items to show
+ <modifier>: see pg. 348 "Writing Device Drivers" for complete list
+ Common:
+ d,D : 2,4-byte decimal
+ x,X : 2,4-byte hex
+ K : 4-byte hex for 32 bit progs (use this for pointers)
+ u,U : 2,4-byte unsigned decimal
+ c,C : character, without or with ^escape notation
+ s,S : string, without or with ^escape notation
+ i : instructions
+ a : prints address in symbolic form
+ w,W : 2,4-byte write
+
+ Examples:
+ HgfsGetNewReq,24/ai --> see first 24 instructions of HgfsGetNewReq
+ 0x01234567,24/X --> see 24 4-byte words at that address in hex
+ *0x1234567,24/X --> see the 24 4-byte words at memory address
+ specified in 0x1234567
+
+Macros
+------
+$r : view registers
+$c : stack trace
+$b : display breakpoints
+
+
+Pager (pause after <lines> lines)
+-----
+ 0t<lines>::more
+
+
+Registers
+---------
+
+ Display a single register: <(register)=K
+ Set value of a register : (value)>(register)
+
+ Examples:
+ <eip=K Display the current contents of eip
+ 0xdf78b1f8>eip Set the value of eip to 0xdf78b1f8
--- /dev/null
+The lifecycle of a request in the Solaris HGFS code
+===================================================
+
+Note that this design is based almost entirely on the Linux HGFS code.
+
+Data Structures
+---------------
+- A preallocated request pool array. (requestPool[])
+- A list of the current free requests. (reqFreeList)
+- A list of the currently pending requests (reqList)
+
+Request States
+--------------
+- Unused:
+ This is a request's initial state and it guarantees that the
+ request is on the free request list. Requests are placed back in
+ this state after the Abandoned, Error, or Completed states.
+
+- Allocated:
+ The request has been allocated to a client and is not on any of
+ the lists.
+
+- Submitted:
+ The request has been filled out and has been placed on the pending
+ request list. The request may leave the pending request list while
+ still in this state.
+
+- Abandoned:
+ The client was interrupted while waiting for the reply and has
+ left the request to be cleaned up when a reply comes. The request
+ is either still on the pending list or is not on any list, and will
+ be accessed by its index into the requestPool array.
+
+- Error:
+ An error occurred while submitting the request or awaiting the
+ reply. The request is not on any lists.
+
+- Completed:
+ The reply for this request has been received correctly. The
+ request is not on any lists.
+
+
+State Transition
+-----------------
+
+Each request starts as Unused and is then Allocated and Submitted. After the
+request is submitted, the request can be Abandoned, can have an Error occur, or
+can Complete successfully. Each of these states transitions back to the Unused
+state.
+
+--> Unused --> Allocated --> Submitted ---> Abandoned ------> Unused
+ \ /
+ --> Error -------->
+ \ /
+ -> Completed -->
+
+Functions
+---------
+These functions will affect the state of requests and their locations on the
+lists.
+
+- HgfsInitRequestList():
+ This will initialize each request's state to UNUSED and ID to its index into
+ the requestPool array. Each request will also be added to the free request
+ list (reqFreeList).
+
+- HgfsCancelAllRequests():
+ Iterates over the pending request list and the request pool and cancels each
+ request. If a request is abandoned it must clean up for the interrupted
+ process; if the requester is waiting, set the request state to error and wake
+ up the client.
+
+- HgfsGetNewReq():
+ Takes an UNUSED request off the free list (reqFreeList) and sets its state
+ to ALLOCATED. The request will not be on any lists after this function is
+ completed.
+
+- HgfsEnqueueRequest():
+ Puts an ALLOCATED request on to the pending request list (reqList) and sets
+ its state to SUBMITTED. The request should not be on any of the lists when
+ this function is called. This function will also assume that the list lock
+ is held since most callers of this function will need to enqueue and do
+ other operations atomically.
+
+- HgfsDequeueRequest():
+ Takes a SUBMITTED or ERROR request from the pending request list (reqList).
+ The request will not be on any lists after this function is completed. For
+ the reason noted above, this function also assumes the list lock is held by
+ the caller.
+
+- HgfsDestroyReq():
+ Puts a COMPLETED, ERROR, or ABANDONED request on to the free list
+ (reqFreeList) and sets its state to UNUSED. The request should not be on
+ any lists when this function is called.
+
+- HgfsReqSetState():
+ Will set the state of the specified request to the specified value. This
+ abstraction is to help prevent forgetting to lock the state's mutex.
+
+- HgfsReqGetState():
+ Will return the state of the specified request. This abstraction is to help
+ prevent forgetting to lock the state's mutex.
+
+
+Submitting a request
+--------------------
+
+{
+ request = HgfsGetNewReq(superinfo);
+ if (request is NULL) {
+ return error
+ }
+
+ Fill in specific request information in request->packet
+ - header.id and header.op of specific request struct
+ should be set
+ - request specific information should be filled in
+ - request->packetSize should be set
+
+ if (HgfsSubmitRequest(superinfo, request) fails) {
+ return error
+ }
+
+ if (HgfsValidateReply(request, minimum size) fails) {
+ HgfsDestroyReq(superinfo, request);
+ return error
+ }
+
+ Process reply and give to client
+
+ HgfsDestroyReq(superinfo, request);
+}
--- /dev/null
+Solaris HGFS Synchronization
+============================
+
+The Solaris synchronization primitives that will be used are mutexes and
+condition variables. Mutexes will protect access to shared resources
+and condition variables will synchronize the order of the operations by
+those accessing these resources.
+
+Synchronization must be achieved in two directions. First, a client
+that requests an operation on a HGFS file must wait for the reply from
+the HGFS server. Second, guestd must wait for requests to be made when
+it queries the HGFS device and there are no pending requests.
+
+To enable this synchronization, the superinfo structure will contain
+these elements:
+- a list of outgoing requests (reqList)
+- a condition variable for waiting until and signaling when requests are put
+ on an empty list (reqCondVar)
+- a flag which indicates whether guestd is waiting for a request
+ (guestdWaiting)
+- a mutex to protect access to these components (reqMutex)
+
+Additionally, each request structure should contain:
+- a condition variable for waiting for and signaling of replies to
+ /this/ request (condVar)
+- a flag indicating whether the requesting thread has been interrupted
+ and is no longer waiting for the reply (abandoned)
+
+The filesystem half of the driver will place requests onto reqList with
+the following pseudocode:
+{
+ Acquire and initialize request structure from free request list
+ mutex_enter(superinfo's reqMutex)
+
+ HgfsEnqueueRequest(request list, request)
+
+ if (superinfo's guestdWaiting is set) {
+ cv_signal(superinfo's reqCondVar)
+ }
+
+ cv_wait_sig(request's condVar, superinfo's reqMutex)
+ if (cv_wait_sig() was interrupted) {
+ request's abandoned = TRUE
+ release mutex and return EINTR
+ }
+
+ mutex_exit(superinfo's reqMutex)
+
+ Read reply in request's packet
+ When done, put request structure back on free list
+}
+
+Note that the calls to cv_signal() and cv_wait_sig() above must be atomic,
+otherwise a reply could come and incur a signal before cv_wait_sig() is
+called. Therefore, the superinfo's reqMutex must also be passed to the
+cv_wait_sig() on the request structure's condition variable.
+
+The device half of the driver will check for and take requests of the
+request list with the following pseudocode (HgfsDevRead()):
+{
+ mutex_enter(superinfo's reqMutex)
+
+ if (request list is empty) {
+ superinfo's guestdWaiting = TRUE
+ cv_wait_sig(superinfo's reqCondVar, superinfo's reqMutex)
+ if (cv_wait_sig() was interrupted) {
+ superinfo's guestdWaiting = FALSE
+ release mutex and return EINTR
+ }
+ superinfo's guestdWaiting = FALSE
+ }
+
+ Take next request off request list
+
+ mutex_exit(superinfo's reqMutex)
+
+ Submit request to guestd
+}
+
+When a reply is received, the device half of the driver will handle it
+with the following pseudocode (HgfsDevWrite()):
+{
+ Read in and verify the reply from guestd
+ Get pointer to the request structure from the handle in reply
+ Copy reply payload to the request packet
+
+ mutex_enter(superinfo's reqMutex)
+ if (request's abandoned is set) {
+ Clean up on behalf of interrupted thread
+ - place request back on free list
+ return <guestd doesn't care about this "error">
+ }
+ cv_signal(request's condVar)
+ mutex_exit(superinfo's reqMutex)
+}
+
+
+
+
+
+
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * filesystem.c --
+ *
+ * Implementation of the filesystem level routines. These include functions
+ * that intialize, mount, unmount, and provide other various filesystem
+ * information.
+ */
+
+#include <sys/param.h> /* MAXNAMELEN */
+
+#include "hgfsSolaris.h"
+#include "hgfsState.h"
+#include "filesystem.h"
+#include "vnode.h"
+#include "request.h"
+#include "debug.h"
+
+/*
+ * Prototypes
+ *
+ * These are only needed because Solaris 10 requires that we create the vfsops
+ * structure through their provided interface (vfs_setfsops()).
+ */
+#if HGFS_VFS_VERSION > 2
+int HgfsMount(struct vfs *vfsp, struct vnode *vnodep,
+ struct mounta *mntp, struct cred *credp);
+int HgfsUnmount(struct vfs *vfsp, int mflag, struct cred *credp);
+int HgfsRoot(struct vfs *vfsp, struct vnode **vnodepp);
+int HgfsStatvfs(struct vfs *vfsp, struct statvfs64 *stats);
+int HgfsSync(struct vfs *vfsp, short flags, struct cred *credp);
+int HgfsVget(struct vfs *vfsp, struct vnode **vnodepp, struct fid *fidp);
+int HgfsMountroot(struct vfs *vfsp, enum whymountroot reason);
+int HgfsVnstate(vfs_t *vfsp, vnode_t *vp, vntrans_t trans);
+#if HGFS_VFS_VERSION == 3
+int HgfsFreevfs(struct vfs *vfsp);
+#else
+void HgfsFreevfs(struct vfs *vfsp);
+#endif
+#endif
+
+
+#if HGFS_VFS_VERSION == 2
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsInit --
+ *
+ * This is the filesystem initialization routine which is run when the
+ * filesystem is placed in the VFS switch table.
+ *
+ * Results:
+ * Returns zero on success.
+ *
+ * Side effects:
+ * The filesystem type (index number) is set to the provided value.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsInit(struct vfssw *vfsswp, // IN: VFS Switch table struct
+ int fstype) // IN: Index into the vfssw table for this filesystem
+{
+ if (!vfsswp) {
+ cmn_err(HGFS_ERROR, "HgfsInit: received NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsInit().\n");
+
+ /*
+ * Hook VFS operations into switch table and save
+ * filesystem type number and pointer to vfsswp.
+ */
+ vfsswp->vsw_vfsops = &HgfsVfsOps;
+ hgfsType = fstype;
+
+ mutex_init(&vfsswp->vsw_lock, NULL, MUTEX_DRIVER, NULL);
+
+ DEBUG(VM_DEBUG_LOAD, "fstype: %d\n", hgfsType);
+ HgfsDebugPrintVfssw("HgfsInit()", vfsswp);
+
+ DEBUG(VM_DEBUG_DONE, "HgfsInit() done.\n");
+
+ return 0;
+}
+
+#else /* Implies Solaris > 9 */
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsInit --
+ *
+ * This is the filesystem initialization routine for Solaris 10. It creates
+ * an array of fs_operation_def_t for all the vfs operations, then calls
+ * vfs_setfsops() to assign them to the filesystem properly.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsInit(int fstype, // IN: Filesystem type
+ char *name) // IN: Name of the filesystem
+{
+ int ret;
+
+ /* Construct the VFS operations array to give to vfs_setfsops() */
+ fs_operation_def_t vfsOpsArr[] = {
+ HGFS_VOP(VFSNAME_MOUNT, vfs_mount, HgfsMount),
+ HGFS_VOP(VFSNAME_UNMOUNT, vfs_unmount, HgfsUnmount),
+ HGFS_VOP(VFSNAME_ROOT, vfs_root, HgfsRoot),
+ HGFS_VOP(VFSNAME_STATVFS, vfs_statvfs, HgfsStatvfs),
+ HGFS_VOP(VFSNAME_VGET, vfs_vget, HgfsVget),
+ HGFS_VOP(VFSNAME_MOUNTROOT, vfs_mountroot, HgfsMountroot),
+ HGFS_VOP(VFSNAME_FREEVFS, vfs_freevfs, HgfsFreevfs),
+ HGFS_VOP(VFSNAME_VNSTATE, vfs_vnstate, HgfsVnstate),
+#if HGFS_VFS_VERSION <= 3
+ HGFS_VOP(VFSNAME_SYNC, vfs_vnstate, (fs_generic_func_p)HgfsSync),
+ { NULL, NULL }
+#else
+ HGFS_VOP(VFSNAME_SYNC, vfs_sync, HgfsSync),
+ { NULL, { NULL }},
+#endif
+ };
+
+ if (!name) {
+ cmn_err(HGFS_ERROR, "HgfsInit: received NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsInit: fstype=%d, name=\"%s\"\n", fstype, name);
+
+ /* Set our filesystem type. */
+ hgfsType = fstype;
+
+ /* Assign VFS operations to our filesystem. */
+ ret = vfs_setfsops(hgfsType, vfsOpsArr, NULL);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsInit: vfs_setfsops returned %d\n", ret);
+ return ret;
+ }
+
+ DEBUG(VM_DEBUG_DONE, "HgfsInit: done. (fstype=%d)\n", hgfsType);
+
+ return 0;
+}
+
+#endif
+
+
+/*
+ * VFS Entry Points
+ */
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsMount --
+ *
+ * This function is invoked when mount(2) is called on our filesystem.
+ * The filesystem is mounted on the supplied vnode.
+ *
+ * Results:
+ * Returns zero on success and an appropriate error code on error.
+ *
+ * Side effects:
+ * The filesystem is mounted on top of vnodep.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsMount(struct vfs *vfsp, // IN: Filesystem to mount
+ struct vnode *vnodep, // IN: Vnode that we are mounting on
+ struct mounta *mntp, // IN: Arguments to mount(2) from user
+ struct cred *credp) // IN: Credentials of caller
+{
+ HgfsSuperInfo *sip;
+ HgfsMountData *mountData;
+ int ret;
+ dev_t dev;
+
+ if (!vfsp || !vnodep || !mntp || !credp) {
+ cmn_err(HGFS_ERROR, "HgfsMount: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsMount().\n");
+
+ //HgfsDebugPrintVfs("HgfsMount", vfsp);
+ //HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsMount", vnodep);
+ //HgfsDebugPrintMounta("HgfsMount", mntp);
+ //HgfsDebugPrintCred("HgfsMount", credp);
+
+ /*
+ * Initial sanity checks to ensure that:
+ * - device side has allocated our superinfo struct (see HgfsDevAttach())
+ * - the device has been sucecssfully opened
+ * - caller is root
+ * - data provided by caller is the expected size
+ *
+ * Note that this is the one place in the filesystem that we call
+ * ddi_get_soft_state() directly to get the superinfo pointer. All other
+ * places use the HgfsGetSuperInfo() wrapper which only returns the
+ * superinfo if the filesystem is in fact mounted.
+ */
+ sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, hgfsInstance);
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMount: superinfo has not been allocated/set\n");
+ return ENXIO;
+ }
+
+ if (!sip->devOpen) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMount: device has not been opened\n");
+ return ENXIO;
+ }
+
+ if (!HgfsSuser(credp)) {
+ return EPERM;
+ }
+
+ if (mntp->datalen != sizeof *mountData) {
+ return EINVAL;
+ }
+
+ /*
+ * If mount data is in kernel space, just cast pointer; otherwise we need
+ * to safely copy it from user space.
+ */
+ if (mntp->flags & MS_SYSSPACE) {
+ mountData = (HgfsMountData *)mntp->dataptr;
+ DEBUG(VM_DEBUG_LOAD, "HgfsMount(): mount data was in kernel space.\n");
+ } else {
+ mountData = (HgfsMountData *)kmem_zalloc(sizeof *mountData, HGFS_ALLOC_FLAG);
+# if HGFS_ALLOC_FLAG != KM_SLEEP
+ /* This check is not needed if HGFS_ALLOC_FLAG == KM_SLEEP */
+ if (!mountData) {
+ return ENOMEM;
+ }
+# endif
+
+ if (ddi_copyin(mntp->dataptr, mountData,
+ sizeof *mountData, HGFS_COPYIN_FLAGS) == -1) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't copy in mount data.\n");
+ ret = EFAULT;
+ goto out;
+ }
+ }
+
+ /*
+ * Make sure mount data matches what guestd will send us.
+ */
+ if (mountData->magic != HGFS_MAGIC) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMount: received invalid magic value: %x\n",
+ mountData->magic);
+ ret = EINVAL;
+ goto out;
+ }
+
+ /*
+ * We need to find a unique device number for this VFS that will be used to
+ * construct the filesystem id.
+ */
+ if ((dev = getudev()) == -1) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMount(): getudev() failed.\n");
+ ret = ENXIO;
+ goto out;
+ }
+
+ DEBUG(VM_DEBUG_LOAD, "HgfsMount: dev=%lu\n", dev);
+
+ if (vfs_devismounted(dev)) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsMount(): dev is not unique. We should loop on this.\n");
+ ret = ENXIO;
+ goto out;
+ }
+
+ /*
+ * Fill in values of the VFS structure for the kernel.
+ *
+ * There are several important values that must be set. In particular, we
+ * need to create a chain of pointers so the Kernel can easily move between
+ * the various filesystems mounted on the system.
+ *
+ * o Each filesystem must set its vfs_vnodecovered to the vnode of the
+ * directory it is mounted upon.
+ * o Each directory that is a mount point must set v_vfsmountedhere to
+ * point to the vfs struct of the filesystem mounted there.
+ * o The root vnode of each filesystem must have the VROOT flag set in its
+ * vnode's v_flag so that the Kernel knows to consult the two previously
+ * mentioned pointers.
+ */
+ vfsp->vfs_vnodecovered = vnodep;
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+ vfsp->vfs_flag |= HGFS_VFS_FLAGS;
+ vfsp->vfs_bsize = HGFS_VFS_BSIZE;
+ vfsp->vfs_fstype = hgfsType;
+ vfsp->vfs_bcount = 0;
+ /* If we had mount options, we'd call vfs_setmntopt with vfsp->vfs_mntopts */
+
+ vfsp->vfs_dev = dev;
+ vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, hgfsType);
+
+ /*
+ * Fill in value(s) of the vnode structure we are mounted on top of. We
+ * aren't allowed to modify this ourselves in Solaris 10.
+ */
+# if HGFS_VFS_VERSION == 2
+ vnodep->v_vfsmountedhere = vfsp;
+# endif
+
+ /* We need to initialize the File hash table before getting our first vnode. */
+ ret = HgfsInitFileHashTable(&sip->fileHashTable);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't initialize hash table.\n");
+ ret = EIO;
+ goto out;
+ }
+
+ /*
+ * Now create the root vnode of the filesystem.
+ *
+ * Note: do not change the name from "/" here without first checking that
+ * HgfsMakeFullName() in vnode.c will still do the right thing. (See the
+ * comment for the ".." special case.)
+ */
+ ret = HgfsVnodeGet(&sip->rootVnode, // vnode to fill in
+ sip, // Superinfo
+ vfsp, // This filesystem
+ "/", // File name
+ HGFS_FILE_TYPE_DIRECTORY, // File type
+ &sip->fileHashTable); // File hash table
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't get root vnode.\n");
+ ret = EIO;
+ goto out;
+ }
+
+ /* We must signify that this is the root of our filesystem. */
+ mutex_enter(&sip->rootVnode->v_lock);
+ sip->rootVnode->v_flag |= VROOT;
+ mutex_exit(&sip->rootVnode->v_lock);
+
+ /*
+ * Fill in value(s) for the HGFS superinfo structure and link it to this
+ * filesystem's VFS structure.
+ */
+ sip->vfsp = vfsp;
+
+ vfsp->vfs_data = (caddr_t)sip;
+
+ /* XXX do this? */
+ VN_HOLD(vnodep);
+
+ ret = 0; /* Return success */
+
+ DEBUG(VM_DEBUG_DONE, "HgfsMount() done.\n");
+
+out:
+ if (!(mntp->flags & MS_SYSSPACE)) {
+ kmem_free(mountData, sizeof *mountData);
+ }
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsUnmount --
+ *
+ * This function is invoked when umount(2) is called on our filesystem.
+ *
+ * Results:
+ * Returns 0 on success and an error code on error.
+ *
+ * Side effects:
+ * The root vnode will be freed.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsUnmount(struct vfs *vfsp, // IN: This filesystem
+ int mflag, // IN: Unmount flags
+ struct cred *credp) // IN: Credentials of caller
+{
+ HgfsSuperInfo *sip;
+
+ if (!vfsp || !credp) {
+ cmn_err(HGFS_ERROR, "HgfsUnmount: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsUnmount().\n");
+ HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsUnmount", vfsp->vfs_vnodecovered);
+
+ /*
+ * Initial check to ensure caller is root.
+ */
+ if (!HgfsSuser(credp)) {
+ return EPERM;
+ }
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EINVAL;
+ }
+
+ if (vfsp != sip->vfsp) {
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsUnmount: vfsp != sip->vfsp.\n");
+ }
+
+ /*
+ * Make sure there are no active files (besides the root vnode which we
+ * release at the end of the function).
+ */
+ HgfsDebugPrintFileHashTable(&sip->fileHashTable, VM_DEBUG_STATE);
+ if (!HgfsFileHashTableIsEmpty(sip, &sip->fileHashTable)) {
+ /*
+ * If the force flag was specified, we'll cancel all requests and perform
+ * the unmount. Otherwise, we'll just return EBUSY.
+ */
+ if (mflag & MS_FORCE) {
+ HgfsCancelAllRequests(sip);
+ } else {
+ DEBUG(VM_DEBUG_FAIL, "HgfsUnmount: there are still active files.\n");
+ return EBUSY;
+ }
+ }
+
+ /* Send an unclean unmount warning to syslog. */
+ if (!sip->devOpen) {
+ cmn_err(HGFS_ERROR, "HgfsUnmount: unclean unmount; device was closed.\n");
+ }
+
+ /*
+ * Set unmounted flag in vfs structure.
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+
+ /*
+ * Clean up fields in vnode structure of mount point and release hold on
+ * vnodes for mount.
+ */
+# if HGFS_VFS_VERSION == 2
+ (vfsp->vfs_vnodecovered)->v_vfsmountedhere = NULL;
+# endif
+ VN_RELE(vfsp->vfs_vnodecovered);
+ VN_RELE( HGFS_ROOT_VNODE(sip) );
+
+ /*
+ * Signify to the device half that the filesystem has been unmounted.
+ */
+ sip->vfsp = NULL;
+ HGFS_ROOT_VNODE(sip) = NULL;
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRoot --
+ *
+ * This supplies the root vnode for the filesystem.
+ *
+ * Results:
+ * Returns zero on success and an error code on error. On success vnodepp
+ * is set to the pointer of the root vnode.
+ *
+ * Side effects:
+ * The root vnode's reference count is incremented by one.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsRoot(struct vfs *vfsp, // IN: Filesystem to find root vnode of
+ struct vnode **vnodepp) // OUT: Set to pointer to root vnode of this fs
+{
+ HgfsSuperInfo *sip;
+
+ if (!vfsp || !vnodepp) {
+ cmn_err(HGFS_ERROR, "HgfsRoot: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRoot().\n");
+
+ /*
+ * Get the root vnode from the superinfo structure.
+ */
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRoot() failed to find superinfo.\n");
+ return EIO;
+ }
+
+ if (vfsp != sip->vfsp) {
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsRoot: vfsp != sip->vfsp.\n");
+ }
+
+ VN_HOLD( HGFS_ROOT_VNODE(sip) );
+ *vnodepp = HGFS_ROOT_VNODE(sip);
+
+ DEBUG(VM_DEBUG_LOAD, " rootvnode=%p", HGFS_ROOT_VNODE(sip));
+
+ DEBUG(VM_DEBUG_DONE, "HgfsRoot() done.\n");
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsStatvfs --
+ *
+ * Provides statistics for the provided filesystem. The values provided
+ * by this function are fake.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on exit.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsStatvfs(struct vfs *vfsp, // IN: Filesystem to get statistics for
+ struct statvfs64 *stats) // OUT: Statistics are placed into this struct
+{
+ dev32_t dev32;
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsStatvfs().\n");
+
+ if (!stats) {
+ cmn_err(HGFS_ERROR, "HgfsStatvfs: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ /* Clear stats struct, then fill it in with our values. */
+ memset(stats, 0, sizeof *stats);
+
+ /*
+ * Macros in case we need these elsewhere later.
+ *
+ * These were selected pretty randomly: the numbers should be large enough
+ * so a user can attempt to create any reasonably sized file, but small
+ * enough to be so the Kernel doesn't give callers who are using statvfs32
+ * an EOVERFLOW.
+ */
+ #define HGFS_BLOCKS_TOTAL 0x00ffffff
+ #define HGFS_BLOCKS_FREE 0x00ffefff
+ #define HGFS_BLOCKS_AVAIL 0x00ffef00
+ #define HGFS_FILES_TOTAL 0x00ffffff
+ #define HGFS_FILES_FREE 0x00ffefff
+ #define HGFS_FILES_AVAIL 0x00ffef00
+
+ /* Compress the device number to 32-bits for consistency on 64-bit systems. */
+ cmpldev(&dev32, vfsp->vfs_dev);
+
+ stats->f_bsize = HGFS_BLOCKSIZE; /* Preferred fs block size */
+ stats->f_frsize = HGFS_BLOCKSIZE; /* Fundamental fs block size */
+ /* Next six are u_longlong_t */
+ stats->f_blocks = HGFS_BLOCKS_TOTAL; /* Total blocks on fs */
+ stats->f_bfree = HGFS_BLOCKS_FREE; /* Total free blocks */
+ stats->f_bavail = HGFS_BLOCKS_AVAIL; /* Total blocks avail to non-root */
+ stats->f_files = HGFS_FILES_TOTAL; /* Total files (inodes) */
+ stats->f_ffree = HGFS_FILES_FREE; /* Total files free */
+ stats->f_favail = HGFS_FILES_AVAIL; /* Total files avail to non-root */
+ stats->f_fsid = dev32; /* Filesystem id */
+ stats->f_flag &= ST_NOSUID; /* Flags: we don't support setuid. */
+ stats->f_namemax = MAXNAMELEN; /* Max filename; use Solaris default. */
+
+ /* Memset above and -1 of array size as n below ensure NUL termination. */
+ strncpy(stats->f_basetype, HGFS_FS_NAME, sizeof stats->f_basetype - 1);
+ strncpy(stats->f_fstr, HGFS_FS_NAME, sizeof stats->f_fstr - 1);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSync --
+ *
+ * Flushes the filesystem cache.
+ *
+ * Results:
+ * Returns zero on success and an error code on failure. Currently this
+ * always succeeds.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsSync(struct vfs *vfsp, // IN: Filesystem to flush
+ short flags, // XXX: ?
+ struct cred *credp) // IN: Credentials of caller
+{
+ //DEBUG(VM_DEBUG_ENTRY, "HgfsSync().\n");
+
+ /*
+ * We just return success and hope the host OS calls its filesystem sync
+ * operation periodically as well.
+ */
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsVget --
+ *
+ * Finds the vnode that matches the unique file identifier.
+ *
+ * XXX: Come back to this when figure out how/if to store fidp to vnode
+ * mappings.
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsVget(struct vfs *vfsp, // IN: Filesystem to operate on
+ struct vnode **vnodepp, // OUT: Set to pointer of found vnode
+ struct fid *fidp) // IN: Unique file identifier for vnode
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsVget() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsMountroot --
+ *
+ * Mounts the file system on the root directory.
+ *
+ * XXX: Still need to figure out when this is invoked.
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsMountroot(struct vfs *vfsp, // IN: Filesystem to mount
+ enum whymountroot reason) // IN: Reason why mounting on root
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsMountroot() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsReserved --
+ *
+ * XXX: Is this a placeholder function in the struct?
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsReserved(struct vfs *vfsp,
+ struct vnode **vnodepp,
+ char *charp)
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsReserved() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFreevfs --
+ *
+ * Called when a filesystem is unmounted to free the resources held by the
+ * filesystem.
+ *
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION > 2
+#if HGFS_VFS_VERSION == 3
+int
+#else
+void
+#endif
+HgfsFreevfs(struct vfs *vfsp)
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsFreevfs().\n");
+
+ /*
+ * The only allocation to undo here is call mutex_destroy() on the vsw_lock
+ * for our filesystem's struct vfssw. Doing this causes a system crash,
+ * from a call to a mutex free function within the Kernel (that is, not from
+ * our code), so we are assured that the Kernel cleans this up for us.
+ *
+ * In Solaris 10 it seemed that we needed to free the vnode and vfs
+ * operations we had made earlier (vn_freevnodeops() and
+ * vfs_freevfsops_by_type()), but this is not so. Freeing these prevents
+ * 1) multiple mounts without first reloading the module, and 2) unloading
+ * the module from the Kernel. The combination of these two meant that
+ * the guest would have to be rebooted to remount the filesystem. Because
+ * of all this, the assumption is made that the Kernel takes care of
+ * removing these structures for us.
+ */
+
+#if HGFS_VFS_VERSION == 3
+ return 0;
+#endif
+}
+#endif
+
+
+#if HGFS_VFS_VERSION > 2
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsVnstate --
+ *
+ * Performs the necessary operations on the provided vnode given the state
+ * transfer that has occurred.
+ *
+ * The possible transfers are VNTRANS_EXISTS, VNTRANS_IDLED,
+ * VNTRANS_RECLAIMED, and VNTRANS_DESTROYED (see <sys/vfs.h>).
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsVnstate(vfs_t *vfsp, // IN: Pointer to our filesystem
+ vnode_t *vp, // IN: Vnode to change state of
+ vntrans_t trans) // IN: Type of state transfer
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsVnstate: entry.\n");
+
+ return ENOTSUP;
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSuser --
+ *
+ * Correctly implements the superuser check depending on the version of
+ * Solaris.
+ *
+ * Results:
+ * Returns zero if this user is not superuser, returns non-zero otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsSuser(struct cred *cr) // IN: Credentials of the caller
+{
+ ASSERT(cr);
+#if HGFS_VFS_VERSION == 2
+ return suser(cr);
+#else
+ /*
+ * I am assuming the crgetuid() is the effective uid, since the other two
+ * related functions are crgetruid() and crgetsuid().
+ */
+ return (crgetuid(cr) == 0);
+#endif
+}
+
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * filesystem.h --
+ *
+ * Includes and definitions for filesystem code.
+ *
+ */
+
+
+
+#ifndef __FILESYSTEM_H_
+#define __FILESYSTEM_H_
+
+
+/*
+ * Solaris includes
+ */
+#include <sys/mntent.h> /* mount flags */
+#include <sys/mount.h>
+
+/*
+ * Kernel only includes
+ */
+#ifdef _KERNEL
+
+#include <sys/modctl.h> /* mod_fsops, ... */
+#include <sys/cmn_err.h> /* cmn_err() */
+#include <sys/ddi.h> /* cmn_err() */
+#include <sys/sunddi.h> /* cmn_err() */
+#include <sys/vfs.h> /* struct vfs, ... */
+#include <sys/vnode.h> /* struct vnode, ... */
+
+#if SOL11
+#include <sys/vfs_opreg.h> /* fs_operation_def_t, ... */
+#endif
+
+#endif /* _KERNEL */
+
+#include "hgfsSolaris.h"
+
+/*
+ * Macros
+ */
+#define HGFS_MAGIC (0xbacbacbc)
+#define HGFS_FSTYPE HGFS_FS_NAME
+
+/*
+ * Struct passed from user (guestd) to kernel (fs module)
+ */
+typedef struct HgfsMountData {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t fd;
+} HgfsMountData;
+
+
+#ifdef _KERNEL
+/*
+ * Macros
+ */
+#ifdef SOL9
+ #define HGFS_VFS_FLAGS VFS_NOSUID
+#else
+ #define HGFS_VFS_FLAGS VFS_NOSETUID
+#endif
+
+#ifdef SOL9
+ #define HGFS_VFS_VERSION 2
+#elif defined(SOL10)
+ #define HGFS_VFS_VERSION 3
+#else
+ #define HGFS_VFS_VERSION 5
+#endif
+
+#define HGFS_VFS_BSIZE HGFS_PACKET_MAX
+#define HGFS_COPYIN_FLAGS (0)
+#define HGFS_VFS_TO_SI(vfsp) ((HgfsSuperInfo *)vfsp->vfs_data)
+
+/* This macro is used for both vnode ops and vfs ops. */
+#if defined(SOL9) || defined(SOL10)
+#define HGFS_VOP(vopName, vopFn, hgfsFn) { vopName, hgfsFn }
+#else
+#define HGFS_VOP(vopName, vopFn, hgfsFn) { vopName, { .vopFn = hgfsFn } }
+#endif
+
+/*
+ * Functions
+ */
+/* To abstract Solaris 9 and 10 differences in suser() calls */
+int HgfsSuser(struct cred *cr);
+
+
+/*
+ * Extern variables
+ */
+#ifdef SOL9
+EXTERN struct vfsops HgfsVfsOps;
+#endif
+
+#endif /* _KERNEL */
+
+
+#endif /* __FILESYSTEM_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+
+/*
+ * hgfs.h --
+ *
+ * Header file for public common data types used in the VMware
+ * Host/Guest File System (hgfs).
+ *
+ * This file is included by hgfsProto.h, which defines message formats
+ * used in the hgfs protocol, and by hgfsDev.h, which defines the
+ * interface between the kernel and the hgfs pserver. [bac]
+ */
+
+
+#ifndef _HGFS_H_
+# define _HGFS_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+#include "vm_assert.h"
+
+/*
+ * Maximum number of pages to transfer to/from the HGFS server for V3 protocol
+ * operations that support large requests/replies, e.g. reads and writes.
+ */
+#define HGFS_LARGE_IO_MAX_PAGES 15
+
+/*
+ * Maximum allowed packet size in bytes. All hgfs code should be made
+ * safe with respect to this limit.
+ */
+#define HGFS_PACKET_MAX 6144
+
+/*
+ * The HGFS_LARGE_PACKET_MAX size is used to allow guests to make
+ * read / write requests of sizes larger than HGFS_PACKET_MAX. The larger size
+ * can only be used with server operations that are specified to be large packet
+ * capable in hgfsProto.h.
+ */
+#define HGFS_LARGE_PACKET_MAX ((4096 * HGFS_LARGE_IO_MAX_PAGES) + 2048)
+
+/* Maximum number of bytes to read or write to a hgfs server in a single packet. */
+#define HGFS_IO_MAX 4096
+
+/* Maximum number of bytes to read or write to a V3 server in a single hgfs packet. */
+#define HGFS_LARGE_IO_MAX (HGFS_LARGE_IO_MAX_PAGES * 4096)
+
+/*
+ * Open mode
+ *
+ * These are equivalent to the O_RDONLY, O_WRONLY, O_RDWR open flags
+ * in Unix; they specify which type of access is being requested. These three
+ * modes are mutually exclusive and one is required; all other flags are
+ * modifiers to the mode and must come afterwards as a bitmask. Beware that
+ * HGFS_OPEN_MODE_READ_ONLY contains the value 0 so simply masking another
+ * variable with it to detect its presence is not safe. The _ACCMODES entry in
+ * the enum serves as a bitmask for the others.
+ *
+ * Changing the order of this enum will break stuff.
+ *
+ * This definition is used in some places that don't include
+ * hgfsProto.h, which is why it is here instead of there.
+ */
+typedef enum {
+ HGFS_OPEN_MODE_READ_ONLY,
+ HGFS_OPEN_MODE_WRITE_ONLY,
+ HGFS_OPEN_MODE_READ_WRITE,
+ HGFS_OPEN_MODE_ACCMODES,
+ /* You cannot add anything else here. Really. */
+} HgfsOpenMode;
+
+/*
+ * Open flags.
+ *
+ * Each should be shifted left by HGFS_OPEN_MODE_READ_WRITE plus whatever flag
+ * number they are, starting with zero.
+ *
+ * The sequential flag indicates that reads and writes on this handle should
+ * not seek on each operation; instead, the system's file pointer will be used
+ * so each operation is performed where the last one finished. This flag is
+ * necessary when reading from or writing to non-seekable files (such as procfs
+ * nodes on Linux) but can also lead to inconsistent results if a client shares
+ * a handle amongst several of its callers. This flag should only be used when
+ * the client knows the file is non-seekable and the burden of ensuring file
+ * handles aren't shared falls upon the hgfs client, not the server.
+ */
+#define HGFS_OPEN_SEQUENTIAL (1 << HGFS_OPEN_MODE_READ_WRITE)
+
+/* Masking helpers. */
+#define HGFS_OPEN_MODE_ACCMODE(mode) (mode & HGFS_OPEN_MODE_ACCMODES)
+#define HGFS_OPEN_MODE_FLAGS(mode) (mode & ~HGFS_OPEN_MODE_ACCMODES)
+
+#define HGFS_OPEN_MODE_IS_VALID_MODE(mode) \
+ (HGFS_OPEN_MODE_ACCMODE(mode) == HGFS_OPEN_MODE_READ_ONLY || \
+ HGFS_OPEN_MODE_ACCMODE(mode) == HGFS_OPEN_MODE_WRITE_ONLY || \
+ HGFS_OPEN_MODE_ACCMODE(mode) == HGFS_OPEN_MODE_READ_WRITE)
+
+
+/*
+ * Return status for replies from the server.
+ *
+ * Changing the order of this enum will break the protocol; new status
+ * types should be added at the end.
+ *
+ * This definition is used in some places that don't include
+ * hgfsProto.h, which is why it is here instead of there.
+ *
+ * XXX: So we have a problem here. At some point, HGFS_STATUS_INVALID_NAME was
+ * added to the list of errors. Later, HGFS_STATUS_GENERIC_ERROR was added, but
+ * it was added /before/ HGFS_STATUS_INVALID_NAME. Nobody noticed because the
+ * error codes travelled from hgfsProto.h to hgfs.h in that same change. Worse,
+ * we GA'ed a product (Server 1.0) this way.
+ *
+ * XXX: I've reversed the order because otherwise new HGFS clients working
+ * against WS55-era HGFS servers will think they got HGFS_STATUS_GENERIC_ERROR
+ * when the server sent them HGFS_STATUS_INVALID_NAME. This was a problem
+ * the Linux client converts HGFS_STATUS_GENERIC_ERROR to -EIO, which causes
+ * HgfsLookup to fail unexpectedly (normally HGFS_STATUS_INVALID_NAME is
+ * converted to -ENOENT, an expected result in HgfsLookup).
+ */
+typedef enum {
+ HGFS_STATUS_SUCCESS,
+ HGFS_STATUS_NO_SUCH_FILE_OR_DIR,
+ HGFS_STATUS_INVALID_HANDLE,
+ HGFS_STATUS_OPERATION_NOT_PERMITTED,
+ HGFS_STATUS_FILE_EXISTS,
+ HGFS_STATUS_NOT_DIRECTORY,
+ HGFS_STATUS_DIR_NOT_EMPTY,
+ HGFS_STATUS_PROTOCOL_ERROR,
+ HGFS_STATUS_ACCESS_DENIED,
+ HGFS_STATUS_INVALID_NAME,
+ HGFS_STATUS_GENERIC_ERROR,
+ HGFS_STATUS_SHARING_VIOLATION,
+ HGFS_STATUS_NO_SPACE,
+ HGFS_STATUS_OPERATION_NOT_SUPPORTED,
+ HGFS_STATUS_NAME_TOO_LONG,
+ HGFS_STATUS_INVALID_PARAMETER,
+} HgfsStatus;
+
+/*
+ * HGFS RPC commands
+ *
+ * HGFS servers can run in a variety of places across several different
+ * transport layers. These definitions constitute all known RPC commands.
+ *
+ * For each definition, there is both the server string (the command itself)
+ * as well as a client "prefix", which is the command followed by a space.
+ * This is provided for convenience, since clients will need to copy both
+ * the command and the space into some buffer that is then sent over the
+ * backdoor.
+ *
+ * In Host --> Guest RPC traffic, the host endpoint is TCLO and the guest
+ * endpoint is RpcIn. TCLO is a particularly confusing name choice which dates
+ * back to when the host was to send raw TCL code to the guest (TCL Out ==
+ * TCLO).
+ *
+ * In Guest --> Host RPC traffic, the guest endpoint is RpcOut and the host
+ * endpoint is RPCI.
+ */
+
+/*
+ * When an RPCI listener registers for this command, HGFS requests are expected
+ * to be synchronously sent from the guest and replies are expected to be
+ * synchronously returned.
+ *
+ * When an RpcIn listener registers for this command, requests are expected to
+ * be asynchronously sent from the host and synchronously returned from the
+ * guest.
+ *
+ * In short, an endpoint sending this command is sending a request whose reply
+ * should be returned synchronously.
+ */
+#define HGFS_SYNC_REQREP_CMD "f"
+#define HGFS_SYNC_REQREP_CLIENT_CMD HGFS_SYNC_REQREP_CMD " "
+#define HGFS_SYNC_REQREP_CLIENT_CMD_LEN (sizeof HGFS_SYNC_REQREP_CLIENT_CMD - 1)
+
+/*
+ * This is just for the sake of macro naming. Since we are guaranteed
+ * equal command lengths, defining command length via a generalized macro name
+ * will prevent confusion.
+ */
+#define HGFS_CLIENT_CMD_LEN HGFS_SYNC_REQREP_CLIENT_CMD_LEN
+
+#endif // _HGFS_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * hgfsEscape.c --
+ *
+ * Escape and unescape illegal filenames for different platforms.
+ *
+ */
+
+#ifdef __KERNEL__
+# include "driver-config.h"
+# include <linux/string.h>
+#elif defined(__FreeBSD__)
+# if defined(_KERNEL)
+# include <sys/libkern.h>
+# define strchr(s,c) index(s,c)
+# else
+# include <string.h>
+# endif
+# define memmove(s1,s2,n) bcopy(s2,s1,n)
+#elif defined(__APPLE__) && defined(KERNEL)
+# include <string.h>
+#elif !defined(sun)
+# include <stdlib.h>
+# include <string.h>
+#else
+# include <string.h>
+#endif
+
+#include "vmware.h"
+#include "hgfsEscape.h"
+#include "cpName.h"
+
+#ifdef _WIN32
+
+#define UNREFERENCED_PARAMETER(P) (P)
+
+/* These characters are illegal in Windows file names. */
+const char* HGFS_ILLEGAL_CHARS = "/\\*?:\"<>|";
+const char* HGFS_SUBSTITUTE_CHARS = "!@#$^&(){}";
+/* Last character of a file name in Windows can be neither dot nor space. */
+const char* HGFS_ILLEGAL_LAST_CHARS = ". ";
+
+/* http://msdn.microsoft.com/en-us/library/aa365247.aspx */
+char *HgfsReservedNames[] = {"CON", "PRN", "AUX", "NUL"};
+char *HgfsReservedNamesWithNumber[] = {"COM", "LPT"};
+
+#define HGFS_RESERVED_NAME_CHARS_LENGTH 3
+#define HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH (HGFS_RESERVED_NAME_CHARS_LENGTH + 1)
+/* Check for special escaping cases - reserved names and illegal last characters. */
+#define IS_SPECIAL_CASE_ESCAPE(b,o,l) HgfsIsSpecialCaseEscape(b,o,l)
+/* Process Windows reserved names. */
+#define PROCESS_RESERVED_NAME(b,s,p,o,c) \
+if (!HgfsProcessReservedName(b,s,p,o,c)) \
+{ \
+ return FALSE; \
+}
+/* Process Windows reserved names. */
+#define PROCESS_LAST_CHARACTER(b,s,p,c) \
+if (!HgfsProcessLastCharacter(b,s,p,c)) \
+{ \
+ return FALSE; \
+}
+
+#else // _WIN32
+
+#define UNREFERENCED_PARAMETER(P)
+/* There is no special escape sequences on other than Windows platforms. */
+#define IS_SPECIAL_CASE_ESCAPE(b,o,l) FALSE
+/* There is no reserved names on other then Windows platforms. */
+#define PROCESS_RESERVED_NAME(b,s,p,o,c)
+/* There is no special processing for the last character on non-Windows platforms. */
+#define PROCESS_LAST_CHARACTER(b,s,p,c)
+
+#if defined(__APPLE__)
+/* These characters are illegal in MAC OS file names. */
+const char* HGFS_ILLEGAL_CHARS = "/:";
+const char* HGFS_SUBSTITUTE_CHARS = "!&";
+#else // __APPLE__
+/* These characters are illegal in Linux file names. */
+const char* HGFS_ILLEGAL_CHARS = "/";
+const char* HGFS_SUBSTITUTE_CHARS = "!";
+#endif // __APPLE__
+
+#endif // _WIN32
+
+#define HGFS_ESCAPE_CHAR '%'
+#define HGFS_ESCAPE_SUBSTITUE_CHAR ']'
+
+typedef enum {
+ HGFS_ESCAPE_ILLEGAL_CHARACTER,
+ HGFS_ESCAPE_RESERVED_NAME,
+ HGFS_ESCAPE_ILLEGAL_LAST_CHARACTER,
+ HGFS_ESCAPE_ESCAPE_SEQUENCE,
+ HGFS_ESCAPE_COMPLETE
+} HgfsEscapeReason;
+
+
+typedef Bool (*HgfsEnumCallback)(char const *bufIn,
+ uint32 offset,
+ HgfsEscapeReason reason,
+ void* context);
+
+/*
+ * The structure is used by HgfsAddEscapeCharacter to keep context information between
+ * invocations
+ * All offsets defined in this structure are in characters, not bytes
+ */
+typedef struct {
+ uint32 processedOffset; // Offset of the first unprocessed input character
+ uint32 outputBufferLength; // Number of characters in the output buffer
+ uint32 outputOffset; // Number of characters that are already in the output
+ char *outputBuffer; // Pointer to the output buffer
+} HgfsEscapeContext;
+
+static void HgfsEscapeUndoComponent(char *bufIn, uint32 *totalLength);
+static int HgfsEscapeGetComponentSize(char const *bufIn, uint32 sizeIn);
+static int HgfsEscapeDoComponent(char const *bufIn, uint32 sizeIn, uint32 sizeBufOut,
+ char *bufOut);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsAddEscapeCharacter --
+ *
+ * Callback function that is called by HgfsEnumerate to insert an escape sequence
+ * into the input name.
+ *
+ * Results:
+ * TRUE if successful, FALSE if there is an error like the output buffer is
+ * too small.
+ *
+ * Side effects:
+ * Updates the output buffer pointer (stored in the context variable).
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsAddEscapeCharacter(char const * bufIn, // IN: input name
+ uint32 offset, // IN: offset that requires escaping
+ HgfsEscapeReason reason, // IN: reason for esaping
+ void *context) // IN/OUT: convertion context
+{
+ HgfsEscapeContext *escapeContext = (HgfsEscapeContext *)context;
+ uint32 charactersToCopy;
+ uint32 outputSpace;
+ char* illegal;
+ Bool result = TRUE;
+
+ ASSERT(offset >= escapeContext->processedOffset); // Scanning forward
+ charactersToCopy = offset - escapeContext->processedOffset;
+
+ if (escapeContext->outputOffset + charactersToCopy >
+ escapeContext->outputBufferLength) {
+ return FALSE;
+ }
+
+ memcpy(escapeContext->outputBuffer + escapeContext->outputOffset,
+ bufIn + escapeContext->processedOffset, charactersToCopy * sizeof *bufIn);
+ escapeContext->outputOffset += charactersToCopy;
+ escapeContext->processedOffset += charactersToCopy;
+
+ outputSpace = escapeContext->outputBufferLength - escapeContext->outputOffset;
+
+ switch(reason) {
+ case HGFS_ESCAPE_ILLEGAL_CHARACTER:
+ if (outputSpace < 2) {
+ return FALSE;
+ }
+ illegal = strchr(HGFS_ILLEGAL_CHARS, bufIn[escapeContext->processedOffset]);
+ escapeContext->processedOffset++; // Skip illegal input character
+ ASSERT(illegal != NULL);
+ escapeContext->outputBuffer[escapeContext->outputOffset] =
+ HGFS_SUBSTITUTE_CHARS[illegal - HGFS_ILLEGAL_CHARS];
+ escapeContext->outputOffset++;
+ escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
+ escapeContext->outputOffset++;
+ break;
+
+ case HGFS_ESCAPE_RESERVED_NAME:
+ if (outputSpace < 1) {
+ return FALSE;
+ }
+ escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
+ escapeContext->outputOffset++;
+ break;
+
+ case HGFS_ESCAPE_ILLEGAL_LAST_CHARACTER:
+ if (outputSpace < 1) {
+ return FALSE;
+ }
+ escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
+ escapeContext->outputOffset++;
+ break;
+
+ case HGFS_ESCAPE_ESCAPE_SEQUENCE:
+ if (outputSpace < 2) {
+ return FALSE;
+ }
+ escapeContext->processedOffset++; // Skip input esape character
+ escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_SUBSTITUE_CHAR;
+ escapeContext->outputOffset++;
+ escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
+ escapeContext->outputOffset++;
+ break;
+
+ case HGFS_ESCAPE_COMPLETE:
+ if (outputSpace < 1) {
+ return FALSE;
+ }
+ escapeContext->outputBuffer[escapeContext->outputOffset] = '\0';
+ break;
+
+ default:
+ result = FALSE;
+ ASSERT(FALSE);
+ }
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsCountEscapeChars --
+ *
+ * Callback function that is called by HgfsEnumerate to count additional characters
+ * that need to be inserted in the input name.
+ *
+ * Results:
+ * TRUE since it never fails.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsCountEscapeChars(char const *bufIn, // IN: input name
+ uint32 offset, // IN: offset where escape is needed
+ HgfsEscapeReason reason, // IN: reason for escaping
+ void *context) // IN/OUT: context info
+{
+ UNREFERENCED_PARAMETER(bufIn);
+ UNREFERENCED_PARAMETER(offset);
+ if (reason != HGFS_ESCAPE_COMPLETE) {
+ uint32 *counter = (uint32*)context;
+ (*counter)++;
+ }
+ return TRUE;
+}
+
+
+#ifdef _WIN32
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsLetterToUpper --
+ *
+ * Converts lowercase English letters to uppercase.
+ * If the symbol is not a lowercase English letter returns the original character.
+ *
+ * Results:
+ * Converted character.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static char
+HgfsLetterToUpper(char letter)
+{
+ if (letter >= 'a' && letter <= 'z') {
+ return letter - ('a' - 'A');
+ }
+ return letter;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsIsEqualPrefix --
+ *
+ * Verifies if the string prefix is equal to the given prefix.
+ * It assumes that the prefix includes only uppercase English letters or numbers
+ * and it does not have any international characters.
+ * The string must be either NULL terminated or not shorter then the prefix.
+ *
+ * Results:
+ * TRUE if the uppcased string starts with the given prefix. False otherwise.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsEqualPrefix(char const *prefix, // IN: prefix to check
+ char const *string, // IN: input string
+ uint32 prefixLength) // IN: length of the prefix in characters
+{
+ int i;
+ for (i = 0; i < prefixLength; i++) {
+ ASSERT(prefix[i] > 0 && (prefix[i] < 'a' || prefix[i] > 'z' ));
+ if (prefix[i] != HgfsLetterToUpper(string[i])) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsIsReservedPrefix --
+ *
+ * Verifies if the name's prefix is one of the reserved names.
+ *
+ * Results:
+ * TRUE if the name's prefix is one of the reserved names.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsReservedPrefix(char const *bufIn) // IN: input name
+{
+ uint32 i;
+ for (i = 0; i < ARRAYSIZE(HgfsReservedNames); i++) {
+ if (HgfsIsEqualPrefix(HgfsReservedNames[i], bufIn,
+ HGFS_RESERVED_NAME_CHARS_LENGTH)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsIsReservedPrefixWithNumber --
+ *
+ * Verifies if the name's prefix is one of the reserved names with number:
+ * COM1-9 or LPT1-9.
+ *
+ * Results:
+ * TRUE if the name's prefix is one of the reserved names with number.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsReservedPrefixWithNumber(char const *bufIn) // IN: input name
+{
+ uint32 i;
+ for (i = 0; i < ARRAYSIZE(HgfsReservedNamesWithNumber); i++) {
+ if (HgfsIsEqualPrefix(HgfsReservedNamesWithNumber[i], bufIn,
+ HGFS_RESERVED_NAME_CHARS_LENGTH) &&
+ bufIn[HGFS_RESERVED_NAME_CHARS_LENGTH] >= '1' &&
+ bufIn[HGFS_RESERVED_NAME_CHARS_LENGTH] <= '9') {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsIsSpecialCaseEscape --
+ *
+ * Verifies if the escape character is a part of special case escape sequence
+ * that exists only in Windows - escaped reserved name or escaped illegal last
+ * character.
+ *
+ * Results:
+ * TRUE if the name's prefix is one of the reserved names with number.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsSpecialCaseEscape(char const *bufIn, // IN: input name
+ uint32 offset, // IN: offset of the escape character
+ uint32 length) // IN: length of the name in characters
+{
+ if (offset + 1 == length &&
+ strchr(HGFS_ILLEGAL_LAST_CHARS, bufIn[offset - 1]) != NULL) {
+ return TRUE;
+ }
+ if (offset == HGFS_RESERVED_NAME_CHARS_LENGTH &&
+ (length == HGFS_RESERVED_NAME_CHARS_LENGTH + 1 || bufIn[offset+1] == '.')) {
+ return HgfsIsReservedPrefix(bufIn);
+ }
+ if (offset == HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH &&
+ (length == HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH + 1 ||
+ bufIn[offset+1] == '.')) {
+ return HgfsIsReservedPrefixWithNumber(bufIn);
+ }
+ return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsProcessReservedName --
+ *
+ * Verifies if the name is one of reserved Windows file names.
+ * If it is a reserved name invokes callback that performs required
+ * processing.
+ *
+ * Results:
+ * TRUE if no processing is required of if processing succeeded,
+ * FALSE if processing failed.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsProcessReservedName(char const *bufIn, // IN: Unescaped input buffer
+ uint32 sizeIn, // IN: Length of the input
+ HgfsEnumCallback processEscape, // IN: Callack that is invoked
+ // if input is reserved name
+ uint32 *offset, // OUT: New offset in the input
+ void *context) // IN/OUT: Context for callback
+{
+ /* Look reserved names: CON, PRN, AUX, NUL. */
+ if (sizeIn >= HGFS_RESERVED_NAME_CHARS_LENGTH && HgfsIsReservedPrefix(bufIn)) {
+ if (HGFS_RESERVED_NAME_CHARS_LENGTH == sizeIn ||
+ bufIn[HGFS_RESERVED_NAME_CHARS_LENGTH] == '.') {
+ if (!processEscape(bufIn, HGFS_RESERVED_NAME_CHARS_LENGTH,
+ HGFS_ESCAPE_RESERVED_NAME, context)) {
+ return FALSE;
+ }
+ *offset = HGFS_RESERVED_NAME_CHARS_LENGTH;
+ }
+ }
+
+ /* Look reserved names with numbers: COM1-9 and LPT1-9. */
+ if (sizeIn >= HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH &&
+ HgfsIsReservedPrefixWithNumber(bufIn)) {
+ if (HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH == sizeIn ||
+ bufIn[HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH] == '.') {
+ if (!processEscape(bufIn, HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH,
+ HGFS_ESCAPE_RESERVED_NAME, context)) {
+ return FALSE;
+ }
+ *offset = HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH;
+ }
+ }
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsProcessLastCharacter --
+ *
+ * Verifies if the trailing character in the name is a valid last character.
+ * In Windows it is illegal to have a file name that ends with dot ('.') or
+ * space (' '). The only exception is "." and ".." directory names.
+ * If the last character is invalid the function invokes a callback to process it.
+ *
+ * Results:
+ * TRUE if no processing is required of if processing succeeded,
+ * FALSE if processing failed.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsProcessLastCharacter(char const *bufIn, // IN: Unescaped input buffer
+ uint32 sizeIn, // IN: Length of the input
+ HgfsEnumCallback processEscape, // IN: Callack that is invoked
+ // when escaping is needed
+ void *context) // IN/OUT: Callback context
+{
+
+ /* If the filename is '.' or '..' we shouldn't escape it. */
+ if ((sizeIn == 1 && bufIn[0] == '.') ||
+ (sizeIn == 2 && bufIn[0] == '.' && bufIn[1] == '.')) {
+ return TRUE;
+ }
+
+ /* Invoke the callback if the last character is illegal. */
+ if (strchr(HGFS_ILLEGAL_LAST_CHARS, bufIn[sizeIn - 1]) != NULL) {
+ if (!processEscape(bufIn, sizeIn, HGFS_ESCAPE_ILLEGAL_LAST_CHARACTER, context)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+#endif // WIN32
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsIsEscapeSequence --
+ *
+ * Verifies if input buffer has an escape sequence at the position
+ * defined by offset.
+ *
+ * Results:
+ * TRUE if there is an escape sequence at the position defined by offset.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsEscapeSequence(char const *bufIn, // IN: input name
+ uint32 offset, // IN: offset of the escape character
+ uint32 length) // IN: length of the name in characters
+{
+ if (bufIn[offset] == HGFS_ESCAPE_CHAR && offset > 0) {
+ char *substitute;
+ if (bufIn[offset - 1] == HGFS_ESCAPE_SUBSTITUE_CHAR) {
+ return TRUE;
+ }
+ substitute = strchr(HGFS_SUBSTITUTE_CHARS, bufIn[offset - 1]);
+ if (substitute != NULL) {
+ return TRUE;
+ }
+ return IS_SPECIAL_CASE_ESCAPE(bufIn,offset,length);
+ }
+ return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsEscapeEnumerate --
+ *
+ * The function scans the input buffer and calls processEscape callback for every
+ * place in the input buffer which require escaping.
+ *
+ * Callback does the required processing. There are two different callbacks -
+ * one counts extra symbols that are needed for escaping and another produces
+ * escaped output name based on input name.
+ *
+ * 1. The first function calculates number of extra characters. It just increments
+ * a counter which is passed to it in context variable every time it is called
+ * for the reason different from "complete processing" assuming that
+ * exactly one extra character is required to escape any invalid input.
+ *
+ * 2. The second function produces output name by copying everything from input
+ * name into the output name up to the place which require escaping and
+ * then inserts appropriate escape sequence into the output. It keeps track of its
+ * progress and keeps pointer to the output buffer in the context variable.
+ * HgfsEscapeEnumerate calls calback function one more time at the end of the
+ * input buffer to let callback finish processing of the input (for example copy
+ * the rest of the name after the last escape sequence from input buffer to
+ * output buffer).
+ *
+ * Results:
+ * TRUE if the input has been processed successfully by the callback, false otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsEscapeEnumerate(char const *bufIn, // IN: Buffer with unescaped input
+ uint32 sizeIn, // IN: Number of input *characters*
+ HgfsEnumCallback processEscape, // IN: Callack that is invoked every
+ // time escaping is required
+ void *context) // IN/OUT: Context for processEscape
+{
+ /* First look for invalid characters in the input name. */
+ uint32 i, offset = 0;
+ if (sizeIn == 0) {
+ return TRUE;
+ }
+
+ ASSERT(processEscape);
+
+ PROCESS_RESERVED_NAME(bufIn, sizeIn, processEscape, &offset, context);
+
+ for (i = offset; i < sizeIn; i++) {
+ if (strchr(HGFS_ILLEGAL_CHARS, bufIn[i]) != NULL) {
+ if (!processEscape(bufIn, i, HGFS_ESCAPE_ILLEGAL_CHARACTER, context)) {
+ return FALSE;
+ }
+ } else if (HgfsIsEscapeSequence(bufIn, i, sizeIn)) {
+ if (!processEscape(bufIn, i, HGFS_ESCAPE_ESCAPE_SEQUENCE, context)) {
+ return FALSE;
+ }
+ }
+ }
+
+ PROCESS_LAST_CHARACTER(bufIn, sizeIn, processEscape, context);
+
+ if (!processEscape(bufIn, sizeIn, HGFS_ESCAPE_COMPLETE, context)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsEscape_Do --
+ *
+ * Escape any characters that are not legal in a windows filename.
+ * Escape reserved file names that can't be used in Windows.
+ * We also of course have to escape the escape character, which is "%",
+ * when it is part of a character sequence that would require unescaping
+ *
+ * sizeBufOut must account for the NUL terminator.
+ *
+ * Results:
+ * On success, the size (excluding the NUL terminator) of the
+ * escaped, NUL terminated buffer.
+ * On failure (bufOut not big enough to hold result), negative value.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsEscape_Do(char const *bufIn, // IN: Buffer with unescaped input
+ uint32 sizeIn, // IN: Size of input buffer
+ uint32 sizeBufOut, // IN: Size of output buffer
+ char *bufOut) // OUT: Buffer for escaped output
+{
+ const char *currentComponent = bufIn;
+ uint32 sizeLeft = sizeBufOut;
+ char *outPointer = bufOut;
+ const char *end = bufIn + sizeIn;
+ const char *next;
+ ASSERT(sizeIn > 0);
+ if (bufIn[sizeIn - 1] == '\0') {
+ /*
+ * In some cases a NUL terminated string is passed to HgfsEscape_Do
+ * so it make sense to support such input even if CPName_GetComponent
+ * does not. Detect this case and make the input compliant with
+ * CPName_GetComponent by removing terminating NUL.
+ */
+ end--;
+ sizeIn--;
+ }
+ /*
+ * Absolute symbolic link name starts with the '\0'. HgfsEscapeDo needs to work
+ * with such names. Leading NULL symbols should be skipped here since
+ * CPName_GetComponent does not support such names.
+ */
+ while (*currentComponent == '\0' && currentComponent - bufIn < sizeIn) {
+ currentComponent++;
+ sizeLeft--;
+ *outPointer++ = '\0';
+ }
+ while (currentComponent - bufIn < sizeIn) {
+ int escapedLength;
+ uint32 componentSize = CPName_GetComponent(currentComponent, end, &next);
+
+ escapedLength = HgfsEscapeDoComponent(currentComponent, componentSize,
+ sizeLeft, outPointer);
+ if (escapedLength < 0) {
+ return escapedLength;
+ }
+ currentComponent = next;
+ sizeLeft -= escapedLength + 1;
+ outPointer += escapedLength + 1;
+ }
+ return (int) (outPointer - bufOut) - 1; // Do not count the last NUL terminator
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsEscape_GetSize --
+ *
+ * Calculates required size in bytes for the buffer that is needed to hold escaped
+ * cross platform path name. Returns 0 if no escaping is required.
+ *
+ * Results:
+ * On success, the size (excluding the NUL terminator) of the
+ * escaped, NUL terminated buffer.
+ * Returns 0 if the name is a valid Windows file name.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsEscape_GetSize(char const *bufIn, // IN: Buffer with unescaped input
+ uint32 sizeIn) // IN: Size of the input buffer
+{
+ uint32 result = 0;
+ const char *currentComponent = bufIn;
+ const char *end = bufIn + sizeIn;
+ const char *next;
+
+ if (sizeIn == 0) { // No need toescape empty name.
+ return 0;
+ }
+ if (bufIn[sizeIn - 1] == '\0') {
+ /*
+ * In some cases a NUL terminated string is passed to HgfsEscape_GeSize
+ * so it make sense to support such input even if CPName_GetComponent
+ * does not. Detect this case and make the input compliant with
+ * CPName_GetComponent by removing terminating NUL.
+ */
+ end--;
+ sizeIn--;
+ }
+ /* Skip leading NULs to keep CPName_GetComponent happy. */
+ while (*currentComponent == '\0' && currentComponent - bufIn < sizeIn) {
+ currentComponent++;
+ }
+ while (currentComponent - bufIn < sizeIn) {
+ uint32 componentSize = CPName_GetComponent(currentComponent, end, &next);
+ result += HgfsEscapeGetComponentSize(currentComponent, componentSize);
+ currentComponent = next;
+ }
+ return (result == 0) ? 0 : result + sizeIn;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsEscape_Undo --
+ *
+ * Unescape a buffer that was escaped using HgfsEscapeBuffer.
+ *
+ * The unescaping is done in place in the input buffer, and
+ * can not fail.
+ *
+ * Results:
+ * The size (excluding the NUL terminator) of the unescaped, NUL
+ * terminated buffer.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsEscape_Undo(char *bufIn, // IN: Characters to be unescaped
+ uint32 sizeIn) // IN: Number of characters in bufIn
+{
+ uint32 componentSize = strlen(bufIn) + 1;
+ uint32 unprocessedSize = sizeIn + 1;
+ uint32 result = 0;
+ char *currentComponent = bufIn;
+ while (currentComponent != NULL) {
+ HgfsEscapeUndoComponent(currentComponent, &unprocessedSize);
+ componentSize = strlen(currentComponent) + 1; // Unescaped size
+ result += componentSize;
+ if (unprocessedSize > 1) {
+ currentComponent = currentComponent + componentSize;
+ componentSize = strlen(currentComponent) + 1; // Size of the next component
+ } else {
+ currentComponent = NULL;
+ }
+ }
+ return result - 1;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsEscapeUndoComponent --
+ *
+ * Unescape a buffer that was escaped using HgfsEscapeBuffer.
+ *
+ * The unescaping is done in place in the input buffer, and
+ * can not fail.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+HgfsEscapeUndoComponent(char *bufIn, // IN: Characters to be unescaped
+ uint32 *unprocessedLength) // IN: Unprocessed characters
+ // in the whole name
+{
+ size_t offset;
+ size_t sizeIn = strlen(bufIn);
+ char* curOutBuffer = bufIn;
+ char* escapePointer;
+ ASSERT(bufIn);
+
+ escapePointer = strchr(curOutBuffer, HGFS_ESCAPE_CHAR);
+ while (escapePointer != NULL) {
+ offset = escapePointer - bufIn;
+ if (HgfsIsEscapeSequence(bufIn, offset, sizeIn)) {
+ char* substitute = strchr(HGFS_SUBSTITUTE_CHARS, bufIn[offset - 1]);
+ if (substitute != NULL) {
+ bufIn[offset - 1] = HGFS_ILLEGAL_CHARS[substitute - HGFS_SUBSTITUTE_CHARS];
+ } else if (bufIn[offset - 1] == HGFS_ESCAPE_SUBSTITUE_CHAR) {
+ bufIn[offset - 1] = HGFS_ESCAPE_CHAR;
+ }
+ memmove(escapePointer, escapePointer + 1, (*unprocessedLength) - offset - 1);
+ (*unprocessedLength)--;
+ sizeIn--;
+ if (sizeIn > 0) {
+ escapePointer = strchr(escapePointer, HGFS_ESCAPE_CHAR);
+ } else {
+ escapePointer = NULL;
+ }
+ } else {
+ escapePointer = strchr(escapePointer + 1, HGFS_ESCAPE_CHAR);
+ }
+ }
+ ASSERT((*unprocessedLength) > sizeIn);
+ (*unprocessedLength) -= sizeIn + 1;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsEscapeDoComponent --
+ *
+ * Escape any characters that are not legal in a windows filename.
+ * Escape reserved file names that can't be used in Windows.
+ * We also of course have to escape the escape character, which is "%",
+ * when it is part of a character sequence that would require unescaping
+ *
+ * sizeBufOut must account for the NUL terminator.
+ *
+ * Results:
+ * On success, the size (excluding the NUL terminator) of the
+ * escaped, NUL terminated buffer.
+ * On failure (bufOut not big enough to hold result), negative value.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsEscapeDoComponent(char const *bufIn, // IN: Buffer with unescaped input
+ uint32 sizeIn, // IN: Size of input buffer
+ uint32 sizeBufOut, // IN: Size of output buffer
+ char *bufOut) // OUT: Buffer for escaped output
+{
+ HgfsEscapeContext conversionContext;
+ conversionContext.processedOffset = 0;
+ conversionContext.outputBufferLength = sizeBufOut / sizeof *bufOut;
+ conversionContext.outputOffset = 0;
+ conversionContext.outputBuffer = bufOut;
+
+ if (!HgfsEscapeEnumerate(bufIn, sizeIn, HgfsAddEscapeCharacter, &conversionContext)) {
+ return -1;
+ }
+ return conversionContext.outputOffset * sizeof *bufOut;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsEscapeGetComponentSize --
+ *
+ * Calculates number of addtitional characters that are needed to escape
+ * name for one NUL terminated component of the path.
+ *
+ * Results:
+ * Number of additional escape characters needed to escape the name.
+ * Returns 0 if no escaping is required.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsEscapeGetComponentSize(char const *bufIn, // IN: Buffer with unescaped input
+ uint32 sizeIn) // IN: Size of the in input buffer
+{
+ int result = 0;
+ HgfsEscapeEnumerate(bufIn, sizeIn, HgfsCountEscapeChars, &result);
+ return result;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * hgfsEscape.h --
+ *
+ * Escape and unescape filenames that are not legal on a particular
+ * platform.
+ *
+ */
+
+#ifndef __HGFS_ESCAPE_H__
+#define __HGFS_ESCAPE_H__
+
+int HgfsEscape_GetSize(char const *bufIn, // IN
+ uint32 sizeIn); // IN
+int HgfsEscape_Do(char const *bufIn, // IN
+ uint32 sizeIn, // IN
+ uint32 sizeBufOut, // IN
+ char *bufOut); // OUT
+
+int HgfsEscape_Undo(char *bufIn, // IN
+ uint32 sizeIn); // IN
+
+#endif // __HGFS_ESCAPE_H__
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * hgfsProto.h --
+ *
+ * Header file for data types and message formats used in the
+ * Host/Guest File System (hgfs) protocol.
+ */
+
+
+#ifndef _HGFS_PROTO_H_
+# define _HGFS_PROTO_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#include "vm_basic_types.h"
+#include "hgfs.h"
+
+/*
+ * Handle used by the server to identify files and searches. Used
+ * by the driver to match server replies with pending requests.
+ */
+
+typedef uint32 HgfsHandle;
+#define HGFS_INVALID_HANDLE ((HgfsHandle)~((HgfsHandle)0))
+
+/*
+ * Opcodes for server operations.
+ *
+ * Changing the ordering of this enum will break the protocol; new ops
+ * should be added at the end (but before HGFS_OP_MAX).
+ */
+
+typedef enum {
+ HGFS_OP_OPEN, /* Open file */
+ HGFS_OP_READ, /* Read from file */
+ HGFS_OP_WRITE, /* Write to file */
+ HGFS_OP_CLOSE, /* Close file */
+ HGFS_OP_SEARCH_OPEN, /* Start new search */
+ HGFS_OP_SEARCH_READ, /* Get next search response */
+ HGFS_OP_SEARCH_CLOSE, /* End a search */
+ HGFS_OP_GETATTR, /* Get file attributes */
+ HGFS_OP_SETATTR, /* Set file attributes */
+ HGFS_OP_CREATE_DIR, /* Create new directory */
+ HGFS_OP_DELETE_FILE, /* Delete a file */
+ HGFS_OP_DELETE_DIR, /* Delete a directory */
+ HGFS_OP_RENAME, /* Rename a file or directory */
+ HGFS_OP_QUERY_VOLUME_INFO, /* Query volume information */
+
+ /*
+ * The following operations are only available in version 2 of the hgfs
+ * protocol. The corresponding version 1 opcodes above are deprecated.
+ */
+
+ HGFS_OP_OPEN_V2, /* Open file */
+ HGFS_OP_GETATTR_V2, /* Get file attributes */
+ HGFS_OP_SETATTR_V2, /* Set file attributes */
+ HGFS_OP_SEARCH_READ_V2, /* Get next search response */
+ HGFS_OP_CREATE_SYMLINK, /* Create a symlink */
+ HGFS_OP_SERVER_LOCK_CHANGE, /* Change the oplock on a file */
+ HGFS_OP_CREATE_DIR_V2, /* Create a directory */
+ HGFS_OP_DELETE_FILE_V2, /* Delete a file */
+ HGFS_OP_DELETE_DIR_V2, /* Delete a directory */
+ HGFS_OP_RENAME_V2, /* Rename a file or directory */
+
+ /*
+ * Operations for version 3, deprecating version 2 operations.
+ */
+
+ HGFS_OP_OPEN_V3, /* Open file */
+ HGFS_OP_READ_V3, /* Read from file */
+ HGFS_OP_WRITE_V3, /* Write to file */
+ HGFS_OP_CLOSE_V3, /* Close file */
+ HGFS_OP_SEARCH_OPEN_V3, /* Start new search */
+ HGFS_OP_SEARCH_READ_V3, /* Start new search */
+ HGFS_OP_SEARCH_CLOSE_V3, /* End a search */
+ HGFS_OP_GETATTR_V3, /* Get file attributes */
+ HGFS_OP_SETATTR_V3, /* Set file attributes */
+ HGFS_OP_CREATE_DIR_V3, /* Create new directory */
+ HGFS_OP_DELETE_FILE_V3, /* Delete a file */
+ HGFS_OP_DELETE_DIR_V3, /* Delete a directory */
+ HGFS_OP_RENAME_V3, /* Rename a file or directory */
+ HGFS_OP_QUERY_VOLUME_INFO_V3, /* Query volume information */
+ HGFS_OP_CREATE_SYMLINK_V3, /* Create a symlink */
+ HGFS_OP_SERVER_LOCK_CHANGE_V3, /* Change the oplock on a file */
+
+ HGFS_OP_MAX, /* Dummy op, must be last in enum */
+} HgfsOp;
+
+
+/* HGFS protocol versions. */
+#define HGFS_VERSION_OLD (1 << 0)
+#define HGFS_VERSION_3 (1 << 1)
+
+/* XXX: Needs change when VMCI is supported. */
+#define HGFS_REQ_PAYLOAD_SIZE_V3(hgfsReq) (sizeof *hgfsReq + sizeof(HgfsRequest))
+#define HGFS_REP_PAYLOAD_SIZE_V3(hgfsRep) (sizeof *hgfsRep + sizeof(HgfsReply))
+
+/* XXX: Needs change when VMCI is supported. */
+#define HGFS_REQ_GET_PAYLOAD_V3(hgfsReq) ((char *)(hgfsReq) + sizeof(HgfsRequest))
+#define HGFS_REP_GET_PAYLOAD_V3(hgfsRep) ((char *)(hgfsRep) + sizeof(HgfsReply))
+
+
+/*
+ * File types, used in HgfsAttr. We support regular files,
+ * directories, and symlinks.
+ *
+ * Changing the order of this enum will break the protocol; new types
+ * should be added at the end.
+ */
+
+typedef enum {
+ HGFS_FILE_TYPE_REGULAR,
+ HGFS_FILE_TYPE_DIRECTORY,
+ HGFS_FILE_TYPE_SYMLINK,
+} HgfsFileType;
+
+
+/*
+ * Open flags.
+ *
+ * Changing the order of this enum will break stuff. Do not add any flags to
+ * this enum: it has been frozen and all new flags should be added to
+ * HgfsOpenMode. This was done because HgfsOpenMode could still be converted
+ * to a bitmask (so that it's easier to add flags to) whereas this enum was
+ * already too large.
+ */
+
+typedef enum { // File doesn't exist File exists
+ HGFS_OPEN, // error
+ HGFS_OPEN_EMPTY, // error size = 0
+ HGFS_OPEN_CREATE, // create
+ HGFS_OPEN_CREATE_SAFE, // create error
+ HGFS_OPEN_CREATE_EMPTY, // create size = 0
+} HgfsOpenFlags;
+
+
+/*
+ * Write flags.
+ */
+
+typedef uint8 HgfsWriteFlags;
+
+#define HGFS_WRITE_APPEND 1
+
+
+/*
+ * Permissions bits.
+ *
+ * These are intentionally similar to Unix permissions bits, and we
+ * convert to/from Unix permissions using simple shift operations, so
+ * don't change these or you will break things.
+ */
+
+typedef uint8 HgfsPermissions;
+
+#define HGFS_PERM_READ 4
+#define HGFS_PERM_WRITE 2
+#define HGFS_PERM_EXEC 1
+
+
+/*
+ * Server-side locking (oplocks and leases).
+ *
+ * The client can ask the server to acquire opportunistic locking/leasing
+ * from the host FS on its behalf. This is communicated as part of an open request.
+ *
+ * HGFS_LOCK_OPPORTUNISTIC means that the client trusts the server
+ * to decide what kind of locking to request from the host FS.
+ * All other values tell the server explicitly the type of lock to
+ * request.
+ *
+ * The server will attempt to acquire the desired lock and will notify the client
+ * which type of lock was acquired as part of the reply to the open request.
+ * Note that HGFS_LOCK_OPPORTUNISTIC should not be specified as the type of
+ * lock acquired by the server, since HGFS_LOCK_OPPORTUNISTIC is not an
+ * actual lock.
+ */
+
+typedef enum {
+ HGFS_LOCK_NONE,
+ HGFS_LOCK_OPPORTUNISTIC,
+ HGFS_LOCK_EXCLUSIVE,
+ HGFS_LOCK_SHARED,
+} HgfsServerLock;
+
+
+/*
+ * Flags to indicate in a setattr request which fields should be
+ * updated. Deprecated.
+ */
+
+typedef uint8 HgfsAttrChanges;
+
+#define HGFS_ATTR_SIZE (1 << 0)
+#define HGFS_ATTR_CREATE_TIME (1 << 1)
+#define HGFS_ATTR_ACCESS_TIME (1 << 2)
+#define HGFS_ATTR_WRITE_TIME (1 << 3)
+#define HGFS_ATTR_CHANGE_TIME (1 << 4)
+#define HGFS_ATTR_PERMISSIONS (1 << 5)
+#define HGFS_ATTR_ACCESS_TIME_SET (1 << 6)
+#define HGFS_ATTR_WRITE_TIME_SET (1 << 7)
+
+
+/*
+ * Hints to indicate in a getattr or setattr which attributes
+ * are valid for the request.
+ * For setattr only, attributes should be set by host even if
+ * no valid values are specified by the guest.
+ */
+
+typedef uint64 HgfsAttrHint;
+
+#define HGFS_ATTR_HINT_SET_ACCESS_TIME (1 << 0)
+#define HGFS_ATTR_HINT_SET_WRITE_TIME (1 << 1)
+#define HGFS_ATTR_HINT_USE_FILE_DESC (1 << 2)
+
+/*
+ * Hint to determine using a name or a handle to determine
+ * what to delete.
+ */
+
+typedef uint64 HgfsDeleteHint;
+
+#define HGFS_DELETE_HINT_USE_FILE_DESC (1 << 0)
+
+/*
+ * Hint to determine using a name or a handle to determine
+ * what to renames.
+ */
+
+typedef uint64 HgfsRenameHint;
+
+#define HGFS_RENAME_HINT_USE_SRCFILE_DESC (1 << 0)
+#define HGFS_RENAME_HINT_USE_TARGETFILE_DESC (1 << 1)
+#define HGFS_RENAME_HINT_NO_REPLACE_EXISTING (1 << 2)
+#define HGFS_RENAME_HINT_NO_COPY_ALLOWED (1 << 3)
+
+/*
+ * File attributes.
+ *
+ * The four time fields below are in Windows NT format, which is in
+ * units of 100ns since Jan 1, 1601, UTC.
+ */
+
+/*
+ * Version 1 attributes. Deprecated.
+ * Version 2 should be using HgfsAttrV2.
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsAttr {
+ HgfsFileType type; /* File type */
+ uint64 size; /* File size (in bytes) */
+ uint64 creationTime; /* Creation time. Ignored by POSIX */
+ uint64 accessTime; /* Time of last access */
+ uint64 writeTime; /* Time of last write */
+ uint64 attrChangeTime; /* Time file attributess were last
+ * changed. Ignored by Windows */
+ HgfsPermissions permissions; /* Permissions bits */
+}
+#include "vmware_pack_end.h"
+HgfsAttr;
+
+
+/* Various flags and Windows attributes. */
+
+typedef uint64 HgfsAttrFlags;
+
+#define HGFS_ATTR_HIDDEN (1 << 0)
+#define HGFS_ATTR_SYSTEM (1 << 1)
+#define HGFS_ATTR_ARCHIVE (1 << 2)
+#define HGFS_ATTR_HIDDEN_FORCED (1 << 3)
+
+
+/*
+ * Specifies which open request fields contain
+ * valid values.
+ */
+
+typedef uint64 HgfsOpenValid;
+
+#define HGFS_OPEN_VALID_NONE 0
+#define HGFS_OPEN_VALID_MODE (1 << 0)
+#define HGFS_OPEN_VALID_FLAGS (1 << 1)
+#define HGFS_OPEN_VALID_SPECIAL_PERMS (1 << 2)
+#define HGFS_OPEN_VALID_OWNER_PERMS (1 << 3)
+#define HGFS_OPEN_VALID_GROUP_PERMS (1 << 4)
+#define HGFS_OPEN_VALID_OTHER_PERMS (1 << 5)
+#define HGFS_OPEN_VALID_FILE_ATTR (1 << 6)
+#define HGFS_OPEN_VALID_ALLOCATION_SIZE (1 << 7)
+#define HGFS_OPEN_VALID_DESIRED_ACCESS (1 << 8)
+#define HGFS_OPEN_VALID_SHARE_ACCESS (1 << 9)
+#define HGFS_OPEN_VALID_SERVER_LOCK (1 << 10)
+#define HGFS_OPEN_VALID_FILE_NAME (1 << 11)
+
+
+/*
+ * Specifies which attribute fields contain
+ * valid values.
+ */
+
+typedef uint64 HgfsAttrValid;
+
+#define HGFS_ATTR_VALID_NONE 0
+#define HGFS_ATTR_VALID_TYPE (1 << 0)
+#define HGFS_ATTR_VALID_SIZE (1 << 1)
+#define HGFS_ATTR_VALID_CREATE_TIME (1 << 2)
+#define HGFS_ATTR_VALID_ACCESS_TIME (1 << 3)
+#define HGFS_ATTR_VALID_WRITE_TIME (1 << 4)
+#define HGFS_ATTR_VALID_CHANGE_TIME (1 << 5)
+#define HGFS_ATTR_VALID_SPECIAL_PERMS (1 << 6)
+#define HGFS_ATTR_VALID_OWNER_PERMS (1 << 7)
+#define HGFS_ATTR_VALID_GROUP_PERMS (1 << 8)
+#define HGFS_ATTR_VALID_OTHER_PERMS (1 << 9)
+#define HGFS_ATTR_VALID_FLAGS (1 << 10)
+#define HGFS_ATTR_VALID_ALLOCATION_SIZE (1 << 11)
+#define HGFS_ATTR_VALID_USERID (1 << 12)
+#define HGFS_ATTR_VALID_GROUPID (1 << 13)
+#define HGFS_ATTR_VALID_FILEID (1 << 14)
+#define HGFS_ATTR_VALID_VOLID (1 << 15)
+/*
+ * Add our file and volume identifiers.
+ * NOTE: On Windows hosts, the file identifier is not guaranteed to be valid
+ * particularly with FAT. A defrag operation could cause it to change.
+ * Therefore, to not confuse older clients, and non-Windows
+ * clients we have added a separate flag.
+ * The Windows client will check for both flags for the
+ * file ID, and return the information to the guest application.
+ * However, it will use the ID internally, when it has an open
+ * handle on the server.
+ * Non-Windows clients need the file ID to be always guaranteed,
+ * which is to say, that the ID remains constant over the course of the
+ * file's lifetime, and will use the HGFS_ATTR_VALID_FILEID flag
+ * only to determine if the ID is valid.
+ */
+#define HGFS_ATTR_VALID_NON_STATIC_FILEID (1 << 16)
+
+
+/*
+ * Specifies which create dir request fields contain
+ * valid values.
+ */
+
+typedef uint64 HgfsCreateDirValid;
+
+#define HGFS_CREATE_DIR_VALID_NONE 0
+#define HGFS_CREATE_DIR_VALID_SPECIAL_PERMS (1 << 0)
+#define HGFS_CREATE_DIR_VALID_OWNER_PERMS (1 << 1)
+#define HGFS_CREATE_DIR_VALID_GROUP_PERMS (1 << 2)
+#define HGFS_CREATE_DIR_VALID_OTHER_PERMS (1 << 3)
+#define HGFS_CREATE_DIR_VALID_FILE_NAME (1 << 4)
+
+/*
+ * Version 2 of HgfsAttr
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsAttrV2 {
+ HgfsAttrValid mask; /* A bit mask to determine valid attribute fields */
+ HgfsFileType type; /* File type */
+ uint64 size; /* File size (in bytes) */
+ uint64 creationTime; /* Creation time. Ignored by POSIX */
+ uint64 accessTime; /* Time of last access */
+ uint64 writeTime; /* Time of last write */
+ uint64 attrChangeTime; /* Time file attributes were last
+ * changed. Ignored by Windows */
+ HgfsPermissions specialPerms; /* Special permissions bits (suid, etc.).
+ * Ignored by Windows */
+ HgfsPermissions ownerPerms; /* Owner permissions bits */
+ HgfsPermissions groupPerms; /* Group permissions bits. Ignored by
+ * Windows */
+ HgfsPermissions otherPerms; /* Other permissions bits. Ignored by
+ * Windows */
+ HgfsAttrFlags flags; /* Various flags and Windows 'attributes' */
+ uint64 allocationSize; /* Actual size of file on disk */
+ uint32 userId; /* User identifier, ignored by Windows */
+ uint32 groupId; /* group identifier, ignored by Windows */
+ uint64 hostFileId; /* File Id of the file on host: inode_t on Linux */
+ uint32 volumeId; /* volume identifier, non-zero is valid. */
+ uint32 reserved1; /* Reserved for future use */
+ uint64 reserved2; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsAttrV2;
+
+
+/*
+ * Cross-platform filename representation
+ *
+ * Cross-platform (CP) names are represented by a string with each
+ * path component separated by NULs, and terminated with a final NUL,
+ * but with no leading path separator.
+ *
+ * For example, the representations of a POSIX and Windows name
+ * are as follows, with "0" meaning NUL.
+ *
+ * Original name Cross-platform name
+ * -----------------------------------------------------
+ * "/home/bac/temp" -> "home0bac0temp0"
+ * "C:\temp\file.txt" -> "C0temp0file.txt0"
+ *
+ * Note that as in the example above, Windows should strip the colon
+ * off of drive letters as part of the conversion. Aside from that,
+ * all characters in each path component should be left unescaped and
+ * unmodified. Each OS is responsible for escaping any characters that
+ * are not legal in its filenames when converting FROM the CP name
+ * format, and unescaping them when converting TO the CP name format.
+ *
+ * In some requests (OPEN, GETATTR, SETATTR, DELETE, CREATE_DIR) the
+ * CP name is used to represent a particular file, but it is also used
+ * to represent a search pattern for looking up files using
+ * SEARCH_OPEN.
+ *
+ * In the current HGFS server implementation, each request has a minimum packet
+ * size that must be met for it to be considered valid. This minimum is simply
+ * the sizeof the particular request, which includes the solitary byte from the
+ * HgfsFileName struct. For these particular requests, clients add an extra
+ * byte to their payload size, without that byte being present anywhere.
+ *
+ * It isn't clear that this behavior is correct, but the end result is that
+ * neither end malfunctions, as an extra byte gets sent by the client and is
+ * ignored by the server. Unfortunately, it cannot be easily fixed. The
+ * server's minimum packet size can be changed, but the client should continue
+ * to send an extra byte, otherwise older servers with a slightly longer
+ * minimum packet size may consider the new client's packets to be too short.
+ *
+ * UTF-8 representation
+ * --------------------
+ * XXX: It is expected that file names in the HGFS protocol will be a valid UTF-8
+ * encoding.
+ * See RFC 3629 (http://tools.ietf.org/html/rfc3629)
+ *
+ * Unicode Format
+ * --------------
+ * HGFS protocol requests that contain file names as in the structure below,
+ * should contain unicode normal form C (precomposed see explanation below)
+ * characters therefore hosts such as Mac OS X which
+ * use HFS+ and unicode form D should convert names before
+ * processing or sending HGFS requests.
+ *
+ * Precomposed (normal form C) versus Decomposed (normal form D)
+ * -------------------------------------------------------------
+ * Certain Unicode characters can be encoded in more than one way.
+ * For example, an (A acute) can be encoded either precomposed,
+ * as U+00C1 (LATIN CAPITAL LETTER A WITH ACUTE), or decomposed,
+ * as U+0041 U+0301 (LATIN CAPITAL LETTER A followed by a COMBINING ACUTE ACCENT).
+ * Precomposed characters are more common in the Windows world,
+ * whereas decomposed characters are more common on the Mac.
+ *
+ * See UAX 15 (http://unicode.org/reports/tr15/)
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsFileName {
+ uint32 length; /* Does NOT include terminating NUL */
+ char name[1];
+}
+#include "vmware_pack_end.h"
+HgfsFileName;
+
+
+/*
+ * Case-sensitiviy flags are only used when any lookup is
+ * involved on the server side.
+ */
+
+typedef enum {
+ HGFS_FILE_NAME_DEFAULT_CASE,
+ HGFS_FILE_NAME_CASE_SENSITIVE,
+ HGFS_FILE_NAME_CASE_INSENSITIVE,
+} HgfsCaseType;
+
+
+/*
+ * HgfsFileNameV3 - new header to incorporate case-sensitivity flags along with
+ * Hgfs file handle.
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsFileNameV3 {
+ uint32 length; /* Does NOT include terminating NUL */
+ uint32 flags; /* Flags described below. */
+ HgfsCaseType caseType; /* Case-sensitivity type. */
+ HgfsHandle fid;
+ char name[1];
+}
+#include "vmware_pack_end.h"
+HgfsFileNameV3;
+
+
+/*
+ * HgfsFileNameV3 flags. Case-sensitiviy flags are only used when any lookup is
+ * involved on the server side.
+ */
+#define HGFS_FILE_NAME_USE_FILE_DESC (1 << 0) /* Case type ignored if set. */
+
+
+/*
+ * Request/reply structs. These are the first members of all
+ * operation request and reply messages, respectively.
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequest {
+ HgfsHandle id; /* Opaque request ID used by the requestor */
+ HgfsOp op;
+}
+#include "vmware_pack_end.h"
+HgfsRequest;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReply {
+ HgfsHandle id; /* Opaque request ID used by the requestor */
+ HgfsStatus status;
+}
+#include "vmware_pack_end.h"
+HgfsReply;
+
+
+/*
+ * Messages for our file operations.
+ */
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestOpen {
+ HgfsRequest header;
+ HgfsOpenMode mode; /* Which type of access is requested */
+ HgfsOpenFlags flags; /* Which flags to open the file with */
+ HgfsPermissions permissions; /* Which permissions to *create* a new file with */
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestOpen;
+
+
+/* Version 2 of HgfsRequestOpen */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestOpenV2 {
+ HgfsRequest header;
+ HgfsOpenValid mask; /* Bitmask that specified which fields are valid. */
+ HgfsOpenMode mode; /* Which type of access requested. See desiredAccess */
+ HgfsOpenFlags flags; /* Which flags to open the file with */
+ HgfsPermissions specialPerms; /* Desired 'special' permissions for file creation */
+ HgfsPermissions ownerPerms; /* Desired 'owner' permissions for file creation */
+ HgfsPermissions groupPerms; /* Desired 'group' permissions for file creation */
+ HgfsPermissions otherPerms; /* Desired 'other' permissions for file creation */
+ HgfsAttrFlags attr; /* Attributes, if any, for file creation */
+ uint64 allocationSize; /* How much space to pre-allocate during creation */
+ uint32 desiredAccess; /* Extended support for windows access modes */
+ uint32 shareAccess; /* Windows only, share access modes */
+ HgfsServerLock desiredLock; /* The type of lock desired by the client */
+ uint64 reserved1; /* Reserved for future use */
+ uint64 reserved2; /* Reserved for future use */
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestOpenV2;
+
+
+/* Version 3 of HgfsRequestOpen */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestOpenV3 {
+ HgfsOpenValid mask; /* Bitmask that specified which fields are valid. */
+ HgfsOpenMode mode; /* Which type of access requested. See desiredAccess */
+ HgfsOpenFlags flags; /* Which flags to open the file with */
+ HgfsPermissions specialPerms; /* Desired 'special' permissions for file creation */
+ HgfsPermissions ownerPerms; /* Desired 'owner' permissions for file creation */
+ HgfsPermissions groupPerms; /* Desired 'group' permissions for file creation */
+ HgfsPermissions otherPerms; /* Desired 'other' permissions for file creation */
+ HgfsAttrFlags attr; /* Attributes, if any, for file creation */
+ uint64 allocationSize; /* How much space to pre-allocate during creation */
+ uint32 desiredAccess; /* Extended support for windows access modes */
+ uint32 shareAccess; /* Windows only, share access modes */
+ HgfsServerLock desiredLock; /* The type of lock desired by the client */
+ uint64 reserved1; /* Reserved for future use */
+ uint64 reserved2; /* Reserved for future use */
+ HgfsFileNameV3 fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestOpenV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyOpen {
+ HgfsReply header;
+ HgfsHandle file; /* Opaque file ID used by the server */
+}
+#include "vmware_pack_end.h"
+HgfsReplyOpen;
+
+
+/* Version 2 of HgfsReplyOpen */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyOpenV2 {
+ HgfsReply header;
+ HgfsHandle file; /* Opaque file ID used by the server */
+ HgfsServerLock acquiredLock; /* The type of lock acquired by the server */
+}
+#include "vmware_pack_end.h"
+HgfsReplyOpenV2;
+
+
+/* Version 3 of HgfsReplyOpen */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyOpenV3 {
+ HgfsHandle file; /* Opaque file ID used by the server */
+ HgfsServerLock acquiredLock; /* The type of lock acquired by the server */
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplyOpenV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestRead {
+ HgfsRequest header;
+ HgfsHandle file; /* Opaque file ID used by the server */
+ uint64 offset;
+ uint32 requiredSize;
+}
+#include "vmware_pack_end.h"
+HgfsRequestRead;
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyRead {
+ HgfsReply header;
+ uint32 actualSize;
+ char payload[1];
+}
+#include "vmware_pack_end.h"
+HgfsReplyRead;
+
+
+/*
+ * Version 3 of HgfsRequestRead.
+ * Server must support HGFS_LARGE_PACKET_MAX to implement this op.
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestReadV3 {
+ HgfsHandle file; /* Opaque file ID used by the server */
+ uint64 offset;
+ uint32 requiredSize;
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsRequestReadV3;
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyReadV3 {
+ uint32 actualSize;
+ uint64 reserved; /* Reserved for future use */
+ char payload[1];
+}
+#include "vmware_pack_end.h"
+HgfsReplyReadV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestWrite {
+ HgfsRequest header;
+ HgfsHandle file; /* Opaque file ID used by the server */
+ HgfsWriteFlags flags;
+ uint64 offset;
+ uint32 requiredSize;
+ char payload[1];
+}
+#include "vmware_pack_end.h"
+HgfsRequestWrite;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyWrite {
+ HgfsReply header;
+ uint32 actualSize;
+}
+#include "vmware_pack_end.h"
+HgfsReplyWrite;
+
+/*
+ * Version 3 of HgfsRequestWrite.
+ * Server must support HGFS_LARGE_PACKET_MAX to implement this op.
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestWriteV3 {
+ HgfsHandle file; /* Opaque file ID used by the server */
+ HgfsWriteFlags flags;
+ uint64 offset;
+ uint32 requiredSize;
+ uint64 reserved; /* Reserved for future use */
+ char payload[1];
+}
+#include "vmware_pack_end.h"
+HgfsRequestWriteV3;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyWriteV3 {
+ uint32 actualSize;
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplyWriteV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestClose {
+ HgfsRequest header;
+ HgfsHandle file; /* Opaque file ID used by the server */
+}
+#include "vmware_pack_end.h"
+HgfsRequestClose;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyClose {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplyClose;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestCloseV3 {
+ HgfsHandle file; /* Opaque file ID used by the server */
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsRequestCloseV3;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyCloseV3 {
+ uint64 reserved;
+}
+#include "vmware_pack_end.h"
+HgfsReplyCloseV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSearchOpen {
+ HgfsRequest header;
+ HgfsFileName dirName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestSearchOpen;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSearchOpenV3 {
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 dirName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestSearchOpenV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySearchOpen {
+ HgfsReply header;
+ HgfsHandle search; /* Opaque search ID used by the server */
+}
+#include "vmware_pack_end.h"
+HgfsReplySearchOpen;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySearchOpenV3 {
+ HgfsHandle search; /* Opaque search ID used by the server */
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplySearchOpenV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSearchRead {
+ HgfsRequest header;
+ HgfsHandle search; /* Opaque search ID used by the server */
+ uint32 offset; /* The first result is offset 0 */
+}
+#include "vmware_pack_end.h"
+HgfsRequestSearchRead;
+
+
+/* Version 2 of HgfsRequestSearchRead */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSearchReadV2 {
+ HgfsRequest header;
+ HgfsHandle search; /* Opaque search ID used by the server */
+ uint32 offset; /* The first result is offset 0 */
+}
+#include "vmware_pack_end.h"
+HgfsRequestSearchReadV2;
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSearchReadV3 {
+ HgfsHandle search; /* Opaque search ID used by the server */
+ uint32 offset; /* The first result is offset 0 */
+ uint32 flags; /* Reserved for reading multiple directory entries. */
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsRequestSearchReadV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySearchRead {
+ HgfsReply header;
+ HgfsAttr attr;
+ HgfsFileName fileName;
+ /* fileName.length = 0 means "no entry at this offset" */
+}
+#include "vmware_pack_end.h"
+HgfsReplySearchRead;
+
+
+/* Version 2 of HgfsReplySearchRead */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySearchReadV2 {
+ HgfsReply header;
+ HgfsAttrV2 attr;
+
+ /*
+ * fileName.length = 0 means "no entry at this offset"
+ * If the file is a symlink (as specified in attr)
+ * this name is the name of the symlink, not the target.
+ */
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsReplySearchReadV2;
+
+
+/* Directory entry structure. */
+
+typedef struct HgfsDirEntry {
+ uint32 nextEntry;
+ HgfsAttrV2 attr;
+
+ /*
+ * fileName.length = 0 means "no entry at this offset"
+ * If the file is a symlink (as specified in attr)
+ * this name is the name of the symlink, not the target.
+ */
+ HgfsFileNameV3 fileName;
+} HgfsDirEntry;
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySearchReadV3 {
+ uint64 count; /* Number of directory entries. */
+ uint64 reserved; /* Reserved for future use. */
+ char payload[1]; /* Directory entries. */
+}
+#include "vmware_pack_end.h"
+HgfsReplySearchReadV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSearchClose {
+ HgfsRequest header;
+ HgfsHandle search; /* Opaque search ID used by the server */
+}
+#include "vmware_pack_end.h"
+HgfsRequestSearchClose;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySearchClose {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplySearchClose;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSearchCloseV3 {
+ HgfsHandle search; /* Opaque search ID used by the server */
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsRequestSearchCloseV3;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySearchCloseV3 {
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplySearchCloseV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestGetattr {
+ HgfsRequest header;
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestGetattr;
+
+
+/* Version 2 of HgfsRequestGetattr */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestGetattrV2 {
+ HgfsRequest header;
+ HgfsAttrHint hints; /* Flags for file handle valid. */
+ HgfsHandle file; /* Opaque file ID used by the server. */
+ HgfsFileName fileName; /* Filename used when file handle invalid. */
+}
+#include "vmware_pack_end.h"
+HgfsRequestGetattrV2;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestGetattrV3 {
+ HgfsAttrHint hints; /* Flags for file handle valid. */
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 fileName; /* Filename used when file handle invalid. */
+}
+#include "vmware_pack_end.h"
+HgfsRequestGetattrV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyGetattr {
+ HgfsReply header;
+ HgfsAttr attr;
+}
+#include "vmware_pack_end.h"
+HgfsReplyGetattr;
+
+
+/* Version 2 of HgfsReplyGetattr */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyGetattrV2 {
+ HgfsReply header;
+ HgfsAttrV2 attr;
+
+ /*
+ * If the file is a symlink, as specified in attr.type, then this is
+ * the target for the symlink. If the file is not a symlink, this should
+ * be ignored.
+ *
+ * This filename is in "CPNameLite" format. See CPNameLite.c for details.
+ */
+ HgfsFileName symlinkTarget;
+}
+#include "vmware_pack_end.h"
+HgfsReplyGetattrV2;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyGetattrV3 {
+ HgfsAttrV2 attr;
+
+ /*
+ * If the file is a symlink, as specified in attr.type, then this is
+ * the target for the symlink. If the file is not a symlink, this should
+ * be ignored.
+ *
+ * This filename is in "CPNameLite" format. See CPNameLite.c for details.
+ */
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 symlinkTarget;
+}
+#include "vmware_pack_end.h"
+HgfsReplyGetattrV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSetattr {
+ HgfsRequest header;
+ HgfsAttrChanges update; /* Which fields need to be updated */
+ HgfsAttr attr;
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestSetattr;
+
+
+/* Version 2 of HgfsRequestSetattr */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSetattrV2 {
+ HgfsRequest header;
+ HgfsAttrHint hints;
+ HgfsAttrV2 attr;
+ HgfsHandle file; /* Opaque file ID used by the server. */
+ HgfsFileName fileName; /* Filename used when file handle invalid. */
+}
+#include "vmware_pack_end.h"
+HgfsRequestSetattrV2;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSetattrV3 {
+ HgfsAttrHint hints;
+ HgfsAttrV2 attr;
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 fileName; /* Filename used when file handle invalid. */
+}
+#include "vmware_pack_end.h"
+HgfsRequestSetattrV3;
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySetattr {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplySetattr;
+
+
+/* Version 2 of HgfsReplySetattr */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySetattrV2 {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplySetattrV2;
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySetattrV3 {
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplySetattrV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestCreateDir {
+ HgfsRequest header;
+ HgfsPermissions permissions;
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestCreateDir;
+
+
+/* Version 2 of HgfsRequestCreateDir */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestCreateDirV2 {
+ HgfsRequest header;
+ HgfsCreateDirValid mask;
+ HgfsPermissions specialPerms;
+ HgfsPermissions ownerPerms;
+ HgfsPermissions groupPerms;
+ HgfsPermissions otherPerms;
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestCreateDirV2;
+
+
+/* Version 3 of HgfsRequestCreateDir */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestCreateDirV3 {
+ HgfsCreateDirValid mask;
+ HgfsPermissions specialPerms;
+ HgfsPermissions ownerPerms;
+ HgfsPermissions groupPerms;
+ HgfsPermissions otherPerms;
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestCreateDirV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyCreateDir {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplyCreateDir;
+
+
+/* Version 2 of HgfsReplyCreateDir */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyCreateDirV2 {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplyCreateDirV2;
+
+
+/* Version 3 of HgfsReplyCreateDir */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyCreateDirV3 {
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplyCreateDirV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestDelete {
+ HgfsRequest header;
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestDelete;
+
+
+/* Version 2 of HgfsRequestDelete */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestDeleteV2 {
+ HgfsRequest header;
+ HgfsDeleteHint hints;
+ HgfsHandle file; /* Opaque file ID used by the server. */
+ HgfsFileName fileName; /* Name used if the file is HGFS_HANDLE_INVALID */
+}
+#include "vmware_pack_end.h"
+HgfsRequestDeleteV2;
+
+
+/* Version 3 of HgfsRequestDelete */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestDeleteV3 {
+ HgfsDeleteHint hints;
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 fileName; /* Name used if the file is HGFS_HANDLE_INVALID */
+}
+#include "vmware_pack_end.h"
+HgfsRequestDeleteV3;
+
+
+/* Deprecated */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyDelete {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplyDelete;
+
+/* Version 2 of HgfsReplyDelete */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyDeleteV2 {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplyDeleteV2;
+
+
+/* Version 2 of HgfsReplyDelete */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyDeleteV3 {
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplyDeleteV3;
+
+
+/*
+ * The size of the HgfsFileName struct is variable depending on the
+ * length of the name, so you can't use request->newName to get the
+ * actual address of the new name, because where it starts is
+ * dependant on how long the oldName is. To get the address of
+ * newName, use this:
+ *
+ * &oldName + sizeof(HgfsFileName) + oldName.length
+ */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestRename {
+ HgfsRequest header;
+ HgfsFileName oldName;
+ HgfsFileName newName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestRename;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyRename {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplyRename;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestRenameV2 {
+ HgfsRequest header;
+ HgfsRenameHint hints;
+ HgfsHandle srcFile; /* Opaque file ID to "old name" used by the server. */
+ HgfsHandle targetFile; /* Opaque file ID to "old name" used by the server. */
+ HgfsFileName oldName;
+ HgfsFileName newName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestRenameV2;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyRenameV2 {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplyRenameV2;
+
+
+/* HgfsRequestRename and HgfsReplyRename for v3. */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestRenameV3 {
+ HgfsRenameHint hints;
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 oldName;
+ HgfsFileNameV3 newName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestRenameV3;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyRenameV3 {
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplyRenameV3;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestQueryVolume {
+ HgfsRequest header;
+ HgfsFileName fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestQueryVolume;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyQueryVolume {
+ HgfsReply header;
+ uint64 freeBytes;
+ uint64 totalBytes;
+}
+#include "vmware_pack_end.h"
+HgfsReplyQueryVolume;
+
+
+/* HgfsRequestQueryVolume and HgfsReplyQueryVolume for v3. */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestQueryVolumeV3 {
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 fileName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestQueryVolumeV3;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyQueryVolumeV3 {
+ uint64 freeBytes;
+ uint64 totalBytes;
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplyQueryVolumeV3;
+
+
+
+/* New operations for Version 2 */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestServerLockChange {
+ HgfsRequest header;
+ HgfsHandle file;
+ HgfsServerLock newServerLock;
+}
+#include "vmware_pack_end.h"
+HgfsRequestServerLockChange;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplyServerLockChange {
+ HgfsReply header;
+ HgfsServerLock serverLock;
+}
+#include "vmware_pack_end.h"
+HgfsReplyServerLockChange;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSymlinkCreate {
+ HgfsRequest header;
+ HgfsFileName symlinkName;
+
+ /* This filename is in "CPNameLite" format. See CPNameLite.c for details. */
+ HgfsFileName targetName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestSymlinkCreate;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySymlinkCreate {
+ HgfsReply header;
+}
+#include "vmware_pack_end.h"
+HgfsReplySymlinkCreate;
+
+
+/* HgfsRequestSymlinkCreate and HgfsReplySymlinkCreate for v3. */
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsRequestSymlinkCreateV3 {
+ uint64 reserved; /* Reserved for future use */
+ HgfsFileNameV3 symlinkName;
+
+ /* This filename is in "CPNameLite" format. See CPNameLite.c for details. */
+ HgfsFileNameV3 targetName;
+}
+#include "vmware_pack_end.h"
+HgfsRequestSymlinkCreateV3;
+
+
+typedef
+#include "vmware_pack_begin.h"
+struct HgfsReplySymlinkCreateV3 {
+ uint64 reserved; /* Reserved for future use */
+}
+#include "vmware_pack_end.h"
+HgfsReplySymlinkCreateV3;
+
+
+#endif /* _HGFS_PROTO_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * hgfsSolaris.h --
+ *
+ * Contains declarations needed in entire Solaris HGFS module.
+ *
+ */
+
+/* XXX: Split the non-kernel parts off into another header */
+
+#ifndef __HGFS_H_
+#define __HGFS_H_
+
+/*
+ * System includes
+ */
+
+/*
+ * Kernel system includes
+ */
+#ifdef _KERNEL
+
+/*
+ * These must be in this order, so we include them here, and only here, so
+ * that this ordering is guaranteed.
+ *
+ * <sys/types.h> defines min and max macros (that shouldn't be used because
+ * they're broken) and <sys/conf.h> includes <sys/systm.h> which defines
+ * functions named min and max. The function definitions must come first.
+ */
+#include <sys/conf.h> /* D_NEW flag and other includes */
+#include <sys/types.h> /* various typedefs */
+
+#endif /* _KERNEL */
+
+/*
+ * VMware includes
+ */
+#include "hgfsProto.h"
+#include "hgfsState.h"
+#include "filesystem.h"
+
+/*
+ * Kernel VMware includes
+ */
+#ifdef _KERNEL
+
+#include "dbllnklst.h"
+#include "vm_assert.h"
+
+#endif /* _KERNEL */
+
+
+/*
+ * Macros
+ */
+
+#define HGFS_PAYLOAD_MAX(reply) (HGFS_PACKET_MAX - sizeof *reply)
+#define HGFS_FS_NAME "vmhgfs"
+#define HGFS_BLOCKSIZE 1024
+
+
+/*
+ * Kernel only macros
+ */
+#ifdef _KERNEL
+
+/* Determines size of request pool */
+#define HGFS_MAX_OUTSTANDING_REQS 4
+
+/* For ddi_soft_state_init() call in _init() */
+#define HGFS_EXPECTED_INSTANCES 23
+
+/* HGFS cmn_err() levels */
+#define HGFS_ERROR (CE_WARN)
+
+/* Internal error code(s) */
+#define HGFS_ERR (-1)
+#define HGFS_ERR_NULL_INPUT (-50)
+#define HGFS_ERR_NODEV (-51)
+#define HGFS_ERR_INVAL (-52)
+
+/* This is what shows up after the ':' in the /devices entry */
+#define HGFS_DEV_NAME "vmware-hgfs"
+
+/*
+ * Don't change this to KM_NOSLEEP without first making sure that we handle
+ * the possibility of kmem_zalloc() failing: KM_SLEEP guarantees it won't fail
+ */
+#define HGFS_ALLOC_FLAG (KM_SLEEP)
+
+/*
+ * hgfsInstance is uninitialized when device is not open and hgfsType is
+ * uninitialized before the file system initialization routine is called.
+ */
+#define HGFS_INSTANCE_UNINITIALIZED (-1)
+#define HGFS_TYPE_UNINITIALIZED (-1)
+
+/* Accessing root inode and vnode */
+#define HGFS_ROOT_VNODE(sip) (sip->rootVnode)
+
+
+#endif /* _KERNEL */
+
+
+/*
+ * Structures
+ */
+
+/* We call them *Header in the Solaris code for clarity. */
+typedef HgfsReply HgfsReplyHeader;
+typedef HgfsRequest HgfsRequestHeader;
+
+/*
+ * Kernel only structures and variables
+ */
+#ifdef _KERNEL
+
+/*
+ * The global state structure for the entire module. This is allocated in
+ * HgfsDevAttach() and deallocated in HgfsDevDetach().
+ *
+ * Note that reqMutex and reqFreeList are also used for synchronization between
+ * the filesystem and driver. See docs/synchronization.txt for details.
+ */
+typedef struct HgfsSuperInfo {
+ dev_info_t *dip; /* Device information pointer */
+ Bool devOpen; /* Flag indicating whether device is open */
+ /* Poll */
+ struct pollhead hgfsPollhead; /* Needed for chpoll() implementation */
+ Bool pollwakeupOnWrite; /* Flag which indicates need to call
+ pollwakeup() on write() -- no mutex
+ since only modified in chpoll() */
+ /* Request list */
+ DblLnkLst_Links reqList; /* Anchor for pending request list */
+ kmutex_t reqMutex; /* For protection of request list */
+ kcondvar_t reqCondVar; /* For waiting on request list */
+ /* Free request list */
+ DblLnkLst_Links reqFreeList; /* Anchor for free request list */
+ kmutex_t reqFreeMutex; /* For protection of reqFreeList */
+ kcondvar_t reqFreeCondVar; /* For waiting on free request list */
+ /* For filesystem */
+ struct vfs *vfsp; /* Our filesystem structure */
+ struct vnode *rootVnode; /* Root vnode of the filesystem */
+ HgfsFileHashTable fileHashTable; /* File hash table */
+# if HGFS_VFS_VERSION > 2
+ /* We keep a pointer to the vnodeops the Kernel created for us in Sol 10 */
+ struct vnodeops *vnodeOps; /* Operations on files */
+# endif
+} HgfsSuperInfo;
+
+/*
+ * Each request will traverse through this set of states. See
+ * docs/request-lifecycle.txt for an explanation of these and the API for
+ * interacting with requests.
+ */
+typedef enum {
+ HGFS_REQ_UNUSED = 1,
+ HGFS_REQ_ALLOCATED,
+ HGFS_REQ_SUBMITTED,
+ HGFS_REQ_ABANDONED,
+ HGFS_REQ_ERROR,
+ HGFS_REQ_COMPLETED
+} HgfsReqState;
+
+
+/*
+ * General request structure. Specific requests and replies are placed in the
+ * packet of this structure.
+ */
+typedef struct HgfsReq {
+ DblLnkLst_Links listNode; /* Node to connect the request to one of
+ * the lists (free or pending) */
+ kcondvar_t condVar; /* Condition variable to wait for and
+ signal presence of reply. Used with
+ the reqMutex in HgfsSuperInfo. */
+ HgfsReqState state; /* Indicates state of request */
+ kmutex_t stateLock; /* Protects state: only used in
+ HgfsReq{S,G}etState() */
+
+ uint32_t id; /* The unique identifier of this request */
+
+ uint32_t packetSize; /* Total size of packet */
+ char packet[HGFS_PACKET_MAX]; /* Contains both requests and replies */
+} HgfsReq;
+
+
+/*
+ * Global Variables
+ */
+
+/* Pool of request structures */
+HgfsReq requestPool[HGFS_MAX_OUTSTANDING_REQS];
+
+/*
+ * Fileystem type number.
+ *
+ * This needs to be stored here rather than in the Superinfo because the soft
+ * state is not guaranteed to have been allocated when HgfsInit() is called.
+ */
+int hgfsType;
+
+/*
+ * Used to access shared state of driver and filesystem. superInfoHead is
+ * a pointer to state managed by Solaris, hgfsInstance is the index into this state
+ * list, and is set in HgfsDevAttach().
+ *
+ * Note that both the driver and filesystem use ddi_get_soft_state() to get
+ * a pointer to the superinfo. Both use superInfoHead, but the device uses the
+ * instance number derived from passed in arguments and the filesystem uses
+ * hgfsInstance. This is not a problem as long as the instance number cannot
+ * change, which /should/ be guaranteed, and there is only a single instance,
+ * which cannot happen.
+ */
+void *superInfoHead;
+int hgfsInstance;
+
+#endif /* _KERNEL */
+
+#endif /* __HGFS_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * hgfsState.c --
+ *
+ * This implements the functions that provide all the filesystem specific state
+ * that is placed underneath the vnodes.
+ *
+ */
+
+
+/*
+ * Includes
+ */
+
+#include "vnode.h"
+#include "hgfsState.h"
+#include "debug.h"
+
+#include "vm_assert.h"
+#include "vm_basic_types.h"
+#include "sha1.h" /* SHA-1 for Node ID calculation */
+
+
+/*
+ * Macros
+ */
+#define HGFS_VNODE_INIT_FLAG VNOMAP /* So we don't have to implement mmap() */
+#define HGFS_VNODE_INIT_COUNT 1
+#define HGFS_VNODE_INIT_RDEV VBLK /* We pretend to be a block device */
+
+#define HGFS_FILE_HT_HEAD(ht, index) (ht->hashTable[index]).next
+#define HGFS_FILE_HT_BUCKET(ht, index) (&ht->hashTable[index])
+
+#define HGFS_IS_ROOT_FILE(sip, file) (HGFS_VP_TO_FP(sip->rootVnode) == file)
+
+/*
+ * Prototypes for internal functions
+ */
+
+/* Allocation/initialization/free of open file state */
+static HgfsOpenFile *HgfsAllocOpenFile(const char *fileName, HgfsFileType fileType,
+ HgfsFileHashTable *htp);
+static void HgfsFreeOpenFile(HgfsOpenFile *ofp, HgfsFileHashTable *htp);
+
+/* Acquiring/releasing file state */
+static HgfsFile *HgfsGetFile(const char *fileName, HgfsFileType fileType,
+ HgfsFileHashTable *htp);
+static void HgfsReleaseFile(HgfsFile *fp, HgfsFileHashTable *htp);
+static int HgfsInitFile(HgfsFile *fp, const char *fileName, HgfsFileType fileType);
+
+/* Adding/finding/removing file state from hash table */
+static inline void HgfsAddFile(HgfsFile *fp, HgfsFileHashTable *htp);
+static inline void HgfsRemoveFile(HgfsFile *fp, HgfsFileHashTable *htp);
+static HgfsFile *HgfsFindFile(const char *fileName, HgfsFileHashTable *htp);
+
+/* Other utility functions */
+static unsigned int HgfsFileNameHash(const char *fileName);
+static void HgfsNodeIdHash(const char *fileName, uint32_t fileNameLength,
+ ino64_t *outHash);
+
+
+/*
+ * Public functions
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsVnodeGet --
+ *
+ * Creates a vnode for the provided filename.
+ *
+ * This will always allocate a vnode and HgfsOpenFile. If a HgfsFile
+ * already exists for this filename then that is used, if a HgfsFile doesn't
+ * exist, one is created.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * If the HgfsFile already exists, its reference count is incremented;
+ * otherwise a HgfsFile is created.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsVnodeGet(struct vnode **vpp, // OUT: Filled with address of created vnode
+ HgfsSuperInfo *sip, // IN: Superinfo
+ struct vfs *vfsp, // IN: Filesystem structure
+ const char *fileName, // IN: Name of this file
+ HgfsFileType fileType, // IN: Type of file
+ HgfsFileHashTable *htp) // IN: File hash table
+{
+ struct vnode *vp;
+
+ ASSERT(vpp);
+ ASSERT(sip);
+ ASSERT(vfsp);
+ ASSERT(fileName);
+ ASSERT(htp);
+
+ /*
+ * Here we need to construct the vnode for the kernel as well as our
+ * internal file system state. Our internal state consists of
+ * a HgfsOpenFile and a HgfsFile. The HgfsOpenFile is state kept per-open
+ * file; the HgfsFile state is kept per-file. We have a one-to-one mapping
+ * between vnodes and HgfsOpenFiles, and a many-to-one mapping from each of
+ * those to a HgfsFile.
+ *
+ * Note that it appears the vnode is intended to be used as a per-file
+ * structure, but we are using it as a per-open-file. The sole exception
+ * for this is the root vnode because it is returned by HgfsRoot(). This
+ * also means that reference counts for all vnodes except the root should
+ * be one; the reference count in our HgfsFile takes on the role of the
+ * vnode reference count.
+ */
+ /*
+ * Note that we no longer embed the vnode in our private structure in
+ * Solaris 9. This was done to simplify this code path and decrease the
+ * differences between Solaris 9 and 10.
+ */
+# ifdef SOL9
+ vp = kmem_zalloc(sizeof *vp, HGFS_ALLOC_FLAG);
+# else
+ vp = vn_alloc(HGFS_ALLOC_FLAG);
+# endif
+ if (!vp) {
+ return HGFS_ERR;
+ }
+
+ /*
+ * Now we'll initialize the vnode. We need to set the file type, vnode
+ * operations, flags, filesystem pointer, reference count, and device.
+ * After that we'll create our private structures and hang them from the
+ * vnode's v_data pointer.
+ */
+ switch (fileType) {
+ case HGFS_FILE_TYPE_REGULAR:
+ vp->v_type = VREG;
+ break;
+
+ case HGFS_FILE_TYPE_DIRECTORY:
+ vp->v_type = VDIR;
+ break;
+
+ default:
+ /* Hgfs only supports directories and regular files */
+ goto vnode_error;
+ }
+
+ /*
+ * Now set the vnode operations. This is handled differently on Solaris
+ * 9 and 10, and we call HgfsSetVnodeOps() to take care of this for us.
+ */
+ if (HgfsSetVnodeOps(sip, vp) != 0) {
+ goto vnode_error;
+ }
+
+ /*
+ * The vnode cache constructor will have initialized the mutex for us on
+ * Solaris 10, so we only do it ourselves on Solaris 9.
+ */
+# ifdef SOL9
+ mutex_init(&vp->v_lock, NULL, MUTEX_DRIVER, NULL);
+# endif
+
+ vp->v_flag = HGFS_VNODE_INIT_FLAG;
+ vp->v_count = HGFS_VNODE_INIT_COUNT;
+ vp->v_vfsp = vfsp;
+ vp->v_rdev = HGFS_VNODE_INIT_RDEV;
+
+ /*
+ * We now allocate our private open file structure. This will correctly
+ * initialize the per-open-file state, as well as locate (or create if
+ * necessary) the per-file state.
+ */
+ vp->v_data = (void *)HgfsAllocOpenFile(fileName, fileType, htp);
+ if (!vp->v_data) {
+ goto openfile_error;
+ }
+
+ /* Fill in the provided address with the new vnode. */
+ *vpp = vp;
+
+ /* Return success */
+ return 0;
+
+ /* Cleanup points for errors. */
+openfile_error:
+# ifdef SOL9
+ mutex_destroy(&vp->v_lock);
+# endif
+vnode_error:
+# ifdef SOL9
+ kmem_free(vp, sizeof *vp);
+# else
+ vn_free(vp);
+# endif
+ return HGFS_ERR;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsVnodePut --
+ *
+ * Releases the provided vnode.
+ *
+ * This will always free both the vnode and its associated HgfsOpenFile.
+ * The HgfsFile's reference count is decremented and, if 0, freed.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsVnodePut(struct vnode *vp, // IN: Vnode to release
+ HgfsFileHashTable *htp) // IN: Hash table pointer
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(vp);
+ ASSERT(htp);
+
+ /* Get our private open-file state. */
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) { // XXX Maybe ASSERT() this?
+ return HGFS_ERR;
+ }
+
+ /*
+ * We need to free the open file structure. This takes care of releasing
+ * our reference on the underlying file structure (and freeing it if
+ * necessary).
+ */
+ HgfsFreeOpenFile(ofp, htp);
+
+ /*
+ * Now we clean up the vnode.
+ */
+# ifdef SOL9
+ mutex_destroy(&vp->v_lock);
+# endif
+
+# ifdef SOL9
+ kmem_free(vp, sizeof *vp);
+# else
+ vn_free(vp);
+# endif
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsVnodeDup --
+ *
+ * Duplicates the vnode and HgfsOpenFile (per-open state) of a file and
+ * increments the reference count of the underlying HgfsFile. This function
+ * just calls HgfsVnodeGet with the right arguments.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure. On success
+ * the address of the duplicated vnode is written to newVpp.
+ *
+ * Side effects:
+ * The HgfsFile for origVp will have an additional reference.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsVnodeDup(struct vnode **newVpp, // OUT: Given address of new vnode
+ struct vnode *origVp, // IN: Vnode to duplicate
+ struct HgfsSuperInfo *sip, // IN: Superinfo pointer
+ HgfsFileHashTable *htp) // IN: File hash table
+{
+ ASSERT(newVpp);
+ ASSERT(origVp);
+ ASSERT(sip);
+ ASSERT(htp);
+
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsVnodeDup: duping %s\n", HGFS_VP_TO_FILENAME(origVp));
+
+ return HgfsVnodeGet(newVpp, sip, origVp->v_vfsp, HGFS_VP_TO_FILENAME(origVp),
+ HGFS_VP_TO_HGFSFILETYPE(origVp), &sip->fileHashTable);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFileNameToVnode --
+ *
+ * Allocates new per-open-file state if a HgfsFile for fileName exists in
+ * the provided file hash table.
+ *
+ * Results:
+ * Returns 0 on success or a non-zero error code on failure. On success,
+ * vpp is filled with the address of the new per-open state.
+ *
+ * Side effects:
+ * The reference count of the HgfsFile for fileName is incremented if it
+ * exists.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsFileNameToVnode(const char *fileName,
+ struct vnode **vpp,
+ struct HgfsSuperInfo *sip,
+ struct vfs *vfsp,
+ HgfsFileHashTable *htp)
+{
+ HgfsFile *fp;
+ HgfsFileType fileType;
+
+ ASSERT(vpp);
+ ASSERT(sip);
+ ASSERT(vfsp);
+ ASSERT(fileName);
+ ASSERT(htp);
+
+ /*
+ * XXX: This locking here is not totally correct. Because we are calling
+ * HgfsVnodeGet(), which does its own locking on the hash table, we must
+ * make finding out the file is in the hash table and then creating our
+ * internal state that increments that file's reference count non-atomic.
+ *
+ * Because of this, it is possible for the file to be in the hash table when
+ * we look, then be removed by the time we look for it again. As
+ * a consquence, we will add the file to the hash table then. This is
+ * partially correct in the fact that the file was in the hash table when we
+ * looked, but it is partially incorrect since it wasn't in the hash table
+ * when we looked again. In practice this shouldn't cause any problems, but
+ * it is possible for a file that is deleted on the host to remain in our
+ * hash table longer than it should.
+ *
+ * A more correct locking scheme was not used because the complexity of
+ * doing so outweighed the problems that can occur from this more simple
+ * approach. This approach was also left as is because it provides an
+ * optimization to the filesystem by decreasing the number of requests that
+ * must be sent significantly. This optimization can be easily turned off
+ * by commenting out the single call to this function in HgfsLookup() in
+ * vnode.c.
+ *
+ * Possible solutions to this are: 1) adding new locks to the top-level
+ * public functions (HgfsVnodeGet(), FileNameToVnode(), and NodeIdGet()), 2)
+ * bringing the hash table locking up to those same functions, or 3)
+ * reimplementing much of the call sequence down to the calls to FindFile()
+ * and AddFile() in HgfsGetFile() that is specific to this function. 1 is
+ * likely the best option as 2 uses hash table locks for an incorrect
+ * purpose and 3 will create a significant amount of repeated code.
+ */
+
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsFileNameToVnode: looking for %s\n", fileName);
+
+ mutex_enter(&htp->mutex);
+
+ fp = HgfsFindFile(fileName, htp);
+ if (!fp) {
+ mutex_exit(&htp->mutex);
+ return HGFS_ERR;
+ }
+
+ /* Guaranteed by HgfsFindFile(). */
+ ASSERT(strcmp(fileName, fp->fileName) == 0);
+
+ /*
+ * We save the type of this file with the lock held in case it goes away:
+ * see the above comment about locking.
+ */
+ fileType = fp->fileType;
+
+ mutex_exit(&htp->mutex);
+
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsFileNameToVnode: found %s\n", fileName);
+
+ return HgfsVnodeGet(vpp, sip, vfsp, fileName, fileType, htp);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsNodeIdGet --
+ *
+ * Gets the node id for the provided file. This will only calculate the
+ * node id again if a per-file state structure doesn't yet exist for this
+ * file. (This situation exists on a readdir since dentries are filled in
+ * rather than creating vnodes.)
+ *
+ * In Solaris, node ids are provided in vnodes and inode numbers are
+ * provided in dentries. For applications to work correctly, we must make
+ * sure that the inode number of a file's dentry and the node id in a file's
+ * vnode match one another. This poses a problem since vnodes typically do
+ * not exist when dentries need to be created, and once a dentry is created
+ * we have no reference to it since it is copied to the user and freed from
+ * kernel space. An example of a program that breaks when these values
+ * don't match is /usr/bin/pwd. This program first acquires the node id of
+ * "." from its vnode, then traverses backwards to ".." and looks for the
+ * dentry in that directory with the inode number matching the node id.
+ * (This is how it obtains the name of the directory it was just in.)
+ * /usr/bin/pwd repeats this until it reaches the root directory, at which
+ * point it concatenates the filenames it acquired along the way and
+ * displays them to the user. When inode numbers don't match the node id,
+ * /usr/bin/pwd displays an error saying it cannot determine the directory.
+ *
+ * The Hgfs protocol does not provide us with unique identifiers for files
+ * since it must support filesystems that do not have the concept of inode
+ * numbers. Therefore, we must maintain a mapping from filename to node id/
+ * inode numbers. This is done in a stateless manner by calculating the
+ * SHA-1 hash of the filename. All points in the Hgfs code that need a node
+ * id/inode number obtain it by either calling this function or directly
+ * referencing the saved node id value in the vnode, if one is available.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+HgfsNodeIdGet(HgfsFileHashTable *htp, // IN: File hash table
+ const char *fileName, // IN: Filename to get node id for
+ uint32_t fileNameLength, // IN: Length of filename
+ ino64_t *outNodeId) // OUT: Destination for nodeid
+{
+ HgfsFile *fp;
+
+ ASSERT(htp);
+ ASSERT(fileName);
+ ASSERT(outNodeId);
+
+ mutex_enter(&htp->mutex);
+
+ fp = HgfsFindFile(fileName, htp);
+ if (fp) {
+ *outNodeId = fp->nodeId;
+ } else {
+ HgfsNodeIdHash(fileName, fileNameLength, outNodeId);
+ }
+
+ mutex_exit(&htp->mutex);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsInitFileHashTable --
+ *
+ * Initializes the hash table used to track per-file state.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsInitFileHashTable(HgfsFileHashTable *htp) // IN: Hash table to initialize
+{
+ int i;
+
+ ASSERT(htp);
+
+ mutex_init(&htp->mutex, NULL, MUTEX_DRIVER, NULL);
+
+ for (i = 0; i < ARRAYSIZE(htp->hashTable); i++) {
+ DblLnkLst_Init(&htp->hashTable[i]);
+ }
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFileHashTableIsEmpty --
+ *
+ * Determines whether the hash table is in an acceptable state to unmount
+ * the file system.
+ *
+ * Note that this is not strictly empty: if the only file in the table is
+ * the root of the filesystem and its reference count is 1, this is
+ * considered empty since this is part of the operation of unmounting the
+ * filesystem.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+Bool
+HgfsFileHashTableIsEmpty(HgfsSuperInfo *sip, // IN: Superinfo
+ HgfsFileHashTable *htp) // IN: File hash table
+{
+ int i;
+
+ ASSERT(sip);
+ ASSERT(htp);
+
+ mutex_enter(&htp->mutex);
+
+ /* Traverse each bucket. */
+ for (i = 0; i < ARRAYSIZE(htp->hashTable); i++) {
+ DblLnkLst_Links *currNode = HGFS_FILE_HT_HEAD(htp, i);
+
+ /* Visit each file in this bucket */
+ while (currNode != HGFS_FILE_HT_BUCKET(htp, i)) {
+ HgfsFile *currFile = DblLnkLst_Container(currNode, HgfsFile, listNode);
+
+ /*
+ * Here we special case the root of our filesystem. In a correct
+ * unmount, the root vnode of the filesystem will have an entry in the
+ * hash table and will have a reference count of 1. We check if the
+ * current entry is the root file, and if so, make sure its vnode's
+ * reference count is not > 1. Note that we are not mapping from file
+ * to vnode here (which is not possible), we are using the root vnode
+ * stored in the superinfo structure. This is the only vnode that
+ * should have multiple references associated with it because whenever
+ * someone calls HgfsRoot(), we return that vnode.
+ */
+ if (HGFS_IS_ROOT_FILE(sip, currFile)) {
+ mutex_enter(&HGFS_ROOT_VNODE(sip)->v_lock);
+ if (HGFS_ROOT_VNODE(sip)->v_count <= 1) {
+ mutex_exit(&HGFS_ROOT_VNODE(sip)->v_lock);
+
+ /* This file is okay; skip to the next one. */
+ currNode = currNode->next;
+ continue;
+ }
+
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileHashTableIsEmpty: %s has count of %d.\n",
+ currFile->fileName, HGFS_ROOT_VNODE(sip)->v_count);
+
+ mutex_exit(&HGFS_ROOT_VNODE(sip)->v_lock);
+ /* Fall through to failure case */
+ }
+
+ /* Fail if a file is found. */
+ mutex_exit(&htp->mutex);
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileHashTableIsEmpty: %s "
+ "still in use (file count=%d).\n",
+ currFile->fileName, currFile->refCount);
+ return FALSE;
+ }
+ }
+
+ mutex_exit(&htp->mutex);
+
+ return TRUE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDebugPrintFileHashTable --
+ *
+ * This will print out all the HgfsFiles that we have in the hash table, as
+ * well as the HgfsFile reference count. This should help finding places
+ * where there may be loose references on files that prevent an unmount
+ * (EBUSY) when it should be allowed.
+ *
+ * Results:
+ * Void.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+HgfsDebugPrintFileHashTable(HgfsFileHashTable *htp, // IN: Hash table to print
+ int level) // IN: Debugging level
+{
+ int bucket;
+
+ ASSERT(htp);
+
+ mutex_enter(&htp->mutex);
+
+ for (bucket = 0; bucket < ARRAYSIZE(htp->hashTable); bucket++) {
+ DblLnkLst_Links *currNode = HGFS_FILE_HT_HEAD(htp, bucket);
+
+ while (currNode != HGFS_FILE_HT_BUCKET(htp, bucket)) {
+ HgfsFile *currFile = DblLnkLst_Container(currNode, HgfsFile, listNode);
+
+ mutex_enter(&currFile->mutex);
+ DEBUG(level, "HgfsDebugPrintFileHashTable: "
+ "file: %s, count: %d (bucket %d)\n",
+ currFile->fileName, currFile->refCount, bucket);
+ mutex_exit(&currFile->mutex);
+
+ currNode = currNode->next;
+ }
+ }
+
+ mutex_exit(&htp->mutex);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsHandleIsSet --
+ *
+ * Determines whether handle of the vnode's open file is currently set.
+ *
+ * Results:
+ * Returns TRUE if the handle is set, FALSE if the handle is not set.
+ * HGFS_ERR is returned on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+Bool
+HgfsHandleIsSet(struct vnode *vp) // IN: Vnode to check handle of
+{
+ HgfsOpenFile *ofp;
+ Bool isSet;
+
+ ASSERT(vp);
+
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) {
+ return HGFS_ERR;
+ }
+
+ mutex_enter(&ofp->handleMutex);
+ isSet = ofp->handleIsSet;
+ mutex_exit(&ofp->handleMutex);
+
+ return isSet;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSetOpenFileHandle --
+ *
+ * Sets the open file handle for the provided vnode.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * The handle may not be set again until it is cleared.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsSetOpenFileHandle(struct vnode *vp, // IN: Vnode to set handle for
+ HgfsHandle handle) // IN: Value of handle
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(vp);
+
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) {
+ return HGFS_ERR;
+ }
+
+ mutex_enter(&ofp->handleMutex);
+
+ if (ofp->handleIsSet) {
+ DEBUG(VM_DEBUG_FAIL, "**HgfsSetOpenFileHandle: handle for %s already set to %d; "
+ "cannot set to %d\n", HGFS_VP_TO_FILENAME(vp), ofp->handle, handle);
+ mutex_exit(&ofp->handleMutex);
+ return HGFS_ERR;
+ }
+
+ ofp->handle = handle;
+ ofp->handleIsSet = TRUE;
+
+ DEBUG(VM_DEBUG_STATE, "HgfsSetOpenFileHandle: set handle for %s to %d\n",
+ HGFS_VP_TO_FILENAME(vp), ofp->handle);
+
+ mutex_exit(&ofp->handleMutex);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetOpenFileHandle --
+ *
+ * Gets the open file handle for the provided vnode.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure. On success,
+ * the value of the vnode's handle is placed in outHandle.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsGetOpenFileHandle(struct vnode *vp, // IN: Vnode to get handle for
+ HgfsHandle *outHandle) // OUT: Filled with value of handle
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(vp);
+ ASSERT(outHandle);
+
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) {
+ return HGFS_ERR;
+ }
+
+ mutex_enter(&ofp->handleMutex);
+
+ if (!ofp->handleIsSet) {
+ DEBUG(VM_DEBUG_FAIL, "**HgfsGetOpenFileHandle: handle for %s is not set.\n",
+ HGFS_VP_TO_FILENAME(vp));
+ mutex_exit(&ofp->handleMutex);
+ return HGFS_ERR;
+ }
+
+ *outHandle = ofp->handle;
+
+ mutex_exit(&ofp->handleMutex);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsClearOpenFileHandle --
+ *
+ * Clears the open file handle for the provided vnode.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * The handle may be set.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsClearOpenFileHandle(struct vnode *vp) // IN: Vnode to clear handle for
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(vp);
+
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) {
+ return HGFS_ERR;
+ }
+
+ mutex_enter(&ofp->handleMutex);
+
+ ofp->handle = 0;
+ ofp->handleIsSet = FALSE;
+
+ DEBUG(VM_DEBUG_STATE, "HgfsClearOpenFileHandle: cleared %s's handle\n",
+ HGFS_VP_TO_FILENAME(vp));
+
+ mutex_exit(&ofp->handleMutex);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSetOpenFileMode --
+ *
+ * Sets the mode of the open file for the provided vnode.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * The mode may not be set again until cleared.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsSetOpenFileMode(struct vnode *vp, // IN: Vnode to set mode for
+ HgfsMode mode) // IN: Mode to set to
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(vp);
+
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) {
+ return HGFS_ERR;
+ }
+
+ mutex_enter(&ofp->modeMutex);
+
+ if (ofp->modeIsSet) {
+ DEBUG(VM_DEBUG_FAIL, "**HgfsSetOpenFileMode: mode for %s already set to %d; "
+ "cannot set to %d\n", HGFS_VP_TO_FILENAME(vp), ofp->mode, mode);
+ mutex_exit(&ofp->modeMutex);
+ return HGFS_ERR;
+ }
+
+ ofp->mode = mode;
+ ofp->modeIsSet = TRUE;
+
+ DEBUG(VM_DEBUG_STATE, "HgfsSetOpenFileMode: set mode for %s to %d\n",
+ HGFS_VP_TO_FILENAME(vp), ofp->mode);
+
+ mutex_exit(&ofp->modeMutex);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetOpenFileMode --
+ *
+ * Gets the mode of the open file for the provided vnode.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsGetOpenFileMode(struct vnode *vp, // IN: Vnode to get mode for
+ HgfsMode *outMode) // OUT: Filled with mode
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(vp);
+ ASSERT(outMode);
+
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) {
+ return HGFS_ERR;
+ }
+
+ mutex_enter(&ofp->modeMutex);
+
+ if (!ofp->modeIsSet) {
+// DEBUG(VM_DEBUG_FAIL, "**HgfsGetOpenFileMode: mode for %s is not set.\n",
+// HGFS_VP_TO_FILENAME(vp));
+ mutex_exit(&ofp->modeMutex);
+ return HGFS_ERR;
+ }
+
+ *outMode = ofp->mode;
+
+ mutex_exit(&ofp->modeMutex);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsClearOpenFileMode --
+ *
+ * Clears the mode of the open file for the provided vnode.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * The mode may be set again.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsClearOpenFileMode(struct vnode *vp) // IN: Vnode to clear mode for
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(vp);
+
+ ofp = HGFS_VP_TO_OFP(vp);
+ if (!ofp) {
+ return HGFS_ERR;
+ }
+
+ mutex_enter(&ofp->modeMutex);
+
+ ofp->mode = 0;
+ ofp->modeIsSet = FALSE;
+
+ DEBUG(VM_DEBUG_STATE, "HgfsClearOpenFileMode: cleared %s's mode\n",
+ HGFS_VP_TO_FILENAME(vp));
+
+ mutex_exit(&ofp->modeMutex);
+
+ return 0;
+}
+
+
+/*
+ * Internal functions
+ */
+
+
+/* Allocation/initialization/free of open file state */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsAllocOpenFile --
+ *
+ * Allocates and initializes an open file structure. Also finds or, if
+ * necessary, creates the underlying HgfsFile per-file state.
+ *
+ * Results:
+ * Returns a pointer to the open file on success, NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static HgfsOpenFile *
+HgfsAllocOpenFile(const char *fileName, // IN: Name of file
+ HgfsFileType fileType, // IN: Type of file
+ HgfsFileHashTable *htp) // IN: Hash table
+{
+ HgfsOpenFile *ofp;
+
+ ASSERT(fileName);
+ ASSERT(htp);
+
+ /*
+ * We allocate and initialize our open-file state.
+ */
+ ofp = (HgfsOpenFile *)kmem_zalloc(sizeof *ofp, HGFS_ALLOC_FLAG);
+ /* kmem_zalloc() cannot fail if given KM_SLEEP; check otherwise */
+# if HGFS_ALLOC_FLAG != KM_SLEEP
+ if (!ofp) {
+ return NULL;
+ }
+# endif
+
+ /* Manually set these since the public functions need the lock. */
+ ofp->handle = 0;
+ ofp->handleIsSet = FALSE;
+
+ ofp->mode = 0;
+ ofp->modeIsSet = FALSE;
+
+ mutex_init(&ofp->handleMutex, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&ofp->handleMutex, NULL, MUTEX_DRIVER, NULL);
+
+ /*
+ * Now we get a reference to the underlying per-file state.
+ */
+ ofp->hgfsFile = HgfsGetFile(fileName, fileType, htp);
+ if (!ofp->hgfsFile) {
+ kmem_free(ofp, sizeof *ofp);
+ return NULL;
+ }
+
+ return ofp;
+
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFreeOpenFile --
+ *
+ * Frees the provided open file.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+HgfsFreeOpenFile(HgfsOpenFile *ofp, // IN: Open file to free
+ HgfsFileHashTable *htp) // IN: File hash table
+{
+ ASSERT(ofp);
+ ASSERT(htp);
+
+ /*
+ * First we release our reference to the underlying per-file state.
+ */
+ HgfsReleaseFile(ofp->hgfsFile, htp);
+
+ /*
+ * Then we destroy anything initialized and free the open file.
+ */
+ mutex_destroy(&ofp->handleMutex);
+ mutex_destroy(&ofp->modeMutex);
+
+ kmem_free(ofp, sizeof *ofp);
+}
+
+
+/* Acquiring/releasing file state */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetFile --
+ *
+ * Gets the file for the provided filename.
+ *
+ * If no file structure exists for this filename, one is created and added
+ * to the hash table.
+ *
+ * Results:
+ * Returns a pointer to the file on success, NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static HgfsFile *
+HgfsGetFile(const char *fileName, // IN: Filename to get file for
+ HgfsFileType fileType, // IN: Type of file
+ HgfsFileHashTable *htp) // IN: Hash table to look in
+{
+ HgfsFile *fp;
+ int err;
+
+ ASSERT(fileName);
+ ASSERT(htp);
+
+ /*
+ * We try to find the file in the hash table. If it exists we increment its
+ * reference count and return it.
+ */
+ mutex_enter(&htp->mutex);
+
+ fp = HgfsFindFile(fileName, htp);
+ if (fp) {
+ /* Signify our reference to this file. */
+ mutex_enter(&fp->mutex);
+ fp->refCount++;
+ mutex_exit(&fp->mutex);
+
+ mutex_exit(&htp->mutex);
+ return fp;
+ }
+
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsGetFile: allocated HgfsFile for %s.\n", fileName);
+
+ /*
+ * If it doesn't exist we create one, initialize it, and add it to the hash
+ * table.
+ */
+ fp = (HgfsFile *)kmem_zalloc(sizeof *fp, HGFS_ALLOC_FLAG);
+# if HGFS_ALLOC_FLAG != KM_SLEEP
+ if (!fp) {
+ /* fp is NULL already */
+ goto out;
+ }
+# endif
+
+ err = HgfsInitFile(fp, fileName, fileType);
+ if (err) {
+ kmem_free(fp, sizeof *fp);
+ fp = NULL;
+ goto out;
+ }
+
+ /*
+ * This is guaranteed to not add a duplicate since we checked above and have
+ * held the lock until now.
+ */
+ HgfsAddFile(fp, htp);
+
+out:
+ mutex_exit(&htp->mutex);
+ DEBUG(VM_DEBUG_DONE, "HgfsGetFile: done\n");
+ return fp;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsReleaseFile --
+ *
+ * Releases a reference to the provided file. If the reference count of
+ * this file becomes zero, the file structure is removed from the hash table
+ * and freed.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+HgfsReleaseFile(HgfsFile *fp, // IN: File to release
+ HgfsFileHashTable *htp) // IN: Hash table to look in/remove from
+{
+ ASSERT(fp);
+ ASSERT(htp);
+
+ /*
+ * Decrement this file's reference count. If it becomes zero, then we
+ * remove it from the hash table and free it.
+ */
+ mutex_enter(&fp->mutex);
+
+ if ( !(--fp->refCount) ) {
+ mutex_exit(&fp->mutex);
+
+ /* Remove file from hash table, then clean up. */
+ HgfsRemoveFile(fp, htp);
+
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsReleaseFile: freeing HgfsFile for %s.\n",
+ fp->fileName);
+
+ rw_destroy(&fp->rwlock);
+ mutex_destroy(&fp->mutex);
+ kmem_free(fp, sizeof *fp);
+ return;
+ }
+
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsReleaseFile: %s has %d references.\n",
+ fp->fileName, fp->refCount);
+
+ mutex_exit(&fp->mutex);
+}
+
+
+/* Allocation/initialization/free of file state */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsInitFile --
+ *
+ * Initializes a file structure.
+ *
+ * This sets the filename of the file and initializes other structure
+ * elements.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsInitFile(HgfsFile *fp, // IN: File to initialize
+ const char *fileName, // IN: Name of file
+ HgfsFileType fileType) // IN: Type of file
+{
+ int len;
+
+ ASSERT(fp);
+ ASSERT(fileName);
+
+ /* Make sure the filename will fit. */
+ len = strlen(fileName);
+ if (len > sizeof fp->fileName - 1) {
+ return HGFS_ERR;
+ }
+
+ fp->fileNameLength = len;
+ memcpy(fp->fileName, fileName, len + 1);
+ fp->fileName[fp->fileNameLength] = '\0';
+
+ /*
+ * We save the file type so we can recreate a vnode for the HgfsFile without
+ * sending a request to the Hgfs Server.
+ */
+ fp->fileType = fileType;
+
+ /* Initialize the links to place this file in our hash table. */
+ DblLnkLst_Init(&fp->listNode);
+
+ /*
+ * Fill in the node id. This serves as the inode number in directory
+ * entries and the node id in vnode attributes.
+ */
+ HgfsNodeIdHash(fp->fileName, fp->fileNameLength, &fp->nodeId);
+
+ /*
+ * The reader/write lock is for the rwlock/rwunlock vnode entry points and
+ * the mutex is to protect the reference count on this structure.
+ */
+ rw_init(&fp->rwlock, NULL, RW_DRIVER, NULL);
+ mutex_init(&fp->mutex, NULL, MUTEX_DRIVER, NULL);
+
+ /* The caller is the single reference. */
+ fp->refCount = 1;
+
+ return 0;
+}
+
+
+/* Adding/finding/removing file state from hash table */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsAddFile --
+ *
+ * Adds the file to the hash table.
+ *
+ * This function must be called with the hash table lock held. This is done
+ * so adding the file in the hash table can be made with any other
+ * operations (such as previously finding out that this file wasn't in the
+ * hash table).
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static inline void
+HgfsAddFile(HgfsFile *fp, // IN: File to add
+ HgfsFileHashTable *htp) // IN: Hash table to add to
+{
+ unsigned int index;
+
+ ASSERT(fp);
+ ASSERT(htp);
+
+ index = HgfsFileNameHash(fp->fileName);
+
+ /* Add this file to the end of the bucket's list */
+ DblLnkLst_LinkLast(HGFS_FILE_HT_HEAD(htp, index), &fp->listNode);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRemoveFile --
+ *
+ * Removes file from the hash table.
+ *
+ * Note that unlike the other two hash functions, this one performs its own
+ * locking since the removal doesn't need to be atomic with other
+ * operations. (This could change in the future if the functions that use
+ * this one are reorganized.)
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static inline void
+HgfsRemoveFile(HgfsFile *fp, // IN: File to remove
+ HgfsFileHashTable *htp) // IN: Hash table to remove from
+{
+ ASSERT(fp);
+ ASSERT(htp);
+
+ mutex_enter(&htp->mutex);
+
+ /* Take this file off its list */
+ DblLnkLst_Unlink1(&fp->listNode);
+
+ mutex_exit(&htp->mutex);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFindFile --
+ *
+ * Looks for a filename in the hash table.
+ *
+ * This function must be called with the hash table lock held. This is done
+ * so finding the file in the hash table and using it (after this function
+ * returns) can be atomic.
+ *
+ * Results:
+ * Returns a pointer to the file if found, NULL otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static HgfsFile *
+HgfsFindFile(const char *fileName, // IN: Filename to look for
+ HgfsFileHashTable *htp) // IN: Hash table to look in
+{
+ HgfsFile *found = NULL;
+ DblLnkLst_Links *currNode;
+ unsigned int index;
+
+ ASSERT(fileName);
+ ASSERT(htp);
+
+ /* Determine which bucket. */
+ index = HgfsFileNameHash(fileName);
+
+ /* Traverse the bucket's list. */
+ for (currNode = HGFS_FILE_HT_HEAD(htp, index);
+ currNode != HGFS_FILE_HT_BUCKET(htp, index);
+ currNode = currNode->next) {
+ HgfsFile *curr;
+ curr = DblLnkLst_Container(currNode, HgfsFile, listNode);
+
+ if (strcmp(curr->fileName, fileName) == 0) {
+ /* We found the file we want. */
+ found = curr;
+ break;
+ }
+ }
+
+ /* Return file if found. */
+ return found;
+}
+
+
+/* Other utility functions */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFileNameHash --
+ *
+ * Hashes the filename to get an index into the hash table. This is known
+ * as the PJW string hash function and it was taken from "Mastering
+ * Algorithms in C".
+ *
+ * Results:
+ * Returns an index between 0 and HGFS_HT_NR_BUCKETS.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static unsigned int
+HgfsFileNameHash(const char *fileName) // IN: Filename to hash
+{
+ unsigned int val = 0;
+
+ ASSERT(fileName);
+
+ while (*fileName != '\0') {
+ unsigned int tmp;
+
+ val = (val << 4) + (*fileName);
+ if ((tmp = (val & 0xf0000000))) {
+ val = val ^ (tmp >> 24);
+ val = val ^ tmp;
+ }
+
+ fileName++;
+ }
+
+ return val % HGFS_HT_NR_BUCKETS;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsNodeIdHash --
+ *
+ * Hashes the provided filename to generate a node id.
+ *
+ * Results:
+ * None. The value of the hash is filled into outHash.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+HgfsNodeIdHash(const char *fileName, // IN: Filename to hash
+ uint32_t fileNameLength, // IN: Length of the filename
+ ino64_t *outHash) // OUT: Location to write hash to
+{
+ SHA1_CTX hashContext;
+ unsigned char digest[SHA1_HASH_LEN];
+ int i;
+
+ ASSERT(fileName);
+ ASSERT(outHash);
+
+ /* Make sure we start at a consistent state. */
+ memset(&hashContext, 0, sizeof hashContext);
+ memset(digest, 0, sizeof digest);
+ memset(outHash, 0, sizeof *outHash);
+
+ /* Generate a SHA1 hash of the filename */
+ SHA1Init(&hashContext);
+ SHA1Update(&hashContext, (unsigned char *)fileName, fileNameLength);
+ SHA1Final(digest, &hashContext);
+
+ /*
+ * Fold the digest into the allowable size of our hash.
+ *
+ * For each group of bytes the same size as our output hash, xor the
+ * contents of the digest together. If there are less than that many bytes
+ * left in the digest, xor each byte that's left.
+ */
+ for(i = 0; i < sizeof digest; i += sizeof *outHash) {
+ int bytesLeft = sizeof digest - i;
+
+ /* Do a byte-by-byte xor if there aren't enough bytes left in the digest */
+ if (bytesLeft < sizeof *outHash) {
+ int j;
+
+ for (j = 0; j < bytesLeft; j++) {
+ uint8 *outByte = (uint8 *)outHash + j;
+ uint8 *inByte = (uint8 *)((u_longlong_t *)(digest + i)) + j;
+ *outByte ^= *inByte;
+ }
+ break;
+ }
+
+ /* Block xor */
+ *outHash ^= *((u_longlong_t *)(digest + i));
+ }
+
+ /*
+ * Clear the most significant byte so that user space apps depending on
+ * a node id/inode number that's only 32 bits won't break. (For example,
+ * gedit's call to stat(2) returns EOVERFLOW if we don't do this.)
+ */
+# ifndef HGFS_BREAK_32BIT_USER_APPS
+ *((uint32_t *)outHash) ^= *((uint32_t *)outHash + 1);
+ *((uint32_t *)outHash + 1) = 0;
+# endif
+
+ DEBUG(VM_DEBUG_INFO, "Hash of: %s (%d) is %"FMT64"u\n", fileName, fileNameLength, *outHash);
+
+ return;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * hgfsState.h --
+ *
+ * Public functions, types, and macros for Hgfs state that is attached to
+ * vnodes.
+ */
+
+#ifndef __HGFS_STATE_H_
+#define __HGFS_STATE_H_
+
+/*
+ * Includes
+ */
+#include <sys/param.h> /* MAXPATHLEN */
+#include <sys/rwlock.h> /* krwlock_t */
+#include <sys/ksynch.h> /* kmutex_t */
+#include <sys/vnode.h> /* struct vnode */
+
+#include "hgfsProto.h"
+#include "dbllnklst.h"
+
+
+/*
+ * Macros
+ */
+/* Number of buckets for the HgfsInode hash table */
+#define HGFS_HT_NR_BUCKETS 5
+
+/* Conversion between different state structures */
+#define HGFS_VP_TO_OFP(vp) ((HgfsOpenFile *)(vp)->v_data)
+#define HGFS_VP_TO_FP(vp) ((HgfsFile *)(HGFS_VP_TO_OFP(vp))->hgfsFile)
+#define HGFS_OFP_TO_VP(ofp) (ofp)->vnodep
+
+#define HGFS_VP_TO_FILENAME(vp) \
+ HGFS_VP_TO_FP(vp)->fileName
+
+#define HGFS_VP_TO_FILENAME_LENGTH(vp) \
+ HGFS_VP_TO_FP(vp)->fileNameLength
+
+#define HGFS_VP_TO_NODEID(vp) \
+ HGFS_VP_TO_FP(vp)->nodeId
+
+#define HGFS_VP_TO_RWLOCK(vp) \
+ HGFS_VP_TO_FP(vp)->rwlock
+
+#define HGFS_VP_TO_RWLOCKP(vp) &(HGFS_VP_TO_RWLOCK(vp))
+
+#define HGFS_VP_TO_HGFSFILETYPE(vp) \
+ HGFS_VP_TO_FP(vp)->fileType
+
+/*
+ * This macro is used for sanity checking the fact that Solaris won't ever call
+ * one of our vnode operations without first calling lookup(), which is the
+ * place where we acquire the filename. I have never seen a function fail
+ * because of this, so it is likely that we can kill this macro and the checks
+ * on it throughout vnode.c.
+ */
+#define HGFS_KNOW_FILENAME(vp) \
+ ( \
+ vp && \
+ HGFS_VP_TO_FP(vp) && \
+ HGFS_VP_TO_FP(vp)->fileName[0] != '\0' \
+ )
+
+/*
+ * Types
+ */
+
+typedef uint32_t HgfsMode;
+
+/*
+ * State kept per shared file from the host.
+ *
+ * All fields are read-only after initialization except reference count, which
+ * is protected by the mutex.
+ */
+typedef struct HgfsFile {
+ /* Link to place this state on the file state hash table */
+ DblLnkLst_Links listNode;
+ /*
+ * Full path of file within the filesystem (that is, taking /mnt/hgfs as /).
+ * These are built from / in HgfsMount() and appending names as provided to
+ * HgfsLookup(). Saving the length in HgfsIget() saves having to calculate
+ * it in each HgfsMakeFullName().
+ */
+ char fileName[MAXPATHLEN + 1];
+ uint32_t fileNameLength;
+ ino64_t nodeId;
+ /*
+ * The file type is saved so additional per-open-file vnodes can be
+ * recreated from a Hgfs file without sending a request to the Hgfs server.
+ */
+ HgfsFileType fileType;
+ /*
+ * Readers/writers lock for this file. Needed by rwlock() and rwunlock()
+ * entry points.
+ */
+ krwlock_t rwlock;
+ /* Lock to protect the reference count of this file state. */
+ kmutex_t mutex;
+ uint32_t refCount;
+} HgfsFile;
+
+
+/*
+ * State kept per vnode, which implies per open file within a process.
+ *
+ * Once created, the hgfsFile and vnode/vnodep values are read-only. The
+ * handle and mode will change throughout this structure's existence, and are
+ * both protected by their own mutex.
+ */
+typedef struct HgfsOpenFile {
+ /* Handle provided by reply to a request */
+ HgfsHandle handle;
+ Bool handleIsSet;
+ kmutex_t handleMutex;
+ /*
+ * Mode specified for this file to be created with. This is necessary
+ * because create is called with the mode then open is called without it.
+ * We save this value in create and access it in open.
+ */
+ HgfsMode mode;
+ Bool modeIsSet;
+ kmutex_t modeMutex;
+ /*
+ * Pointer to the single Hgfs file state structure shared amongst all open
+ * instances of this file.
+ */
+ HgfsFile *hgfsFile;
+ /*
+ * A pointer back to the vnode this open-file state is for.
+ */
+ struct vnode *vnodep;
+} HgfsOpenFile;
+
+/* The hash table for file state. */
+typedef struct HgfsFileHashTable {
+ kmutex_t mutex;
+ DblLnkLst_Links hashTable[HGFS_HT_NR_BUCKETS];
+} HgfsFileHashTable;
+
+/* Forward declaration to prevent circular dependency between this and hgfs.h. */
+struct HgfsSuperInfo;
+
+
+/*
+ * Functions
+ */
+int HgfsVnodeGet(struct vnode **vpp, struct HgfsSuperInfo *sip, struct vfs *vfsp,
+ const char *fileName, HgfsFileType fileType, HgfsFileHashTable *htp);
+int HgfsVnodePut(struct vnode *vp, HgfsFileHashTable *ht);
+int HgfsVnodeDup(struct vnode **newVpp, struct vnode *origVp, struct HgfsSuperInfo *sip,
+ HgfsFileHashTable *htp);
+int HgfsFileNameToVnode(const char *fileName, struct vnode **vpp,
+ struct HgfsSuperInfo *sip, struct vfs *vfsp,
+ HgfsFileHashTable *htp);
+void HgfsNodeIdGet(HgfsFileHashTable *ht, const char *fileName,
+ uint32_t fileNameLength, ino64_t *outNodeId);
+int HgfsInitFileHashTable(HgfsFileHashTable *htp);
+Bool HgfsFileHashTableIsEmpty(struct HgfsSuperInfo *sip, HgfsFileHashTable *htp);
+
+/* Handle get/set/clear functions */
+int HgfsSetOpenFileHandle(struct vnode *vp, HgfsHandle handle);
+int HgfsGetOpenFileHandle(struct vnode *vp, HgfsHandle *outHandle);
+int HgfsClearOpenFileHandle(struct vnode *vp);
+Bool HgfsHandleIsSet(struct vnode *vp);
+
+/* Mode get/set/clear functions */
+int HgfsSetOpenFileMode(struct vnode *vp, HgfsMode mode);
+int HgfsGetOpenFileMode(struct vnode *vp, HgfsMode *outMode);
+int HgfsClearOpenFileMode(struct vnode *vp);
+
+/* Debugging function */
+void HgfsDebugPrintFileHashTable(HgfsFileHashTable *htp, int level);
+
+#endif /* __HGFS_STATE_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * hgfsUtil.c --
+ *
+ * Utility routines used by both HGFS servers and clients, such as
+ * conversion routines between Unix time and Windows NT time.
+ * The former is in units of seconds since midnight 1/1/1970, while the
+ * latter is in units of 100 nanoseconds since midnight 1/1/1601.
+ */
+
+/*
+ * hgfsUtil.h must be included before vm_basic_asm.h, as hgfsUtil.h
+ * includes kernel headers on Linux. That is, vmware.h must come after
+ * hgfsUtil.h.
+ */
+#include "hgfsUtil.h"
+#include "vmware.h"
+#include "vm_basic_asm.h"
+
+#ifndef _WIN32
+/*
+ * NT time of the Unix epoch:
+ * midnight January 1, 1970 UTC
+ */
+#define UNIX_EPOCH ((((uint64)369 * 365) + 89) * 24 * 3600 * 10000000)
+
+/*
+ * NT time of the Unix 32 bit signed time_t wraparound:
+ * 03:14:07 January 19, 2038 UTC
+ */
+#define UNIX_S32_MAX (UNIX_EPOCH + (uint64)0x80000000 * 10000000)
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsConvertToNtTime --
+ *
+ * Convert from Unix time to Windows NT time.
+ *
+ * Results:
+ * The time in Windows NT format.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+uint64
+HgfsConvertToNtTime(time_t unixTime, // IN: Time in Unix format (seconds)
+ long nsec) // IN: nanoseconds
+{
+ return (uint64)unixTime * 10000000 + nsec / 100 + UNIX_EPOCH;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsConvertFromNtTimeNsec --
+ *
+ * Convert from Windows NT time to Unix time. If NT time is outside of
+ * UNIX time range (1970-2038), returned time is nearest time valid in
+ * UNIX.
+ *
+ * Results:
+ * 0 on success
+ * non-zero if NT time is outside of valid range for UNIX
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsConvertFromNtTimeNsec(struct timespec *unixTime, // OUT: Time in UNIX format
+ uint64 ntTime) // IN: Time in Windows NT format
+{
+#ifndef VM_X86_64
+ uint32 sec;
+ uint32 nsec;
+
+ ASSERT(unixTime);
+ /* We assume that time_t is 32bit */
+ ASSERT_ON_COMPILE(sizeof (unixTime->tv_sec) == 4);
+
+ /* Cap NT time values that are outside of Unix time's range */
+
+ if (ntTime >= UNIX_S32_MAX) {
+ unixTime->tv_sec = 0x7FFFFFFF;
+ unixTime->tv_nsec = 0;
+ return 1;
+ }
+#else
+ ASSERT(unixTime);
+#endif
+
+ if (ntTime < UNIX_EPOCH) {
+ unixTime->tv_sec = 0;
+ unixTime->tv_nsec = 0;
+ return -1;
+ }
+
+#ifndef VM_X86_64
+ Div643232(ntTime - UNIX_EPOCH, 10000000, &sec, &nsec);
+ unixTime->tv_sec = sec;
+ unixTime->tv_nsec = nsec * 100;
+#else
+ unixTime->tv_sec = (ntTime - UNIX_EPOCH) / 10000000;
+ unixTime->tv_nsec = ((ntTime - UNIX_EPOCH) % 10000000) * 100;
+#endif
+
+ return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsConvertFromNtTime --
+ *
+ * Convert from Windows NT time to Unix time.
+ *
+ * Results:
+ * 0 on success
+ * nonzero if time is not representable on UNIX
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsConvertFromNtTime(time_t *unixTime, // OUT: Time in UNIX format
+ uint64 ntTime) // IN: Time in Windows NT format
+{
+ struct timespec tm;
+ int ret;
+
+ ret = HgfsConvertFromNtTimeNsec(&tm, ntTime);
+ *unixTime = tm.tv_sec;
+ return ret;
+}
+#endif /* !def(_WIN32) */
+
+
+#undef UNIX_EPOCH
+#undef UNIX_S32_MAX
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsConvertFromInternalStatus --
+ *
+ * This function converts between a platform-specific status code and a
+ * cross-platform status code to be sent down the wire.
+ *
+ * Results:
+ * Converted status code.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#ifdef _WIN32
+HgfsStatus
+HgfsConvertFromInternalStatus(HgfsInternalStatus status) // IN
+{
+ switch(status) {
+ case ERROR_SUCCESS:
+ return HGFS_STATUS_SUCCESS;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ return HGFS_STATUS_NO_SUCH_FILE_OR_DIR;
+ case ERROR_INVALID_HANDLE:
+ return HGFS_STATUS_INVALID_HANDLE;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ return HGFS_STATUS_FILE_EXISTS;
+ case ERROR_DIR_NOT_EMPTY:
+ return HGFS_STATUS_DIR_NOT_EMPTY;
+ case RPC_S_PROTOCOL_ERROR:
+ return HGFS_STATUS_PROTOCOL_ERROR;
+ case ERROR_ACCESS_DENIED:
+ return HGFS_STATUS_ACCESS_DENIED;
+ case ERROR_INVALID_NAME:
+ return HGFS_STATUS_INVALID_NAME;
+ case ERROR_SHARING_VIOLATION:
+ return HGFS_STATUS_SHARING_VIOLATION;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ return HGFS_STATUS_NO_SPACE;
+ case ERROR_NOT_SUPPORTED:
+ return HGFS_STATUS_OPERATION_NOT_SUPPORTED;
+ case ERROR_INVALID_PARAMETER:
+ return HGFS_STATUS_INVALID_PARAMETER;
+ case HGFS_INTERNAL_STATUS_ERROR:
+ default:
+ return HGFS_STATUS_GENERIC_ERROR;
+ }
+}
+
+#else /* Win32 */
+
+HgfsStatus
+HgfsConvertFromInternalStatus(HgfsInternalStatus status) // IN
+{
+ switch(status) {
+ case 0:
+ return HGFS_STATUS_SUCCESS;
+ case ENOENT:
+ return HGFS_STATUS_NO_SUCH_FILE_OR_DIR;
+ case EBADF:
+ return HGFS_STATUS_INVALID_HANDLE;
+ case EPERM:
+ return HGFS_STATUS_OPERATION_NOT_PERMITTED;
+ case EEXIST:
+ return HGFS_STATUS_FILE_EXISTS;
+ case ENOTDIR:
+ return HGFS_STATUS_NOT_DIRECTORY;
+ case ENOTEMPTY:
+ return HGFS_STATUS_DIR_NOT_EMPTY;
+ case EPROTO:
+ return HGFS_STATUS_PROTOCOL_ERROR;
+ case EACCES:
+ return HGFS_STATUS_ACCESS_DENIED;
+ case EINVAL:
+ return HGFS_STATUS_INVALID_NAME;
+ case ENOSPC:
+ return HGFS_STATUS_NO_SPACE;
+ case EOPNOTSUPP:
+ return HGFS_STATUS_OPERATION_NOT_SUPPORTED;
+ case ENAMETOOLONG:
+ return HGFS_STATUS_NAME_TOO_LONG;
+ case EPARAMETERNOTSUPPORTED:
+ return HGFS_STATUS_INVALID_PARAMETER;
+ case HGFS_INTERNAL_STATUS_ERROR:
+ default:
+ return HGFS_STATUS_GENERIC_ERROR;
+ }
+}
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * hgfsUtil.h --
+ *
+ * Utility functions and macros used by hgfs.
+ */
+
+
+#ifndef _HGFSUTIL_H_
+# define _HGFSUTIL_H_
+
+# if defined(__linux__) && defined(__KERNEL__)
+# include "driver-config.h"
+# include <linux/time.h> // for time_t and timespec
+ /* Include time.h in userspace code, but not in Solaris kernel code. */
+# elif defined(__FreeBSD__) && defined(_KERNEL)
+ /* Do nothing. */
+# elif defined(__APPLE__) && defined(KERNEL)
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+# include "vm_basic_types.h"
+# if !defined(_STRUCT_TIMESPEC) && \
+ !defined(_TIMESPEC_DECLARED) && \
+ !defined(__timespec_defined) && \
+ !defined(sun) && \
+ !defined(__FreeBSD__) && \
+ !__APPLE__ && \
+ !defined(_WIN32)
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+# endif
+
+# include "hgfs.h"
+
+/* Cross-platform representation of a platform-specific error code. */
+#ifndef _WIN32
+# if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL)
+# if defined(__linux__)
+# include <linux/errno.h>
+# elif defined(sun) || defined(__FreeBSD__) || defined(__APPLE__)
+# include <sys/errno.h>
+# endif
+# else
+# include <errno.h>
+# endif
+ typedef int HgfsInternalStatus;
+#else
+# include <windows.h>
+ typedef DWORD HgfsInternalStatus;
+#endif
+
+/*
+ * Unfortunately, we need a catch-all "generic error" to use with
+ * HgfsInternalStatus, because there are times when cross-platform code needs
+ * to return its own errors along with errors from platform specific code.
+ *
+ * Using -1 should be safe because we expect our platforms to use zero as
+ * success and a positive range of numbers as error values.
+ */
+#define HGFS_INTERNAL_STATUS_ERROR (-1)
+
+#ifndef _WIN32
+/*
+ * This error code is used to notify the client that some of the parameters passed
+ * (e.g. file handles) are not supported. Clients are expected to correct
+ * the parameter (e.g. pass file name instead) and retry.
+ *
+ * Note that this error code is artificially made up and in future may conflict
+ * with an "official" error code when added.
+ */
+#define EPARAMETERNOTSUPPORTED (MAX_INT32 - 1)
+#endif
+
+/*
+ * FreeBSD (pre-6.0) does not define EPROTO, so we'll define our own error code.
+ */
+#if defined(__FreeBSD__) && !defined(EPROTO)
+#define EPROTO (ELAST + 1)
+#endif
+
+#define HGFS_NAME_BUFFER_SIZE(request) (HGFS_PACKET_MAX - (sizeof *request - 1))
+#define HGFS_NAME_BUFFER_SIZET(sizet) (HGFS_PACKET_MAX - ((sizet) - 1))
+
+#ifndef _WIN32
+/*
+ * Routines for converting between Win NT and unix time formats. The
+ * hgfs attributes use the NT time formats, so the linux driver and
+ * server have to convert back and forth. [bac]
+ */
+
+uint64 HgfsConvertToNtTime(time_t unixTime, // IN
+ long nsec); // IN
+static INLINE uint64
+HgfsConvertTimeSpecToNtTime(const struct timespec *unixTime) // IN
+{
+ return HgfsConvertToNtTime(unixTime->tv_sec, unixTime->tv_nsec);
+}
+
+int HgfsConvertFromNtTime(time_t * unixTime, // OUT
+ uint64 ntTime); // IN
+int HgfsConvertFromNtTimeNsec(struct timespec *unixTime, // OUT
+ uint64 ntTime); // IN
+#endif /* !def(_WIN32) */
+
+HgfsStatus HgfsConvertFromInternalStatus(HgfsInternalStatus status); // IN
+
+#endif /* _HGFSUTIL_H_ */
+
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * includeCheck.h --
+ *
+ * Restrict include file use.
+ *
+ * In every .h file, define one or more of these
+ *
+ * INCLUDE_ALLOW_VMX
+ * INCLUDE_ALLOW_USERLEVEL
+ * INCLUDE_ALLOW_VMMEXT
+ * INCLUDE_ALLOW_VMCORE
+ * INCLUDE_ALLOW_MODULE
+ * INCLUDE_ALLOW_VMNIXMOD
+ * INCLUDE_ALLOW_VMKERNEL
+ * INCLUDE_ALLOW_DISTRIBUTE
+ * INCLUDE_ALLOW_VMK_MODULE
+ * INCLUDE_ALLOW_VMKDRIVERS
+ * INCLUDE_ALLOW_VMIROM
+ *
+ * Then include this file.
+ *
+ * Any file that has INCLUDE_ALLOW_DISTRIBUTE defined will potentially
+ * be distributed in source form along with GPLed code. Ensure
+ * that this is acceptable.
+ */
+
+
+/*
+ * Declare a VMCORE-only variable to help classify object
+ * files. The variable goes in the common block and does
+ * not create multiple definition link-time conflicts.
+ */
+
+#if defined VMCORE && defined VMX86_DEVEL && defined VMX86_DEBUG && \
+ defined linux && !defined MODULE && \
+ !defined COMPILED_WITH_VMCORE
+#define COMPILED_WITH_VMCORE compiled_with_vmcore
+#ifdef ASM
+ .comm compiled_with_vmcore, 0
+#else
+ asm(".comm compiled_with_vmcore, 0");
+#endif /* ASM */
+#endif
+
+
+#if defined VMCORE && \
+ !(defined VMX86_VMX || defined VMM || \
+ defined MONITOR_APP || defined VMMON)
+#error "Makefile problem: VMCORE without VMX86_VMX or \
+ VMM or MONITOR_APP or MODULE."
+#endif
+
+#if defined VMCORE && !defined INCLUDE_ALLOW_VMCORE
+#error "The surrounding include file is not allowed in vmcore."
+#endif
+#undef INCLUDE_ALLOW_VMCORE
+
+#if defined VMX86_VMX && !defined VMCORE && \
+ !(defined INCLUDE_ALLOW_VMX || defined INCLUDE_ALLOW_USERLEVEL)
+#error "The surrounding include file is not allowed in the VMX."
+#endif
+#undef INCLUDE_ALLOW_VMX
+
+#if defined USERLEVEL && !defined VMX86_VMX && !defined VMCORE && \
+ !defined INCLUDE_ALLOW_USERLEVEL
+#error "The surrounding include file is not allowed at userlevel."
+#endif
+#undef INCLUDE_ALLOW_USERLEVEL
+
+#if defined VMM && !defined VMCORE && \
+ !defined INCLUDE_ALLOW_VMMEXT
+#error "The surrounding include file is not allowed in the monitor."
+#endif
+#undef INCLUDE_ALLOW_VMMEXT
+
+#if defined MODULE && !defined VMKERNEL_MODULE && !defined VMNIXMOD && \
+ !defined VMMON && !defined INCLUDE_ALLOW_MODULE
+#error "The surrounding include file is not allowed in driver modules."
+#endif
+#undef INCLUDE_ALLOW_MODULE
+
+#if defined VMMON && !defined INCLUDE_ALLOW_VMMON
+#error "The surrounding include file is not allowed in vmmon."
+#endif
+#undef INCLUDE_ALLOW_VMMON
+
+#if defined VMKERNEL && !defined INCLUDE_ALLOW_VMKERNEL
+#error "The surrounding include file is not allowed in the vmkernel."
+#endif
+#undef INCLUDE_ALLOW_VMKERNEL
+
+#if defined GPLED_CODE && !defined INCLUDE_ALLOW_DISTRIBUTE
+#error "The surrounding include file is not allowed in GPL code."
+#endif
+#undef INCLUDE_ALLOW_DISTRIBUTE
+
+#if defined VMKERNEL_MODULE && !defined VMKERNEL && \
+ !defined INCLUDE_ALLOW_VMK_MODULE && !defined INCLUDE_ALLOW_VMKDRIVERS
+#error "The surrounding include file is not allowed in vmkernel modules."
+#endif
+#undef INCLUDE_ALLOW_VMK_MODULE
+#undef INCLUDE_ALLOW_VMKDRIVERS
+
+#if defined VMNIXMOD && !defined INCLUDE_ALLOW_VMNIXMOD
+#ifndef VMNIXMOD_VM
+#error "The surrounding include file is not allowed in vmnixmod."
+#endif
+#endif
+#undef INCLUDE_ALLOW_VMNIXMOD
+
+#if defined VMIROM && ! defined INCLUDE_ALLOW_VMIROM
+#error "The surrounding include file is not allowed in vmirom."
+#endif
+#undef INCLUDE_ALLOW_VMIROM
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * module.c --
+ *
+ * Structures and modules for loading and unloading of the HGFS module.
+ *
+ */
+
+
+/*
+ * For reference: Solaris Structures
+ * =================================
+ *
+ * Driver module struct hierarchy:
+ * -------------------------------
+ *
+ * (Solaris 9 & 10)
+ *
+ * modlinkage --> modldrv --> dev_ops --> cb_ops
+ *
+ * - modlinkage & modldrv: module loading/unloading
+ * - dev_ops: operations for driver configuration (attach(), detach(), etc)
+ * - cb_ops: operations on device, entry points to character device (open(),
+ * close(), etc)
+ *
+ *
+ * VFS module struct hierarchy:
+ * ----------------------------
+ *
+ * (Solaris 9)
+ *
+ * modlinkage --> modlfs --> mod_ops
+ * \
+ * -> vfssw --> vfsops
+ * \
+ * -> (*fs_init_routine)()
+ *
+ *
+ * modlfs: - points to module loading/unloading operations structure (mod_ops)
+ * - points to VFS Switch strucutre (vfssw)
+ * - contains extended name of filesystem
+ *
+ * mod_ops: - contains pointers to _init(), _fini(), and _info(), which handle
+ * loading/unloading module into kernel and providing information
+ *
+ * vfssw: - points to filesystem initialization routine that is called once at
+ * module load time (not mount time)
+ * - points to vfsops struct that points to fs-specific operations
+ * - contains name of fs (what you would put in /etc/vfstab)
+ * - also contains fs mount options, flags, and mutex
+ *
+ * vfsops: - points to fs-level functions (mount(), umount(), etc.)
+ *
+ * (Solaris 10)
+ *
+ * modlinkage --> modlfs --> mod_ops
+ * \
+ * -> vfsdef_v2 --> (*init)()
+ *
+ * vfsdef_v2: - this contains a pointer to an initialization routine for the
+ * filesystem that takes different arguments
+ * - we no longer provide an address to a vfsops; now we need to
+ * call vfs_makevfsops() with a preconstructed array of
+ * fs_operation_t that defines each vfs op. This needs to occur
+ * in the initialization routine.
+ *
+ * (Build 58 contains the same structure named vfsdev_v3)
+ *
+ *
+ * Combination:
+ * ------------
+ *
+ * In this file we are placing both modldrv and modlfs inside one modlinkage
+ * structure. This is achieved by placing pointers to both of these structures
+ * in modlinkage's ml_linkage arary of pointers to module structures. This is
+ * done so the two modules can have shared state and to prevent dependency
+ * issues between them. This looks like:
+ *
+ * modlinkage --> modldrv --> as above
+ * \
+ * -> modlfs --> as above
+ *
+ */
+
+#include "hgfsSolaris.h"
+#include "module.h"
+#include "debug.h"
+
+
+/*
+ * Macros
+ */
+#define HGFS_VFSSW_FLAGS 0
+
+
+/*
+ * Structures
+ */
+
+/*
+ * Device driver structures
+ */
+
+/* Structure defining operations on device */
+static struct cb_ops HgfsDevCBOps = {
+ HgfsDevOpen, /* open(9E) */
+ HgfsDevClose, /* close(9E) */
+ nodev, /* strategy(9E) */
+ nodev, /* print(9E) */
+ nodev, /* dump(9E) */
+ HgfsDevRead, /* read(9E) */
+ HgfsDevWrite, /* write(9E) */
+ nodev, /* ioctl(9E) */
+ nodev, /* devmap(9E) */
+ nodev, /* mmap(9E) */
+ nodev, /* segmap(9E) */
+ HgfsDevChpoll, /* chpoll(9E) */
+ ddi_prop_op, /* prop_op(9E) */
+ NULL, /* streamtab(9S) */
+ (D_NEW | D_MP), /* cb_flag: D_NEW because this is a new-style
+ driver and D_MP is required */
+ CB_REV, /* cb_rev */
+ nodev, /* aread(9E) */
+ nodev, /* awrite(9E) */
+};
+
+
+/* Structure defining operations for driver */
+static struct dev_ops HgfsDevOps = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ HgfsDevGetinfo, /* getinfo(9E) */
+ nulldev, /* identify(9E) */
+ nulldev, /* probe(9E) */
+ HgfsDevAttach, /* attach(9E) */
+ HgfsDevDetach, /* detach(9E) */
+ nodev, /* devo_reset */
+ &HgfsDevCBOps, /* devo_cb_ops (points to struct cb_ops) */
+ NULL, /* devo_bus_ops */
+ NULL /* power(9E) */
+};
+
+
+/* Driver module linkage structure */
+static struct modldrv HgfsDevModldrv = {
+ &mod_driverops, /* drv_modops (don't change) */
+ "HGFS Device Interface", /* drv_linkinfo */
+ &HgfsDevOps /* drv_dev_ops (points to struct dev_ops) */
+};
+
+
+
+/*
+ * Filesystem structures
+ */
+
+#if HGFS_VFS_VERSION == 2
+/* VFS Operations Structure */
+struct vfsops HgfsVfsOps = {
+ HgfsMount, /* vfs_mount() */
+ HgfsUnmount, /* vfs_unmount() */
+ HgfsRoot, /* vfs_root() */
+ HgfsStatvfs, /* vfs_statvfs() */
+ HgfsSync, /* vfs_sync() */
+ HgfsVget, /* vfs_vget() */
+ HgfsMountroot, /* vfs_mountroot() */
+ HgfsReserved, /* vfs_reserved() */
+ HgfsFreevfs /* vfs_freevfs() */
+};
+
+
+/* VFS Switch structure */
+static struct vfssw HgfsVfsSw = {
+ HGFS_FS_NAME, /* Name of filesystem */
+ HgfsInit, /* Initialization routine */
+ &HgfsVfsOps, /* VFS Operations struct */
+ HGFS_VFSSW_FLAGS, /* Flags: see <sys/vfs.h> */
+ NULL, /* Mount options table prototype */
+ 1, /* Count of references */
+ { { 0 } } /* Lock to protect count */
+};
+#else
+
+/*
+ * Different beta builds of Solaris have different versions of this structure.
+ * We currently don't support v4 which was out in intermediate beta builds of
+ * Solaris 11. Instead we choose to update to the latest revision. But if needed,
+ * v4 could be added without significant effort.
+ */
+#if HGFS_VFS_VERSION == 2
+static struct vfsdef_v2 HgfsVfsDef = {
+#elif HGFS_VFS_VERSION == 3
+static struct vfsdef_v3 HgfsVfsDef = {
+#else
+static struct vfsdef_v5 HgfsVfsDef = {
+#endif
+ VFSDEF_VERSION, /* Structure version: defined in <sys/vfs.h> */
+ HGFS_FS_NAME, /* Name of filesystem */
+ HgfsInit, /* Initialization routine: note this is a different
+ * routine than the one used in 9 */
+ HGFS_VFSSW_FLAGS, /* Filesystem flags */
+ NULL /* No mount options */
+};
+
+#endif /* Solaris version */
+
+
+/* Filesystem module structure */
+static struct modlfs HgfsModlfs = {
+ &mod_fsops, /* Module operation structure: for
+ auto loading/unloading */
+ "Host/Guest Filesystem", /* Name */
+#if HGFS_VFS_VERSION== 2
+ &HgfsVfsSw /* VFS Switch structure */
+#else
+ &HgfsVfsDef /* Filesystem type definition record */
+#endif
+};
+
+
+/*
+ * Modlinkage shared by device driver and filesystem.
+ */
+static struct modlinkage HgfsModlinkage = {
+ MODREV_1, /* Module revision: must be MODREV_1 */
+ /*
+ * We put pointers to /both/ modules in the following NULL terminated array
+ * of pointers (ml_linkage[]). This is what causes both modules to be
+ * loaded into the kernel when the single driver is added.
+ */
+ {
+ &HgfsDevModldrv, /* Driver module structure */
+ &HgfsModlfs, /* FS module structure */
+ NULL, /* NULL terminator */
+ }
+};
+
+
+/*
+ * Driver autoload functions
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * _init --
+ * Invoked when module is being loaded into kernel, and is called before
+ * any function in the module. Any state that spans all instances of the
+ * driver should be allocated and initialized here.
+ *
+ * Results:
+ * Returns the result of mod_install(9F), which is zero on success and a
+ * non-zero value on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+_init(void)
+{
+ int ret;
+
+ DEBUG(VM_DEBUG_ENTRY, "_init() for HGFS.\n");
+
+ /*
+ * Initialize hgfsInstance so filesystem knows device has not been attached
+ * yet, and hgfsType so we know whether to free the vfsops in _fini().
+ */
+ hgfsInstance = HGFS_INSTANCE_UNINITIALIZED;
+ hgfsType = HGFS_TYPE_UNINITIALIZED;
+
+ /*
+ * For reference:
+ *
+ * Solaris provides an interface to simplify allocation and deallocation of
+ * per-driver-instance state through the DDI Soft State functions.
+ *
+ * - ddi_soft_state_init() takes a double pointer and uses it as a handle
+ * to access the various per-instance state structures. This handle will
+ * be passed to the other DDI Soft State functions to indicate which
+ * state list to operate on. The second argument is the amount of memory
+ * to allocate for each state structure, and the last is the expected number
+ * of instances of the driver (number of state structures).
+ *
+ * - ddi_soft_state_zalloc() and ddi_soft_state_free() allocate and free a
+ * soft state structure respectively. The first argument is the
+ * handle pointer (see above) and the second is the item number
+ * associated with this instance.
+ *
+ * - ddi_get_soft_state_free() takes a handle and item and returns a
+ * pointer to the state structure that was allocated with
+ * ddi_soft_state_zalloc() for that item number.
+ *
+ * See man ddi_soft_state_init for more information.
+ *
+ */
+ ret = ddi_soft_state_init(&superInfoHead,
+ sizeof (HgfsSuperInfo), HGFS_EXPECTED_INSTANCES);
+ if (ret) {
+ goto error;
+ }
+
+ ret = mod_install(&HgfsModlinkage);
+ if (ret) {
+ goto error;
+ }
+
+ DEBUG(VM_DEBUG_DONE, "_init() done.\n");
+ return 0;
+
+error:
+ cmn_err(HGFS_ERROR, "could not install HGFS module.\n");
+ ddi_soft_state_fini(&superInfoHead);
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * _fini --
+ * Invoked when a module is being removed from the kernel.
+ *
+ * Results:
+ * Returns the result of mod_remove(9F), which is zero on success, and a
+ * non-zero value on failure.
+ *
+ * Side effects:
+ * The module will be removed from the kernel.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+_fini(void)
+{
+ HgfsSuperInfo *sip;
+ int error;
+
+ DEBUG(VM_DEBUG_ENTRY, "_fini() for HGFS.\n");
+
+ sip = ddi_get_soft_state(superInfoHead, hgfsInstance);
+
+ /*
+ * Make sure the device is closed and the fs is not mounted if we have
+ * a valid superinfo pointer.
+ */
+ if (sip && (sip->devOpen || sip->vfsp)) {
+ DEBUG(VM_DEBUG_FAIL, "Cannot unload module because either device"
+ "is open or file system is mounted\n");
+ return -1;
+ }
+
+ error = mod_remove(&HgfsModlinkage);
+ if (error) {
+ cmn_err(HGFS_ERROR, "could not remove HGFS module.\n");
+ return error;
+ }
+
+#if HGFS_VFS_VERSION > 2
+ if (hgfsType != HGFS_TYPE_UNINITIALIZED) {
+ vfs_freevfsops_by_type(hgfsType);
+ }
+ if (sip && sip->vnodeOps) {
+ vn_freevnodeops(sip->vnodeOps);
+ }
+#endif
+ ddi_soft_state_fini(&superInfoHead); /* void return */
+
+ DEBUG(VM_DEBUG_DONE, "_fini() done.\n");
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * _info --
+ *
+ * Invoked when the modinfo(1M) command is executed. mod_info(9F) handles
+ * this for us.
+ *
+ * Results:
+ * Returns mod_info(9F)'s results, which are a non-zero value on success, and
+ * zero on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+_info(struct modinfo *modinfop) // OUT: Filled in with module's information by
+{
+ DEBUG(VM_DEBUG_ENTRY, "_info().\n");
+ ASSERT(modinfop);
+
+ if (!modinfop) {
+ cmn_err(HGFS_ERROR, "NULL input in _info\n");
+ return EINVAL;
+ }
+
+ return mod_info(&HgfsModlinkage, modinfop);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * module.h --
+ *
+ * EXTERNs needed by structures in module.c
+ */
+
+#ifndef __MODULE_H_
+#define __MODULE_H_
+
+#include "filesystem.h"
+
+/*
+ * Device driver EXTERNs
+ */
+
+/* Functions for the cb_ops structure (see driver.c) */
+EXTERN int HgfsDevOpen(dev_t *devp, int flag, int otyp, cred_t *credp);
+EXTERN int HgfsDevClose(dev_t dev, int flag, int otyp, cred_t *credp);
+EXTERN int HgfsDevRead(dev_t dev, uio_t *uiop, cred_t *credp);
+EXTERN int HgfsDevWrite(dev_t dev, uio_t *uiop, cred_t *credp);
+EXTERN int HgfsDevChpoll(dev_t dev, short int events, int anyyet,
+ short int *reventsp, struct pollhead **phpp);
+
+/* Functions for the dev_ops structure (see driver.c) */
+EXTERN int HgfsDevGetinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
+ void *arg, void **result);
+EXTERN int HgfsDevAttach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+EXTERN int HgfsDevDetach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+/* Struct needed in struct modldrv */
+EXTERN struct mod_ops mod_driverops;
+
+
+/*
+ * Filesystem EXTERNs
+ */
+
+/* Filesystem initialization routine (see filesystem.c) */
+#if HGFS_VFS_VERSION == 2
+EXTERN int HgfsInit(struct vfssw *vfsswp, int fstype);
+#else
+EXTERN int HgfsInit(int, char *);
+#endif
+
+/* Functions for the vfsops structure (see filesystem.c) */
+EXTERN int HgfsMount(struct vfs *vfsp, struct vnode *vnodep,
+ struct mounta *mntp, struct cred *credp);
+EXTERN int HgfsUnmount(struct vfs *vfsp, int mflag, struct cred *credp);
+EXTERN int HgfsRoot(struct vfs *vfsp, struct vnode **vnodepp);
+EXTERN int HgfsStatvfs(struct vfs *vfsp, struct statvfs64 *stats);
+EXTERN int HgfsSync(struct vfs* vfsp, short flags, struct cred *credp);
+EXTERN int HgfsVget(struct vfs *vfsp, struct vnode **vnodepp, struct fid *fidp);
+EXTERN int HgfsMountroot(struct vfs *vfsp, enum whymountroot reason);
+EXTERN int HgfsReserved(struct vfs *vfsp, struct vnode **vnodepp, char * /* ?? */);
+EXTERN void HgfsFreevfs(struct vfs *vfsp);
+
+/* Struct needed in struct modlfs */
+EXTERN struct mod_ops mod_fsops;
+
+
+#endif /* __MODULE_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * request.c --
+ *
+ * Implementation of routines used to initialize, allocate, and move requests
+ * between lists.
+ *
+ */
+
+/*
+ * Includes
+ */
+#include "hgfsSolaris.h"
+#include "request.h"
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsInitRequestList --
+ *
+ * Initializes the request list related members of the HgfsSuperInfo for
+ * this instance of the driver state.
+ *
+ * Results:
+ * The pending request list, free request list, and associated
+ * synchronization primitives are initialized.
+ *
+ * Side effects:
+ * Each request is now in the free list and is set to UNUSED.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+HgfsInitRequestList(HgfsSuperInfo *sip) // IN: Pointer to superinfo structure
+{
+ int i;
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsInitRequestList().\n");
+
+ ASSERT(sip);
+ /*
+ * We ASSERT() this because this function must be called before the device is
+ * opened to ensure the request lists are ready to be used by the
+ * filesystem. (The filesystem won't mount unless the device is open.)
+ * By ASSERT()ing this condition we also obviate the need to acquire the
+ * mutex when adding requests to reqFreeList in the for loop below, since we
+ * are guaranteed (in practice) to have exclusive access.
+ */
+ ASSERT(!sip->devOpen);
+
+ /* None of these fail */
+
+ /* Initialize pending request list */
+ DblLnkLst_Init(&sip->reqList);
+ mutex_init(&sip->reqMutex, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&sip->reqCondVar, NULL, CV_DRIVER, NULL);
+
+ /* Initialize free request list */
+ DblLnkLst_Init(&sip->reqFreeList);
+ mutex_init(&sip->reqFreeMutex, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&sip->reqFreeCondVar, NULL, CV_DRIVER, NULL);
+
+ /*
+ * Initialize pool of requests
+ *
+ * Here we are setting each request's id to its index into the requestPool
+ * so this can be used as an identifier in reply packets. Each request's
+ * state is also set to UNUSED and is added to the free list.
+ */
+ for (i = 0; i < ARRAYSIZE(requestPool); i++) {
+ requestPool[i].id = i;
+ requestPool[i].state = HGFS_REQ_UNUSED;
+ mutex_init(&requestPool[i].stateLock, NULL, MUTEX_DRIVER, NULL);
+
+ DblLnkLst_Init(&requestPool[i].listNode);
+ DblLnkLst_LinkLast(&sip->reqFreeList, &requestPool[i].listNode);
+ }
+
+ //HgfsDebugPrintReqList(&sip->reqFreeList);
+ DEBUG(VM_DEBUG_REQUEST, "HgfsInitRequestList() done.\n");
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsCancelAllRequests --
+ *
+ * Cancels all pending requests by removing them from the request list and
+ * waking up the clients waiting for replies. Also cancels requests that
+ * have already been sent to guestd (and are not on the pending list) and
+ * are either abandoned or still have clients waiting for them.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The pending request list is empty and all requests are in the free list.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+HgfsCancelAllRequests(HgfsSuperInfo *sip) // IN: Superinfo containing
+ // request list
+{
+ DblLnkLst_Links *currNode, *nextNode;
+ HgfsReq *currRequest;
+ int i;
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests().\n");
+
+ ASSERT(sip);
+
+ /* Traverse list to make sure we cancel all pending requests on the list. */
+ mutex_enter(&sip->reqMutex);
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests(): traversing pending request list.\n");
+
+ for (currNode = HGFS_REQ_LIST_HEAD_NODE(sip);
+ currNode != &sip->reqList;
+ currNode = nextNode) {
+
+ /* Get the next element while we still have a pointer to it. */
+ nextNode = currNode->next;
+
+ DblLnkLst_Unlink1(currNode);
+ currRequest = DblLnkLst_Container(currNode, HgfsReq, listNode);
+
+ if (HgfsReqGetState(currRequest) == HGFS_REQ_ABANDONED) {
+ /*
+ * If the client is no longer waiting, clean up for it by destroying
+ * the request.
+ */
+ HgfsDestroyReq(sip, currRequest);
+ } else {
+ /*
+ * ... otherwise indicate an error and wakeup the client. Note that
+ * we can't call HgfsWakeWaitingClient() because we already hold the
+ * reqMutex lock.
+ *
+ * Also, make sure that all non-ABANDONED requests are SUBMITTED.
+ */
+ ASSERT(HgfsReqGetState(currRequest) == HGFS_REQ_SUBMITTED);
+
+ HgfsReqSetState(currRequest, HGFS_REQ_ERROR);
+ cv_signal(&currRequest->condVar);
+ }
+ }
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests(): traversing request pool.\n");
+
+ /*
+ * Now look for abandoned and submitted requests that are in the pool but
+ * not on the pending list (because they were already sent to guestd).
+ */
+ for (i = 0; i < ARRAYSIZE(requestPool); i++) {
+ /*
+ * As above, abandoned requests are cleaned up and we wake up the clients
+ * for submitted requests.
+ */
+ if (HgfsReqGetState(&requestPool[i]) == HGFS_REQ_ABANDONED) {
+ HgfsDestroyReq(sip, &requestPool[i]);
+ } else if (HgfsReqGetState(&requestPool[i]) == HGFS_REQ_SUBMITTED) {
+ HgfsReqSetState(&requestPool[i], HGFS_REQ_ERROR);
+ cv_signal(&requestPool[i].condVar);
+ }
+ }
+
+ mutex_exit(&sip->reqMutex);
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests() done.\n");
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsListIsEmpty --
+ *
+ * Determines whether the provided list is empty.
+ *
+ * Note: this assumes it is called with the list lock held because often
+ * callers will need this function to be atomic with other operations.
+ *
+ * Results:
+ * Returns zero if list is not empty, a positive integer if it is empty.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE Bool
+HgfsListIsEmpty(DblLnkLst_Links *listAnchor) // IN: Anchor of list to check
+{
+ ASSERT(listAnchor);
+
+ return (listAnchor == listAnchor->next);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetNewReq --
+ *
+ * Allocates and initializes a new request structure from the request pool.
+ * This function blocks until a request is available or it has been
+ * interrupted by a signal.
+ *
+ * Results:
+ * Returns pointer to allocated HgfsReq on success, and NULL if interrupted
+ * while waiting to be allocated a request structure.
+ *
+ * Side effects:
+ * Request's state is set to HGFS_REQ_ALLOCATED.
+ * Request is removed from the free list.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+HgfsReq *
+HgfsGetNewReq(HgfsSuperInfo *sip) // IN: Superinfo containing free list
+{
+ HgfsReq *newReq;
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsGetNewReq().\n");
+
+ ASSERT(sip);
+
+ /*
+ * Here we atomically get the next free request from the free list and set
+ * that request's state to ALLOCATED.
+ */
+ mutex_enter(&sip->reqFreeMutex);
+
+ /* Wait for a request structure if there aren't any free */
+ while (HgfsListIsEmpty(&sip->reqFreeList)) {
+ /*
+ * If the list is empty, we wait on the condition variable which is
+ * unconditionally signaled whenever a request is destroyed.
+ */
+ if (cv_wait_sig(&sip->reqFreeCondVar, &sip->reqFreeMutex) == 0) {
+ /*
+ * We were interrupted while waiting for a request, so we must return
+ * NULL and release the mutex.
+ */
+ newReq = NULL;
+ goto out;
+ }
+ }
+
+ newReq = HGFS_FREE_REQ_LIST_HEAD(sip);
+
+ HgfsDebugPrintReq("HgfsGetNewReq", newReq);
+
+ /* Failure of these indicates error in program's logic */
+ ASSERT(newReq && (HgfsReqGetState(newReq) == HGFS_REQ_UNUSED));
+
+ /* Take request off the free list and indicate it has been ALLOCATED */
+ DblLnkLst_Unlink1(&newReq->listNode);
+ HgfsReqSetState(newReq, HGFS_REQ_ALLOCATED);
+
+ /* Clear packet of request before allocating to clients. */
+ bzero(newReq->packet, sizeof newReq->packet);
+
+out:
+ mutex_exit(&sip->reqFreeMutex);
+
+ DEBUG(VM_DEBUG_LIST, "Dequeued from free list: %s", newReq->packet);
+ HgfsDebugPrintReqList(&sip->reqFreeList);
+ DEBUG(VM_DEBUG_REQUEST, "HgfsGetNewReq() done.\n");
+
+ return newReq;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDestroyReq --
+ *
+ * Deallocates a request structure.
+ *
+ * Results:
+ * Returns void.
+ *
+ * Side effects:
+ * Request's state is set to HGFS_REQ_UNUSED.
+ * Request is placed on the free list.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsDestroyReq(HgfsSuperInfo *sip, // IN: Superinfo containing free list
+ HgfsReq *oldReq) // IN: Request to destroy
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsDestroyReq().\n");
+
+ /* XXX This should go away later, just for testing */
+ if (HgfsReqGetState(oldReq) != HGFS_REQ_COMPLETED) {
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsDestroyReq() (oldReq state=%d).\n",
+ HgfsReqGetState(oldReq));
+ }
+
+ ASSERT(sip);
+ ASSERT(oldReq);
+ /* Failure of this check indicates an error in program logic */
+ ASSERT((HgfsReqGetState(oldReq) == HGFS_REQ_COMPLETED) ||
+ (HgfsReqGetState(oldReq) == HGFS_REQ_ABANDONED) ||
+ (HgfsReqGetState(oldReq) == HGFS_REQ_ERROR));
+
+ /*
+ * To make the request available for other clients we change its state to
+ * UNUSED and place it back on the free list.
+ */
+ mutex_enter(&sip->reqFreeMutex);
+
+ HgfsReqSetState(oldReq, HGFS_REQ_UNUSED);
+ DblLnkLst_LinkLast(&sip->reqFreeList, &oldReq->listNode);
+ /* Wake up clients waiting for a request structure */
+ cv_signal(&sip->reqFreeCondVar);
+
+ mutex_exit(&sip->reqFreeMutex);
+
+ HgfsDebugPrintReqList(&sip->reqFreeList);
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsDestroyReq() done.\n");
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsEnqueueRequest --
+ *
+ * Adds the provided request to the end of the request list.
+ *
+ * Note: this assumes it is called with the list lock held because often
+ * callers will need this function to be atomic with other operations.
+ *
+ * Results:
+ * Returns void.
+ *
+ * Side effects:
+ * Request's state is set to HGFS_REQ_SUBMITTED.
+ * Request is added to the pending request list.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsEnqueueRequest(HgfsSuperInfo *sip, // IN: Superinfo containing request list
+ HgfsReq *newReq) // IN: Request to enqueue
+{
+ DEBUG(VM_DEBUG_REQUEST, "HgfsEnqueueRequest().\n");
+
+ ASSERT(sip);
+ ASSERT(newReq);
+ /* Failure of this check indicates error in program logic */
+ ASSERT(HgfsReqGetState(newReq) == HGFS_REQ_ALLOCATED);
+
+ /*
+ * This simply changes the state and places the request on the pending
+ * request list. Signaling the (potentially sleeping) device is handled by
+ * the caller (HgfsSubmitRequest()).
+ */
+ HgfsReqSetState(newReq, HGFS_REQ_SUBMITTED);
+ DblLnkLst_LinkLast(&sip->reqList, &newReq->listNode);
+
+ DEBUG(VM_DEBUG_LIST, "Enqueued on pending list: %s", newReq->packet);
+ HgfsDebugPrintReqList(&sip->reqList);
+ DEBUG(VM_DEBUG_REQUEST, "HgfsEnqueueRequest() done.\n");
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDequeueRequest --
+ *
+ * Removes the next request from the list.
+ *
+ * Note: this assumes it is called with the list lock held because often
+ * callers will need this function to be atomic with other operations.
+ *
+ * Results:
+ * Returns a pointer to the request removed from the list, or NULL if the
+ * list is empty.
+ *
+ * Side effects:
+ * Request is removed from the pending request list.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE HgfsReq *
+HgfsDequeueRequest(HgfsSuperInfo *sip) // IN: Superinfo containing request list
+{
+ HgfsReq *nextReq;
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsDequeueRequest().\n");
+
+ ASSERT(sip);
+
+ if (HgfsListIsEmpty(&sip->reqList)) {
+ return NULL;
+ }
+
+ nextReq = HGFS_REQ_LIST_HEAD(sip);
+
+ /*
+ * nextReq should never be NULL since we are called with the lock held and
+ * we just checked if the list was empty. The request's state should only
+ * be SUBMITTED or ABANDONED. (Errors occurring while copying the request
+ * to guestd happen after removal from this list.)
+ */
+ ASSERT(nextReq);
+ ASSERT(HgfsReqGetState(nextReq) == HGFS_REQ_SUBMITTED ||
+ HgfsReqGetState(nextReq) == HGFS_REQ_ABANDONED);
+
+ DblLnkLst_Unlink1(&nextReq->listNode);
+
+ DEBUG(VM_DEBUG_LIST, "Dequeued from pending list: %s", nextReq->packet);
+ HgfsDebugPrintReqList(&sip->reqList);
+ DEBUG(VM_DEBUG_REQUEST, "HgfsDequeueRequest() done.\n");
+
+ return nextReq;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsWakeWaitingClient --
+ *
+ * Wakes up the client waiting on the specified request.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsWakeWaitingClient(HgfsSuperInfo *sip, // IN: Superinfo with request mutex
+ HgfsReq *req) // IN: Request to wake client for
+{
+// DEBUG(VM_DEBUG_REQUEST, "HgfsWakeWaitingClient().\n");
+
+ ASSERT(sip);
+ ASSERT(req);
+
+ /*
+ * We need to acquire the mutex before signaling the request's condition
+ * variable since it was acquired before sleeping in HgfsSubmitRequest().
+ */
+ mutex_enter(&sip->reqMutex);
+
+ cv_signal(&req->condVar);
+
+ mutex_exit(&sip->reqMutex);
+
+// DEBUG(VM_DEBUG_REQUEST, "HgfsWakeWaitingClient() done.\n");
+}
+
+
+/*
+ * These two state functions will help prevent forgetting to lock.
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ * HgfsSetState --
+ *
+ * Sets the state of the request.
+ *
+ * Results:
+ * Returns void.
+ *
+ * Side effects:
+ * The state of the request is modified.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE void
+HgfsReqSetState(HgfsReq *req, // IN: Request to alter state of
+ HgfsReqState state) // IN: State to set request to
+{
+// DEBUG(VM_DEBUG_REQUEST, "HgfsReqSetState().\n");
+
+ ASSERT(req);
+
+ mutex_enter(&req->stateLock);
+
+ req->state = state;
+
+ mutex_exit(&req->stateLock);
+
+// DEBUG(VM_DEBUG_REQUEST, "HgfsReqSetState() done.\n");
+}
+
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsReqGetState --
+ *
+ * Retrieves state of provided request.
+ *
+ * Results:
+ * Returns state of request.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE HgfsReqState
+HgfsReqGetState(HgfsReq *req) // IN: Request to retrieve state of
+{
+ HgfsReqState state;
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsReqGetState().\n");
+
+ ASSERT(req);
+
+ mutex_enter(&req->stateLock);
+
+ state = req->state;
+
+ mutex_exit(&req->stateLock);
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsReqGetState() done.\n");
+ return state;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * request.h --
+ *
+ * Prototypes for request initialization, allocation, and list manipulation
+ * functions.
+ *
+ */
+
+#ifndef __REQUEST_H_
+#define __REQUEST_H_
+
+
+#include <sys/ksynch.h> /* mutexes and condition variables */
+
+#include "hgfsSolaris.h"
+#include "debug.h"
+
+#include "dbllnklst.h" /* Double link list types */
+
+/*
+ * Macros
+ */
+
+/* Since the DblLnkLst_Links in the superinfo is just an anchor, we want to
+ * skip it (e.g., get the container for the next element) */
+#define HGFS_REQ_LIST_HEAD(si) \
+ (DblLnkLst_Container(si->reqList.next, HgfsReq, listNode))
+#define HGFS_REQ_LIST_HEAD_NODE(si) \
+ (si->reqList.next)
+#define HGFS_FREE_REQ_LIST_HEAD(si) \
+ (DblLnkLst_Container(si->reqFreeList.next, HgfsReq, listNode))
+#define HGFS_FREE_REQ_LIST_HEAD_NODE(si) \
+ (si->reqFreeList.next)
+
+
+/*
+ * Functions
+ */
+void HgfsInitRequestList(HgfsSuperInfo *sip);
+void HgfsCancelAllRequests(HgfsSuperInfo *sip);
+INLINE Bool HgfsListIsEmpty(DblLnkLst_Links *listAnchor);
+HgfsReq *HgfsGetNewReq(HgfsSuperInfo *sip);
+INLINE void HgfsDestroyReq(HgfsSuperInfo *sip, HgfsReq *oldReq);
+INLINE void HgfsEnqueueRequest(HgfsSuperInfo *sip, HgfsReq *newReq);
+INLINE HgfsReq *HgfsDequeueRequest(HgfsSuperInfo *sip);
+INLINE void HgfsWakeWaitingClient(HgfsSuperInfo *sip, HgfsReq *req);
+INLINE void HgfsReqSetState(HgfsReq *req, HgfsReqState state);
+INLINE HgfsReqState HgfsReqGetState(HgfsReq *req);
+
+#endif /* __REQUEST_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+SHA-1 in C
+Originally released by Steve Reid <steve@edmweb.com> into the public domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/*
+ 12/15/98: JEB: Removed main and moved prototypes to sha1.h
+ Made SHA1Transform a static function
+*/
+
+#if defined(USERLEVEL) || defined(_WIN32)
+# include <string.h>
+# if defined(_WIN32)
+# include <memory.h>
+# endif
+#endif
+
+#if defined(sun) && !defined(SOL9)
+#include <memory.h>
+#endif
+
+#if defined(__FreeBSD__)
+# if defined(_KERNEL)
+# include <sys/libkern.h>
+# include <sys/systm.h>
+# else
+# include <string.h>
+# endif
+#endif
+
+#if defined(__APPLE__)
+# include <string.h>
+#endif
+
+#ifdef USERLEVEL
+#include "vmware.h"
+#endif
+#ifdef VMKBOOT
+#include "vm_libc.h"
+#endif
+#include "sha1.h"
+#include "vm_basic_asm.h"
+
+/* If the endianess is not defined (it is done in string.h of glibc 2.1.1), we
+ default to LE --hpreg */
+#ifndef LITTLE_ENDIAN
+# define LITTLE_ENDIAN /* This should be #define'd if true. */
+#endif
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+#define F0(w,x,y) ((w&(x^y))^y)
+#define F1(w,x,y) (w^x^y)
+#define F2(w,x,y) (((w|x)&y)|(w&x))
+#define F3(w,x,y) (w^x^y)
+
+typedef union {
+ unsigned char c[64];
+ uint32 l[16];
+} CHAR64LONG16;
+
+static void
+R(CHAR64LONG16 *block, uint32 *f, int i)
+{
+ uint32 a, b, c, d, e, round, blk;
+ a = f[0];
+ b = f[1];
+ c = f[2];
+ d = f[3];
+ e = f[4];
+ f[1] = a;
+ f[2] = rol(b, 30);
+ f[3] = c;
+ f[4] = d;
+ if (i < 20) {
+ round = 0x5A827999 + F0(b,c,d);
+ } else if (i < 40) {
+ round = 0x6ED9EBA1 + F1(b,c,d);
+ } else if (i < 60) {
+ round = 0x8F1BBCDC + F2(b,c,d);
+ } else {
+ round = 0xCA62C1D6 + F3(b,c,d);
+ }
+ if (i < 16) {
+#ifdef LITTLE_ENDIAN
+ blk = Bswap(block->l[i]);
+#else
+ blk = block->l[i];
+#endif
+ } else {
+ blk = rol(block->l[(i+13) & 15] ^ block->l[(i+8) & 15] ^
+ block->l[(i+2) & 15] ^ block->l[i & 15], 1);
+ }
+ block->l[i & 15] = blk;
+ f[0] = e + round + blk + rol(a, 5);
+}
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void
+_SHA1Transform(uint32 state[5], unsigned char buffer[64])
+{
+ int i;
+ uint32 f[5];
+ CHAR64LONG16* block = (CHAR64LONG16*)buffer;
+
+ /* Copy context->state[] to working vars */
+ for (i = 0; i < 5; i++) {
+ f[i] = state[i];
+ }
+
+ for (i = 0; i < 80; i++) {
+ R(block, f, i);
+ }
+
+ /* Add the working vars back into context.state[] */
+ for (i = 0; i < 5; i++) {
+ state[i] += f[i];
+ }
+}
+
+#ifdef SHA1HANDSOFF
+void SHA1Transform(uint32 state[5], const unsigned char buffer[64])
+#else
+void SHA1Transform(uint32 state[5], unsigned char buffer[64])
+#endif
+{
+#ifdef SHA1HANDSOFF
+ unsigned char workspace[64];
+
+ /* Do not do that work in _SHA1Transform, otherwise gcc 2.7.2.3 will go
+ south and allocate a stack frame of 0x9c8 bytes, that immediately
+ leads to a stack smash and a host reset */
+ memcpy(workspace, buffer, sizeof(workspace));
+ _SHA1Transform(state, workspace);
+#else
+ _SHA1Transform(state, buffer);
+#endif
+}
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+#ifdef SHA1HANDSOFF
+void SHA1Update(SHA1_CTX* context,
+ const unsigned char *data,
+ size_t len)
+#else
+void SHA1Update(SHA1_CTX* context,
+ unsigned char *data,
+ size_t len)
+#endif
+{
+ size_t i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += (uint32) (len << 3)) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (uint32) (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[SHA1_HASH_LEN], SHA1_CTX* context)
+{
+ size_t i, j;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (unsigned char *)"\0", 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < SHA1_HASH_LEN; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, SHA1_HASH_LEN);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+//#ifdef SHA1HANDSOFF
+// /* make SHA1Transform overwrite it's own static vars */
+// SHA1Transform(context->state, context->buffer);
+//#endif
+}
+
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * sha1.h --
+ *
+ * SHA1 encryption
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMNIXMOD
+#include "includeCheck.h"
+
+/* for uint32 */
+#include "vm_basic_types.h"
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/*
+ 12/15/98: JEB: Removed main and moved prototypes to sha1.h
+ Made SHA1Transform a static function
+*/
+
+#define SHA1_HASH_LEN 20
+
+typedef struct {
+ uint32 state[5];
+ uint32 count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+#define SHA1HANDSOFF /* Copies data before messing with it. */
+
+void SHA1Init(SHA1_CTX* context);
+#ifdef SHA1HANDSOFF
+void SHA1Update(SHA1_CTX* context,
+ const unsigned char *data,
+ size_t len);
+#else
+void SHA1Update(SHA1_CTX* context,
+ unsigned char *data,
+ size_t len);
+#endif
+void SHA1Final(unsigned char digest[SHA1_HASH_LEN], SHA1_CTX* context);
+
+void SHA1Transform(uint32 state[5], const unsigned char buffer[64]);
+
+#endif // ifndef _SHA1_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998-2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vm_assert.h --
+ *
+ * The basic assertion facility for all VMware code.
+ *
+ * For proper use, see
+ * http://vmweb.vmware.com/~mts/WebSite/guide/programming/asserts.html
+ */
+
+#ifndef _VM_ASSERT_H_
+#define _VM_ASSERT_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMNIXMOD
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMKDRIVERS
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMIROM
+#include "includeCheck.h"
+
+// XXX not necessary except some places include vm_assert.h improperly
+#include "vm_basic_types.h"
+#include "vm_basic_defs.h"
+
+
+/*
+ * XXX old file code
+ */
+
+#ifdef FILECODEINT
+#error "Don't define FILECODEINT. It is obsolete."
+#endif
+#ifdef FILECODE
+#error "Don't define FILECODE. It is obsolete."
+#endif
+
+
+/*
+ * Panic and log functions
+ */
+
+EXTERN void Log(const char *fmt, ...) PRINTF_DECL(1, 2);
+EXTERN void Warning(const char *fmt, ...) PRINTF_DECL(1, 2);
+EXTERN NORETURN void Panic(const char *fmt, ...) PRINTF_DECL(1, 2);
+
+EXTERN void LogThrottled(uint32 *count, const char *fmt, ...)
+ PRINTF_DECL(2, 3);
+EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...)
+ PRINTF_DECL(2, 3);
+
+/* DB family: messages which are parsed by logfile database system */
+#define WarningDB Warning
+#define LogDB Log
+#define WarningThrottledDB WarningThrottled
+#define LogThrottledDB LogThrottled
+
+
+/*
+ * Stress testing: redefine ASSERT_IFNOT() to taste
+ */
+
+#ifndef ASSERT_IFNOT
+ #ifdef __cplusplus
+ #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0)
+ #else
+ #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0)
+ #endif
+#endif
+
+
+/*
+ * Assert, panic, and log macros
+ *
+ * Some of these are redefined below undef !VMX86_DEBUG.
+ * ASSERT() is special cased because of interaction with Windows DDK.
+ */
+
+#if defined VMX86_DEBUG || defined ASSERT_ALWAYS_AVAILABLE
+#undef ASSERT
+#define ASSERT(cond) \
+ ASSERT_IFNOT(cond, _ASSERT_PANIC(AssertAssert))
+#endif
+#define ASSERT_BUG(bug, cond) \
+ ASSERT_IFNOT(cond, _ASSERT_PANIC_BUG(bug, AssertAssert))
+#define ASSERT_BUG_DEBUGONLY(bug, cond) ASSERT_BUG(bug, cond)
+
+#define PANIC() _ASSERT_PANIC(AssertPanic)
+#define PANIC_BUG(bug) _ASSERT_PANIC_BUG(bug, AssertPanic)
+
+#define ASSERT_NOT_IMPLEMENTED(cond) \
+ ASSERT_IFNOT(cond, NOT_IMPLEMENTED())
+#define ASSERT_NOT_IMPLEMENTED_BUG(bug, cond) \
+ ASSERT_IFNOT(cond, NOT_IMPLEMENTED_BUG(bug))
+
+#define NOT_IMPLEMENTED() _ASSERT_PANIC(AssertNotImplemented)
+#define NOT_IMPLEMENTED_BUG(bug) _ASSERT_PANIC_BUG(bug, AssertNotImplemented)
+
+#define NOT_REACHED() _ASSERT_PANIC(AssertNotReached)
+#define NOT_REACHED_BUG(bug) _ASSERT_PANIC_BUG(bug, AssertNotReached)
+
+#define ASSERT_MEM_ALLOC(cond) \
+ ASSERT_IFNOT(cond, _ASSERT_PANIC(AssertMemAlloc))
+
+#ifdef VMX86_DEVEL
+ #define ASSERT_LENGTH(real, expected) \
+ ASSERT_IFNOT((real) == (expected), \
+ Panic(AssertLengthFmt, __FILE__, __LINE__, real, expected))
+#else
+ #define ASSERT_LENGTH(real, expected) ASSERT((real) == (expected))
+#endif
+
+#ifdef VMX86_DEVEL
+ #define ASSERT_DEVEL(cond) ASSERT(cond)
+#else
+ #define ASSERT_DEVEL(cond) ((void) 0)
+#endif
+
+#define ASSERT_NO_INTERRUPTS() ASSERT(!INTERRUPTS_ENABLED())
+#define ASSERT_HAS_INTERRUPTS() ASSERT(INTERRUPTS_ENABLED())
+
+#define ASSERT_LOG_UNEXPECTED(bug, cond) \
+ (UNLIKELY(!(cond)) ? LOG_UNEXPECTED(bug) : 0)
+#ifdef VMX86_DEVEL
+ #define LOG_UNEXPECTED(bug) \
+ Warning(AssertUnexpectedFmt, __FILE__, __LINE__, bug)
+#else
+ #define LOG_UNEXPECTED(bug) \
+ Log(AssertUnexpectedFmt, __FILE__, __LINE__, bug)
+#endif
+
+#define ASSERT_NOT_TESTED(cond) (UNLIKELY(!(cond)) ? NOT_TESTED() : 0)
+#ifdef VMX86_DEVEL
+ #define NOT_TESTED() Warning(AssertNotTestedFmt, __FILE__, __LINE__)
+#else
+ #define NOT_TESTED() Log(AssertNotTestedFmt, __FILE__, __LINE__)
+#endif
+
+#define NOT_TESTED_ONCE() \
+ do { \
+ static Bool alreadyPrinted = FALSE; \
+ if (UNLIKELY(!alreadyPrinted)) { \
+ alreadyPrinted = TRUE; \
+ NOT_TESTED(); \
+ } \
+ } while (0)
+
+#define NOT_TESTED_1024() \
+ do { \
+ static uint16 count = 0; \
+ if (UNLIKELY(count == 0)) { NOT_TESTED(); } \
+ count = (count + 1) & 1023; \
+ } while (0)
+
+#define LOG_ONCE(_s) \
+ do { \
+ static Bool logged = FALSE; \
+ if (!logged) { \
+ Log _s; \
+ logged = TRUE; \
+ } \
+ } while (0)
+
+
+/*
+ * Redefine macros that are only in debug versions
+ */
+
+#if !defined VMX86_DEBUG && !defined ASSERT_ALWAYS_AVAILABLE // {
+
+#undef ASSERT
+#define ASSERT(cond) ((void) 0)
+
+#undef ASSERT_BUG_DEBUGONLY
+#define ASSERT_BUG_DEBUGONLY(bug, cond) ((void) 0)
+
+#undef ASSERT_LENGTH
+#define ASSERT_LENGTH(real, expected) ((void) 0)
+
+/*
+ * Expand NOT_REACHED() as appropriate for each situation.
+ *
+ * Mainly, we want the compiler to infer the same control-flow
+ * information as it would from Panic(). Otherwise, different
+ * compilation options will lead to different control-flow-derived
+ * errors, causing some make targets to fail while others succeed.
+ *
+ * VC++ has the __assume() built-in function which we don't trust
+ * (see bug 43485); gcc has no such construct; we just panic in
+ * userlevel code. The monitor doesn't want to pay the size penalty
+ * (measured at 212 bytes for the release vmm for a minimal infinite
+ * loop; panic would cost even more) so it does without and lives
+ * with the inconsistency.
+ */
+
+#ifdef VMM
+#undef NOT_REACHED
+#define NOT_REACHED() ((void) 0)
+#else
+// keep debug definition
+#endif
+
+#undef ASSERT_LOG_UNEXPECTED
+#define ASSERT_LOG_UNEXPECTED(bug, cond) ((void) 0)
+
+#undef LOG_UNEXPECTED
+#define LOG_UNEXPECTED(bug) ((void) 0)
+
+#undef ASSERT_NOT_TESTED
+#define ASSERT_NOT_TESTED(cond) ((void) 0)
+#undef NOT_TESTED
+#define NOT_TESTED() ((void) 0)
+#undef NOT_TESTED_ONCE
+#define NOT_TESTED_ONCE() ((void) 0)
+#undef NOT_TESTED_1024
+#define NOT_TESTED_1024() ((void) 0)
+
+#endif // !VMX86_DEBUG }
+
+
+/*
+ * Compile-time assertions.
+ *
+ * ASSERT_ON_COMPILE does not use the common
+ * switch (0) { case 0: case (e): ; } trick because some compilers (e.g. MSVC)
+ * generate code for it.
+ *
+ * The implementation uses both enum and typedef because the typedef alone is
+ * insufficient; gcc allows arrays to be declared with non-constant expressions
+ * (even in typedefs, where it makes no sense).
+ */
+
+#define ASSERT_ON_COMPILE(e) \
+ do { \
+ enum { AssertOnCompileMisused = ((e) ? 1 : -1) }; \
+ typedef char AssertOnCompileFailed[AssertOnCompileMisused]; \
+ } while (0)
+
+
+/*
+ * To put an ASSERT_ON_COMPILE() outside a function, wrap it
+ * in MY_ASSERTS(). The first parameter must be unique in
+ * each .c file where it appears. For example,
+ *
+ * MY_ASSERTS(FS3_INT,
+ * ASSERT_ON_COMPILE(sizeof(FS3_DiskLock) == 128);
+ * ASSERT_ON_COMPILE(sizeof(FS3_DiskLockReserved) == DISK_BLOCK_SIZE);
+ * ASSERT_ON_COMPILE(sizeof(FS3_DiskBlock) == DISK_BLOCK_SIZE);
+ * ASSERT_ON_COMPILE(sizeof(Hardware_DMIUUID) == 16);
+ * )
+ *
+ * Caution: ASSERT() within MY_ASSERTS() is silently ignored.
+ * The same goes for anything else not evaluated at compile time.
+ */
+
+#define MY_ASSERTS(name, assertions) \
+ static INLINE void name(void) { \
+ assertions \
+ }
+
+
+/*
+ * Internal macros, functions, and strings
+ *
+ * The monitor wants to save space at call sites, so it has specialized
+ * functions for each situation. User level wants to save on implementation
+ * so it uses generic functions.
+ */
+
+#if !defined VMM || defined MONITOR_APP // {
+
+#define _ASSERT_PANIC(name) \
+ Panic(_##name##Fmt "\n", __FILE__, __LINE__)
+#define _ASSERT_PANIC_BUG(bug, name) \
+ Panic(_##name##Fmt " bugNr=%d\n", __FILE__, __LINE__, bug)
+
+#define AssertLengthFmt _AssertLengthFmt
+#define AssertUnexpectedFmt _AssertUnexpectedFmt
+#define AssertNotTestedFmt _AssertNotTestedFmt
+
+#endif // }
+
+// these don't have newline so a bug can be tacked on
+#define _AssertPanicFmt "PANIC %s:%d"
+#define _AssertAssertFmt "ASSERT %s:%d"
+#define _AssertNotImplementedFmt "NOT_IMPLEMENTED %s:%d"
+#define _AssertNotReachedFmt "NOT_REACHED %s:%d"
+#define _AssertMemAllocFmt "MEM_ALLOC %s:%d"
+
+// these are complete formats with newline
+#define _AssertLengthFmt "LENGTH %s:%d r=%#x e=%#x\n"
+#define _AssertUnexpectedFmt "UNEXPECTED %s:%d bugNr=%d\n"
+#define _AssertNotTestedFmt "NOT_TESTED %s:%d\n"
+
+#endif /* ifndef _VM_ASSERT_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2003 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vm_basic_asm.h
+ *
+ * Basic asm macros
+ */
+
+#ifndef _VM_BASIC_ASM_H_
+#define _VM_BASIC_ASM_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMNIXMOD
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMIROM
+#include "includeCheck.h"
+
+#include "vm_basic_types.h"
+#include "x86cpuid.h"
+
+#ifdef VM_X86_64
+#include "vm_basic_asm_x86_64.h"
+#else
+#include "vm_basic_asm_x86.h"
+#endif
+
+
+/*
+ * x86-64 windows doesn't support inline asm so we have to use these
+ * intrinsic functions defined in the compiler. Not all of these are well
+ * documented. There is an array in the compiler dll (c1.dll) which has
+ * an array of the names of all the intrinsics minus the leading
+ * underscore. Searching around in the ntddk.h file can also be helpful.
+ *
+ * The declarations for the intrinsic functions were taken from the DDK.
+ * Our declarations must match the ddk's otherwise the 64-bit c++ compiler
+ * will complain about second linkage of the intrinsic functions.
+ * We define the intrinsic using the basic types corresponding to the
+ * Windows typedefs. This avoids having to include windows header files
+ * to get to the windows types.
+ */
+#ifdef _MSC_VER
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * It seems x86 & x86-64 windows still implements these intrinsic
+ * functions. The documentation for the x86-64 suggest the
+ * __inbyte/__outbyte intrinsics eventhough the _in/_out work fine and
+ * __inbyte/__outbyte aren't supported on x86.
+ */
+int _inp(unsigned short);
+unsigned short _inpw(unsigned short);
+unsigned long _inpd(unsigned short);
+
+int _outp(unsigned short, int);
+unsigned short _outpw(unsigned short, unsigned short);
+unsigned long _outpd(uint16, unsigned long);
+#pragma intrinsic(_inp, _inpw, _inpd, _outp, _outpw, _outpw, _outpd)
+void _ReadWriteBarrier(void);
+#pragma intrinsic(_ReadWriteBarrier)
+
+#ifdef VM_X86_64
+/*
+ * intrinsic functions only supported by x86-64 windows as of 2k3sp1
+ */
+void __cpuid(unsigned int*, unsigned int);
+unsigned __int64 __rdtsc(void);
+void __stosw(unsigned short*, unsigned short, size_t);
+void __stosd(unsigned long*, unsigned long, size_t);
+#pragma intrinsic(__cpuid, __rdtsc, __stosw, __stosd)
+
+/*
+ * intrinsic functions supported by x86-64 windows and newer x86
+ * compilers (13.01.2035 for _BitScanForward).
+ */
+unsigned char _BitScanForward(unsigned long*, unsigned long);
+void _mm_pause(void);
+#pragma intrinsic(_BitScanForward, _mm_pause)
+#endif /* VM_X86_64 */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _MSC_VER */
+
+
+#ifdef __GNUC__ // {
+
+/*
+ * Checked against the Intel manual and GCC --hpreg
+ *
+ * volatile because reading from port can modify the state of the underlying
+ * hardware.
+ *
+ * Note: The undocumented %z construct doesn't work (internal compiler error)
+ * with gcc-2.95.1
+ */
+
+#define __GCC_IN(s, type, name) \
+static INLINE type \
+name(uint16 port) \
+{ \
+ type val; \
+ \
+ __asm__ __volatile__( \
+ "in" #s " %w1, %0" \
+ : "=a" (val) \
+ : "Nd" (port) \
+ ); \
+ \
+ return val; \
+}
+
+__GCC_IN(b, uint8, INB)
+__GCC_IN(w, uint16, INW)
+__GCC_IN(l, uint32, IN32)
+
+
+/*
+ * Checked against the Intel manual and GCC --hpreg
+ *
+ * Note: The undocumented %z construct doesn't work (internal compiler error)
+ * with gcc-2.95.1
+ */
+
+#define __GCC_OUT(s, s2, port, val) do { \
+ __asm__( \
+ "out" #s " %" #s2 "1, %w0" \
+ : \
+ : "Nd" (port), "a" (val) \
+ ); \
+} while (0)
+
+#define OUTB(port, val) __GCC_OUT(b, b, port, val)
+#define OUTW(port, val) __GCC_OUT(w, w, port, val)
+#define OUT32(port, val) __GCC_OUT(l, , port, val)
+
+
+#define GET_CURRENT_EIP(_eip) \
+ __asm__ __volatile("call 0\n\tpopl %0" : "=r" (_eip): );
+
+
+/*
+ * Checked against the Intel manual and GCC --hpreg
+ *
+ * Need __volatile__ and "memory" since CPUID has a synchronizing effect.
+ * The CPUID may also change at runtime (APIC flag, etc).
+ *
+ */
+
+static INLINE void
+__GET_CPUID(int eax, // IN
+ CPUIDRegs *regs) // OUT
+{
+ __asm__ __volatile__(
+#if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
+ "movl %%ebx, %1" "\n\t"
+ "cpuid" "\n\t"
+ "xchgl %%ebx, %1"
+ : "=a" (regs->eax), "=&rm" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx)
+#else
+ "cpuid"
+ : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx)
+#endif
+ : "a" (eax)
+ : "memory"
+ );
+}
+
+static INLINE void
+__GET_CPUID2(int eax, // IN
+ int ecx, // IN
+ CPUIDRegs *regs) // OUT
+{
+ __asm__ __volatile__(
+#if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
+ "movl %%ebx, %1" "\n\t"
+ "cpuid" "\n\t"
+ "xchgl %%ebx, %1"
+ : "=a" (regs->eax), "=&rm" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx)
+#else
+ "cpuid"
+ : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx)
+#endif
+ : "a" (eax), "c" (ecx)
+ : "memory"
+ );
+}
+
+static INLINE uint32
+__GET_EAX_FROM_CPUID(int eax) // IN
+{
+#if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
+ uint32 ebx;
+
+ __asm__ __volatile__(
+ "movl %%ebx, %1" "\n\t"
+ "cpuid" "\n\t"
+ "xchgl %%ebx, %1"
+ : "=a" (eax), "=&rm" (ebx)
+ : "a" (eax)
+ : "memory", "%ecx", "%edx"
+ );
+#else
+ __asm__ __volatile__(
+ "cpuid"
+ : "=a" (eax)
+ : "a" (eax)
+ : "memory", "%ebx", "%ecx", "%edx"
+ );
+#endif
+
+ return eax;
+}
+
+static INLINE uint32
+__GET_EBX_FROM_CPUID(int eax) // IN
+{
+ uint32 ebx;
+
+ __asm__ __volatile__(
+#if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
+ "movl %%ebx, %1" "\n\t"
+ "cpuid" "\n\t"
+ "xchgl %%ebx, %1"
+ : "=a" (eax), "=&rm" (ebx)
+#else
+ "cpuid"
+ : "=a" (eax), "=b" (ebx)
+#endif
+ : "a" (eax)
+ : "memory", "%ecx", "%edx"
+ );
+
+ return ebx;
+}
+
+static INLINE uint32
+__GET_ECX_FROM_CPUID(int eax) // IN
+{
+ uint32 ecx;
+#if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
+ uint32 ebx;
+
+ __asm__ __volatile__(
+ "movl %%ebx, %1" "\n\t"
+ "cpuid" "\n\t"
+ "xchgl %%ebx, %1"
+ : "=a" (eax), "=&rm" (ebx), "=c" (ecx)
+ : "a" (eax)
+ : "memory", "%edx"
+ );
+#else
+
+ __asm__ __volatile__(
+ "cpuid"
+ : "=a" (eax), "=c" (ecx)
+ : "a" (eax)
+ : "memory", "%ebx", "%edx"
+ );
+#endif
+
+ return ecx;
+}
+
+static INLINE uint32
+__GET_EDX_FROM_CPUID(int eax) // IN
+{
+ uint32 edx;
+#if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
+ uint32 ebx;
+
+ __asm__ __volatile__(
+ "movl %%ebx, %1" "\n\t"
+ "cpuid" "\n\t"
+ "xchgl %%ebx, %1"
+ : "=a" (eax), "=&rm" (ebx), "=d" (edx)
+ : "a" (eax)
+ : "memory", "%ecx"
+ );
+#else
+
+ __asm__ __volatile__(
+ "cpuid"
+ : "=a" (eax), "=d" (edx)
+ : "a" (eax)
+ : "memory", "%ebx", "%ecx"
+ );
+#endif
+
+ return edx;
+}
+
+
+static INLINE uint32
+__GET_EAX_FROM_CPUID4(int ecx) // IN
+{
+ uint32 eax;
+#if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
+ uint32 ebx;
+
+ __asm__ __volatile__(
+ "movl %%ebx, %1" "\n\t"
+ "cpuid" "\n\t"
+ "xchgl %%ebx, %1"
+ : "=a" (eax), "=&rm" (ebx), "=c" (ecx)
+ : "a" (4), "c" (ecx)
+ : "memory", "%edx"
+ );
+#else
+
+ __asm__ __volatile__(
+ "cpuid"
+ : "=a" (eax), "=c" (ecx)
+ : "a" (4), "c" (ecx)
+ : "memory", "%ebx", "%edx"
+ );
+#endif
+
+ return eax;
+}
+
+#elif defined(_MSC_VER) // } {
+static INLINE uint8
+INB(uint16 port)
+{
+ return (uint8)_inp(port);
+}
+static INLINE void
+OUTB(uint16 port, uint8 value)
+{
+ _outp(port, value);
+}
+static INLINE uint16
+INW(uint16 port)
+{
+ return _inpw(port);
+}
+static INLINE void
+OUTW(uint16 port, uint16 value)
+{
+ _outpw(port, value);
+}
+static INLINE uint32
+IN32(uint16 port)
+{
+ return _inpd(port);
+}
+static INLINE void
+OUT32(uint16 port, uint32 value)
+{
+ _outpd(port, value);
+}
+
+#ifndef VM_X86_64
+#ifdef NEAR
+#undef NEAR
+#endif
+
+#define GET_CURRENT_EIP(_eip) do { \
+ __asm call NEAR PTR $+5 \
+ __asm pop eax \
+ __asm mov _eip, eax \
+} while (0)
+#endif
+
+static INLINE void
+__GET_CPUID(int input, CPUIDRegs *regs)
+{
+#ifdef VM_X86_64
+ __cpuid((unsigned int *)regs, input);
+#else
+ __asm push esi
+ __asm push ebx
+ __asm push ecx
+ __asm push edx
+
+ __asm mov eax, input
+ __asm mov esi, regs
+ __asm _emit 0x0f __asm _emit 0xa2
+ __asm mov 0x0[esi], eax
+ __asm mov 0x4[esi], ebx
+ __asm mov 0x8[esi], ecx
+ __asm mov 0xC[esi], edx
+
+ __asm pop edx
+ __asm pop ecx
+ __asm pop ebx
+ __asm pop esi
+#endif
+}
+
+#ifdef VM_X86_64
+
+/*
+ * No inline assembly in Win64. Implemented in bora/lib/user in
+ * cpuidMasm64.asm.
+ */
+
+extern void
+__GET_CPUID2(int inputEax, int inputEcx, CPUIDRegs *regs);
+
+#else // VM_X86_64
+
+static INLINE void
+__GET_CPUID2(int inputEax, int inputEcx, CPUIDRegs *regs)
+{
+ __asm push esi
+ __asm push ebx
+ __asm push ecx
+ __asm push edx
+
+ __asm mov eax, inputEax
+ __asm mov ecx, inputEcx
+ __asm mov esi, regs
+ __asm _emit 0x0f __asm _emit 0xa2
+ __asm mov 0x0[esi], eax
+ __asm mov 0x4[esi], ebx
+ __asm mov 0x8[esi], ecx
+ __asm mov 0xC[esi], edx
+
+ __asm pop edx
+ __asm pop ecx
+ __asm pop ebx
+ __asm pop esi
+}
+#endif
+
+static INLINE uint32
+__GET_EAX_FROM_CPUID(int input)
+{
+#ifdef VM_X86_64
+ CPUIDRegs regs;
+ __cpuid((unsigned int *)®s, input);
+ return regs.eax;
+#else
+ uint32 output;
+
+ //NOT_TESTED();
+ __asm push ebx
+ __asm push ecx
+ __asm push edx
+
+ __asm mov eax, input
+ __asm _emit 0x0f __asm _emit 0xa2
+ __asm mov output, eax
+
+ __asm pop edx
+ __asm pop ecx
+ __asm pop ebx
+
+ return output;
+#endif
+}
+
+static INLINE uint32
+__GET_EBX_FROM_CPUID(int input)
+{
+#ifdef VM_X86_64
+ CPUIDRegs regs;
+ __cpuid((unsigned int *)®s, input);
+ return regs.ebx;
+#else
+ uint32 output;
+
+ //NOT_TESTED();
+ __asm push ebx
+ __asm push ecx
+ __asm push edx
+
+ __asm mov eax, input
+ __asm _emit 0x0f __asm _emit 0xa2
+ __asm mov output, ebx
+
+ __asm pop edx
+ __asm pop ecx
+ __asm pop ebx
+
+ return output;
+#endif
+}
+
+static INLINE uint32
+__GET_ECX_FROM_CPUID(int input)
+{
+#ifdef VM_X86_64
+ CPUIDRegs regs;
+ __cpuid((unsigned int *)®s, input);
+ return regs.ecx;
+#else
+ uint32 output;
+
+ //NOT_TESTED();
+ __asm push ebx
+ __asm push ecx
+ __asm push edx
+
+ __asm mov eax, input
+ __asm _emit 0x0f __asm _emit 0xa2
+ __asm mov output, ecx
+
+ __asm pop edx
+ __asm pop ecx
+ __asm pop ebx
+
+ return output;
+#endif
+}
+
+static INLINE uint32
+__GET_EDX_FROM_CPUID(int input)
+{
+#ifdef VM_X86_64
+ CPUIDRegs regs;
+ __cpuid((unsigned int *)®s, input);
+ return regs.edx;
+#else
+ uint32 output;
+
+ //NOT_TESTED();
+ __asm push ebx
+ __asm push ecx
+ __asm push edx
+
+ __asm mov eax, input
+ __asm _emit 0x0f __asm _emit 0xa2
+ __asm mov output, edx
+
+ __asm pop edx
+ __asm pop ecx
+ __asm pop ebx
+
+ return output;
+#endif
+}
+
+#ifdef VM_X86_64
+
+/*
+ * No inline assembly in Win64. Implemented in bora/lib/user in
+ * cpuidMasm64.asm.
+ */
+
+extern uint32
+__GET_EAX_FROM_CPUID4(int inputEcx);
+
+#else // VM_X86_64
+
+static INLINE uint32
+__GET_EAX_FROM_CPUID4(int inputEcx)
+{
+ uint32 output;
+
+ //NOT_TESTED();
+ __asm push ebx
+ __asm push ecx
+ __asm push edx
+
+ __asm mov eax, 4
+ __asm mov ecx, inputEcx
+ __asm _emit 0x0f __asm _emit 0xa2
+ __asm mov output, eax
+
+ __asm pop edx
+ __asm pop ecx
+ __asm pop ebx
+
+ return output;
+}
+
+#endif // VM_X86_64
+
+#else // }
+#error
+#endif
+
+#define CPUID_FOR_SIDE_EFFECTS() ((void)__GET_EAX_FROM_CPUID(0))
+
+static INLINE void
+__GET_CPUID4(int inputEcx, CPUIDRegs *regs)
+{
+ __GET_CPUID2(4, inputEcx, regs);
+}
+
+/* The first parameter is used as an rvalue and then as an lvalue. */
+#define GET_CPUID(_ax, _bx, _cx, _dx) { \
+ CPUIDRegs regs; \
+ __GET_CPUID(_ax, ®s); \
+ _ax = regs.eax; \
+ _bx = regs.ebx; \
+ _cx = regs.ecx; \
+ _dx = regs.edx; \
+}
+
+
+/* Sequence recommended by Intel for the Pentium 4. */
+#define INTEL_MICROCODE_VERSION() ( \
+ __SET_MSR(MSR_BIOS_SIGN_ID, 0), \
+ __GET_EAX_FROM_CPUID(1), \
+ __GET_MSR(MSR_BIOS_SIGN_ID))
+
+
+#ifdef _MSC_VER
+static INLINE int
+ffs(uint32 bitVector)
+{
+ int idx;
+ if (!bitVector) {
+ return 0;
+ }
+#ifdef VM_X86_64
+ _BitScanForward((unsigned long*)&idx, (unsigned long)bitVector);
+#else
+ __asm bsf eax, bitVector
+ __asm mov idx, eax
+#endif
+ return idx+1;
+}
+#endif
+
+#ifdef __GNUC__
+static INLINE void *
+uint16set(void *dst, uint16 val, size_t count)
+{
+ int dummy0;
+ int dummy1;
+
+ __asm__ __volatile__("\t"
+ "cld" "\n\t"
+ "rep ; stosw" "\n"
+ : "=c" (dummy0), "=D" (dummy1)
+ : "0" (count), "1" (dst), "a" (val)
+ : "memory", "cc"
+ );
+
+ return dst;
+}
+
+static INLINE void *
+uint32set(void *dst, uint32 val, size_t count)
+{
+ int dummy0;
+ int dummy1;
+
+ __asm__ __volatile__("\t"
+ "cld" "\n\t"
+ "rep ; stosl" "\n"
+ : "=c" (dummy0), "=D" (dummy1)
+ : "0" (count), "1" (dst), "a" (val)
+ : "memory", "cc"
+ );
+
+ return dst;
+}
+
+#elif defined(_MSC_VER)
+
+static INLINE void *
+uint16set(void *dst, uint16 val, size_t count)
+{
+#ifdef VM_X86_64
+ __stosw((uint16*)dst, val, count);
+#else
+ __asm { pushf;
+ mov ax, val;
+ mov ecx, count;
+ mov edi, dst;
+ cld;
+ rep stosw;
+ popf;
+ }
+#endif
+ return dst;
+}
+
+static INLINE void *
+uint32set(void *dst, uint32 val, size_t count)
+{
+#ifdef VM_X86_64
+ __stosd((unsigned long*)dst, (unsigned long)val, count);
+#else
+ __asm { pushf;
+ mov eax, val;
+ mov ecx, count;
+ mov edi, dst;
+ cld;
+ rep stosd;
+ popf;
+ }
+#endif
+ return dst;
+}
+
+#else
+#error "No compiler defined for uint*set"
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Bswap32 --
+ *
+ * Swap the 4 bytes of "v" as follows: 3210 -> 0123.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE uint32
+Bswap32(uint32 v) // IN
+{
+#ifdef __GNUC__ // {
+ /* Checked against the Intel manual and GCC. --hpreg */
+ __asm__(
+ "bswap %0"
+ : "=r" (v)
+ : "0" (v)
+ );
+ return v;
+#else // } {
+ return (v >> 24)
+ | ((v >> 8) & 0xFF00)
+ | ((v & 0xFF00) << 8)
+ | (v << 24) ;
+#endif // }
+}
+#define Bswap Bswap32
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Bswap64 --
+ *
+ * Swap the 8 bytes of "v" as follows: 76543210 -> 01234567.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE uint64
+Bswap64(uint64 v) // IN
+{
+ return ((uint64)Bswap((uint32)v) << 32) | Bswap((uint32)(v >> 32));
+}
+
+
+#ifdef __GNUC__ // {
+/*
+ * COMPILER_MEM_BARRIER prevents the compiler from re-ordering memory
+ * references accross the barrier. NOTE: It does not generate any
+ * instruction, so the CPU is free to do whatever it wants to...
+ */
+#define COMPILER_MEM_BARRIER() __asm__ __volatile__ ("": : :"memory")
+#elif defined(_MSC_VER) // } {
+#define COMPILER_MEM_BARRIER() _ReadWriteBarrier()
+#endif // }
+
+
+/*
+ * PAUSE is a P4 instruction that improves spinlock power+performance;
+ * on non-P4 IA32 systems, the encoding is interpreted as a REPZ-NOP.
+ * Use volatile to avoid NOP removal.
+ */
+static INLINE void
+PAUSE(void)
+#ifdef __GNUC__
+{
+ __asm__ __volatile__( "pause" :);
+}
+#elif defined(_MSC_VER)
+#ifdef VM_X86_64
+{
+ _mm_pause();
+}
+#else /* VM_X86_64 */
+#pragma warning( disable : 4035)
+{
+ __asm _emit 0xf3 __asm _emit 0x90
+}
+#pragma warning (default: 4035)
+#endif /* VM_X86_64 */
+#else /* __GNUC__ */
+#error No compiler defined for PAUSE
+#endif
+
+
+/*
+ * Checked against the Intel manual and GCC --hpreg
+ *
+ * volatile because the tsc always changes without the compiler knowing it.
+ */
+static INLINE uint64
+RDTSC(void)
+#ifdef __GNUC__
+{
+#ifdef VM_X86_64
+ uint64 tscLow;
+ uint64 tscHigh;
+
+ __asm__ __volatile__(
+ "rdtsc"
+ : "=a" (tscLow), "=d" (tscHigh)
+ );
+
+ return tscHigh << 32 | tscLow;
+#else
+ uint64 tim;
+
+ __asm__ __volatile__(
+ "rdtsc"
+ : "=A" (tim)
+ );
+
+ return tim;
+#endif
+}
+#elif defined(_MSC_VER)
+#ifdef VM_X86_64
+{
+ return __rdtsc();
+}
+#else
+#pragma warning( disable : 4035)
+{
+ __asm _emit 0x0f __asm _emit 0x31
+}
+#pragma warning (default: 4035)
+#endif /* VM_X86_64 */
+#else /* __GNUC__ */
+#error No compiler defined for RDTSC
+#endif /* __GNUC__ */
+
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998-2003 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vm_basic_asm_x86.h
+ *
+ * Basic IA32 asm macros
+ */
+
+#ifndef _VM_BASIC_ASM_X86_H_
+#define _VM_BASIC_ASM_X86_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMNIXMOD
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMIROM
+#include "includeCheck.h"
+
+#ifdef VM_X86_64
+/*
+ * The gcc inline asm uses the "A" constraint which differs in 32 & 64
+ * bit mode. 32 bit means eax and edx, 64 means rax or rdx.
+ */
+#error "x86-64 not supported"
+#endif
+
+
+
+
+/*
+ * from linux: usr/include/asm/io.h
+ */
+#ifdef __GNUC__
+#ifndef __SLOW_DOWN_IO
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#else
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#endif
+#endif
+#elif _MSC_VER
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm jmp SHORT $+2 __asm jmp SHORT $+2
+#else
+#define __SLOW_DOWN_IO __asm out 80h,al
+#endif
+#else
+#error
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#else
+#define SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+/*
+ * FXSAVE/FXRSTOR
+ * save/restore SIMD/MMX fpu state
+ *
+ * The pointer passed in must be 16-byte aligned.
+ *
+ * Intel and AMD processors behave differently w.r.t. fxsave/fxrstor. Intel
+ * processors unconditionally save the exception pointer state (instruction
+ * ptr., data ptr., and error instruction opcode). FXSAVE_ES1 and FXRSTOR_ES1
+ * work correctly for Intel processors.
+ *
+ * AMD processors only save the exception pointer state if ES=1. This leads to a
+ * security hole whereby one process/VM can inspect the state of another process
+ * VM. The AMD recommended workaround involves clobbering the exception pointer
+ * state unconditionally, and this is implemented in FXRSTOR_AMD_ES0. Note that
+ * FXSAVE_ES1 will only save the exception pointer state for AMD processors if
+ * ES=1.
+ *
+ * The workaround (FXRSTOR_AMD_ES0) only costs 1 cycle more than just doing an
+ * fxrstor, on both AMD Opteron and Intel Core CPUs.
+ */
+#if defined(__GNUC__)
+static INLINE void
+FXSAVE_ES1(uint8 *save)
+{
+ __asm__ __volatile__ ("fxsave %0\n" : "=m" (*save) : : "memory");
+}
+
+static INLINE void
+FXRSTOR_ES1(const uint8 *load)
+{
+ __asm__ __volatile__ ("fxrstor %0\n" : : "m" (*load) : "memory");
+}
+
+static INLINE void
+FXRSTOR_AMD_ES0(const uint8 *load)
+{
+ uint64 dummy = 0;
+
+ __asm__ __volatile__
+ ("fnstsw %%ax \n" // Grab x87 ES bit
+ "bt $7,%%ax \n" // Test ES bit
+ "jnc 1f \n" // Jump if ES=0
+ "fnclex \n" // ES=1. Clear it so fild doesn't trap
+ "1: \n"
+ "ffree %%st(7) \n" // Clear tag bit - avoid poss. stack overflow
+ "fildl %0 \n" // Dummy Load from "safe address" changes all
+ // x87 exception pointers.
+ "fxrstor %1 \n"
+ :
+ : "m" (dummy), "m" (*load)
+ : "ax", "memory");
+}
+#endif /* __GNUC__ */
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Div643232 --
+ *
+ * Unsigned integer division:
+ * The dividend is 64-bit wide
+ * The divisor is 32-bit wide
+ * The quotient is 32-bit wide
+ *
+ * Use this function if you are certain that:
+ * o Either the quotient will fit in 32 bits,
+ * o Or your code is ready to handle a #DE exception indicating overflow.
+ * If that is not the case, then use Div643264(). --hpreg
+ *
+ * Results:
+ * Quotient and remainder
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__)
+
+static INLINE void
+Div643232(uint64 dividend, // IN
+ uint32 divisor, // IN
+ uint32 *quotient, // OUT
+ uint32 *remainder) // OUT
+{
+ /* Checked against the Intel manual and GCC --hpreg */
+ __asm__(
+ "divl %4"
+ : "=a" (*quotient),
+ "=d" (*remainder)
+ : "0" ((uint32)dividend),
+ "1" ((uint32)(dividend >> 32)),
+ "rm" (divisor)
+ : "cc"
+ );
+}
+
+#elif _MSC_VER
+
+static INLINE void
+Div643232(uint64 dividend, // IN
+ uint32 divisor, // IN
+ uint32 *quotient, // OUT
+ uint32 *remainder) // OUT
+{
+ /* Written and tested by mann, checked by dbudko and hpreg */
+ __asm {
+ mov eax, DWORD PTR [dividend]
+ mov edx, DWORD PTR [dividend+4]
+ div DWORD PTR [divisor]
+ mov edi, DWORD PTR [quotient]
+ mov [edi], eax
+ mov edi, DWORD PTR [remainder]
+ mov [edi], edx
+ }
+}
+
+#else
+#error No compiler defined for Div643232
+#endif
+
+
+#if defined(__GNUC__)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Div643264 --
+ *
+ * Unsigned integer division:
+ * The dividend is 64-bit wide
+ * The divisor is 32-bit wide
+ * The quotient is 64-bit wide --hpreg
+ *
+ * Results:
+ * Quotient and remainder
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+Div643264(uint64 dividend, // IN
+ uint32 divisor, // IN
+ uint64 *quotient, // OUT
+ uint32 *remainder) // OUT
+{
+ uint32 hQuotient;
+ uint32 lQuotient;
+
+ /* Checked against the Intel manual and GCC --hpreg */
+ __asm__(
+ "divl %5" "\n\t"
+ "movl %%eax, %0" "\n\t"
+ "movl %4, %%eax" "\n\t"
+ "divl %5"
+ : "=&rm" (hQuotient),
+ "=a" (lQuotient),
+ "=d" (*remainder)
+ : "1" ((uint32)(dividend >> 32)),
+ "g" ((uint32)dividend),
+ "rm" (divisor),
+ "2" (0)
+ : "cc"
+ );
+ *quotient = (uint64)hQuotient << 32 | lQuotient;
+}
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Mul64x3264 --
+ *
+ * Unsigned integer by fixed point multiplication:
+ * Unsigned 64-bit integer multiplicand.
+ * Unsigned 32-bit fixed point multiplier, represented as
+ * multiplier >> shift, where shift < 64.
+ * Unsigned 64-bit integer product.
+ *
+ * Implementation:
+ * Multiply 64x32 bits to yield a full 96-bit product.
+ * Shift right by shift.
+ * Return the low-order 64 bits of the result.
+ *
+ * Result:
+ * Product
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__)
+
+static INLINE uint64
+Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
+{
+ uint64 result;
+ uint32 tmp1, tmp2;
+ // ASSERT(shift >= 0 && shift < 64);
+
+ /*
+ * Written and tested by mann, improved with suggestions by hpreg.
+ *
+ * The main improvement over the previous version is that the test
+ * of shift against 32 is moved out of the asm and into C code.
+ * This lets the compiler delete the test and one of the
+ * alternative code sequences in the case where shift is a
+ * constant. It also lets us use the best code sequence in each
+ * alternative, rather than a compromise. The downside is that in
+ * the non-constant case, this version takes slightly more code
+ * space.
+ *
+ * Note on the constraints: We don't really want multiplicand to
+ * start in %edx:%eax as the =A constraint dictates; in fact, we'd
+ * prefer any *other* two registers. But gcc doesn't have
+ * constraint syntax for any other register pair, and trying to
+ * constrain ((uint32) multiplicand) to one place and (multiplicand
+ * >> 32) to another generates *really* bad code -- gcc is just not
+ * smart enough, at least in the version we are currently using.
+ */
+ if (shift < 32) {
+ __asm__("mov %%eax, %2 \n\t" // Save lo(multiplicand) in tmp2
+ "mov %%edx, %%eax \n\t" // Get hi(multiplicand)
+ "mull %4 \n\t" // p2 = hi(multiplicand) * multiplier
+ "xchg %%eax, %2 \n\t" // Save lo(p2) in tmp2, get lo(multiplicand)
+ "mov %%edx, %1 \n\t" // Save hi(p2) in tmp1
+ "mull %4 \n\t" // p1 = lo(multiplicand) * multiplier
+ "addl %2, %%edx \n\t" // hi(p1) += lo(p2)
+ "adcl $0, %1 \n\t" // hi(p2) += carry from previous step
+ "shrdl %%edx, %%eax \n\t" // result = hi(p2):hi(p1):lo(p1) >> shift
+ "shrdl %1, %%edx"
+ : "=A" (result),
+ "=&r" (tmp1), // use in shrdl requires it to be a register
+ "=&r" (tmp2) // could be "=&rm" but "m" is slower
+ : "0" (multiplicand),
+ "rm" (multiplier),
+ "c" (shift)
+ : "cc"
+ );
+ } else {
+ __asm__("mov %%edx, %2 \n\t" // Save hi(multiplicand) in tmp2
+ "mull %4 \n\t" // p1 = lo(multiplicand) * multiplier
+ "mov %%edx, %1 \n\t" // Save hi(p1) in tmp1
+ "mov %2, %%eax \n\t" // Discard lo(p1), get hi(multiplicand)
+ "mull %4 \n\t" // p2 = hi(multiplicand) * multiplier
+ "addl %1, %%eax \n\t" // lo(p2) += hi(p1)
+ "adcl $0, %%edx \n\t" // hi(p2) += carry from previous step
+ "shrdl %%edx, %%eax \n\t" // result = p2 >> (shift & 31)
+ "shrl %%cl, %%edx"
+ : "=A" (result),
+ "=&r" (tmp1), // could be "=&rm" but "m" is slower
+ "=&r" (tmp2) // could be "=&rm" but "m" is slower
+ : "0" (multiplicand),
+ "rm" (multiplier),
+ "c" (shift)
+ : "cc"
+ );
+ }
+ return result;
+}
+
+#elif _MSC_VER
+#pragma warning(disable: 4035)
+
+static INLINE uint64
+Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
+{
+ // ASSERT(shift >= 0 && shift < 64);
+
+ /* Written and tested by mann, checked by dbudko and hpreg */
+ __asm {
+ mov eax, DWORD PTR [multiplicand+4] // Get hi(multiplicand)
+ mul DWORD PTR [multiplier] // p2 = hi(multiplicand) * multiplier
+ mov ecx, eax // Save lo(p2)
+ mov ebx, edx // Save hi(p2)
+ mov eax, DWORD PTR [multiplicand] // Get lo(multiplicand)
+ mul DWORD PTR [multiplier+0] // p1 = lo(multiplicand) * multiplier
+ add edx, ecx // hi(p1) += lo(p2)
+ adc ebx, 0 // hi(p2) += carry from previous step
+ mov ecx, DWORD PTR [shift] // Get shift
+ cmp ecx, 32 // shift < 32?
+ jl SHORT l2 // Go if so
+ mov eax, edx // result = hi(p2):hi(p1) >> (shift & 31)
+ mov edx, ebx
+ shrd eax, edx, cl
+ shr edx, cl
+ jmp SHORT l3
+ l2:
+ shrd eax, edx, cl // result = hi(p2):hi(p1):lo(p1) >> shift
+ shrd edx, ebx, cl
+ l3:
+ }
+ // return with result in edx:eax
+}
+
+#pragma warning(default: 4035)
+#else
+#error No compiler defined for Mul64x3264
+#endif
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Muls64x32s64 --
+ *
+ * Signed integer by fixed point multiplication:
+ * Signed 64-bit integer multiplicand.
+ * Unsigned 32-bit fixed point multiplier, represented as
+ * multiplier >> shift, where shift < 64.
+ * Signed 64-bit integer product.
+ *
+ * Implementation:
+ * Multiply 64x32 bits to yield a full 96-bit product.
+ * Shift right by the location of the binary point.
+ * Return the low-order 64 bits of the result.
+ *
+ * Result:
+ * Product
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__)
+
+static INLINE int64
+Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
+{
+ int64 result;
+ uint32 tmp1, tmp2;
+ // ASSERT(shift >= 0 && shift < 64);
+
+ /* Written and tested by mann, checked by dbudko and hpreg */
+ /* XXX hpreg suggested some improvements that we haven't converged on yet */
+ __asm__("mov %%eax, %2\n\t" // Save lo(multiplicand)
+ "mov %%edx, %%eax\n\t" // Get hi(multiplicand)
+ "test %%eax, %%eax\n\t" // Check sign of multiplicand
+ "jl 0f\n\t" // Go if negative
+ "mull %4\n\t" // p2 = hi(multiplicand) * multiplier
+ "jmp 1f\n"
+ "0:\n\t"
+ "mull %4\n\t" // p2 = hi(multiplicand) * multiplier
+ "sub %4, %%edx\n" // hi(p2) += -1 * multiplier
+ "1:\n\t"
+ "xchg %%eax, %2\n\t" // Save lo(p2), get lo(multiplicand)
+ "mov %%edx, %1\n\t" // Save hi(p2)
+ "mull %4\n\t" // p1 = lo(multiplicand) * multiplier
+ "addl %2, %%edx\n\t" // hi(p1) += lo(p2)
+ "adcl $0, %1\n\t" // hi(p2) += carry from previous step
+ "cmpl $32, %%ecx\n\t" // shift < 32?
+ "jl 2f\n\t" // Go if so
+ "mov %%edx, %%eax\n\t" // result = hi(p2):hi(p1) >> (shift & 31)
+ "mov %1, %%edx\n\t"
+ "shrdl %%edx, %%eax\n\t"
+ "sarl %%cl, %%edx\n\t"
+ "jmp 3f\n"
+ "2:\n\t"
+ "shrdl %%edx, %%eax\n\t" // result = hi(p2):hi(p1):lo(p1) >> shift
+ "shrdl %1, %%edx\n"
+ "3:\n\t"
+ : "=A" (result), "=&r" (tmp1), "=&r" (tmp2)
+ : "0" (multiplicand), "rm" (multiplier), "c" (shift)
+ : "cc");
+ return result;
+}
+
+#elif _MSC_VER
+#pragma warning(disable: 4035)
+
+static INLINE int64
+Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
+{
+ //ASSERT(shift >= 0 && shift < 64);
+
+ /* Written and tested by mann, checked by dbudko and hpreg */
+ __asm {
+ mov eax, DWORD PTR [multiplicand+4] // Get hi(multiplicand)
+ test eax, eax // Check sign of multiplicand
+ jl SHORT l0 // Go if negative
+ mul DWORD PTR [multiplier] // p2 = hi(multiplicand) * multiplier
+ jmp SHORT l1
+ l0:
+ mul DWORD PTR [multiplier] // p2 = hi(multiplicand) * multiplier
+ sub edx, DWORD PTR [multiplier] // hi(p2) += -1 * multiplier
+ l1:
+ mov ecx, eax // Save lo(p2)
+ mov ebx, edx // Save hi(p2)
+ mov eax, DWORD PTR [multiplicand] // Get lo(multiplicand)
+ mul DWORD PTR [multiplier] // p1 = lo(multiplicand) * multiplier
+ add edx, ecx // hi(p1) += lo(p2)
+ adc ebx, 0 // hi(p2) += carry from previous step
+ mov ecx, DWORD PTR [shift] // Get shift
+ cmp ecx, 32 // shift < 32?
+ jl SHORT l2 // Go if so
+ mov eax, edx // result = hi(p2):hi(p1) >> (shift & 31)
+ mov edx, ebx
+ shrd eax, edx, cl
+ sar edx, cl
+ jmp SHORT l3
+ l2:
+ shrd eax, edx, cl // result = hi(p2):hi(p1):lo(p1) << shift
+ shrd edx, ebx, cl
+ l3:
+ }
+ // return with result in edx:eax
+}
+
+#pragma warning(default: 4035)
+#else
+#error No compiler defined for Muls64x32s64
+#endif
+
+
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998-2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vm_basic_asm_x86_64.h
+ *
+ * Basic x86_64 asm macros.
+ */
+
+#ifndef _VM_BASIC_ASM_X86_64_H_
+#define _VM_BASIC_ASM_X86_64_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMNIXMOD
+#include "includeCheck.h"
+
+#ifndef VM_X86_64
+#error "This file is x86-64 only!"
+#endif
+
+#ifdef _MSC_VER
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+uint64 _umul128(uint64 multiplier, uint64 multiplicand,
+ uint64 *highProduct);
+int64 _mul128(int64 multiplier, int64 multiplicand,
+ int64 *highProduct);
+uint64 __shiftright128(uint64 lowPart, uint64 highPart, uint8 shift);
+#ifdef __cplusplus
+}
+#endif
+
+#pragma intrinsic(_umul128, _mul128, __shiftright128)
+
+#endif // _MSC_VER
+
+/*
+ * FXSAVE/FXRSTOR
+ * save/restore SIMD/MMX fpu state
+ *
+ * The pointer passed in must be 16-byte aligned.
+ *
+ * Intel and AMD processors behave differently w.r.t. fxsave/fxrstor. Intel
+ * processors unconditionally save the exception pointer state (instruction
+ * ptr., data ptr., and error instruction opcode). FXSAVE_ES1 and FXRSTOR_ES1
+ * work correctly for Intel processors.
+ *
+ * AMD processors only save the exception pointer state if ES=1. This leads to a
+ * security hole whereby one process/VM can inspect the state of another process
+ * VM. The AMD recommended workaround involves clobbering the exception pointer
+ * state unconditionally, and this is implemented in FXRSTOR_AMD_ES0. Note that
+ * FXSAVE_ES1 will only save the exception pointer state for AMD processors if
+ * ES=1.
+ *
+ * The workaround (FXRSTOR_AMD_ES0) only costs 1 cycle more than just doing an
+ * fxrstor, on both AMD Opteron and Intel Core CPUs.
+ */
+#if defined(__GNUC__)
+
+static INLINE void
+FXSAVE_ES1(uint8 *save)
+{
+ __asm__ __volatile__ ("fxsaveq %0 \n" : "=m" (*save) : : "memory");
+}
+
+static INLINE void
+FXSAVE_COMPAT_ES1(uint8 *save)
+{
+ __asm__ __volatile__ ("fxsave %0 \n" : "=m" (*save) : : "memory");
+}
+
+static INLINE void
+FXRSTOR_ES1(const uint8 *load)
+{
+ __asm__ __volatile__ ("fxrstorq %0 \n" : : "m" (*load) : "memory");
+}
+
+static INLINE void
+FXRSTOR_COMPAT_ES1(const uint8 *load)
+{
+ __asm__ __volatile__ ("fxrstor %0 \n" : : "m" (*load) : "memory");
+}
+
+static INLINE void
+FXRSTOR_AMD_ES0(const uint8 *load)
+{
+ uint64 dummy = 0;
+
+ __asm__ __volatile__
+ ("fnstsw %%ax \n" // Grab x87 ES bit
+ "bt $7,%%ax \n" // Test ES bit
+ "jnc 1f \n" // Jump if ES=0
+ "fnclex \n" // ES=1. Clear it so fild doesn't trap
+ "1: \n"
+ "ffree %%st(7) \n" // Clear tag bit - avoid poss. stack overflow
+ "fildl %0 \n" // Dummy Load from "safe address" changes all
+ // x87 exception pointers.
+ "fxrstorq %1 \n"
+ :
+ : "m" (dummy), "m" (*load)
+ : "ax", "memory");
+}
+
+#endif /* __GNUC__ */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Mul64x3264 --
+ *
+ * Unsigned integer by fixed point multiplication:
+ * result = multiplicand * multiplier >> shift
+ *
+ * Unsigned 64-bit integer multiplicand.
+ * Unsigned 32-bit fixed point multiplier, represented as
+ * multiplier >> shift, where shift < 64.
+ * Unsigned 64-bit integer product.
+ *
+ * Implementation:
+ * Multiply 64x64 bits to yield a full 128-bit product.
+ * Shift result in RDX:RAX right by "shift".
+ * Return the low-order 64 bits of the above.
+ *
+ * Result:
+ * Product
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__)
+
+static INLINE uint64
+Mul64x3264(uint64 multiplicand,
+ uint32 multiplier,
+ uint32 shift)
+{
+ uint64 result, dummy;
+ const uint64 multiplier64 = multiplier;
+
+ __asm__("mulq %3 \n\t"
+ "shrdq %1, %0 \n\t"
+ : "=a" (result),
+ "=d" (dummy)
+ : "0" (multiplier64),
+ "rm" (multiplicand),
+ "c" (shift)
+ : "cc");
+ return result;
+}
+
+#elif defined(_MSC_VER)
+
+static INLINE uint64
+Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
+{
+ uint64 tmplo, tmphi;
+ tmplo = _umul128(multiplicand, multiplier, &tmphi);
+ return __shiftright128(tmplo, tmphi, (uint8) shift);
+}
+
+#endif
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Muls64x32s64 --
+ *
+ * Signed integer by fixed point multiplication:
+ * result = multiplicand * multiplier >> shift
+ *
+ * Signed 64-bit integer multiplicand.
+ * Unsigned 32-bit fixed point multiplier, represented as
+ * multiplier >> shift, where shift < 64.
+ * Signed 64-bit integer product.
+ *
+ * Implementation:
+ * Multiply 64x64 bits to yield a full 128-bit product.
+ * Shift result in RDX:RAX right by "shift".
+ * Return the low-order 64 bits of the above.
+ *
+ * Note: using an unsigned shift instruction is correct because
+ * shift < 64 and we return only the low 64 bits of the shifted
+ * result.
+ *
+ * Result:
+ * Product
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__)
+
+static inline int64
+Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
+{
+ int64 result, dummy;
+ const int64 multiplier64 = multiplier;
+
+ __asm__("imulq %3 \n\t"
+ "shrdq %1, %0 \n\t"
+ : "=a" (result),
+ "=d" (dummy)
+ : "0" (multiplier64),
+ "rm" (multiplicand),
+ "c" (shift)
+ : "cc");
+ return result;
+}
+
+#elif defined(_MSC_VER)
+
+static INLINE int64
+Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
+{
+ int64 tmplo, tmphi;
+ tmplo = _mul128(multiplicand, multiplier, &tmphi);
+ return __shiftright128(tmplo, tmphi, (uint8) shift);
+}
+
+#endif
+
+
+#if defined(__GNUC__)
+
+static INLINE void *
+uint64set(void *dst, uint64 val, uint64 count)
+{
+ int dummy0;
+ int dummy1;
+ __asm__ __volatile__("\t"
+ "cld" "\n\t"
+ "rep ; stosq" "\n"
+ : "=c" (dummy0), "=D" (dummy1)
+ : "0" (count), "1" (dst), "a" (val)
+ : "memory", "cc");
+ return dst;
+}
+
+#endif
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Div643232 --
+ *
+ * Unsigned integer division:
+ * The dividend is 64-bit wide
+ * The divisor is 32-bit wide
+ * The quotient is 32-bit wide
+ *
+ * Use this function if you are certain that the quotient will fit in 32 bits,
+ * If that is not the case, a #DE exception was generated in 32-bit version,
+ * but not in this 64-bit version. So please be careful.
+ *
+ * Results:
+ * Quotient and remainder
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+
+static INLINE void
+Div643232(uint64 dividend, // IN
+ uint32 divisor, // IN
+ uint32 *quotient, // OUT
+ uint32 *remainder) // OUT
+{
+ *quotient = (uint32)(dividend / divisor);
+ *remainder = (uint32)(dividend % divisor);
+}
+
+#endif
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Div643264 --
+ *
+ * Unsigned integer division:
+ * The dividend is 64-bit wide
+ * The divisor is 32-bit wide
+ * The quotient is 64-bit wide
+ *
+ * Results:
+ * Quotient and remainder
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__)
+
+static INLINE void
+Div643264(uint64 dividend, // IN
+ uint32 divisor, // IN
+ uint64 *quotient, // OUT
+ uint32 *remainder) // OUT
+{
+ *quotient = dividend / divisor;
+ *remainder = dividend % divisor;
+}
+
+#endif
+
+#endif // _VM_BASIC_ASM_X86_64_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2003 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vm_basic_defs.h --
+ *
+ * Standard macros for VMware source code.
+ */
+
+#ifndef _VM_BASIC_DEFS_H_
+#define _VM_BASIC_DEFS_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMNIXMOD
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMKDRIVERS
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMIROM
+#include "includeCheck.h"
+#include "vm_basic_types.h" // For INLINE.
+
+/* Checks for FreeBSD, filtering out VMKERNEL. */
+#define __IS_FREEBSD__ (!defined(VMKERNEL) && defined(__FreeBSD__))
+#define __IS_FREEBSD_VER__(ver) (__IS_FREEBSD__ && __FreeBSD_version >= (ver))
+
+#if defined _WIN32 && defined USERLEVEL
+ #include <stddef.h> /*
+ * We re-define offsetof macro from stddef, make
+ * sure that its already defined before we do it
+ */
+ #include <windows.h> // for Sleep() and LOWORD() etc.
+#endif
+
+
+/*
+ * Simple macros
+ */
+
+#if (defined __APPLE__ || defined __FreeBSD__) && \
+ (!defined KERNEL && !defined _KERNEL && !defined VMKERNEL && !defined __KERNEL__)
+# include <stddef.h>
+#else
+// XXX the _WIN32 one matches that of VC++, to prevent redefinition warning
+// XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings
+#ifndef offsetof
+#ifdef _WIN32
+#define offsetof(s,m) (size_t)&(((s *)0)->m)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+#endif // __APPLE__
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(a) (sizeof (a) / sizeof *(a))
+#endif
+
+#ifndef MIN
+#define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b))
+#endif
+
+/* The Solaris 9 cross-compiler complains about these not being used */
+#ifndef sun
+static INLINE int
+Min(int a, int b)
+{
+ return a < b ? a : b;
+}
+#endif
+
+#ifndef MAX
+#define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b))
+#endif
+
+#ifndef sun
+static INLINE int
+Max(int a, int b)
+{
+ return a > b ? a : b;
+}
+#endif
+
+#define ROUNDUP(x,y) (((x) + (y) - 1) / (y) * (y))
+#define ROUNDDOWN(x,y) ((x) / (y) * (y))
+#define ROUNDUPBITS(x, bits) (((uintptr_t) (x) + MASK(bits)) & ~MASK(bits))
+#define ROUNDDOWNBITS(x, bits) ((uintptr_t) (x) & ~MASK(bits))
+#define CEILING(x, y) (((x) + (y) - 1) / (y))
+#if defined __APPLE__
+#include <machine/param.h>
+#undef MASK
+#endif
+#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */
+#define DWORD_ALIGN(x) ((((x)+3) >> 2) << 2)
+#define QWORD_ALIGN(x) ((((x)+4) >> 3) << 3)
+
+#define IMPLIES(a,b) (!(a) || (b))
+
+/*
+ * Not everybody (e.g., the monitor) has NULL
+ */
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+#endif
+
+
+/*
+ * Token concatenation
+ *
+ * The C preprocessor doesn't prescan arguments when they are
+ * concatenated or stringified. So we need extra levels of
+ * indirection to convince the preprocessor to expand its
+ * arguments.
+ */
+
+#define CONC(x, y) x##y
+#define XCONC(x, y) CONC(x, y)
+#define XXCONC(x, y) XCONC(x, y)
+#define MAKESTR(x) #x
+#define XSTR(x) MAKESTR(x)
+
+
+/*
+ * Page operations
+ *
+ * It has been suggested that these definitions belong elsewhere
+ * (like x86types.h). However, I deem them common enough
+ * (since even regular user-level programs may want to do
+ * page-based memory manipulation) to be here.
+ * -- edward
+ */
+
+#ifndef PAGE_SHIFT // {
+#if defined VM_I386
+ #define PAGE_SHIFT 12
+#elif defined __APPLE__
+ #define PAGE_SHIFT 12
+#else
+ #error
+#endif
+#endif // }
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE (1<<PAGE_SHIFT)
+#endif
+
+#ifndef PAGE_MASK
+#define PAGE_MASK (PAGE_SIZE - 1)
+#endif
+
+#ifndef PAGE_OFFSET
+#define PAGE_OFFSET(_addr) ((uintptr_t)(_addr)&(PAGE_SIZE-1))
+#endif
+
+#ifndef VM_PAGE_BASE
+#define VM_PAGE_BASE(_addr) ((_addr)&~(PAGE_SIZE-1))
+#endif
+
+#ifndef VM_PAGES_SPANNED
+#define VM_PAGES_SPANNED(_addr, _size) \
+ ((((_addr) & (PAGE_SIZE - 1)) + (_size) + (PAGE_SIZE - 1)) >> PAGE_SHIFT)
+#endif
+
+#ifndef BYTES_2_PAGES
+#define BYTES_2_PAGES(_nbytes) ((_nbytes) >> PAGE_SHIFT)
+#endif
+
+#ifndef PAGES_2_BYTES
+#define PAGES_2_BYTES(_npages) (((uint64)(_npages)) << PAGE_SHIFT)
+#endif
+
+#ifndef MBYTES_2_PAGES
+#define MBYTES_2_PAGES(_nbytes) ((_nbytes) << (20 - PAGE_SHIFT))
+#endif
+
+#ifndef PAGES_2_MBYTES
+#define PAGES_2_MBYTES(_npages) ((_npages) >> (20 - PAGE_SHIFT))
+#endif
+
+#ifndef VM_PAE_LARGE_PAGE_SHIFT
+#define VM_PAE_LARGE_PAGE_SHIFT 21
+#endif
+
+#ifndef VM_PAE_LARGE_PAGE_SIZE
+#define VM_PAE_LARGE_PAGE_SIZE (1 << VM_PAE_LARGE_PAGE_SHIFT)
+#endif
+
+#ifndef VM_PAE_LARGE_PAGE_MASK
+#define VM_PAE_LARGE_PAGE_MASK (VM_PAE_LARGE_PAGE_SIZE - 1)
+#endif
+
+#ifndef VM_PAE_LARGE_2_SMALL_PAGES
+#define VM_PAE_LARGE_2_SMALL_PAGES (BYTES_2_PAGES(VM_PAE_LARGE_PAGE_SIZE))
+#endif
+
+/*
+ * Word operations
+ */
+
+#ifndef LOWORD
+#define LOWORD(_dw) ((_dw) & 0xffff)
+#endif
+#ifndef HIWORD
+#define HIWORD(_dw) (((_dw) >> 16) & 0xffff)
+#endif
+
+#ifndef LOBYTE
+#define LOBYTE(_w) ((_w) & 0xff)
+#endif
+#ifndef HIBYTE
+#define HIBYTE(_w) (((_w) >> 8) & 0xff)
+#endif
+
+#define HIDWORD(_qw) ((uint32)((_qw) >> 32))
+#define LODWORD(_qw) ((uint32)(_qw))
+#define QWORD(_hi, _lo) ((((uint64)(_hi)) << 32) | ((uint32)(_lo)))
+
+
+/*
+ * Deposit a field _src at _pos bits from the right,
+ * with a length of _len, into the integer _target.
+ */
+
+#define DEPOSIT_BITS(_src,_pos,_len,_target) { \
+ unsigned mask = ((1 << _len) - 1); \
+ unsigned shiftedmask = ((1 << _len) - 1) << _pos; \
+ _target = (_target & ~shiftedmask) | ((_src & mask) << _pos); \
+}
+
+
+/*
+ * Get return address.
+ */
+
+#ifdef _MSC_VER
+#ifdef __cplusplus
+extern "C"
+#endif
+void *_ReturnAddress(void);
+#pragma intrinsic(_ReturnAddress)
+#define GetReturnAddress() _ReturnAddress()
+#elif __GNUC__
+#define GetReturnAddress() __builtin_return_address(0)
+#endif
+
+
+#ifdef __GNUC__
+#ifndef sun
+
+/*
+ * Get the frame pointer. We use this assembly hack instead of
+ * __builtin_frame_address() due to a bug introduced in gcc 4.1.1
+ */
+static INLINE_SINGLE_CALLER uintptr_t
+GetFrameAddr(void)
+{
+ uintptr_t bp;
+#if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 0))
+ bp = (uintptr_t)__builtin_frame_address(0);
+#elif (__GNUC__ == 4 && __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 3)
+# if defined(VMM64) || defined(VM_X86_64)
+ __asm__ __volatile__("movq %%rbp, %0\n" : "=g" (bp));
+# else
+ __asm__ __volatile__("movl %%ebp, %0\n" : "=g" (bp));
+# endif
+#else
+ __asm__ __volatile__(
+#ifdef __linux__
+ ".print \"This newer version of GCC may or may not have the "
+ "__builtin_frame_address bug. Need to update this. "
+ "See bug 147638.\"\n"
+ ".abort"
+#else /* MacOS */
+ ".abort \"This newer version of GCC may or may not have the "
+ "__builtin_frame_address bug. Need to update this. "
+ "See bug 147638.\"\n"
+#endif
+ : "=g" (bp)
+ );
+#endif
+ return bp;
+}
+
+
+/*
+ * Returns the frame pointer of the calling function.
+ * Equivalent to __builtin_frame_address(1).
+ */
+static INLINE_SINGLE_CALLER uintptr_t
+GetCallerFrameAddr(void)
+{
+ return *(uintptr_t*)GetFrameAddr();
+}
+
+#endif // sun
+#endif // __GNUC__
+
+/*
+ * Data prefetch was added in gcc 3.1.1
+ * http://www.gnu.org/software/gcc/gcc-3.1/changes.html
+ */
+#ifdef __GNUC__
+# if ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 1) || \
+ (__GNUC__ == 3 && __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ >= 1))
+# define PREFETCH_R(var) __builtin_prefetch((var), 0 /* read */, \
+ 3 /* high temporal locality */)
+# define PREFETCH_W(var) __builtin_prefetch((var), 1 /* write */, \
+ 3 /* high temporal locality */)
+# else
+# define PREFETCH_R(var) ((void)(var))
+# define PREFETCH_W(var) ((void)(var))
+# endif
+#endif /* __GNUC__ */
+
+
+#ifdef USERLEVEL // {
+
+/*
+ * Note this might be a problem on NT b/c while sched_yield guarantees it
+ * moves you to the end of your priority list, Sleep(0) offers no such
+ * guarantee. Bummer. --Jeremy.
+ */
+
+#if defined(N_PLAT_NLM)
+/* We do not have YIELD() as we do not need it yet... */
+#elif defined(_WIN32)
+# define YIELD() Sleep(0)
+#else
+# include <sched.h> // For sched_yield. Don't ask. --Jeremy.
+# define YIELD() sched_yield()
+#endif
+
+
+/*
+ * Standardize some Posix names on Windows.
+ */
+
+#ifdef _WIN32 // {
+
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strtok_r strtok_s
+
+typedef int uid_t;
+typedef int gid_t;
+
+static INLINE void
+sleep(unsigned int sec)
+{
+ Sleep(sec * 1000);
+}
+
+static INLINE void
+usleep(unsigned long usec)
+{
+ Sleep(CEILING(usec, 1000));
+}
+
+typedef int pid_t;
+#define F_OK 0
+#define X_OK 1
+#define W_OK 2
+#define R_OK 4
+
+#endif // }
+
+/*
+ * Macro for username comparison.
+ */
+
+#ifdef _WIN32 // {
+#define USERCMP(x,y) Str_Strcasecmp(x,y)
+#else
+#define USERCMP(x,y) strcmp(x,y)
+#endif // }
+
+
+#endif // }
+
+#ifndef va_copy
+
+#ifdef _WIN32
+
+/*
+ * Windows needs va_copy. This works for both 32 and 64-bit Windows
+ * based on inspection of how varags.h from the Visual C CRTL is
+ * implemented. (Future versions of the RTL may break this).
+ */
+
+#define va_copy(dest, src) ((dest) = (src))
+
+#elif defined(__APPLE__) && defined(KERNEL)
+
+/*
+ * MacOS kernel-mode needs va_copy. Based on inspection of stdarg.h
+ * from the MacOSX10.4u.sdk kernel framework, this should work.
+ * (Future versions of the SDK may break this).
+ */
+
+#define va_copy(dest, src) ((dest) = (src))
+
+#elif defined(__GNUC__) && (__GNUC__ < 3)
+
+/*
+ * Old versions of gcc recognize __va_copy, but not va_copy.
+ */
+
+#define va_copy(dest, src) __va_copy(dest, src)
+
+#endif // _WIN32
+
+#endif // va_copy
+
+/*
+ * This one is outside USERLEVEL because it's used by
+ * files compiled into the Windows hgfs driver or the display
+ * driver.
+ */
+
+#ifdef _WIN32
+#define PATH_MAX 256
+#ifndef strcasecmp
+#define strcasecmp(_s1,_s2) _stricmp((_s1),(_s2))
+#endif
+#ifndef strncasecmp
+#define strncasecmp(_s1,_s2,_n) _strnicmp((_s1),(_s2),(_n))
+#endif
+#endif
+
+/*
+ * Convenience macro for COMMUNITY_SOURCE
+ */
+#undef EXCLUDE_COMMUNITY_SOURCE
+#ifdef COMMUNITY_SOURCE
+ #define EXCLUDE_COMMUNITY_SOURCE(x)
+#else
+ #define EXCLUDE_COMMUNITY_SOURCE(x) x
+#endif
+
+#undef COMMUNITY_SOURCE_INTEL_SECRET
+#if !defined(COMMUNITY_SOURCE) || defined(INTEL_SOURCE)
+/*
+ * It's ok to include INTEL_SECRET source code for non-commsrc,
+ * or for drops directed at Intel.
+ */
+ #define COMMUNITY_SOURCE_INTEL_SECRET
+#endif
+
+/*
+ * Convenience macros and definitions. Can often be used instead of #ifdef.
+ */
+
+#undef DEBUG_ONLY
+#undef SL_DEBUG_ONLY
+#undef VMX86_SL_DEBUG
+#ifdef VMX86_DEBUG
+#define vmx86_debug 1
+#define DEBUG_ONLY(x) x
+/*
+ * Be very, very, very careful with SL_DEBUG. Pls ask ganesh or min before
+ * using it.
+ */
+#define VMX86_SL_DEBUG
+#define vmx86_sl_debug 1
+#define SL_DEBUG_ONLY(x) x
+#else
+#define vmx86_debug 0
+#define DEBUG_ONLY(x)
+#define vmx86_sl_debug 0
+#define SL_DEBUG_ONLY(x)
+#endif
+
+#ifdef VMX86_STATS
+#define vmx86_stats 1
+#define STATS_ONLY(x) x
+#else
+#define vmx86_stats 0
+#define STATS_ONLY(x)
+#endif
+
+#ifdef VMX86_DEVEL
+#define vmx86_devel 1
+#define DEVEL_ONLY(x) x
+#else
+#define vmx86_devel 0
+#define DEVEL_ONLY(x)
+#endif
+
+#ifdef VMX86_LOG
+#define vmx86_log 1
+#define LOG_ONLY(x) x
+#else
+#define vmx86_log 0
+#define LOG_ONLY(x)
+#endif
+
+#ifdef VMX86_VMM_SERIAL_LOGGING
+#define vmx86_vmm_serial_log 1
+#define VMM_SERIAL_LOG_ONLY(x) x
+#else
+#define vmx86_vmm_serial_log 0
+#define VMM_SERIAL_LOG_ONLY(x)
+#endif
+
+#ifdef VMX86_SERVER
+#define vmx86_server 1
+#define SERVER_ONLY(x) x
+#define HOSTED_ONLY(x)
+#else
+#define vmx86_server 0
+#define SERVER_ONLY(x)
+#define HOSTED_ONLY(x) x
+#endif
+
+#ifdef VMX86_WGS
+#define vmx86_wgs 1
+#define WGS_ONLY(x) x
+#else
+#define vmx86_wgs 0
+#define WGS_ONLY(x)
+#endif
+
+#ifdef VMKERNEL
+#define vmkernel 1
+#define VMKERNEL_ONLY(x) x
+#else
+#define vmkernel 0
+#define VMKERNEL_ONLY(x)
+#endif
+
+#ifdef _WIN32
+#define WIN32_ONLY(x) x
+#define POSIX_ONLY(x)
+#else
+#define WIN32_ONLY(x)
+#define POSIX_ONLY(x) x
+#endif
+
+#ifdef VMM
+#define VMM_ONLY(x) x
+#define USER_ONLY(x)
+#else
+#define VMM_ONLY(x)
+#define USER_ONLY(x) x
+#endif
+
+/* VMVISOR ifdef only allowed in the vmkernel */
+#ifdef VMKERNEL
+#ifdef VMVISOR
+#define vmvisor 1
+#define VMVISOR_ONLY(x) x
+#else
+#define vmvisor 0
+#define VMVISOR_ONLY(x)
+#endif
+#endif
+
+#ifdef _WIN32
+#define VMW_INVALID_HANDLE INVALID_HANDLE_VALUE
+#else
+#define VMW_INVALID_HANDLE (-1)
+#endif
+
+#ifdef _WIN32
+#define fsync(fd) _commit(fd)
+#define fileno(f) _fileno(f)
+#else
+#endif
+
+/*
+ * Debug output macros for Windows drivers (the Eng variant is for
+ * display/printer drivers only.
+ */
+#ifdef _WIN32
+#ifndef USES_OLD_WINDDK
+#if defined(VMX86_DEBUG) || defined(ASSERT_ALWAYS_AVAILABLE)
+#define WinDrvPrint(arg, ...) DbgPrint(arg, __VA_ARGS__)
+#define WinDrvEngPrint(arg, ...) EngDbgPrint(arg, __VA_ARGS__)
+#else
+#define WinDrvPrint(arg, ...)
+#define WinDrvEngPrint(arg, ...)
+#endif
+#endif
+#endif // _WIN32
+
+#endif // ifndef _VM_BASIC_DEFS_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998-2008 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ *
+ * vm_basic_types.h --
+ *
+ * basic data types.
+ */
+
+
+#ifndef _VM_BASIC_TYPES_H_
+#define _VM_BASIC_TYPES_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMNIXMOD
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMKDRIVERS
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMIROM
+#include "includeCheck.h"
+
+/* STRICT ANSI means the Xserver build and X defines Bool differently. */
+#if !defined(__STRICT_ANSI__) || defined(__FreeBSD__)
+typedef char Bool;
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define IsBool(x) (((x) & ~1) == 0)
+#define IsBool2(x, y) ((((x) | (y)) & ~1) == 0)
+
+/*
+ * Macros __i386__ and __ia64 are intrinsically defined by GCC
+ */
+#ifdef __i386__
+#define VM_I386
+#endif
+
+#ifdef _WIN64
+#define __x86_64__
+#endif
+
+#ifdef __x86_64__
+#define VM_X86_64
+#define VM_I386
+#define vm_x86_64 (1)
+#else
+#define vm_x86_64 (0)
+#endif
+
+
+
+#ifdef _WIN32
+/* safe assumption for a while */
+#define VM_I386
+#endif
+
+#ifdef _MSC_VER
+
+#pragma warning (3 :4505) // unreferenced local function
+#pragma warning (disable :4018) // signed/unsigned mismatch
+#pragma warning (disable :4761) // integral size mismatch in argument; conversion supplied
+#pragma warning (disable :4305) // truncation from 'const int' to 'short'
+#pragma warning (disable :4244) // conversion from 'unsigned short' to 'unsigned char'
+#pragma warning (disable :4267) // truncation of 'size_t'
+#pragma warning (disable :4146) // unary minus operator applied to unsigned type, result still unsigned
+#pragma warning (disable :4142) // benign redefinition of type
+
+#endif
+
+#if defined(__APPLE__) || defined(HAVE_STDINT_H)
+
+/*
+ * TODO: This is a C99 standard header. We should be able to test for
+ * #if __STDC_VERSION__ >= 199901L, but that breaks the Netware build
+ * (which doesn't have stdint.h).
+ */
+
+#include <stdint.h>
+
+typedef uint64_t uint64;
+typedef int64_t int64;
+typedef uint32_t uint32;
+typedef int32_t int32;
+typedef uint16_t uint16;
+typedef int16_t int16;
+typedef uint8_t uint8;
+
+/*
+ * XXX: int8_t is defined to be 'signed char' on Mac hosts.
+ *
+ * Unfortunately, GCC 4.0.1 warns when doing pointer assignment or
+ * comparison between signed char * and char * (even if char is
+ * signed).
+ *
+ * If we want to use int8_t to define int8, we need to go through and
+ * replace uses of char * with signed char * to prevent warnings.
+ */
+typedef char int8;
+
+#else /* !HAVE_STDINT_H */
+
+#ifdef _MSC_VER
+
+typedef unsigned __int64 uint64;
+typedef signed __int64 int64;
+
+#elif __GNUC__
+/* The Xserver source compiles with -ansi -pendantic */
+#ifndef __STRICT_ANSI__
+#if defined(VM_X86_64)
+typedef unsigned long uint64;
+typedef long int64;
+#else
+typedef unsigned long long uint64;
+typedef long long int64;
+#endif
+#elif __FreeBSD__
+typedef unsigned long long uint64;
+typedef long long int64;
+#endif
+#else
+#error - Need compiler define for int64/uint64
+#endif /* _MSC_VER */
+
+typedef unsigned int uint32;
+typedef unsigned short uint16;
+typedef unsigned char uint8;
+
+typedef int int32;
+typedef short int16;
+typedef char int8;
+
+#endif /* HAVE_STDINT_H */
+
+/*
+ * FreeBSD (for the tools build) unconditionally defines these in
+ * sys/inttypes.h so don't redefine them if this file has already
+ * been included. [greg]
+ *
+ * This applies to Solaris as well.
+ */
+
+/*
+ * Before trying to do the includes based on OS defines, see if we can use
+ * feature-based defines to get as much functionality as possible
+ */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_INTTYPES_H
+#include <sys/inttypes.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/param.h> /* For __FreeBSD_version */
+#endif
+
+#if !defined(USING_AUTOCONF)
+# if defined(__FreeBSD__) || defined(sun)
+# ifdef KLD_MODULE
+# include <sys/types.h>
+# else
+# if !defined(VMKERNEL) && (__FreeBSD_version >= 500043)
+# include <inttypes.h>
+# include <sys/types.h>
+# else
+# include <sys/inttypes.h>
+# endif
+# endif
+# elif defined __APPLE__
+# if KERNEL
+# include <sys/unistd.h>
+# include <sys/types.h> /* mostly for size_t */
+# include <stdint.h>
+# else
+# include <unistd.h>
+# include <inttypes.h>
+# include <stdlib.h>
+# include <stdint.h>
+# endif
+# else
+# if !defined(__intptr_t_defined) && !defined(intptr_t)
+# define __intptr_t_defined
+# define intptr_t intptr_t
+# ifdef VM_I386
+# ifdef VM_X86_64
+typedef int64 intptr_t;
+# else
+typedef int32 intptr_t;
+# endif
+# endif
+# endif
+
+# ifndef _STDINT_H
+# ifdef VM_I386
+# ifdef VM_X86_64
+typedef uint64 uintptr_t;
+# else
+typedef uint32 uintptr_t;
+# endif
+# endif
+# endif
+# endif
+#endif
+
+
+/*
+ * Time
+ * XXX These should be cleaned up. -- edward
+ */
+
+typedef int64 VmTimeType; /* Time in microseconds */
+typedef int64 VmTimeRealClock; /* Real clock kept in microseconds */
+typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */
+
+/*
+ * Printf format specifiers for size_t and 64-bit number.
+ * Use them like this:
+ * printf("%"FMT64"d\n", big);
+ *
+ * FMTH is for handles/fds.
+ */
+
+#ifdef _MSC_VER
+ #define FMT64 "I64"
+ #ifdef VM_X86_64
+ #define FMTSZ "I64"
+ #define FMTPD "I64"
+ #define FMTH "I64"
+ #else
+ #define FMTSZ "I"
+ #define FMTPD "I"
+ #define FMTH "I"
+ #endif
+#elif defined __APPLE__
+ /* Mac OS hosts use the same formatters for 32- and 64-bit. */
+ #define FMT64 "ll"
+ #define FMTSZ "z"
+ #define FMTPD "l"
+ #define FMTH ""
+#elif __GNUC__
+ #define FMTH ""
+ #if defined(N_PLAT_NLM) || defined(sun) || \
+ (defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) < 5))
+ /*
+ * Why (__FreeBSD__ + 0)? See bug 141008.
+ * Yes, we really need to test both (__FreeBSD__ + 0) and
+ * ((__FreeBSD__ + 0) < 5). No, we can't remove "+ 0" from
+ * ((__FreeBSD__ + 0) < 5).
+ */
+ #ifdef VM_X86_64
+ #define FMTSZ "l"
+ #define FMTPD "l"
+ #else
+ #define FMTSZ ""
+ #define FMTPD ""
+ #endif
+ #elif defined(__linux__) \
+ || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) \
+ || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) \
+ || (defined(_POSIX2_VERSION) && _POSIX2_VERSION >= 200112L)
+ /* BSD, Linux */
+ #define FMTSZ "z"
+
+ #if defined(VM_X86_64)
+ #define FMTPD "l"
+ #else
+ #define FMTPD ""
+ #endif
+ #else
+ /* Systems with a pre-C99 libc */
+ #define FMTSZ "Z"
+ #ifdef VM_X86_64
+ #define FMTPD "l"
+ #else
+ #define FMTPD ""
+ #endif
+ #endif
+ #ifdef VM_X86_64
+ #define FMT64 "l"
+ #elif defined(sun) || defined(__FreeBSD__)
+ #define FMT64 "ll"
+ #else
+ #define FMT64 "L"
+ #endif
+#else
+ #error - Need compiler define for FMT64 and FMTSZ
+#endif
+
+/*
+ * Suffix for 64-bit constants. Use it like this:
+ * CONST64(0x7fffffffffffffff) for signed or
+ * CONST64U(0x7fffffffffffffff) for unsigned.
+ *
+ * 2004.08.30(thutt):
+ * The vmcore/asm64/gen* programs are compiled as 32-bit
+ * applications, but must handle 64 bit constants. If the
+ * 64-bit-constant defining macros are already defined, the
+ * definition will not be overwritten.
+ */
+
+#if !defined(CONST64) || !defined(CONST64U)
+#ifdef _MSC_VER
+#define CONST64(c) c##I64
+#define CONST64U(c) c##uI64
+#elif __GNUC__
+#ifdef VM_X86_64
+#define CONST64(c) c##L
+#define CONST64U(c) c##uL
+#else
+#define CONST64(c) c##LL
+#define CONST64U(c) c##uLL
+#endif
+#else
+#error - Need compiler define for CONST64
+#endif
+#endif
+
+/*
+ * Use CONST3264/CONST3264U if you want a constant to be
+ * treated as a 32-bit number on 32-bit compiles and
+ * a 64-bit number on 64-bit compiles. Useful in the case
+ * of shifts, like (CONST3264U(1) << x), where x could be
+ * more than 31 on a 64-bit compile.
+ */
+
+#ifdef VM_X86_64
+ #define CONST3264(a) CONST64(a)
+ #define CONST3264U(a) CONST64U(a)
+#else
+ #define CONST3264(a) (a)
+ #define CONST3264U(a) (a)
+#endif
+
+#define MIN_INT32 ((int32)0x80000000)
+#define MAX_INT32 ((int32)0x7fffffff)
+
+#define MIN_UINT32 ((uint32)0)
+#define MAX_UINT32 ((uint32)0xffffffff)
+
+#define MIN_INT64 (CONST64(0x8000000000000000))
+#define MAX_INT64 (CONST64(0x7fffffffffffffff))
+
+#define MIN_UINT64 (CONST64U(0))
+#define MAX_UINT64 (CONST64U(0xffffffffffffffff))
+
+typedef uint8 *TCA; /* Pointer into TC (usually). */
+
+/*
+ * Type big enough to hold an integer between 0..100
+ */
+typedef uint8 Percent;
+#define AsPercent(v) ((Percent)(v))
+#define CHOOSE_PERCENT AsPercent(101)
+
+
+typedef uintptr_t VA;
+typedef uintptr_t VPN;
+
+typedef uint64 PA;
+typedef uint32 PPN;
+
+typedef uint64 PhysMemOff;
+typedef uint64 PhysMemSize;
+
+/* The Xserver source compiles with -ansi -pendantic */
+#ifndef __STRICT_ANSI__
+typedef uint64 BA;
+#endif
+typedef uint32 BPN;
+typedef uint32 PageNum;
+typedef unsigned MemHandle;
+typedef int32 World_ID;
+
+#define INVALID_WORLD_ID ((World_ID)-1)
+
+typedef World_ID User_CartelID;
+#define INVALID_CARTEL_ID INVALID_WORLD_ID
+
+typedef User_CartelID User_SessionID;
+#define INVALID_SESSION_ID INVALID_CARTEL_ID
+
+typedef User_CartelID User_CartelGroupID;
+#define INVALID_CARTELGROUP_ID INVALID_CARTEL_ID
+
+typedef uint32 Worldlet_ID;
+#define INVALID_WORLDLET_ID ((Worldlet_ID)-1)
+
+/* world page number */
+typedef uint32 WPN;
+
+/* The Xserver source compiles with -ansi -pendantic */
+#ifndef __STRICT_ANSI__
+typedef uint64 MA;
+typedef uint32 MPN;
+#endif
+
+/*
+ * This type should be used for variables that contain sector
+ * position/quantity.
+ */
+typedef uint64 SectorType;
+
+/*
+ * Linear address
+ */
+
+typedef uintptr_t LA;
+typedef uintptr_t LPN;
+#define LA_2_LPN(_la) ((_la) >> PAGE_SHIFT)
+#define LPN_2_LA(_lpn) ((_lpn) << PAGE_SHIFT)
+
+#define LAST_LPN ((((LA) 1) << (8 * sizeof(LA) - PAGE_SHIFT)) - 1)
+#define LAST_LPN32 ((((LA32)1) << (8 * sizeof(LA32) - PAGE_SHIFT)) - 1)
+#define LAST_LPN64 ((((LA64)1) << (8 * sizeof(LA64) - PAGE_SHIFT)) - 1)
+
+/* Valid bits in a LPN. */
+#define LPN_MASK LAST_LPN
+#define LPN_MASK32 LAST_LPN32
+#define LPN_MASK64 LAST_LPN64
+
+/*
+ * On 64 bit platform, address and page number types default
+ * to 64 bit. When we need to represent a 32 bit address, we use
+ * types defined below.
+ *
+ * On 32 bit platform, the following types are the same as the
+ * default types.
+ */
+typedef uint32 VA32;
+typedef uint32 VPN32;
+typedef uint32 LA32;
+typedef uint32 LPN32;
+typedef uint32 PA32;
+typedef uint32 PPN32;
+typedef uint32 MA32;
+typedef uint32 MPN32;
+
+/*
+ * On 64 bit platform, the following types are the same as the
+ * default types.
+ */
+typedef uint64 VA64;
+typedef uint64 VPN64;
+typedef uint64 LA64;
+typedef uint64 LPN64;
+typedef uint64 PA64;
+typedef uint64 PPN64;
+typedef uint64 MA64;
+typedef uint64 MPN64;
+
+/*
+ * VA typedefs for user world apps.
+ */
+typedef VA32 UserVA32;
+typedef VA64 UserVA64;
+typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */
+typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */
+#ifdef VMKERNEL
+typedef UserVA32 UserVA;
+#else
+typedef void * UserVA;
+#endif
+
+
+/*
+ * Maximal possible PPN value (errors too) that PhysMem can handle.
+ * Must be at least as large as MAX_PPN which is the maximum PPN
+ * for any region other than buserror.
+ */
+#define PHYSMEM_MAX_PPN ((PPN)0xffffffff)
+#define MAX_PPN ((PPN)0x1fffffff) /* Maximal observable PPN value. */
+#define INVALID_PPN ((PPN)0xffffffff)
+
+#define INVALID_BPN ((BPN) 0x1fffffff)
+
+#define INVALID_MPN ((MPN)-1)
+#define MEMREF_MPN ((MPN)-2)
+#define RESERVED_MPN ((MPN) 0)
+/* Support 43 bits of address space. */
+#define MAX_MPN ((MPN)0x7fffffff)
+
+#define INVALID_LPN ((LPN)-1)
+#define INVALID_VPN ((VPN)-1)
+#define INVALID_LPN64 ((LPN64)-1)
+#define INVALID_PAGENUM ((PageNum)-1)
+#define INVALID_WPN ((WPN) -1)
+
+
+/*
+ * Format modifier for printing VA, LA, and VPN.
+ * Use them like this: Log("%#"FMTLA"x\n", laddr)
+ */
+
+#if defined(VMM64) || defined(FROBOS64) || vm_x86_64 || defined __APPLE__
+# define FMTLA "l"
+# define FMTVA "l"
+# define FMTVPN "l"
+#else
+# define FMTLA ""
+# define FMTVA ""
+# define FMTVPN ""
+#endif
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+#define CONST const
+
+
+#ifndef INLINE
+# ifdef _MSC_VER
+# define INLINE __inline
+# else
+# define INLINE inline
+# endif
+#endif
+
+
+/*
+ * Annotation for data that may be exported into a DLL and used by other
+ * apps that load that DLL and import the data.
+ */
+#if defined(_WIN32) && defined(VMX86_IMPORT_DLLDATA)
+# define VMX86_EXTERN_DATA extern __declspec(dllimport)
+#else // !_WIN32
+# define VMX86_EXTERN_DATA extern
+#endif
+
+#if defined(_WIN32) && !defined(VMX86_NO_THREADS)
+#define THREADSPECIFIC __declspec(thread)
+#else
+#define THREADSPECIFIC
+#endif
+
+/*
+ * Due to the wonderful "registry redirection" feature introduced in
+ * 64-bit Windows, if you access any key under HKLM\Software in 64-bit
+ * code, you need to open/create/delete that key with
+ * VMKEY_WOW64_32KEY if you want a consistent view with 32-bit code.
+ */
+
+#ifdef _WIN32
+#ifdef _WIN64
+#define VMW_KEY_WOW64_32KEY KEY_WOW64_32KEY
+#else
+#define VMW_KEY_WOW64_32KEY 0x0
+#endif
+#endif
+
+
+/*
+ * Consider the following reasons functions are inlined:
+ *
+ * 1) inlined for performance reasons
+ * 2) inlined because it's a single-use function
+ *
+ * Functions which meet only condition 2 should be marked with this
+ * inline macro; It is not critical to be inlined (but there is a
+ * code-space & runtime savings by doing so), so when other callers
+ * are added the inline-ness should be removed.
+ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+/*
+ * Starting at version 3.3, gcc does not always inline functions marked
+ * 'inline' (it depends on their size). To force gcc to do so, one must use the
+ * extra __always_inline__ attribute.
+ */
+# define INLINE_SINGLE_CALLER INLINE __attribute__((__always_inline__))
+# if defined(VMM) \
+ && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1))
+# warning Verify INLINE_SINGLE_CALLER '__always_inline__' attribute (did \
+ monitor size change?)
+# endif
+#else
+# define INLINE_SINGLE_CALLER INLINE
+#endif
+
+/*
+ * Used when a hard guaranteed of no inlining is needed. Very few
+ * instances need this since the absence of INLINE is a good hint
+ * that gcc will not do inlining.
+ */
+
+#if defined(__GNUC__) && defined(VMM)
+#define ABSOLUTELY_NOINLINE __attribute__((__noinline__))
+#endif
+
+/*
+ * Attributes placed on function declarations to tell the compiler
+ * that the function never returns.
+ */
+
+#ifdef _MSC_VER
+#define NORETURN __declspec(noreturn)
+#elif __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 9)
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#endif
+
+/*
+ * GCC 3.2 inline asm needs the + constraint for input/ouput memory operands.
+ * Older GCCs don't know about it --hpreg
+ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+# define VM_ASM_PLUS 1
+#else
+# define VM_ASM_PLUS 0
+#endif
+
+/*
+ * Branch prediction hints:
+ * LIKELY(exp) - Expression exp is likely TRUE.
+ * UNLIKELY(exp) - Expression exp is likely FALSE.
+ * Usage example:
+ * if (LIKELY(excCode == EXC_NONE)) {
+ * or
+ * if (UNLIKELY(REAL_MODE(vc))) {
+ *
+ * We know how to predict branches on gcc3 and later (hopefully),
+ * all others we don't so we do nothing.
+ */
+
+#if (__GNUC__ >= 3)
+/*
+ * gcc3 uses __builtin_expect() to inform the compiler of an expected value.
+ * We use this to inform the static branch predictor. The '!!' in LIKELY
+ * will convert any !=0 to a 1.
+ */
+#define LIKELY(_exp) __builtin_expect(!!(_exp), 1)
+#define UNLIKELY(_exp) __builtin_expect((_exp), 0)
+#else
+#define LIKELY(_exp) (_exp)
+#define UNLIKELY(_exp) (_exp)
+#endif
+
+/*
+ * GCC's argument checking for printf-like functions
+ * This is conditional until we have replaced all `"%x", void *'
+ * with `"0x%08x", (uint32) void *'. Note that %p prints different things
+ * on different platforms. Argument checking is enabled for the
+ * vmkernel, which has already been cleansed.
+ *
+ * fmtPos is the position of the format string argument, beginning at 1
+ * varPos is the position of the variable argument, beginning at 1
+ */
+
+#if defined(__GNUC__)
+# define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos)))
+#else
+# define PRINTF_DECL(fmtPos, varPos)
+#endif
+
+#if defined(__GNUC__)
+# define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos)))
+#else
+# define SCANF_DECL(fmtPos, varPos)
+#endif
+
+/*
+ * UNUSED_PARAM should surround the parameter name and type declaration,
+ * e.g. "int MyFunction(int var1, UNUSED_PARAM(int var2))"
+ *
+ */
+
+#ifndef UNUSED_PARAM
+# if defined(__GNUC__)
+# define UNUSED_PARAM(_parm) _parm __attribute__((__unused__))
+# else
+# define UNUSED_PARAM(_parm) _parm
+# endif
+#endif
+
+/*
+ * REGPARM defaults to REGPARM3, i.e., a requent that gcc
+ * puts the first three arguments in registers. (It is fine
+ * if the function has fewer than three args.) Gcc only.
+ * Syntactically, put REGPARM where you'd put INLINE or NORETURN.
+ */
+
+#if defined(__GNUC__)
+# define REGPARM0 __attribute__((regparm(0)))
+# define REGPARM1 __attribute__((regparm(1)))
+# define REGPARM2 __attribute__((regparm(2)))
+# define REGPARM3 __attribute__((regparm(3)))
+# define REGPARM REGPARM3
+#else
+# define REGPARM0
+# define REGPARM1
+# define REGPARM2
+# define REGPARM3
+# define REGPARM
+#endif
+
+/*
+ * ALIGNED specifies minimum alignment in "n" bytes.
+ */
+
+#ifdef __GNUC__
+#define ALIGNED(n) __attribute__((__aligned__(n)))
+#else
+#define ALIGNED(n)
+#endif
+
+/*
+ ***********************************************************************
+ * STRUCT_OFFSET_CHECK -- */ /**
+ *
+ * \brief Check if the actual offsef of a member in a structure
+ * is what is expected
+ *
+ *
+ * \param[in] STRUCT Structure the member is a part of.
+ * \param[in] MEMBER Member to check the offset of.
+ * \param[in] OFFSET Expected offset of MEMBER in STRUCTURE.
+ * \param[in] DEBUG_EXTRA Additional bytes to be added to OFFSET to
+ * compensate for extra info in debug builds.
+ *
+ ***********************************************************************
+ */
+#ifdef VMX86_DEBUG
+#define STRUCT_OFFSET_CHECK(STRUCT, MEMBER, OFFSET, DEBUG_EXTRA) \
+ ASSERT_ON_COMPILE(vmk_offsetof(STRUCT, MEMBER) == (OFFSET + DEBUG_EXTRA))
+#else
+#define STRUCT_OFFSET_CHECK(STRUCT, MEMBER, OFFSET, DEBUG_EXTRA) \
+ ASSERT_ON_COMPILE(vmk_offsetof(STRUCT, MEMBER) == OFFSET)
+#endif
+
+/*
+ ***********************************************************************
+ * STRUCT_SIZE_CHECK -- */ /**
+ *
+ * \brief Check if the actual size of a structure is what is expected
+ *
+ *
+ * \param[in] STRUCT Structure whose size is to be checked.
+ * \param[in] SIZE Expected size of STRUCT.
+ * \param[in] DEBUG_EXTRA Additional bytes to be added to SIZE to
+ * compensate for extra info in debug builds.
+ *
+ ***********************************************************************
+ */
+#ifdef VMX86_DEBUG
+#define STRUCT_SIZE_CHECK(STRUCT, SIZE, DEBUG_EXTRA) \
+ ASSERT_ON_COMPILE(sizeof(STRUCT) == (SIZE + DEBUG_EXTRA))
+#else
+#define STRUCT_SIZE_CHECK(STRUCT, SIZE, DEBUG_EXTRA) \
+ ASSERT_ON_COMPILE(sizeof(STRUCT) == SIZE)
+#endif
+
+/*
+ * __func__ is a stringified function name that is part of the C99 standard. The block
+ * below defines __func__ on older systems where the compiler does not support that
+ * macro.
+ */
+#if defined(__GNUC__) \
+ && ((__GNUC__ == 2 && __GNUC_MINOR < 96) \
+ || (__GNUC__ < 2))
+# define __func__ __FUNCTION__
+#endif
+
+/*
+ * Once upon a time, this was used to silence compiler warnings that
+ * get generated when the compiler thinks that a function returns
+ * when it is marked noreturn. Don't do it. Use NOT_REACHED().
+ */
+
+#define INFINITE_LOOP() do { } while (1)
+
+/*
+ * On FreeBSD (for the tools build), size_t is typedef'd if _BSD_SIZE_T_
+ * is defined. Use the same logic here so we don't define it twice. [greg]
+ */
+#ifdef __FreeBSD__
+# ifdef _BSD_SIZE_T_
+# undef _BSD_SIZE_T_
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef uint64 size_t;
+# else
+ typedef uint32 size_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+# ifdef _BSD_SSIZE_T_
+# undef _BSD_SSIZE_T_
+# define _SSIZE_T
+# define __ssize_t_defined
+# define _SSIZE_T_DECLARED
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef int64 ssize_t;
+# else
+ typedef int32 ssize_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+#else
+# ifndef _SIZE_T
+# define _SIZE_T
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef uint64 size_t;
+# else
+ typedef uint32 size_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+# if !defined(FROBOS) && !defined(_SSIZE_T) && !defined(ssize_t) && \
+ !defined(__ssize_t_defined) && !defined(_SSIZE_T_DECLARED)
+# define _SSIZE_T
+# define __ssize_t_defined
+# define _SSIZE_T_DECLARED
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef int64 ssize_t;
+# else
+ typedef int32 ssize_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+#endif
+
+/*
+ * Format modifier for printing pid_t. On sun the pid_t is a ulong, but on
+ * Linux it's an int.
+ * Use this like this: printf("The pid is %"FMTPID".\n", pid);
+ */
+#ifdef sun
+# ifdef VM_X86_64
+# define FMTPID "d"
+# else
+# define FMTPID "lu"
+# endif
+#else
+# define FMTPID "d"
+#endif
+
+/*
+ * Format modifier for printing uid_t. On sun the uid_t is a ulong, but on
+ * Linux it's an int.
+ * Use this like this: printf("The uid is %"FMTUID".\n", uid);
+ */
+#ifdef sun
+# ifdef VM_X86_64
+# define FMTUID "u"
+# else
+# define FMTUID "lu"
+# endif
+#else
+# define FMTUID "u"
+#endif
+
+/*
+ * Format modifier for printing mode_t. On sun the mode_t is a ulong, but on
+ * Linux it's an int.
+ * Use this like this: printf("The mode is %"FMTMODE".\n", mode);
+ */
+#ifdef sun
+# ifdef VM_X86_64
+# define FMTMODE "o"
+# else
+# define FMTMODE "lo"
+# endif
+#else
+# define FMTMODE "o"
+#endif
+
+/*
+ * Format modifier for printing time_t. Most platforms define a time_t to be
+ * a long int, but on FreeBSD (as of 5.0, it seems), the time_t is a signed
+ * size quantity. Refer to the definition of FMTSZ to see why we need silly
+ * preprocessor arithmetic.
+ * Use this like this: printf("The mode is %"FMTTIME".\n", time);
+ */
+#if defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) >= 5)
+# define FMTTIME FMTSZ"d"
+#else
+# define FMTTIME "ld"
+#endif
+
+/*
+ * Define MXSemaHandle here so both vmmon and vmx see this definition.
+ */
+
+#ifdef _WIN32
+typedef uintptr_t MXSemaHandle;
+#else
+typedef int MXSemaHandle;
+#endif
+
+/*
+ * Define type for poll device handles.
+ */
+
+#ifdef _WIN32
+typedef uintptr_t PollDevHandle;
+#else
+typedef int PollDevHandle;
+#endif
+
+/*
+ * Define the utf16_t type.
+ */
+
+#if defined(_WIN32) && defined(_NATIVE_WCHAR_T_DEFINED)
+typedef wchar_t utf16_t;
+#else
+typedef uint16 utf16_t;
+#endif
+
+#endif /* _VM_BASIC_TYPES_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2003 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vmware.h --
+ *
+ * Standard include file for VMware source code.
+ */
+
+#ifndef _VMWARE_H_
+#define _VMWARE_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMNIXMOD
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#include "vm_basic_types.h"
+#include "vm_basic_defs.h"
+#include "vm_assert.h"
+
+/*
+ * Global error codes. Currently used internally, but may be exported
+ * to customers one day, like VM_E_XXX in vmcontrol_constants.h
+ */
+
+typedef enum VMwareStatus {
+ VMWARE_STATUS_SUCCESS, /* success */
+ VMWARE_STATUS_ERROR, /* generic error */
+ VMWARE_STATUS_NOMEM, /* generic memory allocation error */
+ VMWARE_STATUS_INSUFFICIENT_RESOURCES, /* internal or system resource limit exceeded */
+ VMWARE_STATUS_INVALID_ARGS /* invalid arguments */
+} VMwareStatus;
+
+#define VMWARE_SUCCESS(s) ((s) == VMWARE_STATUS_SUCCESS)
+
+
+#endif // ifndef _VMWARE_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2002 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vmware_pack_begin.h --
+ *
+ * Begin of structure packing. See vmware_pack_init.h for details.
+ *
+ * Note that we do not use the following construct in this include file,
+ * because we want to emit the code every time the file is included --hpreg
+ *
+ * #ifndef foo
+ * # define foo
+ * ...
+ * #endif
+ *
+ */
+
+
+#include "vmware_pack_init.h"
+
+
+#ifdef _MSC_VER
+# pragma pack(push, 1)
+#elif __GNUC__
+#else
+# error Compiler packing...
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2002 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vmware_pack_end.h --
+ *
+ * End of structure packing. See vmware_pack_init.h for details.
+ *
+ * Note that we do not use the following construct in this include file,
+ * because we want to emit the code every time the file is included --hpreg
+ *
+ * #ifndef foo
+ * # define foo
+ * ...
+ * #endif
+ *
+ */
+
+
+#include "vmware_pack_init.h"
+
+
+#ifdef _MSC_VER
+# pragma pack(pop)
+#elif __GNUC__
+__attribute__((__packed__))
+#else
+# error Compiler packing...
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2002 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#ifndef __VMWARE_PACK_INIT_H__
+# define __VMWARE_PACK_INIT_H__
+
+
+/*
+ * vmware_pack_init.h --
+ *
+ * Platform-independent code to make the compiler pack (i.e. have them
+ * occupy the smallest possible space) structure definitions. The following
+ * constructs are known to work --hpreg
+ *
+ * #include "vmware_pack_begin.h"
+ * struct foo {
+ * ...
+ * }
+ * #include "vmware_pack_end.h"
+ * ;
+ *
+ * typedef
+ * #include "vmware_pack_begin.h"
+ * struct foo {
+ * ...
+ * }
+ * #include "vmware_pack_end.h"
+ * foo;
+ */
+
+
+#ifdef _MSC_VER
+/*
+ * MSVC 6.0 emits warning 4103 when the pack push and pop pragma pairing is
+ * not balanced within 1 included file. That is annoying because our scheme
+ * is based on the pairing being balanced between 2 included files.
+ *
+ * So we disable this warning, but this is safe because the compiler will also
+ * emit warning 4161 when there is more pops than pushes within 1 main
+ * file --hpreg
+ */
+
+# pragma warning(disable:4103)
+#elif __GNUC__
+#else
+# error Compiler packing...
+#endif
+
+
+#endif /* __VMWARE_PACK_INIT_H__ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+
+/*
+ * vnode.c --
+ *
+ * Implementaiton of entry points for operations on files (vnodes) and
+ * definition of the vnodeops structure.
+ */
+
+#include <sys/types.h>
+#include <sys/dirent.h>
+#include <sys/sysmacros.h>
+#include <sys/file.h> /* FREAD, FWRITE, etc flags */
+#include <sys/fs_subr.h> /* fs_fab_acl */
+#include <sys/stat.h> /* S_IRWXU and friends */
+#include <sys/dnlc.h> /* Directory name lookup cache */
+#include <sys/signal.h> /* k_sigset_t and signal macros */
+#include <sys/proc.h> /* practive (the active process) */
+#include <sys/user.h> /* u macro for current user area */
+#include <sys/thread.h> /* curthread and curproc macros */
+#include <vm/seg_vn.h> /* segment vnode mapping for mmap() */
+#include <sys/vmsystm.h> /* VM address mapping functions */
+
+#include "hgfsSolaris.h"
+#include "hgfsState.h"
+#include "vnode.h"
+#include "filesystem.h"
+#include "request.h"
+#include "debug.h"
+
+#include "hgfsEscape.h" /* Escaping/unescaping buffers */
+#include "cpName.h" /* Cross-platform name conversion */
+#include "hgfsUtil.h" /* Cross-platform time conversion */
+#include "sha1.h" /* SHA-1 for Node ID calculation */
+
+/*
+ * Macros
+ */
+#define HGFS_ATTR_MODE_SHIFT 6
+
+/* Sets the values of request headers properly */
+#define HGFS_INIT_REQUEST_HDR(request, req, _op) \
+ do { \
+ request->header.id = req->id; \
+ request->header.op = _op; \
+ } while(0)
+
+/* Solaris times support nsecs, so only use these functions directly */
+#define HGFS_SET_TIME(unixtm, nttime) \
+ HgfsConvertFromNtTimeNsec(&unixtm, nttime)
+#define HGFS_GET_TIME(unixtm) \
+ HgfsConvertTimeSpecToNtTime(&unixtm)
+
+/* Determine if this is the root vnode. */
+#define HGFS_IS_ROOT_VNODE(sip, vp) \
+ (sip->rootVnode == vp)
+// ((vp->v_flag & VROOT) && (vp->v_vfsp == sip->vfsp))
+
+
+/*
+ * Prototypes
+ */
+
+/* Local vnode functions */
+static int HgfsDirOpen(HgfsSuperInfo *sip, struct vnode *vp);
+static int HgfsFileOpen(HgfsSuperInfo *sip, struct vnode *vp,
+ int flag, int permissions);
+static int HgfsDirClose(HgfsSuperInfo *sip, struct vnode *vp);
+static int HgfsFileClose(HgfsSuperInfo *sip, struct vnode *vp);
+static int HgfsGetNextDirEntry(HgfsSuperInfo *sip, HgfsHandle handle,
+ uint32_t offset, char *nameOut, Bool *done);
+static int HgfsDoRead(HgfsSuperInfo *sip, HgfsHandle handle, uint64_t offset,
+ uint32_t size, uio_t *uiop);
+static int HgfsDoWrite(HgfsSuperInfo *sip, HgfsHandle handle, int ioflag,
+ uint64_t offset, uint32_t size, uio_t *uiop);
+static int HgfsDelete(HgfsSuperInfo *sip, char *filename, HgfsOp op);
+
+/* Local utility functions */
+#if HGFS_VFS_VERSION > 2
+static int HgfsMakeVnodeOps(HgfsSuperInfo *sip);
+#endif
+static int HgfsSubmitRequest(HgfsSuperInfo *sip, HgfsReq *req);
+static int HgfsValidateReply(HgfsReq *req, uint32_t minSize);
+static void HgfsAttrToSolaris(struct vnode *vp, const HgfsAttr *hgfsAttr,
+ struct vattr *solAttr);
+static Bool HgfsSetattrCopy(struct vattr *solAttr, int flags,
+ HgfsAttr *hgfsAttr, HgfsAttrChanges *update);
+static int HgfsMakeFullName(const char *path, uint32_t pathLen, const char *file,
+ char *outBuf, ssize_t bufSize);
+static int HgfsGetOpenMode(uint32 flags);
+static int HgfsGetOpenFlags(uint32 flags);
+INLINE static void HgfsDisableSignals(k_sigset_t *oldIgnoreSet);
+INLINE static void HgfsRestoreSignals(k_sigset_t *oldIgnoreSet);
+
+
+/* vnode Operation prototypes */
+#if HGFS_VFS_VERSION <= 3
+static int HgfsOpen(struct vnode **vpp, int flag, struct cred *cr);
+static int HgfsClose(struct vnode *vp, int flag, int count, offset_t offset,
+ struct cred *cr);
+#else
+static int HgfsOpen(struct vnode **vpp, int flag, struct cred *cr, caller_context_t *ctx);
+static int HgfsClose(struct vnode *vp, int flag, int count, offset_t offset,
+ struct cred *cr, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION == 2
+static int HgfsRead(struct vnode *vp, struct uio *uiop, int ioflag,
+ struct cred *cr);
+static int HgfsWrite(struct vnode *vp, struct uio *uiop, int ioflag,
+ struct cred *cr);
+#else
+static int HgfsRead(struct vnode *vp, struct uio *uiop, int ioflag,
+ struct cred *cr, caller_context_t *ctx);
+static int HgfsWrite(struct vnode *vp, struct uio *uiop, int ioflag,
+ struct cred *cr, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION <= 3
+static int HgfsIoctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
+ struct cred *cr, int *rvalp);
+static int HgfsGetattr(struct vnode *vp, struct vattr *vap, int flags,
+ struct cred *cr);
+#else
+static int HgfsIoctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
+ struct cred *cr, int *rvalp, caller_context_t *ctx);
+static int HgfsGetattr(struct vnode *vp, struct vattr *vap, int flags,
+ struct cred *cr, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION == 2
+static int HgfsSetattr(struct vnode *vp, struct vattr *vap, int flags,
+ struct cred *cr);
+#else
+static int HgfsSetattr(struct vnode *vp, struct vattr *vap, int flags,
+ struct cred *cr, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION <= 3
+static int HgfsAccess(struct vnode *vp, int mode, int flags,
+ struct cred *cr);
+static int HgfsLookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+ struct pathname *pnp, int flags,
+ struct vnode *rdir, struct cred *cr);
+static int HgfsCreate(struct vnode *dvp, char *name, struct vattr *vap,
+ vcexcl_t excl, int mode, struct vnode **vpp,
+ struct cred *cr, int flag);
+static int HgfsRemove(struct vnode *vp, char *nm, struct cred *cr);
+static int HgfsLink(struct vnode *tdvp, struct vnode *svp, char *tnm,
+ struct cred *cr);
+static int HgfsRename(struct vnode *sdvp, char *snm, struct vnode *tdvp,
+ char *tnm, struct cred *cr);
+static int HgfsMkdir(struct vnode *dvp, char *dirname, struct vattr *vap,
+ struct vnode **vpp, struct cred *cr);
+static int HgfsRmdir(struct vnode *vp, char *nm, struct vnode *cdir,
+ struct cred *cr);
+static int HgfsReaddir(struct vnode *vp, struct uio *uiop, struct cred *cr,
+ int *eofp);
+static int HgfsSymlink(struct vnode *dvp, char *linkname, struct vattr *vap,
+ char *target, struct cred *cr);
+static int HgfsReadlink(struct vnode *vp, struct uio *uiop, struct cred *cr);
+static int HgfsFsync(struct vnode *vp, int syncflag, struct cred *cr);
+static void HgfsInactive(struct vnode *vp, struct cred *cr);
+static int HgfsFid(struct vnode *vp, struct fid *fidp);
+#else
+static int HgfsAccess(struct vnode *vp, int mode, int flags,
+ struct cred *cr, caller_context_t *ctx);
+static int HgfsLookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+ struct pathname *pnp, int flags,
+ struct vnode *rdir, struct cred *cr, caller_context_t *ctx,
+ int *direntflags, pathname_t *realpnp);
+static int HgfsCreate(struct vnode *dvp, char *name, struct vattr *vap,
+ vcexcl_t excl, int mode, struct vnode **vpp,
+ struct cred *cr, int flag, caller_context_t *ctx,
+ vsecattr_t *vsecp);
+static int HgfsRemove(struct vnode *vp, char *nm, struct cred *cr,
+ caller_context_t *ctx, int flags);
+static int HgfsLink(struct vnode *tdvp, struct vnode *svp, char *tnm,
+ struct cred *cr, caller_context_t *ctx, int flags);
+static int HgfsRename(struct vnode *sdvp, char *snm, struct vnode *tdvp,
+ char *tnm, struct cred *cr, caller_context_t *ctx, int flags);
+static int HgfsMkdir(struct vnode *dvp, char *dirname, struct vattr *vap,
+ struct vnode **vpp, struct cred *cr, caller_context_t *ctx,
+ int flags, vsecattr_t *vsecp);
+static int HgfsRmdir(struct vnode *vp, char *nm, struct vnode *cdir,
+ struct cred *cr, caller_context_t *ctx, int flags);
+static int HgfsReaddir(struct vnode *vp, struct uio *uiop, struct cred *cr,
+ int *eofp, caller_context_t *ctx, int flags);
+static int HgfsSymlink(struct vnode *dvp, char *linkname, struct vattr *vap,
+ char *target, struct cred *cr, caller_context_t *ctx,
+ int flags);
+static int HgfsReadlink(struct vnode *vp, struct uio *uiop, struct cred *cr,
+ caller_context_t *ctx);
+static int HgfsFsync(struct vnode *vp, int syncflag, struct cred *cr,
+ caller_context_t *ctx);
+static void HgfsInactive(struct vnode *vp, struct cred *cr, caller_context_t *ctx);
+static int HgfsFid(struct vnode *vp, struct fid *fidp, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION == 2
+static void HgfsRwlock(struct vnode *vp, int write_lock);
+#elif HGFS_VFS_VERSION == 3
+static void HgfsRwlock(struct vnode *vp, int write_lock, caller_context_t *ctx);
+#else
+static int HgfsRwlock(struct vnode *vp, int write_lock, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION == 2
+static void HgfsRwunlock(struct vnode *vp, int write_lock);
+#else
+static void HgfsRwunlock(struct vnode *vp, int write_lock, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION <= 3
+static int HgfsSeek(struct vnode *vp, offset_t ooff, offset_t *noffp);
+static int HgfsCmp(struct vnode *vp1, struct vnode *vp2);
+static int HgfsFrlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, struct flk_callback *, struct cred *cr);
+#else
+static int HgfsSeek(struct vnode *vp, offset_t ooff, offset_t *noffp,
+ caller_context_t *ctx);
+static int HgfsCmp(struct vnode *vp1, struct vnode *vp2, caller_context_t *ctx);
+static int HgfsFrlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, struct flk_callback *, struct cred *cr,
+ caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION == 2
+static int HgfsSpace(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, struct cred *cr);
+#else
+static int HgfsSpace(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, struct cred *cr, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION <= 3
+static int HgfsRealvp(struct vnode *vp, struct vnode **vpp);
+static int HgfsGetpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp,
+ struct page **plarr, size_t plsz, struct seg *seg,
+ caddr_t addr, enum seg_rw rw, struct cred *cr);
+static int HgfsPutpage(struct vnode *vp, offset_t off, size_t len, int flags,
+ struct cred *cr);
+static int HgfsMap(struct vnode *vp, offset_t off, struct as *as,
+ caddr_t *addrp, size_t len, uchar_t prot,
+ uchar_t maxprot, uint_t flags, struct cred *cr);
+static int HgfsAddmap(struct vnode *vp, offset_t off, struct as *as,
+ caddr_t addr, size_t len, uchar_t prot,
+ uchar_t maxprot, uint_t flags, struct cred *cr);
+static int HgfsDelmap(struct vnode *vp, offset_t off, struct as *as,
+ caddr_t addr, size_t len, uint_t prot,
+ uint_t maxprot, uint_t flags, struct cred *cr);
+static int HgfsDump(struct vnode *vp, caddr_t addr, int lbdn, int dblks);
+static int HgfsPathconf(struct vnode *vp, int cmd, ulong_t *valp,
+ struct cred *cr);
+static int HgfsPageio(struct vnode *vp, struct page *pp, u_offset_t io_off,
+ size_t io_len, int flags, struct cred *cr);
+static int HgfsDumpctl(struct vnode *vp, int action, int *blkp);
+static void HgfsDispose(struct vnode *vp, struct page *pp, int flag, int dn,
+ struct cred *cr);
+static int HgfsSetsecattr(struct vnode *vp, vsecattr_t *vsap, int flag,
+ struct cred *cr);
+#else
+static int HgfsRealvp(struct vnode *vp, struct vnode **vpp, caller_context_t *ctx);
+static int HgfsGetpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp,
+ struct page **plarr, size_t plsz, struct seg *seg,
+ caddr_t addr, enum seg_rw rw, struct cred *cr,
+ caller_context_t *ctx);
+static int HgfsPutpage(struct vnode *vp, offset_t off, size_t len, int flags,
+ struct cred *cr, caller_context_t *ctx);
+static int HgfsMap(struct vnode *vp, offset_t off, struct as *as,
+ caddr_t *addrp, size_t len, uchar_t prot,
+ uchar_t maxprot, uint_t flags, struct cred *cr, caller_context_t *ctx);
+static int HgfsAddmap(struct vnode *vp, offset_t off, struct as *as,
+ caddr_t addr, size_t len, uchar_t prot,
+ uchar_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ctx);
+static int HgfsDelmap(struct vnode *vp, offset_t off, struct as *as,
+ caddr_t addr, size_t len, uint_t prot,
+ uint_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ctx);
+static int HgfsDump(struct vnode *vp, caddr_t addr, offset_t lbdn, offset_t dblks,
+ caller_context_t *ctx);
+static int HgfsPathconf(struct vnode *vp, int cmd, ulong_t *valp,
+ struct cred *cr, caller_context_t *ctx);
+static int HgfsPageio(struct vnode *vp, struct page *pp, u_offset_t io_off,
+ size_t io_len, int flags, struct cred *cr, caller_context_t *ctx);
+static int HgfsDumpctl(struct vnode *vp, int action, offset_t *blkp,
+ caller_context_t *ctx);
+static void HgfsDispose(struct vnode *vp, struct page *pp, int flag, int dn,
+ struct cred *cr, caller_context_t *ctx);
+static int HgfsSetsecattr(struct vnode *vp, vsecattr_t *vsap, int flag,
+ struct cred *cr, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION == 2
+static int HgfsShrlock(struct vnode *vp, int cmd, struct shrlock *chr,
+ int flag);
+#elif HGFS_VFS_VERSION == 3
+static int HgfsShrlock(struct vnode *vp, int cmd, struct shrlock *chr,
+ int flag, cred_t *cr);
+#else /* SOL11 */
+static int HgfsShrlock(struct vnode *vp, int cmd, struct shrlock *chr,
+ int flag, cred_t *cr, caller_context_t *ctx);
+#endif
+
+#if HGFS_VFS_VERSION == 3
+static int HgfsVnevent(struct vnode *vp, vnevent_t event);
+#elif HGFS_VFS_VERSION == 5
+static int HgfsVnevent(struct vnode *vp, vnevent_t event, vnode_t *dvp,
+ char *fnm, caller_context_t *ctx);
+#endif
+
+
+#if HGFS_VFS_VERSION == 2
+/* vnode Operations Structure */
+struct vnodeops HgfsVnodeOps = {
+ HgfsOpen, /* vop_open() */
+ HgfsClose, /* vop_close() */
+ HgfsRead, /* vop_read() */
+ HgfsWrite, /* vop_write() */
+ HgfsIoctl, /* vop_ioctl() */
+ fs_setfl, /* vop_setfl() */
+ HgfsGetattr, /* vop_getattr() */
+ HgfsSetattr, /* vop_setattr() */
+ HgfsAccess, /* vop_access() */
+ HgfsLookup, /* vop_lookup() */
+ HgfsCreate, /* vop_create() */
+ HgfsRemove, /* vop_remove() */
+ HgfsLink, /* vop_link() */
+ HgfsRename, /* vop_rename() */
+ HgfsMkdir, /* vop_mkdir() */
+ HgfsRmdir, /* vop_rmdir() */
+ HgfsReaddir, /* vop_readdir() */
+ HgfsSymlink, /* vop_symlink() */
+ HgfsReadlink, /* vop_readlink() */
+ HgfsFsync, /* vop_fsync() */
+ HgfsInactive, /* vop_inactive() */
+ HgfsFid, /* vop_fid() */
+ HgfsRwlock, /* vop_rwlock() */
+ HgfsRwunlock, /* vop_rwunlock() */
+ HgfsSeek, /* vop_seek() */
+ HgfsCmp, /* vop_cmp() */
+ HgfsFrlock, /* vop_frlock() */
+ HgfsSpace, /* vop_space() */
+ HgfsRealvp, /* vop_realvp() */
+ HgfsGetpage, /* vop_getpage() */
+ HgfsPutpage, /* vop_putpage() */
+ HgfsMap, /* vop_map() */
+ HgfsAddmap, /* vop_addmap() */
+ HgfsDelmap, /* vop_delmap() */
+ fs_poll, /* vop_poll() */
+ HgfsDump, /* vop_dump() */
+ HgfsPathconf, /* vop_pathconf() */
+ HgfsPageio, /* vop_pageio() */
+ HgfsDumpctl, /* vop_dumpctl() */
+ HgfsDispose, /* vop_dispose() */
+ HgfsSetsecattr, /* vop_setsecattr() */
+ fs_fab_acl, /* vop_getsecattr() */
+ HgfsShrlock /* vop_shrlock() */
+};
+
+#endif
+
+
+/*
+ * Vnode Entry Points
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsOpen --
+ *
+ * Invoked when open(2) is called on a file in our filesystem. Sends an
+ * OPEN request to the Hgfs server with the filename of this vnode.
+ *
+ * "Opens a file referenced by the supplied vnode. The open() system call
+ * has already done a vop_lookup() on the path name, which returned a vnode
+ * pointer and then calls to vop_open(). This function typically does very
+ * little since most of the real work was performed by vop_lookup()."
+ * (Solaris Internals, p537)
+ *
+ * Results:
+ * Returns 0 on success and an error code on error.
+ *
+ * Side effects:
+ * The HgfsOpenFile for this file is given a handle that can be used on
+ * future read and write requests.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsOpen(struct vnode **vpp, // IN: Vnode for file to open
+ int flag, // IN: Open flags
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsOpen(struct vnode **vpp, // IN: Vnode for file to open
+ int flag, // IN: Open flags
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ HgfsSuperInfo *sip;
+
+ if (!vpp || !cr) {
+ cmn_err(HGFS_ERROR, "HgfsOpen: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsOpen().\n");
+
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ /* Make sure we know the filename. */
+ ASSERT(HGFS_KNOW_FILENAME(*vpp));
+
+ /*
+ * Make sure the handle is not already set. If it is, this means the file
+ * has already been opened so we'll need to create a new vnode since we keep
+ * a vnode for each open instance of a file. This ensures that the handle
+ * we'll create now won't clobber the other one's open-file state.
+ */
+ if (HgfsHandleIsSet(*vpp)) {
+ int ret;
+ struct vnode *origVp = *vpp;
+
+ ret = HgfsVnodeDup(vpp, origVp, sip, &sip->fileHashTable);
+ if (ret) {
+ return EIO;
+ }
+ }
+
+ switch((*vpp)->v_type) {
+ case VDIR:
+ DEBUG(VM_DEBUG_COMM, "HgfsOpen: opening a directory\n");
+ return HgfsDirOpen(sip, *vpp);
+
+ case VREG:
+ {
+ HgfsMode mode = 0;
+
+ /*
+ * If HgfsCreate() was called prior to this, this fills in the mode we
+ * saved there. It's okay if this fails since often HgfsCreate()
+ * won't have been called.
+ */
+ HgfsGetOpenFileMode(*vpp, &mode);
+
+ DEBUG(VM_DEBUG_COMM, "HgfsOpen: opening a file with flag %x\n", flag);
+ return HgfsFileOpen(sip, *vpp, flag, mode);
+ }
+
+ default:
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsOpen: unrecognized file of type %d.\n", (*vpp)->v_type);
+ return EINVAL;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsClose --
+ *
+ * Invoked when a user calls close(2) on a file in our filesystem. Sends
+ * a CLOSE request to the Hgfs server with the filename of this vnode.
+ *
+ * "Closes the file given by the supplied vnode. When this is the last
+ * close, some filesystems use vop_close() to initiate a writeback of
+ * outstanding dirty pages by checking the reference cound in the vnode."
+ * (Solaris Internals, p536)
+ *
+ * Results:
+ * Returns 0 on success and an error code on error.
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsClose(struct vnode *vp, // IN: Vnode of file that is being closed
+ int flag, // IN: Flags file was opened with
+ int count, // IN: Reference count on this vnode
+ offset_t offset, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsClose(struct vnode *vp, // IN: Vnode of file that is being closed
+ int flag, // IN: Flags file was opened with
+ int count, // IN: Reference count on this vnode
+ offset_t offset, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ HgfsSuperInfo *sip;
+
+ if (!vp) {
+ cmn_err(HGFS_ERROR, "HgfsClose: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsClose(). (vp=%p)\n", vp);
+ DEBUG(VM_DEBUG_INFO, "HgfsClose: flag=%x, count=%x, offset=%lld\n",
+ flag, count, offset);
+
+ /*
+ * Solaris calls this function with a count greater than one at certain
+ * times. We only want to actually close it on the last close.
+ */
+ if (count > 1) {
+ return 0;
+ }
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ if ( !HGFS_KNOW_FILENAME(vp) ) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsClose: we don't know the filename of:\n");
+ HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsClose", vp);
+ return EINVAL;
+ }
+
+ /*
+ * If we are closing a directory we need to send a SEARCH_CLOSE request,
+ * but if we are closing a regular file we need to send a CLOSE request.
+ * Other file types are not supported by the Hgfs protocol.
+ */
+
+ switch (vp->v_type) {
+ case VDIR:
+ return HgfsDirClose(sip, vp);
+
+ case VREG:
+ return HgfsFileClose(sip, vp);
+
+ default:
+ DEBUG(VM_DEBUG_FAIL, "HgfsClose: unsupported filetype %d.\n", vp->v_type);
+ return EINVAL;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRead --
+ *
+ * Invoked when a user calls read(2) on a file in our filesystem.
+ *
+ * We call HgfsDoRead() to fill the user's buffer until the request is met
+ * or the file has no more data. This is done since we can only transfer
+ * HGFS_IO_MAX bytes in any one request.
+ *
+ * "Reads the range supplied for the given vnode. vop_read() typically
+ * maps the requested range of a file into kernel memory and then uses
+ * vop_getpage() to do the real work." (Solaris Internals, p537)
+ *
+ * Results:
+ * Returns zero on success and an error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION == 2
+static int
+HgfsRead(struct vnode *vp, // IN: Vnode of file to read
+ struct uio *uiop, // IN: User's read request
+ int ioflag, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsRead(struct vnode *vp, // IN: Vnode of file to read
+ struct uio *uiop, // IN: User's read request
+ int ioflag, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ HgfsSuperInfo *sip;
+ HgfsHandle handle;
+ uint64_t offset;
+ int ret;
+
+ if (!vp || !uiop) {
+ cmn_err(HGFS_ERROR, "HgfsRead: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRead: entry.\n");
+
+ /* We can't read from directories, that's what readdir() is for. */
+ if (vp->v_type == VDIR) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRead: cannot read directories.\n");
+ return EISDIR;
+ }
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ /* This is where the user wants to start reading from in the file. */
+ offset = uiop->uio_loffset;
+
+ /*
+ * We need to get the handle for the requests sent to the Hgfs server. Note
+ * that this is guaranteed to not change until a close(2) is called on this
+ * vnode, so it's safe and correct to acquire it outside the loop below.
+ */
+ ret = HgfsGetOpenFileHandle(vp, &handle);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRead: could not get handle.\n");
+ return EINVAL;
+ }
+
+ /*
+ * Here we loop around HgfsDoRead with requests less than or equal to
+ * HGFS_IO_MAX until one of the following conditions is met:
+ * (1) All the requested data has been read
+ * (2) The file has no more data
+ * (3) An error occurred
+ *
+ * Since HgfsDoRead() calls uiomove(9F), we know condition (1) is met when
+ * the uio structure's uio_resid is decremented to zero. If HgfsDoRead()
+ * returns 0 we know condition (2) was met, and if it returns less than 0 we
+ * know condtion (3) was met.
+ */
+ do {
+ uint32_t size;
+
+ DEBUG(VM_DEBUG_INFO, "HgfsRead: offset=%"FMT64"d, uio_loffset=%lld\n",
+ offset, uiop->uio_loffset);
+ DEBUG(VM_DEBUG_HANDLE, "HgfsRead: ** handle=%d, file=%s\n",
+ handle, HGFS_VP_TO_FILENAME(vp));
+
+ /* Request at most HGFS_IO_MAX bytes */
+ size = (uiop->uio_resid > HGFS_IO_MAX) ? HGFS_IO_MAX : uiop->uio_resid;
+
+ /* Send one read request. */
+ ret = HgfsDoRead(sip, handle, offset, size, uiop);
+ if (ret == 0) {
+ /* On end of file we return success */
+ DEBUG(VM_DEBUG_DONE, "HgfsRead: end of file reached.\n");
+ return 0;
+ } else if (ret < 0) {
+ /*
+ * HgfsDoRead() returns the negative of an appropriate error code to
+ * differentiate between success and error cases. We flip the sign
+ * and return the appropriate error code. See the HgfsDoRead()
+ * function header for a fuller explanation.
+ */
+ DEBUG(VM_DEBUG_FAIL, "HgfsRead: HgfsDoRead() failed.\n");
+ return -ret;
+ }
+
+ /* Bump the offset past where we have already read. */
+ offset += ret;
+ } while (uiop->uio_resid);
+
+ /* We fulfilled the user's read request, so return success. */
+ DEBUG(VM_DEBUG_DONE, "HgfsRead: done.\n");
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsWrite --
+ *
+ * This is invoked when a user calls write(2) on a file in our filesystem.
+ *
+ * We call HgfsDoWrite() once with requests less than or equal to
+ * HGFS_IO_MAX bytes until the user's write request has completed.
+ *
+ * "Writes the range supplied for the given vnode. The write system call
+ * typically maps the requested range of a file into kernel memory and then
+ * uses vop_putpage() to do the real work." (Solaris Internals, p538)
+ *
+ * Results:
+ * Returns 0 on success and error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION < 3
+static int
+HgfsWrite(struct vnode *vp, // IN: Vnode of file to write to
+ struct uio *uiop, // IN: User's write request
+ int ioflag, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsWrite(struct vnode *vp, // IN: Vnode of file to write to
+ struct uio *uiop, // IN: User's write request
+ int ioflag, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ HgfsSuperInfo *sip;
+ HgfsHandle handle;
+ uint64_t offset;
+ int ret;
+
+ if (!vp || !uiop) {
+ cmn_err(HGFS_ERROR, "HgfsWrite: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsWrite: entry. (vp=%p)\n", vp);
+ DEBUG(VM_DEBUG_INFO, "HgfsWrite: ***ioflag=%x, uio_resid=%ld\n",
+ ioflag, uiop->uio_resid);
+
+ /* Skip write requests for 0 bytes. */
+ if (uiop->uio_resid == 0) {
+ DEBUG(VM_DEBUG_INFO, "HgfsWrite: write of 0 bytes requested.\n");
+ return 0;
+ }
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ DEBUG(VM_DEBUG_INFO, "HgfsWrite: file is %s\n", HGFS_VP_TO_FILENAME(vp));
+
+ /* This is where the user will begin writing into the file. */
+ offset = uiop->uio_loffset;
+
+ /* Get the handle we need to supply the Hgfs server. */
+ ret = HgfsGetOpenFileHandle(vp, &handle);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsWrite: could not get handle.\n");
+ return EINVAL;
+ }
+
+ /*
+ * We loop around calls to HgfsDoWrite() until either (1) we have written all
+ * of our data or (2) an error has occurred. uiop->uio_resid is decremented
+ * by uiomove(9F) inside HgfsDoWrite(), so condition (1) is met when it
+ * reaches zero. Condition (2) occurs when HgfsDoWrite() returns less than
+ * zero.
+ */
+ do {
+ uint32_t size;
+
+ DEBUG(VM_DEBUG_INFO, "HgfsWrite: ** offset=%"FMT64"d, uio_loffset=%lld\n",
+ offset, uiop->uio_loffset);
+ DEBUG(VM_DEBUG_HANDLE, "HgfsWrite: ** handle=%d, file=%s\n",
+ handle, HGFS_VP_TO_FILENAME(vp));
+
+ /* Write at most HGFS_IO_MAX bytes. */
+ size = (uiop->uio_resid > HGFS_IO_MAX) ? HGFS_IO_MAX : uiop->uio_resid;
+
+ /* Send one write request. */
+ ret = HgfsDoWrite(sip, handle, ioflag, offset, size, uiop);
+ if (ret < 0) {
+ /*
+ * As in HgfsRead(), we need to flip the sign. See the comment in the
+ * function header of HgfsDoWrite() for a more complete explanation.
+ */
+ DEBUG(VM_DEBUG_INFO, "HgfsWrite: HgfsDoWrite failed, returning %d\n", -ret);
+ return -ret;
+ }
+
+ /* Increment the offest by the amount already written. */
+ offset += ret;
+
+ } while (uiop->uio_resid);
+
+ /* We have completed the user's write request, so return success. */
+ DEBUG(VM_DEBUG_DONE, "HgfsWrite: done.\n");
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsIoctl --
+ *
+ * Invoked when a user calls ioctl(2) on a file in our filesystem.
+ * Performs a specified operation on the file.
+ *
+ * Results:
+ * ENOTSUP
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsIoctl(struct vnode *vp, // IN: Vnode of file to operate on
+ int cmd, // IN: Requested command from user
+ intptr_t arg, // IN: Arguments for command
+ int flag, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ int *rvalp) //XXX
+#else
+static int
+HgfsIoctl(struct vnode *vp, // IN: Vnode of file to operate on
+ int cmd, // IN: Requested command from user
+ intptr_t arg, // IN: Arguments for command
+ int flag, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ int *rvalp, //XXX
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsIoctl() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSetfl --
+ *
+ * "Sets file locks on the supplied vnode." (Solaris Internals, p538)
+ *
+ * Use fs_setfl from <sys/fs_subr.h>? Do we need this?
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+/*
+int
+HgfsSetfl(struct vnode *vp,
+ int oflags,
+ int nflags,
+ struct cred *cr)
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsSetfl() NOTSUP.\n");
+
+ return ENOTSUP;
+}*/
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetattr --
+ *
+ * "Gets the attributes for the supplied vnode." (Solaris Internals, p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsGetattr(struct vnode *vp, // IN: Vnode of file to get attributes for
+ struct vattr *vap, // OUT: Filled in with attributes of file
+ int flags, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsGetattr(struct vnode *vp, // IN: Vnode of file to get attributes for
+ struct vattr *vap, // OUT: Filled in with attributes of file
+ int flags, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ HgfsSuperInfo *sip;
+ HgfsReq *req;
+ HgfsRequestGetattr *request;
+ HgfsReplyGetattr *reply;
+ int ret;
+
+ if (!vp || !vap) {
+ cmn_err(HGFS_ERROR, "HgfsGetattr: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsGetattr().\n");
+
+ /*
+ * Here we should send a Getattr request then examine vap->va_mask to
+ * retun the values the user asked for. HgfsAttrToSolaris() handles filling
+ * in the Solaris structure with the correct values based on the Hgfs type.
+ */
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetattr() couldn't get superinfo.\n");
+ return EIO;
+ }
+
+ ASSERT(HGFS_KNOW_FILENAME(vp));
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return EIO;
+ }
+
+ request = (HgfsRequestGetattr *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_GETATTR);
+
+ /*
+ * Now we need to convert the filename to cross-platform and unescaped
+ * format.
+ */
+ ret = CPName_ConvertTo(HGFS_VP_TO_FILENAME(vp), MAXPATHLEN, request->fileName.name);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetattr: CPName_ConvertTo failed.\n");
+ ret = ENAMETOOLONG;
+ /* We need to set the request's state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->fileName.length = ret;
+
+ req->packetSize = sizeof *request + request->fileName.length;
+
+ /*
+ * Now submit request and wait for reply. The request's state will be
+ * properly set to COMPLETED, ERROR, or ABANDONED after calling
+ * HgfsSubmitRequest()
+ */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest destroys the request if necessary */
+ goto out;
+ }
+
+ reply = (HgfsReplyGetattr *)req->packet;
+
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetattr: reply not valid.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ /* Make sure we got all of the attributes */
+ if (req->packetSize != sizeof *reply) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetattr: packet too small.\n");
+ ret = EIO;
+ break;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsGetattr: received reply for ID %d\n",
+ reply->header.id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n",
+ reply->header.status);
+ DEBUG(VM_DEBUG_COMM, " file type: %d\n", reply->attr.type);
+ DEBUG(VM_DEBUG_COMM, " file size: %"FMT64"u\n", reply->attr.size);
+ DEBUG(VM_DEBUG_COMM, " permissions: %o\n", reply->attr.permissions);
+ DEBUG(VM_DEBUG_COMM, "HgfsGetattr: filename %s\n", HGFS_VP_TO_FILENAME(vp));
+
+ /* Map the Hgfs attributes into the Solaris attributes */
+ HgfsAttrToSolaris(vp, &reply->attr, vap);
+
+ DEBUG(VM_DEBUG_DONE, "HgfsGetattr: done.\n");
+ break;
+
+ /* We'll add other error codes based on status here */
+ default:
+ ret = EIO;
+ break;
+ }
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSetattr --
+ *
+ * Maps the Solaris attributes to Hgfs attributes (by calling
+ * HgfsSetattrCopy()) and sends a set attribute request to the Hgfs server.
+ *
+ * "Sets the attributes for the supplied vnode." (Solaris Internals, p537)
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on error.
+ *
+ * Side effects:
+ * The file on the host will have new attributes.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION == 2
+static int
+HgfsSetattr(struct vnode *vp, // IN: Vnode of file to get attributes for
+ struct vattr *vap, // IN: New attributes for the file
+ int flags, // IN: Flags for this call (from sys/vnode.h)
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsSetattr(struct vnode *vp, // IN: Vnode of file to get attributes for
+ struct vattr *vap, // IN: New attributes for the file
+ int flags, // IN: Flags for this call (from sys/vnode.h)
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ HgfsSuperInfo *sip;
+ HgfsReq *req;
+ HgfsRequestSetattr *request;
+ HgfsReplySetattr *reply;
+ int ret;
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsSetattr().\n");
+
+ if (!vp || !vap) {
+ cmn_err(HGFS_ERROR, "HgfsSetattr: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ if ( !HGFS_KNOW_FILENAME(vp) ) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsSetattr: we don't know filename to set attributes for.\n");
+ return EINVAL;
+ }
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return EIO;
+ }
+
+ request = (HgfsRequestSetattr *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SETATTR);
+
+ /*
+ * Fill the attributes and update fields of the request. If no updates are
+ * needed then we will just return success without sending the request.
+ */
+ if (HgfsSetattrCopy(vap, flags, &request->attr, &request->update) == FALSE) {
+ DEBUG(VM_DEBUG_DONE, "HgfsSetattr: don't need to update attributes.\n");
+ ret = 0;
+ /* We need to set the request state to completed before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_COMPLETED);
+ goto destroy_out;
+ }
+
+ /* Convert the filename to cross platform and escape its buffer. */
+ ret = CPName_ConvertTo(HGFS_VP_TO_FILENAME(vp), MAXPATHLEN, request->fileName.name);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: CPName_ConvertTo failed.\n");
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->fileName.length = ret;
+
+ /* The request's size includes the request and filename. */
+ req->packetSize = sizeof *request + request->fileName.length;
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest() destroys the request if necessary. */
+ goto out;
+ }
+
+ reply = (HgfsReplySetattr *)req->packet;
+
+ if (HgfsValidateReply(req, sizeof *reply) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: invalid reply received.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ switch(reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ /* Success handled after switch. */
+ break;
+
+ case HGFS_STATUS_NO_SUCH_FILE_OR_DIR:
+ case HGFS_STATUS_INVALID_NAME:
+ DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: no such file or directory.\n");
+ ret = ENOENT;
+ goto destroy_out;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: operation not permitted.\n");
+ ret = EPERM;
+ goto destroy_out;
+
+ default:
+ DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: default error.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ /* Success */
+ ret = 0;
+ DEBUG(VM_DEBUG_DONE, "HgfsSetattr: done.\n");
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsAccess --
+ *
+ * This function is invoked when the user calls access(2) on a file in our
+ * filesystem. It checks to ensure the user has the specified type of
+ * access to the file.
+ *
+ * We send a GET_ATTRIBUTE request by calling HgfsGetattr() to get the mode
+ * (permissions) for the provided vnode.
+ *
+ * Results:
+ * Returns 0 if access is allowed and a non-zero error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsAccess(struct vnode *vp, // IN: Vnode of file to check access for
+ int mode, // IN: Mode of access
+ int flags, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsAccess(struct vnode *vp, // IN: Vnode of file to check access for
+ int mode, // IN: Mode of access
+ int flags, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ int ret;
+ struct vattr vap;
+
+ if (!vp | !cr) {
+ cmn_err(HGFS_ERROR, "HgfsAccess: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsAccess(). (vp=%p, mode=%o, flags=%x)\n",
+ vp, mode, flags);
+
+ /* We only care about the file's mode (permissions). That is, not the owner */
+ vap.va_mask = AT_MODE;
+
+ /* Get the attributes for this file from the Hgfs server. */
+#if HGFS_VFS_VERSION <= 3
+ ret = HgfsGetattr(vp, &vap, flags, cr);
+#else
+ ret = HgfsGetattr(vp, &vap, flags, cr, NULL);
+#endif
+ if (ret) {
+ return ret;
+ }
+
+ DEBUG(VM_DEBUG_INFO, "HgfsAccess: vp's mode: %o\n", vap.va_mode);
+
+ /*
+ * mode is the desired access from the caller, and is composed of S_IREAD,
+ * S_IWRITE, and S_IEXEC from <sys/stat.h>. Since the mode of the file is
+ * guaranteed to only contain owner permissions (by the Hgfs server), we
+ * don't need to shift any bits.
+ */
+ if ((mode & S_IREAD) && !(vap.va_mode & S_IREAD)) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsAccess: read access not allowed (%s).\n",
+ HGFS_VP_TO_FILENAME(vp));
+ return EPERM;
+ }
+
+ if ((mode & S_IWRITE) && !(vap.va_mode & S_IWRITE)) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsAccess: write access not allowed (%s).\n",
+ HGFS_VP_TO_FILENAME(vp));
+ return EPERM;
+ }
+
+ if ((mode & S_IEXEC) && !(vap.va_mode & S_IEXEC)) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsAccess: execute access not allowed (%s).\n",
+ HGFS_VP_TO_FILENAME(vp));
+ return EPERM;
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsLookup --
+ *
+ * Looks in the provided directory for the specified filename. If we cannot
+ * determine the vnode locally (i.e, the vnode is not the root vnode of the
+ * filesystem or the provided dvp), we send a getattr request to the server
+ * and allocate a vnode and internal filesystem state for this file.
+ *
+ * "Looks up the path name for the supplied vnode. The vop_lookup() does
+ * file-name translation for the open, stat system calls." (Solaris
+ * Internals, p537)
+ *
+ * Results:
+ * Returns zero on success and ENOENT if the file cannot be found
+ * If file is found, a vnode representing the file is returned in vpp.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsLookup(struct vnode *dvp, // IN: Directory to look in
+ char *nm, // IN: Name to lookup in directory
+ struct vnode **vpp, // OUT: Pointer to vnode representing found file
+ struct pathname *pnp,// IN: XXX
+ int flags, // IN: XXX
+ struct vnode *rdir, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+
+#else
+static int
+HgfsLookup(struct vnode *dvp, // IN: Directory to look in
+ char *nm, // IN: Name to lookup in directory
+ struct vnode **vpp, // OUT: Pointer to vnode representing found file
+ struct pathname *pnp, // IN: XXX
+ int flags, // IN: XXX
+ struct vnode *rdir, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx, // IN: Context of caller
+ int *direntflags, // IN: XXX
+ pathname_t *realpnp) // IN: XXX
+#endif
+{
+ int ret;
+ HgfsReq *req;
+ HgfsRequestGetattr *request;
+ HgfsReplyGetattr *reply;
+ HgfsSuperInfo *sip;
+ char path[MAXPATHLEN + 1]; /* Temporary buffer for full path */
+
+ if (!dvp || !nm || !vpp || !cr) {
+ cmn_err(HGFS_ERROR, "HgfsLookup: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsLookup(). (nm=%s)\n", nm);
+ //DEBUG(VM_DEBUG_ENTRY, "HgfsLookup: pnp=%x, rdir=%x\n", pnp, rdir);
+
+ /* First ensure that we are looking in a directory. */
+ if (dvp->v_type != VDIR) {
+ return ENOTDIR;
+ }
+
+ DEBUG(VM_DEBUG_COMM, " looking up \"%s\"\n", nm);
+
+ /*
+ * Get pointer to the superinfo. If the device is not attached,
+ * hgfsInstance will not be valid and we immediately return an error.
+ */
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsLookup: couldn't acquire superinfo "
+ "(hgfsInstance=%x).\n", hgfsInstance);
+ return EIO;
+ }
+
+ /* Construct the full path for this lookup. */
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp), // Path to this file
+ HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of path
+ nm, // File's name
+ path, // Destination buffer
+ sizeof path); // Size of dest buffer
+ if (ret < 0) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ DEBUG(VM_DEBUG_LOAD, "HgfsLookup: full path is \"%s\"\n", path);
+
+ /* See if the lookup is really for the root vnode. */
+ if (strcmp(path, "/") == 0) {
+ DEBUG(VM_DEBUG_INFO, "HgfsLookup: returning the root vnode.\n");
+ *vpp = HGFS_ROOT_VNODE(sip);
+ /*
+ * Note that this is the only vnode we maintain a reference count on; all
+ * others are per-open-file and should only be given to the Kernel once.
+ */
+ VN_HOLD(*vpp);
+ return 0;
+ }
+
+ /*
+ * Now that we know the full filename, we can check our hash table for this
+ * file to prevent having to send a request to the Hgfs Server. If we do
+ * find this file in the hash table, this function will correctly create
+ * a vnode and other per-open state for us.
+ *
+ * On an 'ls -l', this saves sending two requests for each file in the
+ * directory.
+ *
+ * XXX
+ * Note that this optimization leaves open the possibility that a file that
+ * has been removed on the host will not be noticed as promptly by the
+ * filesystem. This shouldn't cause any problems, though, because as far
+ * as we can tell this function is invoked internally by the kernel before
+ * other operations. That is, this function is called implicitly for path
+ * traversal when user applications issue other system calls. The operation
+ * next performed on the vnode we create here should happen prior to
+ * returning to the user application, so if that next operation fails
+ * because the file has been deleted, the user won't see different behavior
+ * than if this optimization was not included. Nonetheless, the #if 1 below
+ * is provided to make it easy to turn off.
+ */
+#if 1
+ ret = HgfsFileNameToVnode(path, vpp, sip, sip->vfsp, &sip->fileHashTable);
+ if (ret == 0) {
+ /*
+ * The filename was in our hash table and we successfully created new
+ * per-open state for it.
+ */
+ DEBUG(VM_DEBUG_DONE, "HgfsLookup: created per-open state from filename.\n");
+ goto out;
+ }
+#endif
+
+ /*
+ * We don't have any reference to this vnode, so we must send a get
+ * attribute request to see if the file exists and create one.
+ */
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return EIO;
+ }
+
+ /* Fill in the header of this request. */
+ request = (HgfsRequestGetattr *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_GETATTR);
+
+ /* Fill in the filename portion of the request. */
+ ret = CPName_ConvertTo(path, MAXPATHLEN, request->fileName.name);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsLookup: CPName_ConvertTo failed.\n");
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+ request->fileName.length = ret;
+
+ /* Packet size includes the request and its payload. */
+ req->packetSize = request->fileName.length + sizeof *request;
+
+ DEBUG(VM_DEBUG_COMM, "HgfsLookup: sending getattr request for ID %d\n",
+ request->header.id);
+ DEBUG(VM_DEBUG_COMM, " fileName.length: %d\n", request->fileName.length);
+ DEBUG(VM_DEBUG_COMM, " fileName.name: \"%s\"\n", request->fileName.name);
+
+ /*
+ * Submit the request and wait for the reply. HgfsSubmitRequest handles
+ * destroying the request on both error and interrupt cases.
+ */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ goto out;
+ }
+
+ /* The reply is in the request's packet */
+ reply = (HgfsReplyGetattr *)req->packet;
+
+ /* Validate the reply was COMPLETED and at least contains a header */
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsLookup(): invalid reply received for ID %d "
+ "with status %d.\n", reply->header.id, reply->header.status);
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsLookup: received reply for ID %d\n", reply->header.id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);
+ DEBUG(VM_DEBUG_COMM, " file type: %d\n", reply->attr.type);
+ DEBUG(VM_DEBUG_COMM, " file size: %"FMT64"u\n", reply->attr.size);
+ DEBUG(VM_DEBUG_COMM, " permissions: %o\n", reply->attr.permissions);
+
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ /* Ensure packet contains correct amount of data */
+ if (req->packetSize != sizeof *reply) {
+ DEBUG(VM_DEBUG_COMM,
+ "HgfsLookup: invalid packet size received for \"%s\".\n", nm);
+ ret = EIO;
+ goto destroy_out;
+ }
+ /* Success */
+ break;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_LOG, "HgfsLookup: operation not permitted on \"%s\".\n", nm);
+ ret = EACCES;
+ goto destroy_out;
+
+ case HGFS_STATUS_NO_SUCH_FILE_OR_DIR:
+ case HGFS_STATUS_INVALID_NAME:
+ /*
+ * Note, this is necessary because before creating a file Solaris does
+ * a lookup to make sure it doesn't already exist. We need to return
+ * ENOENT in this case.
+ */
+ DEBUG(VM_DEBUG_LOG, "HgfsLookup: \"%s\" does not exist.\n", nm);
+ ret = ENOENT;
+ goto destroy_out;
+
+ default:
+ DEBUG(VM_DEBUG_LOG, "HgfsLookup: error on \"%s\".\n", nm);
+ ret = EIO;
+ goto destroy_out;
+ }
+
+ /*
+ * We need to create a vnode for this found file to give back to the Kernel.
+ * Note that v_vfsp of the filesystem's root vnode was set properly in
+ * HgfsMount(), so that value (dvp->v_vfsp) propagates down to each vnode.
+ *
+ */
+ ret = HgfsVnodeGet(vpp, // Location to write vnode's address
+ sip, // Superinfo
+ dvp->v_vfsp, // VFS for our filesystem
+ path, // Full name of the file
+ reply->attr.type, // Type of file
+ &sip->fileHashTable); // File hash table
+
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsLookup: couldn't create vnode for \"%s\".\n", path);
+ ret = EIO;
+ goto destroy_out;
+ }
+ /* HgfsVnodeGet guarantees this. */
+ ASSERT(*vpp);
+
+ DEBUG(VM_DEBUG_LOAD, "HgfsLookup: assigned vnode %p to %s\n", *vpp, path);
+
+ ret = 0; /* Return success */
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsCreate --
+ *
+ * This entry point is invoked when a user calls open(2) with the O_CREAT
+ * flag specified. The kernel calls our open entry point (HgfsOpen()) after
+ * calling this function, so here all we do is consruct the vnode and
+ * save the filename and permission bits for the file to be created within
+ * our filesystem internal state.
+ *
+ * "Creates the supplied pathname." (Solaris Internals, p536)
+ *
+ * Results:
+ * Returns zero on success and an appropriate error code on error.
+ *
+ * Side effects:
+ * If the file exists, the vnode is duplicated since they are kepy per-open.
+ * If the file doesn't exist, a vnode will be created.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsCreate(struct vnode *dvp, // IN: Directory to create file in
+ char *name, // IN: Name of file to create
+ struct vattr *vap, // IN: Attributes for file
+ vcexcl_t excl, // IN: Exclusive creation flag: either NONEXCL or EXCL
+ int mode, // IN: Permissions to create with
+ struct vnode **vpp, // OUT: Vnode of file to create
+ struct cred *cr, // IN: Credentials of caller
+ int flag) // IN: XXX
+#else
+static int
+HgfsCreate(struct vnode *dvp, // IN: Directory to create file in
+ char *name, // IN: Name of file to create
+ struct vattr *vap, // IN: Attributes for file
+ vcexcl_t excl, // IN: Exclusive creation flag: either NONEXCL or EXCL
+ int mode, // IN: Permissions to create with
+ struct vnode **vpp, // OUT: Vnode of file to create
+ struct cred *cr, // IN: Credentials of caller
+ int flag, // IN: XXX
+ caller_context_t *ctx, // IN: Context of caller
+ vsecattr_t *vsecp) // IN: XXX
+#endif
+{
+ HgfsSuperInfo *sip;
+ int ret = 0;
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsCreate(): entry for \"%s\"\n", name);
+
+ if (!dvp || !name || !vap || !vpp || !cr) {
+ cmn_err(HGFS_ERROR, "HgfsCreate: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ if (dvp->v_type != VDIR) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsCreate: files must be created in directories.\n");
+ return ENOTDIR;
+ }
+
+ /*
+ * There are two cases: either the file already exists or it doesn't. If
+ * the file exists already then *vpp points to its vnode that was allocated
+ * in HgfsLookup(). In both cases we need to create a new vnode (since our
+ * vnodes are per-open-file, not per-file), but we don't need to construct
+ * the full name again if we already have it in the existing vnode.
+ */
+ if ( !(*vpp) ) {
+ char fullname[MAXPATHLEN + 1];
+
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp), // Name of directory to create in
+ HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of name
+ name, // Name of file to create
+ fullname, // Buffer to write full name
+ sizeof fullname); // Size of this buffer
+
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsCreate: couldn't create full path name.\n");
+ return ENAMETOOLONG;
+ }
+
+ /* Create the vnode for this file. */
+ ret = HgfsVnodeGet(vpp, sip, dvp->v_vfsp, fullname,
+ HGFS_FILE_TYPE_REGULAR, &sip->fileHashTable);
+ if (ret) {
+ return EIO;
+ }
+ } else {
+ struct vnode *origVp = *vpp;
+
+ ASSERT(origVp->v_type != VDIR); /* HgfsMkdir() should have been invoked */
+
+ ret = HgfsVnodeDup(vpp, origVp, sip, &sip->fileHashTable);
+ if (ret) {
+ return EIO;
+ }
+
+ /* These cannot be the same. */
+ ASSERT(*vpp != origVp);
+ }
+
+ /* HgfsVnodeGet() guarantees this. */
+ ASSERT(*vpp);
+
+ /* Save the mode so when open is called we can reference it. */
+ HgfsSetOpenFileMode(*vpp, vap->va_mode);
+
+ /* Solaris automatically calls open after this, so our work is done. */
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRemove --
+ *
+ * Composes the full pathname of this file and sends a DELETE_FILE request
+ * by calling HgfsDelete().
+ *
+ * "Removes the file for the supplied vnode." (Solaris Internals, p537)
+ *
+ * Results:
+ * Returns 0 on success or a non-zero error code on error.
+ *
+ * Side effects:
+ * If successful, the file specified will be deleted from the host's
+ * filesystem.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsRemove(struct vnode *vp, // IN: Directory containing file to remove
+ char *nm, // IN: Name of file to remove
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsRemove(struct vnode *vp, // IN: Directory containing file to remove
+ char *nm, // IN: Name of file to remove
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx, // IN: Context of caller
+ int flags) // IN: XXX
+#endif
+{
+ HgfsSuperInfo *sip;
+ char fullpath[MAXPATHLEN + 1];
+ int ret;
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRemove().\n");
+
+ if (!vp || !nm) {
+ cmn_err(HGFS_ERROR, "HgfsRemove: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ /* Ensure parent is a directory. */
+ if (vp->v_type != VDIR) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsRemove: provided parent is a file, not a directory.\n");
+ return ENOTDIR;
+ }
+
+ /* Ensure we know the name of the parent. */
+ ASSERT(HGFS_KNOW_FILENAME(vp));
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ /*
+ * We must construct the full name of the file to remove then call
+ * HgfsDelete() to send the deletion request.
+ */
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp), // Name of directory to create in
+ HGFS_VP_TO_FILENAME_LENGTH(vp), // Length of name
+ nm, // Name of file to create
+ fullpath, // Buffer to write full name
+ sizeof fullpath); // Size of this buffer
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRemove: could not construct full name.\n");
+ return ENAMETOOLONG;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsRemove: removing \"%s\".\n", fullpath);
+
+ /* We can now send the delete request. */
+ return HgfsDelete(sip, fullpath, HGFS_OP_DELETE_FILE);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsLink --
+ *
+ * "Creates a hard link to the supplied vnode." (Solaris Internals, p537)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsLink(struct vnode *tdvp, //XXX: Vnode of directory to create link in
+ struct vnode *svp, //XXX: Vnode of file to link to
+ char *tnm, //XXX: Name of link in directory
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsLink(struct vnode *tdvp, //XXX: Vnode of directory to create link in
+ struct vnode *svp, //XXX: Vnode of file to link to
+ char *tnm, //XXX: Name of link in directory
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx, // IN: Context of caller
+ int flags) // IN: XXX
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsLink() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRename --
+ *
+ * Renames the provided source name in the source directory with the
+ * destination name in the destination directory. A RENAME request is sent
+ * to the Hgfs server.
+ *
+ * Results:
+ * Returns 0 on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to move
+ char *snm, // IN: File to move
+ struct vnode *tdvp, // IN: Destination directory of file
+ char *tnm, // IN: Destination name of file
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to move
+ char *snm, // IN: File to move
+ struct vnode *tdvp, // IN: Destination directory of file
+ char *tnm, // IN: Destination name of file
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx, // IN: Context of caller
+ int flags) // IN: XXX
+#endif
+{
+ HgfsSuperInfo *sip;
+ HgfsReq *req;
+ HgfsRequestRename *request;
+ HgfsReplyRename *reply;
+ HgfsFileName *newNameP;
+ char srcFullPath[MAXPATHLEN + 1];
+ char dstFullPath[MAXPATHLEN + 1];
+ int ret;
+
+ if (!sdvp || !snm || !tdvp || !tnm) {
+ cmn_err(HGFS_ERROR, "HgfsRename: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRename().\n");
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ /* Make sure we know the names of both parent directories. */
+ ASSERT(HGFS_KNOW_FILENAME(sdvp) && HGFS_KNOW_FILENAME(tdvp));
+
+ /* Make the full path of the source. */
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(sdvp), HGFS_VP_TO_FILENAME_LENGTH(sdvp),
+ snm,
+ srcFullPath, sizeof srcFullPath);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: could not construct full path of source.\n");
+ return ENAMETOOLONG;
+ }
+
+ /* Make the full path of the destination. */
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(tdvp), HGFS_VP_TO_FILENAME_LENGTH(tdvp),
+ tnm,
+ dstFullPath, sizeof dstFullPath);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: could not construct full path of dest.\n");
+ return ENAMETOOLONG;
+ }
+
+ /* Ensure both names will fit in one request. */
+ if ((sizeof *request + strlen(srcFullPath) + strlen(dstFullPath))
+ > HGFS_PACKET_MAX) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: names too big for one request.\n");
+ return EPROTO;
+ }
+
+ /*
+ * Now we can prepare and send the request.
+ */
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return EIO;
+ }
+
+ request = (HgfsRequestRename *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_RENAME);
+
+ /* Convert the source to cross platform and unescape its buffer. */
+ ret = CPName_ConvertTo(srcFullPath, MAXPATHLEN, request->oldName.name);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsRename: couldn't convert source to cross platform name.\n");
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->oldName.length = ret;
+
+ /*
+ * The new name is placed directly after the old name in the packet and we
+ * access it through this pointer.
+ */
+ newNameP = (HgfsFileName *)((char *)&request->oldName +
+ sizeof request->oldName +
+ request->oldName.length);
+
+ /* Convert the destination to cross platform and unescape its buffer. */
+ ret = CPName_ConvertTo(dstFullPath, MAXPATHLEN, newNameP->name);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsRename: couldn't convert destination to cross platform name.\n");
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ newNameP->length = ret;
+
+ /* The request's size includes the request and both filenames. */
+ req->packetSize = sizeof *request + request->oldName.length + newNameP->length;
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest() destroys the request if necessary. */
+ goto out;
+ }
+
+ reply = (HgfsReplyRename *)req->packet;
+
+ /* Validate the reply's state and size. */
+ if (HgfsValidateReply(req, sizeof *reply) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: invalid reply received.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ /* Return appropriate value. */
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ /* Handled after switch. */
+ break;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: operation not permitted.\n");
+ ret = EACCES;
+ goto destroy_out;
+
+ case HGFS_STATUS_NOT_DIRECTORY:
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: not a directory.\n");
+ ret = ENOTDIR;
+ goto destroy_out;
+
+ case HGFS_STATUS_DIR_NOT_EMPTY:
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: directory not empty.\n");
+ ret = EEXIST;
+ goto destroy_out;
+
+ case HGFS_STATUS_NO_SUCH_FILE_OR_DIR:
+ case HGFS_STATUS_INVALID_NAME:
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: no such file or directory.\n");
+ ret = ENOENT;
+ goto destroy_out;
+
+ default:
+ DEBUG(VM_DEBUG_FAIL, "HgfsRename: default error.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+
+ /* Successfully renamed file. */
+ ret = 0;
+ DEBUG(VM_DEBUG_DONE, "HgfsRename: done.\n");
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsMkdir --
+ *
+ * Makes a directory named dirname in the directory specified by the dvp
+ * vnode by sending a CREATE_DIR request, then allocates a vnode for this
+ * new directory and writes its address into vpp.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * If successful, a directory is created on the host's filesystem.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsMkdir(struct vnode *dvp, // IN: Vnode of directory to create directory in
+ char *dirname, // IN: Name of directory to create
+ struct vattr *vap, // IN: Attributes of new directory
+ struct vnode **vpp, // OUT: Set to point to vnode for new directory
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsMkdir(struct vnode *dvp, // IN: Vnode of directory to create directory in
+ char *dirname, // IN: Name of directory to create
+ struct vattr *vap, // IN: Attributes of new directory
+ struct vnode **vpp, // OUT: Set to point to vnode for new directory
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx, // IN: Context of caller
+ int flags, // IN: XXX
+ vsecattr_t *vsecp) // IN: XXX
+#endif
+{
+ HgfsSuperInfo *sip;
+ HgfsReq *req;
+ HgfsRequestCreateDir *request;
+ HgfsReplyCreateDir *reply;
+ char fullname[MAXPATHLEN + 1];
+ int ret;
+
+ if (!dvp || !dirname || !vap || !vpp) {
+ cmn_err(HGFS_ERROR, "HgfsMkdir: NULL input from Kernel.\n");
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsMkdir: dvp=%p (%s), dirname=%s, vap=%p, vpp=%p\n",
+ dvp, HGFS_VP_TO_FILENAME(dvp), dirname, vap,
+ *vpp);
+
+ /*
+ * We need to construct the full path of the directory to create then send
+ * a CREATE_DIR request. If successful we will create a vnode and fill in
+ * vpp with a pointer to it.
+ *
+ * Note that unlike in HgfsCreate(), *vpp is always NULL.
+ */
+
+ if (dvp->v_type != VDIR) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: must create directory in directory.\n");
+ return ENOTDIR;
+ }
+
+ /* Construct the complete path of the directory to create. */
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp),// Name of directory to create in
+ HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of name
+ dirname, // Name of file to create
+ fullname, // Buffer to write full name
+ sizeof fullname); // Size of this buffer
+
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsCreate: couldn't create full path name.\n");
+ return ENAMETOOLONG;
+ }
+
+ /* Get pointer to our Superinfo */
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return EIO;
+ }
+
+ /* Initialize the request's contents. */
+ request = (HgfsRequestCreateDir *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_CREATE_DIR);
+
+ request->permissions = (vap->va_mode & S_IRWXU) >> HGFS_ATTR_MODE_SHIFT;
+
+ ret = CPName_ConvertTo(fullname, MAXPATHLEN, request->fileName.name);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: cross-platform name is too long.\n");
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->fileName.length = ret;
+
+ /* Set the size of this request. */
+ req->packetSize = sizeof *request + request->fileName.length;
+
+ /* Send the request to guestd. */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* Request is destroyed in HgfsSubmitRequest() if necessary. */
+ goto out;
+ }
+
+ reply = (HgfsReplyCreateDir *)req->packet;
+
+ if (HgfsValidateReply(req, sizeof *reply) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: invalid reply received.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ /* Handled below switch. */
+ break;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: operation not permitted.\n");
+ ret = EACCES;
+ goto destroy_out;
+
+ case HGFS_STATUS_FILE_EXISTS:
+ DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: directory already exists.\n");
+ ret = EEXIST;
+ goto destroy_out;
+
+ default:
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ /* We now create the vnode for the new directory. */
+ ret = HgfsVnodeGet(vpp, sip, dvp->v_vfsp, fullname,
+ HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable);
+ if (ret) {
+ ret = EIO;
+ goto destroy_out;
+ }
+
+ ASSERT(*vpp); /* HgfsIget guarantees this. */
+ ret = 0;
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRmdir --
+ *
+ * Removes the specified name from the provided vnode. Sends a DELETE
+ * request by calling HgfsDelete() with the filename and correct opcode to
+ * indicate deletion of a directory.
+ *
+ * "Removes the directory pointed to by the supplied vnode." (Solaris
+ * Internals, p537)
+ *
+ * Results:
+ * Returns 0 on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsRmdir(struct vnode *vp, // IN: Dir containing dir to remove
+ char *nm, // IN: Name of directory to remove
+ struct vnode *cdir, // XXX?
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsRmdir(struct vnode *vp, // IN: Dir containing dir to remove
+ char *nm, // IN: Name of directory to remove
+ struct vnode *cdir, // XXX?
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx, // IN: Context of caller
+ int flags) // IN: XXX
+#endif
+{
+ HgfsSuperInfo *sip;
+ char fullpath[MAXPATHLEN + 1];
+ int ret;
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRmdir().\n");
+
+ if (!vp || !nm) {
+ cmn_err(HGFS_ERROR, "HgfsRmdir: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRmdir: vp=%p (%s), nm=%s, cdir=%p (%s)\n",
+ vp, (HGFS_VP_TO_FP(vp)) ? HGFS_VP_TO_FILENAME(vp) : "vp->v_data null",
+ nm, cdir, (HGFS_VP_TO_FP(cdir)) ? HGFS_VP_TO_FILENAME(cdir) : "cdir->v_data null");
+
+ /* A few checks to ensure we can remove the directory. */
+ if (vp->v_type != VDIR) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRmdir: provided parent is a file, not a directory.\n");
+ return ENOTDIR;
+ }
+
+ ASSERT(HGFS_KNOW_FILENAME(vp));
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return EIO;
+ }
+
+ /*
+ * We need to construct the full name of the directory to remove then call
+ * HgfsDelete with the proper opcode.
+ */
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp), // Name of directory to create in
+ HGFS_VP_TO_FILENAME_LENGTH(vp), // Length of name
+ nm, // Name of file to create
+ fullpath, // Buffer to write full name
+ sizeof fullpath); // Size of this buffer
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsRmdir: could not construct full name.\n");
+ return ENAMETOOLONG;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsRmdir: removing \"%s\".\n", fullpath);
+
+ /* We can now send the delete request. */
+ return HgfsDelete(sip, fullpath, HGFS_OP_DELETE_DIR);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsReaddir --
+ *
+ * Reads as many entries from the directory as will fit in to the provided
+ * buffer. Each directory entry is read by calling HgfsGetNextDirEntry().
+ *
+ * "The vop_readdir() method reads chunks of the directory into a uio
+ * structure. Each chunk can contain as many entries as will fit within
+ * the size supplied by the uio structure. The uio_resid structure member
+ * shows the size of the getdents request in bytes, which is divided by the
+ * size of the directory entry made by the vop_readdir() method to
+ * calculate how many directory entries to return." (Solaris Internals,
+ * p555)
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsReaddir(struct vnode *vp, // IN: Vnode of directory to read
+ struct uio *uiop, // IN: User's read request
+ struct cred *cr, // IN: Credentials of caller
+ int *eofp) // OUT: Indicates we are done
+#else
+static int
+HgfsReaddir(struct vnode *vp, // IN: Vnode of directory to read
+ struct uio *uiop, // IN: User's read request
+ struct cred *cr, // IN: Credentials of caller
+ int *eofp, // OUT: Indicates we are done
+ caller_context_t *ctx, // IN: Context of caller
+ int flags) // IN: XXX
+#endif
+{
+ HgfsSuperInfo *sip;
+ HgfsHandle handle;
+ struct dirent64 *dirp, *origdirp;
+ ssize_t readSize;
+ uint64_t offset;
+ Bool done;
+ int ret;
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir().\n");
+
+ if (!vp || !uiop || !cr || !eofp) {
+ cmn_err(HGFS_ERROR, "HgfsReaddir: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: uiop->uio_resid=%ld, "
+ "uiop->uio_loffset=%lld\n",
+ uiop->uio_resid, uiop->uio_loffset);
+
+
+ /*
+ * XXX: If would be nice if we could perform some sort of sanity check on
+ * the handle here. Perhaps make sure handle <= NUM_SEARCHES in
+ * hgfsServer.c since the handle is the index number in searchArray.
+ */
+ if ( !HGFS_KNOW_FILENAME(vp) ) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we don't know the filename.\n");
+ return EBADF;
+ }
+
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we can't get the superinfo.\n");
+ return EIO;
+ }
+ /*
+ * In order to fill the user's buffer with directory entries, we must
+ * iterate on HGFS_OP_SEARCH_READ requests until either the user's buffer is
+ * full or there are no more entries. Each call to HgfsGetNextDirEntry()
+ * fills in the name and attribute structure for the next entry. We then
+ * escape that name and place it in a kernel buffer that's the same size as
+ * the user's buffer. Once there are no more entries or no more room in the
+ * buffer, we copy it to user space.
+ */
+
+ /*
+ * XXX
+ * Note that I allocate a large buffer in kernel space so I can do only one
+ * copy to user space, otherwise we would need to do a copy for each
+ * directory entry. This approach is potentially bad since readSize is
+ * as big as the buffer the user called us with, and therefore in their
+ * control. (Actually, it's likely that the user can just say it has a
+ * huge buffer without really having it.) For this reason, I call
+ * kmem_zalloc() with the KM_NOSLEEP flag which fails if it cannot allocate
+ * memory rather than sleeping until it can (as KM_SLEEP does).
+ *
+ * This approach may want to be changed in the future.
+ */
+
+ readSize = uiop->uio_resid;
+ origdirp = dirp = (struct dirent64 *)kmem_zalloc(readSize, KM_NOSLEEP);
+ if (!origdirp) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: couldn't allocate memory.\n");
+ return ENOMEM;
+ }
+
+ /*
+ * We need to get the handle for this open directory to send to the Hgfs
+ * server in our requests.
+ */
+ ret = HgfsGetOpenFileHandle(vp, &handle);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: could not get handle.\n");
+ return EINVAL;
+ }
+
+
+ /*
+ * Loop until one of the following conditions is met:
+ * o An error occurs while reading a directory entry
+ * o There are no more directory entries to read
+ * o The buffer is full and cannot hold the next entry
+ *
+ * We request dentries from the Hgfs server based on their index in the
+ * directory. The offset value is initialized to the value specified in
+ * the user's io request and is incremented each time through the loop.
+ *
+ * dirp is incremented by the record length each time through the loop and
+ * is used to determine where in the kernel buffer we write to.
+ */
+ for (offset = uiop->uio_loffset, done = 0; /* Nothing */ ; offset++) {
+ char nameBuf[MAXNAMELEN + 1];
+ char escName[MAXNAMELEN + 1];
+ char fullName[MAXPATHLEN + 1];
+
+ DEBUG(VM_DEBUG_COMM,
+ "HgfsReaddir: getting directory entry at offset %"FMT64"u.\n", offset);
+
+ DEBUG(VM_DEBUG_HANDLE, "HgfsReaddir: ** handle=%d, file=%s\n",
+ handle, HGFS_VP_TO_FILENAME(vp));
+
+ ret = HgfsGetNextDirEntry(sip, handle, offset, nameBuf, &done);
+ /* If the filename was too long, we skip to the next entry ... */
+ if (ret == EOVERFLOW) {
+ continue;
+ /* ... but if another error occurred, we return that error code ... */
+ } else if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: failure occurred in HgfsGetNextDirEntry\n");
+ goto out;
+ /*
+ * ... and if there are no more entries, we set the end of file pointer
+ * and break out of the loop.
+ */
+ } else if (done == TRUE) {
+ DEBUG(VM_DEBUG_COMM, "HgfsReaddir: Done reading directory entries.\n");
+ *eofp = TRUE;
+ break;
+ }
+
+ /*
+ * We now have the directory entry, so we sanitize the name and try to
+ * put it in our buffer.
+ */
+ DEBUG(VM_DEBUG_COMM, "HgfsReaddir: received filename \"%s\"\n", nameBuf);
+
+ ret = HgfsEscape_Do(nameBuf, strlen(nameBuf), sizeof escName, escName);
+ /* If the escaped name didn't fit in the buffer, skip to the next entry. */
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: HgfsEscape_Do failed.\n");
+ continue;
+ }
+
+ /*
+ * Make sure there is enough room in the buffer for the entire directory
+ * entry. If not, we just break out of the loop and copy what we have.
+ */
+ if (DIRENT64_RECLEN(ret) > (readSize - ((uintptr_t)dirp - (uintptr_t)origdirp))) {
+ DEBUG(VM_DEBUG_INFO, "HgfsReaddir: ran out of room in the buffer.\n");
+ break;
+ }
+
+ /* Fill in the directory entry. */
+ dirp->d_reclen = DIRENT64_RECLEN(ret);
+ dirp->d_off = offset;
+ memcpy(dirp->d_name, escName, ret);
+ dirp->d_name[ret] = '\0';
+
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp), // Directorie's name
+ HGFS_VP_TO_FILENAME_LENGTH(vp), // Length
+ dirp->d_name, // Name of file
+ fullName, // Destination buffer
+ sizeof fullName); // Size of this buffer
+ /* Skip this entry if the full path was too long. */
+ if (ret < 0) {
+ continue;
+ }
+
+ /*
+ * Place the node id, which serves the purpose of inode number, for this
+ * filename directory entry. As long as we are using a dirent64, this is
+ * okay since ino_t is also a u_longlong_t.
+ */
+ HgfsNodeIdGet(&sip->fileHashTable, fullName, (uint32_t)ret,
+ &dirp->d_ino);
+
+ /* Advance to the location for the next directory entry */
+ dirp = (struct dirent64 *)((intptr_t)dirp + dirp->d_reclen);
+ }
+
+ /*
+ * Now that we've filled our buffer with as many dentries as fit, we copy it
+ * into the user's buffer.
+ */
+ ret = uiomove(origdirp, // Source buffer
+ ((uintptr_t)dirp - (uintptr_t)origdirp), // Size of this buffer
+ UIO_READ, // Read flag
+ uiop); // User's request struct
+
+ /*
+ * uiomove(9F) will have incremented the uio offset by the number of bytes
+ * written. We reset it here to the fs-specific offset in our directory so
+ * the next time we are called it is correct. (Note, this does not break
+ * anything and /is/ how this field is intended to be used.)
+ */
+ uiop->uio_loffset = offset;
+
+ DEBUG(VM_DEBUG_DONE, "HgfsReaddir: done (ret=%d, *eofp=%d).\n", ret, *eofp);
+out:
+ kmem_free(origdirp, readSize);
+ DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: exiting.\n");
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSymlink --
+ *
+ * "Creates a symbolic link between the two pathnames" (Solaris Internals,
+ * p538)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <=3
+static int
+HgfsSymlink(struct vnode *dvp, // IN: Directory to create link in
+ char *linkname, // IN: Name of link
+ struct vattr *vap, // IN: Attributes for the symlink
+ char *target, // IN: Name of target for symlink
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsSymlink(struct vnode *dvp, // IN: Directory to create link in
+ char *linkname, // IN: Name of link
+ struct vattr *vap, // IN: Attributes for the symlink
+ char *target, // IN: Name of target for symlink
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx, // IN: Context of caller
+ int flags) // IN: XXX
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsSymlink() NOTSUP.\n");
+
+ /*
+ * Hgfs doesn't support links.
+ */
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsReadlink --
+ *
+ * "Follows the symlink in the supplied vnode." (Solaris Internals, p537)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsReadlink(struct vnode *vp, // IN: Vnode for the symlink
+ struct uio *uiop, // IN: XXX User's request?
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsReadlink(struct vnode *vp, // IN: Vnode for the symlink
+ struct uio *uiop, // IN: XXX User's request?
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsReadlink() NOTSUP.\n");
+
+ /*
+ * Hgfs doesn't support links
+ */
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFsync --
+ *
+ * We don't map any memory so we can safely return success.
+ *
+ * "Flushes out any dirty pages for the supplied vnode." (Solaris
+ * Internals, p536)
+ *
+ * Results:
+ * Returns 0 on success and non-zero on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsFsync(struct vnode *vp, // IN: Vnode for the file to sync
+ int syncflag, // IN: XXX?
+ struct cred *cr) // IN: Credentials of the caller
+#else
+static int
+HgfsFsync(struct vnode *vp, // IN: Vnode for the file to sync
+ int syncflag, // IN: XXX?
+ struct cred *cr, // IN: Credentials of the caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsFsync().\n");
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsInactive --
+ *
+ * Frees a vnode that is no longer referenced. This is done by calling
+ * HgfsVnodePut() from hgfsState.c, which also cleans up our internal
+ * filesystem state.
+ *
+ * "Free resources and releases the supplied vnode. The file system can
+ * choose to destroy the vnode or put it onto an inactive list, which is
+ * managed by the file system implementation." (Solaris Internals, p536)
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static void
+HgfsInactive(struct vnode *vp, // IN: Vnode to operate on
+ struct cred *cr) // IN: Credentials of the caller
+#else
+static void
+HgfsInactive(struct vnode *vp, // IN: Vnode to operate on
+ struct cred *cr, // IN: Credentials of the caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ HgfsSuperInfo *sip;
+
+ if (!vp) {
+ cmn_err(HGFS_ERROR, "HgfsInactive: NULL input from Kernel.\n");
+ return;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsInactive().\n");
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ return;
+ }
+
+ /* We need the check and decrement of v_count to be atomic */
+ mutex_enter(&vp->v_lock);
+
+ if (vp->v_count > 1) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+
+ DEBUG(VM_DEBUG_LOAD, "--> decremented count of vnode %p to %d\n",
+ vp, vp->v_count);
+
+ /*
+ * XXX This should only ever happen for the root vnode with our new state
+ * organization.
+ */
+ if (vp != sip->rootVnode) {
+ DEBUG(VM_DEBUG_ALWAYS, "HgfsInactive: v_count of vnode for %s too high!\n",
+ HGFS_VP_TO_FILENAME(vp));
+ }
+ ASSERT(vp == sip->rootVnode);
+
+ } else {
+ mutex_exit(&vp->v_lock);
+
+ DEBUG(VM_DEBUG_LOAD, "--> freeing vnode %p - \"%s\"\n",
+ vp, HGFS_VP_TO_FILENAME(vp));
+
+ /* Deallocate this vnode. */
+ HgfsVnodePut(vp, &sip->fileHashTable);
+
+ }
+
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFid --
+ *
+ * Provide a unique file identifier for this vnode. Note that I have never
+ * seen this function called by the Kernel.
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsFid(struct vnode *vp, // IN: File to generate file identifier for
+ struct fid *fidp) // XXX: IN/OUT?: File identifier
+#else
+static int
+HgfsFid(struct vnode *vp, // IN: File to generate file identifier for
+ struct fid *fidp, // XXX: IN/OUT?: File identifier
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsFid().\n");
+
+ if (!vp || !fidp) {
+ cmn_err(HGFS_ERROR, "HgfsFid: NULL input from Kernel.\n");
+ }
+
+ /*
+ * Make sure we can fit our node id in the provided structure. This allows
+ * us to call memcpy() with the sizeof the source below.
+ */
+ if (sizeof fidp->fid_data < sizeof HGFS_VP_TO_NODEID(vp)) {
+ return EOVERFLOW;
+ }
+
+ memset(fidp, 0, sizeof *fidp);
+ memcpy(&fidp->fid_data, &HGFS_VP_TO_NODEID(vp), sizeof HGFS_VP_TO_NODEID(vp));
+ fidp->fid_len = sizeof HGFS_VP_TO_NODEID(vp);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRwlock --
+ *
+ * Acquires either a readers or writers lock.
+ *
+ * "Holds the reader/writer lock for the supplied vnode. This method is
+ * called for each vnode, with the rwflag set to 0 inside a read() system
+ * call and the rwflag set to 1 inside a write() at a time. Some file
+ * system implementations have opetions to ignore the writer lock inside
+ * vop_rwlock()." (Solaris Internals, p537)
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The file's readers/writers lock is held after this function.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION == 2
+static void
+HgfsRwlock(struct vnode *vp, // IN: Vnode to get lock for
+ int write_lock) // IN: Set if wants a write lock, cleared for read lock
+#elif HGFS_VFS_VERSION == 3
+static void
+HgfsRwlock(struct vnode *vp, // IN: Vnode to get lock for
+ int write_lock, // IN: Set for write lock, cleared for read lock
+ caller_context_t *context) // IN: Context of caller
+#else /* SOL11 */
+static int
+HgfsRwlock(struct vnode *vp, // IN: Vnode to get lock for
+ int write_lock, // IN: Set for write lock, cleared for read lock
+ caller_context_t *context) // IN: Context of caller
+#endif
+{
+ if (write_lock) {
+ rw_enter(HGFS_VP_TO_RWLOCKP(vp), RW_WRITER);
+ } else {
+ rw_enter(HGFS_VP_TO_RWLOCKP(vp), RW_READER);
+ }
+
+#if HGFS_VFS_VERSION == 5
+ return write_lock ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE;
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRwunlock --
+ *
+ * "Releases the reader/writer lock for the supplied vnode." (Solaris
+ * Internals, p537)
+ *
+ * Results:
+ * Void.
+ *
+ * Side effects:
+ * This file's readers/writer lock is unlocked.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION == 2
+static void
+HgfsRwunlock(struct vnode *vp, // IN: Vnode to release lock for
+ int write_lock) // IN: Set for write lock, cleared for read lock
+#else
+static void
+HgfsRwunlock(struct vnode *vp, // IN: Vnode to release lock for
+ int write_lock, // IN: Set for write lock, cleared for read lock
+ caller_context_t *context) // IN: Context of caller
+#endif
+{
+ //DEBUG(VM_DEBUG_ENTRY, "HgfsRwunlock().\n");
+
+ if (!vp) {
+ cmn_err(HGFS_ERROR, "HgfsRwunlock: NULL input from Kernel.\n");
+ return;
+ }
+
+ rw_exit(HGFS_VP_TO_RWLOCKP(vp));
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSeek --
+ *
+ * Checks to ensure that the specified offset is valid. Actual manipulation
+ * of the file position is handled by the Kernel.
+ *
+ * "Seeks within the supplied vnode." (Solaris Internals, p537)
+ *
+ * Results:
+ * Returns zero if this offset is valid and EINVAL if it isn't.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsSeek(struct vnode *vp, // IN: Vnode to seek within
+ offset_t ooff, // IN: Current offset in this vnode
+ offset_t *noffp) // IN/OUT: Requested new offset within file
+#else
+static int
+HgfsSeek(struct vnode *vp, // IN: Vnode to seek within
+ offset_t ooff, // IN: Current offset in this vnode
+ offset_t *noffp, // IN/OUT: Requested new offset within file
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsSeek().\n");
+
+ if (!noffp) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsSeek: noffp is NULL\n");
+ return EINVAL;
+ }
+
+ if (*noffp < 0) {
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_INFO, "HgfsSeek: file %s\n", HGFS_VP_TO_FILENAME(vp));
+ DEBUG(VM_DEBUG_INFO, "HgfsSeek: ooff %llu\n", ooff);
+ DEBUG(VM_DEBUG_INFO, "HgfsSeek: *noffp %llu\n", *noffp);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsCmp --
+ *
+ * Compares two vnodes to see if they are for the same file. Our
+ * filesystem-specific check is to compare the filenames, file type, and
+ * file flags. Since we keep vnodes per-open-file, rather than per-file,
+ * this function has significance.
+ *
+ * This function is invoked by the VN_CMP macro only if the two given
+ * pointers are different and each has the same operations (v_op).
+ *
+ * Results:
+ * TRUE if vnodes are the same, FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsCmp(struct vnode *vp1, // IN: First vnode
+ struct vnode *vp2) // IN: Second vnode
+#else
+static int
+HgfsCmp(struct vnode *vp1, // IN: First vnode
+ struct vnode *vp2, // IN: Second vnode
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsCmp: vp1=%p, vp2=%p.\n", vp1, vp2);
+
+ /*
+ * This function is only called if:
+ * ((vp1 != vp2) && (vp1->v_op == vp2->v_op))
+ *
+ * We also care if the filenames are the same.
+ */
+
+ if (vp1->v_type != vp2->v_type) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsCmp: %s != %s",
+ (vp1->v_type == VDIR) ? "VDIR" : "VREG",
+ (vp2->v_type == VDIR) ? "VDIR" : "VREG");
+ return FALSE;
+ }
+
+ if (vp1->v_flag != vp2->v_flag) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsCmp: flags: %x != %x\n", vp1->v_flag, vp2->v_flag);
+ return FALSE;
+ }
+
+ if (strcmp(HGFS_VP_TO_FILENAME(vp1), HGFS_VP_TO_FILENAME(vp2)) != 0) {
+ return FALSE;
+ }
+
+ DEBUG(VM_DEBUG_DONE, "HgfsCmp: for \"%s\", vp1 == vp2\n", HGFS_VP_TO_FILENAME(vp1));
+ return TRUE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFrlock --
+ *
+ * "Does file and record locking for the supplied vnode." (Solaris
+ * Internals, p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsFrlock(struct vnode *vp, // XXX
+ int cmd, // IN: Command to carry out
+ struct flock64 *bfp, // XXX
+ int flag, // XXX
+ offset_t offset, // XXX
+ struct flk_callback *flk_callbackp, // XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsFrlock(struct vnode *vp, // XXX
+ int cmd, // IN: Command to carry out
+ struct flock64 *bfp, // XXX
+ int flag, // XXX
+ offset_t offset, // XXX
+ struct flk_callback *flk_callbackp, // XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsFrlock() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSpace --
+ *
+ * "Frees space for the supplied vnode." (Solaris Internals, p538)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION == 2
+static int
+HgfsSpace(struct vnode *vp, // IN: Vnode to free space for
+ int cmd, // IN: XXX Command
+ struct flock64 *bfp, // IN: XXX
+ int flag, // IN: XXX
+ offset_t offset, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsSpace(struct vnode *vp, // IN: Vnode to free space for
+ int cmd, // IN: XXX Command
+ struct flock64 *bfp, // IN: XXX
+ int flag, // IN: XXX
+ offset_t offset, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsSpace() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRealvp --
+ *
+ * "Gets the real vnode from the supplied vnode." (Solaris Internals, p537)
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on error. On success, vpp
+ * is given the value of the real vnode. Currently this always returns
+ * success.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsRealvp(struct vnode *vp, // IN: Vnode to find real vnode for
+ struct vnode **vpp) // OUT: Set to point to real vnode
+#else
+static int
+HgfsRealvp(struct vnode *vp, // IN: Vnode to find real vnode for
+ struct vnode **vpp, // OUT: Set to point to real vnode
+ caller_context_t *ctx)
+#endif
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRealvp().\n");
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRealvp: vp=%p\n", vp);
+ DEBUG(VM_DEBUG_ENTRY, "HgfsRealvp: vp's name=%s\n", HGFS_VP_TO_FILENAME(vp));
+
+ /*
+ * Here we just supply the vnode we were provided. This behavior is correct
+ * since we maintain vnodes per-open-file rather than per-file. The "real"
+ * vnode /is/ the provided one since any other one belongs to a different
+ * "open" file.
+ */
+ *vpp = vp;
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetpage --
+ *
+ * HgfsRead() does not map file data into the Kernel's address space, so we
+ * shouldn't need to support this (that is, page faults will never occur).
+ *
+ * "Gets pages in the range offset and length for the vnode from the
+ * backing store of the file system. Does the real work of reading a
+ * vnode. This method is often called as a result of read(), which causes
+ * a page fault in seg_map, which calls vop_getpage()." (Solaris Internals,
+ * p536)
+ *
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsGetpage(struct vnode *vp, // IN: Vnode of file to get page for
+ offset_t off, // IN: Offset in file/page to retrieve
+ size_t len, // IN: Length of range to retrieve
+ uint_t *protp, // XXX
+ struct page **plarr, // XXX
+ size_t plsz, // IN: XXX
+ struct seg *seg, // XXX: Segment
+ caddr_t addr, // IN: Address of XXX
+ enum seg_rw rw, // IN: XXX
+ struct cred *cr) //IN Credentials of caller
+#else
+static int
+HgfsGetpage(struct vnode *vp, // IN: Vnode of file to get page for
+ offset_t off, // IN: Offset in file/page to retrieve
+ size_t len, // IN: Length of range to retrieve
+ uint_t *protp, // XXX
+ struct page **plarr, // XXX
+ size_t plsz, // IN: XXX
+ struct seg *seg, // XXX: Segment
+ caddr_t addr, // IN: Address of XXX
+ enum seg_rw rw, // IN: XXX
+ struct cred *cr, //IN Credentials of caller
+ caller_context_t *ctx)
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsGetpage() NOTSUP.\n");
+
+ /* We don't currently need this; see the comment above. */
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsPutpage --
+ *
+ * HgfsWrite() does not map file data into the Kernel's address space, so we
+ * shouldn't need to support this (that is, page faults will never occur).
+ *
+ * "Writes pages in the range offset and length for the vnode to the
+ * backing store of the file system. Does the real work of reading a
+ * vnode." (Solaris Internals, p537)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsPutpage(struct vnode *vp, // IN: Vnode of file to put page for
+ offset_t off, // IN: Offset to begin writing
+ size_t len, // IN: Amount of data to write
+ int flags, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsPutpage(struct vnode *vp, // IN: Vnode of file to put page for
+ offset_t off, // IN: Offset to begin writing
+ size_t len, // IN: Amount of data to write
+ int flags, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsPutpage() NOTSUP.\n");
+
+ /* We don't currently need this; see the comment above. */
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsMap --
+ *
+ * Each file has its VNOMAP flag set so this shouldn't be invoked. Most
+ * applications seem to handle this so, if this becomes a problem this
+ * function will need to be implemented.
+ *
+ * "Maps a range of pages into an address space by doing the appropriate
+ * checks and calline as_map()" (Solaris Internals, p537)
+ *
+ *
+ * Results:
+ * Returns 0 on success and a non-zero error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsMap(struct vnode *vp, // IN: Vnode of file to map page for
+ offset_t off, // IN: XXX
+ struct as *as, // IN: Address space
+ caddr_t *addrp, // IN: XXX: Address in this space?
+ size_t len, // IN: XXX:Length of area to map?
+ uchar_t prot, // IN: XXX
+ uchar_t maxprot, // IN: XXX
+ uint_t flags, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsMap(struct vnode *vp, // IN: Vnode of file to map page for
+ offset_t off, // IN: XXX
+ struct as *as, // IN: Address space
+ caddr_t *addrp, // IN: XXX: Address in this space?
+ size_t len, // IN: XXX:Length of area to map?
+ uchar_t prot, // IN: XXX
+ uchar_t maxprot, // IN: XXX
+ uint_t flags, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ /* We specify VNOMAP for each file, so this shouldn't be called. */
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsMap() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsAddmap --
+ *
+ * Since HgfsMap() above is ENOTSUP, this is not needed.
+ *
+ * "Increments the map count." (Solaris Internals, p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsAddmap(struct vnode *vp, // IN: Vnode to increment map count for
+ offset_t off, //XXX
+ struct as *as, // IN: Address space
+ caddr_t addrp, // IN: XXX: Address in this space?
+ size_t len, // IN: XXX: Length of this mapping?
+ uchar_t prot, // IN: XXX
+ uchar_t maxprot, // IN: XXX
+ uint_t flags, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsAddmap(struct vnode *vp, // IN: Vnode to increment map count for
+ offset_t off, //XXX
+ struct as *as, // IN: Address space
+ caddr_t addrp, // IN: XXX: Address in this space?
+ size_t len, // IN: XXX: Length of this mapping?
+ uchar_t prot, // IN: XXX
+ uchar_t maxprot, // IN: XXX
+ uint_t flags, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsAddmap() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDelmap --
+ *
+ * Since HgfsMap() above is ENOTSUP, this is not needed.
+ *
+ * "Decrements the map count." (Solaris Internals, p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsDelmap(struct vnode *vp, // IN: Vnode of file to decrement map count for
+ offset_t off, // XXX
+ struct as *as, // IN: Address space
+ caddr_t addr, // IN: XXX: Address in this space?
+ size_t len, // IN: XXX: Length of this mapping?
+ uint_t prot, // IN: XXX
+ uint_t maxprot, // IN: XXX
+ uint_t flags, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsDelmap(struct vnode *vp, // IN: Vnode of file to decrement map count for
+ offset_t off, // XXX
+ struct as *as, // IN: Address space
+ caddr_t addr, // IN: XXX: Address in this space?
+ size_t len, // IN: XXX: Length of this mapping?
+ uint_t prot, // IN: XXX
+ uint_t maxprot, // IN: XXX
+ uint_t flags, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx)
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsDelmap() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsPoll --
+ *
+ * We are using fs_poll() instead of this, which seems acceptable so far.
+ *
+ * Invoked when user calls poll(2) on a file in our filesystem.
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+/*
+int
+HgfsPoll(struct vnode *vp, // IN: Vnode of file to poll
+ short ev, // IN: Requested events
+ int any, // IN: Whether other file descriptors have had events
+ short *revp, // OUT: Filled in with events that have occurred
+ struct pollhead **phpp)// OUT: Set to a pollhead if necessary
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsPoll() NOTSUP.\n");
+
+ return ENOTSUP;
+}*/
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDump --
+ *
+ * "Dumps data when the kernel is in a frozen state." (Solaris Internals,
+ * p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsDump(struct vnode *vp, // IN: Vnode of XXX
+ caddr_t addr, // IN: XXX: Location to dump to?
+ int lbdn, // IN: XXX
+ int dblks) // IN: XXX
+#else
+static int
+HgfsDump(struct vnode *vp, // IN: Vnode of XXX
+ caddr_t addr, // IN: XXX: Location to dump to?
+ offset_t lbdn, // IN: XXX
+ offset_t dblks, // IN: XXX
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsDump() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsPathconf --
+ *
+ * "Establishes file system parameters with the pathconf system call."
+ * (Solaris Internals, p537)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsPathconf(struct vnode *vp, // IN: Vnode of file to establish parameters for
+ int cmd, // IN: Command
+ ulong_t *valp, // OUT: XXX: Returned value?
+ struct cred *cr) // IN: Credentials of caller
+#else
+static int
+HgfsPathconf(struct vnode *vp, // IN: Vnode of file to establish parameters for
+ int cmd, // IN: Command
+ ulong_t *valp, // OUT: XXX: Returned value?
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsPathconf() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsPageio --
+ *
+ * "Paged I/O supprt for file system swap files." (Solaris Internals, p537)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsPageio(struct vnode *vp,
+ struct page *pp,
+ u_offset_t io_off,
+ size_t io_len,
+ int flags,
+ struct cred *cr)
+#else
+static int
+HgfsPageio(struct vnode *vp,
+ struct page *pp,
+ u_offset_t io_off,
+ size_t io_len,
+ int flags,
+ struct cred *cr,
+ caller_context_t *ctx)
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsPageio() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDumpctl --
+ *
+ * "Prepares the file system before and after a dump" (Solaris Internals,
+ * p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsDumpctl(struct vnode *vp,
+ int action,
+ int *blkp)
+#else
+static int
+HgfsDumpctl(struct vnode *vp,
+ int action,
+ offset_t *blkp,
+ caller_context_t *ctx)
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsDumpctl() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDispose --
+ *
+ * Since we don't map any parts of files to pages, this isn't needed.
+ *
+ * "Frees the given page from the vnode." (Solaris Internals, p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static void
+HgfsDispose(struct vnode *vp, // IN: Vnode to free page for
+ struct page *pp, // IN: Page to free
+ int flag, // IN: XXX
+ int dn, // IN: XXX
+ struct cred *cr) // IN: Credentials of caller
+#else
+static void
+HgfsDispose(struct vnode *vp, // IN: Vnode to free page for
+ struct page *pp, // IN: Page to free
+ int flag, // IN: XXX
+ int dn, // IN: XXX
+ struct cred *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_ENTRY, "HgfsDispose().\n");
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSetsecattr --
+ *
+ * "Sets security access control list attributes." (Solaris Internals,
+ * p538)
+ *
+ * We almost certainly won't support this.
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION <= 3
+static int
+HgfsSetsecattr(struct vnode *vp,
+ vsecattr_t *vsap,
+ int flag,
+ struct cred *cr)
+#else
+static int
+HgfsSetsecattr(struct vnode *vp,
+ vsecattr_t *vsap,
+ int flag,
+ struct cred *cr,
+ caller_context_t *ctx)
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsSetsecattr() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetsecattr --
+ *
+ * We are using fs_fab_acl() instead of this, which seems to do the right
+ * thing.
+ *
+ * "Gets security access control list attributes" (Solaris Internals, p536)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+/*
+int
+HgfsGetsecattr(struct vnode *vp,
+ vsecattr_t *vsap,
+ int flag,
+ struct cred *cr)
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsGetsecattr() NOTSUP.\n");
+
+ return ENOTSUP;
+}*/
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsShrlock --
+ *
+ * "ONC shared lock support." (Solaris Internals, p538)
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION == 2
+static int
+HgfsShrlock(struct vnode *vp, // IN: Vnode of file to lock
+ int cmd, // IN: Command
+ struct shrlock *chr, // IN: Lock
+ int flag) // IN: Flags for XXX
+#elif HGFS_VFS_VERSION == 3
+static int
+HgfsShrlock(struct vnode *vp, // IN: Vnode of file to lock
+ int cmd, // IN: Command
+ struct shrlock *chr, // IN: Lock
+ int flag, // IN: Flags for XXX
+ cred_t *cr) // IN: Credentials of caller
+#else
+static int
+HgfsShrlock(struct vnode *vp, // IN: Vnode of file to lock
+ int cmd, // IN: Command
+ struct shrlock *chr, // IN: Lock
+ int flag, // IN: Flags for XXX
+ cred_t *cr, // IN: Credentials of caller
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsShrlock() NOTSUP.\n");
+
+ return ENOTSUP;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsVnevent --
+ *
+ * Handles an event for the provided vnode.
+ *
+ * Events can be VE_SUPPORT, VE_RENAME_SRC, VE_RENAME_DEST, VE_REMOVE,
+ * VE_RMDIR.
+ *
+ * Note that this function showed up at some point after Build 52 (02/2004)
+ * of Solaris 10 but before (or at) Build 58 (06/2004). We only compile
+ * this in if the driver is being built for Builds greater than 52.
+ * XXX We should find out what build this function first showed up on.
+ *
+ * Results:
+ * Returns zero on success and a non-zero error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#if HGFS_VFS_VERSION > 2
+#if HGFS_VFS_VERSION == 3
+static int
+HgfsVnevent(struct vnode *vp, // IN: Vnode the event is occuring to
+ vnevent_t event) // IN: Event that has occurred
+#else
+static int
+HgfsVnevent(struct vnode *vp, // IN: Vnode the event is occuring to
+ vnevent_t event, // IN: Event that has occurred
+ vnode_t *dvp, // IN: XXX
+ char *fnm, // IN: XXX
+ caller_context_t *ctx) // IN: Context of caller
+#endif
+{
+ DEBUG(VM_DEBUG_NOTSUP, "HgfsVnevent: ENOTSUP\n");
+
+ return ENOTSUP;
+}
+#endif
+
+
+/*
+ * Local vnode functions.
+ *
+ * (The rest of the functions in this file are only invoked by our code so they
+ * ASSERT() their pointer arguments.)
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDirOpen --
+ *
+ * Invoked when HgfsOpen() is called with a vnode of type VDIR.
+ *
+ * Sends a SEARCH_OPEN request to the Hgfs server.
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ struct vnode *vp) // IN: Vnode of directory to open
+{
+ int ret;
+ HgfsReq *req;
+ HgfsRequestSearchOpen *request;
+ HgfsReplySearchOpen *reply;
+
+ ASSERT(sip);
+ ASSERT(vp);
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsDirOpen: opening \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return EIO;
+ }
+
+ /* Set the correct header values */
+ request = (HgfsRequestSearchOpen *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SEARCH_OPEN);
+
+ /*
+ * Convert name to cross-platform and unescape. If the vnode is the root of
+ * our filesystem the Hgfs server expects an empty string.
+ */
+ ret = CPName_ConvertTo((HGFS_IS_ROOT_VNODE(sip, vp)) ? "" : HGFS_VP_TO_FILENAME(vp),
+ MAXPATHLEN, request->dirName.name);
+ if (ret < 0) {
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->dirName.length = ret;
+
+ req->packetSize = request->dirName.length + sizeof *request;
+
+ /* Submit the request to the Hgfs server */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest destroys the request if necessary */
+ goto out;
+ }
+
+ /* Our reply is in the request packet */
+ reply = (HgfsReplySearchOpen *)req->packet;
+
+ /* Perform basic validation of packet transfer */
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen(): invalid reply received.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsDirOpen: received reply for ID %d\n", reply->header.id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);
+ DEBUG(VM_DEBUG_COMM, " handle: %d\n", reply->search);
+
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ if (req->packetSize != sizeof *reply) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: incorrect packet size.\n");
+ ret = EIO;
+ goto destroy_out;
+ }
+ /* Success handled after switch. */
+ break;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: operation not permitted (%s).\n",
+ HGFS_VP_TO_FILENAME(vp));
+ ret = EACCES;
+ goto destroy_out;
+
+ default:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: default error (%s).\n",
+ HGFS_VP_TO_FILENAME(vp));
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ /* Set the search open handle for use in HgfsReaddir() */
+ ret = HgfsSetOpenFileHandle(vp, reply->search);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: couldn't assign handle=%d to %s\n",
+ reply->search, HGFS_VP_TO_FILENAME(vp));
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ ret = EINVAL;
+ goto destroy_out;
+ }
+
+ ret = 0; /* Return success */
+
+destroy_out:
+ /* Make sure we put the request back on the list */
+ HgfsDestroyReq(sip, req);
+out:
+ DEBUG(VM_DEBUG_DONE, "HgfsDirOpen: done\n");
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFileOpen --
+ *
+ * Invoked when HgfsOpen() is called with a vnode of type VREG. Sends
+ * a OPEN request to the Hgfs server.
+ *
+ * Note that this function doesn't need to handle creations since the
+ * HgfsCreate() entry point is called by the kernel for that.
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ struct vnode *vp, // IN: Vnode of file to open
+ int flag, // IN: Flags of open
+ int permissions) // IN: Permissions of open (only when creating)
+{
+ HgfsReq *req;
+ HgfsRequestOpen *request;
+ HgfsReplyOpen *reply;
+ int ret;
+
+ ASSERT(sip);
+ ASSERT(vp);
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsFileOpen: opening \"%s\"\n", HGFS_VP_TO_FILENAME(vp));
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetNewReq failed.\n");
+ return EIO;
+ }
+
+ request = (HgfsRequestOpen *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_OPEN);
+
+ /* Convert Solaris modes to Hgfs modes */
+ ret = HgfsGetOpenMode((uint32_t)flag);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetOpenMode failed.\n");
+ ret = EINVAL;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->mode = ret;
+ DEBUG(VM_DEBUG_COMM, "HgfsFileOpen: open mode is %x\n", request->mode);
+
+ /* Convert Solaris flags to Hgfs flags */
+ ret = HgfsGetOpenFlags((uint32_t)flag);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetOpenFlags failed.\n");
+ ret = EINVAL;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->flags = ret;
+ DEBUG(VM_DEBUG_COMM, "HgfsFileOpen: open flags are %x\n", request->flags);
+
+ request->permissions = (permissions & S_IRWXU) >> HGFS_ATTR_MODE_SHIFT;
+ DEBUG(VM_DEBUG_COMM, "HgfsFileOpen: permissions are %o\n", request->permissions);
+
+ /* Convert the file name to cross platform format. */
+ ret = CPName_ConvertTo(HGFS_VP_TO_FILENAME(vp), MAXPATHLEN, request->fileName.name);
+ if (ret < 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: CPName_ConvertTo failed.\n");
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+ request->fileName.length = ret;
+
+ /* Packet size includes the request and its payload. */
+ req->packetSize = request->fileName.length + sizeof *request;
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest will destroy the request if necessary. */
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: could not submit request.\n");
+ goto out;
+ }
+
+ reply = (HgfsReplyOpen *)req->packet;
+
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: request not valid.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ if (req->packetSize != sizeof *reply) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: size of reply is incorrect.\n");
+ ret = EIO;
+ goto destroy_out;
+ }
+
+ /* Success case is handled after switch. */
+ break;
+
+ case HGFS_STATUS_NO_SUCH_FILE_OR_DIR:
+ case HGFS_STATUS_INVALID_NAME:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: no such file \"%s\".\n",
+ HGFS_VP_TO_FILENAME(vp));
+ ret = ENOENT;
+ goto destroy_out;
+
+ case HGFS_STATUS_FILE_EXISTS:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: \"%s\" exists.\n",
+ HGFS_VP_TO_FILENAME(vp));
+ ret = EEXIST;
+ goto destroy_out;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: operation not permitted on %s.\n",
+ HGFS_VP_TO_FILENAME(vp));
+ ret = EACCES;
+ goto destroy_out;
+
+ case HGFS_STATUS_ACCESS_DENIED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: access denied on %s.\n",
+ HGFS_VP_TO_FILENAME(vp));
+ ret = EACCES;
+ goto destroy_out;
+
+ default:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: default/unknown error %d.\n",
+ reply->header.status);
+ ret = EACCES;
+ goto destroy_out;
+ }
+
+ /*
+ * We successfully received a reply, so we need to save the handle in
+ * this file's HgfsOpenFile and return success.
+ */
+ ret = HgfsSetOpenFileHandle(vp, reply->file);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: couldn't assign handle %d (%s)\n",
+ reply->file, HGFS_VP_TO_FILENAME(vp));
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ ret = EINVAL;
+ goto destroy_out;
+ }
+
+ ret = 0;
+
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDirClose --
+ *
+ * Invoked when HgfsClose() is called with a vnode of type VDIR.
+ *
+ * Sends an SEARCH_CLOSE request to the Hgfs server.
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ struct vnode *vp) // IN: Vnode of directory to close
+{
+ HgfsReq *req;
+ HgfsRequestSearchClose *request;
+ HgfsReplySearchClose *reply;
+ int ret;
+
+ ASSERT(sip);
+ ASSERT(vp);
+
+ req = HgfsGetNewReq(sip);
+ if (!sip) {
+ return EIO;
+ }
+
+ /*
+ * Prepare the request structure. Of note here is that the request is
+ * always the same size so we just set the packetSize to that.
+ */
+ request = (HgfsRequestSearchClose *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SEARCH_CLOSE);
+
+ /* Get this open file's handle, since that is what we want to close. */
+ ret = HgfsGetOpenFileHandle(vp, &request->search);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirClose: couldn't get handle for %s\n",
+ HGFS_VP_TO_FILENAME(vp));
+ ret = EINVAL;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+ req->packetSize = sizeof *request;
+
+ /* Submit the request to the Hgfs server */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest destroys the request if necessary */
+ goto out;
+ }
+
+ reply = (HgfsReplySearchClose *)req->packet;
+
+ /* Ensure reply was received correctly and is necessary size. */
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirClose: invalid reply received.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsDirClose: received reply for ID %d\n", reply->header.id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);
+
+ /* Ensure server was able to close directory. */
+ if (reply->header.status != HGFS_STATUS_SUCCESS) {
+ ret = EIO;
+ goto destroy_out;
+ }
+
+ /* Now clear this open file's handle for future use. */
+ ret = HgfsClearOpenFileHandle(vp);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDirClose: couldn't clear handle.\n");
+ ret = EINVAL;
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ /* The directory was closed successfully so we return success. */
+ ret = 0;
+
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsFileClose --
+ *
+ * Invoked when HgfsClose() is called with a vnode of type VREG.
+ *
+ * Sends a CLOSE request to the Hgfs server.
+ *
+ * Results:
+ * Returns zero on success and an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsFileClose(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ struct vnode *vp) // IN: Vnode of file to close
+{
+ HgfsReq *req;
+ HgfsRequestClose *request;
+ HgfsReplyClose *reply;
+ int ret;
+
+ ASSERT(sip);
+ ASSERT(vp);
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ ret = EIO;
+ goto out;
+ }
+
+ request = (HgfsRequestClose *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_CLOSE);
+
+ /* Tell the Hgfs server which handle to close */
+ ret = HgfsGetOpenFileHandle(vp, &request->file);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: couldn't get handle.\n");
+ ret = EINVAL;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ req->packetSize = sizeof *request;
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest will destroy the request if necessary. */
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: submit request failed.\n");
+ goto out;
+ }
+
+ if (HgfsValidateReply(req, sizeof *reply) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: reply was invalid.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ reply = (HgfsReplyClose *)req->packet;
+
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ /*
+ * We already verified the size of the reply above since this reply type
+ * only contains a header, so we just clear the handle and return success.
+ */
+ ret = HgfsClearOpenFileHandle(vp);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: couldn't clear handle.\n");
+ ret = EINVAL;
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ ret = 0;
+ goto destroy_out;
+
+ case HGFS_STATUS_INVALID_HANDLE:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: invalid handle error.\n");
+ ret = EPROTO;
+ goto destroy_out;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: operation not permitted error.\n");
+ ret = EACCES;
+ goto destroy_out;
+
+ default:
+ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: other/unknown error.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ DEBUG(VM_DEBUG_DONE, "HgfsFileClose: returning %d\n", ret);
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetNextDirEntry --
+ *
+ * Writes the name of the directory entry matching the handle and offset to
+ * nameOut. This requires sending a SEARCH_READ request.
+ *
+ * Results:
+ * Returns zero on success and an error code on error. The done value is
+ * set if there are no more directory entries.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ HgfsHandle handle, // IN: Handle for request
+ uint32_t offset, // IN: Offset
+ char *nameOut, // OUT: Location to write name
+ Bool *done) // OUT: Whether there are any more
+{
+ HgfsReq *req;
+ HgfsRequestSearchRead *request;
+ HgfsReplySearchRead *reply;
+ int ret;
+
+ DEBUG(VM_DEBUG_ENTRY,
+ "HgfsGetNextDirEntry: handle=%d, offset=%d.\n", handle, offset);
+
+ ASSERT(sip);
+ ASSERT(nameOut);
+ ASSERT(done);
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: couldn't get req.\n");
+ return EIO;
+ }
+
+ /*
+ * Fill out the search read request that will return a single directory
+ * entry for the provided handle at the given offset.
+ */
+ request = (HgfsRequestSearchRead *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SEARCH_READ);
+
+ request->search = handle;
+ request->offset = offset;
+
+ req->packetSize = sizeof *request;
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest will destroy the request if necessary. */
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: HgfsSubmitRequest failed.\n");
+ goto out;
+ }
+
+ reply = (HgfsReplySearchRead *)req->packet;
+
+ /* Validate the request state and ensure we have at least a header */
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: reply not valid.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsGetNextDirEntry: received reply for ID %d\n",
+ reply->header.id);
+ DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);
+
+ /* Now ensure the server didn't have an error */
+ if (reply->header.status != HGFS_STATUS_SUCCESS) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: server didn't return success (%d).\n",
+ reply->header.status);
+ ret = EINVAL;
+ goto destroy_out;
+ }
+
+ /* Make sure we got an entire reply (excluding filename) */
+ if (req->packetSize < sizeof *reply) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: server didn't provide entire reply.\n");
+ ret = EIO;
+ goto destroy_out;
+ }
+
+ /* See if there are no more filenames to read */
+ if (reply->fileName.length <= 0) {
+ DEBUG(VM_DEBUG_DONE, "HgfsGetNextDirEntry: no more directory entries.\n");
+ *done = TRUE;
+ ret = 0; /* return success */
+ goto destroy_out;
+ }
+
+ /* Make sure filename isn't too long */
+ if ((reply->fileName.length > MAXNAMELEN) ||
+ (reply->fileName.length > HGFS_PAYLOAD_MAX(reply)) ) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: filename is too long.\n");
+ ret = EOVERFLOW;
+ goto destroy_out;
+ }
+
+ /*
+ * Everything is all right, copy filename to caller's buffer. Note that
+ * Solaris directory entries don't need the attribute information in the
+ * reply.
+ */
+ memcpy(nameOut, reply->fileName.name, reply->fileName.length);
+ nameOut[reply->fileName.length] = '\0';
+ ret = 0;
+
+ DEBUG(VM_DEBUG_DONE, "HgfsGetNextDirEntry: done.\n");
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDoRead --
+ *
+ * Sends a single READ request to the Hgfs server and writes the contents
+ * into the user's buffer if successful.
+ *
+ * This function is called repeatedly by HgfsRead() with requests of size
+ * less than or equal to HGFS_IO_MAX.
+ *
+ * Note that we return the negative of an appropriate error code in this
+ * function so we can differentiate between success and failure. On success
+ * we need to return the number of bytes read, but Solaris' error codes are
+ * positive so we negate them before returning. If callers want to return
+ * these error codes to the Kernel, they will need to flip their sign.
+ *
+ * Results:
+ * Returns number of bytes read on success and a negative value on error.
+ *
+ * Side effects:
+ * On success, size bytes are written into the user's buffer.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsDoRead(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ HgfsHandle handle, // IN: Server's handle to read from
+ uint64_t offset, // IN: File offset to read at
+ uint32_t size, // IN: Number of bytes to read
+ uio_t *uiop) // IN: Defines user's read request
+{
+ HgfsReq *req;
+ HgfsRequestRead *request;
+ HgfsReplyRead *reply;
+ int ret;
+
+ ASSERT(sip);
+ ASSERT(uiop);
+ ASSERT(size <= HGFS_IO_MAX); // HgfsRead() should guarantee this
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsDoRead: entry.\n");
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return -EIO;
+ }
+
+ request = (HgfsRequestRead *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_READ);
+
+ /* Indicate which file, where in the file, and how much to read. */
+ request->file = handle;
+ request->offset = offset;
+ request->requiredSize = size;
+
+ req->packetSize = sizeof *request;
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /*
+ * We need to flip the sign of the return value to indicate error; see
+ * the comment in the function header. HgfsSubmitRequest() handles
+ * destroying the request if necessary, so we don't here.
+ */
+ ret = -ret;
+ goto out;
+ }
+
+ reply = (HgfsReplyRead *)req->packet;
+
+ /* Ensure we got an entire header. */
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDoRead: invalid reply received.\n");
+ ret = -EPROTO;
+ goto destroy_out;
+ }
+
+ if (reply->header.status != HGFS_STATUS_SUCCESS) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDoRead: request not completed successfully.\n");
+ ret = -EACCES;
+ goto destroy_out;
+ }
+
+ /*
+ * Now perform checks on the actualSize. There are three cases:
+ * o actualSize is less than or equal to size, which indicates success
+ * o actualSize is zero, which indicates the end of the file (and success)
+ * o actualSize is greater than size, which indicates a server error
+ */
+ if (reply->actualSize <= size) {
+ /* If we didn't get any data, we don't need to copy to the user. */
+ if (reply->actualSize == 0) {
+ goto success;
+ }
+
+ /* Perform the copy to the user */
+ ret = uiomove(reply->payload, reply->actualSize, UIO_READ, uiop);
+ if (ret) {
+ ret = -EIO;
+ goto destroy_out;
+ }
+
+ /* We successfully copied the payload to the user's buffer */
+ goto success;
+
+ } else {
+ /* We got too much data: server error. */
+ DEBUG(VM_DEBUG_FAIL, "HgfsDoRead: received too much data in payload.\n");
+ ret = -EPROTO;
+ goto destroy_out;
+ }
+
+
+success:
+ ret = reply->actualSize;
+ DEBUG(VM_DEBUG_DONE, "HgfsDoRead: successfully read %d bytes to user.\n", ret);
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDoWrite --
+ *
+ * Sends a single WRITE request to the Hgfs server with the contents of
+ * the user's buffer.
+ *
+ * This function is called repeatedly by HgfsWrite() with requests of size
+ * less than or equal to HGFS_IO_MAX.
+ *
+ * Note that we return the negative of an appropriate error code in this
+ * function so we can differentiate between success and failure. On success
+ * we need to return the number of bytes written, but Solaris' error codes are
+ * positive so we negate them before returning. If callers want to return
+ * these error codes to the kernel, they will need to flip their sign.
+ *
+ * Results:
+ * Returns number of bytes written on success and a negative value on error.
+ *
+ * Side effects:
+ * On success, size bytes are written to the file specified by the handle.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsDoWrite(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ HgfsHandle handle, // IN: Handle representing file to write to
+ int ioflag, // IN: Flags for write
+ uint64_t offset, // IN: Where in the file to begin writing
+ uint32_t size, // IN: How much data to write
+ uio_t *uiop) // IN: Describes user's write request
+{
+ HgfsReq *req;
+ HgfsRequestWrite *request;
+ HgfsReplyWrite *reply;
+ int ret;
+
+ ASSERT(sip);
+ ASSERT(uiop);
+ ASSERT(size <= HGFS_IO_MAX); // HgfsWrite() guarantees this
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return -EIO;
+ }
+
+ request = (HgfsRequestWrite *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_WRITE);
+
+ request->file = handle;
+ request->flags = 0;
+ request->offset = offset;
+ request->requiredSize = size;
+
+ if (ioflag & FAPPEND) {
+ DEBUG(VM_DEBUG_COMM, "HgfsDoWrite: writing in append mode.\n");
+ request->flags |= HGFS_WRITE_APPEND;
+ }
+
+ DEBUG(VM_DEBUG_COMM, "HgfsDoWrite: requesting write of %d bytes.\n", size);
+
+ /* Copy the data the user wants to write into the payload. */
+ ret = uiomove(request->payload, request->requiredSize, UIO_WRITE, uiop);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsDoWrite: uiomove(9F) failed copying data from user.\n");
+ ret = -EIO;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ /* We subtract one so request's 'char payload[1]' member isn't double counted. */
+ req->packetSize = sizeof *request + request->requiredSize - 1;
+
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /*
+ * As in HgfsDoRead(), we need to flip the sign of the error code
+ * returned by HgfsSubmitRequest().
+ */
+ DEBUG(VM_DEBUG_FAIL, "HgfsDoWrite: HgfsSubmitRequest failed.\n");
+ ret = -ret;
+ goto out;
+ }
+
+ reply = (HgfsReplyWrite *)req->packet;
+
+ if (HgfsValidateReply(req, sizeof reply->header) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDoWrite: invalid reply received.\n");
+ ret = -EPROTO;
+ goto destroy_out;
+ }
+
+ if (reply->header.status != HGFS_STATUS_SUCCESS) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDoWrite: write failed (status=%d).\n",
+ reply->header.status);
+ ret = -EACCES;
+ goto destroy_out;
+ }
+
+ if (req->packetSize != sizeof *reply) {
+ DEBUG(VM_DEBUG_FAIL,
+ "HgfsDoWrite: invalid size of reply on successful reply.\n");
+ ret = -EPROTO;
+ goto destroy_out;
+ }
+
+ /* The write was completed successfully, so return the amount written. */
+ ret = reply->actualSize;
+ DEBUG(VM_DEBUG_DONE, "HgfsDoWrite: wrote %d bytes.\n", ret);
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDelete --
+ *
+ * Sends a request to delete a file or directory.
+ *
+ * Results:
+ * Returns 0 on success or an error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsDelete(HgfsSuperInfo *sip, // IN: Superinfo
+ char *filename, // IN: Full name of file to remove
+ HgfsOp op) // IN: Hgfs operation this delete is for
+{
+ HgfsReq *req;
+ HgfsRequestDelete *request;
+ HgfsReplyDelete *reply;
+ int ret;
+
+ ASSERT(sip);
+ ASSERT(filename);
+ ASSERT((op == HGFS_OP_DELETE_FILE) || (op == HGFS_OP_DELETE_DIR));
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsDelete().\n");
+
+ req = HgfsGetNewReq(sip);
+ if (!req) {
+ return EIO;
+ }
+
+ /* Initialize the request's contents. */
+ request = (HgfsRequestDelete *)req->packet;
+ HGFS_INIT_REQUEST_HDR(request, req, op);
+
+ /* Convert filename to cross platform and unescape. */
+ ret = CPName_ConvertTo(filename, MAXPATHLEN, request->fileName.name);
+ if (ret < 0) {
+ ret = ENAMETOOLONG;
+ /* We need to set the request state to error before destroying. */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ goto destroy_out;
+ }
+
+ request->fileName.length = ret;
+
+ /* Set the size of our request. (XXX should this be - 1 for char[1]?) */
+ req->packetSize = sizeof *request + request->fileName.length;
+
+ DEBUG(VM_DEBUG_COMM, "HgfsDelete: deleting \"%s\"\n", filename);
+
+ /* Submit our request to guestd. */
+ ret = HgfsSubmitRequest(sip, req);
+ if (ret) {
+ /* HgfsSubmitRequest() handles destroying the request if necessary. */
+ goto out;
+ }
+
+ reply = (HgfsReplyDelete *)req->packet;
+
+ /* Check the request status and size of reply. */
+ if (HgfsValidateReply(req, sizeof *reply) != 0) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsDelete: invalid reply received.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ /* Return the appropriate value. */
+ switch (reply->header.status) {
+ case HGFS_STATUS_SUCCESS:
+ ret = 0;
+ break;
+
+ case HGFS_STATUS_OPERATION_NOT_PERMITTED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDelete: operation not permitted.\n");
+ ret = EACCES;
+ goto destroy_out;
+
+ case HGFS_STATUS_NOT_DIRECTORY:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDelete: not a directory.\n");
+ ret = ENOTDIR;
+ goto destroy_out;
+
+ case HGFS_STATUS_DIR_NOT_EMPTY:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDelete: directory not empty.\n");
+ ret = EEXIST;
+ goto destroy_out;
+
+ case HGFS_STATUS_NO_SUCH_FILE_OR_DIR:
+ case HGFS_STATUS_INVALID_NAME:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDelete: no such file or directory.\n");
+ ret = ENOENT;
+ goto destroy_out;
+ case HGFS_STATUS_ACCESS_DENIED:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDelete: access denied.\n");
+
+ /* XXX: Add retry behavior after removing r/o bit. */
+ ret = EACCES;
+ goto destroy_out;
+ default:
+ DEBUG(VM_DEBUG_FAIL, "HgfsDelete: default error.\n");
+ ret = EPROTO;
+ goto destroy_out;
+ }
+
+ DEBUG(VM_DEBUG_DONE, "HgfsDelete: done.\n");
+
+destroy_out:
+ HgfsDestroyReq(sip, req);
+out:
+ return ret;
+}
+
+
+/*
+ * Function(s) exported to Solaris Hgfs code
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsGetSuperInfo --
+ *
+ * Provides a pointer to the superinfo structure as long as the filesystem
+ * is mounted.
+ *
+ * Results:
+ * Pointer to the superinfo on success, NULL on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+inline HgfsSuperInfo *
+HgfsGetSuperInfo()
+{
+ HgfsSuperInfo *sip;
+
+ /*
+ * The superInfoHead and hgfsInstance are global variables set by the device
+ * have of the driver at attach-time (HgfsDevAttach()). We access the soft
+ * state from the filesystem in this way to ensure consistency with the
+ * driver.
+ */
+ sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, hgfsInstance);
+ if (!sip) {
+ return NULL;
+ }
+
+ /* Only return the superinfo if the filesystem is mounted. */
+ return (sip->vfsp) ? sip : NULL;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSetVnodeOps --
+ *
+ * Sets the vnode operations for the provided vnode.
+ *
+ * Solaris 9:
+ * We just set the vnode's operations field to point to our statically
+ * created array of entry points.
+ *
+ * Solaris 10:
+ * The first time this function is called it will create the vnode
+ * operations and place a pointer to them in the superinfo structure. On
+ * subsequent invocations the same operations structure will be assigned to
+ * vnodes.
+ *
+ * Results:
+ * Returns zero on success and a non-zero error code on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+HgfsSetVnodeOps(HgfsSuperInfo *sip, // IN: Superinfo pointer
+ struct vnode *vp) // IN: vnode for this file
+#if HGFS_VFS_VERSION == 2
+{
+ ASSERT(vp);
+
+ vp->v_op = &HgfsVnodeOps;
+ return 0;
+}
+#else
+{
+ int ret;
+
+ ASSERT(sip);
+ ASSERT(vp);
+
+ /* See if we need to create the vnode operations structure */
+ if (!sip->vnodeOps) {
+ ret = HgfsMakeVnodeOps(sip);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsSetVnodeOps: couldn't create vnode ops.\n");
+ return HGFS_ERR;
+ } else {
+ DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: made vnode ops.\n");
+ }
+ }
+
+ if (vn_getops(vp) == sip->vnodeOps) {
+ DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: vnode ops already set.\n");
+ } else {
+ DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: we had to set the vnode ops.\n");
+ /* Set the operations for this vnode */
+ vn_setops(vp, sip->vnodeOps);
+ }
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsMakeVnodeOps --
+ *
+ * Registers our vnode operations with the kernel. After this function
+ * completes, all calls to vn_alloc() for our filesystem should return vnodes
+ * with the correct operations.
+ *
+ * The operation structure allocated here will be freed in
+ * HgfsFreevfs() which is called by the Kernel when the filesystem is
+ * unmounted.
+ *
+ * Results:
+ * Return 0 on success and non-zero on failure.
+ *
+ * Side effects:
+ * The kernel allocates memory for our operations structure.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsMakeVnodeOps(HgfsSuperInfo *sip) // IN: Superinfo
+{
+ int ret;
+ fs_operation_def_t vnodeOpsArr[] = {
+ HGFS_VOP(VOPNAME_OPEN, vop_open, HgfsOpen),
+ HGFS_VOP(VOPNAME_CLOSE, vop_close, HgfsClose),
+ HGFS_VOP(VOPNAME_READ, vop_read, HgfsRead),
+ HGFS_VOP(VOPNAME_WRITE, vop_write, HgfsWrite),
+ HGFS_VOP(VOPNAME_IOCTL, vop_ioctl, HgfsIoctl),
+ HGFS_VOP(VOPNAME_SETFL, vop_setfl, fs_setfl),
+ HGFS_VOP(VOPNAME_GETATTR, vop_getattr, HgfsGetattr),
+ HGFS_VOP(VOPNAME_SETATTR, vop_setattr, HgfsSetattr),
+ HGFS_VOP(VOPNAME_ACCESS, vop_access, HgfsAccess),
+ HGFS_VOP(VOPNAME_LOOKUP, vop_lookup, HgfsLookup),
+ HGFS_VOP(VOPNAME_CREATE, vop_create, HgfsCreate),
+ HGFS_VOP(VOPNAME_REMOVE, vop_remove, HgfsRemove),
+ HGFS_VOP(VOPNAME_LINK, vop_link, HgfsLink),
+ HGFS_VOP(VOPNAME_RENAME, vop_rename, HgfsRename),
+ HGFS_VOP(VOPNAME_MKDIR, vop_mkdir, HgfsMkdir),
+ HGFS_VOP(VOPNAME_RMDIR, vop_rmdir, HgfsRmdir),
+ HGFS_VOP(VOPNAME_READDIR, vop_readdir, HgfsReaddir),
+ HGFS_VOP(VOPNAME_SYMLINK, vop_symlink, HgfsSymlink),
+ HGFS_VOP(VOPNAME_READLINK, vop_readlink, HgfsReadlink),
+ HGFS_VOP(VOPNAME_FSYNC, vop_fsync, HgfsFsync),
+#if HGFS_VFS_VERSION <= 3
+ HGFS_VOP(VOPNAME_INACTIVE, vop_inactive, (fs_generic_func_p)HgfsInactive),
+ HGFS_VOP(VOPNAME_RWLOCK, vop_rwlock, (fs_generic_func_p)HgfsRwlock),
+ HGFS_VOP(VOPNAME_RWUNLOCK, vop_rwunlock, (fs_generic_func_p)HgfsRwunlock),
+ HGFS_VOP(VOPNAME_MAP, vop_map, (fs_generic_func_p)HgfsMap),
+ HGFS_VOP(VOPNAME_ADDMAP, vop_addmap, (fs_generic_func_p)HgfsAddmap),
+ HGFS_VOP(VOPNAME_POLL, vop_poll, (fs_generic_func_p)fs_poll),
+ HGFS_VOP(VOPNAME_DISPOSE, vop_dispose, (fs_generic_func_p)HgfsDispose),
+#else
+ HGFS_VOP(VOPNAME_INACTIVE, vop_inactive, HgfsInactive),
+ HGFS_VOP(VOPNAME_RWLOCK, vop_rwlock, HgfsRwlock),
+ HGFS_VOP(VOPNAME_RWUNLOCK, vop_rwunlock, HgfsRwunlock),
+ HGFS_VOP(VOPNAME_MAP, vop_map, HgfsMap),
+ HGFS_VOP(VOPNAME_ADDMAP, vop_addmap, HgfsAddmap),
+ HGFS_VOP(VOPNAME_POLL, vop_poll, fs_poll),
+ HGFS_VOP(VOPNAME_DISPOSE, vop_dispose, HgfsDispose),
+#endif
+ HGFS_VOP(VOPNAME_FID, vop_fid, HgfsFid),
+ HGFS_VOP(VOPNAME_SEEK, vop_seek, HgfsSeek),
+ HGFS_VOP(VOPNAME_CMP, vop_cmp, HgfsCmp),
+ HGFS_VOP(VOPNAME_FRLOCK, vop_frlock, HgfsFrlock),
+ HGFS_VOP(VOPNAME_SPACE, vop_space, HgfsSpace),
+ HGFS_VOP(VOPNAME_REALVP, vop_realvp, HgfsRealvp),
+ HGFS_VOP(VOPNAME_GETPAGE, vop_getpage, HgfsGetpage),
+ HGFS_VOP(VOPNAME_PUTPAGE, vop_putpage, HgfsPutpage),
+ HGFS_VOP(VOPNAME_DELMAP, vop_delmap, HgfsDelmap),
+ HGFS_VOP(VOPNAME_DUMP, vop_dump, HgfsDump),
+ HGFS_VOP(VOPNAME_PATHCONF, vop_pathconf, HgfsPathconf),
+ HGFS_VOP(VOPNAME_PAGEIO, vop_pageio, HgfsPageio),
+ HGFS_VOP(VOPNAME_DUMPCTL, vop_dumpctl, HgfsDumpctl),
+ HGFS_VOP(VOPNAME_GETSECATTR, vop_getsecattr, fs_fab_acl),
+ HGFS_VOP(VOPNAME_SETSECATTR, vop_setsecattr, HgfsSetsecattr),
+ HGFS_VOP(VOPNAME_SHRLOCK, vop_shrlock, HgfsShrlock),
+#if HGFS_VFS_VERSION > 2
+ HGFS_VOP(VOPNAME_VNEVENT, vop_vnevent, HgfsVnevent),
+#endif
+#if HGFS_VFS_VERSION <= 3
+ { NULL, NULL}
+#else
+ { NULL, { NULL } }
+#endif
+ };
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsMakeVnodeOps: making vnode ops.\n");
+
+ /*
+ * Create a vnodeops structure and register it with the kernel. We save the
+ * operations structure in the superinfo so it can be assigned in the
+ * future.
+ */
+ ret = vn_make_ops(HGFS_FS_NAME, vnodeOpsArr, &sip->vnodeOps);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsMakeVnodeOps: vn_make_ops returned %d\n", ret);
+ return HGFS_ERR;
+ }
+
+ DEBUG(VM_DEBUG_DONE, "HgfsMakeVnodeOps: sip->vnodeOps=%p\n", sip->vnodeOps);
+
+ return 0;
+}
+#endif
+
+
+/*
+ * Local utility functions.
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSubmitRequest --
+ *
+ * Places a request on the queue for submission to guestd, then waits for
+ * the response.
+ *
+ * Both submitting request and waiting for reply are in this function
+ * because the signaling of the request list's condition variable and
+ * waiting on the request's condition variable must be atomic.
+ *
+ * Results:
+ * Returns zero on success, and an appropriate error code on error.
+ * Note: EINTR is returned if cv_wait_sig() is interrupted.
+ *
+ * Side effects:
+ * The request list's condition variable is signaled.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsSubmitRequest(HgfsSuperInfo *sip, // IN: Superinfo containing request list,
+ // condition variable, and mutex
+ HgfsReq *req) // IN: Request to submit
+{
+ ASSERT(sip);
+ ASSERT(req);
+
+ /* The device must be open to submit requests */
+ if (!sip->devOpen) {
+ /*
+ * Destroy the request here to make things simpler for callers: they
+ * only need to destroy the request if they get a reply.
+ */
+ HgfsReqSetState(req, HGFS_REQ_ERROR);
+ HgfsDestroyReq(sip, req);
+ return ENODEV;
+ }
+
+ /*
+ * The process of submitting the request involves putting it on the request
+ * list, waking up guestd if it is waiting for a request, then atomically
+ * waiting for the reply. We must loop on cv_wait_sig(9F) because Solaris
+ * says it may return prematurely. We must signify that we have abandoned
+ * the request if we are interrupted.
+ */
+
+ mutex_enter(&sip->reqMutex);
+
+ HgfsEnqueueRequest(sip, req);
+
+ /*
+ * We unconditionally signal the request list's condition variable because
+ * if guestd is waiting then we want to wake it up and if guestd isn't
+ * waiting then this has no effect.
+ *
+ * We also call pollwakeup to let callers of poll(2) on the device know
+ * there is now something to read. XXX: This is bad because a caller of
+ * poll(2) could end up blocked in read(2) if operations are interleaved
+ * just so. For now we only allow the device to be opened once to prevent
+ * this.
+ */
+ cv_signal(&sip->reqCondVar);
+ /*
+ * XXX: This will wake any process waiting for a POLLRDNORM event on our
+ * device. We should probably change this so HgfsDevChpoll() tells us what
+ * event to signal since it knows what guestd is waiting for. This would
+ * prevent having to change this macro should guestd or the poll()
+ * implementation ever change. It seems like that would be a bit messy
+ * though, so we've left this as is.
+ *
+ * We release and reacquire the request mutex around the call to
+ * pollwakeup() since HgfsDevChpoll() also acquires it, and that's not
+ * allowed (see chpoll(9E) man page). This is completely safe since nothing
+ * bad can happen to the request by allowing other code to run; we release
+ * this same lock in the loop below when we call cv_wait_sig().
+ */
+ mutex_exit(&sip->reqMutex);
+ pollwakeup(&sip->hgfsPollhead, POLLRDNORM);
+ mutex_enter(&sip->reqMutex);
+
+
+ while (HgfsReqGetState(req) == HGFS_REQ_SUBMITTED) {
+ k_sigset_t oldIgnoreSet;
+
+ //DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest: currproc is %s.\n", u.u_comm);
+
+ HgfsDisableSignals(&oldIgnoreSet);
+
+ if (cv_wait_sig(&req->condVar, &sip->reqMutex) == 0) {
+ /*
+ * We received a system signal (e.g., SIGKILL) while waiting for the
+ * reply from guestd.
+ *
+ * Since we gave up the mutex while waiting on the condition
+ * variable, we must make sure the reply didn't come /after/ we were
+ * signaled but /before/ we reacquired the mutex. We do this by
+ * checking the state to make sure it is still SUBMITTED. (Note that
+ * this case should be quite rare, but is possible.)
+ *
+ * If the reply has come, we ignore it (since we were interrupted) and
+ * clean up the request. Otherwise we set the state to ABANDONED so
+ * the device half knows we are no longer waiting for the reply and it
+ * can clean up for us.
+ */
+ HgfsRestoreSignals(&oldIgnoreSet);
+
+ DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest(): interrupted while waiting for reply.\n");
+
+ if (HgfsReqGetState(req) != HGFS_REQ_SUBMITTED) {
+ /* It it's not SUBMITTED, it must be COMPLETED or ERROR */
+ ASSERT((HgfsReqGetState(req) == HGFS_REQ_COMPLETED) ||
+ (HgfsReqGetState(req) == HGFS_REQ_ERROR));
+
+ DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): destroying request.\n");
+ HgfsDestroyReq(sip, req);
+ } else {
+ DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): setting request state to abandoned.\n");
+ HgfsReqSetState(req, HGFS_REQ_ABANDONED);
+ }
+
+ mutex_exit(&sip->reqMutex);
+ return EINTR;
+ }
+
+ HgfsRestoreSignals(&oldIgnoreSet);
+ }
+
+ /* The reply should now be in req->packet. */
+ DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest(): awoken because reply received.\n");
+
+ mutex_exit(&sip->reqMutex);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsValidateReply --
+ *
+ * Validates a reply to ensure that its state is set appropriately and the
+ * reply is at least the minimum expected size and not greater than the
+ * maximum allowed packet size.
+ *
+ * Results:
+ * Returns zero on success, and a non-zero on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsValidateReply(HgfsReq *req, // IN: Request that contains reply data
+ uint32_t minSize) // IN: Minimum size expected for the reply
+{
+ ASSERT(req);
+ ASSERT(minSize <= HGFS_PACKET_MAX); /* we want to know if this fails */
+
+ switch (HgfsReqGetState(req)) {
+ case HGFS_REQ_ERROR:
+ DEBUG(VM_DEBUG_FAIL, "HgfsValidateReply(): received reply with error.\n");
+ return -1;
+
+ case HGFS_REQ_COMPLETED:
+ if ((req->packetSize < minSize) || (req->packetSize > HGFS_PACKET_MAX)) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsValidateReply(): successfully "
+ "completed reply is too small/big: !(%d < %d < %d).\n",
+ minSize, req->packetSize, HGFS_PACKET_MAX);
+ return -1;
+ } else {
+ return 0;
+ }
+ /*
+ * If we get here then there is a programming error in this module:
+ * HGFS_REQ_UNUSED should be for requests in the free list
+ * HGFS_REQ_SUBMITTED should be for requests only that are awaiting
+ * a response
+ * HGFS_REQ_ABANDONED should have returned an error to the client
+ */
+ default:
+ NOT_REACHED();
+ return -1; /* avoid compiler warning */
+ }
+}
+
+
+/*
+ * XXX
+ * These were taken and slightly modified from hgfs/driver/linux/driver.c.
+ * Should we move them into a hgfs/driver/posix/driver.c?
+ */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsGetOpenMode --
+ *
+ * Based on the flags requested by the process making the open()
+ * syscall, determine which open mode (access type) to request from
+ * the server.
+ *
+ * Results:
+ * Returns the correct HgfsOpenMode enumeration to send to the
+ * server, or -1 on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsGetOpenMode(uint32 flags) // IN: Open flags
+{
+#ifdef sun
+ /*
+ * Sun uses different values in the kernel. These are defined in
+ * <sys/file.h>.
+ */
+ #undef O_RDONLY
+ #undef O_WRONLY
+ #undef O_RDWR
+
+ #define O_RDONLY FREAD
+ #define O_WRONLY FWRITE
+ #define O_RDWR (FREAD | FWRITE)
+#endif
+
+ uint32 mask = O_RDONLY|O_WRONLY|O_RDWR;
+ int result = -1;
+
+#ifndef sun
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenMode: entered\n"));
+#else
+ DEBUG(VM_DEBUG_LOG, "HgfsGetOpenMode: entered\n");
+#endif
+
+ /*
+ * Mask the flags to only look at the access type.
+ */
+ flags &= mask;
+
+ /* Pick the correct HgfsOpenMode. */
+ switch (flags) {
+
+ case O_RDONLY:
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenMode: O_RDONLY\n");
+ result = HGFS_OPEN_MODE_READ_ONLY;
+ break;
+
+ case O_WRONLY:
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenMode: O_WRONLY\n");
+ result = HGFS_OPEN_MODE_WRITE_ONLY;
+ break;
+
+ case O_RDWR:
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenMode: O_RDWR\n");
+ result = HGFS_OPEN_MODE_READ_WRITE;
+ break;
+
+ default:
+ /* This should never happen. */
+ NOT_REACHED();
+#ifndef sun
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenMode: invalid open flags %o\n",
+ flags));
+#else
+ DEBUG(VM_DEBUG_LOG, "HgfsGetOpenMode: invalid open flags %o\n", flags);
+#endif
+ result = -1;
+ break;
+ }
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsGetOpenFlags --
+ *
+ * Based on the flags requested by the process making the open()
+ * syscall, determine which flags to send to the server to open the
+ * file.
+ *
+ * Results:
+ * Returns the correct HgfsOpenFlags enumeration to send to the
+ * server, or -1 on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsGetOpenFlags(uint32 flags) // IN: Open flags
+{
+#ifdef sun
+ /*
+ * Sun uses different values inside the kernel. These are defined in
+ * <sys/file.h>.
+ */
+ #undef O_CREAT // Must undef b/c included <sys/fs_subr.h>
+ #undef O_TRUNC
+ #undef O_EXCL
+
+ #define O_CREAT FCREAT
+ #define O_TRUNC FTRUNC
+ #define O_EXCL FEXCL
+#endif
+
+ uint32 mask = O_CREAT | O_TRUNC | O_EXCL;
+ int result = -1;
+
+#ifndef sun
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: entered\n"));
+#else
+ DEBUG(VM_DEBUG_INFO, "HgfsGetOpenFlags: entered\n");
+#endif
+
+ /*
+ * Mask the flags to only look at O_CREAT, O_EXCL, and O_TRUNC.
+ */
+
+ flags &= mask;
+
+ /* O_EXCL has no meaning if O_CREAT is not set. */
+ if (!(flags & O_CREAT)) {
+ flags &= ~O_EXCL;
+ }
+
+ /* Pick the right HgfsOpenFlags. */
+ switch (flags) {
+
+ case 0:
+ /* Regular open; fails if file nonexistant. */
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: 0\n");
+ result = HGFS_OPEN;
+ break;
+
+ case O_CREAT:
+ /* Create file; if it exists already just open it. */
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_CREAT\n");
+ result = HGFS_OPEN_CREATE;
+ break;
+
+ case O_TRUNC:
+ /* Truncate existing file; fails if nonexistant. */
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_TRUNC\n");
+ result = HGFS_OPEN_EMPTY;
+ break;
+
+ case (O_CREAT | O_EXCL):
+ /* Create file; fail if it exists already. */
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_CREAT | O_EXCL\n");
+ result = HGFS_OPEN_CREATE_SAFE;
+ break;
+
+ case (O_CREAT | O_TRUNC):
+ /* Create file; if it exists already, truncate it. */
+ DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_CREAT | O_TRUNC\n");
+ result = HGFS_OPEN_CREATE_EMPTY;
+ break;
+
+ default:
+ /*
+ * This can only happen if all three flags are set, which
+ * conceptually makes no sense because O_EXCL and O_TRUNC are
+ * mutually exclusive if O_CREAT is set.
+ *
+ * However, the open(2) man page doesn't say you can't set all
+ * three flags, and certain apps (*cough* Nautilus *cough*) do
+ * so. To be friendly to those apps, we just silenty drop the
+ * O_TRUNC flag on the assumption that it's safer to honor
+ * O_EXCL.
+ */
+#ifndef sun
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: invalid open "
+ "flags %o. Ignoring the O_TRUNC flag.\n", flags));
+#else
+ DEBUG(VM_DEBUG_INFO, "HgfsGetOpenFlags: invalid open flags %o. "
+ "Ignoring the O_TRUNC flag.\n", flags);
+#endif
+ result = HGFS_OPEN_CREATE_SAFE;
+ break;
+ }
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsAttrToSolaris --
+ *
+ * Maps Hgfs attributes to Solaris attributes, filling the provided Solaris
+ * attribute structure appropriately.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+HgfsAttrToSolaris(struct vnode *vp, // IN: The vnode for this file
+ const HgfsAttr *hgfsAttr, // IN: Hgfs attributes to copy from
+ struct vattr *solAttr) // OUT: Solaris attributes to fill
+{
+ ASSERT(vp);
+ ASSERT(hgfsAttr);
+ ASSERT(solAttr);
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsAttrToSolaris: %p -> %p", hgfsAttr, solAttr);
+
+ /*
+ * We only fill in those fields that va_mask tells us to.
+ */
+
+ if (solAttr->va_mask & AT_TYPE) {
+ /* Set the file type. */
+ switch (hgfsAttr->type) {
+ case HGFS_FILE_TYPE_REGULAR:
+ solAttr->va_type = VREG;
+ DEBUG(VM_DEBUG_ATTR, " Type: VREG\n");
+ break;
+
+ case HGFS_FILE_TYPE_DIRECTORY:
+ solAttr->va_type = VDIR;
+ DEBUG(VM_DEBUG_ATTR, " Type: VDIR\n");
+ break;
+
+ default:
+ /*
+ * There are only the above two filetypes. If there is an error
+ * elsewhere that provides another value, we set the Solaris type to
+ * none and ASSERT in devel builds.
+ */
+ solAttr->va_type = VNON;
+ DEBUG(VM_DEBUG_FAIL, "HgfsAttrToSolaris: invalid HgfsFileType provided.\n");
+ ASSERT(0);
+ }
+ }
+
+ if (solAttr->va_mask & AT_MODE) {
+ /* We only have permissions for owners. */
+ solAttr->va_mode = (hgfsAttr->permissions << HGFS_ATTR_MODE_SHIFT);
+ DEBUG(VM_DEBUG_ATTR, " Owner's permissions: %o\n",
+ solAttr->va_mode >> HGFS_ATTR_MODE_SHIFT);
+ }
+
+ if (solAttr->va_mask & AT_UID) {
+ DEBUG(VM_DEBUG_ATTR, " Setting uid\n");
+ solAttr->va_uid = 0; /* XXX root? */
+ }
+
+ if (solAttr->va_mask & AT_GID) {
+ DEBUG(VM_DEBUG_ATTR, " Setting gid\n");
+ solAttr->va_gid = 0; /* XXX root? */
+ }
+
+ if (solAttr->va_mask & AT_FSID) {
+ DEBUG(VM_DEBUG_ATTR, " Setting fsid\n");
+ solAttr->va_fsid = vp->v_vfsp->vfs_dev;
+ }
+
+ if (solAttr->va_mask & AT_NODEID) {
+ /* Get the node id calculated for this file in HgfsVnodeGet() */
+ solAttr->va_nodeid = HGFS_VP_TO_NODEID(vp);
+ DEBUG(VM_DEBUG_ATTR, "*HgfsAttrToSolaris: fileName %s\n",
+ HGFS_VP_TO_FILENAME(vp));
+ DEBUG(VM_DEBUG_ATTR, " Node ID: %llu\n", solAttr->va_nodeid);
+ }
+
+ if (solAttr->va_mask & AT_NLINK) {
+ DEBUG(VM_DEBUG_ATTR, " Setting nlink\n");
+ solAttr->va_nlink = 1; /* fake */
+ }
+
+ if (solAttr->va_mask & AT_SIZE) {
+ DEBUG(VM_DEBUG_ATTR, " Setting size\n");
+ solAttr->va_size = hgfsAttr->size;
+ }
+
+ if (solAttr->va_mask & AT_ATIME) {
+ DEBUG(VM_DEBUG_ATTR, " Setting atime\n");
+ HGFS_SET_TIME(solAttr->va_atime, hgfsAttr->accessTime);
+ }
+
+ if (solAttr->va_mask & AT_MTIME) {
+ DEBUG(VM_DEBUG_ATTR, " Setting mtime\n");
+ HGFS_SET_TIME(solAttr->va_mtime, hgfsAttr->writeTime);
+ }
+
+ if (solAttr->va_mask & AT_CTIME) {
+ DEBUG(VM_DEBUG_ATTR, " Setting ctime\n");
+ /* Since Windows doesn't keep ctime, we may need to use mtime instead. */
+ if (HGFS_SET_TIME(solAttr->va_ctime, hgfsAttr->attrChangeTime)) {
+ solAttr->va_ctime = solAttr->va_mtime;
+ }
+ }
+
+ if (solAttr->va_mask & AT_RDEV) {
+ DEBUG(VM_DEBUG_ATTR, " Setting rdev\n");
+ /* Since Windows doesn't keep ctime, we may need to use mtime instead. */
+ solAttr->va_rdev = 0; /* devices aren't allowed in Hgfs */
+ }
+
+ if (solAttr->va_mask & AT_BLKSIZE) {
+ DEBUG(VM_DEBUG_ATTR, " Setting blksize\n");
+ /* Since Windows doesn't keep ctime, we may need to use mtime instead. */
+ solAttr->va_blksize = HGFS_BLOCKSIZE;
+ }
+
+ if (solAttr->va_mask & AT_NBLOCKS) {
+ DEBUG(VM_DEBUG_ATTR, " Setting nblocks\n");
+ solAttr->va_nblocks = (solAttr->va_size / HGFS_BLOCKSIZE) + 1;
+ }
+
+#if HGFS_VFS_VERSION == 2
+ if (solAttr->va_mask & AT_VCODE) {
+ DEBUG(VM_DEBUG_ATTR, " Setting vcode\n");
+ solAttr->va_vcode = 0; /* fake */
+ }
+#else
+ if (solAttr->va_mask & AT_SEQ) {
+ DEBUG(VM_DEBUG_ATTR, " Setting seq\n");
+ solAttr->va_seq = 0; /* fake */
+ }
+#endif
+
+ HgfsDebugPrintVattr(solAttr);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsSetattrCopy --
+ *
+ * Sets the Hgfs attributes that need to be modified based on the provided
+ * Solaris attribute structure.
+ *
+ * Results:
+ * Returns TRUE if changes need to be made, FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsSetattrCopy(struct vattr *solAttr, // IN: Attributes to change to
+ int flags, // IN: Context of HgfsSetattr call
+ HgfsAttr *hgfsAttr, // OUT: Hgfs attributes to fill in
+ HgfsAttrChanges *update) // OUT: Hgfs attribute changes to make
+{
+ uint32_t mask;
+ Bool ret = FALSE;
+
+ ASSERT(solAttr);
+ ASSERT(hgfsAttr);
+ ASSERT(update);
+
+ memset(hgfsAttr, 0, sizeof *hgfsAttr);
+ memset(update, 0, sizeof *update);
+
+ /* This is the mask of attributes to change. */
+ mask = solAttr->va_mask;
+
+ /*
+ * Hgfs supports changing these attributes:
+ * o mode bits (permissions)
+ * o size
+ * o access/write times
+ */
+
+ if (mask & AT_MODE) {
+ DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating permissions.\n");
+ *update |= HGFS_ATTR_PERMISSIONS;
+ hgfsAttr->permissions = (solAttr->va_mode & S_IRWXU) >> HGFS_ATTR_MODE_SHIFT;
+ ret = TRUE;
+ }
+
+ if (mask & AT_SIZE) {
+ DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating size.\n");
+ *update |= HGFS_ATTR_SIZE;
+ hgfsAttr->size = solAttr->va_size;
+ ret = TRUE;
+ }
+
+ if (mask & AT_ATIME) {
+ DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating access time.\n");
+ *update |= HGFS_ATTR_ACCESS_TIME |
+ ((flags & ATTR_UTIME) ? HGFS_ATTR_ACCESS_TIME_SET : 0);
+ hgfsAttr->accessTime = HGFS_GET_TIME(solAttr->va_atime);
+ ret = TRUE;
+ }
+
+ if (mask & AT_MTIME) {
+ DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating write time.\n");
+ *update |= HGFS_ATTR_WRITE_TIME |
+ ((flags & ATTR_UTIME) ? HGFS_ATTR_WRITE_TIME_SET : 0);
+ hgfsAttr->writeTime = HGFS_GET_TIME(solAttr->va_mtime);
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsMakeFullName --
+ *
+ * Concatenates the path and filename to construct the full path. This
+ * handles the special cases of . and .. filenames so the Hgfs server
+ * doesn't return an error.
+ *
+ * Results:
+ * Returns the length of the full path on success, and a negative value on
+ * error. The full pathname is placed in outBuf.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+HgfsMakeFullName(const char *path, // IN: Path of directory containing file
+ uint32_t pathLen, // IN: Length of path
+ const char *file, // IN: Name of file
+ char *outBuf, // OUT: Location to write full path
+ ssize_t bufSize) // IN: Size of the out buffer
+{
+
+ ASSERT(path);
+ ASSERT(file);
+ ASSERT(outBuf);
+
+
+ DEBUG(VM_DEBUG_INFO, "HgfsMakeFullName:\n"
+ " path: \"%s\" (%d)\n"
+ " file: \"%s\" (%ld)\n",
+ path, pathLen, file, strlen(file));
+
+ /*
+ * Here there are three possibilities:
+ * o file is ".", in which case we just place path in outBuf
+ * o file is "..", in which case we strip the last component from path and
+ * put that in outBuf
+ * o for all other cases, we concatenate path, a path separator, file, and
+ * a NUL terminator and place it in outBuf
+ */
+
+ /* Make sure that the path and a NUL terminator will fit. */
+ if (bufSize < pathLen + 1) {
+ return HGFS_ERR_INVAL;
+ }
+
+
+ /* Copy path for this file into the caller's buffer. */
+ memset(outBuf, 0, bufSize);
+ memcpy(outBuf, path, pathLen);
+
+ /* Handle three cases. */
+ if (strcmp(file, ".") == 0) {
+ /* NUL terminate and return provided length. */
+ outBuf[pathLen] = '\0';
+ return pathLen;
+
+ } else if (strcmp(file, "..") == 0) {
+ /*
+ * Replace the last path separator with a NUL terminator, then return the
+ * size of the buffer.
+ */
+ char *newEnd = strrchr(outBuf, '/');
+ if (!newEnd) {
+ /*
+ * We should never get here since we name the root vnode "/" in
+ * HgfsMount().
+ */
+ return HGFS_ERR_INVAL;
+ }
+
+ *newEnd = '\0';
+ return ((uintptr_t)newEnd - (uintptr_t)outBuf);
+
+ } else {
+
+ /*
+ * The full path consists of path, the path separator, file, plus a NUL
+ * terminator. Make sure it will all fit.
+ */
+ int fileLen = strlen(file);
+ if (bufSize < pathLen + 1 + fileLen + 1) {
+ return HGFS_ERR_INVAL;
+ }
+
+ /*
+ * The CPName_ConvertTo function handles multiple path separators
+ * at the beginning of the filename, so we skip the checks to limit
+ * them to one. This also enables clobbering newEnd above to work
+ * properly on base shares (named "//sharename") that need to turn into
+ * "/".
+ */
+ outBuf[pathLen] = '/';
+
+ /* Now append the filename and NUL terminator. */
+ memcpy(outBuf + pathLen + 1, file, fileLen);
+ outBuf[pathLen + 1 + fileLen] = '\0';
+
+ return pathLen + 1 + fileLen;
+ }
+}
+
+
+/*
+ * Process signal mask manipulation
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsDisableSignals --
+ *
+ * Disables signals of current thread by calling sigintr().
+ *
+ * Results:
+ * Returns the old set of signals this process ignores.
+ *
+ * Side effects:
+ * This process is now only delivered SIGKILL sinals.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE static void
+HgfsDisableSignals(k_sigset_t *oldIgnoreSet) // OUT: Current thread's ignore set
+{
+ ASSERT(oldIgnoreSet);
+
+ /*
+ * Passing sigintr() a 1 ensures that SIGINT will not be blocked.
+ */
+ sigintr(oldIgnoreSet, 1);
+
+ /*
+ * Note that the following alone works for Netscape ...
+ * sigaddset(&curthread->t_hold, SIGALRM);
+ */
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * HgfsRestoreSignals --
+ *
+ * Restores the current process' set of signals to ignore to the provided
+ * signal set.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The process will now be delivered signals as dictated by the oldSigSet.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+INLINE static void
+HgfsRestoreSignals(k_sigset_t *oldIgnoreSet)
+{
+ ASSERT(oldIgnoreSet);
+
+ /*
+ * sigunintr() will replace the current thread's blocked signals with the
+ * provided signal set.
+ */
+ sigunintr(oldIgnoreSet);
+
+ /*
+ * Note that the following alone works for Netscape ...
+ * sigdelset(&curthread->t_hold, SIGALRM);
+ */
+}
+
+
+/*
+ * This is a less-tested, alternate implementation of HgfsReaddir(). The
+ * difference is that this one copies each entry individually so it doesn't
+ * have to a malloc() a buffer of size readSize (see the XXX comment in the
+ * HgfsReaddir() implementation above). The thinking is that this approach is
+ * likely safer, but has the potential to be slower. Initial tests show that
+ * this implementation "feels" the same speed as the other one.
+ */
+#if 0
+static int
+HgfsReaddir(struct vnode *vp, // IN: Vnode of directory to read
+ struct uio *uiop, // IN: User's read request
+ struct cred *cr, // IN: Credentials of caller
+ int *eofp) // OUT: Indicates we are done
+{
+ HgfsSuperInfo *sip;
+ HgfsHandle handle;
+ struct dirent64 *dirp;
+ char buf[sizeof *dirp + MAXNAMELEN];
+ ssize_t readSize;
+ uint64_t offset;
+ Bool done;
+ int ret;
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir().\n");
+
+ if (!vp || !uiop || !cr || !eofp) {
+ cmn_err(HGFS_ERROR, "HgfsReaddir: NULL input from Kernel.\n");
+ return EINVAL;
+ }
+
+ DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: uiop->uio_resid=%d, uiop->uio_loffset=%d\n",
+ uiop->uio_resid, uiop->uio_loffset);
+
+
+ /*
+ * XXX: If would be nice if we could perform some sort of sanity check on
+ * the handle here. Perhaps make sure handle <= NUM_SEARCHES in
+ * hgfsServer.c since the handle is the index number in searchArray.
+ */
+ if ( !HGFS_KNOW_FILENAME(vp) ) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we don't know the filename.\n");
+ return EBADF;
+ }
+
+ sip = HgfsGetSuperInfo();
+ if (!sip) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we can't get the superinfo.\n");
+ return EIO;
+ }
+
+ /*
+ * In order to fill the user's buffer with directory entries, we must
+ * iterate on HGFS_OP_SEARCH_READ requests until either the user's buffer is
+ * full or there are no more entries. Each call to HgfsGetNextDirEntry()
+ * fills in the name and attribute structure for the next entry. We then
+ * escape the name, create the directory entry in our temporary buf, and
+ * copy the entry to the user's buffer.
+ */
+
+ readSize = uiop->uio_resid;
+ dirp = (struct dirent64 *)buf;
+
+ /*
+ * We need to get the handle for this open directory to send to the Hgfs
+ * server in our requests.
+ */
+ ret = HgfsGetOpenFileHandle(vp, &handle);
+ if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: could not get handle.\n");
+ return EINVAL;
+ }
+
+ /*
+ * Loop until one of the following conditions is met:
+ * o An error occurs while reading a directory entry
+ * o There are no more directory entries to read
+ * o The buffer is full and cannot hold the next entry
+ *
+ * We request dentries from the Hgfs server based on their index in the
+ * directory. The offset value is initialized to the value specified in
+ * the user's io request and is incremented each time through the loop.
+ *
+ * We decrement readSize by the size of the directory entry each time we
+ * successfully copy one into the user's buffer.
+ */
+ for (offset = uiop->uio_loffset, done = 0; /* Nothing */ ; offset++) {
+ char nameBuf[MAXNAMELEN + 1];
+ char escName[MAXNAMELEN + 1];
+ char fullName[MAXPATHLEN + 1];
+
+ DEBUG(VM_DEBUG_COMM,
+ "HgfsReaddir: getting directory entry at offset %d.\n", offset);
+
+ memset(nameBuf, 0, sizeof nameBuf);
+ memset(buf, 0, sizeof buf);
+
+ ret = HgfsGetNextDirEntry(sip, handle, offset, nameBuf, &done);
+ /* If the filename was too long, we skip to the next entry ... */
+ if (ret == EOVERFLOW) {
+ continue;
+ /* ... but if another error occurred, we return that error code ... */
+ } else if (ret) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: failure occurred in HgfsGetNextDirEntry\n");
+ goto out;
+ /*
+ * ... and if there are no more entries, we set the end of file pointer
+ * and break out of the loop.
+ */
+ } else if (done == TRUE) {
+ DEBUG(VM_DEBUG_COMM, "HgfsReaddir: Done reading directory entries.\n");
+ *eofp = TRUE;
+ break;
+ }
+
+ /*
+ * We now have the directory entry, so we sanitize the name and try to
+ * put it in our buffer.
+ */
+ DEBUG(VM_DEBUG_COMM, "HgfsReaddir: received filename \"%s\"\n", nameBuf);
+
+ memset(escName, 0, sizeof escName);
+
+ ret = HgfsEscape_Do(nameBuf, strlen(nameBuf), MAXNAMELEN, escName);
+ /* If the escaped name didn't fit in the buffer, skip to the next entry. */
+ if (ret < 0 || ret > MAXNAMELEN) {
+ DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: HgfsEscape_Do failed.\n");
+ continue;
+ }
+
+ /*
+ * Make sure there is enough room in the buffer for the entire directory
+ * entry. If not, we just break out of the loop and copy what we have.
+ */
+ if (DIRENT64_RECLEN(ret) > readSize) {
+ DEBUG(VM_DEBUG_INFO, "HgfsReaddir: ran out of room in the buffer.\n");
+ break;
+ }
+
+ /* Fill in the directory entry. */
+ dirp->d_reclen = DIRENT64_RECLEN(ret);
+ dirp->d_off = offset;
+ memcpy(dirp->d_name, escName, ret);
+ dirp->d_name[ret] = '\0';
+
+ ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp), // Directorie's name
+ HGFS_VP_TO_FILENAME_LENGTH(vp), // Length
+ dirp->d_name, // Name of file
+ fullName, // Destination buffer
+ sizeof fullName); // Size of this buffer
+ /* Skip this entry if the full path was too long. */
+ if (ret < 0) {
+ continue;
+ }
+
+ /*
+ * Place the node id, which serves the purpose of inode number, for this
+ * filename directory entry. As long as we are using a dirent64, this is
+ * okay since ino_t is also a u_longlong_t.
+ */
+ HgfsNodeIdGet(&sip->fileHashTable, fullName, (uint32_t)ret,
+ (u_longlong_t *)&dirp->d_ino);
+
+ /*
+ * Now that we've filled our buffer with as many dentries as fit, we copy it
+ * into the user's buffer.
+ */
+ ret = uiomove(dirp, // Source buffer
+ dirp->d_reclen, // Size of this buffer
+ UIO_READ, // Read flag
+ uiop); // User's request struct
+
+ /* Break the loop if we can't copy this dentry into the user's buffer. */
+ if (ret) {
+ goto out;
+ }
+
+ /* Decrement the number of bytes copied on success */
+ readSize -= dirp->d_reclen;
+ }
+
+ /* Return success */
+ ret = 0;
+
+out:
+ /*
+ * uiomove(9F) will have incremented the uio offset by the number of bytes
+ * written. We reset it here to the fs-specific offset in our directory so
+ * the next time we are called it is correct. (Note, this does not break
+ * anything and /is/ how this field is intended to be used.)
+ */
+ uiop->uio_loffset = offset; // XXX ok to do this on error too?
+
+ DEBUG(VM_DEBUG_DONE, "HgfsReaddir: done (ret=%d, *eofp=%d).\n", ret, *eofp);
+ DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: exiting.\n");
+ return ret;
+}
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vnode.h --
+ *
+ * Functions exported by vnode.c
+ */
+
+#ifndef __VNODE_H_
+#define __VNODE_H_
+
+#include "hgfsSolaris.h"
+
+int HgfsSetVnodeOps(HgfsSuperInfo *sip, struct vnode *vp);
+inline HgfsSuperInfo *HgfsGetSuperInfo(void);
+
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 1998-2008 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#ifndef _X86CPUID_H_
+#define _X86CPUID_H_
+
+/* http://www.sandpile.org/ia32/cpuid.htm */
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMX
+#define INCLUDE_ALLOW_VMMEXT
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMNIXMOD
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMMON
+#include "includeCheck.h"
+
+#include "vm_basic_types.h"
+
+/*
+ * The linux kernel's ptrace.h stupidly defines the bare
+ * EAX/EBX/ECX/EDX, which wrecks havoc with our preprocessor tricks.
+ */
+#undef EAX
+#undef EBX
+#undef ECX
+#undef EDX
+
+typedef struct CPUIDRegs {
+ uint32 eax, ebx, ecx, edx;
+} CPUIDRegs;
+
+typedef union CPUIDRegsUnion {
+ uint32 array[4];
+ CPUIDRegs regs;
+} CPUIDRegsUnion;
+
+/*
+ * Results of calling cpuid(eax, ecx) on all host logical CPU.
+ */
+#ifdef _MSC_VER
+#pragma warning (disable :4200) // non-std extension: zero-sized array in struct
+#endif
+
+typedef
+#include "vmware_pack_begin.h"
+struct CPUIDReply {
+ /*
+ * Unique host logical CPU identifier. It does not change across queries, so
+ * we use it to correlate the replies of multiple queries.
+ */
+ uint64 tag; // OUT
+
+ CPUIDRegs regs; // OUT
+}
+#include "vmware_pack_end.h"
+CPUIDReply;
+
+typedef
+#include "vmware_pack_begin.h"
+struct CPUIDQuery {
+ uint32 eax; // IN
+ uint32 ecx; // IN
+ uint32 numLogicalCPUs; // IN/OUT
+ CPUIDReply logicalCPUs[0]; // OUT
+}
+#include "vmware_pack_end.h"
+CPUIDQuery;
+
+/*
+ * CPUID levels the monitor caches and ones that are not cached, but
+ * have fields defined below (short name and actual value).
+ *
+ * The first parameter defines whether the level is masked/tested
+ * during power-on/migration. Any level which is marked as FALSE here
+ * *must* have all field masks defined as IGNORE in CPUID_FIELD_DATA.
+ * A static assert in lib/cpuidcompat/cpuidcompat.c will check this.
+ *
+ * IMPORTANT: WHEN ADDING A NEW FIELD TO THE CACHED LEVELS, make sure
+ * you update vmcore/vmm/cpu/priv.c:Priv_CPUID() and vmcore/vmm64/bt/
+ * cpuid_shared.S (and geninfo) to include the new level.
+ */
+
+#define CPUID_CACHED_LEVELS \
+ CPUIDLEVEL(TRUE, 0, 0) \
+ CPUIDLEVEL(TRUE, 1, 1) \
+ CPUIDLEVEL(FALSE,400, 0x40000000) \
+ CPUIDLEVEL(FALSE,410, 0x40000010) \
+ CPUIDLEVEL(FALSE, 80, 0x80000000) \
+ CPUIDLEVEL(TRUE, 81, 0x80000001) \
+ CPUIDLEVEL(FALSE, 88, 0x80000008) \
+ CPUIDLEVEL(TRUE, 8A, 0x8000000A)
+
+#define CPUID_UNCACHED_LEVELS \
+ CPUIDLEVEL(FALSE, 4, 4) \
+ CPUIDLEVEL(FALSE, 5, 5) \
+ CPUIDLEVEL(FALSE, 6, 6) \
+ CPUIDLEVEL(FALSE, A, 0xA) \
+ CPUIDLEVEL(FALSE, 86, 0x80000006) \
+ CPUIDLEVEL(FALSE, 87, 0x80000007) \
+
+#define CPUID_ALL_LEVELS \
+ CPUID_CACHED_LEVELS \
+ CPUID_UNCACHED_LEVELS
+
+/* Define cached CPUID levels in the form: CPUID_LEVEL_<ShortName> */
+typedef enum {
+#define CPUIDLEVEL(t, s, v) CPUID_LEVEL_##s,
+ CPUID_CACHED_LEVELS
+#undef CPUIDLEVEL
+ CPUID_NUM_LEVELS
+} CpuidLevels;
+
+/*
+ * CPUID result registers
+ */
+
+#define CPUID_REGS \
+ CPUIDREG(EAX, eax) \
+ CPUIDREG(EBX, ebx) \
+ CPUIDREG(ECX, ecx) \
+ CPUIDREG(EDX, edx)
+
+typedef enum {
+#define CPUIDREG(uc, lc) CPUID_REG_##uc,
+ CPUID_REGS
+#undef CPUIDREG
+ CPUID_NUM_REGS
+} CpuidRegs;
+
+/*
+ * CPU vendors
+ */
+
+typedef enum {
+ CPUID_VENDOR_UNKNOWN,
+ CPUID_VENDOR_COMMON,
+ CPUID_VENDOR_INTEL,
+ CPUID_VENDOR_AMD,
+ CPUID_VENDOR_CYRIX,
+ CPUID_VENDOR_VIA,
+ CPUID_NUM_VENDORS
+} CpuidVendors;
+
+#define CPUID_INTEL_VENDOR_STRING "GenuntelineI"
+#define CPUID_AMD_VENDOR_STRING "AuthcAMDenti"
+#define CPUID_CYRIX_VENDOR_STRING "CyriteadxIns"
+#define CPUID_VIA_VENDOR_STRING "CentaulsaurH"
+#define CPUID_HYPERV_HYPERVISOR_VENDOR_STRING "Microsoft Hv"
+#define CPUID_INTEL_VENDOR_STRING_FIXED "GenuineIntel"
+#define CPUID_AMD_VENDOR_STRING_FIXED "AuthenticAMD"
+#define CPUID_CYRIX_VENDOR_STRING_FIXED "CyrixInstead"
+#define CPUID_VIA_VENDOR_STRING_FIXED "CentaurHauls"
+
+/*
+ * FIELDDEF can be defined to process the CPUID information provided
+ * in the following CPUID_FIELD_DATA macro. The first parameter is
+ * the CPUID level of the feature (must be defined in CPUID_*_LEVELS.
+ * The second parameter is the register the field is contained in
+ * (defined in CPUID_REGS). The third field is the vendor this
+ * feature applies to. "COMMON" means all vendors apply. UNKNOWN may
+ * not be used here. The fourth and fifth parameters are the bit
+ * position of the field and the width, respectively. The sixth is
+ * the text name of the field.
+ *
+ * The seventh and eighth parameters specify the default CPUID
+ * behavior for power-on, guest view, and migration tests (cpt/rsm &
+ * vmotion). The eighth parameter is ignored for types other than
+ * MASK & TEST, and must be zero in this case.
+ *
+ * When adding a new field, be sure to consider its purpose. The
+ * following list of types is provided in order of likely use.
+ *
+ * NOTE: this form of representation is separate from the masking
+ * system specified via the config file. That is because this
+ * representation must take into account multi-bit fields.
+ *
+ * HOST - Passthrough host value and cannot change during migration.
+ * MASK, 0 - Hide from the guest, because we don't support it or we
+ * don't want the guest to know that it exists.
+ * IGNORE - Ignore this field for all tests
+ *
+ * (Think twice before using the below mask types/combinations)
+ *
+ * MASK, x - Force the guest to always see x, and don't compare for
+ * migration -- only APIC as of today; it is controlled by
+ * software and we know how to toggle it
+ * TEST, x - Require host CPUID field to be x for power-on
+ * RSVD - Hidden from the guest, but compared during migration
+ *
+ *
+ * Table to explain mask type meanings:
+ *
+ * IGNR MASK TEST HOST RSVD
+ * --------------------------------------------------------
+ * Req'd val for power-on - - x - -
+ * Value guest sees * x * * 0
+ * Checked on migration? N N Y Y Y
+ *
+ * * - initial host's power-on CPUID value
+ *
+ * FIELDDEFA takes a ninth parameter, the name used when creating
+ * accessor functions in lib/public/cpuidInfoFuncs.h.
+ *
+ * FLAGDEF/FLAGDEFA is defined identically to fields, but their
+ * accessors are more appropriate for 1-bit flags.
+ */
+
+typedef enum {
+ CPUID_FIELD_MASK_IGNORE,
+ CPUID_FIELD_MASK_MASK,
+ CPUID_FIELD_MASK_TEST,
+ CPUID_FIELD_MASK_HOST,
+ CPUID_FIELD_MASK_RSVD,
+ CPUID_NUM_FIELD_MASKS
+} CpuidFieldMasks;
+
+
+typedef enum {
+ CPUID_FIELD_SUPPORTED_NO,
+ CPUID_FIELD_SUPPORTED_YES,
+ CPUID_FIELD_SUPPORTED_ANY,
+ CPUID_FIELD_SUPPORTED_NA,
+ CPUID_NUM_FIELD_SUPPORTEDS
+} CpuidFieldSupported;
+
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_0 \
+FIELDDEF( 0, EAX, COMMON, 0, 32, NUMLEVELS, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 0, EBX, COMMON, 0, 32, VENDOR1, YES, HOST, 0, TRUE) \
+FIELDDEF( 0, ECX, COMMON, 0, 32, VENDOR3, YES, HOST, 0, TRUE) \
+FIELDDEF( 0, EDX, COMMON, 0, 32, VENDOR2, YES, HOST, 0, TRUE)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_1 \
+FIELDDEFA( 1, EAX, COMMON, 0, 4, STEPPING, ANY, IGNORE, 0, FALSE, STEPPING) \
+FIELDDEFA( 1, EAX, COMMON, 4, 4, MODEL, ANY, IGNORE, 0, FALSE, MODEL) \
+FIELDDEFA( 1, EAX, COMMON, 8, 4, FAMILY, YES, HOST, 0, FALSE, FAMILY) \
+FIELDDEF( 1, EAX, COMMON, 12, 2, TYPE, ANY, IGNORE, 0, FALSE) \
+FIELDDEFA( 1, EAX, COMMON, 16, 4, EXTMODEL, ANY, IGNORE, 0, FALSE, EXT_MODEL) \
+FIELDDEFA( 1, EAX, COMMON, 20, 8, EXTFAMILY, YES, HOST, 0, FALSE, EXT_FAMILY) \
+FIELDDEF( 1, EBX, COMMON, 0, 8, BRAND_ID, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 1, EBX, COMMON, 8, 8, CLFL_SIZE, ANY, IGNORE, 0, FALSE) \
+FIELDDEFA( 1, EBX, COMMON, 16, 8, LCPU_COUNT, ANY, IGNORE, 0, FALSE, LCPU_COUNT) \
+FIELDDEFA( 1, EBX, COMMON, 24, 8, APICID, ANY, IGNORE, 0, FALSE, APICID) \
+FLAGDEFA( 1, ECX, COMMON, 0, 1, SSE3, YES, HOST, 0, TRUE, SSE3) \
+FLAGDEF( 1, ECX, INTEL, 2, 1, NDA2, NO, MASK, 0, FALSE) \
+FLAGDEFA( 1, ECX, COMMON, 3, 1, MWAIT, NO, MASK, 0, FALSE, MWAIT) \
+FLAGDEFA( 1, ECX, INTEL, 4, 1, DSCPL, NO, MASK, 0, FALSE, DSCPL) \
+FLAGDEFA( 1, ECX, INTEL, 5, 1, VMX, NO, MASK, 0, FALSE, VMX) \
+FLAGDEF( 1, ECX, INTEL, 6, 1, SMX, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, ECX, INTEL, 7, 1, EST, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, ECX, INTEL, 8, 1, TM2, NO, MASK, 0, FALSE) \
+FLAGDEFA( 1, ECX, COMMON, 9, 1, SSSE3, YES, HOST, 0, TRUE, SSSE3) \
+FLAGDEF( 1, ECX, INTEL, 10, 1, HTCACHE, NO, MASK, 0, FALSE) \
+FLAGDEFA( 1, ECX, COMMON, 13, 1, CMPX16, YES, HOST, 0, TRUE, CMPX16) \
+FLAGDEF( 1, ECX, INTEL, 14, 1, xPPR, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, ECX, INTEL, 15, 1, PERF_MSR, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, ECX, INTEL, 18, 1, DCA, NO, MASK, 0, FALSE) \
+FLAGDEFA( 1, ECX, INTEL, 19, 1, SSE41, YES, HOST, 0, TRUE, SSE41) \
+FLAGDEFA( 1, ECX, INTEL, 20, 1, SSE42, YES, HOST, 0, TRUE, SSE42) \
+FLAGDEF( 1, ECX, INTEL, 21, 1, X2APIC, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, ECX, INTEL, 22, 1, MOVBE, NO, RSVD, 0, TRUE) \
+FLAGDEFA( 1, ECX, COMMON, 23, 1, POPCNT, YES, HOST, 0, TRUE, POPCNT) \
+FLAGDEF( 1, ECX, INTEL, 24, 1, ULE, NO, RSVD, 0, TRUE) \
+FLAGDEF( 1, ECX, INTEL, 26, 1, XSAVE, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, ECX, INTEL, 27, 1, OSXSAVE, NO, RSVD, 0, TRUE) \
+FLAGDEFA( 1, ECX, COMMON, 31, 1, HYPERVISOR, ANY, IGNORE, 0, FALSE, HYPERVISOR)\
+FLAGDEFA( 1, EDX, COMMON, 0, 1, FPU, YES, HOST, 0, TRUE, FPU) \
+FLAGDEFA( 1, EDX, COMMON, 1, 1, VME, YES, HOST, 0, FALSE, VME) \
+FLAGDEF( 1, EDX, COMMON, 2, 1, DBGE, YES, HOST, 0, FALSE) \
+FLAGDEF( 1, EDX, COMMON, 3, 1, PGSZE, YES, HOST, 0, FALSE) \
+FLAGDEFA( 1, EDX, COMMON, 4, 1, TSC, YES, HOST, 0, TRUE, TSC) \
+FLAGDEF( 1, EDX, COMMON, 5, 1, MSR, YES, HOST, 0, FALSE) \
+FLAGDEFA( 1, EDX, COMMON, 6, 1, PAE, YES, HOST, 0, FALSE, PAE) \
+FLAGDEF( 1, EDX, COMMON, 7, 1, MCK, YES, HOST, 0, FALSE) \
+FLAGDEF( 1, EDX, COMMON, 8, 1, CPMX, YES, HOST, 0, TRUE) \
+FLAGDEFA( 1, EDX, COMMON, 9, 1, APIC, ANY, MASK, 1, FALSE, APIC) \
+FLAGDEFA( 1, EDX, COMMON, 11, 1, SEP, YES, HOST, 0, TRUE, SEP) \
+FLAGDEFA( 1, EDX, COMMON, 12, 1, MTRR, YES, HOST, 0, FALSE, MTRR) \
+FLAGDEFA( 1, EDX, COMMON, 13, 1, PGE, YES, HOST, 0, FALSE, PGE) \
+FLAGDEFA( 1, EDX, COMMON, 14, 1, MCA, YES, HOST, 0, FALSE, MCA) \
+FLAGDEFA( 1, EDX, COMMON, 15, 1, CMOV, YES, HOST, 0, TRUE, CMOV) \
+FLAGDEFA( 1, EDX, COMMON, 16, 1, PAT, YES, HOST, 0, FALSE, PAT) \
+FLAGDEF( 1, EDX, COMMON, 17, 1, 36PG, YES, HOST, 0, FALSE) \
+FLAGDEF( 1, EDX, INTEL, 18, 1, PSN, YES, HOST, 0, FALSE) \
+FLAGDEFA( 1, EDX, COMMON, 19, 1, CLFL, YES, HOST, 0, TRUE, CLFL) \
+FLAGDEF( 1, EDX, INTEL, 21, 1, DTES, YES, HOST, 0, FALSE) \
+FLAGDEF( 1, EDX, INTEL, 22, 1, ACPI, YES, HOST, 0, FALSE) \
+FLAGDEFA( 1, EDX, COMMON, 23, 1, MMX, YES, HOST, 0, TRUE, MMX) \
+FLAGDEFA( 1, EDX, COMMON, 24, 1, FXSAVE, YES, HOST, 0, TRUE, FXSAVE) \
+FLAGDEFA( 1, EDX, COMMON, 25, 1, SSE, YES, HOST, 0, TRUE, SSE) \
+FLAGDEFA( 1, EDX, COMMON, 26, 1, SSE2, YES, HOST, 0, TRUE, SSE2) \
+FLAGDEF( 1, EDX, INTEL, 27, 1, SS, YES, HOST, 0, FALSE) \
+FLAGDEFA( 1, EDX, COMMON, 28, 1, HT, NO, MASK, 0, FALSE, HT) \
+FLAGDEF( 1, EDX, INTEL, 29, 1, TM, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, EDX, INTEL, 30, 1, IA64, NO, MASK, 0, FALSE) \
+FLAGDEF( 1, EDX, INTEL, 31, 1, PBE, NO, MASK, 0, FALSE)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_4 \
+FIELDDEF( 4, EAX, INTEL, 0, 5, CACHE_TYPE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 4, EAX, INTEL, 5, 3, CACHE_LEVEL, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 4, EAX, INTEL, 14, 12, CACHE_NUMHT_SHARING, NA, IGNORE, 0, FALSE) \
+FIELDDEFA( 4, EAX, INTEL, 26, 6, CORE_COUNT, NA, IGNORE, 0, FALSE, INTEL_CORE_COUNT) \
+FIELDDEF( 4, EBX, INTEL, 0, 12, CACHE_LINE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 4, EBX, INTEL, 12, 10, CACHE_PART, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 4, EBX, INTEL, 22, 10, CACHE_WAYS, NA, IGNORE, 0, FALSE)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_5 \
+FIELDDEF( 5, EAX, COMMON, 0, 16, MWAIT_MIN_SIZE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 5, EBX, COMMON, 0, 16, MWAIT_MAX_SIZE, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 5, ECX, COMMON, 0, 1, MWAIT_EXTENSIONS, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 5, ECX, COMMON, 1, 1, MWAIT_INTR_BREAK, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 5, EDX, INTEL, 0, 4, MWAIT_C0_SUBSTATE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 5, EDX, INTEL, 4, 4, MWAIT_C1_SUBSTATE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 5, EDX, INTEL, 8, 4, MWAIT_C2_SUBSTATE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 5, EDX, INTEL, 12, 4, MWAIT_C3_SUBSTATE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 5, EDX, INTEL, 16, 4, MWAIT_C4_SUBSTATE, NA, IGNORE, 0, FALSE)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_6 \
+FLAGDEF( 6, EAX, INTEL, 0, 1, THERMAL_SENSOR, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 6, EAX, INTEL, 1, 1, TURBO_MODE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 6, EBX, INTEL, 0, 4, NUM_INTR_THRESHOLDS, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 6, ECX, INTEL, 0, 1, HW_COORD_FEEDBACK, NA, IGNORE, 0, FALSE)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_A \
+FIELDDEFA( A, EAX, INTEL, 0, 8, PMC_VERSION, NA, IGNORE, 0, FALSE, PMC_VERSION) \
+FIELDDEFA( A, EAX, INTEL, 8, 8, NUM_PMCS, NA, IGNORE, 0, FALSE, NUM_PMCS) \
+FIELDDEF( A, EAX, INTEL, 16, 8, PMC_BIT_WIDTH, NA, IGNORE, 0, FALSE) \
+FIELDDEFA( A, EAX, INTEL, 24, 8, PMC_EBX_LENGTH, NA, IGNORE, 0, FALSE, PMC_EBX_LENGTH) \
+FLAGDEF( A, EBX, INTEL, 0, 1, PMC_CORE_CYCLE, NA, IGNORE, 0, FALSE) \
+FLAGDEF( A, EBX, INTEL, 1, 1, PMC_INSTR_RETIRED, NA, IGNORE, 0, FALSE) \
+FLAGDEF( A, EBX, INTEL, 2, 1, PMC_REF_CYCLES, NA, IGNORE, 0, FALSE) \
+FLAGDEF( A, EBX, INTEL, 3, 1, PMC_LAST_LVL_CREF, NA, IGNORE, 0, FALSE) \
+FLAGDEF( A, EBX, INTEL, 4, 1, PMC_LAST_LVL_CMISS, NA, IGNORE, 0, FALSE) \
+FLAGDEF( A, EBX, INTEL, 5, 1, PMC_BR_INST_RETIRED, NA, IGNORE, 0, FALSE) \
+FLAGDEF( A, EBX, INTEL, 6, 1, PMC_BR_MISS_RETIRED, NA, IGNORE, 0, FALSE) \
+FIELDDEF( A, EDX, INTEL, 0, 5, PMC_FIXED_NUM, NA, IGNORE, 0, FALSE) \
+FIELDDEF( A, EDX, INTEL, 5, 8, PMC_FIXED_SIZE, NA, IGNORE, 0, FALSE)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_80 \
+FIELDDEF( 80, EAX, COMMON, 0, 32, NUM_EXT_LEVELS, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 80, EBX, AMD, 0, 32, AMD_VENDOR1, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 80, ECX, AMD, 0, 32, AMD_VENDOR3, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 80, EDX, AMD, 0, 32, AMD_VENDOR2, NA, IGNORE, 0, FALSE)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_81 \
+FIELDDEF( 81, EAX, INTEL, 0, 32, UNKNOWN81EAX, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EAX, AMD, 0, 4, STEPPING, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EAX, AMD, 4, 4, MODEL, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EAX, AMD, 8, 4, FAMILY, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EAX, AMD, 12, 2, TYPE, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EAX, AMD, 16, 4, EXTMODEL, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EAX, AMD, 20, 8, EXTFAMILY, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EBX, INTEL, 0, 32, UNKNOWN81EBX, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EBX, AMD, 0, 16, BRAND_ID, ANY, IGNORE, 0, FALSE) \
+FIELDDEF( 81, EBX, AMD, 16, 16, UNDEF, ANY, IGNORE, 0, FALSE) \
+FLAGDEFA( 81, ECX, COMMON, 0, 1, LAHF, YES, HOST, 0, TRUE, LAHF64) \
+FLAGDEFA( 81, ECX, AMD, 1, 1, CMPLEGACY, NO, MASK, 0, FALSE, CMPLEGACY) \
+FLAGDEFA( 81, ECX, AMD, 2, 1, SVM, NO, MASK, 0, FALSE, SVM) \
+FLAGDEFA( 81, ECX, AMD, 3, 1, EXTAPICSPC, YES, HOST, 0, FALSE, EXTAPICSPC) \
+FLAGDEFA( 81, ECX, AMD, 4, 1, CR8AVAIL, NO, MASK, 0, FALSE, CR8AVAIL) \
+FLAGDEFA( 81, ECX, AMD, 5, 1, ABM, YES, HOST, 0, TRUE, ABM) \
+FLAGDEFA( 81, ECX, AMD, 6, 1, SSE4A, YES, HOST, 0, TRUE, SSE4A) \
+FLAGDEF( 81, ECX, AMD, 7, 1, MISALIGNED_SSE, YES, HOST, 0, TRUE) \
+FLAGDEFA( 81, ECX, AMD, 8, 1, 3DNPREFETCH, YES, HOST, 0, TRUE, 3DNPREFETCH) \
+FLAGDEF( 81, ECX, AMD, 9, 1, OSVW, NO, MASK, 0, FALSE) \
+FLAGDEF( 81, ECX, AMD, 10, 1, IBS, NO, MASK, 0, FALSE) \
+FLAGDEF( 81, ECX, AMD, 11, 1, SSE5, NO, RSVD, 0, TRUE) \
+FLAGDEF( 81, ECX, AMD, 12, 1, SKINIT, NO, MASK, 0, FALSE) \
+FLAGDEF( 81, ECX, AMD, 13, 1, WATCHDOG, NO, MASK, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 0, 1, FPU, YES, HOST, 0, TRUE) \
+FLAGDEF( 81, EDX, AMD, 1, 1, VME, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 2, 1, DBGE, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 3, 1, PGSZE, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 4, 1, TSC, YES, HOST, 0, TRUE) \
+FLAGDEF( 81, EDX, AMD, 5, 1, MSR, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 6, 1, PAE, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 7, 1, MCK, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 8, 1, CPMX, YES, HOST, 0, TRUE) \
+FLAGDEF( 81, EDX, AMD, 9, 1, APIC, ANY, MASK, 1, FALSE) \
+FLAGDEFA( 81, EDX, COMMON, 11, 1, SYSC, ANY, IGNORE, 0, TRUE, SYSC) \
+FLAGDEF( 81, EDX, AMD, 12, 1, MTRR, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 13, 1, PGE, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 14, 1, MCA, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 15, 1, CMOV, YES, HOST, 0, TRUE) \
+FLAGDEF( 81, EDX, AMD, 16, 1, PAT, YES, HOST, 0, FALSE) \
+FLAGDEF( 81, EDX, AMD, 17, 1, 36PG, YES, HOST, 0, FALSE) \
+FLAGDEFA( 81, EDX, COMMON, 20, 1, NX, YES, HOST, 0, FALSE, NX) \
+FLAGDEFA( 81, EDX, AMD, 22, 1, MMXEXT, YES, HOST, 0, TRUE, MMXEXT) \
+FLAGDEF( 81, EDX, AMD, 23, 1, MMX, YES, HOST, 0, TRUE) \
+FLAGDEF( 81, EDX, AMD, 24, 1, FXSAVE, YES, HOST, 0, TRUE) \
+FLAGDEFA( 81, EDX, AMD, 25, 1, FFXSR, YES, HOST, 0, FALSE, FFXSR) \
+FLAGDEF( 81, EDX, AMD, 26, 1, PDPE1GB, NO, MASK, 0, FALSE) \
+FLAGDEFA( 81, EDX, COMMON, 27, 1, RDTSCP, YES, HOST, 0, TRUE, RDTSCP) \
+FLAGDEFA( 81, EDX, COMMON, 29, 1, LM, YES, TEST, 1, FALSE, LM) \
+FLAGDEFA( 81, EDX, AMD, 30, 1, 3DNOWPLUS, YES, HOST, 0, TRUE, 3DNOWPLUS) \
+FLAGDEFA( 81, EDX, AMD, 31, 1, 3DNOW, YES, HOST, 0, TRUE, 3DNOW)
+
+/* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */
+#define CPUID_FIELD_DATA_LEVEL_8x \
+FIELDDEF( 86, ECX, AMD, 0, 8, L2CACHE_LINE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 86, ECX, AMD, 8, 4, L2CACHE_LINE_PER_TAG, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 86, ECX, AMD, 12, 4, L2CACHE_WAYS, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 86, ECX, AMD, 16, 16, L2CACHE_SIZE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 86, EDX, AMD, 0, 8, L3CACHE_LINE, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 86, EDX, AMD, 8, 4, L3CACHE_LINE_PER_TAG,NA, IGNORE, 0, FALSE) \
+FIELDDEF( 86, EDX, AMD, 12, 4, L3CACHE_WAYS, NA, IGNORE, 0, FALSE) \
+FIELDDEF( 86, EDX, AMD, 18, 14, L3CACHE_SIZE, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 0, 1, TS, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 1, 1, FID, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 2, 1, VID, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 3, 1, TTP, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 4, 1, TM, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 5, 1, STC, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 6, 1, 100MHZSTEPS, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 7, 1, HWPSTATE, NA, IGNORE, 0, FALSE) \
+FLAGDEF( 87, EDX, AMD, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \
+FIELDDEFA(88, EAX, COMMON, 0, 8, PHYSBITS, NA, IGNORE, 0, FALSE, PHYS_BITS) \
+FIELDDEFA(88, EAX, COMMON, 8, 8, VIRTBITS, NA, IGNORE, 0, FALSE, VIRT_BITS) \
+FIELDDEFA(88, ECX, AMD, 0, 8, CORE_COUNT, NA, IGNORE, 0, FALSE, AMD_CORE_COUNT) \
+FIELDDEFA(88, ECX, AMD, 12, 4, APICID_COREID_SIZE, NA, IGNORE, 0, FALSE, AMD_APICID_COREID_SIZE) \
+FIELDDEFA(8A, EAX, AMD, 0, 8, SVM_REVISION, NO, MASK, 0, FALSE, SVM_REVISION) \
+FLAGDEF( 8A, EAX, AMD, 8, 1, SVM_HYPERVISOR, NO, MASK, 0, FALSE) \
+FIELDDEF( 8A, EAX, AMD, 9, 23, SVMEAX_RSVD, NO, MASK, 0, FALSE) \
+FIELDDEF( 8A, EBX, AMD, 0, 32, SVM_N_ASIDS, NO, MASK, 0, FALSE) \
+FIELDDEF( 8A, ECX, AMD, 0, 32, SVMECX_RSVD, NO, MASK, 0, FALSE) \
+FLAGDEFA( 8A, EDX, AMD, 0, 1, SVM_NP, NO, MASK, 0, FALSE, NPT) \
+FLAGDEF( 8A, EDX, AMD, 1, 1, SVM_LBR, NO, MASK, 0, FALSE) \
+FLAGDEF( 8A, EDX, AMD, 2, 1, SVM_LOCK, NO, MASK, 0, FALSE) \
+FLAGDEF( 8A, EDX, AMD, 3, 1, SVM_NRIP, NO, MASK, 0, FALSE) \
+FIELDDEF( 8A, EDX, AMD, 4, 28, SVMEDX_RSVD, NO, MASK, 0, FALSE)
+
+#define CPUID_FIELD_DATA \
+ CPUID_FIELD_DATA_LEVEL_0 \
+ CPUID_FIELD_DATA_LEVEL_1 \
+ CPUID_FIELD_DATA_LEVEL_4 \
+ CPUID_FIELD_DATA_LEVEL_5 \
+ CPUID_FIELD_DATA_LEVEL_6 \
+ CPUID_FIELD_DATA_LEVEL_A \
+ CPUID_FIELD_DATA_LEVEL_80 \
+ CPUID_FIELD_DATA_LEVEL_81 \
+ CPUID_FIELD_DATA_LEVEL_8x
+
+/*
+ * Define all field and flag values as an enum. The result is a full
+ * set of values taken from the table above in the form:
+ *
+ * CPUID_FEATURE_<vendor>_ID<level><reg>_<name> == mask for feature
+ * CPUID_<vendor>_ID<level><reg>_<name>_MASK == mask for field
+ * CPUID_<vendor>_ID<level><reg>_<name>_SHIFT == offset of field
+ *
+ * e.g. - CPUID_FEATURE_COMMON_ID1EDX_FPU = 0x1
+ * - CPUID_COMMON_ID88EAX_VIRTBITS_MASK = 0xff00
+ * - CPUID_COMMON_ID88EAX_VIRTBITS_SHIFT = 8
+ *
+ * Note: The FEATURE/MASK definitions must use some gymnastics to get
+ * around a warning when shifting left by 32.
+ */
+#define VMW_BIT_MASK(shift) (((1 << (shift - 1)) << 1) - 1)
+
+#define FIELDDEF(lvl, reg, vend, bitpos, size, name, s, m, v, c3) \
+ CPUID_##vend##_ID##lvl##reg##_##name##_SHIFT = bitpos, \
+ CPUID_##vend##_ID##lvl##reg##_##name##_MASK = \
+ VMW_BIT_MASK(size) << bitpos, \
+ CPUID_FEATURE_##vend##_ID##lvl##reg##_##name = \
+ CPUID_##vend##_ID##lvl##reg##_##name##_MASK,
+
+/* Before simplifying this take a look at bug 293638... */
+#define FIELDDEFA(lvl, reg, vend, bitpos, size, name, s, m, v, c3, f) \
+ CPUID_##vend##_ID##lvl##reg##_##name##_SHIFT = bitpos, \
+ CPUID_##vend##_ID##lvl##reg##_##name##_MASK = \
+ VMW_BIT_MASK(size) << bitpos, \
+ CPUID_FEATURE_##vend##_ID##lvl##reg##_##name = \
+ CPUID_##vend##_ID##lvl##reg##_##name##_MASK,
+
+#define FLAGDEFA FIELDDEFA
+#define FLAGDEF FIELDDEF
+
+enum {
+ /* Define data for every CPUID field we have */
+ CPUID_FIELD_DATA
+};
+#undef VMW_BIT_MASK
+#undef FIELDDEF
+#undef FLAGDEF
+#undef FIELDDEFA
+#undef FLAGDEFA
+
+/*
+ * Legal CPUID config file mask characters. For a description of the
+ * cpuid masking system, please see:
+ *
+ * http://vmweb.vmware.com/~mts/cgi-bin/view.cgi/Apps/CpuMigrationChecks
+ */
+
+#define CPUID_MASK_HIDE_CHR '0'
+#define CPUID_MASK_HIDE_STR "0"
+#define CPUID_MASK_FORCE_CHR '1'
+#define CPUID_MASK_FORCE_STR "1"
+#define CPUID_MASK_PASS_CHR '-'
+#define CPUID_MASK_PASS_STR "-"
+#define CPUID_MASK_TRUE_CHR 'T'
+#define CPUID_MASK_TRUE_STR "T"
+#define CPUID_MASK_FALSE_CHR 'F'
+#define CPUID_MASK_FALSE_STR "F"
+#define CPUID_MASK_IGNORE_CHR 'X'
+#define CPUID_MASK_IGNORE_STR "X"
+#define CPUID_MASK_HOST_CHR 'H'
+#define CPUID_MASK_HOST_STR "H"
+#define CPUID_MASK_RSVD_CHR 'R'
+#define CPUID_MASK_RSVD_STR "R"
+#define CPUID_MASK_INSTALL_CHR 'I'
+#define CPUID_MASK_INSTALL_STR "I"
+
+/*
+ * If a level is listed as not masked/tested in CPUID_LEVELS above,
+ * use all "don't care" values for its mask.
+ */
+
+#define CPT_DFLT_UNDEFINED_MASK "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX"
+
+/*
+ * When LM is disabled, we overlay the following masks onto the
+ * guest's default masks. Any level that is not defined below should
+ * be treated as all "-"s
+ */
+
+#define CPT_ID1ECX_LM_DISABLED "----:----:----:----:--0-:----:----:----"
+#define CPT_ID81EDX_LM_DISABLED "--0-:----:----:----:----:----:----:----"
+#define CPT_ID81ECX_LM_DISABLED "----:----:----:----:----:----:----:---0"
+
+#define CPT_GET_LM_DISABLED_MASK(lvl, reg) \
+ ((lvl == 1 && reg == CPUID_REG_ECX) ? CPT_ID1ECX_LM_DISABLED : \
+ (lvl == 0x80000001 && reg == CPUID_REG_ECX) ? CPT_ID81ECX_LM_DISABLED : \
+ (lvl == 0x80000001 && reg == CPUID_REG_EDX) ? CPT_ID81EDX_LM_DISABLED : \
+ NULL)
+
+/*
+ * Macro to define GET and SET functions for various common CPUID
+ * fields. To create function for a new field, simply name it (CPUID_
+ * and CPUID_SET_ are automatically prepended), and list the field
+ * name that it needs to use.
+ */
+
+#define FIELD_FUNC(name, field) \
+ static INLINE uint32 CPUID_##name(uint32 reg) \
+ { \
+ return (reg & field##_MASK) >> field##_SHIFT; \
+ } \
+ static INLINE void CPUID_SET_##name(uint32 *reg, uint32 val) \
+ { \
+ *reg = (*reg & ~field##_MASK) | (val << field##_SHIFT); \
+ }
+
+FIELD_FUNC(STEPPING, CPUID_COMMON_ID1EAX_STEPPING)
+FIELD_FUNC(MODEL, CPUID_COMMON_ID1EAX_MODEL)
+FIELD_FUNC(FAMILY, CPUID_COMMON_ID1EAX_FAMILY)
+FIELD_FUNC(TYPE, CPUID_COMMON_ID1EAX_TYPE)
+FIELD_FUNC(EXTENDED_MODEL, CPUID_COMMON_ID1EAX_EXTMODEL)
+FIELD_FUNC(EXTENDED_FAMILY, CPUID_COMMON_ID1EAX_EXTFAMILY)
+FIELD_FUNC(LCPU_COUNT, CPUID_COMMON_ID1EBX_LCPU_COUNT)
+FIELD_FUNC(APICID, CPUID_COMMON_ID1EBX_APICID)
+FIELD_FUNC(PA_BITS, CPUID_COMMON_ID88EAX_PHYSBITS)
+FIELD_FUNC(VIRT_BITS, CPUID_COMMON_ID88EAX_VIRTBITS)
+FIELD_FUNC(SVM_REVISION, CPUID_AMD_ID8AEAX_SVM_REVISION)
+FIELD_FUNC(SVM_N_ASIDS, CPUID_AMD_ID8AEBX_SVM_N_ASIDS)
+FIELD_FUNC(INTEL_CORE_COUNT, CPUID_INTEL_ID4EAX_CORE_COUNT)
+FIELD_FUNC(AMD_CORE_COUNT, CPUID_AMD_ID88ECX_CORE_COUNT)
+FIELD_FUNC(AMD_APICID_COREID_SIZE, CPUID_AMD_ID88ECX_APICID_COREID_SIZE)
+FIELD_FUNC(AMD_EXTAPICSPC, CPUID_AMD_ID81ECX_EXTAPICSPC)
+FIELD_FUNC(NUM_PMCS, CPUID_INTEL_IDAEAX_NUM_PMCS)
+FIELD_FUNC(MWAIT_MIN_SIZE, CPUID_COMMON_ID5EAX_MWAIT_MIN_SIZE)
+FIELD_FUNC(MWAIT_MAX_SIZE, CPUID_COMMON_ID5EBX_MWAIT_MAX_SIZE)
+FIELD_FUNC(MWAIT_C0_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C0_SUBSTATE)
+FIELD_FUNC(MWAIT_C1_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C1_SUBSTATE)
+FIELD_FUNC(MWAIT_C2_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C2_SUBSTATE)
+FIELD_FUNC(MWAIT_C3_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C3_SUBSTATE)
+FIELD_FUNC(MWAIT_C4_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C4_SUBSTATE)
+#undef FIELD_FUNC
+
+
+/*
+ * Definitions of various fields' values and more complicated
+ * macros/functions for reading cpuid fields.
+ */
+
+/* Effective Intel CPU Families */
+#define CPUID_FAMILY_486 4
+#define CPUID_FAMILY_P5 5
+#define CPUID_FAMILY_P6 6
+#define CPUID_FAMILY_P4 15
+
+/* Effective AMD CPU Families */
+#define CPUID_FAMILY_5x86 4
+#define CPUID_FAMILY_K5 5
+#define CPUID_FAMILY_K6 5
+#define CPUID_FAMILY_K7 6
+#define CPUID_FAMILY_K8 15
+#define CPUID_FAMILY_K8L 16
+#define CPUID_FAMILY_K8MOBILE 17
+#define CPUID_FAMILY_EXTENDED 15
+
+/* Intel model information */
+#define CPUID_MODEL_PPRO 1
+#define CPUID_MODEL_PII_03 3
+#define CPUID_MODEL_PII_05 5
+#define CPUID_MODEL_CELERON_06 6
+#define CPUID_MODEL_PM_09 9
+#define CPUID_MODEL_PM_0D 13
+#define CPUID_MODEL_PM_0E 14 // Yonah / Sossaman
+#define CPUID_MODEL_CORE_0F 15 // Conroe / Merom
+#define CPUID_MODEL_CORE_17 0x17 // Penryn
+#define CPUID_MODEL_NEHALEM_1A 0x1a // Nehalem / Gainestown
+#define CPUID_MODEL_ATOM_1C 0x1c // Silverthorne / Diamondville
+#define CPUID_MODEL_CORE_1D 0x1d // Dunnington
+
+#define CPUID_MODEL_PIII_07 7
+#define CPUID_MODEL_PIII_08 8
+#define CPUID_MODEL_PIII_0A 10
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPUID_IsVendor{AMD,Intel} --
+ *
+ * Determines if the vendor string in cpuid id0 is from {AMD,Intel}.
+ *
+ * Results:
+ * True iff vendor string is CPUID_{AMD,INTEL}_VENDOR_STRING
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE Bool
+CPUID_IsRawVendor(CPUIDRegs *id0, const char* vendor)
+{
+ // hard to get strcmp() in some environments, so do it in the raw
+ return (id0->ebx == *(const uint32 *) (vendor + 0) &&
+ id0->ecx == *(const uint32 *) (vendor + 4) &&
+ id0->edx == *(const uint32 *) (vendor + 8));
+}
+
+static INLINE Bool
+CPUID_IsVendorAMD(CPUIDRegs *id0)
+{
+ return CPUID_IsRawVendor(id0, CPUID_AMD_VENDOR_STRING);
+}
+
+static INLINE Bool
+CPUID_IsVendorIntel(CPUIDRegs *id0)
+{
+ return CPUID_IsRawVendor(id0, CPUID_INTEL_VENDOR_STRING);
+}
+
+
+static INLINE uint32
+CPUID_EFFECTIVE_FAMILY(uint32 v) /* %eax from CPUID with %eax=1. */
+{
+ return CPUID_FAMILY(v) +
+ (CPUID_FAMILY(v) == CPUID_FAMILY_EXTENDED ? CPUID_EXTENDED_FAMILY(v) : 0);
+}
+
+/* Normally only used when FAMILY==CPUID_FAMILY_EXTENDED, but Intel is
+ * now using the extended model field for FAMILY==CPUID_FAMILY_P6 to
+ * refer to the newer Core2 CPUs
+ */
+static INLINE uint32
+CPUID_EFFECTIVE_MODEL(uint32 v) /* %eax from CPUID with %eax=1. */
+{
+ return CPUID_MODEL(v) + (CPUID_EXTENDED_MODEL(v) << 4);
+}
+
+/*
+ * Notice that CPUID families for Intel and AMD overlap. The following macros
+ * should only be used AFTER the manufacturer has been established (through
+ * the use of CPUID standard function 0).
+ */
+static INLINE Bool
+CPUID_FAMILY_IS_486(uint32 _eax)
+{
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_486;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_P5(uint32 _eax)
+{
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_P5;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_P6(uint32 _eax)
+{
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_P6;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_PENTIUM4(uint32 _eax)
+{
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_P4;
+}
+
+/*
+ * Intel Pentium M processors are Yonah/Sossaman or an older P-M
+ */
+static INLINE Bool
+CPUID_UARCH_IS_PENTIUM_M(uint32 v) // IN: %eax from CPUID with %eax=1.
+{
+ /* Assumes the CPU manufacturer is Intel. */
+ return CPUID_FAMILY_IS_P6(v) &&
+ (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_PM_09 ||
+ CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_PM_0D ||
+ CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_PM_0E);
+}
+
+/*
+ * Intel Core processors are Merom, Conroe, Woodcrest, Clovertown,
+ * Penryn, Dunnington, Kentsfield, Yorktown, Harpertown, ........
+ */
+static INLINE Bool
+CPUID_UARCH_IS_CORE(uint32 v) // IN: %eax from CPUID with %eax=1.
+{
+ uint32 model = CPUID_EFFECTIVE_MODEL(v);
+ /* Assumes the CPU manufacturer is Intel. */
+ return CPUID_FAMILY_IS_P6(v) &&
+ model >= CPUID_MODEL_CORE_0F &&
+ (model < CPUID_MODEL_NEHALEM_1A ||
+ model == CPUID_MODEL_CORE_1D);
+}
+
+
+/*
+ * Intel Nehalem processors are: Nehalem, Gainestown.
+ */
+static INLINE Bool
+CPUID_UARCH_IS_NEHALEM(uint32 v) // IN: %eax from CPUID with %eax=1.
+{
+ /* Assumes the CPU manufacturer is Intel. */
+ return CPUID_FAMILY_IS_P6(v) &&
+ CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_NEHALEM_1A;
+}
+
+
+static INLINE Bool
+CPUID_FAMILY_IS_K7(uint32 _eax)
+{
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K7;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_K8(uint32 _eax)
+{
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K8;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_K8EXT(uint32 _eax)
+{
+ /*
+ * We check for this pattern often enough that it's
+ * worth a separate function, for syntactic sugar.
+ */
+ return CPUID_FAMILY_IS_K8(_eax) &&
+ CPUID_EXTENDED_MODEL(_eax) != 0;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_K8L(uint32 _eax)
+{
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K8L;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_K8MOBILE(uint32 _eax)
+{
+ /* Essentially a K8 (not K8L) part, but with mobile features. */
+ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K8MOBILE;
+}
+
+static INLINE Bool
+CPUID_FAMILY_IS_K8STAR(uint32 _eax)
+{
+ /*
+ * Read function name as "K8*", as in wildcard.
+ * Matches K8 or K8L or K8MOBILE
+ */
+ return CPUID_FAMILY_IS_K8(_eax) || CPUID_FAMILY_IS_K8L(_eax) ||
+ CPUID_FAMILY_IS_K8MOBILE(_eax);
+}
+
+
+#define CPUID_TYPE_PRIMARY 0
+#define CPUID_TYPE_OVERDRIVE 1
+#define CPUID_TYPE_SECONDARY 2
+
+#define CPUID_INTEL_ID4EAX_CACHE_TYPE_NULL 0
+#define CPUID_INTEL_ID4EAX_CACHE_TYPE_DATA 1
+#define CPUID_INTEL_ID4EAX_CACHE_TYPE_INST 2
+#define CPUID_INTEL_ID4EAX_CACHE_TYPE_UNIF 3
+
+#define CPUID_INTEL_ID4EAX_CACHE_SELF_INIT 0x00000100
+#define CPUID_INTEL_ID4EAX_CACHE_FULLY_ASSOC 0x00000200
+
+
+/*
+ * On AMD chips before Opteron and Intel chips before P4 model 3,
+ * WRMSR(TSC) clears the upper half of the TSC instead of using %edx.
+ */
+static INLINE Bool
+CPUID_FullyWritableTSC(Bool isIntel, // IN
+ uint32 v) // IN: %eax from CPUID with %eax=1.
+{
+ /*
+ * Returns FALSE if:
+ * - Intel && P6 (pre-core) or
+ * - Intel && P4 (model < 3) or
+ * - !Intel && pre-K8 Opteron
+ * Otherwise, returns TRUE.
+ */
+ return !((isIntel &&
+ ((CPUID_FAMILY_IS_P6(v) &&
+ CPUID_EFFECTIVE_MODEL(v) < CPUID_MODEL_PM_0E) ||
+ (CPUID_FAMILY_IS_PENTIUM4(v) &&
+ CPUID_EFFECTIVE_MODEL(v) < 3))) ||
+ (!isIntel &&
+ CPUID_FAMILY(v) < CPUID_FAMILY_K8));
+}
+
+
+/*
+ * For certain AMD processors, an lfence instruction is necessary at various
+ * places to ensure ordering.
+ */
+
+static INLINE Bool
+CPUID_VendorRequiresFence(CpuidVendors vendor)
+{
+ return vendor == CPUID_VENDOR_AMD;
+}
+
+static INLINE Bool
+CPUID_VersionRequiresFence(uint32 version)
+{
+ return CPUID_EFFECTIVE_FAMILY(version) == CPUID_FAMILY_K8 &&
+ CPUID_EFFECTIVE_MODEL(version) < 0x40;
+}
+
+static INLINE Bool
+CPUID_ID0RequiresFence(CPUIDRegs *id0)
+{
+ if (id0->eax == 0) {
+ return FALSE;
+ }
+ return CPUID_IsVendorAMD(id0);
+}
+
+static INLINE Bool
+CPUID_ID1RequiresFence(CPUIDRegs *id1)
+{
+ return CPUID_VersionRequiresFence(id1->eax);
+}
+
+static INLINE Bool
+CPUID_RequiresFence(CpuidVendors vendor, // IN
+ uint32 version) // IN: %eax from CPUID with %eax=1.
+{
+ return CPUID_VendorRequiresFence(vendor) &&
+ CPUID_VersionRequiresFence(version);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CPUID_CountsCPUIDAsBranch --
+ *
+ * Returns TRUE iff the cpuid given counts CPUID as a branch
+ * (i.e. is a pre-Merom E CPU).
+ *
+ *----------------------------------------------------------------------
+ */
+
+static INLINE Bool
+CPUID_CountsCPUIDAsBranch(uint32 v) /* %eax from CPUID with %eax=1 */
+{
+ /*
+ * CPUID no longer a branch starting with Merom E. Bug 148411.
+ * Penryn (Extended Model: 1) also has this fixed.
+ *
+ * Merom E is: CPUID.1.eax & 0xfff = 0x6f9
+ */
+ return !(CPUID_FAMILY_IS_P6(v) &&
+ (CPUID_EFFECTIVE_MODEL(v) > CPUID_MODEL_CORE_0F ||
+ (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_CORE_0F &&
+ CPUID_STEPPING(v) >= 9)));
+}
+
+/*
+ * On Merom and later Intel chips, not present PDPTEs with reserved bits
+ * set do not fault with a #GP. See PR# 109120.
+ */
+static INLINE Bool
+CPUID_FaultOnNPReservedPDPTE(uint32 v) // IN: %eax from CPUID with %eax=1.
+{
+ return !(CPUID_FAMILY_IS_P6(v) &&
+ (CPUID_EFFECTIVE_MODEL(v) >= CPUID_MODEL_CORE_0F));
+}
+
+
+/*
+ * The following low-level functions compute the number of
+ * cores per cpu. They should be used cautiously because
+ * they do not necessarily work on all types of CPUs.
+ * High-level functions that are correct for all CPUs are
+ * available elsewhere: see lib/cpuidInfo/cpuidInfo.c.
+ */
+
+static INLINE uint32
+CPUID_IntelCoresPerPackage(uint32 v) /* %eax from CPUID with %eax=4 and %ecx=0. */
+{
+ // Note: This is not guaranteed to work on older Intel CPUs.
+ return 1 + CPUID_INTEL_CORE_COUNT(v);
+}
+
+static INLINE uint32
+CPUID_AMDCoresPerPackage(uint32 v) /* %ecx from CPUID with %eax=0x80000008. */
+{
+ // Note: This is not guaranteed to work on older AMD CPUs.
+ return 1 + CPUID_AMD_CORE_COUNT(v);
+}
+
+/*
+ * Hypervisor CPUID space is 0x400000XX.
+ */
+static INLINE Bool
+CPUID_IsHypervisorLevel(uint32 level, uint32 *offset)
+{
+ *offset = level & 0xff;
+ return (level & 0xffffff00) == 0x40000000;
+}
+
+
+#endif
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+################################################################################
+### Copyright 2008 VMware, Inc. All rights reserved.
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of version 2 of the GNU General Public License as
+### published by the Free Software Foundation.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+################################################################################
+
+bin_PROGRAMS = vmware-vmblock-fuse
+
+# _XOPEN_SOURCE needs to be defined to at least 500 when compiling
+# vmblock-fuse's os.h to get pthread_rwlock_t.
+# -D_FILE_OFFSET_BITS=64 required for fuse.
+
+AM_CFLAGS =
+AM_CFLAGS += -Dvmblock_fuse
+AM_CFLAGS += -D_XOPEN_SOURCE=500
+AM_CFLAGS += -DUSERLEVEL
+AM_CFLAGS += -D_FILE_OFFSET_BITS=64
+AM_CFLAGS += @FUSE_CPPFLAGS@
+AM_CFLAGS += @VMWARE_USER_CPPFLAGS@
+
+vmware_vmblock_fuse_LDADD =
+vmware_vmblock_fuse_LDADD += ../lib/misc/libMisc.la
+vmware_vmblock_fuse_LDADD += @FUSE_LIBS@
+
+vmware_vmblock_fuse_SOURCES =
+vmware_vmblock_fuse_SOURCES += stubs.c
+vmware_vmblock_fuse_SOURCES += block.c
+vmware_vmblock_fuse_SOURCES += util.c
+vmware_vmblock_fuse_SOURCES += fsops.c
+vmware_vmblock_fuse_SOURCES += main.c
+
+if HAVE_ICU
+ vmware_vmblock_fuse_LDADD += @ICU_LIBS@
+ vmware_vmblock_fuse_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+else
+ vmware_vmblock_fuse_LINK = $(LINK)
+endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * block.c --
+ *
+ * Blocking operation implementions for the vmblock driver.
+ */
+
+/* os.h includes necessary OS-specific headers. */
+#include "os.h"
+
+#if defined(vmblock_fuse)
+#elif defined(linux)
+# include "vmblockInt.h"
+#elif defined(sun)
+# include "module.h"
+#elif defined(__FreeBSD__)
+# include "vmblock_k.h"
+#endif
+#include "block.h"
+#include "stubs.h"
+#include "dbllnklst.h"
+
+typedef struct BlockInfo {
+ DblLnkLst_Links links;
+ os_atomic_t refcount;
+ os_blocker_id_t blocker;
+ os_completion_t completion;
+ char filename[OS_PATH_MAX];
+} BlockInfo;
+
+
+/* XXX: Is it worth turning this into a hash table? */
+static DblLnkLst_Links blockedFiles;
+static os_rwlock_t blockedFilesLock;
+static os_kmem_cache_t *blockInfoCache = NULL;
+
+/* Utility functions */
+static Bool BlockExists(const char *filename);
+static BlockInfo *GetBlock(const char *filename, const os_blocker_id_t blocker);
+static BlockInfo *AllocBlock(os_kmem_cache_t *cache,
+ const char *filename, const os_blocker_id_t blocker);
+static void FreeBlock(os_kmem_cache_t *cache, BlockInfo *block);
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockInit --
+ *
+ * Initializes blocking portion of module.
+ *
+ * Results:
+ * Zero on success, error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+BlockInit(void)
+{
+ ASSERT(!blockInfoCache);
+
+ blockInfoCache = os_kmem_cache_create("blockInfoCache",
+ sizeof (BlockInfo),
+ 0,
+ NULL);
+ if (!blockInfoCache) {
+ return OS_ENOMEM;
+ }
+
+ DblLnkLst_Init(&blockedFiles);
+ os_rwlock_init(&blockedFilesLock);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockCleanup --
+ *
+ * Cleans up the blocking portion of the module.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+BlockCleanup(void)
+{
+ ASSERT(blockInfoCache);
+ ASSERT(!DblLnkLst_IsLinked(&blockedFiles));
+
+ os_rwlock_destroy(&blockedFilesLock);
+ os_kmem_cache_destroy(blockInfoCache);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockAddFileBlock --
+ *
+ * Adds a block for the provided filename. filename should be the name of
+ * the actual file being blocked, not the name within our namespace. The
+ * provided blocker ID should uniquely identify this blocker.
+ *
+ * All calls to BlockWaitOnFile() with the same filename will not return
+ * until BlockRemoveFileBlock() is called.
+ *
+ * Note that this function assumes a block on filename does not already
+ * exist.
+ *
+ * Results:
+ * Zero on success, error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+BlockAddFileBlock(const char *filename, // IN: name of file to block
+ const os_blocker_id_t blocker) // IN: blocker adding the block
+{
+ BlockInfo *block;
+
+ ASSERT(filename);
+
+ /* Create a new block. */
+ block = AllocBlock(blockInfoCache, filename, blocker);
+ if (!block) {
+ Warning("BlockAddFileBlock: out of memory\n");
+ return OS_ENOMEM;
+ }
+ os_write_lock(&blockedFilesLock);
+
+ /*
+ * Prevent duplicate blocks of any filename. Done under same lock as list
+ * addition to ensure check for and adding of file are atomic.
+ */
+ if (BlockExists(filename)) {
+ Warning("BlockAddFileBlock: block already exists for [%s]\n", filename);
+ os_write_unlock(&blockedFilesLock);
+ FreeBlock(blockInfoCache, block);
+ return OS_EEXIST;
+ }
+
+ DblLnkLst_LinkLast(&blockedFiles, &block->links);
+
+ os_write_unlock(&blockedFilesLock);
+
+ LOG(4, "added block for [%s]\n", filename);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockRemoveFileBlock --
+ *
+ * Removes the provided file block and wakes up any threads waiting within
+ * BlockWaitOnFile(). Note that only the blocker that added a block can
+ * remove it.
+ *
+ * Results:
+ * Zero on success, error code on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+BlockRemoveFileBlock(const char *filename, // IN: block to remove
+ const os_blocker_id_t blocker) // IN: blocker removing this block
+{
+ BlockInfo *block;
+
+ ASSERT(filename);
+
+ os_write_lock(&blockedFilesLock);
+
+ block = GetBlock(filename, blocker);
+ if (!block) {
+ os_write_unlock(&blockedFilesLock);
+ return OS_ENOENT;
+ }
+
+ DblLnkLst_Unlink1(&block->links);
+ os_write_unlock(&blockedFilesLock);
+
+ /* Undo GetBlock's refcount increment first. */
+ os_atomic_dec(&block->refcount);
+
+ /*
+ * Now remove /our/ reference. (As opposed to references by waiting
+ * threads.)
+ */
+ if (os_atomic_dec_and_test(&block->refcount)) {
+ /* No threads are waiting, so clean up ourself. */
+ LOG(4, "Freeing block with no waiters on [%s]\n", filename);
+ FreeBlock(blockInfoCache, block);
+ } else {
+ /* Wake up waiters; the last one will free the BlockInfo */
+ LOG(4, "Completing block on [%s]\n", filename);
+ os_complete_all(&block->completion);
+ }
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockRemoveAllBlocks --
+ *
+ * Removes all blocks added by the provided blocker.
+ *
+ * Results:
+ * Returns the number of entries removed from the blocklist.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+unsigned int
+BlockRemoveAllBlocks(const os_blocker_id_t blocker) // IN: blocker to remove blocks for
+{
+ struct DblLnkLst_Links *curr;
+ struct DblLnkLst_Links *tmp;
+ unsigned int removed = 0;
+
+ os_write_lock(&blockedFilesLock);
+
+ DblLnkLst_ForEachSafe(curr, tmp, &blockedFiles) {
+ BlockInfo *currBlock = DblLnkLst_Container(curr, BlockInfo, links);
+ if (currBlock->blocker == blocker || blocker == OS_UNKNOWN_BLOCKER) {
+
+ DblLnkLst_Unlink1(&currBlock->links);
+
+ /*
+ * We count only entries removed from the -list-, regardless of whether
+ * or not other waiters exist.
+ */
+ ++removed;
+
+ /*
+ * BlockInfos, as the result of placing a block on a file or directory,
+ * reference themselves. When the block is lifted, we need to remove
+ * this self-reference and handle the result appropriately.
+ */
+ if (os_atomic_dec_and_test(&currBlock->refcount)) {
+ /* Free blocks without any waiters ... */
+ LOG(4, "Freeing block with no waiters for blocker [%p] (%s)\n",
+ blocker, currBlock->filename);
+ FreeBlock(blockInfoCache, currBlock);
+ } else {
+ /* ... or wakeup the waiting threads */
+ LOG(4, "Completing block for blocker [%p] (%s)\n",
+ blocker, currBlock->filename);
+ os_complete_all(&currBlock->completion);
+ }
+ }
+ }
+
+ os_write_unlock(&blockedFilesLock);
+
+ return removed;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockWaitOnFile --
+ *
+ * Searches for a block on the provided filename. If one exists, this
+ * function does not return until that block has been lifted; otherwise, it
+ * returns right away.
+ *
+ * Results:
+ * Zero on success, otherwise an appropriate system error if our sleep/
+ * block is interrupted.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+BlockWaitOnFile(const char *filename, // IN: file to block on
+ BlockHandle cookie) // IN: previously found block
+{
+ BlockInfo *block = NULL;
+ int error = 0;
+
+ ASSERT(filename);
+
+ /*
+ * Caller may have used BlockLookup to conditionally search for a
+ * block before actually going to sleep. (This allows the caller to
+ * do a little housekeeping, such as releasing vnode locks, before
+ * blocking here.)
+ */
+ if (cookie == NULL) {
+ os_read_lock(&blockedFilesLock);
+ block = GetBlock(filename, OS_UNKNOWN_BLOCKER);
+ os_read_unlock(&blockedFilesLock);
+
+ if (!block) {
+ /* This file is not blocked, just return */
+ return 0;
+ }
+ } else {
+ /*
+ * Note that the "cookie's" reference count was incremented when it
+ * was fetched via BlockLookup, so this is completely safe. (We'll
+ * decrement it below.)
+ */
+ block = cookie;
+ }
+
+ LOG(4, "(%"OS_FMTTID") Waiting for completion on [%s]\n", os_threadid, filename);
+ error = os_wait_for_completion(&block->completion);
+ LOG(4, "(%"OS_FMTTID") Wokeup from block on [%s]\n", os_threadid, filename);
+
+ /*
+ * The assumptions here are as follows:
+ * 1. The BlockInfo holds a reference to itself. (BlockInfo's refcount
+ * is initialized to 1.)
+ * 2. BlockInfo's self reference is deleted only when BlockInfo is
+ * /also/ removed removed from the block list.
+ *
+ * Therefore, if the reference count hits zero, it's because the block is
+ * no longer in the list, and there is no chance of another thread finding
+ * and referencing this block between our dec_and_test and freeing it.
+ */
+ if (os_atomic_dec_and_test(&block->refcount)) {
+ /* We were the last thread, so clean up */
+ LOG(4, "(%"OS_FMTTID") I am the last to wakeup, freeing the block on [%s]\n",
+ os_threadid, filename);
+ FreeBlock(blockInfoCache, block);
+ }
+
+ return error;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * BlockLookup --
+ *
+ * VFS-exported function for searching for blocks.
+ *
+ * Results:
+ * Opaque pointer to a blockInfo if a block is found, NULL otherwise.
+ *
+ * Side effects:
+ * Located blockInfo, if any, has an incremented reference count.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+BlockHandle
+BlockLookup(const char *filename, // IN: pathname to test for
+ // blocking
+ const os_blocker_id_t blocker) // IN: specific blocker to
+ // search for
+{
+ BlockInfo *block;
+
+ os_read_lock(&blockedFilesLock);
+
+ block = GetBlock(filename, blocker);
+
+ os_read_unlock(&blockedFilesLock);
+
+ return block;
+}
+
+
+#ifdef VMX86_DEVEL
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockListFileBlocks --
+ *
+ * Lists all the current file blocks.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+BlockListFileBlocks(void)
+{
+ DblLnkLst_Links *curr;
+ int count = 0;
+
+ os_read_lock(&blockedFilesLock);
+
+ DblLnkLst_ForEach(curr, &blockedFiles) {
+ BlockInfo *currBlock = DblLnkLst_Container(curr, BlockInfo, links);
+ LOG(1, "BlockListFileBlocks: (%d) Filename: [%s], Blocker: [%p]\n",
+ count++, currBlock->filename, currBlock->blocker);
+ }
+
+ os_read_unlock(&blockedFilesLock);
+
+ if (!count) {
+ LOG(1, "BlockListFileBlocks: No blocks currently exist.\n");
+ }
+}
+#endif
+
+
+/* Utility functions */
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * BlockExists --
+ *
+ * Checks if a block already exists for the provided filename.
+ *
+ * Note that this assumes the proper locking has been done on the data
+ * structure holding the blocked files (including ensuring the atomic_dec()
+ * without a kmem_cache_free() is safe).
+ *
+ * Results:
+ * TRUE if a block exists, FALSE otherwise.
+ *
+ * Side effects:
+ * If a block exists, its refcount is incremented and decremented.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static Bool
+BlockExists(const char *filename)
+{
+ BlockInfo *block = GetBlock(filename, OS_UNKNOWN_BLOCKER);
+
+ if (block) {
+ os_atomic_dec(&block->refcount);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * GetBlock --
+ *
+ * Searches for a block on the provided filename by the provided blocker.
+ * If blocker is NULL, it is ignored and any matching filename is returned.
+ * If a block is found, the refcount is incremented.
+ *
+ * Note that this assumes the proper locking has been done on the data
+ * structure holding the blocked files.
+ *
+ * Results:
+ * A pointer to the corresponding BlockInfo if found, NULL otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static BlockInfo *
+GetBlock(const char *filename, // IN: file to find block for
+ const os_blocker_id_t blocker) // IN: blocker associated with this block
+{
+ struct DblLnkLst_Links *curr;
+
+ /* XXX The following is only temporary. */
+#ifdef __FreeBSD__
+ os_assert_rwlock_held(&blockedFilesLock);
+#else
+ ASSERT(os_rwlock_held(&blockedFilesLock));
+#endif
+
+ DblLnkLst_ForEach(curr, &blockedFiles) {
+ BlockInfo *currBlock = DblLnkLst_Container(curr, BlockInfo, links);
+ if ((blocker == OS_UNKNOWN_BLOCKER || currBlock->blocker == blocker) &&
+ strcmp(currBlock->filename, filename) == 0) {
+ os_atomic_inc(&currBlock->refcount);
+ return currBlock;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * AllocBlock --
+ *
+ * Allocates and initializes a new block structure.
+ *
+ * Results:
+ * Pointer to the struct on success, NULL on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+BlockInfo *
+AllocBlock(os_kmem_cache_t *cache, // IN: cache to allocate from
+ const char *filename, // IN: filname of block
+ const os_blocker_id_t blocker) // IN: blocker id
+{
+ BlockInfo *block;
+ size_t ret;
+
+ /* Initialize this file's block structure. */
+ block = os_kmem_cache_alloc(blockInfoCache);
+ if (!block) {
+ return NULL;
+ }
+
+ ret = strlcpy(block->filename, filename, sizeof block->filename);
+ if (ret >= sizeof block->filename) {
+ Warning("BlockAddFileBlock: filename is too large\n");
+ os_kmem_cache_free(blockInfoCache, block);
+ return NULL;
+ }
+
+ DblLnkLst_Init(&block->links);
+ os_atomic_set(&block->refcount, 1);
+ os_completion_init(&block->completion);
+ block->blocker = blocker;
+
+ return block;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * FreeBlock --
+ *
+ * Frees the provided block structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+FreeBlock(os_kmem_cache_t *cache, // IN: cache block was allocated from
+ BlockInfo *block) // IN: block to free
+{
+ ASSERT(cache);
+ ASSERT(block);
+
+ os_completion_destroy(&block->completion);
+ os_kmem_cache_free(cache, block);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * block.h --
+ *
+ * Blocking operations for the vmblock driver.
+ */
+
+#ifndef __BLOCK_H__
+#define __BLOCK_H__
+
+#include "os.h"
+
+typedef struct BlockInfo * BlockHandle;
+
+/*
+ * Global functions
+ */
+
+int BlockInit(void);
+void BlockCleanup(void);
+int BlockAddFileBlock(const char *filename, const os_blocker_id_t blocker);
+int BlockRemoveFileBlock(const char *filename, const os_blocker_id_t blocker);
+unsigned int BlockRemoveAllBlocks(const os_blocker_id_t blocker);
+int BlockWaitOnFile(const char *filename, BlockHandle cookie);
+BlockHandle BlockLookup(const char *filename, const os_blocker_id_t blocker);
+#ifdef VMX86_DEVEL
+void BlockListFileBlocks(void);
+#endif
+
+#endif /* __BLOCK_H__ */
--- /dev/null
+-----------------------
+| VMBlock Fuse Design |
+-----------------------
+
+Filesystem User Interface
+-------------------------
+Reexports /tmp/VMwareDnD. I'll call this the target directory.
+
+Mount point will contain:
+ * /blockdir/
+ * /dev
+ * /version - Proposed, not implemented.
+
+/blockdir/ - Contains symlinks to the contents of the target directory. In the linux port
+this is called /mountPoint/; it is created by the blocking driver and then the fs is
+mounted there. That's kind of confusing here, because we don't have 2 separate pieces.
+It's just a special directory within the file system.
+
+/dev - Control file. Control operations happen by opening this file and doing a write on
+it. The data that we write is a character representing the operation to be done,
+sometimes followed by the path of the file to be blocked or unblocked (its original path,
+eg: "/tmp/VMwareDnD/foo/bar"). The count argument to write must be the length of the
+string. This will be the length of the path + 1 for the operation character.
+
+COMMAND OPERATION CHARACTER EXAMPLE
+Add block a write(fd, "a/some/path/in/target/dir", 25)
+Delete block d write(fd, "d/another/path/in/target/dir", 28)
+List blocks (only available l (lower case L) write(fd, "l", 1)
+ with VMX86_DEVEL)
+
+I was hoping to use the same interface as the linux kernel port, but doing writes with
+too-long sizes through fuse isn't supported and may not always work.
+
+When an entry in /blockdir/ is blocked, any readlink() on that entry will block.
+
+The control file is owned and only writable by root. We might want to change that (more
+below). Right now we have a wrapper with root priveleges acquire a vmblock control file
+descriptor, but we want to get rid of it. Specifically, this will be useful for remote
+DnD where there might not be a process with root permission.
+
+/version - NEW. XXX: Maybe we want to add a read-only file which contains a version
+number? This would allow us to make changes later and have it be easy for the tools to
+know what they can do.
+
+
+Internal Design
+---------------
+Uses FUSE's default multithreaded mode where normally, each filesystem request is serviced
+by it's own thread. This will allow accesses to block without preventing new accesses.
+
+IMPORTANT: Please read the deadlock section for more about the threading model.
+
+I'm using the common blocking code that the rest of the vmblock implementations share. It
+stores blocks as a linked list of structs which contain the direct path of the file being
+blocked (eg: "/tmp/VMwareDnD/foo/bar"), variables to link it into the list, an id of the
+control file which caused the block, a completion/condition variable, and a reference
+count. The purpose of the ref count is so that the block instance is only cleaned up once
+all threads waiting on it have woken up. Or more specifically, so that the last thread to
+wake up knows that the block isn't being used any more and will clean it up.
+
+The common blocking code uses a few constructs which are defined by port specific headers
+(os.h). For vmblock-fuse, the global blocked list will be protected by a pthread rwlock.
+Blocking will be done by waiting on condition variables. The completion type is a struct
+containing a mutex, condition variable, and done int (treated as a bool). Macros are
+defined to manipulate these properly. The atomic type used for reference counting is a
+vm_atomic type.
+
+
+Deadlock
+--------
+Older versions of fuse are limited to having 10 worker threads for processing filesystem
+requests. Once this limit is exceeded, fuse will block any operation until a thread
+becomes available to handle it. So if a block is placed on a file and many requests to
+access it are made and block, the filesystem will deadlock.
+
+This limit has been removed in fuse 2.6.0.
+The macfuse patch to fuse doesn't seem to affect this functionality and it's based off of
+fuse 2.7. I haven't testing it on macfuse though.
+
+Another possible deadlock from a fuse-devel email:
+
+On Wed, 9 Jul 2008, bargav yaarov wrote:
+
+> I have a point within my read handler where I hold a pthread_rwlock_t. At the same
+> time, another read handler is blocked waiting to acquire this lock. Now, if I attempt
+> to unmount at this time, the fuse library code calls pthread_cancel() against the
+> handling thread and I am left in a deadlocked state. How do you suggest I handle this
+> situation?
+
+Actually, CVS version of fuse has a fix for this:
+
+ * Only enable cancelation when reading a request, otherwise cancellation could happen
+ with a mutex held, which could hang the process on umount
+
+... OK, I put out a prerelease so there's no need to mess with CVS:
+
+http://sourceforge.net/project/showfiles.php?group_id=121684&package_id=140422
+
+XXX: Check if I hit this problem.
+
+
+Testing
+-------
+ * Some unit tests for fsops functions- bora/modules/vmblock/test/fuse-fsops-test.cpp.
+ * Existing full vmblock tester- bora/modules/vmblock/test/vmblocktest.c.
+ * Very simple testing program to block and unblock a single file- demo.c.
+
+
+Enforcing Permissions
+---------------------
+The vmblock-fuse code correctly report the file permissions but doesn't check them. It
+should be mounted with the "-o default_permissions" option which will tell fuse to do the
+checking for us.
+
+If the filesystem it going to be mounted by a non-root user for system wide use, it
+should also be given the "-o allow_other" option to allow users other than the one that
+mounted it to be able to use it. This will require enabling the "user_allow_other" option
+in /etc/fuse.conf.
+
+
+API
+---
+There are some helper functions, macros, and constants in the public header vmblock.h.
+I've added a new set of them which work with vmblock-fuse and I hope make using it API
+compatible with the other vmblock ports. I need to look at users of vmblock to make sure
+they access vmblock via the header the way I expect.
+
+
+Blocking Indexing Software
+--------------------------
+There are a number of program which may be running on various systems which index the
+contents of the computer. If an indexer was to hit a blocked file, it would get stuck
+which is undesireable. For the linux kernel port, the blocked files are in /proc which
+shouldn't be indexed. On solaris and freebsd, it's in /var/run which I guess is also not
+normally indexed. Right now, vmblock-fuse gets mounted for system wide use in
+/tmp/vmblock/.
+
+(s)locate/updatedb (linux)
+On ubuntu 8.04, /etc/updatedb.conf it set to ignore /tmp. I would guess this is pretty
+standard.
+
+beagle (linux)
+According to http://beagle-project.org/Indexing_Data, by default, everything in $(HOME)
+is indexed.
+
+spotlight (mac)
+I don't know if /tmp even exists on macs. I need to revisit this once I get vmblock-fuse
+running on mac.
+
+google desktop search (windows, linux)
+On linux, doesn't index /tmp. Also doesn't index hidden (.*) directories. This might be
+useful to know later on. In case it matters, a list of things that it doesn't search on
+windows is at http://desktop.google.com/support/bin/answer.py?answer=12634.
+
+
+Unprivileged User Blocking
+--------------------------
+If I have time later on, I'd like to investigate having non-root users be able to create
+blocks. This will be helpful for scenarios where we don't have a process with root
+privileges to do our blocking for us, like remote copy/paste. The security aspects need
+some careful thought but I'd like to jot down some notes on thoughts Elliot and I had
+about possible ways to do this.
+
+ * Have control file be accessible by any user as far as vmblock-fuse is concerned.
+ Have each user mount their own copy of vmblock which only they can access. Possibly
+ in their home dir. This will basically work with the current code if mounted
+ without the default_permissions option. I might want to change the permissions it
+ shows on the files.
+ * Have a single vmblock-fuse mount. Any user can add blocks but those blocks only
+ apply to accesses by that user.
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * fsops.c --
+ *
+ * Vmblock fuse filesystem operations.
+ *
+ * See design.txt for more information.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdint.h>
+
+#include "fsops.h"
+#include "block.h"
+#include "stubs.h"
+#include "vm_basic_types.h"
+
+/* Regular directories on a linux ext3 partition are 4K. */
+
+#define DIR_SIZE (4 * 1024)
+
+typedef struct vmblockSpecialDirEntry {
+ char *path;
+ int mode;
+ unsigned int nlink;
+ size_t size;
+} vmblockSpecialDirEntry;
+
+static vmblockSpecialDirEntry specialDirEntries[] = {
+ { "/", S_IFDIR | 0555, 3, DIR_SIZE },
+ { CONTROL_FILE, S_IFREG | 0200, 1, 0 },
+ { REDIRECT_DIR, S_IFDIR | 0555, 3, DIR_SIZE },
+ { NULL, 0, 0, 0 }
+};
+static vmblockSpecialDirEntry symlinkDirEntry =
+ { REDIRECT_DIR "/*", S_IFLNK | 0777, 1, -1 };
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * RealReadLink --
+ *
+ * Gets the target of a symlink.
+ *
+ * The same idea as unix readlink() except that it returns 0 on
+ * success, and fills buf with a null terminated string. If target doesn't
+ * fit in buf, it is truncated.
+ *
+ * Results:
+ * 0 on success. Possible errors (as negative values):
+ * -EINVAL bufSize is not positive.
+ * -ENOENT path does not exist.
+ * -ENAMETOOLONG path or target of symlink was too long.
+ * Any other errors lstat can return.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+RealReadLink(const char *path, // IN: Path within vmblock filesystem. Must be
+ // within the redirect directory.
+ char *buf, // OUT: Target of link if valid (or part of it).
+ size_t bufSize) // IN: Size of buf.
+{
+ int status;
+ const char redirectPrefix[] = REDIRECT_DIR "/";
+ const int redirectPrefixLength = sizeof redirectPrefix - 1;
+ const char targetPrefix[] = TARGET_DIR "/";
+ const int targetPrefixLength = sizeof targetPrefix - 1;
+ const char *relativeTarget = path + redirectPrefixLength;
+ char target[PATH_MAX + 1];
+ const size_t spaceForRelativeTarget = sizeof target - targetPrefixLength;
+ struct stat dummyStatBuf; // Don't care what goes here.
+
+ /* TARGET_DIR + '/' needs to leave room for relative target. */
+ ASSERT_ON_COMPILE(sizeof TARGET_DIR + 1 < PATH_MAX);
+
+ ASSERT(strncmp(path, redirectPrefix, redirectPrefixLength) == 0 &&
+ strlen(path) > redirectPrefixLength);
+ if (bufSize < 1) {
+ return -EINVAL;
+ }
+
+ /*
+ * Assemble path to destination of link. This goes into a temporary buffer
+ * instead of directly into buf, because buf may not be big enough for the
+ * whole thing, but this should still return success if the target
+ * exists which means it must lstat the full target path.
+ */
+
+ strlcpy(target, targetPrefix, sizeof target);
+
+ /*
+ * spaceForRelativeTarget must be greater than strlen(relativeTarget) to
+ * leave room for the nul terminator.
+ */
+
+ if (spaceForRelativeTarget <= strlen(relativeTarget)) {
+ return -ENAMETOOLONG;
+ }
+ strlcpy(target + targetPrefixLength, relativeTarget, spaceForRelativeTarget);
+
+ /* Verify that target exists. */
+
+ status = lstat(target, &dummyStatBuf);
+ if (status != 0) {
+ return -errno;
+ }
+
+ strlcpy(buf, target, bufSize);
+ return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockReadLink --
+ *
+ * Gets the target of a symlink. Blocks if there is a block on the path.
+ *
+ * Basically the same as unix readlink() except that it returns 0 on
+ * success, fills buf with a null terminated string, and adds our blocking
+ * functionality.
+ *
+ * Results:
+ * 0 on success. Possible errors (as negative values):
+ * -EINVAL bufSize is not positive.
+ * -ENOENT path is not within the redirect directory.
+ * -ENAMETOOLONG path or target of symlink was too long.
+ * Any other errors lstat can return.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMBlockReadLink(const char *path, // IN: Path within vmblock (top level) mount point.
+ char *buf, // OUT: Target of link if valid (or part of it).
+ size_t bufSize) // IN: Size of buf.
+{
+ int status;
+ char target[PATH_MAX + 1];
+
+ if (strncmp(path, REDIRECT_DIR, strlen(REDIRECT_DIR)) != 0) {
+ return -ENOENT;
+ }
+ status = RealReadLink(path, target, sizeof target);
+ if (status < 0) {
+ return status;
+ }
+
+ BlockWaitOnFile(target, NULL);
+
+ strlcpy(buf, target, bufSize);
+ return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SetTimesToNow --
+ *
+ * Sets the atime, mtime, and ctime of a stat struct to the current time.
+ * If there's an error getting the current time, sets them to 0.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+SetTimesToNow(struct stat *statBuf) // OUT
+{
+ int status;
+ struct timeval time;
+
+ status = gettimeofday(&time, NULL);
+ if (status < 0) {
+ statBuf->st_atime = statBuf->st_mtime = statBuf->st_ctime = 0;
+ } else {
+ statBuf->st_atime = statBuf->st_mtime = statBuf->st_ctime = time.tv_sec;
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockGetAttr --
+ *
+ * Gets the attributes of a directory entry. Equivalent to stat().
+ *
+ * Returns fixed results for /, /dev, and /blockdir. For anything within
+ * /blockdir, if a target exists with that name in the target directory,
+ * it returns fixed stats for a symlink. Reads the stats to return from the
+ * globals specialDirEntries and symlinkDirEntries.
+ *
+ * Results:
+ * 0 on success. Possible errors (as negative values):
+ * -ENOENT File or directory does not exist.
+ * -ENAMETOOLONG path too long.
+ * Any other errors lstat can return.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMBlockGetAttr(const char *path, // IN: File to get attributes of.
+ struct stat *statBuf) // OUT: Where to put the attributes.
+{
+ int status;
+ char target[PATH_MAX + 1];
+ vmblockSpecialDirEntry *dirEntry;
+ ASSERT(path != NULL);
+ ASSERT(statBuf != NULL);
+
+ if (strlen(path) > PATH_MAX) {
+ return -ENAMETOOLONG;
+ }
+
+ for (dirEntry = specialDirEntries; dirEntry->path != NULL; ++dirEntry) {
+ if (strcmp(path, dirEntry->path) == 0) {
+ memset(statBuf, 0, sizeof *statBuf);
+ statBuf->st_mode = dirEntry->mode;
+ statBuf->st_nlink = dirEntry->nlink;
+ statBuf->st_size = dirEntry->size;
+ SetTimesToNow(statBuf);
+ return 0;
+ }
+ }
+ if (strncmp(path, REDIRECT_DIR, strlen(REDIRECT_DIR)) == 0) {
+ status = RealReadLink(path, target, sizeof target);
+ LOG(4, "Called RealReadLink which returned: %d\n", status);
+ if (status != 0) {
+ return status;
+ }
+ memset(statBuf, 0, sizeof *statBuf);
+ statBuf->st_mode = symlinkDirEntry.mode;
+ statBuf->st_nlink = symlinkDirEntry.nlink;
+ statBuf->st_size = strlen(target);
+ SetTimesToNow(statBuf);
+ return 0;
+ }
+ return -ENOENT;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ExternalReadDir --
+ *
+ * Gets the contents of a directory outside the vmblock-fuse filesystem.
+ *
+ * Results:
+ * 0 on success. Possible errors (as negative values):
+ * -EACCES Permission denied.
+ * -EBADF Invalid directory stream descriptor (I don't think this can
+ * happen).
+ * -EMFILE Too many file descriptors in use by process.
+ * -ENFILE Too many files are currently open in the system.
+ * -ENOENT Directory does not exist, or name is an empty string.
+ * -ENOMEM Insufficient memory to complete the operation.
+ * -ENOTDIR path is not a directory.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+ExternalReadDir(const char *path, // IN: Full (real) path to
+ // directory to read.
+ void *buf, // OUT: Destination for
+ // directory listing.
+ fuse_fill_dir_t filler, // IN: Function to use to add
+ // an entry to buf. Returns
+ // 1 if buffer full, 0
+ // otherwise.
+ off_t offset, // IN: Ignored.
+ struct fuse_file_info *fileInfo) // IN: Ignored.
+{
+ int status;
+ struct dirent *dentry = NULL;
+ struct stat statBuf;
+ DIR *dir = NULL;
+
+ LOG(4, "ExternalReadDir: path: %s\n", path);
+ dir = opendir(path);
+ if (dir == NULL) {
+ return -errno;
+ }
+
+ /*
+ * readdir() only needs to fill in the type bits of the mode in the stat
+ * struct it passes to filler().
+ * http://sourceforge.net/mailarchive/forum.php?thread_name=E1KNlwx-00008e-Fb%40pomaz-ex.szeredi.hu&forum_name=fuse-devel
+ */
+
+ memset(&statBuf, 0, sizeof statBuf);
+ statBuf.st_mode = S_IFLNK;
+
+ /* Clear errno because readdir won't change it if it succeeds. */
+
+ errno = 0;
+ while ((dentry = readdir(dir)) != NULL) {
+ status = filler(buf, dentry->d_name, &statBuf, 0);
+ if (status == 1) {
+ break;
+ }
+ }
+ if (errno != 0) {
+ return -errno;
+ }
+
+ status = closedir(dir);
+ if (status != 0) {
+ return -errno;
+ }
+ return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockReadDir --
+ *
+ * Gets the contents of a directory within the filesystem.
+ *
+ * If filler() runs out of memory, it will store an error in buf (which fuse
+ * will check) and then return 1.
+ * http://sourceforge.net/mailarchive/forum.php?thread_name=E1KMUnu-0007gI-29%40pomaz-ex.szeredi.hu&forum_name=fuse-devel
+ *
+ * Results:
+ * 0 on success or filler() fail. Possible errors (as negative values):
+ * -EACCES Permission denied.
+ * -EBADF Invalid directory stream descriptor (I don't think this can
+ * happen).
+ * -EMFILE Too many file descriptors in use by process.
+ * -ENFILE Too many files are currently open in the system.
+ * -ENOENT Directory does not exist, or name is an empty string.
+ * -ENOMEM Insufficient memory to complete the operation.
+ * -ENOTDIR path is not a directory.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMBlockReadDir(const char *path, // IN: Directory to read.
+ void *buf, // OUT: Where to put directory
+ // listing.
+ fuse_fill_dir_t filler, // IN: Function to add an entry
+ // to buf.
+ off_t offset, // IN: Ignored.
+ struct fuse_file_info *fileInfo) // IN: Ignored.
+{
+ struct stat fileStat;
+ struct stat dirStat;
+ LOG(4, "VMBlockReadDir: path: %s\n", path);
+
+ /*
+ * readdir() only needs to fill in the type bits of the mode in the stat
+ * struct it passes to filler().
+ * http://sourceforge.net/mailarchive/forum.php?thread_name=E1KNlwx-00008e-Fb%40pomaz-ex.szeredi.hu&forum_name=fuse-devel
+ */
+
+ memset(&fileStat, 0, sizeof fileStat);
+ fileStat.st_mode = S_IFREG;
+ memset(&dirStat, 0, sizeof dirStat);
+ dirStat.st_mode = S_IFDIR;
+
+ if (strcmp(path, "/") == 0) {
+ (void)(filler(buf, ".", &dirStat, 0) ||
+ filler(buf, "..", &dirStat, 0) ||
+ filler(buf, VMBLOCK_DEVICE_NAME, &fileStat, 0) ||
+ filler(buf, REDIRECT_DIR_NAME, &dirStat, 0));
+ return 0;
+ } else if (strcmp(path, REDIRECT_DIR) == 0) {
+ return ExternalReadDir(TARGET_DIR, buf, filler, offset, fileInfo);
+ } else {
+ return -ENOENT;
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockOpen --
+ *
+ * Opens the control file. Trying to open anything else will fail.
+ *
+ * The file handle that is filled into fileInfo is a memory address so it
+ * can be any number that is at least 1 and at most (void *)(-1);
+ *
+ * fileInfo will be marked as requiring direct io. This disables any
+ * caching, which may not be needed, but can't hurt. It also somehow helps
+ * allow the offset to advance past 4k which matters if we take out seeking
+ * to 0 before writing.
+ *
+ * Results:
+ * 0 on success. Possible errors (as negative values):
+ * -EACCES Tried to open the control file in anything other than write-
+ * only mode. We don't need to check user/group permissions
+ * because fuse will do that if we want them enforced.
+ * -ENOENT path is anything other than the control file.
+ * -ENOMEM Not enough memory available.
+ * If this was a proper file system, it would probably do additional
+ * checking and return -EISDIR or -ENAMETOOLONG in the right situations,
+ * but we don't care about that. If it's not the control file, it's
+ * -ENOENT.
+ *
+ * Side effects:
+ * Allocates one byte of memory, the address of which is used as a unique
+ * identifier of the open file.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMBlockOpen(const char *path, // IN
+ struct fuse_file_info *fileInfo) // IN/OUT
+{
+ /*
+ * The blocking code needs a unique value associated with each open file to
+ * know who owns what block. The memory address of a malloc()ed byte is used
+ * for this purpose.
+ */
+
+ char *uniqueValue = NULL;
+
+ if (strcmp(path, CONTROL_FILE) != 0) {
+ return -ENOENT;
+ }
+ if ((fileInfo->flags & O_WRONLY) == 0) {
+ return -EACCES;
+ }
+ uniqueValue = malloc(sizeof *uniqueValue);
+ if (uniqueValue == NULL) {
+ return -ENOMEM;
+ }
+ fileInfo->fh = CharPointerToFuseFileHandle(uniqueValue);
+ fileInfo->direct_io = 1;
+ return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StripExtraPathSeparators --
+ *
+ * Removes extra repeated '/' characters from a null terminated string.
+ * Eg: "/foo//bar" -> "/foo/bar".
+ * Also removes any trailing '/'s from the string.
+ *
+ * Sometimes programs wind up building paths with extra '/'s in them which
+ * works, but we want to know that they're the same for blocking purposes.
+ * Similarly, we want /foo/bar/ and /foo/bar to match.
+ *
+ * Results:
+ * path has extra separators removed (see above).
+ * Returns the new length of the string.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+size_t
+StripExtraPathSeparators(char *path) // IN/OUT: nul terminated string.
+{
+ unsigned int i;
+ size_t length;
+ Bool lastCharWasSep = FALSE;
+
+ ASSERT(path != NULL);
+ length = strlen(path);
+
+ for (i = 0; i < length; i++) {
+ if (path[i] == '/') {
+ if (lastCharWasSep) {
+ memmove(path + i - 1, path + i, length - i + 1);
+ --length;
+ ASSERT(i > 0);
+ --i;
+ } else {
+ lastCharWasSep = TRUE;
+ }
+ } else {
+ lastCharWasSep = FALSE;
+ }
+ }
+
+ /*
+ * Strip trailing slash when appropriate.
+ */
+
+ if (length > 1 && path[length - 1] == '/') {
+ --length;
+ }
+
+ path[length] = '\0';
+ return length;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockWrite --
+ *
+ * Writes to the control file to perform a blocking operation.
+ *
+ * The write is the means by which a file block is add or removed. In a
+ * development build, it also allows all blocks to be listed. The desired
+ * operation is indicated by the first character in buf. Constants are
+ * defined in vmblock.h:
+ * VMBLOCK_ADD_FILEBLOCK: Add block
+ * VMBLOCK_DEL_FILEBLOCK: Remove block
+ * VMBLOCK_LIST_FILEBLOCKS: List blocks (only with VMX86_DEVEL)
+ *
+ * The control file must already be open.
+ *
+ * Results:
+ * Returns size argument on success. Possible errors (as negative values):
+ * -EINVAL Operation character is not a valid value.
+ * -ENAMETOOLONG File to be blocked is too long.
+ * -ENOENT Tried to remove a block that doesn't exist.
+ * Maybe others.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMBlockWrite(const char *path, // IN: Must be control file.
+ const char *buf, // IN: Operation and file operand.
+ // Not nul terminated for
+ // consistency with other
+ // special fses, eg: procfs.
+ size_t size, // IN: Size of buf.
+ off_t offset, // IN: Ignored.
+ struct fuse_file_info *fileInfo) // IN: Ignored.
+{
+ int status;
+ char trimmedBuf[PATH_MAX + 1];
+ os_blocker_id_t blockerId;
+
+ LOG(4, "VMBlockWrite: path: %s, size: %"FMTSZ"u\n", path, size);
+ LOG(4, "fileInfo->fh: %p\n", FuseFileHandleToCharPointer(fileInfo->fh));
+ ASSERT(strcmp(path, CONTROL_FILE) == 0);
+ if (size > PATH_MAX) {
+ return -ENAMETOOLONG;
+ }
+
+ memcpy(trimmedBuf, buf, size);
+ trimmedBuf[size] = '\0';
+ LOG(4, "buf: %s\n", trimmedBuf);
+ StripExtraPathSeparators(trimmedBuf);
+
+ blockerId = FuseFileHandleToCharPointer(fileInfo->fh);
+ switch (trimmedBuf[0]) {
+ case VMBLOCK_ADD_FILEBLOCK:
+ status = BlockAddFileBlock(trimmedBuf + 1, blockerId);
+ break;
+ case VMBLOCK_DEL_FILEBLOCK:
+ status = BlockRemoveFileBlock(trimmedBuf + 1, blockerId);
+ break;
+#ifdef VMX86_DEVEL
+ case VMBLOCK_LIST_FILEBLOCKS:
+ BlockListFileBlocks();
+ status = 0;
+ break;
+#endif // VMX86_DEVEL
+ default:
+ status = -EINVAL;
+ }
+ return status == 0 ? size : status;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockRelease --
+ *
+ * Releases an open (control) file. Removes any blocks created via this
+ * file.
+ *
+ * Results:
+ * Returns 0.
+ *
+ * Side effects:
+ * Free's the memory allocated as a unique identifier for the open file.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VMBlockRelease(const char *path, // IN: Must be control file.
+ struct fuse_file_info *fileInfo) // IN/OUT: Contains fh which is
+ // freed.
+{
+ char *blockerId = FuseFileHandleToCharPointer(fileInfo->fh);
+
+ ASSERT(path);
+ ASSERT(fileInfo);
+ ASSERT(strcmp(path, CONTROL_FILE) == 0);
+ ASSERT(blockerId != NULL);
+ BlockRemoveAllBlocks(blockerId);
+ free(blockerId);
+ blockerId = NULL;
+ fileInfo->fh = CharPointerToFuseFileHandle(NULL);
+ return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockInit --
+ *
+ * Initializes the filesystem.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Calls BlockInit which allocates memory and does other initialization.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void *
+VMBlockInit(void)
+{
+ Atomic_Init();
+ BlockInit();
+ return NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMBlockDestroy --
+ *
+ * Cleans up after the filesystem.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Calls BlockDestroy which frees memory and does other cleanup.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VMBlockDestroy(void *private_data) // IN: Not used.
+{
+ BlockCleanup();
+}
+
+
+struct fuse_operations vmblockOperations = {
+ .readlink = VMBlockReadLink,
+ .getattr = VMBlockGetAttr,
+ .readdir = VMBlockReadDir,
+ .open = VMBlockOpen,
+ .write = VMBlockWrite,
+ .release = VMBlockRelease,
+ .init = VMBlockInit,
+ .destroy = VMBlockDestroy
+};
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * fsops.h --
+ *
+ * Vmblock fuse filesystem operations.
+ *
+ * The file ops and internal functions are prototyped here and the main
+ * function put in a separate file to enable unit testing of these
+ * functions directly. This is also the reason none of the functions are
+ * static.
+ */
+
+#ifndef _VMBLOCK_FUSE_H_
+#define _VMBLOCK_FUSE_H_
+
+/*
+ * FUSE_USE_VERSION sets the version of the FUSE API that will be exported.
+ * Version 25 is the newest version supported by the libfuse in our toolchain
+ * as of 2008-07.
+ */
+
+#define FUSE_USE_VERSION 25
+#include <fuse.h>
+
+#include "vmblock.h"
+#include "vm_assert.h"
+#include "vm_basic_types.h"
+
+#define REDIRECT_DIR_NAME VMBLOCK_CONTROL_MOUNTPOINT
+#define REDIRECT_DIR "/" VMBLOCK_CONTROL_MOUNTPOINT
+#define TARGET_DIR "/tmp/VMwareDnD"
+#define CONTROL_FILE "/" VMBLOCK_DEVICE_NAME
+
+/*
+ * FS operation functions
+ */
+
+int VMBlockReadLink(const char *path, char *buf, size_t bufSize);
+int VMBlockGetAttr(const char *path, struct stat *statBuf);
+int VMBlockReadDir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fileInfo);
+int VMBlockOpen(const char *path, struct fuse_file_info *fileInfo);
+int VMBlockWrite(const char *path, const char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fileInfo);
+int VMBlockRelease(const char *path, struct fuse_file_info *fileInfo);
+
+extern struct fuse_operations vmblockOperations;
+
+/*
+ * Internal functions
+ */
+
+int RealReadLink(const char *path, char *buf, size_t bufSize);
+void SetTimesToNow(struct stat *statBuf);
+int ExternalReadDir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fileInfo);
+size_t StripExtraPathSeparators(char *path);
+
+
+/*
+ * CharPointerToFuseFileHandle and FuseFileHandleToCharPointer --
+ *
+ * Simple functions to keep all typecasting in one place.
+ *
+ * Storing a pointer in the fh field of fuse_file_info is the recommended
+ * way to associate a pointer with an open file according to the fuse FAQ:
+ * http://fuse.sourceforge.net/wiki/index.php/FAQ#Is_it_possible_to_store_a_pointer_to_private_data_in_the_fuse_file_info_structurex3f.
+ */
+
+static INLINE uint64_t
+CharPointerToFuseFileHandle(const char *pointer) // IN
+{
+ ASSERT(sizeof (uint64_t) >= sizeof (char *));
+ return (uintptr_t)(pointer);
+}
+
+static INLINE char *
+FuseFileHandleToCharPointer(const uint64_t fileHandle) // IN
+{
+ ASSERT(fileHandle <= (uintptr_t)((void *)(-1)));
+ return (char *)((uintptr_t)(fileHandle));
+}
+
+
+#endif /* _VMBLOCK_FUSE_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * main.c --
+ *
+ * Entry point for vmblock-fuse file system.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "fsops.h"
+
+int LOGLEVEL_THRESHOLD = 0;
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * main --
+ *
+ * Entry point for the vmblock-fuse file system. Calls fuse_main(). See
+ * http://fuse.sourceforge.net/doxygen/fuse_8h.html#3bf31250361d44c2436d76f47f2400ed
+ * for more information.
+ *
+ * There are many command line options that fuse filesystems can take. Run
+ * with --help for a listing or consult the fuse documentation.
+ * Options which are likely to be usefor for vmblock-fuse are
+ * -o default_permissions and -o allow_other.
+ *
+ * If the -d option is specified, enables our logging in addition to
+ * what fuse does.
+ *
+ * Results:
+ * Returns 0 on success and nonzero on failure.
+ *
+ * Side effects:
+ * None/all.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+main(int argc, // IN
+ char *argv[]) // IN
+{
+ int i;
+ for (i = 1; i < argc && strcmp(argv[i], "--") != 0; ++i) {
+ if (strcmp(argv[i], "-d") == 0) {
+ LOGLEVEL_THRESHOLD = 4;
+ }
+ }
+ return fuse_main(argc, argv, &vmblockOperations);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+
+/*
+ * os.h --
+ *
+ * OS-specific definitions.
+ */
+
+
+#ifndef __OS_H__
+#define __OS_H__
+
+#include <limits.h>
+#include <errno.h>
+#include <pthread.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "vm_atomic.h"
+#include "vm_assert.h"
+
+typedef pthread_rwlock_t os_rwlock_t;
+typedef void os_kmem_cache_t; // Not used.
+typedef struct os_completion_t {
+ pthread_cond_t cv;
+ pthread_mutex_t mutex;
+ int completed;
+} os_completion_t;
+typedef Atomic_Int os_atomic_t;
+typedef char * os_blocker_id_t;
+
+#define OS_UNKNOWN_BLOCKER 0
+#define OS_ENOMEM (-ENOMEM)
+#define OS_ENOENT (-ENOENT)
+#define OS_EEXIST (-EEXIST)
+#define OS_PATH_MAX PATH_MAX
+
+#define OS_FMTTID "u"
+#define os_threadid ((unsigned)(pthread_self()))
+
+#define os_panic(fmt, args) \
+({ \
+ fprintf(stderr, fmt, args); \
+ abort(); \
+})
+
+#define os_rwlock_init(lock) pthread_rwlock_init(lock, NULL)
+#define os_rwlock_destroy(lock) pthread_rwlock_destroy(lock)
+
+/*
+ * XXX I don't know of anything better for os_rwlock_held that pthreads offers.
+ */
+
+#define os_rwlock_held(lock) TRUE
+#define os_read_lock(lock) pthread_rwlock_rdlock(lock)
+#define os_write_lock(lock) pthread_rwlock_wrlock(lock)
+#define os_read_unlock(lock) pthread_rwlock_unlock(lock)
+#define os_write_unlock(lock) pthread_rwlock_unlock(lock)
+
+/*
+ * os_kmem_cache_create can't evaluate to NULL because there's a !NULL check
+ * on its result.
+ */
+
+#define os_kmem_cache_create(name, size, align, ctor) ((void *)1)
+#define os_kmem_cache_destroy(cache)
+#define os_kmem_cache_alloc(cache) malloc(sizeof(struct BlockInfo))
+#define os_kmem_cache_free(cache, elem) free(elem)
+
+/*
+ * Completion Functions
+ */
+
+#define os_completion_init(comp) \
+({ \
+ pthread_cond_init(&(comp)->cv, NULL); \
+ pthread_mutex_init(&(comp)->mutex, NULL); \
+ (comp)->completed = 0; \
+})
+#define os_completion_destroy(comp) \
+({ \
+ pthread_cond_destroy(&(comp)->cv); \
+ pthread_mutex_destroy(&(comp)->mutex); \
+})
+#define os_wait_for_completion(comp) \
+({ \
+ pthread_mutex_lock(&(comp)->mutex); \
+ while ((comp)->completed == 0) { \
+ pthread_cond_wait(&(comp)->cv, &(comp)->mutex); \
+ } \
+ pthread_mutex_unlock(&(comp)->mutex); \
+ 0; \
+})
+#define os_complete_all(comp) \
+({ \
+ pthread_mutex_lock(&(comp)->mutex); \
+ (comp)->completed = 1; \
+ pthread_cond_broadcast(&(comp)->cv); \
+ pthread_mutex_unlock(&(comp)->mutex); \
+ 0; \
+})
+
+/*
+ * Atomic Value Functions
+ *
+ * os_atomic_dec_and_test needs to test against 1 because ReadDecInt returns
+ * the value before the dec.
+ */
+
+#define os_atomic_dec_and_test(atomic) (Atomic_ReadDecInt(atomic) == 1)
+#define os_atomic_dec Atomic_DecInt
+#define os_atomic_set Atomic_WriteInt
+#define os_atomic_inc Atomic_IncInt
+
+/*
+ * Extra stuff fuse port needs defined (ie not in os.h for other ports).
+ */
+
+#ifdef VMX86_DEVEL
+extern int LOGLEVEL_THRESHOLD;
+# define LOG(level, fmt, args...) \
+ ((void) (LOGLEVEL_THRESHOLD >= (level) ? \
+ fprintf(stderr, "DEBUG: " fmt, ## args) : \
+ 0) \
+ )
+#else
+# define LOG(level, fmt, args...)
+#endif
+#define Warning(fmt, args...) \
+ fprintf(stderr, "WARNING: " fmt, ## args)
+
+/*
+ * The GNU C library doesn't include strlcpy.
+ * XXX: This prototype is moving. See util.c.
+ */
+
+size_t strlcpy(char *dest, const char *src, size_t count);
+
+#endif /* __OS_H__ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2007 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+
+/*
+ * stubs.c --
+ *
+ * Common stubs.
+ */
+
+#include <stdarg.h>
+
+#include "os.h"
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Panic --
+ *
+ * Panic implementation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+void
+Panic(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ os_panic(fmt, args);
+ va_end(args);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2007 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+
+/*
+ * stubs.h --
+ *
+ */
+
+
+#ifndef __STUBS_H__
+#define __STUBS_H__
+
+#if defined(linux) && !defined(vmblock_fuse)
+# include "driver-config.h"
+# include "compat_version.h"
+#endif
+
+void Panic(const char *fmt, ...);
+
+#endif /* __STUBS_H__ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#include <string.h>
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * strlcpy --
+ *
+ * XXX: Copied from vmblock/linux/module.c. Share them.
+ *
+ * Copies at most count - 1 bytes from src to dest, and ensures dest is NUL
+ * terminated.
+ *
+ * Results:
+ * Length of src. If src >= count, src was truncated in copy.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+size_t
+strlcpy(char *dest, // OUT: destination to copy string to
+ const char *src, // IN : source to copy string from
+ size_t count) // IN : size of destination buffer
+{
+ size_t ret;
+ size_t len;
+
+ ret = strlen(src);
+ len = ret >= count ? count - 1 : ret;
+ memcpy(dest, src, len);
+ dest[len] = '\0';
+ return ret;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2007 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * copyPaste.h --
+ *
+ * Handles the linux specific copy paste operations.
+ *
+ * TODO: Data conversion out into another class to be shared between dnd
+ * and copypaste.
+ */
+
+#ifndef LUI_COPYPASTE_HH
+#define LUI_COPYPASTE_HH
+
+#include "cui/dnd/copyPaste.hh"
+#include "cui/vm.hh"
+
+extern "C" {
+#include "dnd.h"
+#include "../dnd/dndClipboard.h"
+}
+
+namespace lui {
+ class CopyPaste
+ : public sigc::trackable
+ {
+ public:
+ CopyPaste(cui::dnd::CopyPaste *cp);
+ ~CopyPaste(void);
+
+ void UpdateLocalClipboard(void);
+ void UpdateRemoteClipboard(void);
+
+ private:
+ /* gh */
+ void GetRemoteClipboardCB(const CPClipboard *clip);
+ void RemoteGetFilesDone(void);
+ /* clipboard stuff */
+ void LocalGetFileRequestCB(Gtk::SelectionData& selection_data, guint info);
+ void LocalClearClipboardCB(void);
+
+ /* hg */
+ /* Clipboard stuff */
+ void LocalClipboardTimestampCB(const Gtk::SelectionData& sd);
+ void LocalPrimTimestampCB(const Gtk::SelectionData& sd);
+ void LocalReceivedTargetsCB(const Glib::StringArrayHandle& targets_array);
+ void LocalReceivedFileListCB(const Gtk::SelectionData& selection_data);
+ void LocalReceivedTextCB(const Glib::ustring& text);
+
+ /* Convertion methods. */
+ void LocalGetSelectionFileList(const Gtk::SelectionData& sd);
+ utf::utf8string GetNextPath(utf::utf8string &str, size_t& index);
+ utf::string GetLastDirName(const utf::string &str);
+
+ cui::dnd::CopyPaste * const mCP;
+
+ CPClipboard mClipboard;
+ bool mIsClipboardOwner;
+ uint64 mClipTime;
+ uint64 mPrimTime;
+ GdkAtom mHGSelection;
+
+ /* File vars. */
+ bool mGHGetFilesInitated;
+ VmTimeType mGHGetListTime;
+ utf::string mGHCopiedUriList;
+ utf::utf8string mGHFCPData;
+
+ std::list<Gtk::TargetEntry> mListTargets;
+ };
+
+} //namespace lui
+
+#endif // LUI_COPYPASTE_HH