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
Warning("%s: the URI list did not begin with %s or %s\n", __func__,
175
DND_URI_LIST_PRE, DND_URI_LIST_PRE_KDE);
182
/* Walk the filename looking for the end */
184
while (*curr != '\0' && *curr != '\r' && *curr != '\n') {
190
/* Bump curr based on trailing newline characters found */
191
while (*curr == '\r' || *curr == '\n') {
195
*index = curr - uriList;
196
*length = nameEnd - nameStart + 1;
198
return (char *)nameStart;
203
*----------------------------------------------------------------------------
205
* DnD_UriListGetNextFile --
207
* Retrieves and unescapes the next file from the provided test/uri-list
208
* mime type string. The index provided is used to iteratively retrieve
209
* successive files from the list.
212
* An allocated, unescaped, NUL-terminated string containing the filename
213
* within the uri-list after the specified index. index is updated to point
214
* to the next entry of the uri-list, and length (if provided) is set to the
215
* length of the allocated string (not including the NUL terminator).
216
* NULL if there are no more entries in the list, or on error.
221
*----------------------------------------------------------------------------
225
DnD_UriListGetNextFile(char const *uriList, // IN : text/uri-list string
226
size_t *index, // IN/OUT: current index
227
size_t *length) // OUT : length of returned string
231
size_t fileLength = 0;
233
size_t unescapedLength;
240
/* Get pointer to and length of next filename */
241
file = DnDUriListGetFile(uriList, &nextIndex, &fileLength);
247
* Retrieve an allocated, unescaped name. This undoes the ' ' -> "%20"
248
* escaping as required by RFC 1630 for entries in a uri-list.
251
unescapedName = Escape_Undo('%', file, fileLength, &unescapedLength);
252
if (!unescapedName) {
253
Warning("%s: error unescaping filename\n", __func__);
260
*length = unescapedLength;
263
return unescapedName;
267
/* We need to make this suck less. */
268
#if defined(linux) || defined(sun) || defined(__FreeBSD__)
271
*----------------------------------------------------------------------------
273
* DnD_AddBlockLegacy --
275
* Adds a block to blockPath.
278
* TRUE on success, FALSE on failure.
281
* Processes trying to access this path will block until DnD_RemoveBlock
284
*----------------------------------------------------------------------------
288
DnD_AddBlockLegacy(int blockFd, // IN
289
const char *blockPath) // IN
291
ASSERT(blockFd >= 0);
293
if (VMBLOCK_CONTROL(blockFd, VMBLOCK_ADD_FILEBLOCK, blockPath) != 0) {
294
LOG(1, ("%s: Cannot add block on %s (%s)\n",
295
__func__, blockPath, strerror(errno)));
305
*----------------------------------------------------------------------------
307
* DnD_RemoveBlockLegacy --
309
* Removes block on blockedPath.
312
* TRUE on success, FALSE on failure.
315
* Processes blocked on accessing this path will continue.
317
*----------------------------------------------------------------------------
321
DnD_RemoveBlockLegacy(int blockFd, // IN
322
const char *blockedPath) // IN
325
if (VMBLOCK_CONTROL(blockFd, VMBLOCK_DEL_FILEBLOCK, blockedPath) != 0) {
326
Log("%s: Cannot delete block on %s (%s)\n",
327
__func__, blockedPath, strerror(errno));
332
LOG(4, ("%s: Could not remove block on %s: "
333
"fd to vmblock no longer exists.\n", __func__, blockedPath));
341
*----------------------------------------------------------------------------
343
* DnD_CheckBlockLegacy --
345
* Verifies that given file descriptor is truly a control file of
346
* kernel-based vmblock implementation. Just a stub, for now at
347
* least since we don't have a good way to check.
350
* TRUE on success, FALSE on failure.
355
*----------------------------------------------------------------------------
359
DnD_CheckBlockLegacy(int blockFd) // IN
366
*----------------------------------------------------------------------------
368
* DnD_AddBlockFuse --
370
* Adds a block to blockPath.
373
* TRUE on success, FALSE on failure.
376
* Processes trying to access this path will block until DnD_RemoveBlock
379
*----------------------------------------------------------------------------
383
DnD_AddBlockFuse(int blockFd, // IN
384
const char *blockPath) // IN
386
ASSERT(blockFd >= 0);
388
if (VMBLOCK_CONTROL_FUSE(blockFd, VMBLOCK_FUSE_ADD_FILEBLOCK,
390
LOG(1, ("%s: Cannot add block on %s (%s)\n",
391
__func__, blockPath, strerror(errno)));
401
*----------------------------------------------------------------------------
403
* DnD_RemoveBlockFuse --
405
* Removes block on blockedPath.
408
* TRUE on success, FALSE on failure.
411
* Processes blocked on accessing this path will continue.
413
*----------------------------------------------------------------------------
417
DnD_RemoveBlockFuse(int blockFd, // IN
418
const char *blockedPath) // IN
421
if (VMBLOCK_CONTROL_FUSE(blockFd, VMBLOCK_FUSE_DEL_FILEBLOCK,
423
Log("%s: Cannot delete block on %s (%s)\n",
424
__func__, blockedPath, strerror(errno));
429
LOG(4, ("%s: Could not remove block on %s: "
430
"fd to vmblock no longer exists.\n", __func__, blockedPath));
438
*----------------------------------------------------------------------------
440
* DnD_CheckBlockFuse --
442
* Verifies that given file descriptor is truly a control file of
443
* FUSE-based vmblock implementation.
446
* TRUE on success, FALSE on failure.
451
*----------------------------------------------------------------------------
455
DnD_CheckBlockFuse(int blockFd) // IN
457
char buf[sizeof(VMBLOCK_FUSE_READ_RESPONSE)];
460
size = read(blockFd, buf, sizeof(VMBLOCK_FUSE_READ_RESPONSE));
462
LOG(4, ("%s: read failed, error %s.\n",
463
__func__, strerror(errno)));
468
if (size != sizeof(VMBLOCK_FUSE_READ_RESPONSE)) {
469
LOG(4, ("%s: Response too short (%"FMTSZ"d vs. %"FMTSZ"u).\n",
470
__func__, size, sizeof(VMBLOCK_FUSE_READ_RESPONSE)));
475
if (memcmp(buf, VMBLOCK_FUSE_READ_RESPONSE,
476
sizeof(VMBLOCK_FUSE_READ_RESPONSE))) {
477
LOG(4, ("%s: Invalid response %.*s",
478
__func__, (int)sizeof(VMBLOCK_FUSE_READ_RESPONSE) - 1, buf));
488
*----------------------------------------------------------------------------
490
* DnD_TryInitVmblock --
492
* Initializes file blocking needed to prevent access to file before
493
* transfer has finished.
496
* Block descriptor on success, -1 on failure.
501
*----------------------------------------------------------------------------
505
DnD_TryInitVmblock(const char *vmbFsName, // IN
506
const char *vmbMntPoint, // IN
507
const char *vmbDevice, // IN
508
mode_t vmbDeviceMode, // IN
509
Bool (*verifyBlock)(int fd)) // IN
514
DECLARE_MNTINFO(mnt);
516
/* Make sure the vmblock file system is mounted. */
517
fp = OPEN_MNTFILE("r");
519
LOG(1, ("%s: could not open mount file\n", __func__));
524
while (GETNEXT_MNTINFO(fp, mnt)) {
526
* In the future we can publish the mount point in VMDB so that the UI
527
* can use it rather than enforcing the VMBLOCK_MOUNT_POINT check here.
530
if (strcmp(MNTINFO_FSTYPE(mnt), vmbFsName) == 0 &&
531
strcmp(MNTINFO_MNTPT(mnt), vmbMntPoint) == 0) {
537
(void) CLOSE_MNTFILE(fp);
540
/* Open device node for communication with vmblock. */
541
blockFd = Posix_Open(vmbDevice, vmbDeviceMode);
543
LOG(1, ("%s: Can not open blocker device (%s)\n",
544
__func__, strerror(errno)));
546
LOG(4, ("%s: Opened blocker device at %s\n",
547
__func__, VMBLOCK_DEVICE));
548
if (verifyBlock && !verifyBlock(blockFd)) {
549
LOG(4, ("%s: Blocker device at %s did not pass checks, closing.\n",
550
__func__, VMBLOCK_DEVICE));
562
*----------------------------------------------------------------------------
564
* DnD_InitializeBlocking --
566
* Initializes file blocking needed to prevent access to file before
567
* transfer has finished.
570
* TRUE on success, FALSE on failure.
575
*----------------------------------------------------------------------------
579
DnD_InitializeBlocking(DnDBlockControl *blkCtrl) // OUT
584
/* Root access is needed for opening the vmblock device. */
585
uid = Id_BeginSuperUser();
587
/* Fitrst try FUSE and see if it is available. */
588
blockFd = DnD_TryInitVmblock(VMBLOCK_FUSE_FS_NAME, VMBLOCK_FUSE_MOUNT_POINT,
589
VMBLOCK_FUSE_DEVICE, VMBLOCK_FUSE_DEVICE_MODE,
592
blkCtrl->fd = blockFd;
593
/* Setup FUSE methods. */
594
blkCtrl->blockRoot = VMBLOCK_FUSE_FS_ROOT;
595
blkCtrl->AddBlock = DnD_AddBlockFuse;
596
blkCtrl->RemoveBlock = DnD_RemoveBlockFuse;
600
/* Now try OS-specific VMBlock driver. */
601
blockFd = DnD_TryInitVmblock(VMBLOCK_FS_NAME, VMBLOCK_MOUNT_POINT,
602
VMBLOCK_DEVICE, VMBLOCK_DEVICE_MODE,
605
blkCtrl->fd = blockFd;
606
/* Setup legacy in-kernel methods. */
607
blkCtrl->blockRoot = VMBLOCK_FS_ROOT;
608
blkCtrl->AddBlock = DnD_AddBlockLegacy;
609
blkCtrl->RemoveBlock = DnD_RemoveBlockLegacy;
613
LOG(4, ("%s: could not find vmblock mounted\n", __func__));
615
Id_EndSuperUser(uid);
617
return blockFd != -1;
622
*----------------------------------------------------------------------------
624
* DnD_UninitializeBlocking --
626
* Uninitialize file blocking.
629
* TRUE on success, FALSE on failure.
632
* All existing blocks will be removed.
634
*----------------------------------------------------------------------------
638
DnD_UninitializeBlocking(DnDBlockControl *blkCtrl) // IN
642
if (blkCtrl->fd >= 0) {
643
if (close(blkCtrl->fd) < 0) {
644
Log("%s: Can not close blocker device (%s)\n",
645
__func__, strerror(errno));
656
* DnD_CompleteBlockInitialization --
658
* Complete block initialization in case when we were handed
659
* blocking file descriptor (presumably opened for us by a
663
* TRUE on success, FALSE on failure (invalid type).
666
* Adjusts blkCtrl to match blocking device type (legacy or fuse).
671
DnD_CompleteBlockInitialization(int fd, // IN
672
DnDBlockControl *blkCtrl) // OUT
676
if (DnD_CheckBlockFuse(fd)) {
677
/* Setup FUSE methods. */
678
blkCtrl->blockRoot = VMBLOCK_FUSE_FS_ROOT;
679
blkCtrl->AddBlock = DnD_AddBlockFuse;
680
blkCtrl->RemoveBlock = DnD_RemoveBlockFuse;
681
} else if (DnD_CheckBlockLegacy(fd)) {
682
/* Setup legacy methods. */
683
blkCtrl->blockRoot = VMBLOCK_FS_ROOT;
684
blkCtrl->AddBlock = DnD_AddBlockLegacy;
685
blkCtrl->RemoveBlock = DnD_RemoveBlockLegacy;
687
Log("%s: Can't determine block type.\n", __func__);
695
#endif /* linux || sun || FreeBSD */
699
*----------------------------------------------------------------------------
701
* DnDRootDirUsable --
703
* Determines whether the provided directory is usable as the root for
704
* staging directories.
707
* TRUE if the root directory is usable, FALSE otherwise.
712
*----------------------------------------------------------------------------
716
DnDRootDirUsable(ConstUnicode pathName) // IN:
720
if (Posix_Stat(pathName, &buf) < 0) {
724
return S_ISDIR(buf.st_mode) &&
725
(buf.st_mode & S_ISVTX) == S_ISVTX &&
726
(buf.st_mode & ACCESSPERMS) == DND_ROOTDIR_PERMS;
731
*----------------------------------------------------------------------------
733
* DnDSetPermissionsOnRootDir --
735
* Sets the correct permissions for the root staging directory. We set the
736
* root directory to 1777 so that all users can create their own staging
737
* directories within it and that other users cannot delete that directory.
740
* TRUE on success, FALSE on failure.
745
*----------------------------------------------------------------------------
749
DnDSetPermissionsOnRootDir(ConstUnicode pathName) // IN:
751
return Posix_Chmod(pathName, S_ISVTX | DND_ROOTDIR_PERMS) == 0;
756
*----------------------------------------------------------------------------
758
* DnDStagingDirectoryUsable --
760
* Determines whether a staging directory is usable for the current
761
* process. A directory is only usable by the current process if it is
762
* owned by the effective uid of the current process.
765
* TRUE if the directory is usable, FALSE if it is not.
770
*----------------------------------------------------------------------------
774
DnDStagingDirectoryUsable(ConstUnicode pathName) // IN:
778
if (Posix_Stat(pathName, &buf) < 0) {
782
return buf.st_uid == Id_GetEUid();
787
*----------------------------------------------------------------------------
789
* DnDSetPermissionsOnStagingDir --
791
* Sets the correct permissions for staging directories.
794
* TRUE on success, FALSE on failure.
799
*----------------------------------------------------------------------------
803
DnDSetPermissionsOnStagingDir(ConstUnicode pathName) // IN:
805
return Posix_Chmod(pathName, DND_STAGINGDIR_PERMS) == 0;