]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Internal branch sync. Included in this change:
authorVMware, Inc <>
Mon, 20 Sep 2010 18:25:42 +0000 (11:25 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Mon, 20 Sep 2010 18:25:42 +0000 (11:25 -0700)
. VIX: add new opcodes for TerminateProcess, DeleteFile, DeleteDirectory.

. changes in shared code that don't affect open-vm-tools functionality.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/lib/file/file.c
open-vm-tools/lib/foundryMsg/foundryMsg.c
open-vm-tools/lib/include/file.h
open-vm-tools/lib/include/vixCommands.h
open-vm-tools/lib/include/vm_product.h
open-vm-tools/lib/include/vm_version.h
open-vm-tools/lib/misc/hostType.c
open-vm-tools/services/plugins/vix/vixTools.c

index 31ae9320c76ec31ebf49ab15a05f3c5a1619ae43..5928d5fb6fa8c328d9eae2fe7ae4ccf2acf4f78e 100644 (file)
@@ -355,7 +355,27 @@ File_EnsureDirectory(ConstUnicode pathName)  // IN:
 Bool
 File_DeleteEmptyDirectory(ConstUnicode pathName)  // IN:
 {
-   return (FileRemoveDirectory(pathName) == 0) ? TRUE : FALSE;
+   Bool returnValue = TRUE;
+
+   if (FileRemoveDirectory(pathName) != 0) {
+#if defined(_WIN32)
+      /*
+       * Directory may have read-only bit set. Unset the
+       * read-only bit and try deleting one more time.
+       */
+      if (File_SetFilePermissions(pathName, S_IWUSR)) {
+         if (FileRemoveDirectory(pathName) != 0) {
+            returnValue = FALSE;
+         }
+      } else {
+         returnValue = FALSE;
+      }
+#else
+      returnValue =  FALSE;
+#endif
+   }
+
+   return returnValue;
 }
 
 
@@ -2563,3 +2583,271 @@ FileSleeper(uint32 msecMinSleepTime,  // IN:
    return msecActualSleepTime;
 }
 #endif // N_PLAT_NLM
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileRotateByRename --
+ *
+ *      The oldest indexed file should be removed so that the
+ *      consequent rename succeeds.
+ *
+ *      The last dst is 'fileName' and should not be deleted.
+ *
+ * Results:
+ *      If newFileName is non-NULL: the new path is returned to
+ *      *newFileName if the rotation succeeded, otherwise NULL
+ *      is returned in *newFileName.  The caller is responsible
+ *      for freeing the string returned in *newFileName.
+ *
+ * Side effects:
+ *      Rename backup old files kept so far.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FileRotateByRename(const char *fileName,  // IN: full path to file
+                   const char *baseName,  // IN: filename w/o extension.
+                   const char *ext,       // IN: extension
+                   int n,                 // IN: number of old files to keep
+                   char **newFileName)    // OUT/OPT: new path to file
+{
+   char *src = NULL;
+   char *dst = NULL;
+   int i;
+   int result;
+
+   for (i = n; i >= 0; i--) {
+      src = (i == 0) ? (char *) fileName :
+                       Str_SafeAsprintf(NULL, "%s-%d%s", baseName, i - 1, ext);
+
+      if (dst != NULL) {
+         result = Posix_Rename(src, dst);
+         if (result == -1) {
+            int error = Err_Errno();
+            if (error != ENOENT) {
+               Log("LOG failed to rename %s -> %s: %s\n", src, dst,
+                   Err_Errno2String(error));
+            }
+         }
+      } else {
+         result = File_UnlinkIfExists(src);
+         if (result == -1) {
+            Log("LOG failed to remove %s: %s\n", src, Msg_ErrString());
+         }
+      }
+      if (src == fileName && newFileName != NULL) {
+         *newFileName = result == -1 ? NULL : strdup(dst);
+      }
+      ASSERT(dst != fileName);
+      free(dst);
+      dst = src;
+   }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileNumberCompare --
+ *
+ *      Helper function for comparing the contents of two
+ *      uint32 pointers a and b, suitable for use by qsort
+ *      to order an array of file numbers.
+ *
+ * Results:
+ *      The contents of 'a' minus the contents of 'b'.
+ *
+ * Side effects:
+ *      None.
+ */
+
+static int
+FileNumberCompare(const void *a,  // IN:
+                  const void *b)  // IN:
+{
+   return *(uint32 *)a - *(uint32 *)b;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileRotateByRenumber --
+ *
+ *      File rotation scheme optimized for vmfs:
+ *        1) find highest numbered file (maxNr)
+ *        2) rename <base>.<ext> to <base>-<maxNr + 1>.<ext>
+ *        3) delete (nFound - numToKeep) lowest numbered files.
+ *
+ *        Wrap around is handled incorrectly.
+ *
+ * Results:
+ *      If newFilePath is non-NULL: the new path is returned to
+ *      *newFilePath if the rotation succeeded, otherwise NULL
+ *      is returned in *newFilePath.  The caller is responsible
+ *      for freeing the string returned in *newFilePath.
+ *
+ * Side effects:
+ *      Files renamed / deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FileRotateByRenumber(const char *filePath,       // IN: full path to file
+                     const char *filePathNoExt,  // IN: filename w/o extension.
+                     const char *ext,            // IN: extension
+                     int n,                      // IN: number old files to keep
+                     char **newFilePath)         // OUT/OPT: new path to file
+{
+   char *baseDir = NULL, *fmtString = NULL, *baseName = NULL, *tmp;
+   char *fullPathNoExt = NULL;
+   uint32 maxNr = 0;
+   int i, nrFiles, nFound = 0;
+   char **fileList = NULL;
+   uint32 *fileNumbers;
+   int result;
+
+   fullPathNoExt = File_FullPath(filePathNoExt);
+   if (fullPathNoExt == NULL) {
+      Log("%s: failed to get full path for '%s'.\n", __FUNCTION__,
+          filePathNoExt);
+      goto cleanup;
+   }
+
+   File_GetPathName(fullPathNoExt, &baseDir, &baseName);
+   if ((baseDir[0] == '\0') || (baseName[0] == '\0')) {
+      Log("%s: failed to get base dir for path '%s'.\n", __FUNCTION__,
+          filePathNoExt);
+      goto cleanup;
+   }
+
+   fmtString = Str_SafeAsprintf(NULL, "%s-%%d%s%%n", baseName, ext);
+
+   nrFiles = File_ListDirectory(baseDir, &fileList);
+   if (nrFiles == -1) {
+      Log("%s: failed to read the directory '%s'.\n", __FUNCTION__,
+          baseDir);
+      goto cleanup;
+   }
+
+   fileNumbers = Util_SafeCalloc(nrFiles, sizeof(uint32));
+
+   for (i = 0; i < nrFiles; i++) {
+      uint32 curNr;
+      int bytesProcessed = 0;
+
+      /*
+       * Make sure the whole file name matched what we expect for the file.
+       */
+
+      if ((sscanf(fileList[i], fmtString, &curNr, &bytesProcessed) >= 1) &&
+          (bytesProcessed == strlen(fileList[i]))) {
+         fileNumbers[nFound++] = curNr;
+      }
+      free(fileList[i]);
+   }
+
+   if (nFound > 0) {
+      qsort(fileNumbers, nFound, sizeof(uint32), FileNumberCompare);
+      maxNr = fileNumbers[nFound - 1];
+   }
+
+   /* rename the existing file to the next number */
+   tmp = Str_SafeAsprintf(NULL, "%s/%s-%d%s", baseDir, baseName, maxNr + 1, ext);
+   result = Posix_Rename(filePath, tmp);
+   if (result == -1) {
+      int error = Err_Errno();
+      if (error != ENOENT) {
+         Log("%s: failed to rename %s -> %s failed: %s\n", __FUNCTION__,
+             filePath, tmp, Err_Errno2String(error));
+      }
+   }
+   if (newFilePath != NULL) {
+      if (result == -1) {
+         *newFilePath = NULL;
+         free(tmp);
+      } else {
+         *newFilePath = tmp;
+      }
+   }
+
+   if (nFound >= n) {
+      /* Delete the extra files. */
+      for (i = 0; i <= nFound - n; i++) {
+         tmp = Str_SafeAsprintf(NULL, "%s/%s-%d%s", baseDir, baseName,
+                                fileNumbers[i], ext);
+
+         if (Posix_Unlink(tmp) == -1) {
+            Log("%s: failed to remove %s: %s\n", __FUNCTION__,
+                tmp, Msg_ErrString());
+         }
+         free(tmp);
+      }
+   }
+
+  cleanup:
+   free(fileList);
+   free(fmtString);
+   free(baseDir);
+   free(baseName);
+   free(fullPathNoExt);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * File_Rotate --
+ *
+ *      Rotate old files. The 'noRename' option is useful for filesystems
+ *      where rename is hideously expensive (*cough* vmfs).
+ *
+ * Results:
+ *      If newFileName is non-NULL: the new path is returned to
+ *      *newFileName if the rotation succeeded, otherwise NULL
+ *      is returned in *newFileName.  The caller is responsible
+ *      for freeing the string returned in *newFileName.
+ *
+ * Side effects:
+ *      Files are renamed / deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+File_Rotate(const char *fileName,  // IN: original file
+            int n,                 // IN: number of backup files
+            Bool noRename,         // IN: don't rename all files
+            char **newFileName)    // OUT/OPT: new path to file
+{
+   const char *ext;
+   size_t baseLen;
+   char *baseName;
+
+   if ((ext = Str_Strrchr(fileName, '.')) == NULL) {
+      ext = fileName + strlen(fileName);
+   }
+   baseLen = ext - fileName;
+
+   /*
+    * Backup base of file name.
+    *
+    * Since the Str_Asprintf(...) doesn't like format of %.*s and crashes
+    * in Windows 2000. (Daniel Liu)
+    */
+
+   baseName = Util_SafeStrdup(fileName);
+   baseName[baseLen] = '\0';
+
+   if (noRename) {
+      FileRotateByRenumber(fileName, baseName, ext, n, newFileName);
+   } else {
+      FileRotateByRename(fileName, baseName, ext, n, newFileName);
+   }
+
+   free(baseName);
+}
index 6042f00b5e7e90eca0f94859fe65f5a3327f6cab..3ca2f5334a83b4be9639bb7c629ba0ba31778a5f 100644 (file)
@@ -470,6 +470,13 @@ static const VixCommandInfo vixCommandInfoTable[] = {
    VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VALIDATE_CREDENTIALS,
                            VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
 
+   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_TERMINATE_PROCESS,
+                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
+   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_FILE_EX,
+                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
+   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX,
+                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
+
 };
 
 
index af8aefa87f998369edff297618285aaada45ffd7..20576cb81c904f2433a4208fcd06c267efbe2e90 100644 (file)
@@ -276,6 +276,11 @@ Bool File_Replace(ConstUnicode oldFile,
 Bool File_Rename(ConstUnicode oldFile, 
                  ConstUnicode newFile);
 
+void File_Rotate(const char *fileName,
+                 int n,
+                 Bool noRename,
+                 char **newFileName);
+
 int64 File_GetSize(ConstUnicode pathName);
 
 int64 File_GetSizeByPath(ConstUnicode pathName);
index fefda03e0504bff44854fc7f2a536ddb8ffcf9bd..45df21ff9e626d4b73b1cf0410a2e15eff718522 100644 (file)
@@ -1630,6 +1630,19 @@ struct VixMsgCreateTempFileRequestEx {
 VixMsgCreateTempFileRequestEx;
 
 
+typedef
+#include "vmware_pack_begin.h"
+struct {
+   VixCommandRequestHeader header;
+
+   int32                   fileOptions;
+   uint32                  guestPathNameLength;
+   uint32                  filePropertiesLength;
+   Bool                    recursive;
+}
+#include "vmware_pack_end.h"
+VixMsgDeleteDirectoryRequest;
+
 /*
  * **********************************************************
  * Connect/Disconnect device request. The response is just a generic
@@ -2385,6 +2398,9 @@ enum {
    VIX_COMMAND_ACQUIRE_CREDENTIALS              = 190,
    VIX_COMMAND_RELEASE_CREDENTIALS              = 191,
    VIX_COMMAND_VALIDATE_CREDENTIALS             = 192,
+   VIX_COMMAND_TERMINATE_PROCESS                = 193,
+   VIX_COMMAND_DELETE_GUEST_FILE_EX             = 194,
+   VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX        = 195,
 
    /*
     * HOWTO: Adding a new Vix Command. Step 2a.
@@ -2396,7 +2412,7 @@ enum {
     * Once a new command is added here, a command info field needs to be added
     * in bora/lib/foundryMsg/foundryMsg.c as well.
     */
-   VIX_COMMAND_LAST_NORMAL_COMMAND              = 193,
+   VIX_COMMAND_LAST_NORMAL_COMMAND              = 196,
 
    VIX_TEST_UNSUPPORTED_TOOLS_OPCODE_COMMAND    = 998,
    VIX_TEST_UNSUPPORTED_VMX_OPCODE_COMMAND      = 999,
index 5b7aba965160e550b373c58478c6e118588b4c73..d424a270d19612c46d676ee48b2b446832d6b996 100644 (file)
 
 #define PRODUCT_VPX_NAME MAKE_NAME("VirtualCenter")
 
+#define PRODUCT_VPXA_NAME PRODUCT_VPX_NAME " Agent"
+
+#define PRODUCT_FDM_NAME MAKE_NAME("Fault Domain Manager")
+
 #define PRODUCT_WBC_NAME MAKE_NAME("WebCenter")
 
 #define PRODUCT_SDK_NAME MAKE_NAME("SDK")
 #define PRODUCT_VDM_CLIENT_NAME MAKE_NAME("View Client")
 #define PRODUCT_VDM_CLIENT_NAME_FOR_LICENSE PRODUCT_VDM_CLIENT_NAME
 
+#define PRODUCT_XVP_SHORT_NAME "XVP"
+#define PRODUCT_XVP_NAME MAKE_NAME("vCenter XVP Manager")
 #define PRODUCT_RMKSCONTAINER_NAME MAKE_NAME("Remote MKS Container")
 
 // XXX VMvisor is the underlying technology for possibly several products,
 #     define PRODUCT_SHORT_NAME PRODUCT_API_SCRIPTING_PERL_NAME
 #  endif
 #elif defined(VMX86_VPX)
-# define PRODUCT_SHORT_NAME PRODUCT_VPX_NAME
+#  if defined(CSI_FDM)
+#     define PRODUCT_SHORT_NAME PRODUCT_FDM_NAME
+#  elif defined(VPXA)
+#     define PRODUCT_SHORT_NAME PRODUCT_VPXA_NAME
+#  elif defined(XVP)
+#     define PRODUCT_SHORT_NAME PRODUCT_XVP_NAME
+#  else
+#     define PRODUCT_SHORT_NAME PRODUCT_VPX_NAME
+#  endif
 #elif defined(VMX86_WBC)
 # define PRODUCT_SHORT_NAME PRODUCT_WBC_NAME
 #elif defined(VMX86_SDK)
 #         define PRODUCT_NAME_FOR_LICENSE "VMware Workstation"
 #      endif
 #      define PRODUCT_SMP_NAME_FOR_LICENSE "" // None
+#   elif defined(VMX86_VPX)
+#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME " Server"
+#      define PRODUCT_SMP_NAME_FOR_LICENSE "" // None
 #   elif defined(VMX86_WGS_MIGRATION)
 #      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME " for " PRODUCT_OS
 #      define PRODUCT_SMP_NAME_FOR_LICENSE "" // None
 #         define PRODUCT_NAME_FOR_LICENSE "VMware Workstation"
 #      endif
 #      define PRODUCT_SMP_NAME_FOR_LICENSE "" // None
+#   elif defined(VMX86_VPX)
+#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME " Server"
+#      define PRODUCT_SMP_NAME_FOR_LICENSE "" // None
 #   elif defined(VMX86_WGS_MIGRATION)
 #      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME " for Win32"
 #      define PRODUCT_SMP_NAME_FOR_LICENSE "" // None
index ce05e4c304b05638f2a312eb66983b3ff856f8f8..2a6586b85015ab0878c5cdeaf18e227d8f0bd962 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 1998-2004 VMware, Inc. All rights reserved.
+ * Copyright (C) 1998-2010 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
 #define VMSAFE_FILE_VERSION    1,1,0,PRODUCT_BUILD_NUMBER_NUMERIC
 #define NETDUMP_VERSION        "1.1.0"
 #define NETDUMP_FILE_VERSION    1,1,0,PRODUCT_BUILD_NUMBER_NUMERIC
-#define VDDK_VERSION          "1.1.0"
-#define VDDK_FILE_VERSION      1,1,0,PRODUCT_BUILD_NUMBER_NUMERIC
+#define VDDK_VERSION          "5.1.0"
+#define VDDK_FILE_VERSION      5,1,0,PRODUCT_BUILD_NUMBER_NUMERIC
 #define OVFTOOL_VERSION "2.0.1"
 #define VDM_CLIENT_VERSION "e.x.p"
 
index e210ceaa2cb1724071c07974ed0804f370aa6ed3..1d42d362f0791608873666426bc837c52ebefda1 100644 (file)
@@ -30,7 +30,7 @@
 #include "hostType.h"
 #include "str.h"
 
-#ifdef VMX86_SERVER
+#if defined(VMX86_SERVER) || (defined(VMX86_VPX) && defined(linux))
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/sysctl.h>
@@ -66,7 +66,7 @@
 static int
 HostTypeOSVMKernelType(void)
 {
-#ifdef VMX86_SERVER
+#if defined(VMX86_SERVER) || (defined(VMX86_VPX) && defined(linux))
    static int vmkernelType = -1;
 
    if (vmkernelType == -1) {
index 3c6d3f1ae0bd72c027ebfbbb87171c74e6f3220e..115f1c3d1cd59f5337df28e3016085b137508466 100644 (file)
@@ -290,11 +290,11 @@ static const char *fileExtendedInfoLinuxFormatString = "<fxi>"
                                           "<ft>%d</ft>"
                                           "<fs>%"FMT64"u</fs>"
                                           "<mt>%"FMT64"u</mt>"
-                                          "<ct>%"FMT64"u</ct>"
                                           "<at>%"FMT64"u</at>"
                                           "<uid>%d</uid>"
                                           "<gid>%d</gid>"
                                           "<perm>%d</perm>"
+                                          "<slt>%s</slt>"
                                           "</fxi>";
 #endif
 
@@ -2597,7 +2597,8 @@ VixToolsDeleteObject(VixCommandRequestHeader *requestMsg)  // IN
    impersonatingVMWareUser = TRUE;
 
    ///////////////////////////////////////////
-   if (VIX_COMMAND_DELETE_GUEST_FILE == requestMsg->opCode) {
+   if ((VIX_COMMAND_DELETE_GUEST_FILE == requestMsg->opCode) ||
+       (VIX_COMMAND_DELETE_GUEST_FILE_EX == requestMsg->opCode)) {
       /*
        * if pathName is an invalid symbolic link, we still want to delete it.
        */
@@ -2674,6 +2675,99 @@ abort:
 } // VixToolsDeleteObject
 
 
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VixToolsDeleteDirectory --
+ *
+ *    Delete a directory on the guest.
+ *
+ * Return value:
+ *    TRUE on success
+ *    FALSE on failure
+ *
+ * Side effects:
+ *    None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+VixError
+VixToolsDeleteDirectory(VixCommandRequestHeader *requestMsg)  // IN
+{
+   VixError err = VIX_OK;
+   char *directoryPath = NULL;
+   Bool success;
+   Bool impersonatingVMWareUser = FALSE;
+   void *userToken = NULL;
+   Bool recursive = TRUE;
+   VixMsgDeleteDirectoryRequest *deleteDirectoryRequest =
+      (VixMsgDeleteDirectoryRequest *) requestMsg;
+
+   if ((requestMsg->commonHeader.bodyLength +
+        requestMsg->commonHeader.headerLength) !=
+       (((uint64) sizeof(*deleteDirectoryRequest)) +
+        deleteDirectoryRequest->guestPathNameLength + 1)) {
+      ASSERT(0);
+      Debug("%s: Invalid request message received\n", __FUNCTION__);
+      err = VIX_E_INVALID_MESSAGE_BODY;
+      goto abort;
+   }
+
+   directoryPath = ((char *) deleteDirectoryRequest) +
+                    sizeof(*deleteDirectoryRequest);
+
+   if (0 == *directoryPath) {
+      err = VIX_E_INVALID_ARG;
+      goto abort;
+   }
+
+   if ('\0' != *(directoryPath + deleteDirectoryRequest->guestPathNameLength)) {
+      ASSERT(0);
+      Debug("%s: Invalid request message received.\n", __FUNCTION__);
+      err = VIX_E_INVALID_MESSAGE_BODY;
+      goto abort;
+   }
+
+   recursive = deleteDirectoryRequest->recursive;
+   err = VixToolsImpersonateUser(requestMsg, &userToken);
+   if (VIX_OK != err) {
+      goto abort;
+   }
+   impersonatingVMWareUser = TRUE;
+
+   success = File_Exists(directoryPath);
+   if (!success) {
+      err = VIX_E_FILE_NOT_FOUND;
+      goto abort;
+   }
+
+   if (File_IsSymLink(directoryPath) || File_IsFile(directoryPath)) {
+      err = VIX_E_NOT_A_DIRECTORY;
+      goto abort;
+   }
+
+   if (recursive) {
+      success = File_DeleteDirectoryTree(directoryPath);
+   } else {
+      success = File_DeleteEmptyDirectory(directoryPath);
+   }
+
+   if (!success) {
+      err = FoundryToolsDaemon_TranslateSystemErr();
+      goto abort;
+   }
+
+abort:
+   if (impersonatingVMWareUser) {
+      VixToolsUnimpersonateUser(userToken);
+   }
+   VixToolsLogoutUser(userToken);
+
+   return err;
+} // VixToolsDeleteDirectory
+
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -4301,6 +4395,10 @@ VixToolsListFiles(VixCommandRequestHeader *requestMsg,    // IN
    int numResults;
    GRegex *regex = NULL;
    GError *gerr = NULL;
+   char *pathName;
+#ifdef linux
+   char *symlinkTarget = NULL;
+#endif
 
    ASSERT(NULL != requestMsg);
 
@@ -4405,6 +4503,7 @@ VixToolsListFiles(VixCommandRequestHeader *requestMsg,    // IN
    for (fileNum = offset + index;
         fileNum < numFiles;
         fileNum++) {
+
       currentFileName = fileNameList[fileNum];
 
       if (regex) {
@@ -4422,9 +4521,31 @@ VixToolsListFiles(VixCommandRequestHeader *requestMsg,    // IN
 
       resultBufferSize += formatStringLength;
       resultBufferSize += 2; // DIRSEPC chars
-      resultBufferSize += 10 + 20 + (20 * 3); // properties + size + times
+      resultBufferSize += 10 + 20 + (20 * 2); // properties + size + times
+#ifdef _WIN32
+      resultBufferSize += 20;                // createTime
+#endif
 #ifdef linux
       resultBufferSize += 10 * 3;            // uid, gid, perms
+
+      /*
+       * It would be nice if this were cleaner, but then we'd have to save
+       * off the symlinks for the second loop.
+       */
+      if (listingSingleFile && File_IsSymLink(currentFileName)) {
+         symlinkTarget = Posix_ReadLink(currentFileName);
+         resultBufferSize += strlen(symlinkTarget);
+         free(symlinkTarget);
+      } else {
+         pathName = Str_SafeAsprintf(NULL, "%s%s%s", dirPathName, DIRSEPS,
+                                     currentFileName);
+         if (File_IsSymLink(pathName)) {
+            symlinkTarget = Posix_ReadLink(pathName);
+            resultBufferSize += strlen(symlinkTarget);
+            free(symlinkTarget);
+         }
+         free(pathName);
+      }
 #endif
       resultBufferSize += strlen(currentFileName);
 
@@ -4472,7 +4593,6 @@ VixToolsListFiles(VixCommandRequestHeader *requestMsg,    // IN
         count < numResults;
         fileNum++, count++) {
       /* File_ListDirectory never returns "." or ".." */
-      char *pathName;
 
       currentFileName = fileNameList[fileNum];
 
@@ -4817,16 +4937,17 @@ VixToolsPrintFileExtendedInfo(const char *filePathName,     // IN
    int64 fileSize = 0;
    VmTimeType modTime = 0;
    VmTimeType accessTime = 0;
-   VmTimeType createTime = 0;
    int32 fileProperties = 0;
 #ifdef _WIN32
    DWORD fileAttr = 0;
    Bool hidden = FALSE;
    Bool readOnly = FALSE;
+   VmTimeType createTime = 0;
 #elif defined(linux)
    int permissions = 0;
    int ownerId = 0;
    int groupId = 0;
+   char *symlinkTarget = NULL;
 #endif
    struct stat statbuf;
 
@@ -4842,6 +4963,23 @@ VixToolsPrintFileExtendedInfo(const char *filePathName,     // IN
       fileSize = File_GetSize(filePathName);
    }
 
+#ifdef linux
+   /*
+    * If the file is a symlink, figure out where it points.
+    */
+   if (fileProperties & VIX_FILE_ATTRIBUTES_SYMLINK) {
+      symlinkTarget = Posix_ReadLink(filePathName);
+   }
+
+   /*
+    * Have a nice empty value if it's not a link or there's some error
+    * reading the link.
+    */
+   if (NULL == symlinkTarget) {
+      symlinkTarget = Util_SafeStrdup("");
+   }
+#endif
+
 #ifdef _WIN32
    fileAttr = Win32U_GetFileAttributes(filePathName);
    if (fileAttr != INVALID_FILE_ATTRIBUTES) {
@@ -4860,8 +4998,14 @@ VixToolsPrintFileExtendedInfo(const char *filePathName,     // IN
       groupId = statbuf.st_gid;
       permissions = statbuf.st_mode;
 #endif
-      modTime = statbuf.st_mtime;
+      /*
+       * We want create time.  ctime is the inode change time for Linux,
+       * so we can't report anything.
+       */
+#ifdef _WIN32
       createTime = statbuf.st_ctime;
+#endif
+      modTime = statbuf.st_mtime;
       accessTime = statbuf.st_atime;
    } else {
       Debug("%s: Posix_Stat(%s) failed with %d\n",
@@ -4888,11 +5032,12 @@ VixToolsPrintFileExtendedInfo(const char *filePathName,     // IN
                            fileProperties,
                            fileSize,
                            modTime,
-                           createTime,
                            accessTime,
                            ownerId,
                            groupId,
-                           permissions);
+                           permissions,
+                           symlinkTarget);
+   free(symlinkTarget);
 #endif
 #endif   // defined(_WIN32) || defined(linux)
 } // VixToolsPrintFileExtendedInfo
@@ -6924,15 +7069,18 @@ VixToolsCheckIfVixCommandEnabled(int opcode,                          // IN
                                    VIX_TOOLS_CONFIG_API_LIST_FILES_NAME);
          break;
       case VIX_COMMAND_DELETE_GUEST_FILE:
+      case VIX_COMMAND_DELETE_GUEST_FILE_EX:
          enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                    VIX_TOOLS_CONFIG_API_DELETE_FILE_NAME);
          break;
       case VIX_COMMAND_DELETE_GUEST_DIRECTORY:
       case VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY:
+      case VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX:
          enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                    VIX_TOOLS_CONFIG_API_DELETE_DIRECTORY_NAME);
          break;
       case VIX_COMMAND_KILL_PROCESS:
+      case VIX_COMMAND_TERMINATE_PROCESS:
          enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                    VIX_TOOLS_CONFIG_API_TERMINATE_PROCESS_NAME);
          break;
@@ -7147,12 +7295,18 @@ VixTools_ProcessVixCommand(VixCommandRequestHeader *requestMsg,   // IN
          break;
       ////////////////////////////////////
       case VIX_COMMAND_DELETE_GUEST_FILE:
+      case VIX_COMMAND_DELETE_GUEST_FILE_EX:
       case VIX_COMMAND_DELETE_GUEST_REGISTRY_KEY:
       case VIX_COMMAND_DELETE_GUEST_DIRECTORY:
       case VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY:
          err = VixToolsDeleteObject(requestMsg);
          break;
 
+      ////////////////////////////////////
+      case VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX:
+         err = VixToolsDeleteDirectory(requestMsg);
+         break;
+
       ////////////////////////////////////
       case VIX_COMMAND_REGISTRY_KEY_EXISTS:
       case VIX_COMMAND_GUEST_FILE_EXISTS:
@@ -7174,6 +7328,7 @@ VixTools_ProcessVixCommand(VixCommandRequestHeader *requestMsg,   // IN
 
       ////////////////////////////////////
       case VIX_COMMAND_KILL_PROCESS:
+      case VIX_COMMAND_TERMINATE_PROCESS:
          err = VixToolsKillProcess(requestMsg);
          break;