1
/*********************************************************
2
* Copyright (C) 2005 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
22
* Some common dnd functions for UNIX guests and hosts.
38
#include "vm_assert.h"
42
#if defined(linux) || defined(sun) || defined(__FreeBSD__)
43
#include "vmblock_user.h"
47
#define LOGLEVEL_MODULE dnd
48
#include "loglevel_user.h"
49
#include "unicodeOperations.h"
52
#define DND_ROOTDIR_PERMS (S_IRWXU | S_IRWXG | S_IRWXO)
53
#define DND_STAGINGDIR_PERMS (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
55
#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO)
60
*-----------------------------------------------------------------------------
64
* Gets the root path of the staging directory for DnD file transfers.
67
* The path to the staging directory.
72
*-----------------------------------------------------------------------------
78
return "/tmp/VMwareDnD/";
83
*-----------------------------------------------------------------------------
85
* DnD_PrependFileRoot --
87
* Given a buffer of '\0' delimited filenames, this prepends the file root
88
* to each one and uses '\0' delimiting for the output buffer. The buffer
89
* pointed to by *src will be freed and *src will point to a new buffer
90
* containing the results. *srcSize is set to the size of the new buffer,
91
* not including the NUL-terminator.
94
* TRUE on success, FALSE on failure.
97
* *src will be freed, and a new buffer will be allocated. This buffer must
98
* be freed by the caller.
100
*-----------------------------------------------------------------------------
104
DnD_PrependFileRoot(const char *fileRoot, // IN : file root to append
105
char **src, // IN/OUT: NUL-delimited list of paths
106
size_t *srcSize) // IN/OUT: size of list
113
return DnDPrependFileRoot(fileRoot, '\0', src, srcSize);
118
*----------------------------------------------------------------------------
120
* DnDUriListGetFile --
122
* Retrieves the filename and length from the file scheme (file://) entry at
123
* the specified index in a text/uri-list string.
126
* The address of the beginning of the next filename on success, NULL if
127
* there are no more entries or on error. index is updated with the
128
* location of the next entry in the list, and length is updated with the
129
* size of the filename starting at the returned value.
134
*----------------------------------------------------------------------------
138
DnDUriListGetFile(char const *uriList, // IN : text/uri-list string
139
size_t *index, // IN/OUT: current index
140
size_t *length) // OUT : length of returned string
142
char const *nameStart;
150
/* The common case on the last entry */
151
if (uriList[*index] == '\0') {
156
* Ensure the URI list is formatted properly. This is ugly, but we have to
157
* special case for KDE (which doesn't follow the standard).
159
* XXX Note that this assumes we only support dropping files, based on the
160
* definition of the macro that is used.
163
nameStart = &uriList[*index];
165
if (strncmp(nameStart,
167
sizeof DND_URI_LIST_PRE - 1) == 0) {
168
nameStart += sizeof DND_URI_LIST_PRE - 1;
169
} else if (strncmp(nameStart,
170
DND_URI_LIST_PRE_KDE,
171
sizeof DND_URI_LIST_PRE_KDE - 1) == 0) {
172
nameStart += sizeof DND_URI_LIST_PRE_KDE - 1;
174
} else if (DnD_UriIsNonFileSchemes(nameStart)) {
178
Warning("%s: the URI list did not begin with %s or %s\n", __func__,
179
DND_URI_LIST_PRE, DND_URI_LIST_PRE_KDE);
186
/* Walk the filename looking for the end */
188
while (*curr != '\0' && *curr != '\r' && *curr != '\n') {
194
/* Bump curr based on trailing newline characters found */
195
while (*curr == '\r' || *curr == '\n') {
199
*index = curr - uriList;
200
*length = nameEnd - nameStart + 1;
202
return (char *)nameStart;
207
*----------------------------------------------------------------------------
209
* DnD_UriListGetNextFile --
211
* Retrieves and unescapes the next file from the provided test/uri-list
212
* mime type string. The index provided is used to iteratively retrieve
213
* successive files from the list.
216
* An allocated, unescaped, NUL-terminated string containing the filename
217
* within the uri-list after the specified index. index is updated to point
218
* to the next entry of the uri-list, and length (if provided) is set to the
219
* length of the allocated string (not including the NUL terminator).
220
* NULL if there are no more entries in the list, or on error.
225
*----------------------------------------------------------------------------
229
DnD_UriListGetNextFile(char const *uriList, // IN : text/uri-list string
230
size_t *index, // IN/OUT: current index
231
size_t *length) // OUT : length of returned string
235
size_t fileLength = 0;
237
size_t unescapedLength;
244
/* Get pointer to and length of next filename */
245
file = DnDUriListGetFile(uriList, &nextIndex, &fileLength);
251
* Retrieve an allocated, unescaped name. This undoes the ' ' -> "%20"
252
* escaping as required by RFC 1630 for entries in a uri-list.
255
unescapedName = Escape_Undo('%', file, fileLength, &unescapedLength);
256
if (!unescapedName) {
257
Warning("%s: error unescaping filename\n", __func__);
264
*length = unescapedLength;
267
return unescapedName;
272
*----------------------------------------------------------------------------
274
* DnD_UriIsNonFileSchemes --
276
* Check if the uri contains supported non-file scheme.
279
* TRUE if the uri contains supported non-file scheme. FALSE otherwise.
284
*----------------------------------------------------------------------------
288
DnD_UriIsNonFileSchemes(const char *uri)
290
const char *schemes[] = DND_URI_NON_FILE_SCHEMES;
293
while (schemes[i] != NULL) {
296
strlen(schemes[i])) == 0) {
305
/* We need to make this suck less. */
306
#if defined(linux) || defined(sun) || defined(__FreeBSD__)
309
*----------------------------------------------------------------------------
311
* DnD_AddBlockLegacy --
313
* Adds a block to blockPath.
316
* TRUE on success, FALSE on failure.
319
* Processes trying to access this path will block until DnD_RemoveBlock
322
*----------------------------------------------------------------------------
326
DnD_AddBlockLegacy(int blockFd, // IN
327
const char *blockPath) // IN
329
ASSERT(blockFd >= 0);
331
if (VMBLOCK_CONTROL(blockFd, VMBLOCK_ADD_FILEBLOCK, blockPath) != 0) {
332
LOG(1, ("%s: Cannot add block on %s (%s)\n",
333
__func__, blockPath, strerror(errno)));
343
*----------------------------------------------------------------------------
345
* DnD_RemoveBlockLegacy --
347
* Removes block on blockedPath.
350
* TRUE on success, FALSE on failure.
353
* Processes blocked on accessing this path will continue.
355
*----------------------------------------------------------------------------
359
DnD_RemoveBlockLegacy(int blockFd, // IN
360
const char *blockedPath) // IN
363
if (VMBLOCK_CONTROL(blockFd, VMBLOCK_DEL_FILEBLOCK, blockedPath) != 0) {
364
Log("%s: Cannot delete block on %s (%s)\n",
365
__func__, blockedPath, strerror(errno));
370
LOG(4, ("%s: Could not remove block on %s: "
371
"fd to vmblock no longer exists.\n", __func__, blockedPath));
379
*----------------------------------------------------------------------------
381
* DnD_CheckBlockLegacy --
383
* Verifies that given file descriptor is truly a control file of
384
* kernel-based vmblock implementation. Just a stub, for now at
385
* least since we don't have a good way to check.
388
* TRUE on success, FALSE on failure.
393
*----------------------------------------------------------------------------
397
DnD_CheckBlockLegacy(int blockFd) // IN
404
*----------------------------------------------------------------------------
406
* DnD_AddBlockFuse --
408
* Adds a block to blockPath.
411
* TRUE on success, FALSE on failure.
414
* Processes trying to access this path will block until DnD_RemoveBlock
417
*----------------------------------------------------------------------------
421
DnD_AddBlockFuse(int blockFd, // IN
422
const char *blockPath) // IN
424
ASSERT(blockFd >= 0);
426
if (VMBLOCK_CONTROL_FUSE(blockFd, VMBLOCK_FUSE_ADD_FILEBLOCK,
428
LOG(1, ("%s: Cannot add block on %s (%s)\n",
429
__func__, blockPath, strerror(errno)));
439
*----------------------------------------------------------------------------
441
* DnD_RemoveBlockFuse --
443
* Removes block on blockedPath.
446
* TRUE on success, FALSE on failure.
449
* Processes blocked on accessing this path will continue.
451
*----------------------------------------------------------------------------
455
DnD_RemoveBlockFuse(int blockFd, // IN
456
const char *blockedPath) // IN
459
if (VMBLOCK_CONTROL_FUSE(blockFd, VMBLOCK_FUSE_DEL_FILEBLOCK,
461
Log("%s: Cannot delete block on %s (%s)\n",
462
__func__, blockedPath, strerror(errno));
467
LOG(4, ("%s: Could not remove block on %s: "
468
"fd to vmblock no longer exists.\n", __func__, blockedPath));
476
*----------------------------------------------------------------------------
478
* DnD_CheckBlockFuse --
480
* Verifies that given file descriptor is truly a control file of
481
* FUSE-based vmblock implementation.
484
* TRUE on success, FALSE on failure.
489
*----------------------------------------------------------------------------
493
DnD_CheckBlockFuse(int blockFd) // IN
495
char buf[sizeof(VMBLOCK_FUSE_READ_RESPONSE)];
498
size = read(blockFd, buf, sizeof(VMBLOCK_FUSE_READ_RESPONSE));
500
LOG(4, ("%s: read failed, error %s.\n",
501
__func__, strerror(errno)));
506
if (size != sizeof(VMBLOCK_FUSE_READ_RESPONSE)) {
507
LOG(4, ("%s: Response too short (%"FMTSZ"d vs. %"FMTSZ"u).\n",
508
__func__, size, sizeof(VMBLOCK_FUSE_READ_RESPONSE)));
513
if (memcmp(buf, VMBLOCK_FUSE_READ_RESPONSE,
514
sizeof(VMBLOCK_FUSE_READ_RESPONSE))) {
515
LOG(4, ("%s: Invalid response %.*s",
516
__func__, (int)sizeof(VMBLOCK_FUSE_READ_RESPONSE) - 1, buf));
526
*----------------------------------------------------------------------------
528
* DnD_TryInitVmblock --
530
* Initializes file blocking needed to prevent access to file before
531
* transfer has finished.
534
* Block descriptor on success, -1 on failure.
539
*----------------------------------------------------------------------------
543
DnD_TryInitVmblock(const char *vmbFsName, // IN
544
const char *vmbMntPoint, // IN
545
const char *vmbDevice, // IN
546
mode_t vmbDeviceMode, // IN
547
Bool (*verifyBlock)(int fd)) // IN
552
DECLARE_MNTINFO(mnt);
554
/* Make sure the vmblock file system is mounted. */
555
fp = OPEN_MNTFILE("r");
557
LOG(1, ("%s: could not open mount file\n", __func__));
562
while (GETNEXT_MNTINFO(fp, mnt)) {
564
* In the future we can publish the mount point in VMDB so that the UI
565
* can use it rather than enforcing the VMBLOCK_MOUNT_POINT check here.
568
if (strcmp(MNTINFO_FSTYPE(mnt), vmbFsName) == 0 &&
569
strcmp(MNTINFO_MNTPT(mnt), vmbMntPoint) == 0) {
575
(void) CLOSE_MNTFILE(fp);
578
/* Open device node for communication with vmblock. */
579
blockFd = Posix_Open(vmbDevice, vmbDeviceMode);
581
LOG(1, ("%s: Can not open blocker device (%s)\n",
582
__func__, strerror(errno)));
584
LOG(4, ("%s: Opened blocker device at %s\n",
585
__func__, VMBLOCK_DEVICE));
586
if (verifyBlock && !verifyBlock(blockFd)) {
587
LOG(4, ("%s: Blocker device at %s did not pass checks, closing.\n",
588
__func__, VMBLOCK_DEVICE));
600
*----------------------------------------------------------------------------
602
* DnD_InitializeBlocking --
604
* Initializes file blocking needed to prevent access to file before
605
* transfer has finished.
608
* TRUE on success, FALSE on failure.
613
*----------------------------------------------------------------------------
617
DnD_InitializeBlocking(DnDBlockControl *blkCtrl) // OUT
622
/* Root access is needed for opening the vmblock device. */
623
uid = Id_BeginSuperUser();
625
/* Fitrst try FUSE and see if it is available. */
626
blockFd = DnD_TryInitVmblock(VMBLOCK_FUSE_FS_NAME, VMBLOCK_FUSE_MOUNT_POINT,
627
VMBLOCK_FUSE_DEVICE, VMBLOCK_FUSE_DEVICE_MODE,
630
blkCtrl->fd = blockFd;
631
/* Setup FUSE methods. */
632
blkCtrl->blockRoot = VMBLOCK_FUSE_FS_ROOT;
633
blkCtrl->AddBlock = DnD_AddBlockFuse;
634
blkCtrl->RemoveBlock = DnD_RemoveBlockFuse;
638
/* Now try OS-specific VMBlock driver. */
639
blockFd = DnD_TryInitVmblock(VMBLOCK_FS_NAME, VMBLOCK_MOUNT_POINT,
640
VMBLOCK_DEVICE, VMBLOCK_DEVICE_MODE,
643
blkCtrl->fd = blockFd;
644
/* Setup legacy in-kernel methods. */
645
blkCtrl->blockRoot = VMBLOCK_FS_ROOT;
646
blkCtrl->AddBlock = DnD_AddBlockLegacy;
647
blkCtrl->RemoveBlock = DnD_RemoveBlockLegacy;
651
LOG(4, ("%s: could not find vmblock mounted\n", __func__));
653
Id_EndSuperUser(uid);
655
return blockFd != -1;
660
*----------------------------------------------------------------------------
662
* DnD_UninitializeBlocking --
664
* Uninitialize file blocking.
667
* TRUE on success, FALSE on failure.
670
* All existing blocks will be removed.
672
*----------------------------------------------------------------------------
676
DnD_UninitializeBlocking(DnDBlockControl *blkCtrl) // IN
680
if (blkCtrl->fd >= 0) {
681
if (close(blkCtrl->fd) < 0) {
682
Log("%s: Can not close blocker device (%s)\n",
683
__func__, strerror(errno));
694
* DnD_CompleteBlockInitialization --
696
* Complete block initialization in case when we were handed
697
* blocking file descriptor (presumably opened for us by a
701
* TRUE on success, FALSE on failure (invalid type).
704
* Adjusts blkCtrl to match blocking device type (legacy or fuse).
709
DnD_CompleteBlockInitialization(int fd, // IN
710
DnDBlockControl *blkCtrl) // OUT
714
if (DnD_CheckBlockFuse(fd)) {
715
/* Setup FUSE methods. */
716
blkCtrl->blockRoot = VMBLOCK_FUSE_FS_ROOT;
717
blkCtrl->AddBlock = DnD_AddBlockFuse;
718
blkCtrl->RemoveBlock = DnD_RemoveBlockFuse;
719
} else if (DnD_CheckBlockLegacy(fd)) {
720
/* Setup legacy methods. */
721
blkCtrl->blockRoot = VMBLOCK_FS_ROOT;
722
blkCtrl->AddBlock = DnD_AddBlockLegacy;
723
blkCtrl->RemoveBlock = DnD_RemoveBlockLegacy;
725
Log("%s: Can't determine block type.\n", __func__);
733
#endif /* linux || sun || FreeBSD */
737
*----------------------------------------------------------------------------
739
* DnDRootDirUsable --
741
* Determines whether the provided directory is usable as the root for
742
* staging directories.
745
* TRUE if the root directory is usable, FALSE otherwise.
750
*----------------------------------------------------------------------------
754
DnDRootDirUsable(ConstUnicode pathName) // IN:
758
if (Posix_Stat(pathName, &buf) < 0) {
762
return S_ISDIR(buf.st_mode) &&
763
(buf.st_mode & S_ISVTX) == S_ISVTX &&
764
(buf.st_mode & ACCESSPERMS) == DND_ROOTDIR_PERMS;
769
*----------------------------------------------------------------------------
771
* DnDSetPermissionsOnRootDir --
773
* Sets the correct permissions for the root staging directory. We set the
774
* root directory to 1777 so that all users can create their own staging
775
* directories within it and that other users cannot delete that directory.
778
* TRUE on success, FALSE on failure.
783
*----------------------------------------------------------------------------
787
DnDSetPermissionsOnRootDir(ConstUnicode pathName) // IN:
789
return Posix_Chmod(pathName, S_ISVTX | DND_ROOTDIR_PERMS) == 0;
794
*----------------------------------------------------------------------------
796
* DnDStagingDirectoryUsable --
798
* Determines whether a staging directory is usable for the current
799
* process. A directory is only usable by the current process if it is
800
* owned by the effective uid of the current process.
803
* TRUE if the directory is usable, FALSE if it is not.
808
*----------------------------------------------------------------------------
812
DnDStagingDirectoryUsable(ConstUnicode pathName) // IN:
816
if (Posix_Stat(pathName, &buf) < 0) {
820
return buf.st_uid == Id_GetEUid();
825
*----------------------------------------------------------------------------
827
* DnDSetPermissionsOnStagingDir --
829
* Sets the correct permissions for staging directories.
832
* TRUE on success, FALSE on failure.
837
*----------------------------------------------------------------------------
841
DnDSetPermissionsOnStagingDir(ConstUnicode pathName) // IN:
843
return Posix_Chmod(pathName, DND_STAGINGDIR_PERMS) == 0;