From: VMware, Inc <> Date: Tue, 24 Aug 2010 18:41:49 +0000 (-0700) Subject: Internal branch sync. Included in this change: X-Git-Tag: 2010.08.24-292196~20 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8fcfa39decfe1a8534645b088d82675fed0e344b;p=thirdparty%2Fopen-vm-tools.git Internal branch sync. Included in this change: . vix: add new opcodes for intiateFileTransfer[To|From]Guest operations. . add new vmbackup entry point that allows more configurability of the quiescing operation (only enabled on Win32 currently). . changes in shared code that don't affect open-vm-tools functionality. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/lib/foundryMsg/foundryMsg.c b/open-vm-tools/lib/foundryMsg/foundryMsg.c index c90f49085..aacb80b08 100644 --- a/open-vm-tools/lib/foundryMsg/foundryMsg.c +++ b/open-vm-tools/lib/foundryMsg/foundryMsg.c @@ -456,6 +456,12 @@ static const VixCommandInfo vixCommandInfoTable[] = { VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_READ_ENV_VARIABLES, VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED), + VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST, + VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED), + + VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST, + VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED), + }; diff --git a/open-vm-tools/lib/foundryMsg/vixTranslateErrOpenSource.c b/open-vm-tools/lib/foundryMsg/vixTranslateErrOpenSource.c index be2568e25..b7d78dc96 100644 --- a/open-vm-tools/lib/foundryMsg/vixTranslateErrOpenSource.c +++ b/open-vm-tools/lib/foundryMsg/vixTranslateErrOpenSource.c @@ -75,6 +75,9 @@ Vix_TranslateSystemError(int systemError) // IN case ERROR_BUFFER_OVERFLOW: err = VIX_E_FILE_NOT_FOUND; break; + case ERROR_DIR_NOT_EMPTY: + err = VIX_E_DIRECTORY_NOT_EMPTY; + break; case ERROR_TOO_MANY_OPEN_FILES: case ERROR_NO_MORE_FILES: case ERROR_WRITE_FAULT: @@ -138,14 +141,18 @@ Vix_TranslateSystemError(int systemError) // IN case EFBIG: err = VIX_E_FILE_TOO_BIG; break; + case ENOTEMPTY: + err = VIX_E_DIRECTORY_NOT_EMPTY; + break; + case ENOTDIR: + err = VIX_E_NOT_A_DIRECTORY; + break; case ETIMEDOUT: case EIO: case EMFILE: case ENFILE: case EMLINK: case ENOBUFS: - case ENOTDIR: - case ENOTEMPTY: case EROFS: Log("%s: system error = %d\n", __FUNCTION__, systemError); diff --git a/open-vm-tools/lib/include/vixCommands.h b/open-vm-tools/lib/include/vixCommands.h index 4bd840958..926bf2cc0 100644 --- a/open-vm-tools/lib/include/vixCommands.h +++ b/open-vm-tools/lib/include/vixCommands.h @@ -756,6 +756,18 @@ struct VixMsgListFilesRequest { #include "vmware_pack_end.h" VixMsgListFilesRequest; +typedef +#include "vmware_pack_begin.h" +struct VixCommandInitiateFileTransferToGuestRequest { + VixCommandRequestHeader header; + + int32 options; + uint32 guestPathNameLength; + Bool overwrite; +} +#include "vmware_pack_end.h" +VixCommandInitiateFileTransferToGuestRequest; + /* * This is used to reply to several operations, like testing whether @@ -2328,6 +2340,10 @@ enum { VIX_COMMAND_READ_ENV_VARIABLES = 187, + VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST = 188, + + VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST = 189, + /* * HOWTO: Adding a new Vix Command. Step 2a. * @@ -2338,7 +2354,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 = 188, + VIX_COMMAND_LAST_NORMAL_COMMAND = 190, VIX_TEST_UNSUPPORTED_TOOLS_OPCODE_COMMAND = 998, VIX_TEST_UNSUPPORTED_VMX_OPCODE_COMMAND = 999, diff --git a/open-vm-tools/lib/include/vixOpenSource.h b/open-vm-tools/lib/include/vixOpenSource.h index 4a5ddc28c..7dc038010 100644 --- a/open-vm-tools/lib/include/vixOpenSource.h +++ b/open-vm-tools/lib/include/vixOpenSource.h @@ -98,6 +98,9 @@ VixError Vix_TranslateCOMError(HRESULT comError); enum { VIX_E_OP_NOT_SUPPORTED_ON_NON_VMWARE_VM = 3038, + /* File Errors */ + VIX_E_DIRECTORY_NOT_EMPTY = 20006, + /* Reg Errors*/ VIX_E_REG_INCORRECT_VALUE_TYPE = 25000 /* WARNING. Do not exceed 2**16 */ diff --git a/open-vm-tools/lib/include/vm_device_version.h b/open-vm-tools/lib/include/vm_device_version.h index cceed4fe0..ab5c89e2f 100644 --- a/open-vm-tools/lib/include/vm_device_version.h +++ b/open-vm-tools/lib/include/vm_device_version.h @@ -123,6 +123,22 @@ #define E1000E_PCI_SUB_VENDOR_ID_CONFIG_STR "e1000e.pci.subVendorID" #define E1000E_PCI_SUB_DEVICE_ID_CONFIG_STR "e1000e.pci.subDeviceID" +/* + * Fresco Logic xHCI (USB 3.0) Controller + */ +#define PCI_VENDOR_ID_FRESCO 0x1B73 +#define PCI_DEVICE_ID_FRESCO_FL1000 0x1000 // Original 1-port chip +#define PCI_DEVICE_ID_FRESCO_FL1009 0x1009 // New 2-port chip (Driver 3.0.98+) +#define PCI_DEVICE_ID_FRESCO_DEVEL 0x1400 // Unknown (development hardware?) + +/* + * NEC/Renesas xHCI (USB 3.0) Controller + */ +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_UPD720200 0x0194 +#define PCI_REVISION_NEC_UPD720200 0x03 +#define PCI_FIRMWARE_NEC_UPD720200 0x3015 + /************* Strings for IDE Identity Fields **************************/ #define VIDE_ID_SERIAL_STR "00000000000000000001" /* Must be 20 Bytes */ @@ -171,6 +187,9 @@ /************* Ethernet implementation limits ***************************/ #define MAX_ETHERNET_CARDS 10 +/********************** Floppy limits ***********************************/ +#define MAX_FLOPPY_DRIVES 2 + /************* PCI Passthrough implementation limits ********************/ #define MAX_PCI_PASSTHRU_DEVICES 6 diff --git a/open-vm-tools/lib/include/vm_version.h b/open-vm-tools/lib/include/vm_version.h index f90be91b3..bc545bd5a 100644 --- a/open-vm-tools/lib/include/vm_version.h +++ b/open-vm-tools/lib/include/vm_version.h @@ -90,6 +90,9 @@ #define PRODUCT_VERSION 1,0,0,PRODUCT_BUILD_NUMBER_NUMERIC #elif defined(VMX86_VLICENSE) #define PRODUCT_VERSION 1,1,2,PRODUCT_BUILD_NUMBER_NUMERIC +#elif defined(VMX86_VPX) + /* this should be kept in sync with the corresponding vpx branch. */ + #define PRODUCT_VERSION 5,0,0,PRODUCT_BUILD_NUMBER_NUMERIC #else #define PRODUCT_VERSION 3,1,0,PRODUCT_BUILD_NUMBER_NUMERIC /* PLAYER_VERSION_NUMBER below has to match this */ #endif @@ -118,7 +121,7 @@ * VMI 2.0 : 3.1.0 * P2VA 3.0 : 3.?.? */ -#define VIE_FILEVERSION 3,2,0,PRODUCT_BUILD_NUMBER_NUMERIC +#define VIE_FILEVERSION 5,0,0,PRODUCT_BUILD_NUMBER_NUMERIC /* * This string can be a little more "free form". The license @@ -209,13 +212,14 @@ #define FUSION_VERSION "e.x.p" // These must match VIE_FILEVERSION above -#define SYSIMAGE_VERSION "4.0.0" +#define SYSIMAGE_VERSION "5.0.0" #define SYSIMAGE_FILE_VERSION VIE_FILEVERSION #define VCB_VERSION "4.0.0" #define VCB_FILE_VERSION 4,0,0,0 -#define VPX_VERSION "e.x.p" -#define WBC_VERSION "e.x.p" +#define VIM_VERSION "5.0.0" +#define VPX_VERSION "5.0.0" +#define WBC_VERSION "5.0.0" #define SDK_VERSION "4.1.0" #define FOUNDRY_VERSION "1.10.0" #define FOUNDRY_FILE_VERSION 1,10,0,PRODUCT_BUILD_NUMBER_NUMERIC @@ -300,7 +304,11 @@ #elif defined(VMX86_API) # define PRODUCT_VERSION_NUMBER API_SCRIPTING_VERSION #elif defined(VMX86_VPX) -# define PRODUCT_VERSION_NUMBER VPX_VERSION +# if defined(XVP) +# define PRODUCT_VERSION_NUMBER XVP_VERSION +# else +# define PRODUCT_VERSION_NUMBER VPX_VERSION +# endif #elif defined(VMX86_WBC) # define PRODUCT_VERSION_NUMBER WBC_VERSION #elif defined(VMX86_SDK) @@ -371,7 +379,7 @@ # define PRODUCT_LICENSE_VERSION "7.0" # endif # elif defined(VMX86_VPX) -# define PRODUCT_LICENSE_VERSION "1.0" +# define PRODUCT_LICENSE_VERSION "5.0" # elif defined(VMX86_WBC) # define PRODUCT_LICENSE_VERSION "1.0" # elif defined(VMX86_SDK) diff --git a/open-vm-tools/lib/include/vmware/guestrpc/vmbackup.h b/open-vm-tools/lib/include/vmware/guestrpc/vmbackup.h index 76780187d..db2dadf8a 100644 --- a/open-vm-tools/lib/include/vmware/guestrpc/vmbackup.h +++ b/open-vm-tools/lib/include/vmware/guestrpc/vmbackup.h @@ -50,6 +50,7 @@ /* These are RPC messages used between the VMX and the Tools. */ #define VMBACKUP_PROTOCOL_PREFIX "vmbackup." #define VMBACKUP_PROTOCOL_START VMBACKUP_PROTOCOL_PREFIX"start" +#define VMBACKUP_PROTOCOL_START_WITH_OPTS VMBACKUP_PROTOCOL_PREFIX"startWithOpts" #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" diff --git a/open-vm-tools/services/plugins/vix/vixTools.c b/open-vm-tools/services/plugins/vix/vixTools.c index f44da4b5f..29a3896db 100644 --- a/open-vm-tools/services/plugins/vix/vixTools.c +++ b/open-vm-tools/services/plugins/vix/vixTools.c @@ -99,6 +99,7 @@ #include "vixOpenSource.h" #include "vixTools.h" +#include "vixToolsInt.h" #ifdef _WIN32 #include "registryWin32.h" @@ -349,6 +350,8 @@ static VixError VixToolsListFiles(VixCommandRequestHeader *requestMsg, size_t maxBufferSize, char **result); +static VixError VixToolsInitiateFileTransferToGuest(VixCommandRequestHeader *requestMsg); + static VixError VixToolsKillProcess(VixCommandRequestHeader *requestMsg); static VixError VixToolsCreateDirectory(VixCommandRequestHeader *requestMsg); @@ -3098,6 +3101,156 @@ abort: } // VixToolsMoveObject +/* + *----------------------------------------------------------------------------- + * + * VixToolsInitiateFileTransferToGuest -- + * + * Return value: + * VixError + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +VixError +VixToolsInitiateFileTransferToGuest(VixCommandRequestHeader *requestMsg) // IN +{ + VixError err = VIX_OK; + char *guestPathName = NULL; + Bool impersonatingVMWareUser = FALSE; + void *userToken = NULL; + Bool overwrite = TRUE; + char *dirName = NULL; + char *baseName = NULL; +#if defined(_WIN32) + int fd = -1; + char *tempFilePath = NULL; +#else + FileIOResult res; +#endif + + VixCommandInitiateFileTransferToGuestRequest *commandRequest = + (VixCommandInitiateFileTransferToGuestRequest *) requestMsg; + + ASSERT(NULL != requestMsg); + + guestPathName = ((char *) commandRequest) + sizeof(*commandRequest); + overwrite = commandRequest->overwrite; + + if ((requestMsg->commonHeader.bodyLength + + requestMsg->commonHeader.headerLength) != + (((uint64) sizeof(*commandRequest)) + + commandRequest->guestPathNameLength + 1)) { + ASSERT(0); + Debug("%s: Invalid request message received\n", __FUNCTION__); + err = VIX_E_INVALID_MESSAGE_BODY; + goto abort; + } + + if (0 == *guestPathName) { + err = VIX_E_INVALID_ARG; + goto abort; + } + + err = VixToolsImpersonateUser(requestMsg, &userToken); + if (VIX_OK != err) { + goto abort; + } + impersonatingVMWareUser = TRUE; + + if (File_Exists(guestPathName)) { + if (File_IsDirectory(guestPathName)) { + err = VIX_E_NOT_A_FILE; + goto abort; + } else if (!overwrite) { + err = VIX_E_FILE_ALREADY_EXISTS; + goto abort; + } + } + + File_GetPathName(guestPathName, &dirName, &baseName); + if ((NULL == dirName) || (NULL == baseName)) { + err = VIX_E_FILE_NAME_INVALID; + goto abort; + } + + if (!File_IsDirectory(dirName)) { + err = VIX_E_FILE_NAME_INVALID; + goto abort; + } + +#if defined(_WIN32) + /* + * Ideally, we just need to check if the user has proper write + * access to create a child inside the directory. This can be + * checked by calling FileIO_Access(). FileIO_Access works perfectly + * fine for linux platforms. But on Windows, FileIO_Access just + * checks the read-only attribute of the directory and returns the result + * based on that. This is not the proper way to check the write + * permissions. + * + * One other way to check the write access is to call CreateFile() + * with GENERIC_WRITE and OPEN_EXISTING flags. Check the documentation + * for CreateFile() at + * http://msdn.microsoft.com/en-us/library/aa363858%28v=VS.85%29.aspx. + * But this has got few limitations. CreateFile() doesn't return proper + * result when called for directories on NTFS systems. + * Checks the KB article available at + * http://support.microsoft.com/kb/810881. + * + * So, for windows, the best bet is to create an empty temporary file + * inside the directory and immediately unlink that. If creation is + * successful, it ensures that the user has proper write access for + * the directory. + * + * Since we are just checking the write access, there is no need to + * create the temporary file with the exact specified filename. Any name + * would be fine. + */ + fd = File_MakeTempEx(dirName, baseName, &tempFilePath); + + if (fd > 0) { + close(fd); + } else { + err = FoundryToolsDaemon_TranslateSystemErr(); + Debug("Unable to create a tmp file to test directory permissions," + " errno is %d\n", errno); + goto abort; + } + + free(tempFilePath); +#else + /* + * We need to check if the user has write access to create + * a child inside the directory. Call FileIO_Access() to check + * for the proper write permissions for the directory. + */ + res = FileIO_Access(dirName, FILEIO_ACCESS_WRITE); + + if (FILEIO_SUCCESS != res) { + err = FoundryToolsDaemon_TranslateSystemErr(); + Debug("Unable to get access permissions for the directory: %s\n", + dirName); + goto abort; + } +#endif + +abort: + free(baseName); + free(dirName); + + if (impersonatingVMWareUser) { + VixToolsUnimpersonateUser(userToken); + } + VixToolsLogoutUser(userToken); + + return err; +} // VixToolsInitiateFileTransferToGuest + + /* *----------------------------------------------------------------------------- * @@ -3672,6 +3825,12 @@ abort: * * VixToolsListFiles -- * + * This function is called to implement two opcodes i.e. + * VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST and + * VIX_COMMAND_LIST_FILES. + * + * If the opcode is VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST, + * then the specified filepath should not point to a directory. * * Return value: * VixError @@ -3716,6 +3875,17 @@ VixToolsListFiles(VixCommandRequestHeader *requestMsg, // IN GError *gerr = NULL; #endif + ASSERT(NULL != requestMsg); + + if ((VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST != requestMsg->opCode) && + (VIX_COMMAND_LIST_FILES != requestMsg->opCode)) { + ASSERT(0); + err = VIX_E_FAIL; + Debug("%s: Received a request with an invalid opcode: %d\n", + __FUNCTION__, requestMsg->opCode); + goto abort; + } + listRequest = (VixMsgListFilesRequest *) requestMsg; offset = listRequest->offset; index = listRequest->index; @@ -3759,6 +3929,17 @@ VixToolsListFiles(VixCommandRequestHeader *requestMsg, // IN } if (File_IsDirectory(dirPathName)) { + /* + * Ideally we should not overload VixToolsListFiles(). We should + * implement a separate function for implementing VIX_COMMAND_LIST_FILES + * and VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST. For now, adding + * this check is OK. But, we should revisit this later. + */ + if (VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST == requestMsg->opCode) { + err = VIX_E_NOT_A_FILE; + goto abort; + } + numFiles = File_ListDirectory(dirPathName, &fileNameList); if (numFiles < 0) { err = FoundryToolsDaemon_TranslateSystemErr(); @@ -5221,6 +5402,24 @@ VixToolsGetTempFile(VixCommandRequestHeader *requestMsg, // IN */ if (VIX_SUCCEEDED(err)) { + + /* + * If the specified directory path doesn't exist or points to an + * existing regular file, then File_MakeTempEx2() returns different + * errors on Windows and Linux platforms. So, check for the proper + * filetype and return proper errors before calling + * File_MakeTempEx2(). + */ + if (!File_Exists(directoryPath)) { + err = VIX_E_FILE_NOT_FOUND; + goto abort; + } + + if (File_IsFile(directoryPath)) { + err = VIX_E_NOT_A_FILE; + goto abort; + } + fd = File_MakeTempEx2(directoryPath, createTempFile, VixToolsGetTempFileCreateNameFunc, @@ -5249,6 +5448,23 @@ VixToolsGetTempFile(VixCommandRequestHeader *requestMsg, // IN directoryPath = File_GetTmpDir(TRUE); } + /* + * If the specified directory path doesn't exist or points to an + * existing regular file, then File_MakeTempEx2() returns different + * errors on Windows and Linux platforms. So, check for the proper + * filetype and return proper errors before calling + * File_MakeTempEx2(). + */ + if (!File_Exists(directoryPath)) { + err = VIX_E_FILE_NOT_FOUND; + goto abort; + } + + if (File_IsFile(directoryPath)) { + err = VIX_E_NOT_A_FILE; + goto abort; + } + fd = File_MakeTempEx2(directoryPath, createTempFile, VixToolsGetTempFileCreateNameFunc, @@ -6091,6 +6307,16 @@ VixToolsCheckIfVixCommandEnabled(int opcode, // IN VIX_TOOLS_CONFIG_API_CHANGE_FILE_ATTRS_NAME); break; + case VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST: + enabled = !VixToolsGetAPIDisabledFromConf(confDictRef, + VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_FROM_GUEST_NAME); + break; + + case VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST: + enabled = !VixToolsGetAPIDisabledFromConf(confDictRef, + VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_TO_GUEST_NAME); + break; + /* * None of these opcode have a matching config entry (yet), * so they can all share. @@ -6232,6 +6458,7 @@ VixTools_ProcessVixCommand(VixCommandRequestHeader *requestMsg, // IN break; //////////////////////////////////// + case VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST: case VIX_COMMAND_LIST_FILES: err = VixToolsListFiles(requestMsg, maxResultBufferSize, @@ -6386,6 +6613,11 @@ VixTools_ProcessVixCommand(VixCommandRequestHeader *requestMsg, // IN // resultValue is static. Do not free it. break; + //////////////////////////////////// + case VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST: + err = VixToolsInitiateFileTransferToGuest(requestMsg); + break; + //////////////////////////////////// default: break; diff --git a/open-vm-tools/services/plugins/vix/vixTools.h b/open-vm-tools/services/plugins/vix/vixTools.h index a4ab1b50d..2c1562d2c 100644 --- a/open-vm-tools/services/plugins/vix/vixTools.h +++ b/open-vm-tools/services/plugins/vix/vixTools.h @@ -102,13 +102,6 @@ void VixToolsUnimpersonateUser(void *userToken); void VixToolsLogoutUser(void *userToken); -#ifdef _WIN32 -VixError VixToolsGetUserTmpDir(void *userToken, - char **tmpDirPath); - -Bool VixToolsUserIsMemberOfAdministratorGroup(VixCommandRequestHeader *requestMsg); -#endif // _WIN32 - #endif /* __VIX_TOOLS_H__ */ diff --git a/open-vm-tools/services/plugins/vix/vixToolsInt.h b/open-vm-tools/services/plugins/vix/vixToolsInt.h new file mode 100644 index 000000000..7b452247e --- /dev/null +++ b/open-vm-tools/services/plugins/vix/vixToolsInt.h @@ -0,0 +1,41 @@ +/********************************************************* + * Copyright (C) 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 + * 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. + * + *********************************************************/ + +/* + * vixToolsInt.h -- + * + * Helper routines shared between different files in the vixTools + * module. + */ + +#ifndef __VIX_TOOLS_INT_H__ +#define __VIX_TOOLS_INT_H__ + +#include "vmware.h" +#include "vix.h" + + +#ifdef _WIN32 +VixError VixToolsGetUserTmpDir(void *userToken, + char **tmpDirPath); + +Bool VixToolsUserIsMemberOfAdministratorGroup(VixCommandRequestHeader *requestMsg); +#endif // _WIN32 + + +#endif // #ifndef __VIX_TOOLS_INT_H__ diff --git a/open-vm-tools/services/plugins/vmbackup/scriptOps.c b/open-vm-tools/services/plugins/vmbackup/scriptOps.c index db573134c..1c483343d 100644 --- a/open-vm-tools/services/plugins/vmbackup/scriptOps.c +++ b/open-vm-tools/services/plugins/vmbackup/scriptOps.c @@ -155,12 +155,24 @@ VmBackupRunNextScript(VmBackupScriptOp *op) // IN/OUT char *cmd; if (File_IsFile(scripts[index].path)) { - cmd = Str_Asprintf(NULL, "\"%s\" %s", scripts[index].path, scriptOp); + if (op->state->scriptArg != NULL) { + cmd = Str_Asprintf(NULL, "\"%s\" %s \"%s\"", scripts[index].path, + scriptOp, op->state->scriptArg); + } else { + cmd = Str_Asprintf(NULL, "\"%s\" %s", scripts[index].path, + scriptOp); + } if (cmd != NULL) { + g_debug("Running script: %s\n", cmd); scripts[index].proc = ProcMgr_ExecAsync(cmd, NULL); + } else { + g_debug("Failed to allocate memory to run script: %s\n", + scripts[index].path); + scripts[index].proc = NULL; } + vm_free(cmd); - if (cmd == NULL || scripts[index].proc == NULL) { + if (scripts[index].proc == NULL) { if (op->type == VMBACKUP_SCRIPT_FREEZE) { ret = -1; break; diff --git a/open-vm-tools/services/plugins/vmbackup/stateMachine.c b/open-vm-tools/services/plugins/vmbackup/stateMachine.c index 45fb4012a..ee966378f 100644 --- a/open-vm-tools/services/plugins/vmbackup/stateMachine.c +++ b/open-vm-tools/services/plugins/vmbackup/stateMachine.c @@ -35,6 +35,7 @@ #include "vmBackupInt.h" +#include "dynxdr.h" #include #include #include "guestApp.h" @@ -42,8 +43,12 @@ #include "strutil.h" #include "util.h" #include "vmBackupSignals.h" +#if defined(_WIN32) +#include "vmware/guestrpc/guestQuiesce.h" +#endif #include "vmware/tools/utils.h" #include "vmware/tools/vmbackup.h" +#include "xdrutil.h" #if !defined(__APPLE__) #include "embed_version.h" @@ -205,6 +210,7 @@ VmBackupFinalize(void) } gBackupState->provider->release(gBackupState->provider); + g_free(gBackupState->scriptArg); g_free(gBackupState->volumes); g_free(gBackupState->snapshots); g_free(gBackupState); @@ -247,7 +253,8 @@ VmBackupStartScripts(VmBackupScriptType type) NOT_REACHED(); } - if (!VmBackup_SetCurrentOp(gBackupState, + if (gBackupState->execScripts && + !VmBackup_SetCurrentOp(gBackupState, VmBackup_NewScriptOp(type, gBackupState), NULL, opName)) { @@ -530,29 +537,56 @@ VmBackupEnableSync(void) } -/* RpcIn callbacks. */ +/** + * Get boolean entry for the key from the config file. + * + * @param[in] config Config file to read the key from. + * @param[in] key Key to look for in the config file. + * @param[in] defaultValue Default value if the key is not found. + */ + +static gboolean +VmBackupConfigGetBoolean(GKeyFile *config, + const char *key, + gboolean defValue) +{ + GError *err = NULL; + gboolean value = defValue; + + if (key != NULL) { + value = g_key_file_get_boolean(config, + "vmbackup", + key, + &err); + if (err != NULL) { + g_clear_error(&err); + value = defValue; + } + } + return value; +} /** - * Handler for the "vmbackup.start" message. Starts the "freeze" scripts - * unless there's another backup operation going on or some other - * unexpected error occurs. + * Starts the quiesce operation according to the supplied specification unless + * some unexpected error occurs. * - * @param[in] data RPC data. + * @param[in] data RPC data. + * @param[in] forceVss Only allow Vss quiescing or no quiescing. * * @return TRUE on success. */ static gboolean -VmBackupStart(RpcInData *data) +VmBackupStartCommon(RpcInData *data, + gboolean forceVss) { GError *err = NULL; - guint timeout; ToolsAppCtx *ctx = data->appCtx; VmBackupSyncProvider *provider = NULL; size_t i; - + /* List of available providers, in order of preference for loading. */ struct SyncProvider { VmBackupSyncProvider *(*ctor)(void); @@ -565,31 +599,32 @@ VmBackupStart(RpcInData *data) { VmBackup_NewNullProvider, NULL }, }; - g_debug("*** %s\n", __FUNCTION__); - if (gBackupState != NULL) { - return RPCIN_SETRETVALS(data, "Backup operation already in progress.", FALSE); - } - - /* Instantiate the sync provider. */ - for (i = 0; i < ARRAYSIZE(providers); i++) { - gboolean enabled = TRUE; - struct SyncProvider *sp = &providers[i]; - - if (sp->cfgEntry != NULL) { - enabled = g_key_file_get_boolean(ctx->config, - "vmbackup", - sp->cfgEntry, - &err); - if (err != NULL) { - g_clear_error(&err); - enabled = TRUE; - } + if (forceVss) { + if (gBackupState->quiesceApps || gBackupState->quiesceFS) { + /* If quiescing is requested, only allow VSS provider */ +#if defined(_WIN32) + if (VmBackupConfigGetBoolean(ctx->config, "enableVSS", TRUE)) { + provider = VmBackup_NewVssProvider(); + } +#endif + } else { + /* If no quiescing is requested only allow null provider */ + provider = VmBackup_NewNullProvider(); } - - if (enabled) { - provider = sp->ctor(); - if (provider != NULL) { - break; + if (provider == NULL) { + g_warning("Requested quiescing cannot be initialized."); + goto error; + } + } else { + /* Instantiate the sync provider. */ + for (i = 0; i < ARRAYSIZE(providers); i++) { + struct SyncProvider *sp = &providers[i]; + + if (VmBackupConfigGetBoolean(ctx->config, sp->cfgEntry, TRUE)) { + provider = sp->ctor(); + if (provider != NULL) { + break; + } } } } @@ -597,24 +632,18 @@ VmBackupStart(RpcInData *data) ASSERT(provider != NULL); /* Instantiate the backup state and start the operation. */ - gBackupState = g_new0(VmBackupState, 1); gBackupState->ctx = data->appCtx; gBackupState->pollPeriod = 1000; gBackupState->machineState = VMBACKUP_MSTATE_IDLE; gBackupState->provider = provider; - - if (data->argsSize > 0) { - int generateManifests = 0; - int index = 0; - - if (StrUtil_GetNextIntToken(&generateManifests, &index, data->args, " ")) { - gBackupState->generateManifests = generateManifests; - } - - if (data->args[index] != '\0') { - gBackupState->volumes = g_strndup(data->args + index, data->argsSize - index); - } - } + g_debug("Using quiesceApps = %d, quiesceFS = %d, allowHWProvider = %d," + "execScripts = %d, scriptArg = %s, timeout = %u\n", + gBackupState->quiesceApps, gBackupState->quiesceFS, + gBackupState->allowHWProvider, gBackupState->execScripts, + (gBackupState->scriptArg != NULL) ? gBackupState->scriptArg : "", + gBackupState->timeout); + g_debug("Quiescing volumes: %s", + (gBackupState->volumes) ? gBackupState->volumes : "(null)"); gBackupState->configDir = GuestApp_GetConfPath(); if (gBackupState->configDir == NULL) { @@ -636,20 +665,27 @@ VmBackupStart(RpcInData *data) * anyone wants to play with it), so that we abort any ongoing operation * if we also hit that timeout. * + * First check if the timeout is specified by the RPC command, if not, + * check the tools.conf file, otherwise use the default. + * * See bug 506106. */ - timeout = (guint) g_key_file_get_integer(gBackupState->ctx->config, - "vmbackup", - "timeout", - &err); - if (err != NULL) { - g_clear_error(&err); - timeout = 15 * 60; + if (gBackupState->timeout == 0) { + gBackupState->timeout = (guint) g_key_file_get_integer( + gBackupState->ctx->config, + "vmbackup", + "timeout", + &err); + if (err != NULL) { + g_clear_error(&err); + gBackupState->timeout = 15 * 60; + } } /* Treat "0" as no timeout. */ - if (timeout != 0) { - gBackupState->abortTimer = g_timeout_source_new_seconds(timeout); + if (gBackupState->timeout != 0) { + gBackupState->abortTimer = + g_timeout_source_new_seconds(gBackupState->timeout); VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx, gBackupState->abortTimer, VmBackupAbortTimer, @@ -661,12 +697,130 @@ VmBackupStart(RpcInData *data) return RPCIN_SETRETVALS(data, "", TRUE); error: - gBackupState->provider->release(gBackupState->provider); + if (gBackupState->provider) { + gBackupState->provider->release(gBackupState->provider); + } + g_free(gBackupState->scriptArg); + g_free(gBackupState->volumes); g_free(gBackupState); + gBackupState = NULL; return RPCIN_SETRETVALS(data, "Error initializing backup.", FALSE); } +/* RpcIn callbacks. */ + + +/** + * Handler for the "vmbackup.start" message. Starts the "freeze" scripts + * unless there's another backup operation going on or some other + * unexpected error occurs. + * + * @param[in] data RPC data. + * + * @return TRUE on success. + */ + +static gboolean +VmBackupStart(RpcInData *data) +{ + g_debug("*** %s\n", __FUNCTION__); + if (gBackupState != NULL) { + return RPCIN_SETRETVALS(data, "Backup operation already in progress.", FALSE); + } + gBackupState = g_new0(VmBackupState, 1); + if (data->argsSize > 0) { + int generateManifests = 0; + uint32 index = 0; + + if (StrUtil_GetNextIntToken(&generateManifests, &index, data->args, " ")) { + gBackupState->generateManifests = generateManifests; + } + gBackupState->quiesceApps = TRUE; + gBackupState->quiesceFS = TRUE; + gBackupState->allowHWProvider = TRUE; + gBackupState->execScripts = TRUE; + gBackupState->scriptArg = NULL; + gBackupState->timeout = 0; + + /* get volume uuids if provided */ + if (data->args[index] != '\0') { + gBackupState->volumes = g_strndup(data->args + index, + data->argsSize - index); + } + } + return VmBackupStartCommon(data, FALSE); +} + +#if defined(_WIN32) + +/** + * Handler for the "vmbackup.startWithOpts" message. Starts processing the + * quiesce operation according to the supplied specification unless there's + * another backup operation going on or some other unexpected error occurs. + * + * . If createManifest is true, the guest generates a manifest about the + * application involved during quiescing. + * . If quiesceApps is true, the guest involves applications during + * quiescing. If quiesceFS is true, the guest performs file system + * quiescing. If both quiesceApps and quiesceFS are true, the guest + * falls back to file system quiescing if application quiescing is not + * supported in the guest. If both quiesceApps and quiesceFS are false, + * the guest performs no quiescing but will still run the custom scripts + * provided execScripts is true. + * . If writableSnapshot is true, the guest assumes that writable snapshot + * based quiescing can be performed. + * . If execScripts is true, the guest calls pre-freeze and post-thaw + * scripts before and after quiescing. + * . The scriptArg string is passed to the pre-freeze and post-thaw scripts + * as an argument so that the scripts can be configured to perform + * actions based this argument. + * . The timeout in seconds overrides the default timeout of 15 minutes + * that the guest uses to abort a long quiesce operation. If the timeout + * is 0, the default timeout is used. + * . The volumes argument is a list of diskUuids separated by space. + * + * @param[in] data RPC data. + * + * @return TRUE on success. + */ + +static gboolean +VmBackupStartWithOpts(RpcInData *data) +{ + GuestQuiesceParams *params; + GuestQuiesceParamsV1 *paramsV1; + gboolean retval; + + g_debug("*** %s\n", __FUNCTION__); + if (gBackupState != NULL) { + return RPCIN_SETRETVALS(data, "Backup operation already in progress.", + FALSE); + } + params = (GuestQuiesceParams *)data->args; + if (params->ver != GUESTQUIESCEPARAMS_V1) { + g_warning("%s: Incompatible quiesce parameter version. \n", __FUNCTION__); + return RPCIN_SETRETVALS(data, "Incompatible quiesce parameter version", + FALSE); + } + gBackupState = g_new0(VmBackupState, 1); + paramsV1 = params->GuestQuiesceParams_u.guestQuiesceParamsV1; + gBackupState->generateManifests = paramsV1->createManifest; + gBackupState->quiesceApps = paramsV1->quiesceApps; + gBackupState->quiesceFS = paramsV1->quiesceFS; + gBackupState->allowHWProvider = paramsV1->writableSnapshot; + gBackupState->execScripts = paramsV1->execScripts; + gBackupState->scriptArg = g_strndup(paramsV1->scriptArg, + strlen(paramsV1->scriptArg)); + gBackupState->timeout = paramsV1->timeout; + gBackupState->volumes = g_strndup(paramsV1->diskUuids, + strlen(paramsV1->diskUuids)); + retval = VmBackupStartCommon(data, TRUE); + return retval; +} + +#endif + /** * Aborts the current operation if one is active, and stops the backup * process. If the sync provider has been activated, tell it to abort @@ -809,6 +963,11 @@ ToolsOnLoad(ToolsAppCtx *ctx) RpcChannelCallback rpcs[] = { { VMBACKUP_PROTOCOL_START, VmBackupStart, NULL, NULL, NULL, 0 }, +#if defined(_WIN32) + /* START_WITH_OPTS command supported only on Windows for now */ + { VMBACKUP_PROTOCOL_START_WITH_OPTS, VmBackupStartWithOpts, NULL, + xdr_GuestQuiesceParams, NULL, sizeof (GuestQuiesceParams) }, +#endif { VMBACKUP_PROTOCOL_ABORT, VmBackupAbort, NULL, NULL, NULL, 0 }, { VMBACKUP_PROTOCOL_SNAPSHOT_DONE, VmBackupSnapshotDone, NULL, NULL, NULL, 0 } }; diff --git a/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h b/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h index 1a58fc5f3..d6382a642 100644 --- a/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h +++ b/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h @@ -90,6 +90,12 @@ typedef struct VmBackupState { Bool (*callback)(struct VmBackupState *); Bool forceRequeue; Bool generateManifests; + Bool quiesceApps; + Bool quiesceFS; + Bool allowHWProvider; + Bool execScripts; + char *scriptArg; + guint timeout; gpointer clientData; void *scripts; const char *configDir;