From: Oliver Kurth Date: Tue, 19 Jun 2018 18:07:45 +0000 (-0700) Subject: [Wayland Copy & Paste] Part1: Add notification mechanism to the VMBlock module X-Git-Tag: stable-10.3.0~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c04b9987786901f544c4df04f7701b48ac8f6475;p=thirdparty%2Fopen-vm-tools.git [Wayland Copy & Paste] Part1: Add notification mechanism to the VMBlock module This patch is part of the new feature for Workstation 15: [P0] Wayland support in Linux guest - Copy & Paste. In the implementation of current VMTools, there is a restriction, in the first second after grab into the guest, the CopyPasteUIX11::LocalGetFileRequestCB, which is a callback from a file paste request from another guest application and begins copying the files from host to guest and return the file list, will return none directly. The Wayland file explorer will request the content in the clipboard after the user clicks on the file explorer, and the request will get nothing since the restriction in the VMTools, so the following ‘Paste’ operation will fail. Solution: This solution is only used for the Linux guest with Wayland. The behavior for Linux guest with X11 will not change. The restriction, the CopyPasteUIX11::LocalGetFileRequestCB returns directly in the first second after grab into the guest, will be removed. And, the notification mechanism will be added into the VM block file system. This mechanism is similar with the inotify module. Take the VMBlock Fuse File System as an example, currently, the mount point contains below contents: * /blockdir/ * /dev /blockdir/ contains the symlinks to the contents of the target directory. /dev is the control file for VMBlock Fuse File System. In this new solution, a new folder /notifydir/ will be added, each file in this folder is a special file, read from this file will be blocked until any other process read from the corresponding file in target directory or the block on the target directory is removed. This patch only focus on the notification mechanism. --- diff --git a/open-vm-tools/lib/include/vmblock.h b/open-vm-tools/lib/include/vmblock.h index 78858da0b..6a03e40ca 100644 --- a/open-vm-tools/lib/include/vmblock.h +++ b/open-vm-tools/lib/include/vmblock.h @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2006-2017 VMware, Inc. All rights reserved. + * Copyright (C) 2006-2018 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 @@ -120,6 +120,8 @@ extern "C" { #define VMBLOCK_FUSE_FS_ROOT VMBLOCK_FUSE_MOUNT_POINT "/" VMBLOCK_FUSE_CTRL_MNTPNT #define VMBLOCK_FUSE_DEVICE_NAME "dev" #define VMBLOCK_FUSE_DEVICE VMBLOCK_FUSE_MOUNT_POINT "/" VMBLOCK_FUSE_DEVICE_NAME +#define VMBLOCK_FUSE_NOTIFY_MNTPNT "notifydir" +#define VMBLOCK_FUSE_NOTIFY_ROOT VMBLOCK_FUSE_MOUNT_POINT "/" VMBLOCK_FUSE_NOTIFY_MNTPNT #define VMBLOCK_FUSE_DEVICE_MODE O_RDWR /* Commands for the control half of vmblock driver */ diff --git a/open-vm-tools/modules/shared/vmblock/block.c b/open-vm-tools/modules/shared/vmblock/block.c index 55db11006..f0604f2f3 100644 --- a/open-vm-tools/modules/shared/vmblock/block.c +++ b/open-vm-tools/modules/shared/vmblock/block.c @@ -80,6 +80,7 @@ typedef struct BlockInfo { os_atomic_t refcount; os_blocker_id_t blocker; os_completion_t completion; + os_completion_t notification; char filename[OS_PATH_MAX]; } BlockInfo; @@ -193,6 +194,7 @@ AllocBlock(os_kmem_cache_t *cache, // IN: cache to allocate from DblLnkLst_Init(&block->links); os_atomic_set(&block->refcount, 1); os_completion_init(&block->completion); + os_completion_init(&block->notification); block->blocker = blocker; return block; @@ -229,6 +231,7 @@ FreeBlock(os_kmem_cache_t *cache, // IN: cache block was allocated from } os_completion_destroy(&block->completion); + os_completion_destroy(&block->notification); os_kmem_cache_free(cache, block); } @@ -365,6 +368,7 @@ BlockDoRemoveBlock(BlockInfo *block) LOG(4, "Completing block on [%s] (%d waiters)\n", block->filename, os_atomic_read(&block->refcount) - 1); os_complete_all(&block->completion); + os_complete_all(&block->notification); /* Now drop our reference */ BlockDropReference(block); @@ -517,6 +521,47 @@ BlockRemoveAllBlocks(const os_blocker_id_t blocker) // IN: blocker to remove bl } +/* + *---------------------------------------------------------------------------- + * + * BlockWaitFileBlock -- + * + * The caller will be blocked until any other thread accesses the file + * specified by the filename or the block on the file is removed. + * + * Results: + * Zero on success, error code on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +int +BlockWaitFileBlock(const char *filename, // IN: block to wait + const os_blocker_id_t blocker) // IN: blocker +{ + BlockInfo *block; + int retval = 0; + + ASSERT(filename); + + os_write_lock(&blockedFilesLock); + block = GetBlock(filename, blocker); + os_write_unlock(&blockedFilesLock); + + if (!block) { + retval = OS_ENOENT; + return retval; + } + + os_wait_for_completion(&block->notification); + + return retval; +} + + /* *---------------------------------------------------------------------------- * @@ -572,6 +617,9 @@ BlockWaitOnFile(const char *filename, // IN: file to block on block = cookie; } + // Call the callback + os_complete_all(&block->notification); + LOG(4, "(%"OS_FMTTID") Waiting for completion on [%s]\n", os_threadid, filename); error = os_wait_for_completion(&block->completion); LOG(4, "(%"OS_FMTTID") Wokeup from block on [%s]\n", os_threadid, filename); diff --git a/open-vm-tools/modules/shared/vmblock/block.h b/open-vm-tools/modules/shared/vmblock/block.h index 29e211830..951acbabd 100644 --- a/open-vm-tools/modules/shared/vmblock/block.h +++ b/open-vm-tools/modules/shared/vmblock/block.h @@ -77,6 +77,7 @@ void BlockCleanup(void); int BlockAddFileBlock(const char *filename, const os_blocker_id_t blocker); int BlockRemoveFileBlock(const char *filename, const os_blocker_id_t blocker); unsigned int BlockRemoveAllBlocks(const os_blocker_id_t blocker); +int BlockWaitFileBlock(const char *filename, const os_blocker_id_t blocker); int BlockWaitOnFile(const char *filename, BlockHandle cookie); BlockHandle BlockLookup(const char *filename, const os_blocker_id_t blocker); #ifdef VMX86_DEVEL diff --git a/open-vm-tools/vmblock-fuse/fsops.c b/open-vm-tools/vmblock-fuse/fsops.c index 6e10b0b47..77d87c2a4 100644 --- a/open-vm-tools/vmblock-fuse/fsops.c +++ b/open-vm-tools/vmblock-fuse/fsops.c @@ -56,11 +56,14 @@ static vmblockSpecialDirEntry specialDirEntries[] = { { "/", S_IFDIR | 0555, 3, DIR_SIZE }, { CONTROL_FILE, S_IFREG | 0600, 1, 0 }, { REDIRECT_DIR, S_IFDIR | 0555, 3, DIR_SIZE }, + { NOTIFY_DIR, S_IFDIR | 0555, 3, DIR_SIZE }, { NULL, 0, 0, 0 } }; static vmblockSpecialDirEntry symlinkDirEntry = { REDIRECT_DIR "/*", S_IFLNK | 0777, 1, -1 }; +static vmblockSpecialDirEntry notifyDirEntry = + { NOTIFY_DIR "/*", S_IFREG | 0444, 1, 0 }; /* *----------------------------------------------------------------------------- @@ -282,6 +285,14 @@ VMBlockGetAttr(const char *path, // IN: File to get attributes of. SetTimesToNow(statBuf); return 0; } + if (strncmp(path, NOTIFY_DIR, strlen(NOTIFY_DIR)) == 0) { + memset(statBuf, 0, sizeof *statBuf); + statBuf->st_mode = notifyDirEntry.mode; + statBuf->st_nlink = notifyDirEntry.nlink; + statBuf->st_size = notifyDirEntry.size; + SetTimesToNow(statBuf); + return 0; + } return -ENOENT; } @@ -311,7 +322,8 @@ VMBlockGetAttr(const char *path, // IN: File to get attributes of. */ int -ExternalReadDir(const char *path, // IN: Full (real) path to +ExternalReadDir(const char *blockPath, // IN: + const char *realPath, // IN: Full (real) path to // directory to read. void *buf, // OUT: Destination for // directory listing. @@ -327,8 +339,8 @@ ExternalReadDir(const char *path, // IN: Full (real) path to struct stat statBuf; DIR *dir = NULL; - LOG(4, "%s: path: %s\n", __func__, path); - dir = opendir(path); + LOG(4, "%s: blockPath: %s, realPath: %s\n", __func__, blockPath, realPath); + dir = opendir(realPath); if (dir == NULL) { return -errno; } @@ -340,17 +352,22 @@ ExternalReadDir(const char *path, // IN: Full (real) path to */ memset(&statBuf, 0, sizeof statBuf); - statBuf.st_mode = S_IFLNK; + if (strncmp(blockPath, NOTIFY_DIR, strlen(NOTIFY_DIR)) == 0) { + statBuf.st_mode = S_IFREG; + } else { + statBuf.st_mode = S_IFLNK; + } /* Clear errno because readdir won't change it if it succeeds. */ - errno = 0; + while ((dentry = readdir(dir)) != NULL) { status = filler(buf, dentry->d_name, &statBuf, 0); if (status == 1) { break; } } + if (errno != 0) { return -errno; } @@ -419,10 +436,12 @@ VMBlockReadDir(const char *path, // IN: Directory to read. (void)(filler(buf, ".", &dirStat, 0) || filler(buf, "..", &dirStat, 0) || filler(buf, VMBLOCK_DEVICE_NAME, &fileStat, 0) || - filler(buf, REDIRECT_DIR_NAME, &dirStat, 0)); + filler(buf, REDIRECT_DIR_NAME, &dirStat, 0) || + filler(buf, NOTIFY_DIR_NAME, &dirStat, 0)); return 0; - } else if (strcmp(path, REDIRECT_DIR) == 0) { - return ExternalReadDir(TARGET_DIR, buf, filler, offset, fileInfo); + } else if ( (strcmp(path, REDIRECT_DIR) == 0) + || (strcmp(path, NOTIFY_DIR) == 0)) { + return ExternalReadDir(path, TARGET_DIR, buf, filler, offset, fileInfo); } else { return -ENOENT; } @@ -472,7 +491,8 @@ VMBlockOpen(const char *path, // IN char *uniqueValue = NULL; - if (strcmp(path, CONTROL_FILE) != 0) { + if ( (strcmp(path, CONTROL_FILE) != 0) + && (strncmp(path, NOTIFY_DIR, strlen(NOTIFY_DIR)) != 0)) { return -ENOENT; } @@ -655,18 +675,38 @@ VMBlockRead(const char *path, // IN: Must be control file. off_t offset, // IN: Ignored. struct fuse_file_info *fileInfo) // IN: Ignored. { + char target[PATH_MAX+1]; + char targetLink[PATH_MAX+1]; + const char redirectPrefix[] = REDIRECT_DIR "/"; + const char redirectPrefixLength = sizeof redirectPrefix - 1; + const char notifyPrefix[] = NOTIFY_DIR "/"; + const char notifyPrefixLength = sizeof notifyPrefix - 1; + const char *relativePath = path + notifyPrefixLength; + LOG(4, "%s: path: %s, size: %"FMTSZ"u\n", __func__, path, size); LOG(4, "%s: fileInfo->fh: %p\n", __func__, FuseFileHandleToCharPointer(fileInfo->fh)); - ASSERT(strcmp(path, CONTROL_FILE) == 0); - if (size < sizeof VMBLOCK_FUSE_READ_RESPONSE) { - return -EINVAL; + if (strcmp(path, CONTROL_FILE) == 0) { + if (size < sizeof VMBLOCK_FUSE_READ_RESPONSE) { + return -EINVAL; + } + memcpy(buf, VMBLOCK_FUSE_READ_RESPONSE, sizeof VMBLOCK_FUSE_READ_RESPONSE); + return sizeof VMBLOCK_FUSE_READ_RESPONSE; } - memcpy(buf, VMBLOCK_FUSE_READ_RESPONSE, sizeof VMBLOCK_FUSE_READ_RESPONSE); + if (strncmp(path, NOTIFY_DIR, strlen(NOTIFY_DIR)) == 0) { + strlcpy(target, redirectPrefix, sizeof target); + strlcpy(target + redirectPrefixLength, + relativePath, + sizeof target - redirectPrefixLength); + if (RealReadLink(target, targetLink, sizeof targetLink) < 0) { + return -EINVAL; + } + return BlockWaitFileBlock(targetLink, OS_UNKNOWN_BLOCKER); + } - return sizeof VMBLOCK_FUSE_READ_RESPONSE; + return -EINVAL; } /* @@ -695,9 +735,11 @@ VMBlockRelease(const char *path, // IN: Must be control file. ASSERT(path); ASSERT(fileInfo); - ASSERT(strcmp(path, CONTROL_FILE) == 0); - ASSERT(blockerId != NULL); - BlockRemoveAllBlocks(blockerId); + + if (strcmp(path, CONTROL_FILE) == 0) { + ASSERT(blockerId != NULL); + BlockRemoveAllBlocks(blockerId); + } free(blockerId); blockerId = NULL; fileInfo->fh = CharPointerToFuseFileHandle(NULL); diff --git a/open-vm-tools/vmblock-fuse/fsops.h b/open-vm-tools/vmblock-fuse/fsops.h index 73b4675ad..2f16c8bcd 100644 --- a/open-vm-tools/vmblock-fuse/fsops.h +++ b/open-vm-tools/vmblock-fuse/fsops.h @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2008-2016 VMware, Inc. All rights reserved. + * Copyright (C) 2008-2018 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 @@ -47,6 +47,8 @@ #define REDIRECT_DIR "/" VMBLOCK_CONTROL_MOUNTPOINT #define TARGET_DIR "/tmp/VMwareDnD" #define CONTROL_FILE "/" VMBLOCK_DEVICE_NAME +#define NOTIFY_DIR_NAME VMBLOCK_FUSE_NOTIFY_MNTPNT +#define NOTIFY_DIR "/" NOTIFY_DIR_NAME /* * FS operation functions @@ -69,7 +71,8 @@ extern struct fuse_operations vmblockOperations; int RealReadLink(const char *path, char *buf, size_t bufSize); void SetTimesToNow(struct stat *statBuf); -int ExternalReadDir(const char *path, void *buf, fuse_fill_dir_t filler, +int ExternalReadDir(const char *blockPath, const char *realPath, + void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fileInfo); size_t StripExtraPathSeparators(char *path);