From: VMware, Inc <> Date: Wed, 26 Dec 2012 21:18:49 +0000 (-0800) Subject: HGFS: Fix server directory read hack for the virtual shares folder X-Git-Tag: 2012.12.26-958366~34 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e6f5213ca52dc2fe9c5105afbf92bde0669d304d;p=thirdparty%2Fopen-vm-tools.git HGFS: Fix server directory read hack for the virtual shares folder For some uncommented reason, the HGFS virtual shares folder was always refreshed on the first search read even if this was the first search read since the search open. This means that the initial scan of shares on the search open was always thrown away - making it a total waste of time even constructing the list of dents. Secondly, the was an incorrect assumption that the search type was always the shares folder if there was not a share path. This maybe true for Posix (Linux/OS X) hosts but certainly not true for Windows. Replace this crap. Fixed the function to call into the server platform specific function to handle refreshing directory listings and handle the search type appropriately according to what is supported on the platform. Track whether the directory listing has been already returned completely to the client and use that to determine whether a restart (and rescan of the contents) is required. This means we don't scan the virtual directory contents twice for the first read request from the client. Renamed HgfsServerGetDents to match more closely what it really does and make it consistent with the equivalent function name for operating on real folders. Signed-off-by: Dmitry Torokhov --- diff --git a/open-vm-tools/lib/hgfsServer/hgfsServer.c b/open-vm-tools/lib/hgfsServer/hgfsServer.c index 16c01ebb2..52f319a5a 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServer.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServer.c @@ -2292,6 +2292,7 @@ HgfsAddNewSearch(char const *utf8Dir, // IN: UTF8 name of dir to search in newSearch->dents = NULL; newSearch->numDents = 0; + newSearch->flags = 0; newSearch->type = type; newSearch->handle = HgfsServerGetNextHandleCounter(); @@ -2419,6 +2420,86 @@ HgfsRemoveSearch(HgfsHandle handle, // IN: search } +/* + *---------------------------------------------------------------------------- + * + * HgfsSearchHasReadAllEntries -- + * + * Return whether the client has read all the search entries or not. + * + * Results: + * TRUE on success, FALSE on failure. readAllEntries is filled in on + * success. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static Bool +HgfsSearchHasReadAllEntries(HgfsHandle handle, // IN: Hgfs file handle + HgfsSessionInfo *session, // IN: Session info + Bool *readAllEntries) // OUT: If open was sequential +{ + HgfsSearch *search; + Bool success = FALSE; + + ASSERT(NULL != readAllEntries); + + MXUser_AcquireExclLock(session->searchArrayLock); + + search = HgfsSearchHandle2Search(handle, session); + if (NULL == search) { + goto exit; + } + + *readAllEntries = search->flags & HGFS_SEARCH_FLAG_READ_ALL_ENTRIES; + success = TRUE; + +exit: + MXUser_ReleaseExclLock(session->searchArrayLock); + + return success; +} + + +/* + *---------------------------------------------------------------------------- + * + * HgfsSearchSetReadAllEntries -- + * + * Set the flag to indicate the client has read all the search entries. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static void +HgfsSearchSetReadAllEntries(HgfsHandle handle, // IN: Hgfs file handle + HgfsSessionInfo *session) // IN: Session info +{ + HgfsSearch *search; + + MXUser_AcquireExclLock(session->searchArrayLock); + + search = HgfsSearchHandle2Search(handle, session); + if (NULL == search) { + goto exit; + } + + search->flags |= HGFS_SEARCH_FLAG_READ_ALL_ENTRIES; + +exit: + MXUser_ReleaseExclLock(session->searchArrayLock); +} + + /* *----------------------------------------------------------------------------- * @@ -4917,7 +4998,9 @@ HgfsServerDumpDents(HgfsHandle searchHandle, // IN: Handle to dump dents from /* *----------------------------------------------------------------------------- * - * HgfsServerGetDents -- + * HgfsServerScanvdir -- + * + * Perform a scandir on our virtual directory. * * Get directory entry names from the given callback function, and * build an array of DirectoryEntrys of all the names. Somewhat similar to @@ -4934,7 +5017,7 @@ HgfsServerDumpDents(HgfsHandle searchHandle, // IN: Handle to dump dents from */ static int -HgfsServerGetDents(HgfsGetNameFunc getName, // IN: Function to get name +HgfsServerScanvdir(HgfsGetNameFunc getName, // IN: Function to get name HgfsInitFunc initName, // IN: Setup function HgfsCleanupFunc cleanupName, // IN: Cleanup function DirectoryEntry ***dents) // OUT: Array of DirectoryEntrys @@ -5190,7 +5273,7 @@ HgfsServerSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator { HgfsInternalStatus status = 0; HgfsSearch *search = NULL; - int result = 0; + int scanDirResult; ASSERT(getName); ASSERT(initName); @@ -5206,15 +5289,15 @@ HgfsServerSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator goto out; } - result = HgfsServerGetDents(getName, initName, cleanupName, &search->dents); - if (result < 0) { + scanDirResult = HgfsServerScanvdir(getName, initName, cleanupName, &search->dents); + if (scanDirResult < 0) { LOG(4, ("%s: couldn't get dents\n", __FUNCTION__)); HgfsRemoveSearchInternal(search, session); status = HGFS_ERROR_INTERNAL; goto out; } - search->numDents = result; + search->numDents = scanDirResult; *handle = HgfsSearch2SearchHandle(search); out: @@ -5224,6 +5307,77 @@ HgfsServerSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator } +/* + *----------------------------------------------------------------------------- + * + * HgfsServerRestartSearchVirtualDir -- + * + * Restart a search on a virtual directory (i.e. one that does not + * really exist on the server). Takes a pointer to an enumerator + * for the directory's contents and returns a handle to a search that is + * correctly set up with the virtual directory's entries. + * + * Results: + * Zero on success, returns a handle to the created search. + * Non-zero on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +HgfsInternalStatus +HgfsServerRestartSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator + HgfsInitFunc *initName, // IN: Init function + HgfsCleanupFunc *cleanupName, // IN: Cleanup function + HgfsSessionInfo *session, // IN: Session info + HgfsHandle searchHandle) // IN: search to restart +{ + HgfsInternalStatus status = 0; + HgfsSearch *vdirSearch; + int scanDirResult; + + ASSERT(getName); + ASSERT(initName); + ASSERT(cleanupName); + ASSERT(searchHandle); + + MXUser_AcquireExclLock(session->searchArrayLock); + + vdirSearch = HgfsSearchHandle2Search(searchHandle, session); + if (NULL == vdirSearch) { + status = HGFS_ERROR_INVALID_HANDLE; + goto exit; + } + + /* Release the virtual directory's old set of entries. */ + HgfsFreeSearchDirents(vdirSearch); + + /* Restart by rescanning the virtual directory. */ + scanDirResult = HgfsServerScanvdir(getName, + initName, + cleanupName, + &vdirSearch->dents); + if (scanDirResult < 0) { + ASSERT_DEVEL(0); + LOG(4, ("%s: couldn't get root dents\n", __FUNCTION__)); + vdirSearch->numDents = 0; + status = HGFS_ERROR_INTERNAL; + } + + vdirSearch->numDents = scanDirResult; + /* Clear the flag to indicate that the client has read the entries. */ + vdirSearch->flags &= ~HGFS_SEARCH_FLAG_READ_ALL_ENTRIES; + +exit: + MXUser_ReleaseExclLock(session->searchArrayLock); + + LOG(4, ("%s: refreshing dents return %d\n", __FUNCTION__, status)); + return status; +} + + /* *----------------------------------------------------------------------------- * @@ -7851,6 +8005,7 @@ HgfsGetDirEntry(HgfsHandle hgfsSearchHandle, // IN: ID for search data *nameLength = 0; *moreEntries = FALSE; info->replyFlags |= HGFS_SEARCH_READ_REPLY_FINAL_ENTRY; + HgfsSearchSetReadAllEntries(hgfsSearchHandle, session); } return status; } @@ -8055,27 +8210,35 @@ HgfsServerSearchRead(HgfsInputParam *input) // IN: Input params status = HGFS_ERROR_FILE_NOT_FOUND; } } else if (0 == info.startIndex) { - HgfsSearch *rootSearch; - - MXUser_AcquireExclLock(input->session->searchArrayLock); - - rootSearch = HgfsSearchHandle2Search(hgfsSearchHandle, input->session); - ASSERT(NULL != rootSearch); - HgfsFreeSearchDirents(rootSearch); - - rootSearch->numDents = - HgfsServerGetDents(HgfsServerPolicy_GetShares, - HgfsServerPolicy_GetSharesInit, - HgfsServerPolicy_GetSharesCleanup, - &rootSearch->dents); - if (((int) (rootSearch->numDents)) < 0) { - ASSERT_DEVEL(0); - LOG(4, ("%s: couldn't get root dents\n", __FUNCTION__)); - rootSearch->numDents = 0; + Bool readAllEntries = FALSE; + + /* + * Reading the first entry, we check if this is a second scan + * of the directory. If so, in some cases we restart the scan + * by refreshing the entries first. + */ + if (!HgfsSearchHasReadAllEntries(hgfsSearchHandle, + input->session, + &readAllEntries)) { status = HGFS_ERROR_INTERNAL; } - MXUser_ReleaseExclLock(input->session->searchArrayLock); + if (readAllEntries) { + /* + * XXX - a hack that is now required until Fusion 5.0 end + * of lifes see bug 710697. + * The coder modified the server instead of the OS X client + * for the shares directory refresh needed by OS X clients in + * order to work around handles remaining open by Finder. + * This was fixed CLN 1988575 in the OS X client for 5.0.2. + * However, Fusion 4.0 and Fusion 5.0 tools will rely on this hack. + * At least now it works correctly without breaking everything + * else. + */ + status = HgfsPlatformRestartSearchDir(hgfsSearchHandle, + input->session, + search.type); + } } if (HGFS_ERROR_SUCCESS == status) { diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h index 245a7f451..feb1afb24 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h +++ b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h @@ -240,6 +240,9 @@ typedef struct HgfsSearch { /* Links to place the object on various lists */ DblLnkLst_Links links; + /* Flags to track state and information: see below. */ + uint32 flags; + /* HGFS handle uniquely identifying this search. */ HgfsHandle handle; @@ -272,6 +275,11 @@ typedef struct HgfsSearch { HgfsShareInfo shareInfo; } HgfsSearch; +/* HgfsSearch flags. */ + +/* TRUE if opened in append mode */ +#define HGFS_SEARCH_FLAG_READ_ALL_ENTRIES (1 << 0) + /* HgfsSessionInfo flags. */ typedef enum { HGFS_SESSION_TYPE_REGULAR, /* Dynamic session, created by the HgfsTransport. */ @@ -605,6 +613,13 @@ HgfsServerSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator HgfsSessionInfo *session, // IN: Session info HgfsHandle *handle); // OUT: Search handle +HgfsInternalStatus +HgfsServerRestartSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator + HgfsInitFunc *initName, // IN: Init function + HgfsCleanupFunc *cleanupName, // IN: Cleanup function + HgfsSessionInfo *session, // IN: Session info + HgfsHandle searchHandle); // IN: search to restart + /* Allocate/Add sessions helper functions. */ Bool HgfsServerAllocateSession(HgfsTransportSessionInfo *transportSession, @@ -1127,9 +1142,9 @@ HgfsPlatformSearchDir(HgfsNameStatus nameStatus, // IN: name status HgfsSessionInfo *session, // IN: session info HgfsHandle *handle); // OUT: search handle HgfsInternalStatus -HgfsAccess(char *fileName, // IN: local file path - char *shareName, // IN: Name of the share - size_t shareNameLen); // IN: Length of the share name +HgfsPlatformRestartSearchDir(HgfsHandle handle, // IN: search handle + HgfsSessionInfo *session, // IN: session info + DirectorySearchType searchType); // IN: Kind of search HgfsInternalStatus HgfsPlatformReadFile(HgfsHandle file, // IN: Hgfs file handle HgfsSessionInfo *session, // IN: session info diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c b/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c index b7a6ae097..4e3bfe212 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c @@ -3629,6 +3629,51 @@ HgfsPlatformSearchDir(HgfsNameStatus nameStatus, // IN: name status } +/* + *----------------------------------------------------------------------------- + * + * HgfsPlatformRestartSearchDir -- + * + * Handle platform specific restarting of a directory search. + * + * Results: + * ERROR_SUCCESS or an appropriate Win32 error code. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +HgfsInternalStatus +HgfsPlatformRestartSearchDir(HgfsHandle handle, // IN: search handle + HgfsSessionInfo *session, // IN: session info + DirectorySearchType searchType) // IN: Kind of search +{ + HgfsInternalStatus status; + + switch (searchType) { + case DIRECTORY_SEARCH_TYPE_BASE: + /* Entries are shares */ + status = HgfsServerRestartSearchVirtualDir(HgfsServerPolicy_GetShares, + HgfsServerPolicy_GetSharesInit, + HgfsServerPolicy_GetSharesCleanup, + session, + handle); + break; + case DIRECTORY_SEARCH_TYPE_OTHER: + /* Entries of this type are unknown and not supported for this platform. */ + case DIRECTORY_SEARCH_TYPE_DIR: + /* Entries are files and subdirectories: currently not implemented! */ + default: + status = EINVAL; + break; + } + + return status; +} + + /* *----------------------------------------------------------------------------- *