From: VMware, Inc <> Date: Mon, 20 Dec 2010 22:05:30 +0000 (-0800) Subject: Host part of directory change notification. X-Git-Tag: 2010.12.19-339835~34 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=575d96e945d6f2f64d67891820ec9abf0da350ef;p=thirdparty%2Fopen-vm-tools.git Host part of directory change notification. Initial implementation of directory change notification support for Mac OS. 1. Parse set notification watch request from the guest and pass it down to platform specific library. 2. Parse remove change notification watch and call plaform specific library. 3. Call platform specific library when shared folder is being added/removed and enumerate all shared folders during initialization. 4. Implement callback to send notifications back. 5. Integrated with Mac OS specific directory notification code. 6. Implemented prototype of change directory notification support for WIndows host. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/lib/hgfsServer/hgfsDirNotify.h b/open-vm-tools/lib/hgfsServer/hgfsDirNotify.h index a2eb899e3..a62abe23a 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsDirNotify.h +++ b/open-vm-tools/lib/hgfsServer/hgfsDirNotify.h @@ -16,58 +16,39 @@ * *********************************************************/ +#ifndef _HGFS_DIRNOTIFY_H +#define _HGFS_DIRNOTIFY_H + /* * hgfsDirNotify.h -- * * Function definitions for directory change notification. */ -/* - * XXX: - * Following constants comes from HGFS protocol definition. - * When hgfsProto.h is updated with new definitions for V4 - * these constants should be removed and definitions from - * hgfsProto.h should be used instead. - */ -#define HGFS_FILE_NOTIFY_ADD_FILE (1 << 0) -#define HGFS_FILE_NOTIFY_ADD_DIR (1 << 1) -#define HGFS_FILE_NOTIFY_DELETE_FILE (1 << 2) -#define HGFS_FILE_NOTIFY_DELETE_DIR (1 << 3) -#define HGFS_FILE_NOTIFY_RENAME_FILE (1 << 4) -#define HGFS_FILE_NOTIFY_RENAME_DIR (1 << 5) -#define HGFS_FILE_NOTIFY_CHANGE_SIZE (1 << 6) -#define HGFS_FILE_NOTIFY_CHANGE_LAST_WRITE (1 << 7) -#define HGFS_FILE_NOTIFY_CHANGE_LAST_ACCESS (1 << 8) -#define HGFS_FILE_NOTIFY_CHANGE_CREATION (1 << 9) -#define HGFS_FILE_NOTIFY_CHANGE_EA (1 << 10) -#define HGFS_FILE_NOTIFY_CHANGE_SECURITY (1 << 11) -#define HGFS_FILE_NOTIFY_ADD_STREAM (1 << 12) -#define HGFS_FILE_NOTIFY_DELETE_STREAM (1 << 13) -#define HGFS_FILE_NOTIFY_CHANGE_STREAM_SIZE (1 << 14) -#define HGFS_FILE_NOTIFY_CHANGE_STREAM_LAST_WRITE (1 << 15) -#define HGFS_FILE_NOTIFY_WATCH_DELETED (1 << 16) -#define HGFS_FILE_NOTIFY_EVENTS_DROPPED (1 << 17) - -#define INVALID_OBJECT_HANDLE 0xffffffff - -typedef uint32 HgfsSharedFolderHandle; -typedef uint64 HgfsSubscriberHandle; - -uint32 HgfsNotify_Init(void); +struct HgfsSessionInfo; +/* This is a callback that is implemented in hgfsServer.c */ +typedef void HgfsNotificationCallbackFunc(HgfsSharedFolderHandle sharedFolder, + HgfsSubscriberHandle subscriber, + char *name, + uint32 mask, + struct HgfsSessionInfo *session); +HgfsInternalStatus HgfsNotify_Init(void); void HgfsNotify_Shutdown(void); -HgfsSharedFolderHandle HgfsNotify_AddSharedFolder(const char *path); +HgfsSharedFolderHandle HgfsNotify_AddSharedFolder(const char *path, + const char *shareName); HgfsSubscriberHandle HgfsNotify_AddSubscriber(HgfsSharedFolderHandle sharedFolder, const char *path, uint32 eventFilter, - uint32 recursive); - -uint32 HgfsNotify_RemoveSharedFolder(HgfsSharedFolderHandle sharedFolder); -uint32 HgfsNotify_RemoveSubscriber(HgfsSubscriberHandle subscriber); - -/* This is a callback that is implemented in hgfsServer.c */ -void Hgfs_NotificationCallback(HgfsSharedFolderHandle sharedFolder, - HgfsSubscriberHandle subscriber, - char *name, - char *newName, - uint32 mask); + uint32 recursive, + HgfsNotificationCallbackFunc notify, + struct HgfsSessionInfo *session); + +Bool HgfsNotify_RemoveSharedFolder(HgfsSharedFolderHandle sharedFolder); +Bool HgfsNotify_RemoveSubscriber(HgfsSubscriberHandle subscriber); +void HgfsNotify_CleanupSession(struct HgfsSessionInfo *session); +Bool HgfsNotify_GetShareName(HgfsSharedFolderHandle sharedFolder, + size_t *shareNameLen, + char **shareName); + +#endif // _HGFS_DIRNOTIFY_H diff --git a/open-vm-tools/lib/hgfsServer/hgfsDirNotifyStub.c b/open-vm-tools/lib/hgfsServer/hgfsDirNotifyStub.c index d04f5504a..3ca29d850 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsDirNotifyStub.c +++ b/open-vm-tools/lib/hgfsServer/hgfsDirNotifyStub.c @@ -28,6 +28,9 @@ #include "vmware.h" #include "vm_basic_types.h" +#include "hgfsProto.h" +#include "hgfsServer.h" +#include "hgfsUtil.h" #include "hgfsDirNotify.h" @@ -39,7 +42,7 @@ * One time initialization of the library. * * Results: - * 0 if success, error code otherwise. + * Invalid value error. * * Side effects: * None. @@ -47,7 +50,7 @@ *----------------------------------------------------------------------------- */ -uint32 +HgfsInternalStatus HgfsNotify_Init(void) { return EINVAL; @@ -84,7 +87,7 @@ HgfsNotify_Shutdown(void) * Allocates memory and initializes new shared folder structure. * * Results: - * Opaque subscriber handle for the new subscriber or INVALID_OBJECT_HANDLE + * Opaque subscriber handle for the new subscriber or HGFS_INVALID_FOLDER_HANDLE * if adding shared folder fails. * * Side effects: @@ -94,9 +97,10 @@ HgfsNotify_Shutdown(void) */ HgfsSharedFolderHandle -HgfsNotify_AddSharedFolder(const char *path) // IN +HgfsNotify_AddSharedFolder(const char *path, // IN: path in the host + const char *shareName) // IN: name of the shared folder { - return INVALID_OBJECT_HANDLE; + return HGFS_INVALID_FOLDER_HANDLE; } @@ -109,7 +113,7 @@ HgfsNotify_AddSharedFolder(const char *path) // IN * Inserts allocated subscriber into corrspondent array. * * Results: - * Opaque subscriber handle for the new subscriber or INVALID_OBJECT_HANDLE + * Opaque subscriber handle for the new subscriber or HGFS_INVALID_SUBSCRIBER_HANDLE * if adding subscriber fails. * * Side effects: @@ -119,12 +123,14 @@ HgfsNotify_AddSharedFolder(const char *path) // IN */ HgfsSubscriberHandle -HgfsNotify_AddSubscriber(HgfsSharedFolderHandle sharedFolder, // IN - const char *path, // IN path relative to shared folder - uint32 eventFilter, // IN - uint32 recursive) // IN TRUE if look in subdirectories +HgfsNotify_AddSubscriber(HgfsSharedFolderHandle sharedFolder, // IN: shared folder handle + const char *path, // IN: relative path + uint32 eventFilter, // IN: event filter + uint32 recursive, // IN: look in subfolders + HgfsNotificationCallbackFunc notify, // IN notification callback + struct HgfsSessionInfo *session) // IN: server context { - return INVALID_OBJECT_HANDLE; + return HGFS_INVALID_SUBSCRIBER_HANDLE; } /* @@ -136,7 +142,7 @@ HgfsNotify_AddSubscriber(HgfsSharedFolderHandle sharedFolder, // IN * Also deletes all subscribers that are defined for the shared folder. * * Results: - * 0 if success, error code otherwise. + * FALSE. * * Side effects: * Removes all subscribers that correspond to the shared folder and invalidates @@ -145,10 +151,10 @@ HgfsNotify_AddSubscriber(HgfsSharedFolderHandle sharedFolder, // IN *----------------------------------------------------------------------------- */ -uint32 +Bool HgfsNotify_RemoveSharedFolder(HgfsSharedFolderHandle sharedFolder) // IN { - return EINVAL; + return FALSE; } @@ -160,7 +166,7 @@ HgfsNotify_RemoveSharedFolder(HgfsSharedFolderHandle sharedFolder) // IN * Deallcates memory used by NotificationSubscriber and performs necessary cleanup. * * Results: - * 0 if success, error code otherwise. + * FALSE. * * Side effects: * None. @@ -168,8 +174,30 @@ HgfsNotify_RemoveSharedFolder(HgfsSharedFolderHandle sharedFolder) // IN *----------------------------------------------------------------------------- */ -uint32 +Bool HgfsNotify_RemoveSubscriber(HgfsSubscriberHandle subscriber) // IN { - return EINVAL; + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsNotify_CleanupSession -- + * + * Removes all entries that are related to a particular session. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +HgfsNotify_CleanupSession(struct HgfsSessionInfo *session) // IN +{ } diff --git a/open-vm-tools/lib/hgfsServer/hgfsServer.c b/open-vm-tools/lib/hgfsServer/hgfsServer.c index c4f252802..bc745f8e6 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServer.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServer.c @@ -33,6 +33,7 @@ #include "file.h" #include "util.h" #include "wiper.h" +#include "hgfsServer.h" #include "hgfsDirNotify.h" #include "hgfsTransport.h" #include "userlock.h" @@ -95,6 +96,9 @@ #define HGFS_ASSERT_MINIMUM_OP(op) #endif +#ifdef VMX86_TOOLS +#define Config_GetBool(defaultValue,fmt) (defaultValue) +#endif /* * This ensures that the hgfs name conversion code never fails on long @@ -178,7 +182,25 @@ HgfsServerSessionCallbacks hgfsServerSessionCBTable = { HgfsServerSessionSendComplete, }; -static Bool hgfsChangeNotificationSupported = FALSE; +/* Lock that protects shared folders list. */ +static MXUserExclLock *gHgfsSharedFoldersLock = NULL; + +/* List of shared folders nodes. */ +static DblLnkLst_Links gHgfsSharedFoldersList; + +/* + * Number of active sessions that support change directory notification. HGFS server + * needs to maintain up-to-date shared folders list when there is + * at least one such session. + */ +static Bool gHgfsDirNotifyActive = FALSE; + +typedef struct HgfsSharedFolderProperties { + DblLnkLst_Links links; + char *name; /* Name of the share. */ + HgfsSharedFolderHandle notificationHandle; /* Directory notification handle. */ + Bool markedForDeletion; +} HgfsSharedFolderProperties; /* * Limit payload to 16M + header. @@ -233,6 +255,16 @@ static Bool HgfsIsShareRoot(char const *cpName, size_t cpNameSize); static void HgfsServerCompleteRequest(HgfsInternalStatus status, size_t replyPayloadSize, HgfsInputParam *input); +static Bool HgfsHandle2NotifyInfo(HgfsHandle handle, + HgfsSessionInfo *session, + char **fileName, + size_t *fileNameSize, + HgfsSharedFolderHandle *folderHandle); +static void Hgfs_NotificationCallback(HgfsSharedFolderHandle sharedFolder, + HgfsSubscriberHandle subscriber, + char* fileName, + uint32 mask, + struct HgfsSessionInfo *session); /* * Opcode handlers @@ -257,6 +289,8 @@ static void HgfsServerCreateSession(HgfsInputParam *input); static void HgfsServerDestroySession(HgfsInputParam *input); static void HgfsServerClose(HgfsInputParam *input); static void HgfsServerSearchClose(HgfsInputParam *input); +static void HgfsServerSetDirNotifyWatch(HgfsInputParam *input); +static void HgfsServerRemoveDirNotifyWatch(HgfsInputParam *input); /* @@ -809,6 +843,7 @@ HgfsHandle2FileName(HgfsHandle handle, // IN: Hgfs file handle fileNameSize); } + /* *----------------------------------------------------------------------------- * @@ -872,6 +907,58 @@ exit_unlock: } +/* + *----------------------------------------------------------------------------- + * + * HgfsHandle2FileNameMode -- + * + * Given an OS handle/fd, return information needed for directory + * notification package: relative to the root share file name and + * shared folder notification handle. + * + * Results: + * TRUE if the node was found. + * FALSE otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsHandle2NotifyInfo(HgfsHandle handle, // IN: Hgfs file handle + HgfsSessionInfo *session, // IN: Session info + char **fileName, // OUT: UTF8 file name + size_t *fileNameSize, // OUT: UTF8 file name size + HgfsSharedFolderHandle *folderHandle) // OUT: shared folder handle +{ + Bool found = FALSE; + HgfsFileNode *existingFileNode; + char *name; + size_t nameSize; + + ASSERT(fileName != NULL && fileNameSize != NULL); + MXUser_AcquireExclLock(session->nodeArrayLock); + + existingFileNode = HgfsHandle2FileNode(handle, session); + if (NULL != existingFileNode) { + nameSize = existingFileNode->utf8NameLen - existingFileNode->shareInfo.rootDirLen; + name = Util_SafeMalloc(nameSize + 1); + *folderHandle = existingFileNode->shareInfo.handle; + memcpy(name, existingFileNode->utf8Name, nameSize); + name[nameSize] = '\0'; + *fileName = name; + *fileNameSize = nameSize; + found = TRUE; + } + + MXUser_ReleaseExclLock(session->nodeArrayLock); + + return found; +} + + /* *----------------------------------------------------------------------------- * @@ -1677,6 +1764,7 @@ HgfsAddNewFileNode(HgfsFileOpenInfo *openInfo, // IN: open info struct newNode->state = FILENODE_STATE_IN_USE_NOT_CACHED; newNode->shareInfo.readPermissions = openInfo->shareInfo.readPermissions; newNode->shareInfo.writePermissions = openInfo->shareInfo.writePermissions; + newNode->shareInfo.handle = openInfo->shareInfo.handle; LOG(4, ("%s: got new node, handle %u\n", __FUNCTION__, HgfsFileNode2Handle(newNode))); @@ -2191,27 +2279,13 @@ HgfsAddNewSearch(char const *utf8Dir, // IN: UTF8 name of dir to search in newSearch->handle = HgfsServerGetNextHandleCounter(); newSearch->utf8DirLen = strlen(utf8Dir); - newSearch->utf8Dir = strdup(utf8Dir); - if (newSearch->utf8Dir == NULL) { - HgfsRemoveSearchInternal(newSearch, session); - - return NULL; - } + newSearch->utf8Dir = Util_SafeStrdup(utf8Dir); newSearch->utf8ShareNameLen = strlen(utf8ShareName); - newSearch->utf8ShareName = strdup(utf8ShareName); - if (newSearch->utf8ShareName == NULL) { - HgfsRemoveSearchInternal(newSearch, session); - - return NULL; - } + newSearch->utf8ShareName = Util_SafeStrdup(utf8ShareName); newSearch->shareInfo.rootDirLen = strlen(rootDir); - newSearch->shareInfo.rootDir = strdup(rootDir); - if (newSearch->shareInfo.rootDir == NULL) { - HgfsRemoveSearchInternal(newSearch, session); - return NULL; - } + newSearch->shareInfo.rootDir = Util_SafeStrdup(rootDir); LOG(4, ("%s: got new search, handle %u\n", __FUNCTION__, HgfsSearch2SearchHandle(newSearch))); @@ -2667,6 +2741,8 @@ static struct { { HgfsServerDestroySession, offsetof(HgfsRequestDestroySessionV4, reserved), REQ_SYNC}, { HgfsServerRead, sizeof (HgfsRequestReadV3), REQ_SYNC}, { HgfsServerWrite, sizeof (HgfsRequestWriteV3), REQ_SYNC}, + { HgfsServerSetDirNotifyWatch, sizeof (HgfsRequestSetWatchV4), REQ_SYNC}, + { HgfsServerRemoveDirNotifyWatch, sizeof (HgfsRequestRemoveWatchV4), REQ_SYNC}, }; @@ -2883,6 +2959,209 @@ HgfsServerSessionReceive(HgfsPacket *packet, // IN: Hgfs Packet } +/* + *----------------------------------------------------------------------------- + * + * HgfsServerCleanupDeletedFolders -- + * + * This function iterates through all shared folders and removes all deleted + * shared folders, removes them from notification package and from the folders list. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsServerCleanupDeletedFolders(void) +{ + DblLnkLst_Links *link, *nextElem; + + MXUser_AcquireExclLock(gHgfsSharedFoldersLock); + DblLnkLst_ForEachSafe(link, nextElem, &gHgfsSharedFoldersList) { + HgfsSharedFolderProperties *folder = + DblLnkLst_Container(link, HgfsSharedFolderProperties, links); + if (folder->markedForDeletion) { + if (!HgfsNotify_RemoveSharedFolder(folder->notificationHandle)) { + LOG(4, ("Problem removing %d shared folder handle\n", + folder->notificationHandle)); + } + DblLnkLst_Unlink1(link); + free(folder); + } + } + MXUser_ReleaseExclLock(gHgfsSharedFoldersLock); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsServer_RegisterSharedFolder -- + * + * This is a callback function which is invoked by hgfsServerManagement + * for every shared folder when something changed in shared folders configuration. + * The function iterates through the list of exisitng shared folders trying to locate + * an entry with the shareName. If the entry is found the function returns corresponding + * handle. Otherwise it creates a new entry and assigns a new handle to it. + * + * Currently there is no notification that a shared folder has been deleted. The only + * way to find out that a shred folder is deleted is to notice that it is not + * enumerated any more. Thus an explicit "end of list" notification is needed. + * "sharedFolder == NULL" notifies that enumeration is completed which allows to delete + * all shared folders that were not mentioned during current enumeration. + * + * Results: + * HgfsSharedFolderHandle for the entry. + * + * Side effects: + * May add an entry to known shared folders list. + * + *----------------------------------------------------------------------------- + */ + +HgfsSharedFolderHandle +HgfsServer_RegisterSharedFolder(const char *shareName, // IN: shared folder name + const char *sharePath, // IN: shared folder path + Bool addFolder) // IN: add or remove folder +{ + DblLnkLst_Links *link, *nextElem; + HgfsSharedFolderHandle result = HGFS_INVALID_FOLDER_HANDLE; + + if (!gHgfsDirNotifyActive) { + return HGFS_INVALID_FOLDER_HANDLE; + } + + if (NULL == shareName) { + /* + * The function is invoked with shareName NULL when all shares has been + * enumerated. + * Need to delete all shared folders that were marked for deletion. + */ + HgfsServerCleanupDeletedFolders(); + return HGFS_INVALID_FOLDER_HANDLE; + } + + MXUser_AcquireExclLock(gHgfsSharedFoldersLock); + + DblLnkLst_ForEachSafe(link, nextElem, &gHgfsSharedFoldersList) { + HgfsSharedFolderProperties *folder = + DblLnkLst_Container(link, HgfsSharedFolderProperties, links); + if (strcmp(folder->name, shareName) == 0) { + result = folder->notificationHandle; + folder->markedForDeletion = !addFolder; + break; + } + } + if (addFolder && HGFS_INVALID_FOLDER_HANDLE == result) { + result = HgfsNotify_AddSharedFolder(sharePath, shareName); + if (HGFS_INVALID_FOLDER_HANDLE != result) { + HgfsSharedFolderProperties *folder = + (HgfsSharedFolderProperties *)Util_SafeMalloc(sizeof *folder); + folder->notificationHandle = result; + folder->name = Util_SafeStrdup(shareName); + folder->markedForDeletion = FALSE; + DblLnkLst_Init(&folder->links); + DblLnkLst_LinkLast(&gHgfsSharedFoldersList, &folder->links); + } + } + MXUser_ReleaseExclLock(gHgfsSharedFoldersLock); + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsServerGetShareHandle -- + * + * The function returns shared folder notification handle for the specified + * shared folder. + * + * Results: + * HgfsSharedFolderHandle that corresponds to the shared folder. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static HgfsSharedFolderHandle +HgfsServerGetShareHandle(const char *shareName) // IN: name of the shared folder +{ + DblLnkLst_Links *link; + HgfsSharedFolderHandle result = HGFS_INVALID_FOLDER_HANDLE; + + if (!gHgfsDirNotifyActive) { + return HGFS_INVALID_FOLDER_HANDLE; + } + + MXUser_AcquireExclLock(gHgfsSharedFoldersLock); + + DblLnkLst_ForEach(link, &gHgfsSharedFoldersList) { + HgfsSharedFolderProperties *folder = + DblLnkLst_Container(link, HgfsSharedFolderProperties, links); + if (strcmp(folder->name, shareName) == 0) { + result = folder->notificationHandle; + break; + } + } + MXUser_ReleaseExclLock(gHgfsSharedFoldersLock); + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsServerGetShareName -- + * + * Get the share name for a shared folder handle by looking at the requested + * handle, finding the matching share (if any), and returning the share's name. + * + * Results: + * An Bool value indicating if the result is returned. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +HgfsServerGetShareName(HgfsSharedFolderHandle sharedFolder, // IN: Notify handle + size_t *shareNameLen, // OUT: Name length + char **shareName) // OUT: Share name +{ + Bool result = FALSE; + DblLnkLst_Links *link; + + if (!gHgfsDirNotifyActive) { + return FALSE; + } + + MXUser_AcquireExclLock(gHgfsSharedFoldersLock); + + DblLnkLst_ForEach(link, &gHgfsSharedFoldersList) { + HgfsSharedFolderProperties *folder = + DblLnkLst_Container(link, HgfsSharedFolderProperties, links); + if (folder->notificationHandle == sharedFolder) { + *shareName = Util_SafeStrdup(folder->name); + result = TRUE; + *shareNameLen = strlen(*shareName); + break; + } + } + MXUser_ReleaseExclLock(gHgfsSharedFoldersLock); + return result; +} + + /* *----------------------------------------------------------------------------- * @@ -2911,23 +3190,32 @@ HgfsServer_InitState(HgfsServerSessionCallbacks **callbackTable, // IN/OUT: our maxCachedOpenNodes = Config_GetLong(MAX_CACHED_FILENODES, "hgfs.fdCache.maxNodes"); -#ifndef VMX86_TOOLS - if (Config_GetBool(FALSE, "hgfs.alwaysUseHostTime")) { - alwaysUseHostTime = TRUE; - } -#endif + alwaysUseHostTime = Config_GetBool(FALSE, "hgfs.alwaysUseHostTime"); - if (HgfsNotify_Init() == 0) { - hgfsChangeNotificationSupported = TRUE; + /* + * Initialize the globals for handling the active shared folders. + */ + + gHgfsSharedFoldersLock = MXUser_CreateExclLock("sharedFoldersLock", RANK_hgfsSharedFolders); + if (NULL == gHgfsSharedFoldersLock) { + LOG(4, ("%s: Could not create shared folders mutex.\n", __FUNCTION__)); + + return FALSE; } + DblLnkLst_Init(&gHgfsSharedFoldersList); if (!HgfsServerPlatformInit()) { LOG(4, ("Could not initialize server platform specific \n")); + MXUser_DestroyExclLock(gHgfsSharedFoldersLock); + gHgfsSharedFoldersLock = NULL; return FALSE; } *callbackTable = &hgfsServerSessionCBTable; + if (Config_GetBool(TRUE, "isolation.tools.hgfs.notify.enable")) { + gHgfsDirNotifyActive = HgfsNotify_Init() == HGFS_STATUS_SUCCESS; + } return TRUE; } @@ -2956,9 +3244,14 @@ HgfsServer_InitState(HgfsServerSessionCallbacks **callbackTable, // IN/OUT: our void HgfsServer_ExitState(void) { - - if (hgfsChangeNotificationSupported) { + if (gHgfsDirNotifyActive) { HgfsNotify_Shutdown(); + gHgfsDirNotifyActive = FALSE; + } + + if (NULL != gHgfsSharedFoldersLock) { + MXUser_DestroyExclLock(gHgfsSharedFoldersLock); + gHgfsSharedFoldersLock = NULL; } HgfsServerPlatformDestroy(); @@ -3025,6 +3318,60 @@ HgfsServerSetSessionCapability(HgfsOp op, // IN: operation code } +/* + *----------------------------------------------------------------------------- + * + * HgfsServerEnumerateSharedFolders -- + * + * Enumerates all exisitng shared folders and registers shared folders with + * directory notification package. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +HgfsServerEnumerateSharedFolders(void) +{ + void *state; + Bool success = FALSE; + + state = HgfsServerPolicy_GetSharesInit(); + if (NULL != state) { + Bool done; + + do { + char const *shareName; + size_t len; + + success = HgfsServerPolicy_GetShares(state, &shareName, &len, &done); + if (success && !done) { + HgfsSharedFolderHandle handle; + char const *sharePath; + size_t sharePathLen; + HgfsNameStatus nameStatus; + + nameStatus = HgfsServerPolicy_GetSharePath(shareName, len, + HGFS_OPEN_MODE_READ_ONLY, + &sharePathLen, &sharePath); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + handle = HgfsServer_RegisterSharedFolder(shareName, sharePath, TRUE); + success = handle != HGFS_INVALID_FOLDER_HANDLE; + } + } + } while (!done && success); + + HgfsServerPolicy_GetSharesCleanup(state); + } + return success; +} + + /* *----------------------------------------------------------------------------- * @@ -3093,6 +3440,7 @@ HgfsServerSessionConnect(void *transportData, // IN: tra session->sessionId = HgfsGenerateSessionId(); session->maxPacketSize = MAX_SERVER_PACKET_SIZE_V4; + session->activeNotification = FALSE; /* * Initialize the node handling components. */ @@ -3151,6 +3499,16 @@ HgfsServerSessionConnect(void *transportData, // IN: tra if (channelCapabililies & HGFS_CHANNEL_SHARED_MEM) { HgfsServerSetSessionCapability(HGFS_OP_READ_FAST_V4, HGFS_REQUEST_SUPPORTED, session); HgfsServerSetSessionCapability(HGFS_OP_WRITE_FAST_V4, HGFS_REQUEST_SUPPORTED, session); + if (gHgfsDirNotifyActive) { + if (HgfsServerEnumerateSharedFolders()) { + HgfsServerSetSessionCapability(HGFS_OP_SET_WATCH_V4, HGFS_REQUEST_SUPPORTED, session); + HgfsServerSetSessionCapability(HGFS_OP_REMOVE_WATCH_V4, HGFS_REQUEST_SUPPORTED, session); + session->activeNotification = TRUE; + } else { + HgfsServerSetSessionCapability(HGFS_OP_SET_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED, session); + HgfsServerSetSessionCapability(HGFS_OP_REMOVE_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED, session); + } + } } return TRUE; @@ -3187,6 +3545,10 @@ HgfsServerSessionDisconnect(void *clientData) // IN: session context ASSERT(session->searchArray); session->state = HGFS_SESSION_STATE_CLOSED; + + if (session->activeNotification) { + HgfsNotify_CleanupSession(session); + } } @@ -3693,6 +4055,7 @@ HgfsServerGetShareInfo(char *cpName, // IN: Cross-platform filename len, &shareInfo->readPermissions, &shareInfo->writePermissions, + &shareInfo->handle, &shareInfo->rootDir); if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { LOG(4, ("%s: No such share (%s)\n", __FUNCTION__, cpName)); @@ -3704,7 +4067,6 @@ HgfsServerGetShareInfo(char *cpName, // IN: Cross-platform filename nameStatus = HgfsServerPolicy_GetShareOptions(cpName, len, &shareOptions); if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, cpName)); - return nameStatus; } @@ -5858,6 +6220,264 @@ HgfsServerWriteWin32Stream(HgfsInputParam *input) // IN: Input params } +/* + *----------------------------------------------------------------------------- + * + * HgfsServerSetDirWatchByHandle -- + * + * Sets directory notification watch request using directory handle. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static HgfsInternalStatus +HgfsServerSetDirWatchByHandle(HgfsInputParam *input, // IN: Input params + HgfsHandle dir, // IN: directory handle + uint32 events, // IN: event types to report + Bool watchTree, // IN: recursive watch + HgfsSubscriberHandle *watchId) // OUT: watch id +{ + HgfsInternalStatus status; + char *fileName = NULL; + size_t fileNameSize; + HgfsSharedFolderHandle sharedFolder = HGFS_INVALID_FOLDER_HANDLE; + + ASSERT(watchId != NULL); + + if (HgfsHandle2NotifyInfo(dir, input->session, &fileName, &fileNameSize, + &sharedFolder)) { + *watchId = HgfsNotify_AddSubscriber(sharedFolder, fileName, events, watchTree, + Hgfs_NotificationCallback, input->session); + status = (HGFS_INVALID_SUBSCRIBER_HANDLE == *watchId) ? HGFS_ERROR_INTERNAL : + HGFS_ERROR_SUCCESS; + } else { + status = HGFS_ERROR_INTERNAL; + } + free(fileName); + return status; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsServerSetDirWatchByName -- + * + * Sets directory notification watch request using directory name. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static HgfsInternalStatus +HgfsServerSetDirWatchByName(HgfsInputParam *input, // IN: Input params + char *cpName, // IN: directory name + uint32 cpNameSize, // IN: directory name length + uint32 caseFlags, // IN: case flags + uint32 events, // IN: event types to report + Bool watchTree, // IN: recursive watch + HgfsSubscriberHandle *watchId) // OUT: watch id +{ + HgfsInternalStatus status; + HgfsNameStatus nameStatus; + char *utf8Name = NULL; + size_t utf8NameLen; + HgfsShareInfo shareInfo; + HgfsSharedFolderHandle sharedFolder = HGFS_INVALID_FOLDER_HANDLE; + + ASSERT(cpName != NULL); + ASSERT(watchId != NULL); + + nameStatus = HgfsServerGetShareInfo(cpName, cpNameSize, caseFlags, &shareInfo, + &utf8Name, &utf8NameLen); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + char const *inEnd = cpName + cpNameSize; + char const *next; + int len; + + ASSERT(utf8Name); + /* + * Get first component. + */ + len = CPName_GetComponent(cpName, inEnd, (char const **) &next); + if (len < 0) { + LOG(4, ("%s: get first component failed\n", __FUNCTION__)); + nameStatus = HGFS_NAME_STATUS_FAILURE; + } else if (0 == len) { + /* See if we are dealing with the base of the namespace */ + nameStatus = HGFS_NAME_STATUS_INCOMPLETE_BASE; + } else { + sharedFolder = HgfsServerGetShareHandle(cpName); + } + + if (HGFS_NAME_STATUS_COMPLETE == nameStatus && + HGFS_INVALID_FOLDER_HANDLE != sharedFolder) { + if (cpNameSize > len + 1) { + size_t nameSize = cpNameSize - len - 1; + char tempBuf[HGFS_PATH_MAX]; + char *tempPtr = tempBuf; + size_t tempSize = sizeof tempBuf; + + nameStatus = CPName_ConvertFrom((char const **) &next, &nameSize, + &tempSize, &tempPtr); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + *watchId = HgfsNotify_AddSubscriber(sharedFolder, tempBuf, events, + watchTree, Hgfs_NotificationCallback, + input->session); + status = (HGFS_INVALID_SUBSCRIBER_HANDLE == *watchId) ? + HGFS_ERROR_INTERNAL : HGFS_ERROR_SUCCESS; + } else { + LOG(4, ("%s: Conversion to platform specific name failed\n", + __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } + } else { + *watchId = HgfsNotify_AddSubscriber(sharedFolder, "", events, watchTree, + Hgfs_NotificationCallback, + input->session); + status = (HGFS_INVALID_SUBSCRIBER_HANDLE == *watchId) ? HGFS_ERROR_INTERNAL : + HGFS_ERROR_SUCCESS; + } + } else if (HGFS_NAME_STATUS_INCOMPLETE_BASE == nameStatus) { + LOG(4, ("%s: Notification for root share is not supported yet\n", + __FUNCTION__)); + status = HGFS_ERROR_INVALID_PARAMETER; + } else { + LOG(4, ("%s: file not found.\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } + } else { + LOG(4, ("%s: file not found.\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } + free(utf8Name); + return status; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsServerSetDirNotifyWatch -- + * + * Handle a set directory notification watch request. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsServerSetDirNotifyWatch(HgfsInputParam *input) // IN: Input params +{ + char *cpName; + size_t cpNameSize; + HgfsInternalStatus status; + HgfsHandle dir; + uint32 caseFlags; + size_t replyPayloadSize = 0; + uint32 flags; + uint32 events; + HgfsSubscriberHandle watchId = HGFS_INVALID_SUBSCRIBER_HANDLE; + Bool useHandle; + + HGFS_ASSERT_INPUT(input); + + /* + * If the active session does not support directory change notification - bail out + * with an error immediately. Otherwise setting watch may succeed but no notification + * will be delivered when a change occurs. + */ + if (!input->session->activeNotification) { + HgfsServerCompleteRequest(HGFS_ERROR_PROTOCOL, 0, input); + return; + } + + if (HgfsUnpackSetWatchRequest(input->payload, input->payloadSize, input->op, + &useHandle, &cpName, &cpNameSize, &flags, &events, + &dir, &caseFlags)) { + Bool watchTree = 0 != (flags & HGFS_NOTIFY_FLAG_WATCH_TREE); + if (useHandle) { + status = HgfsServerSetDirWatchByHandle(input, dir, events, watchTree, &watchId); + } else { + status = HgfsServerSetDirWatchByName(input, cpName, cpNameSize, caseFlags, + events, watchTree, &watchId); + } + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackSetWatchReply(input->packet, input->metaPacket, input->op, + watchId, &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + + HgfsServerCompleteRequest(status, replyPayloadSize, input); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsServerRemoveDirNotifyWatch -- + * + * Handle a remove directory notification watch request. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsServerRemoveDirNotifyWatch(HgfsInputParam *input) // IN: Input params +{ + HgfsSubscriberHandle watchId; + HgfsInternalStatus status; + size_t replyPayloadSize = 0; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackRemoveWatchRequest(input->payload, input->payloadSize, input->op, + &watchId)) { + if (HgfsNotify_RemoveSubscriber(watchId)) { + status = HGFS_ERROR_SUCCESS; + } else { + status = HGFS_ERROR_INTERNAL; + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackRemoveWatchReply(input->packet, input->metaPacket, input->op, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + + HgfsServerCompleteRequest(status, replyPayloadSize, input); +} + + /* *----------------------------------------------------------------------------- * @@ -6495,7 +7115,7 @@ HgfsGetDirEntry(HgfsHandle hgfsSearchHandle, * We need to unescape the name before sending it back to the client */ if (HGFS_ERROR_SUCCESS == status) { - *entryName = strdup(dent->d_name); + *entryName = Util_SafeStrdup(dent->d_name); if (unescapeName) { *nameLength = HgfsEscape_Undo(*entryName, length + 1); } else { @@ -6775,10 +7395,9 @@ HgfsBuildRelativePath(const char* source, // IN: source file name * Callback which is called by directory notification package when in response * to a event. * - * XXX: - * The function must build directory notification packet and send it to the - * client. At the moment it just logs a message, actual logic will be - * implemented later when required infrastructure is ready. + * The function builds directory notification packet and queues it to be sent + * to the client. It processes one notification at a time. It relies on transport + * to perform coalescing. * * Results: * None. @@ -6790,16 +7409,48 @@ HgfsBuildRelativePath(const char* source, // IN: source file name */ void -Hgfs_NotificationCallback(HgfsSharedFolderHandle sharedFolder, - HgfsSubscriberHandle subscriber, - char* name, - char* newName, - uint32 mask) +Hgfs_NotificationCallback(HgfsSharedFolderHandle sharedFolder, // IN: shared folder + HgfsSubscriberHandle subscriber, // IN: subsciber + char* fileName, // IN: name of the file + uint32 mask, // IN: event type + struct HgfsSessionInfo *session) // IN: session info { - LOG(4, ("%s: notification for folder: %d index: %d file name %s " - "(new name %s) mask %x\n", __FUNCTION__, sharedFolder, - (int)subscriber, name, (newName == NULL) ? "" : newName, - mask)); + HgfsPacket *packet; + size_t sizeNeeded; + char *shareName; + size_t shareNameLen; + HgfsHeader *packetHeader; + uint32 flags; + + if (HgfsServerGetShareName(sharedFolder, &shareNameLen, &shareName)) { + + sizeNeeded = HgfsPackCalculateNotificationSize(shareName, fileName); + + packetHeader = Util_SafeCalloc(1, sizeNeeded); + packet = Util_SafeCalloc(1, sizeof *packet); + packet->guestInitiated = FALSE; + packet->metaPacketSize = sizeNeeded; + packet->metaPacket = packetHeader; + packet->dataPacketIsAllocated = TRUE; + flags = 0; + if (mask & HGFS_NOTIFY_EVENTS_DROPPED) { + flags |= HGFS_NOTIFY_FLAG_OVERFLOW; + } + + HgfsPackChangeNotificationRequest(packetHeader, subscriber, shareName, fileName, mask, + flags, session, &sizeNeeded); + if (!HgfsPacketSend(packet, (char *)packetHeader, sizeNeeded, session, 0)) { + LOG(4, ("%s: failed to send notification to the host\n", __FUNCTION__)); + } + + LOG(4, ("%s: notification for folder: %d index: %d file name %s " + " mask %x\n", __FUNCTION__, sharedFolder, + (int)subscriber, fileName, mask)); + free(shareName); + } else { + LOG(4, ("%s: failed to find shared folder for a handle %x\n", + __FUNCTION__, sharedFolder)); + } } @@ -7013,7 +7664,7 @@ TestNodeFreeList(void) printf("\nadding node with name: %s\n", tempName); localId.volumeId = 0; localId.fileId = i + 1000; - node = HgfsAddNewFileNode(strdup(tempName), &localId); + node = HgfsAddNewFileNode(Util_SafeStrdup(tempName), &localId); array[i] = HgfsFileNode2Handle(node); } @@ -7045,7 +7696,7 @@ TestSearchFreeList(void) Str_Sprintf(tempName, sizeof tempName, "baseDir%u", i); printf("\nadding search with baseDir: \"%s\"\n", tempName); - search = HgfsAddNewSearch(strdup(tempName)); + search = HgfsAddNewSearch(Util_SafeStrdup(tempName)); array[i] = HgfsSearch2SearchHandle(search); } diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h index e67837390..abad31ef7 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h +++ b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h @@ -147,6 +147,12 @@ typedef struct HgfsShareInfo { /* Write permissions for the shared folder, needed for handle => name conversions. */ Bool writePermissions; + + /* + * Shared folder handle used by change directory notification code to identify + * shared folder. + */ + HgfsSharedFolderHandle handle; } HgfsShareInfo; /* @@ -278,7 +284,6 @@ typedef enum { HGFS_SESSION_STATE_CLOSED, } HgfsSessionInfoState; - typedef struct HgfsSessionInfo { /* Unique session id. */ uint64 sessionId; @@ -353,6 +358,8 @@ typedef struct HgfsSessionInfo { uint32 numberOfCapabilities; + Bool activeNotification; + } HgfsSessionInfo; @@ -795,6 +802,47 @@ HgfsPackDestorySessionReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet void HgfsServerGetDefaultCapabilities(HgfsCapability *capabilities, // OUT: uint32 *numberOfCapabilities); // OUT: +Bool +HgfsUnpackSetWatchRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: requested operation + Bool *useHandle, // OUT: handle or cpName + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + uint32 *flags, // OUT: flags for the new watch + uint32 *events, // OUT: event filter + HgfsHandle *dir, // OUT: direrctory handle + uint32 *caseFlags); // OUT: case-sensitivity flags +Bool +HgfsPackSetWatchReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: operation code + HgfsSubscriberHandle watchId, // IN: new watch id + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session info +Bool +HgfsUnpackRemoveWatchRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: packet size + HgfsOp op, // IN: operation code + HgfsSubscriberHandle *watchId); // OUT: watch Id +Bool +HgfsPackRemoveWatchReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: operation code + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session info +size_t +HgfsPackCalculateNotificationSize(char const *shareName, // IN: shared folder name + char *fileName); // IN: file name +Bool +HgfsPackChangeNotificationRequest(void *packet, // IN/OUT: Hgfs Packet + HgfsSubscriberHandle subscriber, // IN: watch + char const *shareName, // IN: share name + char *fileName, // IN: file name + uint32 mask, // IN: event mask + uint32 flags, // IN: notify flags + HgfsSessionInfo *session, // IN: session + size_t *bufferSize); // IN/OUT: packet size /* Node cache functions. */ Bool diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c b/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c index 3829055a5..78337a6c3 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c @@ -102,6 +102,9 @@ static HgfsCapability hgfsDefaultCapabilityTable[] = {HGFS_OP_DESTROY_SESSION_V4, HGFS_REQUEST_SUPPORTED}, {HGFS_OP_READ_FAST_V4, HGFS_REQUEST_NOT_SUPPORTED}, {HGFS_OP_WRITE_FAST_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_SET_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_REMOVE_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_NOTIFY_V4, HGFS_REQUEST_NOT_SUPPORTED}, {HGFS_OP_OPEN_V4, HGFS_REQUEST_NOT_SUPPORTED}, {HGFS_OP_DIRECTORY_READ_V4, HGFS_REQUEST_NOT_SUPPORTED}, {HGFS_OP_ENUMERATE_STREAMS_V4, HGFS_REQUEST_NOT_SUPPORTED}, @@ -119,9 +122,6 @@ static HgfsCapability hgfsDefaultCapabilityTable[] = {HGFS_OP_UNLOCK_BYTE_RANGE_V4, HGFS_REQUEST_NOT_SUPPORTED}, {HGFS_OP_QUERY_EAS_V4, HGFS_REQUEST_NOT_SUPPORTED}, {HGFS_OP_SET_EAS_V4, HGFS_REQUEST_NOT_SUPPORTED}, - {HGFS_OP_SET_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED}, - {HGFS_OP_REMOVE_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED}, - {HGFS_OP_NOTIFY_V4, HGFS_REQUEST_NOT_SUPPORTED}, }; /* @@ -4869,3 +4869,586 @@ HgfsServerGetDefaultCapabilities(HgfsCapability *capabilities, // OUT: capabili ASSERT(*numberOfCapabilities <= HGFS_OP_MAX); memcpy(capabilities, hgfsDefaultCapabilityTable, sizeof hgfsDefaultCapabilityTable); } + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSetWatchReplyV4 -- + * + * Pack hgfs set watch V4 reply payload to the HgfsReplySetWatchV4 structure. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsPackSetWatchReplyV4(HgfsSubscriberHandle watchId, // IN: host id of thee new watch + HgfsReplySetWatchV4 *reply) // OUT: reply buffer to fill +{ + reply->watchId = watchId; + reply->reserved = 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSetWatchReply -- + * + * Pack hgfs set watch reply to the HgfsReplySetWatchV4 structure. + * + * Results: + * TRUE if successfully allocated reply request, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackSetWatchReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: operation code + HgfsSubscriberHandle watchId, // IN: id of the new watch + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + HgfsReplySetWatchV4 *reply; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + if (HGFS_OP_SET_WATCH_V4 != op) { + NOT_REACHED(); + result = FALSE; + } else { + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + HgfsPackSetWatchReplyV4(watchId, reply); + *payloadSize = sizeof *reply; + } + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSetWatchPayloadV4 -- + * + * Unpack HGFS set directory notication watch payload version 4 and initializes + * a corresponding HgfsHandle or file name to tell us which directory to watch. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +HgfsUnpackSetWatchPayloadV4(HgfsRequestSetWatchV4 *requestV4, // IN: request payload + size_t payloadSize, // IN: payload size + Bool *useHandle, // OUT: handle or cpName + uint32 *flags, // OUT: watch flags + uint32 *events, // OUT: event filter + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsHandle *dir, // OUT: directory handle + uint32 *caseFlags) // OUT: case-sensitivity +{ + if (payloadSize < sizeof *requestV4) { + return FALSE; + } + + *flags = requestV4->flags; + *events = requestV4->events; + + return HgfsUnpackFileNameV3(&requestV4->fileName, + payloadSize - sizeof *requestV4, + useHandle, + cpName, + cpNameSize, + dir, + caseFlags); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSetWatchRequest -- + * + * Unpack hgfs set directory notication watch request and initialize a corresponding + * HgfsHandle or directory name to tell us which directory to monitor. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSetWatchRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: requested operation + Bool *useHandle, // OUT: handle or cpName + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + uint32 *flags, // OUT: flags for the new watch + uint32 *events, // OUT: event filter + HgfsHandle *dir, // OUT: direrctory handle + uint32 *caseFlags) // OUT: case-sensitivity flags +{ + HgfsRequestSetWatchV4 *requestV4 = (HgfsRequestSetWatchV4 *)packet; + Bool result; + + ASSERT(packet); + ASSERT(cpName); + ASSERT(cpNameSize); + ASSERT(dir); + ASSERT(flags); + ASSERT(events); + ASSERT(caseFlags); + ASSERT(useHandle); + + if (HGFS_OP_SET_WATCH_V4 != op) { + NOT_REACHED(); + result = FALSE; + } else { + result = HgfsUnpackSetWatchPayloadV4(requestV4, packetSize, useHandle, flags, + events, cpName, cpNameSize, dir, caseFlags); + } + + if (!result) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackRemoveWatchReply -- + * + * Pack hgfs remove watch reply to the HgfsReplyRemoveWatchV4 structure. + * + * Results: + * TRUE if successfully allocated reply request, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackRemoveWatchReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: operation code + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + HgfsReplyRemoveWatchV4 *reply; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + if (HGFS_OP_REMOVE_WATCH_V4 != op) { + NOT_REACHED(); + result = FALSE; + } else { + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->reserved = 0; + *payloadSize = sizeof *reply; + } + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackRemoveWatchPayload -- + * + * Unpack HGFS remove directory notication watch payload version 4. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +HgfsUnpackRemoveWatchPayloadV4(HgfsRequestRemoveWatchV4 *requestV4, // IN: request payload + size_t payloadSize, // IN: payload size + HgfsSubscriberHandle *watchId) // OUT: watch id +{ + if (payloadSize < sizeof *requestV4) { + return FALSE; + } + + *watchId = requestV4->watchId; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackRemoveWatchRequest -- + * + * Unpack hgfs remove directory notication watch request. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackRemoveWatchRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: requested operation + HgfsSubscriberHandle *watchId) // OUT: watch Id to remove +{ + HgfsRequestRemoveWatchV4 *requestV4 = (HgfsRequestRemoveWatchV4 *)packet; + + ASSERT(packet); + ASSERT(watchId); + + ASSERT(HGFS_OP_REMOVE_WATCH_V4 == op); + + if (HGFS_OP_REMOVE_WATCH_V4 != op) { + return FALSE; + } else if (!HgfsUnpackRemoveWatchPayloadV4(requestV4, packetSize, watchId)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackCalculateNotificationSize -- + * + * Calculates size needed for change notification packet. + * + * Results: + * TRUE if successfully allocated reply request, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +size_t +HgfsPackCalculateNotificationSize(char const *shareName, // IN: shared folder name + char *fileName) // IN: relative file path +{ + size_t result = sizeof(HgfsRequestNotifyV4); + + if (NULL != fileName) { + size_t shareNameLen = strlen(shareName); + result += strlen(fileName) + 1 + shareNameLen; + } + result += sizeof(HgfsHeader); + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBuildCPName -- + * + * Build crossplatform name out of share name and relative to the shared folder + * file path. + * + * Results: + * Length of the output crossplatform name. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static size_t +HgfsBuildCPName(char const *shareName, // IN: utf8 share name + char *fileName, // IN: utf8 file path + char **cpName) // OUT: full name in cp format +{ + size_t shareNameLen = strlen(shareName) + 1; + size_t fileNameLen = strlen(fileName) + 1; + char *fullName = Util_SafeMalloc(shareNameLen + fileNameLen); + size_t result; + + *cpName = Util_SafeMalloc(shareNameLen + fileNameLen); + Str_Strcpy(fullName, shareName, shareNameLen); + fullName[shareNameLen - 1] = DIRSEPC; + Str_Strcpy(fullName + shareNameLen, fileName, fileNameLen); + + result = CPName_ConvertTo(fullName, shareNameLen + fileNameLen, *cpName); + ASSERT(result > 0); // Unescaped name can't be longer then escaped thus it must fit. + free(fullName); + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackHgfsName -- + * + * Pack cpName into HgfsFileName structure. + * + * Results: + * TRUE if there is enough space in the buffer, + * FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +HgfsPackHgfsName(char *cpName, // IN: cpName to pack + size_t cpNameLen, // IN: length of the cpName + size_t availableSpace, // IN: space available for HgfsFileName + size_t *nameSize, // OUT: space consumed by HgfsFileName + HgfsFileName *fileName) // OUT: structure to pack cpName into +{ + if (availableSpace < offsetof(HgfsFileName, name) + cpNameLen) { + return FALSE; + } + fileName->length = cpNameLen; + memcpy(fileName->name, cpName, cpNameLen); + *nameSize = offsetof(HgfsFileName, name) + cpNameLen; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackChangeNotificationV4 -- + * + * Pack single change directory notification event information. + * + * Results: + * Length of the packed structure or 0 if the structure does not fit in the + * the buffer. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static size_t +HgfsPackChangeNotificationV4(uint32 mask, // IN: event mask + char const *shareName, // IN: share name + char *fileName, // IN: file name + size_t bufferSize, // IN: available space + HgfsNotifyEventV4 *reply) // OUT: notificaiton buffer +{ + size_t remainingSize; + size_t totalLength = sizeof *reply; + + if (sizeof *reply > bufferSize) { + return 0; + } + + reply->nextOffset = 0; + reply->mask = mask; + if (NULL != fileName) { + char *fullPath; + size_t nameSize; + + nameSize = HgfsBuildCPName(shareName, fileName, &fullPath); + remainingSize = bufferSize - offsetof(HgfsNotifyEventV4, fileName); + if (HgfsPackHgfsName(fullPath, nameSize, remainingSize, &nameSize, + &reply->fileName)) { + remainingSize -= nameSize; + totalLength += nameSize; + } else { + totalLength = 0; + } + free(fullPath); + } else { + reply->fileName.length = 0; + totalLength = sizeof *reply; + } + return totalLength; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackChangeNotifyRequestV4 -- + * + * Pack hgfs directory change notification request to be sent to the guest. + * + * Results: + * Length of the packed structure or 0 if the structure does not fit in the + * the buffer. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static size_t +HgfsPackChangeNotifyRequestV4(HgfsSubscriberHandle watchId, // IN: watch + uint32 flags, // IN: notify flags + uint32 mask, // IN: event mask + char const *shareName, // IN: share name + char *fileName, // IN: relative file path + size_t bufferSize, // IN: available space + HgfsRequestNotifyV4 *reply) // OUT: notification buffer +{ + size_t size; + size_t notificationOffset; + + if (bufferSize < sizeof *reply) { + return 0; + } + reply->watchId = watchId; + reply->flags = flags; + if ((flags & HGFS_NOTIFY_FLAG_OVERFLOW) == HGFS_NOTIFY_FLAG_OVERFLOW) { + size = sizeof *reply; + reply->count = 0; + reply->flags = HGFS_NOTIFY_FLAG_OVERFLOW; + } else { + /* + * For the moment server sends only one notification at a time and it relies + * on transport to coalesce requests. + * Later on we may consider supporting multiple notifications. + */ + reply->count = 1; + notificationOffset = offsetof(HgfsRequestNotifyV4, events); + size = HgfsPackChangeNotificationV4(mask, shareName, fileName, + bufferSize - notificationOffset, + reply->events); + if (size != 0) { + size += notificationOffset; + } else { + /* + * Set event flag to tell guest that some events were dropped + * when filling out notification details failed. + */ + size = sizeof *reply; + reply->count = 0; + reply->flags = HGFS_NOTIFY_FLAG_OVERFLOW; + } + } + return size; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackChangeNotificationRequest -- + * + * Pack hgfs directory change notification request to the + * HgfsRequestNotifyV4 structure. + * + * Results: + * TRUE if successfully allocated reply request, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackChangeNotificationRequest(void *packet, // IN/OUT: Hgfs Packet + HgfsSubscriberHandle subscriber, // IN: watch + char const *shareName, // IN: share name + char *fileName, // IN: relative name + uint32 mask, // IN: event mask + uint32 flags, // IN: notify flags + HgfsSessionInfo *session, // IN: session + size_t *bufferSize) // INOUT: size of packet +{ + size_t size; + HgfsRequestNotifyV4 *reply; + HgfsHeader *header = (HgfsHeader *)packet; + Bool result; + + ASSERT(packet); + ASSERT(shareName); + ASSERT(NULL != fileName || + (flags & HGFS_NOTIFY_FLAG_OVERFLOW) == HGFS_NOTIFY_FLAG_OVERFLOW); + ASSERT(session); + ASSERT(bufferSize); + + if (*bufferSize < sizeof *header) { + return FALSE; + } + + /* + * Initialize notification header. + * Set status and requestId to 0 since these fields are not relevant for + * notifications. + * Initialize payload size to 0 - it is not known yet and will be filled later. + */ + header->headerSize = sizeof *header; + + HgfsPackReplyHeaderV4(0, 0, HGFS_OP_NOTIFY_V4, session->sessionId, 0, header); + reply = (HgfsRequestNotifyV4 *)((char *)header + header->headerSize); + size = HgfsPackChangeNotifyRequestV4(subscriber, flags, mask, shareName, fileName, + *bufferSize - header->headerSize, reply); + if (0 != size) { + header->packetSize = header->headerSize + size; + result = TRUE; + } else { + result = FALSE; + } + + return result; +} diff --git a/open-vm-tools/lib/hgfsServerManagerGuest/hgfsServerManagerGuest.c b/open-vm-tools/lib/hgfsServerManagerGuest/hgfsServerManagerGuest.c index eafcebd20..1343211f5 100644 --- a/open-vm-tools/lib/hgfsServerManagerGuest/hgfsServerManagerGuest.c +++ b/open-vm-tools/lib/hgfsServerManagerGuest/hgfsServerManagerGuest.c @@ -95,7 +95,7 @@ HgfsServerManager_Register(HgfsServerMgrData *data) // IN: RpcIn channel * policy server never change, invalidating the need for an invalidate * function. */ - if (!HgfsServerPolicy_Init(NULL)) { + if (!HgfsServerPolicy_Init(NULL, NULL)) { return FALSE; } diff --git a/open-vm-tools/lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c b/open-vm-tools/lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c index 79d74e96c..4079ce115 100644 --- a/open-vm-tools/lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c +++ b/open-vm-tools/lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c @@ -125,7 +125,8 @@ HgfsServerPolicyDestroyShares(DblLnkLst_Links *head) // IN */ Bool -HgfsServerPolicy_Init(HgfsInvalidateObjectsFunc *invalidateObjects) // Ignored +HgfsServerPolicy_Init(HgfsInvalidateObjectsFunc *invalidateObjects, // Unused + HgfsRegisterSharedFolderFunc *registerFolder) // Unused { HgfsSharedFolder *rootShare; @@ -159,6 +160,7 @@ HgfsServerPolicy_Init(HgfsInvalidateObjectsFunc *invalidateObjects) // Ignored /* These are strictly optimizations to save work later */ rootShare->pathLen = strlen(rootShare->path); rootShare->nameLen = strlen(rootShare->name); + rootShare->handle = HGFS_INVALID_FOLDER_HANDLE; /* Add the root node to the end of the list */ DblLnkLst_LinkLast(&myState.shares, &rootShare->links); @@ -502,11 +504,12 @@ HgfsServerPolicy_GetSharePath(char const *nameIn, // IN: Name to check */ HgfsNameStatus -HgfsServerPolicy_ProcessCPName(char const *nameIn, // IN: name in CPName form - size_t nameInLen, // IN: length of the name - Bool *readAccess, // OUT: Read permissions - Bool *writeAccess, // OUT: Write permissions - char const **shareBaseDir) // OUT: Shared directory +HgfsServerPolicy_ProcessCPName(char const *nameIn, // IN: name in CPName form + size_t nameInLen, // IN: length of the name + Bool *readAccess, // OUT: Read permissions + Bool *writeAccess, // OUT: Write permissions + HgfsSharedFolderHandle *handle,// OUT: folder handle + char const **shareBaseDir) // OUT: Shared directory { HgfsSharedFolder *myShare; @@ -522,6 +525,7 @@ HgfsServerPolicy_ProcessCPName(char const *nameIn, // IN: name in CPName *readAccess = myShare->readAccess; *writeAccess = myShare->writeAccess; *shareBaseDir = myShare->path; + *handle = myShare->handle; return HGFS_NAME_STATUS_COMPLETE; } diff --git a/open-vm-tools/lib/include/hgfsProto.h b/open-vm-tools/lib/include/hgfsProto.h index 6e3554072..aa51418d4 100644 --- a/open-vm-tools/lib/include/hgfsProto.h +++ b/open-vm-tools/lib/include/hgfsProto.h @@ -1831,27 +1831,37 @@ HgfsRequestSetWatchV4; /* * Fine grain notification event types. * HgfsRequestSetWatch events. - * Events marked with (*) apply only to directories. */ -#define HGFS_NOTIFY_ACCESS (1 << 0) /* File accessed (read) */ -#define HGFS_NOTIFY_ATTRIB (1 << 1) /* Windows file attributes changed. */ -#define HGFS_NOTIFY_SIZE (1 << 2) /* File size changed. */ -#define HGFS_NOTIFY_ATIME (1 << 3) /* Access time changed. */ -#define HGFS_NOTIFY_MTIME (1 << 4) /* Modification time changed. */ -#define HGFS_NOTIFY_CTIME (1 << 5) /* Attribute change time changed. */ -#define HGFS_NOTIFY_CRTIME (1 << 6) /* Creation time changed. */ -#define HGFS_NOTIFY_NAME (1 << 7) /* File / Directory name changed. */ -#define HGFS_NOTIFY_OPEN (1 << 8) /* File opened */ -#define HGFS_NOTIFY_CLOSE_WRITE (1 << 9) /* File opened for writing closed */ -#define HGFS_NOTIFY_CLOSE_NOWRITE (1 << 10) /* File opened for reading closed */ -#define HGFS_NOTIFY_CREATE (1 << 11) /* File / directory created */ -#define HGFS_NOTIFY_DELETE (1 << 12) /* (*) File / directory deleted */ -#define HGFS_NOTIFY_DELETE_SELF (1 << 13) /* Watched file / directory deleted */ -#define HGFS_NOTIFY_MODIFY (1 << 14) /* File or directory modified. */ -#define HGFS_NOTIFY_MOVE_SELF (1 << 15) /* Watched file / directory moved. */ -#define HGFS_NOTIFY_MOVE_FROM (1 << 16) /* (*) Moved out of watched directory. */ -#define HGFS_NOTIFY_MOVE_TO (1 << 17) /* (*) Moved into watched directory. */ -#define HGFS_NOTIFY_RENAME (1 << 18) /* File / directory name changed. */ +#define HGFS_NOTIFY_ACCESS (1 << 0) /* File accessed (read) */ +#define HGFS_NOTIFY_ATTRIB (1 << 1) /* File attributes changed. */ +#define HGFS_NOTIFY_SIZE (1 << 2) /* File size changed. */ +#define HGFS_NOTIFY_ATIME (1 << 3) /* Access time changed. */ +#define HGFS_NOTIFY_MTIME (1 << 4) /* Modification time changed. */ +#define HGFS_NOTIFY_CTIME (1 << 5) /* Attribute time changed. */ +#define HGFS_NOTIFY_CRTIME (1 << 6) /* Creation time changed. */ +#define HGFS_NOTIFY_NAME (1 << 7) /* File / Directory name. */ +#define HGFS_NOTIFY_OPEN (1 << 8) /* File opened */ +#define HGFS_NOTIFY_CLOSE_WRITE (1 << 9) /* Modified file closed. */ +#define HGFS_NOTIFY_CLOSE_NOWRITE (1 << 10) /* Non-modified file closed. */ +#define HGFS_NOTIFY_CREATE_FILE (1 << 11) /* File created */ +#define HGFS_NOTIFY_CREATE_DIR (1 << 12) /* Directory created */ +#define HGFS_NOTIFY_DELETE_FILE (1 << 13) /* File deleted */ +#define HGFS_NOTIFY_DELETE_DIR (1 << 14) /* Directory deleted */ +#define HGFS_NOTIFY_DELETE_SELF (1 << 15) /* Watched directory deleted */ +#define HGFS_NOTIFY_MODIFY (1 << 16) /* File modified. */ +#define HGFS_NOTIFY_MOVE_SELF (1 << 17) /* Watched directory moved. */ +#define HGFS_NOTIFY_OLD_FILE_NAME (1 << 18) /* Rename: old file name. */ +#define HGFS_NOTIFY_NEW_FILE_NAME (1 << 19) /* Rename: new file name. */ +#define HGFS_NOTIFY_OLD_DIR_NAME (1 << 20) /* Rename: old dir name. */ +#define HGFS_NOTIFY_NEW_DIR_NAME (1 << 21) /* Rename: new dir name. */ +#define HGFS_NOTIFY_CHANGE_EA (1 << 22) /* Extended attributes. */ +#define HGFS_NOTIFY_CHANGE_SECURITY (1 << 23) /* Security/permissions. */ +#define HGFS_NOTIFY_ADD_STREAM (1 << 24) /* Named stream created. */ +#define HGFS_NOTIFY_DELETE_STREAM (1 << 25) /* Named stream deleted. */ +#define HGFS_NOTIFY_CHANGE_STREAM_SIZE (1 << 26) /* Named stream size changed. */ +#define HGFS_NOTIFY_CHANGE_STREAM_LAST_WRITE (1 << 27) /* Stream timestamp changed. */ +#define HGFS_NOTIFY_WATCH_DELETED (1 << 28) /* Dir with watch deleted. */ +#define HGFS_NOTIFY_EVENTS_DROPPED (1 << 29) /* Notifications dropped. */ /* HgfsRequestSetWatch flags. */ #define HGFS_NOTIFY_FLAG_WATCH_TREE (1 << 0) /* Watch the entire directory tree. */ @@ -1864,11 +1874,14 @@ HgfsRequestSetWatchV4; * set. */ +typedef uint64 HgfsSubscriberHandle; +#define HGFS_INVALID_SUBSCRIBER_HANDLE ((HgfsSubscriberHandle)~((HgfsSubscriberHandle)0)) + typedef #include "vmware_pack_begin.h" struct HgfsReplySetWatchV4 { - HgfsHandle watchId; /* Watch identifier for subsequent references. */ - uint64 reserved; /* Reserved for future use. */ + HgfsSubscriberHandle watchId; /* Watch identifier for subsequent references. */ + uint64 reserved; /* Reserved for future use. */ } #include "vmware_pack_end.h" HgfsReplySetWatchV4; @@ -1876,7 +1889,7 @@ HgfsReplySetWatchV4; typedef #include "vmware_pack_begin.h" struct HgfsRequestRemoveWatchV4 { - HgfsHandle watchId; /* Watch identifier to remove. */ + HgfsSubscriberHandle watchId; /* Watch identifier to remove. */ } #include "vmware_pack_end.h" HgfsRequestRemoveWatchV4; @@ -1896,7 +1909,6 @@ struct HgfsNotifyEventV4 { uint64 mask; /* Event occurred. */ uint64 reserved; /* Reserved for future use. */ HgfsFileName fileName; /* Filename. */ - HgfsFileName oldName; /* New filename for rename operation. */ } #include "vmware_pack_end.h" HgfsNotifyEventV4; @@ -1911,7 +1923,7 @@ HgfsNotifyEventV4; typedef #include "vmware_pack_begin.h" struct HgfsRequestNotifyV4 { - HgfsHandle watchId; /* Watch identifier. */ + HgfsSubscriberHandle watchId; /* Watch identifier. */ uint32 flags; /* Various flags. */ uint32 count; /* Number of events occured. */ uint64 reserved; /* Reserved for future use. */ @@ -1960,21 +1972,21 @@ HgfsReplyQueryEAV4; typedef #include "vmware_pack_begin.h" -struct HgfsEA { +struct HgfsEAV4 { uint32 nextOffset; /* Offset of the next structure in the chain. */ uint32 valueLength; /* EA value length. */ char data[1]; /* NULL terminated EA name followed by EA value. */ } #include "vmware_pack_end.h" -HgfsEA; +HgfsEAV4; typedef #include "vmware_pack_begin.h" struct HgfsRequestSetEAV4 { - uint32 flags; /* Flags, see below. */ - uint64 reserved; /* Reserved for future use. */ - uint32 numEAs; /* Number of EAs in this request. */ - HgfsEA attributes[1]; /* Array of attributes. */ + uint32 flags; /* Flags, see below. */ + uint64 reserved; /* Reserved for future use. */ + uint32 numEAs; /* Number of EAs in this request. */ + HgfsEAV4 attributes[1]; /* Array of attributes. */ } #include "vmware_pack_end.h" HgfsRequestSetEAV4; diff --git a/open-vm-tools/lib/include/hgfsServer.h b/open-vm-tools/lib/include/hgfsServer.h index e3731e9fc..72ff9495a 100644 --- a/open-vm-tools/lib/include/hgfsServer.h +++ b/open-vm-tools/lib/include/hgfsServer.h @@ -173,4 +173,19 @@ HgfsCleanupFunc(void *); // IN typedef void HgfsInvalidateObjectsFunc(DblLnkLst_Links *shares); // IN +/* + * Function used to notify HGFS server that a shared folder has been created or updated. + * It allows HGFS server to maintain up-to-date list of shared folders and its + * properties. + */ +typedef uint32 HgfsSharedFolderHandle; +#define HGFS_INVALID_FOLDER_HANDLE ((HgfsSharedFolderHandle)~((HgfsSharedFolderHandle)0)) + +typedef HgfsSharedFolderHandle +HgfsRegisterSharedFolderFunc(const char *shareName, + const char *sharePath, + Bool addFolder); +HgfsSharedFolderHandle HgfsServer_RegisterSharedFolder(const char *shareName, + const char *sharePath, + Bool addFolder); #endif // _HGFS_SERVER_H_ diff --git a/open-vm-tools/lib/include/hgfsServerPolicy.h b/open-vm-tools/lib/include/hgfsServerPolicy.h index 4339a795c..060f65d1e 100644 --- a/open-vm-tools/lib/include/hgfsServerPolicy.h +++ b/open-vm-tools/lib/include/hgfsServerPolicy.h @@ -32,7 +32,7 @@ #define HGFS_SERVER_POLICY_ROOT_SHARE_NAME "root" Bool -HgfsServerPolicy_Init(HgfsInvalidateObjectsFunc *invalidateObjects); +HgfsServerPolicy_Init(HgfsInvalidateObjectsFunc *invalidateObjects, HgfsRegisterSharedFolderFunc *registerFolder); Bool HgfsServerPolicy_Cleanup(void); @@ -46,11 +46,11 @@ typedef uint32 HgfsShareOptions; */ typedef struct HgfsSharedFolder { DblLnkLst_Links links; - char *name; /* Name of share */ - char *path; /* - * Path of share in server's filesystem. Should - * not include final path separator. - */ + char *name; /* Name of share */ + char *path; /* + * Path of share in server's filesystem. Should + * not include final path separator. + */ char *shareTags; /* Tags associated with this share (comma delimited). */ size_t shareTagsLen; /* Length of shareTag string */ size_t nameLen; /* Length of name string */ @@ -58,6 +58,15 @@ typedef struct HgfsSharedFolder { Bool readAccess; /* Read permission for this share */ Bool writeAccess; /* Write permission for this share */ HgfsShareOptions configOptions; /* User-config options. */ + HgfsSharedFolderHandle handle; /* Handle assigned by HGFS server + * when the folder was registered with it. + * Policy package keeps the context and returns + * it along with other shared folder properties. + * Keeping it here ensures consistent lookup all + * properties of the shared folder which takes into + * account such details like case sensitive/case + * insensitive name lookup. + */ } HgfsSharedFolder; /* Per share user configurable options. */ @@ -93,11 +102,12 @@ Bool HgfsServerPolicy_IsShareOptionSet(HgfsShareOptions shareOptions, // IN: Config options uint32 option); // IN: Option to check HgfsNameStatus -HgfsServerPolicy_ProcessCPName(char const *nameIn, // IN: name in CPName form - size_t nameInLen, // IN: length of the name - Bool *readAccess, // OUT: Read permissions - Bool *writeAccess, // OUT: Write permissions - char const **shareBaseDir); // OUT: Shared directory +HgfsServerPolicy_ProcessCPName(char const *nameIn, // IN: name in CPName form + size_t nameInLen, // IN: length of the name + Bool *readAccess, // OUT: Read permissions + Bool *writeAccess, // OUT: Write permissions + HgfsSharedFolderHandle *handle,// OUT: folder handle + char const **shareBaseDir); // OUT: Shared directory void HgfsServerPolicy_FreeShareList(HgfsServerPolicy_ShareList *shareList); // IN: list to free diff --git a/open-vm-tools/lib/include/mutexRankLib.h b/open-vm-tools/lib/include/mutexRankLib.h index b0b8bf2e6..90b895695 100644 --- a/open-vm-tools/lib/include/mutexRankLib.h +++ b/open-vm-tools/lib/include/mutexRankLib.h @@ -50,6 +50,7 @@ /* * hgfs locks */ +#define RANK_hgfsSharedFolders (RANK_libLockBase + 0x4003) #define RANK_hgfsNotifyLock (RANK_libLockBase + 0x4005) #define RANK_hgfsFileIOLock (RANK_libLockBase + 0x4010) #define RANK_hgfsSearchArrayLock (RANK_libLockBase + 0x4020)