]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
[Wayland Copy & Paste] Part1: Add notification mechanism to the VMBlock module
authorOliver Kurth <okurth@vmware.com>
Tue, 5 Jun 2018 22:45:05 +0000 (15:45 -0700)
committerOliver Kurth <okurth@vmware.com>
Tue, 5 Jun 2018 22:45:05 +0000 (15:45 -0700)
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.

open-vm-tools/lib/include/vmblock.h
open-vm-tools/modules/shared/vmblock/block.c
open-vm-tools/modules/shared/vmblock/block.h
open-vm-tools/vmblock-fuse/fsops.c
open-vm-tools/vmblock-fuse/fsops.h

index 78858da0b0077efdad7558b77be0422b606c6160..6a03e40ca4714e8fa92acf6a94beb2f5fff3ff9a 100644 (file)
@@ -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 */
index 55db110066d7887d002ad5eab42ab8db7a0c044d..f0604f2f35da59fddd41f51eff2c817f9f630cf6 100644 (file)
@@ -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);
index 29e21183089d5e3edb30820e0a8b614fc05e1282..951acbabdaad687ce7909c2cdc4b27279dad5812 100644 (file)
@@ -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
index 6e10b0b475cc5f85e6c413dd3e57b3dc2691b761..77d87c2a493c0b46cd7e50c6c4c52e6840865206 100644 (file)
@@ -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);
index 73b4675ad8a873bc89044c238a105f52d14209cb..2f16c8bcd0102423577dfefad729c9f10cb4f979 100644 (file)
@@ -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);