diff options
28 files changed, 3786 insertions, 450 deletions
diff --git a/Config.kmk b/Config.kmk index 3fa5632473b..63aebda3b53 100644 --- a/Config.kmk +++ b/Config.kmk @@ -897,6 +897,11 @@ VBOX_WITH_HOST_CHANNEL = 1 # Enable the guest control service. if1of ($(KBUILD_TARGET), darwin freebsd linux solaris win) VBOX_WITH_GUEST_CONTROL = 1 + # Enables support for handling certain commands via the built-in (busybox-like) toolbox in VBoxService. See @bugref{9783} + # This is for supporting Guest Additions < 7.1. + VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT = 1 + # Enables treating the toolbox as built-in commands. Requires 7.1 Guest Additions. See @bugref{9783} + VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS = endif # Enable ballooning VBOX_WITH_MEMBALLOON = 1 diff --git a/include/VBox/GuestHost/GuestControl.h b/include/VBox/GuestHost/GuestControl.h index 6d36cc3daea..4ad8bf3507c 100644 --- a/include/VBox/GuestHost/GuestControl.h +++ b/include/VBox/GuestHost/GuestControl.h @@ -42,6 +42,7 @@ # pragma once #endif +#include <iprt/time.h> #include <iprt/types.h> /* Everything defined in this file lives in this namespace. */ @@ -141,6 +142,33 @@ enum eProcessStatus #define PATHRENAME_FLAG_VALID_MASK UINT32_C(0x00000003) /** @} */ +/** @name GSTCTL_CREATETEMP_F_XXX - Guest temporary directory/file creation flags. + * @{ + */ +/** Does not specify anything. */ +#define GSTCTL_CREATETEMP_F_NONE UINT32_C(0) +/** Creates a directory instead of a file. */ +#define GSTCTL_CREATETEMP_F_DIRECTORY RT_BIT(0) +/** Creates a secure temporary file / directory. + * Might not be supported on all (guest) OSes. + * + * @sa IPRT's implementation of RTDirCreateTempSecure() / RTFileCreateTempSecumre(). */ +#define GSTCTL_CREATETEMP_F_SECURE RT_BIT(1) +/** Mask of valid flags. */ +#define GSTCTL_CREATETEMP_F_VALID_MASK UINT32_C(0x00000003) +/** @} */ + +/** @name GSTCTL_CREATEDIRECTORY_F_XXX - Guest directory creation flags. + * @{ + */ +/** Does not specify anything. */ +#define GSTCTL_CREATEDIRECTORY_F_NONE UINT32_C(0) +/** Also creates parent directories if they don't exist yet. */ +#define GSTCTL_CREATEDIRECTORY_F_PARENTS RT_BIT(0) +/** Mask of valid flags. */ +#define GSTCTL_CREATEDIRECTORY_F_VALID_MASK UINT32_C(0x00000001) +/** @} */ + /** @name GUEST_SHUTDOWN_FLAG_XXX - Guest shutdown flags. * Must match Main's GuestShutdownFlag_ definitions. * @{ @@ -228,7 +256,281 @@ enum eInputStatus INPUT_STS_OVERFLOW = 30 }; +/** + * Guest file system object -- additional information in a GSTCTLFSOBJATTR object. + */ +enum GSTCTLFSOBJATTRADD +{ + /** No additional information is available / requested. */ + GSTCTLFSOBJATTRADD_NOTHING = 1, + /** The additional unix attributes (RTFSOBJATTR::u::Unix) are available / + * requested. */ + GSTCTLFSOBJATTRADD_UNIX, + /** The additional unix attributes (RTFSOBJATTR::u::UnixOwner) are + * available / requested. */ + GSTCTLFSOBJATTRADD_UNIX_OWNER, + /** The additional unix attributes (RTFSOBJATTR::u::UnixGroup) are + * available / requested. */ + GSTCTLFSOBJATTRADD_UNIX_GROUP, + /** The additional extended attribute size (RTFSOBJATTR::u::EASize) is available / requested. */ + GSTCTLFSOBJATTRADD_EASIZE, + /** The last valid item (inclusive). + * The valid range is RTFSOBJATTRADD_NOTHING thru RTFSOBJATTRADD_LAST. */ + GSTCTLFSOBJATTRADD_LAST = GSTCTLFSOBJATTRADD_EASIZE, + + /** The usual 32-bit hack. */ + GSTCTLFSOBJATTRADD_32BIT_SIZE_HACK = 0x7fffffff +}; + +/** The number of bytes reserved for the additional attribute union. */ +#define GSTCTLFSOBJATTRUNION_MAX_SIZE 128 +/** + * Additional Unix Attributes (GSTCTLFSOBJATTRADD_UNIX). + */ +typedef struct GSTCTLFSOBJATTRUNIX +{ + /** The user owning the filesystem object (st_uid). + * This field is NIL_RTUID if not supported. */ + RTUID uid; + + /** The group the filesystem object is assigned (st_gid). + * This field is NIL_RTGID if not supported. */ + RTGID gid; + + /** Number of hard links to this filesystem object (st_nlink). + * This field is 1 if the filesystem doesn't support hardlinking or + * the information isn't available. + */ + uint32_t cHardlinks; + + /** The device number of the device which this filesystem object resides on (st_dev). + * This field is 0 if this information is not available. */ + RTDEV INodeIdDevice; + + /** The unique identifier (within the filesystem) of this filesystem object (st_ino). + * Together with INodeIdDevice, this field can be used as a OS wide unique id + * when both their values are not 0. + * This field is 0 if the information is not available. + * + * @remarks The special '..' dir always shows up with 0 on NTFS/Windows. */ + RTINODE INodeId; + + /** User flags (st_flags). + * This field is 0 if this information is not available. */ + uint32_t fFlags; + + /** The current generation number (st_gen). + * This field is 0 if this information is not available. */ + uint32_t GenerationId; + + /** The device number of a character or block device type object (st_rdev). + * This field is 0 if the file isn't of a character or block device type and + * when the OS doesn't subscribe to the major+minor device idenfication scheme. */ + RTDEV Device; +} GSTCTLFSOBJATTRUNIX; + +/** + * Additional guest Unix attributes (GSTCTLFSOBJATTRADD_UNIX_OWNER). + */ +typedef struct GSTCTLFSOBJATTRUNIXOWNER +{ + /** The user owning the filesystem object (st_uid). + * This field is NIL_RTUID if not supported. */ + RTUID uid; + /** The user name. + * Empty if not available or not supported, truncated if too long. */ + char szName[GSTCTLFSOBJATTRUNION_MAX_SIZE - sizeof(RTUID)]; +} GSTCTLFSOBJATTRUNIXOWNER; + +/** + * Additional guest Unix attributes (GSTCTLFSOBJATTRADD_UNIX_GROUP). + */ +typedef struct GSTCTLFSOBJATTRUNIXGROUP +{ + /** The user owning the filesystem object (st_uid). + * This field is NIL_RTUID if not supported. */ + RTGID gid; + /** The group name. + * Empty if not available or not supported, truncated if too long. */ + char szName[GSTCTLFSOBJATTRUNION_MAX_SIZE - sizeof(RTGID)]; +} GSTCTLFSOBJATTRUNIXGROUP; + +/** + * Guest filesystem object attributes. + */ +#pragma pack(1) +typedef struct GSTCTLFSOBJATTR +{ + /** Mode flags (st_mode). RTFS_UNIX_*, RTFS_TYPE_*, and RTFS_DOS_*. */ + RTFMODE fMode; + + /** The additional attributes available. */ + GSTCTLFSOBJATTRADD enmAdditional; + + /** + * Additional attributes. + * + * Unless explicitly specified to an API, the API can provide additional + * data as it is provided by the underlying OS. + */ + union GSTCTLFSOBJATTRUNION + { + /** Additional Unix Attributes - GUEST_FSOBJATTRADD_UNIX. */ + GSTCTLFSOBJATTRUNIX Unix; + /** Additional Unix Owner Attributes - GUEST_FSOBJATTRADD_UNIX_OWNER. */ + GSTCTLFSOBJATTRUNIXOWNER UnixOwner; + /** Additional Unix Group Attributes - GUEST_FSOBJATTRADD_UNIX_GROUP. */ + GSTCTLFSOBJATTRUNIXGROUP UnixGroup; + + /** + * Extended attribute size is available when RTFS_DOS_HAVE_EA_SIZE is set. + */ + struct GSTCTLFSOBJATTREASIZE + { + /** Size of EAs. */ + RTFOFF cb; + } EASize; + /** Reserved space. */ + uint8_t abReserveSpace[128]; + } u; +} GSTCTLFSOBJATTR; +#pragma pack() +/** Pointer to a guest filesystem object attributes structure. */ +typedef GSTCTLFSOBJATTR *PGSTCTLFSOBJATTR; +/** Pointer to a const guest filesystem object attributes structure. */ +typedef const GSTCTLFSOBJATTR *PCGSTCTLFSOBJATTR; + +/** @name GSTCTL_QUERYINFO_F_XXX - Generic flags for querying guest file system information. + * @{ */ +/** No guest stat flags specified. */ +#define GSTCTL_QUERYINFO_F_NONE UINT32_C(0) +/** Last component: Work on the link. */ +#define GSTCTL_QUERYINFO_F_ON_LINK RT_BIT_32(0) +/** Last component: Follow if link. */ +#define GSTCTL_QUERYINFO_F_FOLLOW_LINK RT_BIT_32(1) +/** Don't allow symbolic links as part of the path. + * @remarks this flag is currently not implemented and will be ignored. */ +#define GSTCTL_QUERYINFO_F_NO_SYMLINKS RT_BIT_32(2) +/** GSTCTL_QUERYINFO_F_XXX flag valid mask. */ +#define GSTCTL_QUERYINFO_F_VALID_MASK UINT32_C(0x00000007) +/** @} */ + +/** + * Filter option for HOST_MSG_DIR_OPEN. + */ +typedef enum GSTCTLDIRFILTER +{ + /** The usual invalid 0 entry. */ + GSTCTLDIRFILTER_INVALID = 0, + /** No filter should be applied (and none was specified). */ + GSTCTLDIRFILTER_NONE, + /** The Windows NT filter. + * The following wildcard chars: *, ?, <, > and " + * The matching is done on the uppercased strings. */ + GSTCTLDIRFILTER_WINNT, + /** The UNIX filter. + * The following wildcard chars: *, ?, [..] + * The matching is done on exact case. */ + GSTCTLDIRFILTER_UNIX, + /** The UNIX filter, uppercased matching. + * Same as GSTCTLDIRFILTER_UNIX except that the strings are uppercased before comparing. */ + GSTCTLDIRFILTER_UNIX_UPCASED, + + /** The usual full 32-bit value. */ + GSTCTLDIRFILTER_32BIT_HACK = 0x7fffffff +} GSTCTLDIRFILTER; + +/** @name GSTCTLDIR_F_XXX - Directory flags for HOST_MSG_DIR_OPEN. + * @{ */ +/** Don't allow symbolic links as part of the path. + * @remarks this flag is currently not implemented and will be ignored. */ +#define GSTCTLDIR_F_NO_SYMLINKS RT_BIT_32(0) +/** Deny relative opening of anything above this directory. */ +#define GSTCTLDIR_F_DENY_ASCENT RT_BIT_32(1) +/** Don't follow symbolic links in the final component. */ +#define GSTCTLDIR_F_NO_FOLLOW RT_BIT_32(2) +/** Long path hack: Don't apply RTPathAbs to the path. */ +#define GSTCTLDIR_F_NO_ABS_PATH RT_BIT_32(3) +/** Valid flag mask. */ +#define GSTCTLDIR_F_VALID_MASK UINT32_C(0x0000000f) + +/** + * Guest filesystem object information structure. + * + * This is returned by + * - GUEST_FS_NOTIFYTYPE_QUERY_INFO + * - GUEST_DIR_NOTIFYTYPE_READ + */ +#pragma pack(1) +typedef struct GSTCTLFSOBJINFO +{ + /** Logical size (st_size). + * For normal files this is the size of the file. + * For symbolic links, this is the length of the path name contained + * in the symbolic link. + * For other objects this fields needs to be specified. + */ + RTFOFF cbObject; + + /** Disk allocation size (st_blocks * DEV_BSIZE). */ + RTFOFF cbAllocated; + + /** Time of last access (st_atime). */ + RTTIMESPEC AccessTime; + + /** Time of last data modification (st_mtime). */ + RTTIMESPEC ModificationTime; + + /** Time of last status change (st_ctime). + * If not available this is set to ModificationTime. + */ + RTTIMESPEC ChangeTime; + + /** Time of file birth (st_birthtime). + * If not available this is set to ChangeTime. + */ + RTTIMESPEC BirthTime; + + /** Attributes. */ + GSTCTLFSOBJATTR Attr; + +} GSTCTLFSOBJINFO; +#pragma pack() +/** Pointer to a guest filesystem object information structure. */ +typedef GSTCTLFSOBJINFO *PGSTCTLFSOBJINFO; +/** Pointer to a const guest filesystem object information structure. */ +typedef const GSTCTLFSOBJINFO *PCGSTCTLFSOBJINFO; + +/** + * Guest directory entry with extended information. + * + * This is inspired by IPRT + the PC interfaces. + */ +#pragma pack(1) +typedef struct GSTCTLDIRENTRYEX +{ + /** Full information about the guest object. */ + GSTCTLFSOBJINFO Info; + /** The length of the short field (number of RTUTF16 entries (not chars)). + * It is 16-bit for reasons of alignment. */ + uint16_t cwcShortName; + /** The short name for 8.3 compatibility. + * Empty string if not available. + * Since the length is a bit tricky for a UTF-8 encoded name, and since this + * is practically speaking only a windows thing, it is encoded as UCS-2. */ + RTUTF16 wszShortName[14]; + /** The length of the filename. */ + uint16_t cbName; + /** The filename. (no path) + * Using the pcbDirEntry parameter of RTDirReadEx makes this field variable in size. */ + char szName[260]; +} GSTCTLDIRENTRYEX; +#pragma pack() +/** Pointer to a guest directory entry. */ +typedef GSTCTLDIRENTRYEX *PGSTCTLDIRENTRYEX; +/** Pointer to a const guest directory entry. */ +typedef GSTCTLDIRENTRYEX const *PCGSTCTLDIRENTRYEX; } /* namespace guestControl */ diff --git a/include/VBox/HostServices/GuestControlSvc.h b/include/VBox/HostServices/GuestControlSvc.h index 5816566a727..248c794d9ae 100644 --- a/include/VBox/HostServices/GuestControlSvc.h +++ b/include/VBox/HostServices/GuestControlSvc.h @@ -40,10 +40,12 @@ # pragma once #endif +#include <iprt/assert.h> +#include <VBox/hgcmsvc.h> + #include <VBox/VMMDevCoreTypes.h> +#include <VBox/GuestHost/GuestControl.h> #include <VBox/VBoxGuestCoreTypes.h> -#include <VBox/hgcmsvc.h> -#include <iprt/assert.h> /* Everything defined in this file lives in this namespace. */ namespace guestControl { @@ -199,11 +201,37 @@ enum eHostMsg /** * Gets the current file position of an opened guest file. */ - HOST_MSG_FILE_TELL, + HOST_MSG_FILE_TELL = 271, /** * Changes the file size. */ - HOST_MSG_FILE_SET_SIZE, + HOST_MSG_FILE_SET_SIZE = 272, +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + /** + * Removes a file on the guest. + */ + HOST_MSG_FILE_REMOVE = 273, + /** + * Opens (creates) a directory on the guest. + */ + HOST_MSG_DIR_OPEN = 310, + /** + * Closes a directory on the guest. + */ + HOST_MSG_DIR_CLOSE = 311, + /** + * Reads the next directory entry on the guest. + */ + HOST_MSG_DIR_READ = 312, + /** + * Rewinds and restarts the directory reading on the guest. + */ + HOST_MSG_DIR_REWIND = 313, + /** + * Creates a directory on the guest. + */ + HOST_MSG_DIR_CREATE = 314, +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ /** * Removes a directory on the guest. */ @@ -215,16 +243,25 @@ enum eHostMsg /** * Retrieves the user's documents directory. */ - HOST_MSG_PATH_USER_DOCUMENTS, + HOST_MSG_PATH_USER_DOCUMENTS = 331, /** * Retrieves the user's home directory. */ - HOST_MSG_PATH_USER_HOME, + HOST_MSG_PATH_USER_HOME = 332, /** * Issues a shutdown / reboot of the guest OS. */ - HOST_MSG_SHUTDOWN, - + HOST_MSG_SHUTDOWN = 333, +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + /** + * Retrieves information about a file system object. + */ + HOST_MSG_FS_QUERY_INFO = 334, + /** + * Creates a temporary file or directory. + */ + HOST_MSG_FS_CREATE_TEMP = 335, +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ /** Blow the type up to 32-bits. */ HOST_MSG_32BIT_HACK = 0x7fffffff }; @@ -257,11 +294,23 @@ DECLINLINE(const char *) GstCtrlHostMsgtoStr(enum eHostMsg enmMsg) RT_CASE_RET_STR(HOST_MSG_FILE_SEEK); RT_CASE_RET_STR(HOST_MSG_FILE_TELL); RT_CASE_RET_STR(HOST_MSG_FILE_SET_SIZE); +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + RT_CASE_RET_STR(HOST_MSG_FILE_REMOVE); + RT_CASE_RET_STR(HOST_MSG_DIR_OPEN); + RT_CASE_RET_STR(HOST_MSG_DIR_CLOSE); + RT_CASE_RET_STR(HOST_MSG_DIR_READ); + RT_CASE_RET_STR(HOST_MSG_DIR_REWIND); + RT_CASE_RET_STR(HOST_MSG_DIR_CREATE); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ RT_CASE_RET_STR(HOST_MSG_DIR_REMOVE); RT_CASE_RET_STR(HOST_MSG_PATH_RENAME); RT_CASE_RET_STR(HOST_MSG_PATH_USER_DOCUMENTS); RT_CASE_RET_STR(HOST_MSG_PATH_USER_HOME); RT_CASE_RET_STR(HOST_MSG_SHUTDOWN); +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + RT_CASE_RET_STR(HOST_MSG_FS_QUERY_INFO); + RT_CASE_RET_STR(HOST_MSG_FS_CREATE_TEMP); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ RT_CASE_RET_STR(HOST_MSG_32BIT_HACK); } return "Unknown"; @@ -578,7 +627,19 @@ enum eGuestMsg * Guest notifies the host about some file event. * @todo proper docs. */ - GUEST_MSG_FILE_NOTIFY = 240 + GUEST_MSG_FILE_NOTIFY = 240, +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + /** + * Guest notifies the host about some file system event. + * + * @retval VINF_SUCCESS on success. + * @retval VERR_INVALID_CLIENT_ID + * @retval VERR_WRONG_PARAMETER_COUNT + * @retval VERR_WRONG_PARAMETER_TYPE + * @since 7.1 + */ + GUEST_MSG_FS_NOTIFY = 241 +#endif }; /** @@ -617,11 +678,13 @@ DECLINLINE(const char *) GstCtrlGuestMsgToStr(enum eGuestMsg enmMsg) RT_CASE_RET_STR(GUEST_MSG_EXEC_IO_NOTIFY); RT_CASE_RET_STR(GUEST_MSG_DIR_NOTIFY); RT_CASE_RET_STR(GUEST_MSG_FILE_NOTIFY); +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + RT_CASE_RET_STR(GUEST_MSG_FS_NOTIFY); +#endif } return "Unknown"; } - /** * Guest session notification types. * @sa HGCMMsgSessionNotify. @@ -649,7 +712,7 @@ enum GUEST_SESSION_NOTIFYTYPE /** * Guest directory notification types. - * @sa HGCMMsgDirNotify. + * @sa HGCMMsgReplyDirNotify. */ enum GUEST_DIR_NOTIFYTYPE { @@ -660,6 +723,12 @@ enum GUEST_DIR_NOTIFYTYPE GUEST_DIR_NOTIFYTYPE_OPEN = 10, /** Guest directory closed. */ GUEST_DIR_NOTIFYTYPE_CLOSE = 20, +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + /** Guest directory read. */ + GUEST_DIR_NOTIFYTYPE_READ = 21, + /** Guest directory was rewind. */ + GUEST_DIR_NOTIFYTYPE_REWIND = 22, +#endif /** Information about an open guest directory. */ GUEST_DIR_NOTIFYTYPE_INFO = 40, /** Guest directory created. */ @@ -688,6 +757,21 @@ enum GUEST_FILE_NOTIFYTYPE }; /** + * Guest file system notification types. + */ +enum GUEST_FS_NOTIFYTYPE +{ + /** Unknown fs notification type; do not use. */ + GUEST_FS_NOTIFYTYPE_UNKNOWN = 0, + /** File system query information notification from the guest. + * @since 7.1 */ + GUEST_FS_NOTIFYTYPE_QUERY_INFO = 2, + /** Temporary directory creation notification from the guest. + * @since 7.1 */ + GUEST_FS_NOTIFYTYPE_CREATE_TEMP = 1, +}; + +/** * Guest file seeking types. Has to match FileSeekType in Main. * * @note This is not compatible with RTFileSeek, which is an unncessary pain. @@ -715,6 +799,12 @@ enum GUEST_FILE_SEEKTYPE #define VBOX_GUESTCTRL_GF_0_PROCESS_DYNAMIC_SIZES RT_BIT_64(2) /** Supports shutting down / rebooting the guest. */ #define VBOX_GUESTCTRL_GF_0_SHUTDOWN RT_BIT_64(3) +/** VBoxService' toolbox commands (vbox_rm, vbox_stat, ++) are supported by + * dedicated built-in HGCM commands. + * + * The toolbox commands now are being marked as deprecated. + * @since 7.1 */ +# define VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS RT_BIT_64(4) /** Bit that must be set in the 2nd parameter, will be cleared if the host reponds * correctly (old hosts might not). */ #define VBOX_GUESTCTRL_GF_1_MUST_BE_ONE RT_BIT_64(63) @@ -803,6 +893,29 @@ typedef struct HGCMMsgCancelPendingWaits VBGLIOCHGCMCALL hdr; } HGCMMsgCancelPendingWaits; +/** + * Generic reply header for reply-based messages. + * + * @note Be careful when changing this, as older Guest Additions might depend on this + * and other stuff can break, too. So better leave this alone. + */ +typedef struct HGCMReplyHdr +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Message type. */ + HGCMFunctionParameter type; + /** IPRT result of overall operation. */ + HGCMFunctionParameter rc; +} HGCMReplyHdr; + +/** Number of HGCM parameters the HGCMReplyHdr has. */ +#define GSTCTL_HGCM_REPLY_HDR_PARMS 3 + +/** + * Generic reply message from guest to the host. + */ typedef struct HGCMMsgReply { VBGLIOCHGCMCALL hdr; @@ -812,10 +925,51 @@ typedef struct HGCMMsgReply HGCMFunctionParameter type; /** IPRT result of overall operation. */ HGCMFunctionParameter rc; - /** Optional payload to this reply. */ + /** Optional payload to this reply + * Uses the REPLY_PAYLOAD_XXX structs. */ HGCMFunctionParameter payload; } HGCMMsgReply; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Creates a temporary directory / file on the guest. + */ +typedef struct HGCMMsgFsCreateTemp +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Template name to use for file/directory creation. + * If \a tmpdir is set, this path will be relative to \a tmpdir and must not be an absolute path. */ + HGCMFunctionParameter template_name; + /** Temporary directory to use. + * If empty, the guest OS' temporary directory will be determined via IPRT on the guest side. */ + HGCMFunctionParameter tmpdir; + /** Creation flags. + * See GSTCTL_CREATETEMP_F_XXX. */ + HGCMFunctionParameter flags; + /** File mode to use for creation (ignored if GSTCTL_CREATETEMP_F_SECURE is defined). + * See GSTCTL_CREATETEMP_F_XXX. */ + HGCMFunctionParameter mode; +} HGCMMsgFsCreateTemp; + +/** + * Queries information for a file system object on the guest. + */ +typedef struct HGCMMsgFsQueryInfo +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Path to query information for. */ + HGCMFunctionParameter path; + /** Additional file system attributes to lookup (GSTCTLFSOBJATTRADD). */ + HGCMFunctionParameter add_attributes; + /** Flags (GSTCTL_QUERYINFO_F_XXX). */ + HGCMFunctionParameter flags; +} HGCMMsgFsQueryInfo; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + /** * Creates a guest session. */ @@ -863,6 +1017,84 @@ typedef struct HGCMMsgSessionNotify HGCMFunctionParameter result; } HGCMMsgSessionNotify; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Opens a guest directory. + */ +typedef struct HGCMMsgDirOpen +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Path of directory to open. */ + HGCMFunctionParameter path; + /** Filter string to use (wildcard style). */ + HGCMFunctionParameter filter; + /** Filter type to use when walking the directory (GSTCTLDIRFILTER). */ + HGCMFunctionParameter filter_type; + /** Directory open flags (GSTCTLDIR_F_XXX). */ + HGCMFunctionParameter flags; +} HGCMMsgDirOpen; + +/** + * Closes a guest directory. + */ +typedef struct HGCMMsgDirClose +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Directory handle to close. */ + HGCMFunctionParameter handle; +} HGCMMsgDirClose; + +/** + * Reads the next entry of a guest directory. + */ +typedef struct HGCMMsgDirRead +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Handle of directory listing to read the next entry for. */ + HGCMFunctionParameter handle; + /** Custom directory entry size (in bytes) to use. */ + HGCMFunctionParameter entry_size; + /** Additional directory attributes to use (GSTCTLFSOBJATTRADD). */ + HGCMFunctionParameter add_attributes; + /** Directory reading flags. */ + HGCMFunctionParameter flags; +} HGCMMsgDirRead; + +/** + * Rewinds the listing of a guest directory. + */ +typedef struct HGCMMsgDirRewind +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Handle of directory listing to rewind. */ + HGCMFunctionParameter handle; +} HGCMMsgDirRewind; + +/** + * Creates a directory on the guest. + */ +typedef struct HGCMMsgDirCreate +{ + VBGLIOCHGCMCALL hdr; + /** Context ID. */ + HGCMFunctionParameter context; + /** Path of directory to create. */ + HGCMFunctionParameter path; + /** Creation mode. */ + HGCMFunctionParameter mode; + /** Creation flags (GSTCTL_CREATEDIRECTORY_F_XXX). */ + HGCMFunctionParameter flags; +} HGCMMsgDirCreate; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + typedef struct HGCMMsgPathRename { VBGLIOCHGCMCALL hdr; @@ -1213,6 +1445,20 @@ typedef struct HGCMMsgFileSetSize HGCMFunctionParameter cb64NewSize; } HGCMMsgFileSetSize; +/** + * Removes (deletes) a guest file. + * + * @since 7.1 + */ +typedef struct HGCMMsgFileRemove +{ + VBGLIOCHGCMCALL hdr; + /** UInt32: Context ID. */ + HGCMFunctionParameter context; + /** File to open. */ + HGCMFunctionParameter filename; +} HGCMMsgFileRemove; + /****************************************************************************** * HGCM replies from the guest. These are handled in Main's low-level HGCM * @@ -1277,37 +1523,92 @@ typedef struct HGCMReplyFileNotify typedef struct HGCMReplyDirNotify { - VBGLIOCHGCMCALL hdr; - /** Context ID. */ - HGCMFunctionParameter context; - /** Notification type. */ - HGCMFunctionParameter type; - /** IPRT result of overall operation. */ - HGCMFunctionParameter rc; + /** The generic reply header. */ + HGCMReplyHdr reply_hdr; + /** Union based on \a reply_hdr.type. */ union { - struct - { - /** Directory information. */ - HGCMFunctionParameter objInfo; - } info; + /** + * Parameters used for \a type GUEST_DIR_NOTIFYTYPE_OPEN. + * + * @since 7.1 + */ struct { /** Guest directory handle. */ HGCMFunctionParameter handle; } open; + /** + * Parameters used for \a type GUEST_DIR_NOTIFYTYPE_READ. + * + * @since 7.1 + */ struct { - /** Current read directory entry. */ + /** Current read directory entry (GSTCTLDIRENTRYEX). */ HGCMFunctionParameter entry; - /** Extended entry object information. Optional. */ - HGCMFunctionParameter objInfo; + /** Resolved user ID as a string (uid). */ + HGCMFunctionParameter user; + /** Resolved group IDs as a string. + * + * Multiple groups are delimited by "\r\n", whereas + * the first group always is the primary group. */ + HGCMFunctionParameter groups; + /** @todo ACL; not implemented yet. + * Windows ACL, defined in SDDL. */ + HGCMFunctionParameter acl; } read; } u; } HGCMReplyDirNotify; +/** + * Reply to a HOST_MSG_FS_QUERY_INFO or HOST_MSG_FS_CREATE_TEMP message. + * + * @since 7.1 + */ +typedef struct HGCMReplyFsNotify +{ + /** The generic reply header. */ + HGCMReplyHdr reply_hdr; + /** Union based on \a reply_hdr.type. */ + union + { + /** + * Parameters used for \a type GUEST_FS_NOTIFYTYPE_QUERY_INFO. + * + * @since 7.1 + */ + struct + { + /** File system object information (GSTCTLFSOBJINFO). */ + HGCMFunctionParameter obj_info; + /** Resolved user ID as a string (uid). */ + HGCMFunctionParameter user; + /** Resolved group IDs as a string. + * + * Multiple groups are delimited by "\r\n", whereas + * the first group always is the primary group. */ + HGCMFunctionParameter groups; + /** @todo ACL; not implemented yet. + * Windows ACL, defined in SDDL. */ + HGCMFunctionParameter acl; + } queryinfo; + /** + * Parameters used for \a type GUEST_FS_NOTIFYTYPE_CREATE_TEMP. + * + * @since 7.1 + */ + struct + { + /** The create temporary file / directory when \a rc + * indicates success. */ + HGCMFunctionParameter path; + } createtemp; + } u; +} HGCMReplyFsNotify; #pragma pack () + /****************************************************************************** * Callback data structures. * ******************************************************************************/ @@ -1420,10 +1721,8 @@ typedef struct CALLBACKDATA_DIR_NOTIFY { struct { - /** Size (in bytes) of directory information. */ - uint32_t cbObjInfo; /** Pointer to directory information. */ - void *pvObjInfo; + PGSTCTLFSOBJINFO pObjInfo; } info; struct { @@ -1434,13 +1733,9 @@ typedef struct CALLBACKDATA_DIR_NOTIFY struct { /** Size (in bytes) of directory entry information. */ - uint32_t cbEntry; + uint32_t cbEntry; /** Pointer to directory entry information. */ - void *pvEntry; - /** Size (in bytes) of directory entry object information. */ - uint32_t cbObjInfo; - /** Pointer to directory entry object information. */ - void *pvObjInfo; + GSTCTLDIRENTRYEX *pEntry; } read; } u; } CALLBACKDATA_DIR_NOTIFY, *PCALLBACKDATA_DIR_NOTIFY; @@ -1494,6 +1789,19 @@ typedef struct CALLBACKDATA_FILE_NOTIFY } u; } CALLBACKDATA_FILE_NOTIFY, *PCALLBACKDATA_FILE_NOTIFY; + +/******************************************************************************* +* Payload structures for the generic reply message (HGCMMsgReply). * +* * +* The name suffix must match the host command name, e.g. * +* Host command HOST_MSG_FOO_BAR -> REPLY_PAYLOAD_FOO_BAR * +*******************************************************************************/ + +typedef struct REPLY_PAYLOAD_FS_QUERY_INFO +{ + GSTCTLFSOBJINFO objInfo; + +} REPLY_PAYLOAD_FS_QUERY_INFO; } /* namespace guestControl */ #endif /* !VBOX_INCLUDED_HostServices_GuestControlSvc_h */ diff --git a/include/VBox/VBoxGuestLib.h b/include/VBox/VBoxGuestLib.h index 72799ced42a..8d52e9fc886 100644 --- a/include/VBox/VBoxGuestLib.h +++ b/include/VBox/VBoxGuestLib.h @@ -40,6 +40,10 @@ # include <VBox/GuestHost/DragAndDrop.h> # include <VBox/GuestHost/DragAndDropDefs.h> # endif +# ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +# include <VBox/GuestHost/GuestControl.h> + using namespace guestControl; +# endif # ifdef VBOX_WITH_SHARED_CLIPBOARD # include <VBox/GuestHost/SharedClipboard.h> # ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS @@ -1078,11 +1082,21 @@ VBGLR3DECL(int) VbglR3GuestCtrlSessionClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_ VBGLR3DECL(int) VbglR3GuestCtrlSessionNotify(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uType, int32_t iResult); VBGLR3DECL(int) VbglR3GuestCtrlSessionGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, PVBGLR3GUESTCTRLSESSIONSTARTUPINFO *ppStartupInfo); VBGLR3DECL(int) VbglR3GuestCtrlSessionGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfFlags, uint32_t *pidSession); +VBGLR3DECL(int) VbglR3GuestCtrlFileGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle); +VBGLR3DECL(int) VbglR3GuestCtrlFileGetRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint32_t *puToRead); /* Guest path handling. */ VBGLR3DECL(int) VbglR3GuestCtrlPathGetRename(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszSource, uint32_t cbSource, char *pszDest, uint32_t cbDest, uint32_t *pfFlags); VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserDocuments(PVBGLR3GUESTCTRLCMDCTX pCtx); VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserHome(PVBGLR3GUESTCTRLCMDCTX pCtx); +# ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** @name Guest Control file system functions. + * @{ + */ +VBGLR3DECL(int) VbglR3GuestCtrlFsGetQueryInfo(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszPath, uint32_t cbPath, GSTCTLFSOBJATTRADD *penmAddAttrib, uint32_t *pfFlags); +VBGLR3DECL(int) VbglR3GuestCtrlFsGetCreateTemp(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszTemplate, uint32_t cbTemplate, char *pszPath, uint32_t cbPath, uint32_t *pfFlags, uint32_t *pfMode); +/** @} */ +# endif VBGLR3DECL(int) VbglR3GuestCtrlGetShutdown(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfAction); /* Guest process execution. */ VBGLR3DECL(int) VbglR3GuestCtrlProcStartupInfoInit(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo); @@ -1098,6 +1112,17 @@ VBGLR3DECL(int) VbglR3GuestCtrlProcGetOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32 VBGLR3DECL(int) VbglR3GuestCtrlProcGetWaitFor(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puPID, uint32_t *puWaitFlags, uint32_t *puTimeoutMS); /* Guest native directory handling. */ +# ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** @name Guest Control directory functions. + * @{ + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetCreate(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszPath, uint32_t cbPath, uint32_t *pfMode, uint32_t *pfFlags); +VBGLR3DECL(int) VbglR3GuestCtrlDirGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszPath, uint32_t cbPath, uint32_t *pfFlags, GSTCTLDIRFILTER *penmFilter); +VBGLR3DECL(int) VbglR3GuestCtrlDirGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle); +VBGLR3DECL(int) VbglR3GuestCtrlDirGetRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint32_t *pcbDirEntry, uint32_t *penmAddAttrib, uint32_t *pfFlags); +VBGLR3DECL(int) VbglR3GuestCtrlDirGetRewind(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle); +/** @} */ +# endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ VBGLR3DECL(int) VbglR3GuestCtrlDirGetRemove(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszPath, uint32_t cbPath, uint32_t *pfFlags); /* Guest native file handling. */ VBGLR3DECL(int) VbglR3GuestCtrlFileGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszFileName, uint32_t cbFileName, char *pszOpenMode, @@ -1115,28 +1140,48 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uint32_t *puSeekMethod, uint64_t *poffSeek); VBGLR3DECL(int) VbglR3GuestCtrlFileGetTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle); VBGLR3DECL(int) VbglR3GuestCtrlFileGetSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint64_t *pcbNew); +# ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +VBGLR3DECL(int) VbglR3GuestCtrlFileGetRemove(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszFileName, uint32_t cbFileName); +# endif /* Guest -> Host. */ +# ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** @name Guest Control directory callbacks. + * @{ + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirCbOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t uFileHandle); +VBGLR3DECL(int) VbglR3GuestCtrlDirCbClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc); +VBGLR3DECL(int) VbglR3GuestCtrlDirCbReadEx(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLDIRENTRYEX pEntry, uint32_t cbSize, char *pszUser, char *pszGroups, void *pvACL, size_t cbACL); +VBGLR3DECL(int) VbglR3GuestCtrlDirCbRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLDIRENTRYEX pEntry, uint32_t cbSize); +VBGLR3DECL(int) VbglR3GuestCtrlDirCbRewind(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc); +/** @} */ +# endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + VBGLR3DECL(int) VbglR3GuestCtrlFileCbOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t uFileHandle); VBGLR3DECL(int) VbglR3GuestCtrlFileCbClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc); VBGLR3DECL(int) VbglR3GuestCtrlFileCbError(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc); VBGLR3DECL(int) VbglR3GuestCtrlFileCbRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, void *pvData, uint32_t cbData); -VBGLR3DECL(int) VbglR3GuestCtrlFileCbReadOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, - void *pvData, uint32_t cbData, int64_t offNew); +VBGLR3DECL(int) VbglR3GuestCtrlFileCbReadOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, void *pvData, uint32_t cbData, int64_t offNew); VBGLR3DECL(int) VbglR3GuestCtrlFileCbWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten); VBGLR3DECL(int) VbglR3GuestCtrlFileCbWriteOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten, int64_t offNew); VBGLR3DECL(int) VbglR3GuestCtrlFileCbSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent); VBGLR3DECL(int) VbglR3GuestCtrlFileCbTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent); VBGLR3DECL(int) VbglR3GuestCtrlFileCbSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t cbNew); -VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatus(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uPID, uint32_t uStatus, uint32_t fFlags, - void *pvData, uint32_t cbData); -VBGLR3DECL(int) VbglR3GuestCtrlProcCbOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uPID, uint32_t uHandle, uint32_t fFlags, - void *pvData, uint32_t cbData); -VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatusInput(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t u32PID, uint32_t uStatus, - uint32_t fFlags, uint32_t cbWritten); -/** @} */ +# ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** @name Guest Control file system callbacks. + * @{ + */ +VBGLR3DECL(int) VbglR3GuestCtrlFsCbQueryInfoEx(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLFSOBJINFO pObjInfo, char *pszUser, char *pszGroups, void *pvACL, uint32_t cbACL); +VBGLR3DECL(int) VbglR3GuestCtrlFsCbQueryInfo(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLFSOBJINFO pObjInfo); +VBGLR3DECL(int) VbglR3GuestCtrlFsCbCreateTemp(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, const char *pszPath); +/** @} */ +# endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatus(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uPID, uint32_t uStatus, uint32_t fFlags, void *pvData, uint32_t cbData); +VBGLR3DECL(int) VbglR3GuestCtrlProcCbOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uPID, uint32_t uHandle, uint32_t fFlags, void *pvData, uint32_t cbData); +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatusInput(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t u32PID, uint32_t uStatus, uint32_t fFlags, uint32_t cbWritten); # endif /* VBOX_WITH_GUEST_CONTROL defined */ /** @name Auto-logon handling diff --git a/include/VBox/log.h b/include/VBox/log.h index c77f2809851..9648e18368d 100644 --- a/include/VBox/log.h +++ b/include/VBox/log.h @@ -490,6 +490,8 @@ typedef enum VBOXLOGGROUP LOG_GROUP_MAIN_GUESTDEBUGCONTROL, /** Main group, IGuestDirectory. */ LOG_GROUP_MAIN_GUESTDIRECTORY, + /** Main group, IGuestDirectoryEvent. */ + LOG_GROUP_MAIN_GUESTDIRECTORYEVENT, /** Main group, IGuestDnDSource. */ LOG_GROUP_MAIN_GUESTDNDSOURCE, /** Main group, IGuestDnDTarget. */ @@ -1062,6 +1064,7 @@ typedef enum VBOXLOGGROUP "MAIN_GUEST", \ "MAIN_GUESTDEBUGCONTROL", \ "MAIN_GUESTDIRECTORY", \ + "MAIN_GUESTDIRECTORYEVENT", \ "MAIN_GUESTDNDSOURCE", \ "MAIN_GUESTDNDTARGET", \ "MAIN_GUESTERRORINFO", \ diff --git a/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk index 3949e15d67c..288d3ff8930 100644 --- a/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk +++ b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk @@ -120,7 +120,8 @@ VBoxGuestR3Lib_DEFS = \ $(if $(VBOX_WITH_SHARED_CLIPBOARD),VBOX_WITH_SHARED_CLIPBOARD,) \ $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) \ $(if $(VBOX_WITH_SHARED_FOLDERS),VBOX_WITH_SHARED_FOLDERS,) \ - $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,) + $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,) \ + $(if $(VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS),VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS,) VBoxGuestR3Lib_SOURCES = \ VBoxGuestR3Lib.cpp \ VBoxGuestR3LibAdditions.cpp \ diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp index dacf4b098a2..a012ca52cfc 100644 --- a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp @@ -979,6 +979,163 @@ VBGLR3DECL(int) VbglR3GuestCtrlSessionGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Retrieves a HOST_MSG_DIR_OPEN message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param pszPath Where to return the directory path to open. + * @param cbPath Size (in bytes) of \a pszPath. + * @param pfFlags Where to return the directory listing flags. + * @param enmFilter Where to return the directory filter type. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszPath, uint32_t cbPath, uint32_t *pfFlags, + GSTCTLDIRFILTER *penmFilter) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cbPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + AssertPtrReturn(penmFilter, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgDirOpen Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_OPEN); + VbglHGCMParmPtrSet(&Msg.path, pszPath, cbPath); + VbglHGCMParmUInt64Set(&Msg.flags, 0); + VbglHGCMParmUInt64Set(&Msg.filter, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + Msg.filter.GetUInt32((uint32_t *)penmFilter); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_MSG_DIR_CLOSE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param puHandle Where to return the handle of the guest directory to close. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgDirClose Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_CLOSE); + VbglHGCMParmUInt64Set(&Msg.handle, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_MSG_DIR_READ message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param puHandle Where to return the directory handle to rewind. + * @param pcbDirEntry Where to return the directory entry size. + * @param penmAddAttrib Where to return the additional attributes enumeration. + * @param pfFlags Where to return the directory reading flags.. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint32_t *pcbDirEntry, + uint32_t *penmAddAttrib, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pcbDirEntry, VERR_INVALID_POINTER); + AssertPtrReturn(penmAddAttrib, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgDirRead Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_READ); + VbglHGCMParmUInt64Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.entry_size, 0); + VbglHGCMParmUInt32Set(&Msg.add_attributes, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.entry_size.GetUInt32(pcbDirEntry); + Msg.add_attributes.GetUInt32(penmAddAttrib); + Msg.flags.GetUInt32(pfFlags); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_MSG_DIR_REWIND message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param puHandle Where to return the directory handle to rewind. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetRewind(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgDirRewind Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_REWIND); + VbglHGCMParmUInt64Set(&Msg.handle, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + /** * Retrieves a HOST_PATH_RENAME message. */ @@ -1063,6 +1220,103 @@ VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserHome(PVBGLR3GUESTCTRLCMDCTX pCtx) return rc; } + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Retrieves a HOST_MSG_FS_QUERY_INFO message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param pszPath Where to return the path of the file system object to query. + * @param cbPath Size (in bytes) of \a pszPath. + * @param penmAddAttrib Where to return the additional attributes enumeration. + * @param pfFlags Where to return the flags for . + */ +VBGLR3DECL(int) VbglR3GuestCtrlFsGetQueryInfo(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszPath, uint32_t cbPath, GSTCTLFSOBJATTRADD *penmAddAttrib, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cbPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(penmAddAttrib, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFsQueryInfo Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FS_QUERY_INFO); + VbglHGCMParmPtrSet(&Msg.path, pszPath, cbPath); + VbglHGCMParmUInt32Set(&Msg.add_attributes, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.add_attributes.GetUInt32((uint32_t *)penmAddAttrib); + Msg.flags.GetUInt32(pfFlags); + } + + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_MSG_FS_CREATE_TEMP message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param pszTemplate Where to return the template name. + * @param cbTemplate Size (in bytes) of \a pszTemplate. + * @param pszPath Where to return the temporary directory path. + * @param cbTemplate Size (in bytes) of \a pszPath. + * @param pfFlags Where to return the creation flags. + * @param pfMode Where to return the creation mode. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFsGetCreateTemp(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszTemplate, uint32_t cbTemplate, char *pszPath, uint32_t cbPath, + uint32_t *pfFlags, uint32_t *pfMode) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszTemplate, VERR_INVALID_POINTER); + AssertReturn(cbTemplate, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cbPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + AssertPtrReturn(pfMode, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFsCreateTemp Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FS_CREATE_TEMP); + VbglHGCMParmPtrSet(&Msg.template_name, pszTemplate, cbTemplate); + VbglHGCMParmPtrSet(&Msg.tmpdir, pszPath, cbPath); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmUInt32Set(&Msg.mode, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + Msg.mode.GetUInt32(pfMode); + } + + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + /** * Retrieves a HOST_MSG_SHUTDOWN message. * @@ -1468,6 +1722,51 @@ VBGLR3DECL(int) VbglR3GuestCtrlProcGetInput(PVBGLR3GUESTCTRLCMDCTX pCtx, } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Retrieves a HOST_MSG_DIR_CREATE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param pszPath Where to return the path. + * @param cbPath Size (in bytes) of \a pszPath. + * @param pfMode Where to return the creation mode. + * @param pfFlags Where to return the creation flags (GSTCTL_CREATEDIRECTORY_F_XXX). + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetCreate(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszPath, uint32_t cbPath, uint32_t *pfMode, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cbPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + AssertPtrReturn(pfMode, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgDirCreate Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_CREATE); + VbglHGCMParmPtrSet(&Msg.path, pszPath, cbPath); + VbglHGCMParmUInt32Set(&Msg.mode, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + Msg.mode.GetUInt32(pfMode); + } + + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + /** * Retrieves a HOST_DIR_REMOVE message. */ @@ -1827,6 +2126,34 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint3 } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +VBGLR3DECL(int) VbglR3GuestCtrlFileGetRemove(PVBGLR3GUESTCTRLCMDCTX pCtx, char *pszFileName, uint32_t cbFileName) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); + AssertReturn(cbFileName, VERR_INVALID_PARAMETER); + + int rc; + do + { + HGCMMsgFileRemove Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_REMOVE); + VbglHGCMParmPtrSet(&Msg.filename, pszFileName, cbFileName); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + /** * Retrieves a HOST_EXEC_TERMINATE message. */ @@ -1890,6 +2217,147 @@ VBGLR3DECL(int) VbglR3GuestCtrlProcGetWaitFor(PVBGLR3GUESTCTRLCMDCTX pCtx, } +/********************************************************************************************************************************* + * Directory callbacks * + ********************************************************************************************************************************/ + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Replies to a HOST_MSG_DIR_OPEN message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param uDirHandle Directory handle of opened directory. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirCbOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t uDirHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyDirNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.reply_hdr.hdr, pCtx->uClientID, GUEST_MSG_DIR_NOTIFY, + RT_SUCCESS((int)uRc) ? GSTCTL_HGCM_REPLY_HDR_PARMS + 1 : GSTCTL_HGCM_REPLY_HDR_PARMS); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.type, GUEST_DIR_NOTIFYTYPE_OPEN); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.rc, uRc); + + if (RT_SUCCESS((int)uRc)) + VbglHGCMParmUInt32Set(&Msg.u.open.handle, uDirHandle); + + return VbglR3HGCMCall(&Msg.reply_hdr.hdr, RT_UOFFSET_AFTER(HGCMReplyDirNotify, u.open)); +} + + +/** + * Replies to a HOST_MSG_DIR_CLOSE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirCbClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyDirNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.reply_hdr.hdr, pCtx->uClientID, GUEST_MSG_DIR_NOTIFY, GSTCTL_HGCM_REPLY_HDR_PARMS); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.type, GUEST_DIR_NOTIFYTYPE_CLOSE); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.rc, uRc); + + return VbglR3HGCMCall(&Msg.reply_hdr.hdr, RT_UOFFSET_AFTER(HGCMReplyDirNotify, u)); +} + + +/** + * Replies to a HOST_MSG_DIR_READ message, extended version. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pEntry Directory entry to send. + * @param cbSize Size (in bytes) of \a pDirEntry to send. + * This might be needed as the size can be bigger than GSTCTLDIRENTRYEX. + * See RTDirReadEx() for more information. + * @param pszUser Associated user ID (owner, uid) as a string. + * @param pszGroups Associated user groups as a string. + * Multiple groups are delimited by "\r\n", whereas the first group always is the primary group. + * @param pvACL ACL block to send. + * @param cbACL Size (in bytes) of ACL block to send. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirCbReadEx(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLDIRENTRYEX pEntry, uint32_t cbSize, + char *pszUser, char *pszGroups, void *pvACL, size_t cbACL) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(RT_FAILURE((int)uRc) || pszUser, VERR_INVALID_POINTER); + AssertReturn(RT_FAILURE((int)uRc) || pszGroups, VERR_INVALID_POINTER); + AssertReturn(RT_FAILURE((int)uRc) || pvACL, VERR_INVALID_POINTER); + AssertReturn(RT_FAILURE((int)uRc) || cbACL, VERR_INVALID_PARAMETER); + + HGCMReplyDirNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.reply_hdr.hdr, pCtx->uClientID, GUEST_MSG_DIR_NOTIFY, + RT_SUCCESS((int)uRc) ? GSTCTL_HGCM_REPLY_HDR_PARMS + 4 : GSTCTL_HGCM_REPLY_HDR_PARMS); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.type, GUEST_DIR_NOTIFYTYPE_READ); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.rc, uRc); + + if (RT_SUCCESS((int)uRc)) + { + VbglHGCMParmPtrSet(&Msg.u.read.entry, pEntry, cbSize); + VbglHGCMParmPtrSetString(&Msg.u.read.user, pszUser); + VbglHGCMParmPtrSetString(&Msg.u.read.groups, pszGroups); + VbglHGCMParmPtrSet (&Msg.u.read.acl, pvACL, cbACL); + } + + return VbglR3HGCMCall(&Msg.reply_hdr.hdr, RT_UOFFSET_AFTER(HGCMReplyDirNotify, u.read)); +} + + +/** + * Replies to a HOST_MSG_DIR_READ message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pEntry Directory entry to send. + * @param cbSize Size (in bytes) of \a pDirEntry to send. + * This might be needed as the size can be bigger than GSTCTLDIRENTRYEX. + * See RTDirReadEx() for more information. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirCbRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLDIRENTRYEX pEntry, uint32_t cbSize) +{ + char szIgnored[1]; + return VbglR3GuestCtrlDirCbReadEx(pCtx, uRc, pEntry, cbSize, szIgnored /* pszUser */, szIgnored /* pszGroups */, + szIgnored /* pvACL */, sizeof(szIgnored) /* cbACL */); +} + + +/** + * Replies to a HOST_MSG_DIR_REWIND message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirCbRewind(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyDirNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.reply_hdr.hdr, pCtx->uClientID, GUEST_MSG_DIR_NOTIFY, GSTCTL_HGCM_REPLY_HDR_PARMS); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.type, GUEST_DIR_NOTIFYTYPE_REWIND); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.rc, uRc); + + return VbglR3HGCMCall(&Msg.reply_hdr.hdr, RT_UOFFSET_AFTER(HGCMReplyDirNotify, u)); +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + +/********************************************************************************************************************************* + * File callbacks * + ********************************************************************************************************************************/ + /** * Replies to a HOST_MSG_FILE_OPEN message. * @@ -2127,6 +2595,94 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileCbSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32 } +/********************************************************************************************************************************* + * File system callbacks * + ********************************************************************************************************************************/ + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Replies to a HOST_MSG_FS_QUERY_INFO message, extended version. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pFsObjInfo Guest file system object information to send. + * @param pszUser Associated user ID (owner, uid) as a string. + * @param pszGroups Associated user groups as a string. + * Multiple groups are delimited by "\r\n", whereas the first group always is the primary group. + * @param pvACL ACL block to send. + * @param cbACL Size (in bytes) of ACL block to send. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFsCbQueryInfoEx(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLFSOBJINFO pFsObjInfo, + char *pszUser, char *pszGroups, void *pvACL, uint32_t cbACL) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pFsObjInfo, VERR_INVALID_POINTER); + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + AssertPtrReturn(pszGroups, VERR_INVALID_POINTER); + AssertPtrReturn(pvACL, VERR_INVALID_POINTER); + AssertReturn(cbACL, VERR_INVALID_PARAMETER); + + HGCMReplyFsNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.reply_hdr.hdr, pCtx->uClientID, GUEST_MSG_FS_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.type, GUEST_FS_NOTIFYTYPE_QUERY_INFO); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.rc, uRc); + VbglHGCMParmPtrSet (&Msg.u.queryinfo.obj_info, pFsObjInfo, sizeof(GSTCTLFSOBJINFO)); + VbglHGCMParmPtrSetString(&Msg.u.queryinfo.user, pszUser); + VbglHGCMParmPtrSetString(&Msg.u.queryinfo.groups, pszGroups); + VbglHGCMParmPtrSet (&Msg.u.queryinfo.acl, pvACL, cbACL); + + return VbglR3HGCMCall(&Msg.reply_hdr.hdr, RT_UOFFSET_AFTER(HGCMReplyDirNotify, u.read)); +} + + +/** + * Replies to a HOST_MSG_FS_QUERY_INFO message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pFsObjInfo Guest file system object information to send. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFsCbQueryInfo(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, PGSTCTLFSOBJINFO pFsObjInfo) +{ + char szIgnored[1]; + return VbglR3GuestCtrlFsCbQueryInfoEx(pCtx, uRc, pFsObjInfo, szIgnored /* pszUser */, szIgnored /* pszGroups */, + szIgnored /* pvACL */, sizeof(szIgnored) /* cbACL */); +} + + +/** + * Replies to a HOST_MSG_FS_CREATE_TEMP message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pszPath Path of created temporary file / directory, if \a uRc marks a success. + * Specify an empty path on failure -- NULL is not allowed! + */ +VBGLR3DECL(int) VbglR3GuestCtrlFsCbCreateTemp(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, const char *pszPath) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + + HGCMReplyFsNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.reply_hdr.hdr, pCtx->uClientID, GUEST_MSG_FS_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.type, GUEST_FS_NOTIFYTYPE_CREATE_TEMP); + VbglHGCMParmUInt32Set(&Msg.reply_hdr.rc, uRc); + VbglHGCMParmPtrSetString(&Msg.u.createtemp.path, pszPath); + + return VbglR3HGCMCall(&Msg.reply_hdr.hdr, RT_UOFFSET_AFTER(HGCMReplyFsNotify, u.createtemp)); +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + +/********************************************************************************************************************************* + * Process callbacks * + ********************************************************************************************************************************/ + /** * Callback for reporting a guest process status (along with some other stuff) to the host. * diff --git a/src/VBox/Additions/common/VBoxService/Makefile.kmk b/src/VBox/Additions/common/VBoxService/Makefile.kmk index c21854aaa04..e84385c6b59 100644 --- a/src/VBox/Additions/common/VBoxService/Makefile.kmk +++ b/src/VBox/Additions/common/VBoxService/Makefile.kmk @@ -47,7 +47,9 @@ PROGRAMS += VBoxService VBOX_WITH_VBOXSERVICE_TIMESYNC := 1 # Busybox-like toolbox, embedded into VBoxService. -VBOX_WITH_VBOXSERVICE_TOOLBOX := 1 +ifndef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + VBOX_WITH_VBOXSERVICE_TOOLBOX := 1 +endif # VM-management functions, like memory ballooning and statistics. VBOX_WITH_VBOXSERVICE_MANAGEMENT := 1 @@ -100,7 +102,9 @@ VBoxService_DEFS = \ $(if $(VBOX_WITH_DBUS),VBOX_WITH_DBUS,) \ $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,) \ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ - $(if $(VBOX_WITH_HGCM),VBOX_WITH_HGCM,) + $(if $(VBOX_WITH_HGCM),VBOX_WITH_HGCM,) \ + $(if $(VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT),VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT,) \ + $(if $(VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS),VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS,) ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING VBoxService_DEFS += VBOX_BUILD_TARGET="$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)" else diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp index cfe13cfc19e..0726d408872 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp @@ -257,8 +257,10 @@ static int vgsvcGstCtrlInvalidate(void) const uint64_t fGuestFeatures = VBOX_GUESTCTRL_GF_0_SET_SIZE | VBOX_GUESTCTRL_GF_0_PROCESS_ARGV0 | VBOX_GUESTCTRL_GF_0_PROCESS_DYNAMIC_SIZES +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + | VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ | VBOX_GUESTCTRL_GF_0_SHUTDOWN; - rc = VbglR3GuestCtrlReportFeatures(g_idControlSvcClient, fGuestFeatures, &g_fControlHostFeatures0); if (RT_SUCCESS(rc)) VGSvcVerbose(3, "Host features: %#RX64\n", g_fControlHostFeatures0); diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h index 445a3923171..eb308afa839 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h @@ -57,13 +57,37 @@ typedef enum VBOXSERVICECTRLPIPEID VBOXSERVICECTRLPIPEID_IPC_NOTIFY = 100 } VBOXSERVICECTRLPIPEID; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Structure for one (opened) guest directory. + */ +typedef struct VBOXSERVICECTRLDIR +{ + /** Pointer to list archor of following list node. + * @todo Would be nice to have a RTListGetAnchor(). */ + PRTLISTANCHOR pAnchor; + /** Node to global guest control directory list. */ + /** @todo Use a map later? */ + RTLISTNODE Node; + /** The (absolute) directory path. */ + char *pszPathAbs; + /** The directory handle on the guest. */ + RTDIR hDir; + /** Directory handle to identify this directory. */ + uint32_t uHandle; + /** Context ID. */ + uint32_t uContextID; +} VBOXSERVICECTRLDIR; +/** Pointer to a guest directory. */ +typedef VBOXSERVICECTRLDIR *PVBOXSERVICECTRLDIR; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + /** * Structure for one (opened) guest file. */ typedef struct VBOXSERVICECTRLFILE { - /** Pointer to list archor of following - * list node. + /** Pointer to list archor of following list node. * @todo Would be nice to have a RTListGetAnchor(). */ PRTLISTANCHOR pAnchor; /** Node to global guest control file list. */ @@ -80,7 +104,7 @@ typedef struct VBOXSERVICECTRLFILE /** RTFILE_O_XXX flags. */ uint64_t fOpen; } VBOXSERVICECTRLFILE; -/** Pointer to thread data. */ +/** Pointer to a guest file. */ typedef VBOXSERVICECTRLFILE *PVBOXSERVICECTRLFILE; /** @@ -176,9 +200,15 @@ typedef struct VBOXSERVICECTRLSESSION RTLISTANCHOR lstProcesses; /** Number of guest processes in the process list. */ uint32_t cProcesses; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + /** List of guest control files (VBOXSERVICECTRLDIR). */ + RTLISTANCHOR lstDirs; + /** Number of guest directories in \a lstDirs. */ + uint32_t cDirs; +#endif /** List of guest control files (VBOXSERVICECTRLFILE). */ RTLISTANCHOR lstFiles; - /** Number of guest files in the file list. */ + /** Number of guest files in \a lstFiles. */ uint32_t cFiles; /** The session's critical section. */ RTCRITSECT CritSect; diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp index 67be297b9f8..106ec54855e 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp @@ -100,6 +100,77 @@ static bool vgsvcGstCtrlSessionGrowScratchBuf(void **ppvScratchBuf, uint32_t *pc } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Free's a guest directory entry. + * + * @returns VBox status code. + * @param pDir Directory entry to free. + * The pointer will be invalid on success. + */ +static int vgsvcGstCtrlSessionDirFree(PVBOXSERVICECTRLDIR pDir) +{ + if (!pDir) + return VINF_SUCCESS; + + int rc; + if (pDir->hDir != NIL_RTDIR) + { + rc = RTDirClose(pDir->hDir); + pDir->hDir = NIL_RTDIR; + } + else + rc = VINF_SUCCESS; + + if (RT_SUCCESS(rc)) + { + RTStrFree(pDir->pszPathAbs); + RTListNodeRemove(&pDir->Node); + RTMemFree(pDir); + } + + return rc; +} + + +/** + * Acquires an internal guest directory. + * + * Must be released via vgsvcGstCtrlSessionDirRelease(). + * + * @returns VBox status code. + * @param pSession Guest control session to acquire guest directory for. + * @param uHandle Handle of directory to acquire. + * + * @note No locking done yet. + */ +static PVBOXSERVICECTRLDIR vgsvcGstCtrlSessionDirAcquire(const PVBOXSERVICECTRLSESSION pSession, uint32_t uHandle) +{ + AssertPtrReturn(pSession, NULL); + + /** @todo Use a map later! */ + PVBOXSERVICECTRLDIR pDirCur; + RTListForEach(&pSession->lstDirs, pDirCur, VBOXSERVICECTRLDIR, Node) + { + if (pDirCur->uHandle == uHandle) + return pDirCur; + } + + return NULL; +} + + +/** + * Releases a formerly acquired guest directory. + * + * @param pDir Directory to release. + */ +static void vgsvcGstCtrlSessionDirRelease(PVBOXSERVICECTRLDIR pDir) +{ + RT_NOREF(pDir); +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + static int vgsvcGstCtrlSessionFileFree(PVBOXSERVICECTRLFILE pFile) { @@ -389,7 +460,7 @@ static int vgsvcGstCtrlSessionHandleFileOpen(PVBOXSERVICECTRLSESSION pSession, P } else { - VGSvcError("[File %s] empty filename!\n", szFile); + VGSvcError("Opening file failed: Empty filename!\n"); rc = VERR_INVALID_NAME; } @@ -901,6 +972,330 @@ static int vgsvcGstCtrlSessionHandleFileSetSize(const PVBOXSERVICECTRLSESSION pS } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +static int vgsvcGstCtrlSessionHandleFileRemove(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + /* + * Retrieve the request. + */ + char szPath[RTPATH_MAX]; + int rc = VbglR3GuestCtrlFileGetRemove(pHostCtx, szPath, sizeof(szPath)); + if (RT_SUCCESS(rc)) + { + VGSvcVerbose(4, "Deleting file szPath=%s\n", szPath); + rc = RTFileDelete(szPath); + + /* + * Report result back to host. + */ + int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc); + if (RT_FAILURE(rc2)) + { + VGSvcError("Failed to report file deletion status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + else + { + VGSvcError("Error fetching parameters for file deletion operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + VGSvcVerbose(5, "Deleting file returned rc=%Rrc\n", rc); + return rc; +} + + +static int vgsvcGstCtrlSessionHandleDirOpen(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + char szPath[RTPATH_MAX]; + uint32_t fFlags; + GSTCTLDIRFILTER enmFilter; + uint32_t uHandle = 0; + int rc = VbglR3GuestCtrlDirGetOpen(pHostCtx, szPath, sizeof(szPath), &fFlags, &enmFilter); + VGSvcVerbose(4, "[Dir %s]: fFlags=%#x, enmFilter=%#x, rc=%Rrc\n", szPath, enmFilter, rc); + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLDIR pDir = (PVBOXSERVICECTRLDIR)RTMemAllocZ(sizeof(VBOXSERVICECTRLDIR)); + AssertPtrReturn(pDir, VERR_NO_MEMORY); + pDir->hDir = NIL_RTDIR; /* Not zero or NULL! */ + if (szPath[0]) + { + pDir->pszPathAbs = RTStrDup(szPath); + if (!pDir->pszPathAbs) + rc = VERR_NO_MEMORY; + + if (RT_SUCCESS(rc)) + { + rc = RTDirOpenFiltered(&pDir->hDir, pDir->pszPathAbs, (RTDIRFILTER)enmFilter, fFlags); + if (RT_SUCCESS(rc)) + { + uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHostCtx->uContextID); + pDir->uHandle = uHandle; + RTListAppend(&pSession->lstDirs, &pDir->Node); + VGSvcVerbose(2, "[Dir %s] Opened (ID=%RU32)\n", pDir->pszPathAbs, pDir->uHandle); + } + } + } + else + { + VGSvcError("Opening directory failed: Empty path!\n"); + rc = VERR_INVALID_NAME; + } + + /* Clean up if we failed. */ + if (RT_FAILURE(rc)) + { + RTStrFree(pDir->pszPathAbs); + if (pDir->hDir != NIL_RTDIR) + RTDirClose(pDir->hDir); + RTMemFree(pDir); + } + + /* + * Report result back to host. + */ + int rc2 = VbglR3GuestCtrlDirCbOpen(pHostCtx, rc, uHandle); + if (RT_FAILURE(rc2)) + { + VGSvcError("[Dir %s]: Failed to report directory open status, rc=%Rrc\n", szPath, rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + else + { + VGSvcError("Error fetching parameters for directory open operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + + VGSvcVerbose(4, "[Dir %s] Opening (flags=%#x, filter flags=%#x) returned rc=%Rrc\n", + szPath, fFlags, enmFilter, rc); + return rc; +} + + +static int vgsvcGstCtrlSessionHandleDirClose(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + /* + * Retrieve the message. + */ + uint32_t uHandle = 0; + int rc = VbglR3GuestCtrlDirGetClose(pHostCtx, &uHandle /* Dir handle to close */); + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle); + if (pDir) + { + VGSvcVerbose(2, "[Dir %s] Closing (handle=%RU32)\n", pDir ? pDir->pszPathAbs : "<Not found>", uHandle); + rc = vgsvcGstCtrlSessionDirFree(pDir); + + vgsvcGstCtrlSessionDirRelease(pDir); + } + else + { + VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle); + rc = VERR_NOT_FOUND; + } + + /* + * Report result back to host. + */ + int rc2 = VbglR3GuestCtrlDirCbClose(pHostCtx, rc); + if (RT_FAILURE(rc2)) + { + VGSvcError("Failed to report directory close status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + else + { + VGSvcError("Error fetching parameters for directory close operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + return rc; +} + + +static int vgsvcGstCtrlSessionHandleDirRead(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + /* + * Retrieve the message. + */ + uint32_t uHandle; + size_t cbDirEntry; + GSTCTLFSOBJATTRADD enmAttrAdd; + uint32_t fFlags; + GSTCTLDIRENTRYEX DirEntryEx; + int rc = VbglR3GuestCtrlDirGetRead(pHostCtx, &uHandle, (uint32_t *)&cbDirEntry, (uint32_t *)&enmAttrAdd, &fFlags); + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle); + if (pDir) + { + VGSvcVerbose(2, "[Dir %s] Reading next entry (handle=%RU32)\n", pDir ? pDir->pszPathAbs : "<Not found>", uHandle); + + /* + * For now we ASSUME that RTDIRENTRYEX == GSTCTLDIRENTRYEX, which implies that we simply can cast RTDIRENTRYEX + * to GSTCTLDIRENTRYEX. This might change in the future, however, so be extra cautious here. + * + * Ditto for RTFSOBJATTRADD == GSTCTLFSOBJATTRADD. + */ + AssertCompileSize(DirEntryEx, sizeof(RTDIRENTRYEX)); + AssertCompile (RT_OFFSETOF(GSTCTLDIRENTRYEX, Info) == RT_OFFSETOF(RTDIRENTRYEX, Info)); + AssertCompile (RT_OFFSETOF(GSTCTLDIRENTRYEX, szName) == RT_OFFSETOF(RTDIRENTRYEX, szName)); + AssertCompile (RT_OFFSETOF(GSTCTLFSOBJINFO, Attr) == RT_OFFSETOF(RTFSOBJINFO, Attr)); + + PRTDIRENTRYEX pDirEntryExRuntime = (PRTDIRENTRYEX)&DirEntryEx; + + rc = RTDirReadEx(pDir->hDir, pDirEntryExRuntime, &cbDirEntry, (RTFSOBJATTRADD)enmAttrAdd, fFlags); + + /* Paranoia. */ + AssertStmt(cbDirEntry <= _256K, rc = VERR_BUFFER_OVERFLOW); + + if (RT_SUCCESS(rc)) + { + int rc2 = VbglR3GuestCtrlDirCbRead(pHostCtx, rc, &DirEntryEx, (uint32_t)cbDirEntry); + if (RT_FAILURE(rc2)) + VGSvcError("Failed to report directory read status, rc=%Rrc\n", rc2); + } + + vgsvcGstCtrlSessionDirRelease(pDir); + } + else + { + VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle); + rc = VERR_NOT_FOUND; + } + + if (RT_FAILURE(rc)) + { + /* On failure we send a simply reply to save bandwidth. */ + int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc); + if (RT_FAILURE(rc2)) + { + VGSvcError("Failed to report directory read error %Rrc, rc=%Rrc\n", rc, rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + } + else + { + VGSvcError("Error fetching parameters for directory read operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + return rc; +} + + +static int vgsvcGstCtrlSessionHandleDirRewind(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + /* + * Retrieve the message. + */ + uint32_t uHandle = 0; + int rc = VbglR3GuestCtrlDirGetRewind(pHostCtx, &uHandle); + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLDIR pDir = vgsvcGstCtrlSessionDirAcquire(pSession, uHandle); + if (pDir) + { + VGSvcVerbose(2, "[Dir %s] Rewinding (handle=%RU32)\n", pDir ? pDir->pszPathAbs : "<Not found>", uHandle); + + rc = RTDirRewind(pDir->hDir); + + vgsvcGstCtrlSessionDirRelease(pDir); + } + else + { + VGSvcError("Directory %u (%#x) not found!\n", uHandle, uHandle); + rc = VERR_NOT_FOUND; + } + + /* + * Report result back to host. + */ + int rc2 = VbglR3GuestCtrlDirCbRewind(pHostCtx, rc); + if (RT_FAILURE(rc2)) + { + VGSvcError("Failed to report directory rewind status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + else + { + VGSvcError("Error fetching parameters for directory rewind operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + return rc; +} + + +static int vgsvcGstCtrlSessionHandleDirCreate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + /* + * Retrieve the request. + */ + char szPath[RTPATH_MAX]; + RTFMODE fMode; + uint32_t fCreate; + int rc = VbglR3GuestCtrlDirGetCreate(pHostCtx, szPath, sizeof(szPath), &fMode, &fCreate); + if (RT_SUCCESS(rc)) + { + if (!(fCreate & ~GSTCTL_CREATEDIRECTORY_F_VALID_MASK)) + { + VGSvcVerbose(4, "Creating directory (szPath='%s', fMode=%#x, fCreate=%#x), rc=%Rrc\n", szPath, fMode, fCreate, rc); + rc = RTDirCreate(szPath, fMode, fCreate); + } + else + { + VGSvcError("Invalid directory creation flags: %#x\n", fCreate); + rc = VERR_NOT_SUPPORTED; + } + + /* + * Report result back to host. + */ + int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc); + if (RT_FAILURE(rc2)) + { + VGSvcError("Failed to report directory creation status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + else + { + VGSvcError("Error fetching parameters for directory creation operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + VGSvcVerbose(5, "Creating directory returned rc=%Rrc\n", rc); + return rc; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + static int vgsvcGstCtrlSessionHandlePathRename(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) { AssertPtrReturn(pSession, VERR_INVALID_POINTER); @@ -1367,6 +1762,158 @@ static int vgsvcGstCtrlSessionHandleProcWaitFor(const PVBOXSERVICECTRLSESSION pS } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +static int vgsvcGstCtrlSessionHandleFsQueryInfo(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + /* + * Retrieve the request. + */ + char szPath[RTPATH_MAX]; + GSTCTLFSOBJATTRADD enmAttrAdd; + uint32_t fFlags; + RTFSOBJINFO objInfoRuntime; + + int rc = VbglR3GuestCtrlFsGetQueryInfo(pHostCtx, szPath, sizeof(szPath), &enmAttrAdd, &fFlags); + if (RT_SUCCESS(rc)) + { + if (!(fFlags & ~GSTCTL_QUERYINFO_F_VALID_MASK)) + { + uint32_t fFlagsRuntime = 0; + if (fFlags & GSTCTL_QUERYINFO_F_ON_LINK) + fFlagsRuntime |= RTPATH_F_ON_LINK; + if (fFlags & GSTCTL_QUERYINFO_F_FOLLOW_LINK) + fFlagsRuntime |= RTPATH_F_FOLLOW_LINK; + if (fFlags & GSTCTL_QUERYINFO_F_NO_SYMLINKS) + fFlagsRuntime |= RTPATH_F_NO_SYMLINKS; + +#define CASE_ATTR_ADD_VAL(a_Val) \ + case GSTCTL##a_Val: enmAttrRuntime = RT##a_Val; break; + + RTFSOBJATTRADD enmAttrRuntime; + switch (enmAttrAdd) + { + CASE_ATTR_ADD_VAL(FSOBJATTRADD_NOTHING); + CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX); + CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX_OWNER); + CASE_ATTR_ADD_VAL(FSOBJATTRADD_UNIX_GROUP); + CASE_ATTR_ADD_VAL(FSOBJATTRADD_EASIZE); + default: + enmAttrRuntime = RTFSOBJATTRADD_NOTHING; + break; + } + +#undef CASE_ATTR_ADD_VAL + + /* + * For now we ASSUME that RTFSOBJINFO == GSTCTLFSOBJINFO, which implies that we simply can cast RTFSOBJINFO + * to GSTCTLFSOBJINFO. This might change in the future, however, so be extra cautious here. + * + * Ditto for RTFSOBJATTR == GSTCTLFSOBJATTR. + */ + AssertCompileSize(objInfoRuntime, sizeof(GSTCTLFSOBJINFO)); + AssertCompile (RT_OFFSETOF(GSTCTLFSOBJINFO, cbObject) == RT_OFFSETOF(GSTCTLFSOBJINFO, cbObject)); + AssertCompile (RT_OFFSETOF(GSTCTLFSOBJINFO, Attr) == RT_OFFSETOF(GSTCTLFSOBJINFO, Attr)); + AssertCompileSize(RTFSOBJATTR, sizeof(GSTCTLFSOBJATTR)); + + rc = RTPathQueryInfoEx(szPath, &objInfoRuntime, enmAttrRuntime, fFlagsRuntime); + } + else + { + VGSvcError("Invalid stat flags: %#x\n", fFlags); + rc = VERR_NOT_SUPPORTED; + } + + PGSTCTLFSOBJINFO pObjInfo = (PGSTCTLFSOBJINFO)&objInfoRuntime; + + /** @todo Implement lookups! */ + char szNotImplemented[] = "<not-implemented>"; + char *pszUser = szNotImplemented; + char *pszGroups = szNotImplemented; + int rc2 = VbglR3GuestCtrlFsCbQueryInfoEx(pHostCtx, rc, pObjInfo, pszUser, pszGroups, + szNotImplemented, sizeof(szNotImplemented)); + if (RT_FAILURE(rc2)) + { + VGSvcError("Failed to reply to fsqueryinfo request %Rrc, rc=%Rrc\n", rc, rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + else + { + VGSvcError("Error fetching parameters for fsqueryinfo operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + return rc; +} + + +static int vgsvcGstCtrlSessionHandleFsCreateTemp(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + /* + * Retrieve the request. + */ + char szTemplate[RTPATH_MAX]; + char szPath[RTPATH_MAX]; + uint32_t fFlags = GSTCTL_CREATETEMP_F_NONE; + RTFMODE fMode = 0700; + int rc = VbglR3GuestCtrlFsGetCreateTemp(pHostCtx, szTemplate, sizeof(szTemplate), szPath, sizeof(szPath), &fFlags, &fMode); + if (RT_SUCCESS(rc)) + { + if (!(fFlags & ~GSTCTL_CREATETEMP_F_VALID_MASK)) + { + const char *pszWhat = fMode & GSTCTL_CREATETEMP_F_DIRECTORY ? "directory" : "file"; + VGSvcVerbose(4, "Creating temporary %s (szTemplate='%s', fFlags=%#x), rc=%Rrc\n", pszWhat, szTemplate, fFlags, rc); + + bool const fSecure = RT_BOOL(fMode & GSTCTL_CREATETEMP_F_SECURE); + if (fMode & GSTCTL_CREATETEMP_F_DIRECTORY) + { + if (fSecure) + rc = RTDirCreateTempSecure(szTemplate); /* File mode is fixed to 0700. */ + else + rc = RTDirCreateTemp(szTemplate, fMode); + } + else /* File */ + { + if (fSecure) + rc = RTFileCreateTempSecure(szTemplate); /* File mode is fixed to 0700. */ + else + rc = RTFileCreateTemp(szTemplate, fMode); + } + } + else + { + VGSvcError("Invalid temporary directory/file creation flags: %#x\n", fFlags); + rc = VERR_NOT_SUPPORTED; + } + + /* + * Report result back to host. + */ + int rc2 = VbglR3GuestCtrlFsCbCreateTemp(pHostCtx, rc, szTemplate); + if (RT_FAILURE(rc2)) + { + VGSvcError("Failed to report temporary file/directory creation status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + else + { + VGSvcError("Error fetching parameters for file/directory creation operation: %Rrc\n", rc); + VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX); + } + VGSvcVerbose(5, "Creating temporary file/directory returned rc=%Rrc\n", rc); + return rc; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx, void **ppvScratchBuf, uint32_t *pcbScratchBuf, volatile bool *pfShutdown) { @@ -1393,11 +1940,6 @@ int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, *pfShutdown = true; /* Shutdown in any case. */ break; - case HOST_MSG_DIR_REMOVE: - if (fImpersonated) - rc = vgsvcGstCtrlSessionHandleDirRemove(pSession, pHostCtx); - break; - case HOST_MSG_EXEC_CMD: rc = vgsvcGstCtrlSessionHandleProcExec(pSession, pHostCtx); break; @@ -1418,6 +1960,18 @@ int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, rc = vgsvcGstCtrlSessionHandleProcWaitFor(pSession, pHostCtx); break; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + case HOST_MSG_FS_QUERY_INFO: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleFsQueryInfo(pSession, pHostCtx); + break; + + case HOST_MSG_FS_CREATE_TEMP: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleFsCreateTemp(pSession, pHostCtx); + break; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + case HOST_MSG_FILE_OPEN: if (fImpersonated) rc = vgsvcGstCtrlSessionHandleFileOpen(pSession, pHostCtx); @@ -1463,6 +2017,43 @@ int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, rc = vgsvcGstCtrlSessionHandleFileSetSize(pSession, pHostCtx); break; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + case HOST_MSG_FILE_REMOVE: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleFileRemove(pSession, pHostCtx); + break; + + case HOST_MSG_DIR_OPEN: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleDirOpen(pSession, pHostCtx); + break; + + case HOST_MSG_DIR_CLOSE: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleDirClose(pSession, pHostCtx); + break; + + case HOST_MSG_DIR_READ: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleDirRead(pSession, pHostCtx); + break; + + case HOST_MSG_DIR_REWIND: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleDirRewind(pSession, pHostCtx); + break; + + case HOST_MSG_DIR_CREATE: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleDirCreate(pSession, pHostCtx); + break; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + case HOST_MSG_DIR_REMOVE: + if (fImpersonated) + rc = vgsvcGstCtrlSessionHandleDirRemove(pSession, pHostCtx); + break; + case HOST_MSG_PATH_RENAME: if (fImpersonated) rc = vgsvcGstCtrlSessionHandlePathRename(pSession, pHostCtx); @@ -1488,7 +2079,8 @@ int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, if (RT_SUCCESS(rc)) { /* likely */ } else if (rc != VERR_NOT_SUPPORTED) /* Note: Reply to host must must be sent by above handler. */ - VGSvcError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", uMsg, pHostCtx->uNumParms, rc); + VGSvcError("Error while handling message %s (%#x, cParms=%RU32), rc=%Rrc\n", + GstCtrlHostMsgtoStr((eHostMsg)uMsg), uMsg, pHostCtx->uNumParms, rc); else { /* We must skip and notify host here as best we can... */ @@ -1500,9 +2092,6 @@ int VGSvcGstCtrlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, rc = VINF_SUCCESS; } - if (RT_FAILURE(rc)) - VGSvcError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", uMsg, pHostCtx->uNumParms, rc); - return rc; } @@ -2035,6 +2624,12 @@ int VGSvcGstCtrlSessionClose(PVBOXSERVICECTRLSESSION pSession) pFile = NULL; /* To make it obvious. */ } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + AssertMsg(pSession->cDirs == 0, + ("Session directory list still contains %RU32 when it should not\n", pSession->cDirs)); + AssertMsg(RTListIsEmpty(&pSession->lstDirs), + ("Session directory list is not empty when it should\n")); +#endif AssertMsg(pSession->cFiles == 0, ("Session file list still contains %RU32 when it should not\n", pSession->cFiles)); AssertMsg(RTListIsEmpty(&pSession->lstFiles), @@ -2067,6 +2662,9 @@ int VGSvcGstCtrlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t fFlags) AssertPtrReturn(pSession, VERR_INVALID_POINTER); RTListInit(&pSession->lstProcesses); +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + RTListInit(&pSession->lstDirs); +#endif RTListInit(&pSession->lstFiles); pSession->cProcesses = 0; diff --git a/src/VBox/HostServices/GuestControl/Makefile.kmk b/src/VBox/HostServices/GuestControl/Makefile.kmk index beb5c1c15ad..3160a4900bd 100644 --- a/src/VBox/HostServices/GuestControl/Makefile.kmk +++ b/src/VBox/HostServices/GuestControl/Makefile.kmk @@ -38,6 +38,9 @@ DLLS += VBoxGuestControlSvc VBoxGuestControlSvc_TEMPLATE = VBoxR3Dll VBoxGuestControlSvc_NAME.os2 = VBoxGCTL VBoxGuestControlSvc_DEFS = VBOX_WITH_HGCM +ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + VBoxGuestControlSvc_DEFS += VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +endif VBoxGuestControlSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include VBoxGuestControlSvc_INCS.win = \ $(VBOX_PATH_SDK) diff --git a/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp index 74063e448a0..be244d33503 100644 --- a/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp +++ b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp @@ -73,6 +73,8 @@ #include <VBox/HostServices/GuestControlSvc.h> #include <VBox/GuestHost/GuestControl.h> /** @todo r=bird: Why two headers??? */ +#include "VBoxGuestControlSvc-internal.h" + #include <VBox/err.h> #include <VBox/log.h> #include <VBox/AssertGuest.h> @@ -1625,6 +1627,7 @@ int GstCtrlService::clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall hostCallback(GUEST_MSG_FILE_NOTIFY, 3, aReplyParams); break; case HOST_MSG_FILE_READ: + RT_FALL_THROUGH(); case HOST_MSG_FILE_READ_AT: HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_READ); /* type */ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ @@ -1632,6 +1635,7 @@ int GstCtrlService::clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams); break; case HOST_MSG_FILE_WRITE: + RT_FALL_THROUGH(); case HOST_MSG_FILE_WRITE_AT: HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_WRITE); /* type */ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ @@ -1656,14 +1660,36 @@ int GstCtrlService::clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */ hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams); break; - - case HOST_MSG_EXEC_GET_OUTPUT: /** @todo This can't be right/work. */ - case HOST_MSG_EXEC_TERMINATE: /** @todo This can't be right/work. */ - case HOST_MSG_EXEC_WAIT_FOR: /** @todo This can't be right/work. */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + case HOST_MSG_FS_QUERY_INFO: + RT_FALL_THROUGH(); + case HOST_MSG_FS_CREATE_TEMP: + RT_FALL_THROUGH(); + case HOST_MSG_FILE_REMOVE: + RT_FALL_THROUGH(); + case HOST_MSG_DIR_OPEN: + RT_FALL_THROUGH(); + case HOST_MSG_DIR_CLOSE: + RT_FALL_THROUGH(); + case HOST_MSG_DIR_READ: + RT_FALL_THROUGH(); + case HOST_MSG_DIR_REWIND: + RT_FALL_THROUGH(); + case HOST_MSG_DIR_CREATE: + RT_FALL_THROUGH(); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + case HOST_MSG_EXEC_GET_OUTPUT: /** @todo BUGBUG This can't be right/work. */ + case HOST_MSG_EXEC_TERMINATE: /** @todo BUGBUG This can't be right/work. */ + case HOST_MSG_EXEC_WAIT_FOR: /** @todo BUGBUG This can't be right/work. */ + break; + case HOST_MSG_DIR_REMOVE: + RT_FALL_THROUGH(); + case HOST_MSG_PATH_RENAME: + RT_FALL_THROUGH(); case HOST_MSG_PATH_USER_DOCUMENTS: + RT_FALL_THROUGH(); case HOST_MSG_PATH_USER_HOME: - case HOST_MSG_PATH_RENAME: - case HOST_MSG_DIR_REMOVE: + RT_FALL_THROUGH(); default: HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mType); HGCMSvcSetU32(&aReplyParams[2], (uint32_t)rcSkip); @@ -2609,4 +2635,3 @@ extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTa LogFlowFunc(("Returning %Rrc\n", rc)); return rc; } - diff --git a/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk b/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk index abaa7a187e3..a48c9edc6b5 100644 --- a/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk +++ b/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk @@ -30,6 +30,38 @@ include $(KBUILD_PATH)/subheader.kmk if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) + # + # Testcase which mocks HGCM to also test the VbglR3-side of Guest Control. + # + # Goal is to use and test as much guest side code as possible as a self-contained + # binary on the host here. + # + # Note: No #ifdef TESTCASE hacks or similar allowed, has to run + # without #ifdef modifications to the core code! + # + PROGRAMS += tstGuestControlMockHGCM + + tstGuestControlMockHGCM_TEMPLATE = VBoxR3TstExe + tstGuestControlMockHGCM_DEFS = VBOX_WITH_HGCM VBOX_WITH_GUEST_CONTROL + tstGuestControlMockHGCM_SOURCES = \ + ../VBoxGuestControlSvc.cpp \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp \ + $(PATH_ROOT)/src/VBox/HostServices/common/message.cpp \ + tstGuestControlMockHGCM.cpp + tstGuestControlMockHGCM_LIBS = $(LIB_RUNTIME) + tstGuestControlMockHGCM_CLEAN = $(tstGuestControlMockHGCM_0_OUTDIR)/tstGuestControlMockHGCM.run + + if 0 # Enable this if you want automatic runs after compilation. + $$(tstGuestControlMockHGCM_0_OUTDIR)/tstGuestControlMockHGCM.run: $$(tstGuestControlMockHGCM_1_STAGE_TARGET) + export VBOX_LOG_DEST=nofile; $(tstGuestControlMockHGCM_1_STAGE_TARGET) quiet + $(QUIET)$(APPEND) -t "$@" "done" + OTHERS += $(tstGuestControlMockHGCM_0_OUTDIR)/tstGuestControlMockHGCM.run + endif + + # Set this in LocalConfig.kmk if you are working on the guest property # service to automatically run the testcase at build time. # OTHERS += $(tstGuestControlSvc_0_OUTDIR)/tstGuestControlSvc.run @@ -52,5 +84,14 @@ if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBO endif -include $(FILE_KBUILD_SUB_FOOTER) +# +# List of above testcases that will be included in the ValKit. +# +ifdef VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING + if1of ($(KBUILD_TARGET), linux solaris win) + VALKIT_UNITTESTS_WHITELIST_GUEST_ADDITIONS += \ + tstGuestControlMockHGCM + endif +endif # VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/HostServices/GuestControl/testcase/tstGuestControlMockHGCM.cpp b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlMockHGCM.cpp new file mode 100644 index 00000000000..41f12d4219a --- /dev/null +++ b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlMockHGCM.cpp @@ -0,0 +1,322 @@ +/* $Id$ */ +/** @file + * Guest Control host service test case. + */ + +/* + * Copyright (C) 2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include <VBox/HostServices/GuestControlSvc.h> +#include <VBox/VBoxGuestLib.h> + +#include <VBox/GuestHost/HGCMMock.h> +#include <VBox/GuestHost/HGCMMockUtils.h> + +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/rand.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/test.h> +#include <iprt/utf16.h> + + +/********************************************************************************************************************************* +* Static globals * +*********************************************************************************************************************************/ +static RTTEST g_hTest; + + +/********************************************************************************************************************************* +* Shared Clipboard testing * +*********************************************************************************************************************************/ +struct CLIPBOARDTESTDESC; +/** Pointer to a test description. */ +typedef CLIPBOARDTESTDESC *PTESTDESC; + +struct CLIPBOARDTESTCTX; +/** Pointer to a test context. */ +typedef CLIPBOARDTESTCTX *PCLIPBOARDTESTCTX; + +/** Pointer a test descriptor. */ +typedef CLIPBOARDTESTDESC *PTESTDESC; + +typedef DECLCALLBACKTYPE(int, FNTESTSETUP,(PCLIPBOARDTESTCTX pTstCtx, void **ppvCtx)); +/** Pointer to a test setup callback. */ +typedef FNTESTSETUP *PFNTESTSETUP; + +typedef DECLCALLBACKTYPE(int, FNTESTEXEC,(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx)); +/** Pointer to a test exec callback. */ +typedef FNTESTEXEC *PFNTESTEXEC; + +typedef DECLCALLBACKTYPE(int, FNTESTDESTROY,(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx)); +/** Pointer to a test destroy callback. */ +typedef FNTESTDESTROY *PFNTESTDESTROY; + + +/** + * Structure for keeping a clipboard test task. + */ +typedef struct CLIPBOARDTESTTASK +{ + +} CLIPBOARDTESTTASK; +typedef CLIPBOARDTESTTASK *PCLIPBOARDTESTTASK; + +/** + * Structure for keeping a clipboard test context. + */ +typedef struct CLIPBOARDTESTCTX +{ + /** The HGCM Mock utils context. */ + TSTHGCMUTILSCTX HGCM; + /** Clipboard-specific task data. */ + CLIPBOARDTESTTASK Task; + struct + { + /** The VbglR3 Guest Control context to work on. */ + VBGLR3GUESTCTRLCMDCTX CmdCtx; + } Guest; +} CLIPBOARDTESTCTX; + +/** The one and only clipboard test context. One at a time. */ +CLIPBOARDTESTCTX g_TstCtx; + +/** + * Structure for keeping a clipboard test description. + */ +typedef struct CLIPBOARDTESTDESC +{ + /** The setup callback. */ + PFNTESTSETUP pfnSetup; + /** The exec callback. */ + PFNTESTEXEC pfnExec; + /** The destruction callback. */ + PFNTESTDESTROY pfnDestroy; +} CLIPBOARDTESTDESC; + +typedef struct SHCLCONTEXT +{ +} SHCLCONTEXT; + + +static void testGuestSimple(void) +{ + RTTestISub("Testing client (guest) API - Simple"); + + PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst(); RT_NOREF(pSvc); + + /* Preparations. */ + VBGLR3GUESTCTRLCMDCTX Ctx; + RT_ZERO(Ctx); + + /* + * Multiple connects / disconnects. + */ + RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlConnect(&Ctx.uClientID)); + RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlDisconnect(Ctx.uClientID)); + + /* + * Feature tests. + */ + RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlConnect(&Ctx.uClientID)); + RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlReportFeatures(Ctx.uClientID, 0x0, NULL /* pfHostFeatures */)); + /* Report bogus features to the host. */ + RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlReportFeatures(Ctx.uClientID, 0xdeadb33f, NULL /* pfHostFeatures */)); + RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlDisconnect(Ctx.uClientID)); +} + +static void testHostCall(void) +{ +} + + +/********************************************************************************************************************************* + * Test: Guest reading from host * + ********************************************************************************************************************************/ +typedef struct TSTUSERMOCK +{ +} TSTUSERMOCK; +typedef TSTUSERMOCK *PTSTUSERMOCK; + +static void tstTestReadFromHost_MockInit(PTSTUSERMOCK pUsrMock, const char *pszName) +{ + RT_NOREF(pUsrMock, pszName); +} + +static void tstTestReadFromHost_MockDestroy(PTSTUSERMOCK pUsrMock) +{ + RT_NOREF(pUsrMock); +} + +static DECLCALLBACK(int) tstTestReadFromHost_ThreadGuest(PTSTHGCMUTILSCTX pCtx, void *pvCtx) +{ + RTThreadSleep(1000); /* Fudge; wait until the host has prepared the data for the clipboard. */ + + PCLIPBOARDTESTCTX pTstCtx = (PCLIPBOARDTESTCTX)pvCtx; + AssertPtr(pTstCtx); + + RT_ZERO(pTstCtx->Guest.CmdCtx); + RTTEST_CHECK_RC_OK(g_hTest, VbglR3GuestCtrlConnect(&pTstCtx->Guest.CmdCtx.uClientID)); + + RTThreadSleep(1000); /* Fudge; wait until the host has prepared the data for the clipboard. */ + + /* Signal that the task ended. */ + TstHGCMUtilsTaskSignal(&pCtx->Task, VINF_SUCCESS); + + RTTEST_CHECK_RC_OK(g_hTest, VbglR3GuestCtrlDisconnect(pTstCtx->Guest.CmdCtx.uClientID)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) tstTestReadFromHostSetup(PCLIPBOARDTESTCTX pTstCtx, void **ppvCtx) +{ + RT_NOREF(pTstCtx, ppvCtx); + + int rc = VINF_SUCCESS; + + return rc; +} + +static DECLCALLBACK(int) tstTestReadFromHostExec(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx) +{ + RT_NOREF(pvCtx); + + TstHGCMUtilsGuestThreadStart(&pTstCtx->HGCM, tstTestReadFromHost_ThreadGuest, pTstCtx); + + PTSTHGCMUTILSTASK pTask = (PTSTHGCMUTILSTASK)TstHGCMUtilsTaskGetCurrent(&pTstCtx->HGCM); + + bool fUseMock = false; + TSTUSERMOCK UsrMock; + if (fUseMock) + tstTestReadFromHost_MockInit(&UsrMock, "tstX11Hst"); + + /* Wait until the task has been finished. */ + TstHGCMUtilsTaskWait(pTask, RT_MS_30SEC); + + if (fUseMock) + tstTestReadFromHost_MockDestroy(&UsrMock); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) tstTestReadFromHostDestroy(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx) +{ + RT_NOREF(pvCtx); + + int vrc = TstHGCMUtilsGuestThreadStop(&pTstCtx->HGCM); + AssertRC(vrc); + vrc = TstHGCMUtilsHostThreadStop(&pTstCtx->HGCM); + AssertRC(vrc); + + return vrc; +} + + +/********************************************************************************************************************************* + * Main * + ********************************************************************************************************************************/ + +/** Test definition table. */ +CLIPBOARDTESTDESC g_aTests[] = +{ + /* Tests guest reading clipboard data from the host. */ + { tstTestReadFromHostSetup, tstTestReadFromHostExec, tstTestReadFromHostDestroy } +}; +/** Number of tests defined. */ +unsigned g_cTests = RT_ELEMENTS(g_aTests); + +static int tstOne(PTESTDESC pTstDesc) +{ + PCLIPBOARDTESTCTX pTstCtx = &g_TstCtx; + + void *pvCtx; + int rc = pTstDesc->pfnSetup(pTstCtx, &pvCtx); + if (RT_SUCCESS(rc)) + { + rc = pTstDesc->pfnExec(pTstCtx, pvCtx); + + int rc2 = pTstDesc->pfnDestroy(pTstCtx, pvCtx); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + return rc; +} + +int main(int argc, char *argv[]) +{ + /* + * Init the runtime, test and say hello. + */ + const char *pcszExecName; + NOREF(argc); + pcszExecName = strrchr(argv[0], '/'); + pcszExecName = pcszExecName ? pcszExecName + 1 : argv[0]; + RTEXITCODE rcExit = RTTestInitAndCreate(pcszExecName, &g_hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + RTTestBanner(g_hTest); + +#ifndef DEBUG_andy + /* Don't let assertions in the host service panic (core dump) the test cases. */ + RTAssertSetMayPanic(false); +#endif + + PTSTHGCMMOCKSVC const pSvc = TstHgcmMockSvcInst(); + + TstHgcmMockSvcCreate(pSvc, 42 /** @todo */); + TstHgcmMockSvcStart(pSvc); + + /* + * Run the tests. + */ + if (1) + { + testGuestSimple(); + testHostCall(); + } + + RT_ZERO(g_TstCtx); + + PTSTHGCMUTILSCTX pCtx = &g_TstCtx.HGCM; + TstHGCMUtilsCtxInit(pCtx, pSvc); + + PTSTHGCMUTILSTASK pTask = (PTSTHGCMUTILSTASK)TstHGCMUtilsTaskGetCurrent(pCtx); + TstHGCMUtilsTaskInit(pTask); + pTask->pvUser = &g_TstCtx.Task; + + for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++) + tstOne(&g_aTests[i]); + + TstHGCMUtilsTaskDestroy(pTask); + + TstHgcmMockSvcStop(pSvc); + TstHgcmMockSvcDestroy(pSvc); + + /* + * Summary + */ + return RTTestSummaryAndDestroy(g_hTest); +} + diff --git a/src/VBox/Main/Makefile.kmk b/src/VBox/Main/Makefile.kmk index 5247c84dc02..6dd832f4cb6 100644 --- a/src/VBox/Main/Makefile.kmk +++ b/src/VBox/Main/Makefile.kmk @@ -200,6 +200,12 @@ endif ifdef VBOX_WITH_MAIN_NLS VBOX_MAIN_DEFS += VBOX_WITH_MAIN_NLS endif +ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + VBOX_MAIN_DEFS += VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT +endif +ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + VBOX_MAIN_DEFS += VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +endif # Unconditionally enable the new semaphore key generation code VBOX_MAIN_DEFS += VBOX_WITH_NEW_SYS_V_KEYGEN diff --git a/src/VBox/Main/idl/VirtualBox.xidl b/src/VBox/Main/idl/VirtualBox.xidl index 4639f99b3b9..f6b4c5f4519 100644 --- a/src/VBox/Main/idl/VirtualBox.xidl +++ b/src/VBox/Main/idl/VirtualBox.xidl @@ -14244,6 +14244,34 @@ </enum> <enum + name="DirectoryStatus" + uuid="a50ca1fc-85a9-4a7a-b755-68c3db01caf1" + > + <desc> + Directory statuses. + </desc> + + <const name="Undefined" value="0"> + <desc>Directory is in an undefined state.</desc> + </const> + <const name="Open" value="1"> + <desc>Guest directory has been successfully opened.</desc> + </const> + <const name="Close" value="2"> + <desc>Guest directory has been closed.</desc> + </const> + <const name="Rewind" value="3"> + <desc>Guest directory reading was rewound.</desc> + </const> + <const name="Down" value="4"> + <desc>Service/OS is stopping, guest directory was closed.</desc> + </const> + <const name="Error" value="5"> + <desc>Something went wrong.</desc> + </const> + </enum> + + <enum name="FileAccessMode" uuid="231a578f-47fb-ea30-3b3e-8489558227f0" > @@ -16379,7 +16407,7 @@ <interface name="IDirectory" extends="$unknown" - uuid="758d7eac-e4b1-486a-8f2e-747ae346c3e9" + uuid="c7b24ad6-dba7-486c-b1ed-5f16d8d6da22" wsmap="managed" reservedMethods="4" reservedAttributes="8" > @@ -16391,10 +16419,28 @@ <desc>The path specified when opening the directory.</desc> </attribute> + <attribute name="eventSource" type="IEventSource" readonly="yes"> + <desc> + Event source for directory events. + </desc> + </attribute> + <attribute name="filter" type="wstring" readonly="yes"> <desc>Directory listing filter to (specified when opening the directory).</desc> </attribute> + <attribute name="id" type="unsigned long" readonly="yes"> + <desc> + The ID VirtualBox internally assigned to the open directory. + </desc> + </attribute> + + <attribute name="status" type="DirectoryStatus" readonly="yes"> + <desc> + Current directory status. + </desc> + </attribute> + <method name="close"> <desc> Closes this directory. After closing operations like reading the next @@ -16415,6 +16461,12 @@ </param> </method> + <method name="rewind"> + <desc> + Rewinds the directory reading. + </desc> + </method> + <!-- Would be useful to add queryInfo() and setACL() here later, but at present IPRT isn't doing a race free job with the former and doesn't have the latter. So, let it be for now. --> @@ -16798,7 +16850,7 @@ </attribute> <attribute name="fileAttributes" type="wstring" readonly="yes"> <desc> - File attributes. Not implemented yet. + File attributes. </desc> </attribute> <attribute name="objectSize" type="long long" readonly="yes"> @@ -26259,7 +26311,7 @@ Snapshot 1 (B.vdi) Snapshot 1 (B.vdi) --> <enum name="VBoxEventType" - uuid="e4c5252d-7d1a-4051-8cfb-5b2d7a73d992" + uuid="03b0e6ea-28fe-4f0a-a3ec-1a21703da6f7" > <desc> @@ -26758,8 +26810,25 @@ Snapshot 1 (B.vdi) Snapshot 1 (B.vdi) See <link to="IMachineGroupsChangedEvent">IMachineGroupsChangedEvent</link>. </desc> </const> + <const name="OnGuestDirectoryRegistered" value="119"> + <desc> + See <link to="IGuestDirectoryRegisteredEvent">IGuestDirectoryRegisteredEvent</link>. + </desc> + </const> + <const name="OnGuestDirectoryStateChanged" value="120"> + <desc> + See <link to="IGuestDirectoryStateChangedEvent">IGuestDirectoryStateChangedEvent</link>. + </desc> + </const> + <const name="OnGuestDirectoryRead" value="121"> + <desc> + See <link to="IGuestDirectoryReadEvent">IGuestDirectoryReadEvent</link>. + <note internal="yes">For performance reasons this is a separate event to + not unnecessarily overflow the event queue.</note> + </desc> + </const> <!-- End event marker --> - <const name="End" value="119"> + <const name="End" value="122"> <desc> Must be last event, used for iterations and structures relying on numerical event values. </desc> @@ -27983,6 +28052,83 @@ Snapshot 1 (B.vdi) Snapshot 1 (B.vdi) </interface> <interface + name="IGuestDirectoryEvent" extends="IGuestSessionEvent" + uuid="02b69798-7cc2-4005-ac57-1ad7ff7a0997" + wsmap="managed" + > + <desc>Base abstract interface for all guest directory events.</desc> + + <attribute name="directory" type="IGuestDirectory" readonly="yes"> + <desc> + Guest directory object which is related to this event. + </desc> + </attribute> + + </interface> + + <interface + name="IGuestDirectoryRegisteredEvent" extends="IGuestDirectoryEvent" + uuid="926baa39-cfc9-462e-a1a1-c439e28f7f89" + wsmap="managed" autogen="VBoxEvent" id="OnGuestDirectoryRegistered" + > + <desc> + Notification when a guest directory was registered or unregistered. + </desc> + + <attribute name="registered" type="boolean" readonly="yes"> + <desc> + If @c true, the guest directory was registered, otherwise it was + unregistered. + </desc> + </attribute> + + </interface> + + <interface + name="IGuestDirectoryStateChangedEvent" extends="IGuestDirectoryEvent" + uuid="c32bce60-d69d-4eb7-a02a-411ecbab6a18" + wsmap="managed" autogen="VBoxEvent" id="OnGuestDirectoryStateChanged" + > + <desc> + Notification when a guest directory changed its state. + </desc> + + <attribute name="status" type="DirectoryStatus" readonly="yes"> + <desc> + New guest directory status. + </desc> + </attribute> + <attribute name="error" type="IVirtualBoxErrorInfo" readonly="yes"> + <desc> + Error information in case of new session status is indicating an error. + + The attribute <link to="IVirtualBoxErrorInfo::resultDetail"/> will contain + the runtime (IPRT) error code from the guest. See include/iprt/err.h and + include/VBox/err.h for details. + </desc> + </attribute> + <!-- Note: No events for reads for performance reasons. + See dedicated event IGuestDirectoryReadEvent. --> + + </interface> + + <interface + name="IGuestDirectoryReadEvent" extends="IGuestDirectoryEvent" + uuid="6eef78d9-51d6-495b-8f91-807654a189a2" + wsmap="managed" autogen="VBoxEvent" id="OnGuestDirectoryRead" + > + <desc> + Notification when a directory entry has been read. + </desc> + + <attribute name="objInfo" type="IFsObjInfo" readonly="yes"> + <desc>Object information of the current directory entry read. Also see + <link to="IFsObjInfo"/>.</desc> + </attribute> + + </interface> + + <interface name="IGuestFileEvent" extends="IGuestSessionEvent" uuid="c8adb7b0-057d-4391-b928-f14b06b710c5" wsmap="managed" diff --git a/src/VBox/Main/include/GuestCtrlImplPrivate.h b/src/VBox/Main/include/GuestCtrlImplPrivate.h index c90d1af8490..70695b17846 100644 --- a/src/VBox/Main/include/GuestCtrlImplPrivate.h +++ b/src/VBox/Main/include/GuestCtrlImplPrivate.h @@ -61,7 +61,7 @@ typedef std::vector <LONG> ProcessAffinity; /** Vector holding process startup arguments. */ typedef std::vector <Utf8Str> ProcessArguments; -class GuestProcessStreamBlock; +class GuestToolboxStreamBlock; class GuestSession; @@ -635,8 +635,9 @@ public: Type_File, /** Guest error is from a guest directory object. */ Type_Directory, - /** Guest error is from a the built-in toolbox "vbox_cat" command. */ - Type_ToolCat, + /** Guest error is from a file system operation. */ + Type_Fs, +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT /** Guest error is from a the built-in toolbox "vbox_ls" command. */ Type_ToolLs, /** Guest error is from a the built-in toolbox "vbox_rm" command. */ @@ -647,6 +648,7 @@ public: Type_ToolMkTemp, /** Guest error is from a the built-in toolbox "vbox_stat" command. */ Type_ToolStat, +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ /** The usual 32-bit hack. */ Type_32BIT_HACK = 0x7fffffff }; @@ -720,13 +722,16 @@ protected: struct GuestDirectoryOpenInfo { GuestDirectoryOpenInfo(void) - : mFlags(0) { } + : menmFilter(GSTCTLDIRFILTER_NONE) + , mFlags(0) { } /** The directory path. */ Utf8Str mPath; - /** Then open filter. */ + /** The filter to use (wildcard style). */ Utf8Str mFilter; - /** Opening flags. */ + /** The filter option to use. */ + GSTCTLDIRFILTER menmFilter; + /** Opening flags (of type GSTCTLDIRFILTER_XXX). */ uint32_t mFlags; }; @@ -798,12 +803,30 @@ struct GuestFileOpenInfo /** + * Helper class for guest file system operations. + */ +class GuestFs +{ + DECLARE_TRANSLATE_METHODS(GuestFs) + +private: + + /* Not directly instantiable. */ + GuestFs(void) { } + +public: + + static Utf8Str guestErrorToString(const GuestErrorInfo &guestErrorInfo); +}; + + +/** * Structure representing information of a * file system object. */ struct GuestFsObjData { - GuestFsObjData(void) + GuestFsObjData(const Utf8Str &strName = "") : mType(FsObjType_Unknown) , mObjectSize(0) , mAllocatedSize(0) @@ -818,21 +841,32 @@ struct GuestFsObjData , mNumHardLinks(0) , mDeviceNumber(0) , mGenerationID(0) - , mUserFlags(0) { } + , mUserFlags(0) { mName = strName; } + + void Init(const Utf8Str &strName) { mName = strName; } + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + int FromGuestFsObjInfo(PCGSTCTLFSOBJINFO pFsObjInfo, const Utf8Str &strUser = "", const Utf8Str &strGroups = "", + const void *pvACL = NULL, size_t cbACL = 0); +#endif +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT /** @name Helper functions to extract the data from a certin VBoxService tool's guest stream block. * @{ */ - int FromLs(const GuestProcessStreamBlock &strmBlk, bool fLong); - int FromRm(const GuestProcessStreamBlock &strmBlk); - int FromStat(const GuestProcessStreamBlock &strmBlk); - int FromMkTemp(const GuestProcessStreamBlock &strmBlk); + int FromToolboxLs(const GuestToolboxStreamBlock &strmBlk, bool fLong); + int FromToolboxRm(const GuestToolboxStreamBlock &strmBlk); + int FromToolboxStat(const GuestToolboxStreamBlock &strmBlk); + int FromToolboxMkTemp(const GuestToolboxStreamBlock &strmBlk); /** @} */ +#endif +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT /** @name Static helper functions to work with time from stream block keys. * @{ */ - static PRTTIMESPEC TimeSpecFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec); - static int64_t UnixEpochNsFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey); + static PRTTIMESPEC TimeSpecFromKey(const GuestToolboxStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec); + static int64_t UnixEpochNsFromKey(const GuestToolboxStreamBlock &strmBlk, const Utf8Str &strKey); /** @} */ +#endif /** @name helper functions to work with IPRT stuff. * @{ */ @@ -931,19 +965,19 @@ public: /** * Class representing the "value" side of a "key=value" pair. */ -class GuestProcessStreamValue +class GuestToolboxStreamValue { public: - GuestProcessStreamValue(void) { } - GuestProcessStreamValue(const char *pszValue) + GuestToolboxStreamValue(void) { } + GuestToolboxStreamValue(const char *pszValue) : mValue(pszValue) {} - GuestProcessStreamValue(const GuestProcessStreamValue& aThat) + GuestToolboxStreamValue(const GuestToolboxStreamValue& aThat) : mValue(aThat.mValue) { } /** Copy assignment operator. */ - GuestProcessStreamValue &operator=(GuestProcessStreamValue const &a_rThat) RT_NOEXCEPT + GuestToolboxStreamValue &operator=(GuestToolboxStreamValue const &a_rThat) RT_NOEXCEPT { mValue = a_rThat.mValue; @@ -954,23 +988,26 @@ public: }; /** Map containing "key=value" pairs of a guest process stream. */ -typedef std::pair< Utf8Str, GuestProcessStreamValue > GuestCtrlStreamPair; -typedef std::map < Utf8Str, GuestProcessStreamValue > GuestCtrlStreamPairMap; -typedef std::map < Utf8Str, GuestProcessStreamValue >::iterator GuestCtrlStreamPairMapIter; -typedef std::map < Utf8Str, GuestProcessStreamValue >::const_iterator GuestCtrlStreamPairMapIterConst; +typedef std::pair< Utf8Str, GuestToolboxStreamValue > GuestCtrlStreamPair; +typedef std::map < Utf8Str, GuestToolboxStreamValue > GuestCtrlStreamPairMap; +typedef std::map < Utf8Str, GuestToolboxStreamValue >::iterator GuestCtrlStreamPairMapIter; +typedef std::map < Utf8Str, GuestToolboxStreamValue >::const_iterator GuestCtrlStreamPairMapIterConst; /** * Class representing a block of stream pairs (key=value). Each block in a raw guest * output stream is separated by "\0\0", each pair is separated by "\0". The overall * end of a guest stream is marked by "\0\0\0\0". + * + * Only used for the busybox-like toolbox commands within VBoxService. + * Deprecated, do not use anymore. */ -class GuestProcessStreamBlock +class GuestToolboxStreamBlock { public: - GuestProcessStreamBlock(void); + GuestToolboxStreamBlock(void); - virtual ~GuestProcessStreamBlock(void); + virtual ~GuestToolboxStreamBlock(void); public: @@ -999,22 +1036,24 @@ protected: }; /** Vector containing multiple allocated stream pair objects. */ -typedef std::vector< GuestProcessStreamBlock > GuestCtrlStreamObjects; -typedef std::vector< GuestProcessStreamBlock >::iterator GuestCtrlStreamObjectsIter; -typedef std::vector< GuestProcessStreamBlock >::const_iterator GuestCtrlStreamObjectsIterConst; +typedef std::vector< GuestToolboxStreamBlock > GuestCtrlStreamObjects; +typedef std::vector< GuestToolboxStreamBlock >::iterator GuestCtrlStreamObjectsIter; +typedef std::vector< GuestToolboxStreamBlock >::const_iterator GuestCtrlStreamObjectsIterConst; /** * Class for parsing machine-readable guest process output by VBoxService' * toolbox commands ("vbox_ls", "vbox_stat" etc), aka "guest stream". + * + * Deprecated, do not use anymore. */ -class GuestProcessStream +class GuestToolboxStream { public: - GuestProcessStream(); + GuestToolboxStream(); - virtual ~GuestProcessStream(); + virtual ~GuestToolboxStream(); public: @@ -1030,7 +1069,7 @@ public: size_t GetSize() { return m_cbUsed; } - int ParseBlock(GuestProcessStreamBlock &streamBlock); + int ParseBlock(GuestToolboxStreamBlock &streamBlock); protected: diff --git a/src/VBox/Main/include/GuestDirectoryImpl.h b/src/VBox/Main/include/GuestDirectoryImpl.h index cfc55553d57..277ffd31fd4 100644 --- a/src/VBox/Main/include/GuestDirectoryImpl.h +++ b/src/VBox/Main/include/GuestDirectoryImpl.h @@ -67,11 +67,22 @@ public: public: /** @name Public internal methods. * @{ */ - int i_closeInternal(int *pvrcGuest); + int i_open(int *pvrcGuest); + int i_close(int *pvrcGuest); + EventSource *i_getEventSource(void) { return mEventSource; } + int i_onDirNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData); int i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *pvrcGuest); - int i_readInternal(GuestFsObjData &objData, int *prcGuest); + int i_readInternal(GuestFsObjData &objData, int *pvrcGuest); + int i_rewind(uint32_t uTimeoutMS, int *pvrcGuest); + int i_setStatus(DirectoryStatus_T enmStatus, int vrcDir); /** @} */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + int i_openViaToolbox(int *pvrcGuest); + int i_closeViaToolbox(int *pvrcGuest); + int i_readInternalViaToolbox(GuestFsObjData &objData, int *pvrcGuest); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ + public: /** @name Public static internal methods. * @{ */ @@ -89,15 +100,30 @@ private: /** Wrapped @name IGuestDirectory methods. * @{ */ HRESULT close(); + HRESULT getEventSource(ComPtr<IEventSource> &aEventSource); + HRESULT getId(ULONG *aId); + HRESULT getStatus(DirectoryStatus_T *aStatus); HRESULT read(ComPtr<IFsObjInfo> &aObjInfo); + HRESULT rewind(void); /** @} */ + /** This can safely be used without holding any locks. + * An AutoCaller suffices to prevent it being destroy while in use and + * internally there is a lock providing the necessary serialization. */ + const ComObjPtr<EventSource> mEventSource; + struct Data { /** The directory's open info. */ GuestDirectoryOpenInfo mOpenInfo; + /** The current directory status. */ + DirectoryStatus_T mStatus; + /** The last returned directory error returned from the guest side. */ + int mLastError; +# ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT /** The process tool instance to use. */ - GuestProcessTool mProcessTool; + GuestProcessToolbox mProcessTool; +# endif /** Object data cache. * Its mName attribute acts as a beacon if the cache is valid or not. */ GuestFsObjData mObjData; diff --git a/src/VBox/Main/include/GuestProcessImpl.h b/src/VBox/Main/include/GuestProcessImpl.h index 86fadf76385..5c01d31ee83 100644 --- a/src/VBox/Main/include/GuestProcessImpl.h +++ b/src/VBox/Main/include/GuestProcessImpl.h @@ -228,14 +228,14 @@ struct GuestProcessToolErrorInfo * Note! When implementing new functionality / commands, do *not* use this approach anymore! * This class has to be kept to guarantee backwards-compatibility. */ -class GuestProcessTool +class GuestProcessToolbox { public: DECLARE_TRANSLATE_METHODS(GuestProcessTool) - GuestProcessTool(void); + GuestProcessToolbox(void); - virtual ~GuestProcessTool(void); + virtual ~GuestProcessToolbox(void); public: @@ -243,19 +243,19 @@ public: void uninit(void); - int getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock &strmBlock); + int getCurrentBlock(uint32_t uHandle, GuestToolboxStreamBlock &strmBlock); int getRc(void) const; /** Returns the stdout output from the guest process tool. */ - GuestProcessStream &getStdOut(void) { return mStdOut; } + GuestToolboxStream &getStdOut(void) { return mStdOut; } /** Returns the stderr output from the guest process tool. */ - GuestProcessStream &getStdErr(void) { return mStdErr; } + GuestToolboxStream &getStdErr(void) { return mStdErr; } int wait(uint32_t fToolWaitFlags, int *pvrcGuest); - int waitEx(uint32_t fToolWaitFlags, GuestProcessStreamBlock *pStreamBlock, int *pvrcGuest); + int waitEx(uint32_t fToolWaitFlags, GuestToolboxStreamBlock *pStreamBlock, int *pvrcGuest); bool isRunning(void); @@ -301,9 +301,9 @@ protected: /** The toolbox' startup info. */ GuestProcessStartupInfo mStartupInfo; /** Stream object for handling the toolbox' stdout data. */ - GuestProcessStream mStdOut; + GuestToolboxStream mStdOut; /** Stream object for handling the toolbox' stderr data. */ - GuestProcessStream mStdErr; + GuestToolboxStream mStdErr; }; #endif /* !MAIN_INCLUDED_GuestProcessImpl_h */ diff --git a/src/VBox/Main/src-client/GuestCtrlPrivate.cpp b/src/VBox/Main/src-client/GuestCtrlPrivate.cpp index 164152558c6..7db93e428c5 100644 --- a/src/VBox/Main/src-client/GuestCtrlPrivate.cpp +++ b/src/VBox/Main/src-client/GuestCtrlPrivate.cpp @@ -52,6 +52,177 @@ #include <VBox/AssertGuest.h> +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS +/** + * Returns a stringyfied error of a guest fs error. + * + * @returns Stringyfied error. + * @param guestErrorInfo Guest error info to get stringyfied error for. + */ +/* static */ +Utf8Str GuestFs::guestErrorToString(const GuestErrorInfo &guestErrorInfo) +{ + Utf8Str strErr; + + /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */ + switch (guestErrorInfo.getVrc()) + { + case VERR_ACCESS_DENIED: + strErr.printf(tr("Access to \"%s\" denied"), guestErrorInfo.getWhat().c_str()); + break; + + case VERR_FILE_NOT_FOUND: /* This is the most likely error. */ + RT_FALL_THROUGH(); + case VERR_PATH_NOT_FOUND: + strErr.printf(tr("No such file or directory \"%s\""), guestErrorInfo.getWhat().c_str()); + break; + + case VERR_INVALID_VM_HANDLE: + strErr.printf(tr("VMM device is not available (is the VM running?)")); + break; + + case VERR_HGCM_SERVICE_NOT_FOUND: + strErr.printf(tr("The guest execution service is not available")); + break; + + case VERR_BAD_EXE_FORMAT: + strErr.printf(tr("The file \"%s\" is not an executable format"), guestErrorInfo.getWhat().c_str()); + break; + + case VERR_AUTHENTICATION_FAILURE: + strErr.printf(tr("The user \"%s\" was not able to logon"), guestErrorInfo.getWhat().c_str()); + break; + + case VERR_INVALID_NAME: + strErr.printf(tr("The file \"%s\" is an invalid name"), guestErrorInfo.getWhat().c_str()); + break; + + case VERR_TIMEOUT: + strErr.printf(tr("The guest did not respond within time")); + break; + + case VERR_CANCELLED: + strErr.printf(tr("The execution operation was canceled")); + break; + + case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED: + strErr.printf(tr("Maximum number of concurrent guest processes has been reached")); + break; + + case VERR_NOT_FOUND: + strErr.printf(tr("The guest execution service is not ready (yet)")); + break; + + default: + strErr.printf(tr("Unhandled error %Rrc for \"%s\" occurred on guest -- please file a bug report"), + guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str()); + break; + } + + return strErr; +} + + +/** + * Set the file system object data from a given GSTCTLFSOBJINFO struct. + * + * @returns VBox status code. + * @param pFsObjInfo Pointer to GSTCTLFSOBJINFO struct to use. + * @param strUser Resolved user name owning the object on the guest. + * @param strGroups Resolved user group(s) the object on the guest is associated with. + * On Windows there can be multiple groups assigned. The groups are separated with ";" + * The first group found is always the primary group. + * @param pvACL ACL data associated with the object. + * @param cbACL Size of ACL data (in bytes) associated with the object. + */ +int GuestFsObjData::FromGuestFsObjInfo(PCGSTCTLFSOBJINFO pFsObjInfo, + const Utf8Str &strUser /* = "" */, const Utf8Str &strGroups /* = "" */, const void *pvACL /* = NULL */, size_t cbACL /* = 0 */) +{ + RT_NOREF(pvACL, cbACL); + + int rc; + + mType = GuestBase::fileModeToFsObjType(pFsObjInfo->Attr.fMode); + + mFileAttrs = ""; + switch (mType) + { + case FsObjType_File: mFileAttrs += '-'; break; + case FsObjType_Directory: mFileAttrs += 'd'; break; + case FsObjType_Symlink: mFileAttrs += 'l'; break; + case FsObjType_DevChar: mFileAttrs += 'c'; break; + case FsObjType_DevBlock: mFileAttrs += 'b'; break; + case FsObjType_Fifo: mFileAttrs += 'f'; break; + case FsObjType_Socket: mFileAttrs += 's'; break; + case FsObjType_WhiteOut: mFileAttrs += 'w'; break; + default: + mFileAttrs += '?'; + AssertFailedStmt(rc = VERR_NOT_SUPPORTED); + break; + } + +#define ADD_ATTR(a_Flag, a_Set, a_Clear) \ + mFileAttrs += pFsObjInfo->Attr.fMode & a_Flag ? a_Set : a_Clear + + ADD_ATTR(RTFS_UNIX_IRUSR, 'r', '-'); + ADD_ATTR(RTFS_UNIX_IWUSR, 'w', '-'); + ADD_ATTR(RTFS_UNIX_IXUSR, 'x', '-'); + + ADD_ATTR(RTFS_UNIX_IRGRP, 'r', '-'); + ADD_ATTR(RTFS_UNIX_IWGRP, 'w', '-'); + ADD_ATTR(RTFS_UNIX_IXGRP, 'x', '-'); + + ADD_ATTR(RTFS_UNIX_IROTH, 'r', '-'); + ADD_ATTR(RTFS_UNIX_IWOTH, 'w', '-'); + ADD_ATTR(RTFS_UNIX_IXOTH, 'x', '-'); + + /** @todo Implement sticky bits. */ + mFileAttrs += " "; /* Reserve 3 chars for sticky bits. */ + + mFileAttrs += " "; /* Separator. */ + + ADD_ATTR(RTFS_DOS_READONLY , 'R', '-'); + ADD_ATTR(RTFS_DOS_HIDDEN , 'H', '-'); + ADD_ATTR(RTFS_DOS_SYSTEM , 'S', '-'); + ADD_ATTR(RTFS_DOS_DIRECTORY , 'D', '-'); + ADD_ATTR(RTFS_DOS_ARCHIVED , 'A', '-'); + ADD_ATTR(RTFS_DOS_NT_DEVICE , 'd', '-'); + ADD_ATTR(RTFS_DOS_NT_NORMAL , 'N', '-'); + ADD_ATTR(RTFS_DOS_NT_TEMPORARY , 'T', '-'); + ADD_ATTR(RTFS_DOS_NT_SPARSE_FILE , 'P', '-'); + ADD_ATTR(RTFS_DOS_NT_REPARSE_POINT , 'J', '-'); + ADD_ATTR(RTFS_DOS_NT_COMPRESSED , 'C', '-'); + ADD_ATTR(RTFS_DOS_NT_OFFLINE , 'O', '-'); + ADD_ATTR(RTFS_DOS_NT_NOT_CONTENT_INDEXED, 'I', '-'); + ADD_ATTR(RTFS_DOS_NT_ENCRYPTED , 'E', '-'); + +#undef ADD_ATTR + + mObjectSize = pFsObjInfo->cbObject; + mAllocatedSize = pFsObjInfo->cbAllocated; + mAccessTime = pFsObjInfo->AccessTime.i64NanosecondsRelativeToUnixEpoch; + mBirthTime = pFsObjInfo->BirthTime.i64NanosecondsRelativeToUnixEpoch; + mChangeTime = pFsObjInfo->ChangeTime.i64NanosecondsRelativeToUnixEpoch; + mModificationTime = pFsObjInfo->ModificationTime.i64NanosecondsRelativeToUnixEpoch; + mUserName = strUser; + mUID = pFsObjInfo->Attr.u.Unix.uid; + mGID = pFsObjInfo->Attr.u.Unix.gid; + mGroupName = strGroups; /** @todo Separate multiple group. */ + mNumHardLinks = pFsObjInfo->Attr.u.Unix.cHardlinks; + mNodeIDDevice = pFsObjInfo->Attr.u.Unix.INodeIdDevice; + mNodeID = pFsObjInfo->Attr.u.Unix.INodeId; + mDeviceNumber = RTFS_IS_DEV_BLOCK(pFsObjInfo->Attr.fMode) + || RTFS_IS_DEV_CHAR (pFsObjInfo->Attr.fMode) ? pFsObjInfo->Attr.u.Unix.Device : 0; + mGenerationID = pFsObjInfo->Attr.u.Unix.GenerationId; + mUserFlags = 0; + + mACL = ""; /** @todo Implement ACL handling. */ + + return VINF_SUCCESS; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT /** * Extracts the timespec from a given stream block key. * @@ -61,7 +232,7 @@ * @param pTimeSpec Where to store the extracted timespec. */ /* static */ -PRTTIMESPEC GuestFsObjData::TimeSpecFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec) +PRTTIMESPEC GuestFsObjData::TimeSpecFromKey(const GuestToolboxStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec) { AssertPtrReturn(pTimeSpec, NULL); @@ -83,7 +254,7 @@ PRTTIMESPEC GuestFsObjData::TimeSpecFromKey(const GuestProcessStreamBlock &strmB * @param strKey Key to get nanoseconds for. */ /* static */ -int64_t GuestFsObjData::UnixEpochNsFromKey(const GuestProcessStreamBlock &strmBlk, const Utf8Str &strKey) +int64_t GuestFsObjData::UnixEpochNsFromKey(const GuestToolboxStreamBlock &strmBlk, const Utf8Str &strKey) { RTTIMESPEC TimeSpec; if (!GuestFsObjData::TimeSpecFromKey(strmBlk, strKey, &TimeSpec)) @@ -102,7 +273,7 @@ int64_t GuestFsObjData::UnixEpochNsFromKey(const GuestProcessStreamBlock &strmBl * @param strmBlk Stream block to use for initialization. * @param fLong Whether the stream block contains long (detailed) information or not. */ -int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk, bool fLong) +int GuestFsObjData::FromToolboxLs(const GuestToolboxStreamBlock &strmBlk, bool fLong) { LogFlowFunc(("\n")); #ifdef DEBUG @@ -252,7 +423,7 @@ int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk, bool fLong) * @returns VBox status code. * @param strmBlk Stream block output data to parse. */ -int GuestFsObjData::FromRm(const GuestProcessStreamBlock &strmBlk) +int GuestFsObjData::FromToolboxRm(const GuestToolboxStreamBlock &strmBlk) { #ifdef DEBUG strmBlk.DumpToLog(); @@ -271,10 +442,10 @@ int GuestFsObjData::FromRm(const GuestProcessStreamBlock &strmBlk) * @returns VBox status code. * @param strmBlk Stream block output data to parse. */ -int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk) +int GuestFsObjData::FromToolboxStat(const GuestToolboxStreamBlock &strmBlk) { /* Should be identical output. */ - return GuestFsObjData::FromLs(strmBlk, true /*fLong*/); + return GuestFsObjData::FromToolboxLs(strmBlk, true /*fLong*/); } /** @@ -284,7 +455,7 @@ int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk) * @returns VBox status code. * @param strmBlk Stream block output data to parse. */ -int GuestFsObjData::FromMkTemp(const GuestProcessStreamBlock &strmBlk) +int GuestFsObjData::FromToolboxMkTemp(const GuestToolboxStreamBlock &strmBlk) { LogFlowFunc(("\n")); @@ -301,6 +472,8 @@ int GuestFsObjData::FromMkTemp(const GuestProcessStreamBlock &strmBlk) return vrc; } +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ + /** * Returns the IPRT-compatible file mode. * Note: Only handling RTFS_TYPE_ flags are implemented for now. @@ -336,15 +509,16 @@ RTFMODE GuestFsObjData::GetFileMode(void) const /////////////////////////////////////////////////////////////////////////////// +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT /** @todo *NOT* thread safe yet! */ /** @todo Add exception handling for STL stuff! */ -GuestProcessStreamBlock::GuestProcessStreamBlock(void) +GuestToolboxStreamBlock::GuestToolboxStreamBlock(void) { } -GuestProcessStreamBlock::~GuestProcessStreamBlock() +GuestToolboxStreamBlock::~GuestToolboxStreamBlock() { Clear(); } @@ -352,7 +526,7 @@ GuestProcessStreamBlock::~GuestProcessStreamBlock() /** * Clears (destroys) the currently stored stream pairs. */ -void GuestProcessStreamBlock::Clear(void) +void GuestToolboxStreamBlock::Clear(void) { mPairs.clear(); } @@ -361,7 +535,7 @@ void GuestProcessStreamBlock::Clear(void) /** * Dumps the currently stored stream pairs to the (debug) log. */ -void GuestProcessStreamBlock::DumpToLog(void) const +void GuestToolboxStreamBlock::DumpToLog(void) const { LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n", this, mPairs.size())); @@ -381,7 +555,7 @@ void GuestProcessStreamBlock::DumpToLog(void) const * @param pszKey Name of key to get the value for. * @param piVal Pointer to value to return. */ -int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const +int GuestToolboxStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const { AssertPtrReturn(pszKey, VERR_INVALID_POINTER); AssertPtrReturn(piVal, VERR_INVALID_POINTER); @@ -400,7 +574,7 @@ int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) cons * @return int64_t Value to return, 0 if not found / on failure. * @param pszKey Name of key to get the value for. */ -int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const +int64_t GuestToolboxStreamBlock::GetInt64(const char *pszKey) const { int64_t iVal; if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal))) @@ -413,7 +587,7 @@ int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const * * @return uint32_t Current number of stream pairs. */ -size_t GuestProcessStreamBlock::GetCount(void) const +size_t GuestToolboxStreamBlock::GetCount(void) const { return mPairs.size(); } @@ -424,7 +598,7 @@ size_t GuestProcessStreamBlock::GetCount(void) const * @return VBox status code. * @retval VERR_NOT_FOUND if the return code string ("rc") was not found. */ -int GuestProcessStreamBlock::GetVrc(void) const +int GuestToolboxStreamBlock::GetVrc(void) const { const char *pszValue = GetString("rc"); if (pszValue) @@ -439,7 +613,7 @@ int GuestProcessStreamBlock::GetVrc(void) const * @return uint32_t Pointer to string to return, NULL if not found / on failure. * @param pszKey Name of key to get the value for. */ -const char *GuestProcessStreamBlock::GetString(const char *pszKey) const +const char *GuestToolboxStreamBlock::GetString(const char *pszKey) const { AssertPtrReturn(pszKey, NULL); @@ -463,7 +637,7 @@ const char *GuestProcessStreamBlock::GetString(const char *pszKey) const * @param pszKey Name of key to get the value for. * @param puVal Pointer to value to return. */ -int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const +int GuestToolboxStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const { const char *pszValue = GetString(pszKey); if (pszValue) @@ -481,7 +655,7 @@ int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) co * @param pszKey Name of key to get the value for. * @param iDefault The default to return on error if not found. */ -int32_t GuestProcessStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) const +int32_t GuestToolboxStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) const { const char *pszValue = GetString(pszKey); if (pszValue) @@ -502,7 +676,7 @@ int32_t GuestProcessStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) * @param pszKey Name of key to get the value for. * @param uDefault The default value to return. */ -uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefault /*= 0*/) const +uint32_t GuestToolboxStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefault /*= 0*/) const { uint32_t uVal; if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal))) @@ -517,7 +691,7 @@ uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefaul * @param pszKey Key name to process. * @param pszValue Value to set. Set NULL for deleting the key. */ -int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue) +int GuestToolboxStreamBlock::SetValue(const char *pszKey, const char *pszValue) { AssertPtrReturn(pszKey, VERR_INVALID_POINTER); @@ -537,7 +711,7 @@ int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue) if (pszValue) { - GuestProcessStreamValue val(pszValue); + GuestToolboxStreamValue val(pszValue); mPairs[strKey] = val; } } @@ -550,14 +724,14 @@ int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue) /////////////////////////////////////////////////////////////////////////////// -GuestProcessStream::GuestProcessStream(void) +GuestToolboxStream::GuestToolboxStream(void) : m_cbMax(_32M) , m_cbAllocated(0) , m_cbUsed(0) , m_offBuffer(0) , m_pbBuffer(NULL) { } -GuestProcessStream::~GuestProcessStream(void) +GuestToolboxStream::~GuestToolboxStream(void) { Destroy(); } @@ -570,7 +744,7 @@ GuestProcessStream::~GuestProcessStream(void) * @param pbData Pointer to data to add. * @param cbData Size (in bytes) of data to add. */ -int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData) +int GuestToolboxStream::AddData(const BYTE *pbData, size_t cbData) { AssertPtrReturn(pbData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); @@ -641,7 +815,7 @@ int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData) /** * Destroys the internal data buffer. */ -void GuestProcessStream::Destroy(void) +void GuestToolboxStream::Destroy(void) { if (m_pbBuffer) { @@ -661,7 +835,7 @@ void GuestProcessStream::Destroy(void) * * @param pszFile Absolute path to host file to dump the output to. */ -void GuestProcessStream::Dump(const char *pszFile) +void GuestToolboxStream::Dump(const char *pszFile) { LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n", m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuffer, pszFile)); @@ -693,7 +867,7 @@ void GuestProcessStream::Dump(const char *pszFile) * @return VBox status code. * @param streamBlock Reference to guest stream block to fill. */ -int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock) +int GuestToolboxStream::ParseBlock(GuestToolboxStreamBlock &streamBlock) { if ( !m_pbBuffer || !m_cbUsed) @@ -748,6 +922,7 @@ int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock) return vrc; } +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ GuestBase::GuestBase(void) : mConsole(NULL) @@ -1351,12 +1526,14 @@ int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEv Utf8Str strErr; -#define CASE_TOOL_ERROR(a_eType, a_strTool) \ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT +# define CASE_TOOL_ERROR(a_eType, a_strTool) \ case a_eType: \ { \ - strErr = GuestProcessTool::guestErrorToString(a_strTool, guestErrorInfo); \ + strErr = GuestProcessToolbox::guestErrorToString(a_strTool, guestErrorInfo); \ break; \ } +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ switch (guestErrorInfo.getType()) { @@ -1376,13 +1553,17 @@ int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEv strErr = GuestDirectory::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str()); break; - CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolCat, VBOXSERVICE_TOOL_CAT); + case GuestErrorInfo::Type_Fs: + strErr = GuestFs::guestErrorToString(guestErrorInfo); + break; + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolLs, VBOXSERVICE_TOOL_LS); CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkDir, VBOXSERVICE_TOOL_MKDIR); CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkTemp, VBOXSERVICE_TOOL_MKTEMP); CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolRm, VBOXSERVICE_TOOL_RM); CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolStat, VBOXSERVICE_TOOL_STAT); - +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ default: AssertMsgFailed(("Type not implemented (type=%RU32, vrc=%Rrc)\n", guestErrorInfo.getType(), guestErrorInfo.getVrc())); strErr = Utf8StrFmt("Unknown / Not implemented -- Please file a bug report (type=%RU32, vrc=%Rrc)\n", @@ -1404,9 +1585,14 @@ int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEv /* static */ FsObjType_T GuestBase::fileModeToFsObjType(RTFMODE fMode) { - if (RTFS_IS_FILE(fMode)) return FsObjType_File; + if (RTFS_IS_FIFO(fMode)) return FsObjType_Fifo; + else if (RTFS_IS_DEV_CHAR(fMode)) return FsObjType_DevChar; else if (RTFS_IS_DIRECTORY(fMode)) return FsObjType_Directory; + else if (RTFS_IS_DEV_BLOCK(fMode)) return FsObjType_DevBlock; + else if (RTFS_IS_FILE(fMode)) return FsObjType_File; else if (RTFS_IS_SYMLINK(fMode)) return FsObjType_Symlink; + else if (RTFS_IS_SOCKET(fMode)) return FsObjType_Socket; + else if (RTFS_IS_WHITEOUT(fMode)) return FsObjType_WhiteOut; return FsObjType_Unknown; } diff --git a/src/VBox/Main/src-client/GuestDirectoryImpl.cpp b/src/VBox/Main/src-client/GuestDirectoryImpl.cpp index 5f20dfd0e1d..e3fd5e8b15b 100644 --- a/src/VBox/Main/src-client/GuestDirectoryImpl.cpp +++ b/src/VBox/Main/src-client/GuestDirectoryImpl.cpp @@ -35,14 +35,18 @@ #ifndef VBOX_WITH_GUEST_CONTROL # error "VBOX_WITH_GUEST_CONTROL must defined in this file" #endif +#include "GuestImpl.h" #include "GuestDirectoryImpl.h" #include "GuestSessionImpl.h" #include "GuestCtrlImplPrivate.h" +#include "VirtualBoxErrorInfoImpl.h" #include "Global.h" #include "AutoCaller.h" +#include "VBoxEvents.h" #include <VBox/com/array.h> +#include <VBox/AssertGuest.h> // constructor / destructor @@ -69,8 +73,8 @@ void GuestDirectory::FinalRelease(void) int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjectID, const GuestDirectoryOpenInfo &openInfo) { - LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, strFilter=%s, uFlags=%x\n", - pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags)); + LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, enmFilter=%#x, fFlags=%x\n", + pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.menmFilter, openInfo.mFlags)); AssertPtrReturn(pConsole, VERR_INVALID_POINTER); AssertPtrReturn(pSession, VERR_INVALID_POINTER); @@ -85,55 +89,14 @@ int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjec mSession = pSession; mObjectID = aObjectID; - mData.mOpenInfo = openInfo; - } - - if (RT_SUCCESS(vrc)) - { - /* Start the directory process on the guest. */ - GuestProcessStartupInfo procInfo; - procInfo.mName.printf(tr("Opening directory \"%s\""), openInfo.mPath.c_str()); - procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */ - procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; - procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS); - - procInfo.mArguments.push_back(procInfo.mExecutable); - procInfo.mArguments.push_back(Utf8Str("--machinereadable")); - /* We want the long output format which contains all the object details. */ - procInfo.mArguments.push_back(Utf8Str("-l")); -#if 0 /* Flags are not supported yet. */ - if (uFlags & DirectoryOpenFlag_NoSymlinks) - procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */ -#endif - /** @todo Recursion support? */ - procInfo.mArguments.push_back(openInfo.mPath); /* The directory we want to open. */ + mData.mOpenInfo = openInfo; + mData.mStatus = DirectoryStatus_Undefined; + mData.mLastError = VINF_SUCCESS; - /* - * Start the process synchronously and keep it around so that we can use - * it later in subsequent read() calls. - */ - vrc = mData.mProcessTool.init(mSession, procInfo, false /*fAsync*/, NULL /*pvrcGuest*/); - if (RT_SUCCESS(vrc)) - { - /* As we need to know if the directory we were about to open exists and and is accessible, - * do the first read here in order to return a meaningful status here. */ - int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; - vrc = i_readInternal(mData.mObjData, &vrcGuest); - if (RT_FAILURE(vrc)) - { - /* - * We need to actively terminate our process tool in case of an error here, - * as this otherwise would be done on (directory) object destruction implicitly. - * This in turn then will run into a timeout, as the directory object won't be - * around anymore at that time. Ugly, but that's how it is for the moment. - */ - int vrcTerm = mData.mProcessTool.terminate(30 * RT_MS_1SEC, NULL /* prcGuest */); - AssertRC(vrcTerm); - - if (vrc == VERR_GSTCTL_GUEST_ERROR) - vrc = vrcGuest; - } - } + unconst(mEventSource).createObject(); + HRESULT hr = mEventSource->init(); + if (FAILED(hr)) + vrc = VERR_COM_UNEXPECTED; } /* Confirm a successful initialization when it's the case. */ @@ -176,6 +139,14 @@ HRESULT GuestDirectory::getDirectoryName(com::Utf8Str &aDirectoryName) return S_OK; } +HRESULT GuestDirectory::getEventSource(ComPtr<IEventSource> &aEventSource) +{ + /* No need to lock - lifetime constant. */ + mEventSource.queryInterfaceTo(aEventSource.asOutParam()); + + return S_OK; +} + HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter) { LogFlowThisFuncEnter(); @@ -187,6 +158,26 @@ HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter) return S_OK; } +HRESULT GuestDirectory::getId(ULONG *aId) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aId = mObjectID; + + return S_OK; +} + +HRESULT GuestDirectory::getStatus(DirectoryStatus_T *aStatus) +{ + LogFlowThisFuncEnter(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aStatus = mData.mStatus; + + return S_OK; +} + // private methods ///////////////////////////////////////////////////////////////////////////// @@ -208,35 +199,288 @@ int GuestDirectory::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGU int vrc; switch (pCbCtx->uMessage) { + case GUEST_MSG_DISCONNECTED: + /** @todo vrc = i_onGuestDisconnected(pCbCtx, pSvcCb); */ + vrc = VINF_SUCCESS; // TODO + break; + case GUEST_MSG_DIR_NOTIFY: { - int idx = 1; /* Current parameter index. */ - CALLBACKDATA_DIR_NOTIFY dataCb; - /* pSvcCb->mpaParms[0] always contains the context ID. */ - HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType); - HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc); + vrc = i_onDirNotify(pCbCtx, pSvcCb); + break; + } - LogFlowFunc(("uType=%RU32, vrcGguest=%Rrc\n", dataCb.uType, (int)dataCb.rc)); + default: + /* Silently ignore not implemented functions. */ + vrc = VERR_NOT_SUPPORTED; + break; + } - switch (dataCb.uType) - { - /* Nothing here yet, nothing to dispatch further. */ + LogFlowFuncLeaveRC(vrc); + return vrc; +} - default: - vrc = VERR_NOT_SUPPORTED; - break; +/** + * Opens the directory on the guest side. + * + * @return VBox status code. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + */ +int GuestDirectory::i_open(int *pvrcGuest) +{ + int vrc; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + if ((mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[8]; + int i = 0; + HGCMSvcSetU32(&paParms[i++], pEvent->ContextID()); + HGCMSvcSetPv(&paParms[i++], (void *)mData.mOpenInfo.mPath.c_str(), (ULONG)mData.mOpenInfo.mPath.length() + 1); + HGCMSvcSetPv(&paParms[i++], (void *)mData.mOpenInfo.mFilter.c_str(), (ULONG)mData.mOpenInfo.mFilter.length() + 1); + HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.menmFilter); + HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.mFlags); + + alock.release(); /* Drop lock before sending. */ + + vrc = sendMessage(HOST_MSG_DIR_OPEN, i, paParms); + if (RT_SUCCESS(vrc)) + { + vrc = pEvent->Wait(30 * 1000); + if (RT_SUCCESS(vrc)) + { } + } + } + else + { +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + vrc = i_openViaToolbox(pvrcGuest); +#else + RT_NOREF(pvrcGuest); + vrc = VERR_NOT_SUPPORTED; +#endif +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + } +#endif + + return vrc; +} + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT +/** + * Opens the directory on the guest side (legacy version). + * + * @returns VBox status code. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + * + * @note This uses an own guest process via the built-in toolbox in VBoxSerivce. + */ +int GuestDirectory::i_openViaToolbox(int *pvrcGuest) +{ + /* Start the directory process on the guest. */ + GuestProcessStartupInfo procInfo; + procInfo.mName.printf(tr("Opening directory \"%s\""), mData.mOpenInfo.mPath.c_str()); + procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */ + procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; + procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS); + + procInfo.mArguments.push_back(procInfo.mExecutable); + procInfo.mArguments.push_back(Utf8Str("--machinereadable")); + /* We want the long output format which contains all the object details. */ + procInfo.mArguments.push_back(Utf8Str("-l")); +# if 0 /* Flags are not supported yet. */ + if (uFlags & DirectoryOpenFlag_NoSymlinks) + procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */ +# endif + /** @todo Recursion support? */ + procInfo.mArguments.push_back(mData.mOpenInfo.mPath); /* The directory we want to open. */ + + /* + * Start the process synchronously and keep it around so that we can use + * it later in subsequent read() calls. + */ + int vrc = mData.mProcessTool.init(mSession, procInfo, false /*fAsync*/, NULL /*pvrcGuest*/); + if (RT_SUCCESS(vrc)) + { + /* As we need to know if the directory we were about to open exists and and is accessible, + * do the first read here in order to return a meaningful status here. */ + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; + vrc = i_readInternal(mData.mObjData, &vrcGuest); + if (RT_FAILURE(vrc)) + { + /* + * We need to actively terminate our process tool in case of an error here, + * as this otherwise would be done on (directory) object destruction implicitly. + * This in turn then will run into a timeout, as the directory object won't be + * around anymore at that time. Ugly, but that's how it is for the moment. + */ + /* ignore rc */ mData.mProcessTool.terminate(30 * RT_MS_1SEC, NULL /* pvrcGuest */); + } + + if (pvrcGuest) + *pvrcGuest = vrcGuest; + } + + return vrc; +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ + +/** + * Called when the guest side notifies the host of a directory event. + * + * @returns VBox status code. + * @param pCbCtx Host callback context. + * @param pSvcCbData Host callback data. + */ +int GuestDirectory::i_onDirNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData) +{ +#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + RT_NOREF(pCbCtx, pSvcCbData); + return VERR_NOT_SUPPORTED; +#else + AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER); + + LogFlowThisFuncEnter(); + + if (pSvcCbData->mParms < 3) + return VERR_INVALID_PARAMETER; + + int idx = 1; /* Current parameter index. */ + CALLBACKDATA_DIR_NOTIFY dataCb; + RT_ZERO(dataCb); + /* pSvcCb->mpaParms[0] always contains the context ID. */ + HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.uType); + HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.rc); + + int vrcGuest = (int)dataCb.rc; /* uint32_t vs. int. */ + + LogFlowThisFunc(("uType=%RU32, vrcGuest=%Rrc\n", dataCb.uType, vrcGuest)); + + if (RT_FAILURE(vrcGuest)) + { + /** @todo Set status? */ + + /* Ignore return code, as the event to signal might not be there (anymore). */ + signalWaitEventInternal(pCbCtx, vrcGuest, NULL /* pPayload */); + return VINF_SUCCESS; /* Report to the guest. */ + } + + int vrc = VERR_NOT_SUPPORTED; /* Play safe by default. */ + + ComObjPtr<VirtualBoxErrorInfo> errorInfo; + HRESULT hrc = errorInfo.createObject(); + ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED); + if (RT_FAILURE(vrcGuest)) + { + hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcGuest, + COM_IIDOF(IGuestFile), getComponentName(), + i_guestErrorToString(vrcGuest, mData.mOpenInfo.mPath.c_str())); + ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED); + } + + switch (dataCb.uType) + { + case GUEST_DIR_NOTIFYTYPE_ERROR: + { + vrc = i_setStatus(DirectoryStatus_Error, vrcGuest); + break; + } + + case GUEST_DIR_NOTIFYTYPE_OPEN: + { + vrc = i_setStatus(DirectoryStatus_Open, vrcGuest); + break; + } + + case GUEST_DIR_NOTIFYTYPE_CLOSE: + { + vrc = i_setStatus(DirectoryStatus_Close, vrcGuest); + break; + } + + case GUEST_DIR_NOTIFYTYPE_READ: + { + ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 7, ("mParms=%u\n", pSvcCbData->mParms), + vrc = VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_PTR, + ("type=%u\n", pSvcCbData->mpaParms[idx].type), + vrc = VERR_WRONG_PARAMETER_TYPE); + + PGSTCTLFSOBJINFO pObjInfo; + uint32_t cbObjInfo; + vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[idx++], (void **)&pObjInfo, &cbObjInfo); + AssertRCBreak(vrc); + AssertBreakStmt(cbObjInfo == sizeof(GSTCTLFSOBJINFO), VERR_INVALID_PARAMETER); + + GuestFsObjData fsObjData(mData.mOpenInfo.mPath); + vrc = fsObjData.FromGuestFsObjInfo(pObjInfo); + AssertRCBreak(vrc); + ComObjPtr<GuestFsObjInfo> ptrFsObjInfo; + hrc = ptrFsObjInfo.createObject(); + ComAssertComRCBreak(hrc, VERR_COM_UNEXPECTED); + vrc = ptrFsObjInfo->init(fsObjData); + AssertRCBreak(vrc); + + ::FireGuestDirectoryReadEvent(mEventSource, mSession, this, ptrFsObjInfo); + break; + } + + case GUEST_DIR_NOTIFYTYPE_REWIND: + { + /* Note: This does not change the overall status of the directory (i.e. open). */ + ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, DirectoryStatus_Rewind, errorInfo); break; } default: - /* Silently ignore not implemented functions. */ - vrc = VERR_NOT_SUPPORTED; + AssertFailed(); break; } - LogFlowFuncLeaveRC(vrc); + try + { + if (RT_SUCCESS(vrc)) + { + GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb)); + + /* Ignore return code, as the event to signal might not be there (anymore). */ + signalWaitEventInternal(pCbCtx, vrcGuest, &payload); + } + else /* OOM situation, wrong HGCM parameters or smth. not expected. */ + { + /* Ignore return code, as the event to signal might not be there (anymore). */ + signalWaitEventInternalEx(pCbCtx, vrc, 0 /* guestRc */, NULL /* pPayload */); + } + } + catch (int vrcEx) /* Thrown by GuestWaitEventPayload constructor. */ + { + /* Also try to signal the waiter, to let it know of the OOM situation. + * Ignore return code, as the event to signal might not be there (anymore). */ + signalWaitEventInternalEx(pCbCtx, vrcEx, 0 /* guestRc */, NULL /* pPayload */); + vrc = vrcEx; + } + + LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc, vrc=%Rrc\n", dataCb.uType, vrcGuest, vrc)); return vrc; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ } /** @@ -301,15 +545,29 @@ int GuestDirectory::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatu * guest session's directory list. * * @return VBox status code. - * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. */ -int GuestDirectory::i_closeInternal(int *prcGuest) +int GuestDirectory::i_close(int *pvrcGuest) { - AssertPtrReturn(prcGuest, VERR_INVALID_POINTER); - - int vrc = mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, prcGuest); - if (RT_FAILURE(vrc)) - return vrc; + int vrc; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS) + { + // TODO + vrc = VERR_NOT_IMPLEMENTED; + } + else + { +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ +#ifndef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + RT_NOREF(pvrcGuest); + vrc = VINF_SUCCESS; /* Nothing to do here. */ +#else + vrc = i_closeViaToolbox(pvrcGuest); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + } +#endif AssertPtr(mSession); int vrc2 = mSession->i_directoryUnregister(this); @@ -320,19 +578,71 @@ int GuestDirectory::i_closeInternal(int *prcGuest) return vrc; } +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT +/** + * Closes this guest directory and removes it from the guest session's directory list (legacy version). + * + * @return VBox status code. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + * + * @note This uses an own guest process via the built-in toolbox in VBoxSerivce. + */ +int GuestDirectory::i_closeViaToolbox(int *pvrcGuest) +{ + return mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, pvrcGuest); +} +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ + /** * Reads the next directory entry, internal version. * * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available. * @param objData Where to store the read directory entry as internal object data. - * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. */ -int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *prcGuest) +int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *pvrcGuest) { - AssertPtrReturn(prcGuest, VERR_INVALID_POINTER); + AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER); + + int vrc; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS) + { + // TODO + RT_NOREF(objData, pvrcGuest); + vrc = 0; + } + else + { +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ +#ifndef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + RT_NOREF(objData); + vrc = VERR_NOT_SUPPORTED; +#else /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ + vrc = i_readInternalViaToolbox(objData, pvrcGuest); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + } +#endif - GuestProcessStreamBlock curBlock; - int vrc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, prcGuest); + LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc)); + return vrc; +} + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT +/** + * Reads the next directory entry, internal version (legacy version). + * + * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available. + * @param objData Where to store the read directory entry as internal object data. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + * + * @note This uses an own guest process via the built-in toolbox in VBoxSerivce. + */ +int GuestDirectory::i_readInternalViaToolbox(GuestFsObjData &objData, int *pvrcGuest) +{ + GuestToolboxStreamBlock curBlock; + int vrc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, pvrcGuest); if (RT_SUCCESS(vrc)) { /* @@ -348,7 +658,7 @@ int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *prcGuest) { if (curBlock.GetString("name")) { - vrc = objData.FromLs(curBlock, true /* fLong */); + vrc = objData.FromToolboxLs(curBlock, true /* fLong */); } else vrc = VERR_PATH_NOT_FOUND; @@ -361,20 +671,20 @@ int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *prcGuest) } } - LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc)); return vrc; } +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ /** * Reads the next directory entry. * * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available. * @param fsObjInfo Where to store the read directory entry. - * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. */ -int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *prcGuest) +int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *pvrcGuest) { - AssertPtrReturn(prcGuest, VERR_INVALID_POINTER); + AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER); /* Create the FS info object. */ HRESULT hr = fsObjInfo.createObject(); @@ -392,11 +702,11 @@ int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *prcGuest) mData.mObjData.mName = ""; /* Mark the object data as being empty (beacon). */ } } - else /* Otherwise ask the guest for the next object data (block). */ + else /* Otherwise ask the guest for the next object data. */ { GuestFsObjData objData; - vrc = i_readInternal(objData, prcGuest); + vrc = i_readInternal(objData, pvrcGuest); if (RT_SUCCESS(vrc)) vrc = fsObjInfo->init(objData); } @@ -405,6 +715,127 @@ int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *prcGuest) return vrc; } +/** + * Rewinds the directory reading. + * + * @returns VBox status code. + * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received. + * @param uTimeoutMS Timeout (in ms) to wait. + * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + */ +int GuestDirectory::i_rewind(uint32_t uTimeoutMS, int *pvrcGuest) +{ + RT_NOREF(pvrcGuest); +#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + RT_NOREF(uTimeoutMS, pvrcGuest); +#else + /* Only available for Guest Additions 7.1+. */ + if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged); + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + HGCMSvcSetU32(&paParms[i++], pEvent->ContextID()); + HGCMSvcSetU32(&paParms[i++], mObjectID /* Directory handle */); + + alock.release(); /* Drop lock before sending. */ + + vrc = sendMessage(HOST_MSG_DIR_REWIND, i, paParms); + if (RT_SUCCESS(vrc)) + { + VBoxEventType_T evtType; + ComPtr<IEvent> pIEvent; + vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam()); + if (RT_SUCCESS(vrc)) + { + if (evtType == VBoxEventType_OnGuestDirectoryStateChanged) + { + ComPtr<IGuestDirectoryStateChangedEvent> pEvt = pIEvent; + Assert(!pEvt.isNull()); + } + else + vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED; + } + else if (pEvent->HasGuestError()) /* Return guest vrc if available. */ + vrc = pEvent->GetGuestError(); + } + + unregisterWaitEvent(pEvent); + return vrc; + } +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ + + return VERR_NOT_SUPPORTED; +} + +/** + * Sets the current internal directory object status. + * + * @returns VBox status code. + * @param enmStatus New directory status to set. + * @param vrcDir New result code to set. + * + * @note Takes the write lock. + */ +int GuestDirectory::i_setStatus(DirectoryStatus_T enmStatus, int vrcDir) +{ + LogFlowThisFuncEnter(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcDir=%Rrc\n", mData.mStatus, enmStatus, vrcDir)); + +#ifdef VBOX_STRICT + if (enmStatus == DirectoryStatus_Error) + AssertMsg(RT_FAILURE(vrcDir), ("Guest vrc must be an error (%Rrc)\n", vrcDir)); + else + AssertMsg(RT_SUCCESS(vrcDir), ("Guest vrc must not be an error (%Rrc)\n", vrcDir)); +#endif + + if (mData.mStatus != enmStatus) + { + mData.mStatus = enmStatus; + mData.mLastError = vrcDir; + + ComObjPtr<VirtualBoxErrorInfo> errorInfo; + HRESULT hrc = errorInfo.createObject(); + ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED); + if (RT_FAILURE(vrcDir)) + { + hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcDir, + COM_IIDOF(IGuestDirectory), getComponentName(), + i_guestErrorToString(vrcDir, mData.mOpenInfo.mPath.c_str())); + ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED); + } + /* Note: On vrcDir success, errorInfo is set to S_OK and also sent via the event below. */ + + alock.release(); /* Release lock before firing off event. */ + + ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, enmStatus, errorInfo); + } + + return VINF_SUCCESS; +} + // implementation of public methods ///////////////////////////////////////////////////////////////////////////// HRESULT GuestDirectory::close() @@ -417,7 +848,7 @@ HRESULT GuestDirectory::close() HRESULT hrc = S_OK; int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; - int vrc = i_closeInternal(&vrcGuest); + int vrc = i_close(&vrcGuest); if (RT_FAILURE(vrc)) { switch (vrc) @@ -467,16 +898,24 @@ HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo) { case VERR_GSTCTL_GUEST_ERROR: { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolLs, vrcGuest, mData.mOpenInfo.mPath.c_str()); + GuestErrorInfo ge( +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + GuestErrorInfo::Type_ToolLs +#else + GuestErrorInfo::Type_Fs +#endif + , vrcGuest, mData.mOpenInfo.mPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Reading guest directory failed: %s"), GuestBase::getErrorAsString(ge).c_str()); break; } + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT case VERR_GSTCTL_PROCESS_EXIT_CODE: hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc()); break; - +#endif case VERR_PATH_NOT_FOUND: hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: Path not found"), mData.mOpenInfo.mPath.c_str()); @@ -489,7 +928,7 @@ HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo) break; default: - hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" returned error: %Rrc\n"), + hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" returned unhandled error: %Rrc\n"), mData.mOpenInfo.mPath.c_str(), vrc); break; } @@ -499,3 +938,18 @@ HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo) return hrc; } +HRESULT GuestDirectory::rewind(void) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.hrc())) return autoCaller.hrc(); + + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; + int vrc = i_rewind(30 * 1000 /* Timeout in ms */, &vrcGuest); + if (RT_SUCCESS(vrc)) + return S_OK; + + GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str()); + return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Rewinding guest directory failed: %s"), + GuestBase::getErrorAsString(ge).c_str()); +} + diff --git a/src/VBox/Main/src-client/GuestFileImpl.cpp b/src/VBox/Main/src-client/GuestFileImpl.cpp index dcbbfe88e13..101a0b5ca46 100644 --- a/src/VBox/Main/src-client/GuestFileImpl.cpp +++ b/src/VBox/Main/src-client/GuestFileImpl.cpp @@ -899,7 +899,7 @@ int GuestFile::i_openFile(uint32_t uTimeoutMS, int *prcGuest) */ int GuestFile::i_queryInfo(GuestFsObjData &objData, int *prcGuest) { - AssertPtr(mSession); + AssertPtrReturn(mSession, VERR_OBJECT_DESTROYED); return mSession->i_fsQueryInfo(mData.mOpenInfo.mFilename, FALSE /* fFollowSymlinks */, objData, prcGuest); } @@ -1575,7 +1575,13 @@ HRESULT GuestFile::queryInfo(ComPtr<IFsObjInfo> &aObjInfo) { if (GuestProcess::i_isGuestError(vrc)) { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, vrcGuest, mData.mOpenInfo.mFilename.c_str()); + GuestErrorInfo ge( +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + GuestErrorInfo::Type_ToolStat, +#else + GuestErrorInfo::Type_File, +#endif + vrcGuest, mData.mOpenInfo.mFilename.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file information failed: %s"), GuestBase::getErrorAsString(ge).c_str()); } @@ -1608,7 +1614,13 @@ HRESULT GuestFile::querySize(LONG64 *aSize) { if (GuestProcess::i_isGuestError(vrc)) { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, vrcGuest, mData.mOpenInfo.mFilename.c_str()); + GuestErrorInfo ge( +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + GuestErrorInfo::Type_ToolStat, +#else + GuestErrorInfo::Type_File, +#endif + vrcGuest, mData.mOpenInfo.mFilename.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file size failed: %s"), GuestBase::getErrorAsString(ge).c_str()); } diff --git a/src/VBox/Main/src-client/GuestProcessImpl.cpp b/src/VBox/Main/src-client/GuestProcessImpl.cpp index bc9a6538c33..829047325b9 100644 --- a/src/VBox/Main/src-client/GuestProcessImpl.cpp +++ b/src/VBox/Main/src-client/GuestProcessImpl.cpp @@ -2252,13 +2252,13 @@ HRESULT GuestProcess::writeArray(ULONG aHandle, const std::vector<ProcessInputFl /////////////////////////////////////////////////////////////////////////////// -GuestProcessTool::GuestProcessTool(void) +GuestProcessToolbox::GuestProcessToolbox(void) : pSession(NULL), pProcess(NULL) { } -GuestProcessTool::~GuestProcessTool(void) +GuestProcessToolbox::~GuestProcessToolbox(void) { uninit(); } @@ -2273,8 +2273,8 @@ GuestProcessTool::~GuestProcessTool(void) * @param pvrcGuest Where to return the guest error when * VERR_GSTCTL_GUEST_ERROR was returned. Optional. */ -int GuestProcessTool::init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo, - bool fAsync, int *pvrcGuest) +int GuestProcessToolbox::init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo, + bool fAsync, int *pvrcGuest) { LogFlowThisFunc(("pGuestSession=%p, exe=%s, fAsync=%RTbool\n", pGuestSession, startupInfo.mExecutable.c_str(), fAsync)); @@ -2315,7 +2315,7 @@ int GuestProcessTool::init(GuestSession *pGuestSession, const GuestProcessStartu /** * Unitializes a guest process tool by terminating it on the guest. */ -void GuestProcessTool::uninit(void) +void GuestProcessToolbox::uninit(void) { /* Make sure the process is terminated and unregistered from the guest session. */ int vrcGuestIgnored; @@ -2338,9 +2338,9 @@ void GuestProcessTool::uninit(void) * @param uHandle Guest process file handle to get current block for. * @param strmBlock Where to return the stream block on success. */ -int GuestProcessTool::getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock &strmBlock) +int GuestProcessToolbox::getCurrentBlock(uint32_t uHandle, GuestToolboxStreamBlock &strmBlock) { - const GuestProcessStream *pStream = NULL; + const GuestToolboxStream *pStream = NULL; if (uHandle == GUEST_PROC_OUT_H_STDOUT) pStream = &mStdOut; else if (uHandle == GUEST_PROC_OUT_H_STDERR) @@ -2369,13 +2369,13 @@ int GuestProcessTool::getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock * * @returns Result code from guest process tool. */ -int GuestProcessTool::getRc(void) const +int GuestProcessToolbox::getRc(void) const { LONG exitCode = -1; HRESULT hrc = pProcess->COMGETTER(ExitCode(&exitCode)); AssertComRC(hrc); - return GuestProcessTool::exitCodeToRc(mStartupInfo, exitCode); + return GuestProcessToolbox::exitCodeToRc(mStartupInfo, exitCode); } /** @@ -2383,7 +2383,7 @@ int GuestProcessTool::getRc(void) const * * @returns \c true if running, or \c false if not. */ -bool GuestProcessTool::isRunning(void) +bool GuestProcessToolbox::isRunning(void) { AssertReturn(!pProcess.isNull(), false); @@ -2408,7 +2408,7 @@ bool GuestProcessTool::isRunning(void) * @return @c true if the tool has been run correctly (exit status 0), or @c false if some error * occurred (exit status <> 0 or wrong process state). */ -bool GuestProcessTool::isTerminatedOk(void) +bool GuestProcessToolbox::isTerminatedOk(void) { return getTerminationStatus() == VINF_SUCCESS ? true : false; } @@ -2427,9 +2427,9 @@ bool GuestProcessTool::isTerminatedOk(void) * VERR_GSTCTL_GUEST_ERROR is returned. */ /* static */ -int GuestProcessTool::run( GuestSession *pGuestSession, - const GuestProcessStartupInfo &startupInfo, - int *pvrcGuest /* = NULL */) +int GuestProcessToolbox::run( GuestSession *pGuestSession, + const GuestProcessStartupInfo &startupInfo, + int *pvrcGuest /* = NULL */) { int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; @@ -2441,7 +2441,7 @@ int GuestProcessTool::run( GuestSession *pGuestSession, if (GuestProcess::i_isGuestError(errorInfo.vrcGuest)) { if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */ - vrcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode); + vrcGuest = GuestProcessToolbox::exitCodeToRc(startupInfo, errorInfo.iExitCode); else /* At least return something. */ vrcGuest = errorInfo.vrcGuest; @@ -2466,9 +2466,9 @@ int GuestProcessTool::run( GuestSession *pGuestSession, * @param errorInfo Error information returned for error handling. */ /* static */ -int GuestProcessTool::runErrorInfo(GuestSession *pGuestSession, - GuestProcessStartupInfo const &startupInfo, - GuestProcessToolErrorInfo &errorInfo) +int GuestProcessToolbox::runErrorInfo(GuestSession *pGuestSession, + GuestProcessStartupInfo const &startupInfo, + GuestProcessToolErrorInfo &errorInfo) { return runExErrorInfo(pGuestSession, startupInfo, NULL /* paStrmOutObjects */, 0 /* cStrmOutObjects */, errorInfo); } @@ -2485,23 +2485,23 @@ int GuestProcessTool::runErrorInfo(GuestSession *pGuestSessio * @param pvrcGuest Error code returned from the guest side if VERR_GSTCTL_GUEST_ERROR is returned. Optional. */ /* static */ -int GuestProcessTool::runEx(GuestSession *pGuestSession, - GuestProcessStartupInfo const &startupInfo, - GuestCtrlStreamObjects *paStrmOutObjects, - uint32_t cStrmOutObjects, - int *pvrcGuest /* = NULL */) +int GuestProcessToolbox::runEx(GuestSession *pGuestSession, + GuestProcessStartupInfo const &startupInfo, + GuestCtrlStreamObjects *paStrmOutObjects, + uint32_t cStrmOutObjects, + int *pvrcGuest /* = NULL */) { int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; GuestProcessToolErrorInfo errorInfo = { VERR_IPE_UNINITIALIZED_STATUS, INT32_MAX }; - int vrc = GuestProcessTool::runExErrorInfo(pGuestSession, startupInfo, paStrmOutObjects, cStrmOutObjects, errorInfo); + int vrc = GuestProcessToolbox::runExErrorInfo(pGuestSession, startupInfo, paStrmOutObjects, cStrmOutObjects, errorInfo); if (RT_SUCCESS(vrc)) { /* Make sure to check the error information we got from the guest tool. */ if (GuestProcess::i_isGuestError(errorInfo.vrcGuest)) { if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE) /* Translate exit code to a meaningful error code. */ - vrcGuest = GuestProcessTool::exitCodeToRc(startupInfo, errorInfo.iExitCode); + vrcGuest = GuestProcessToolbox::exitCodeToRc(startupInfo, errorInfo.iExitCode); else /* At least return something. */ vrcGuest = errorInfo.vrcGuest; @@ -2532,18 +2532,18 @@ int GuestProcessTool::runEx(GuestSession *pGuestSession, * @param errorInfo Error information returned for error handling. */ /* static */ -int GuestProcessTool::runExErrorInfo(GuestSession *pGuestSession, - GuestProcessStartupInfo const &startupInfo, - GuestCtrlStreamObjects *paStrmOutObjects, - uint32_t cStrmOutObjects, - GuestProcessToolErrorInfo &errorInfo) +int GuestProcessToolbox::runExErrorInfo(GuestSession *pGuestSession, + GuestProcessStartupInfo const &startupInfo, + GuestCtrlStreamObjects *paStrmOutObjects, + uint32_t cStrmOutObjects, + GuestProcessToolErrorInfo &errorInfo) { AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER); /* paStrmOutObjects is optional. */ /** @todo Check if this is a valid toolbox. */ - GuestProcessTool procTool; + GuestProcessToolbox procTool; int vrc = procTool.init(pGuestSession, startupInfo, false /* Async */, &errorInfo.vrcGuest); if (RT_SUCCESS(vrc)) { @@ -2551,7 +2551,7 @@ int GuestProcessTool::runExErrorInfo(GuestSession *pGuestSessio { try { - GuestProcessStreamBlock strmBlk; + GuestToolboxStreamBlock strmBlk; vrc = procTool.waitEx( paStrmOutObjects ? GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK : GUESTPROCESSTOOL_WAIT_FLAG_NONE, &strmBlk, &errorInfo.vrcGuest); @@ -2589,7 +2589,7 @@ int GuestProcessTool::runExErrorInfo(GuestSession *pGuestSessio * * @param piExitCode Exit code of the tool. Optional. */ -int GuestProcessTool::getTerminationStatus(int32_t *piExitCode /* = NULL */) +int GuestProcessToolbox::getTerminationStatus(int32_t *piExitCode /* = NULL */) { Assert(!pProcess.isNull()); /* pExitCode is optional. */ @@ -2621,7 +2621,7 @@ int GuestProcessTool::getTerminationStatus(int32_t *piExitCode /* = NULL */) * @param pvrcGuest Where to return the guest error when * VERR_GSTCTL_GUEST_ERROR was returned. Optional. */ -int GuestProcessTool::wait(uint32_t fToolWaitFlags, int *pvrcGuest) +int GuestProcessToolbox::wait(uint32_t fToolWaitFlags, int *pvrcGuest) { return waitEx(fToolWaitFlags, NULL /* pStrmBlkOut */, pvrcGuest); } @@ -2635,7 +2635,7 @@ int GuestProcessTool::wait(uint32_t fToolWaitFlags, int *pvrcGuest) * @param pvrcGuest Where to return the guest error when * VERR_GSTCTL_GUEST_ERROR was returned. Optional. */ -int GuestProcessTool::waitEx(uint32_t fToolWaitFlags, GuestProcessStreamBlock *pStrmBlkOut, int *pvrcGuest) +int GuestProcessToolbox::waitEx(uint32_t fToolWaitFlags, GuestToolboxStreamBlock *pStrmBlkOut, int *pvrcGuest) { LogFlowThisFunc(("fToolWaitFlags=0x%x, pStreamBlock=%p, pvrcGuest=%p\n", fToolWaitFlags, pStrmBlkOut, pvrcGuest)); @@ -2829,7 +2829,7 @@ int GuestProcessTool::waitEx(uint32_t fToolWaitFlags, GuestProcessStreamBlock *p * @param pvrcGuest Where to return the guest error when * VERR_GSTCTL_GUEST_ERROR was returned. Optional. */ -int GuestProcessTool::terminate(uint32_t uTimeoutMS, int *pvrcGuest) +int GuestProcessToolbox::terminate(uint32_t uTimeoutMS, int *pvrcGuest) { LogFlowThisFuncEnter(); @@ -2851,7 +2851,7 @@ int GuestProcessTool::terminate(uint32_t uTimeoutMS, int *pvrcGuest) * @param iExitCode The toolbox tool's exit code to lookup IPRT error for. */ /* static */ -int GuestProcessTool::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode) +int GuestProcessToolbox::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode) { if (startupInfo.mArguments.size() == 0) { @@ -2870,7 +2870,7 @@ int GuestProcessTool::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, i * @param iExitCode The toolbox tool's exit code to lookup IPRT error for. */ /* static */ -int GuestProcessTool::exitCodeToRc(const char *pszTool, int32_t iExitCode) +int GuestProcessToolbox::exitCodeToRc(const char *pszTool, int32_t iExitCode) { AssertPtrReturn(pszTool, VERR_INVALID_POINTER); @@ -2952,7 +2952,7 @@ int GuestProcessTool::exitCodeToRc(const char *pszTool, int32_t iExitCode) * @param guestErrorInfo Guest error info to get stringyfied error for. */ /* static */ -Utf8Str GuestProcessTool::guestErrorToString(const char *pszTool, const GuestErrorInfo &guestErrorInfo) +Utf8Str GuestProcessToolbox::guestErrorToString(const char *pszTool, const GuestErrorInfo &guestErrorInfo) { Utf8Str strErr; diff --git a/src/VBox/Main/src-client/GuestSessionImpl.cpp b/src/VBox/Main/src-client/GuestSessionImpl.cpp index 93b31712432..bc5e2015c2b 100644 --- a/src/VBox/Main/src-client/GuestSessionImpl.cpp +++ b/src/VBox/Main/src-client/GuestSessionImpl.cpp @@ -951,47 +951,90 @@ int GuestSession::i_directoryCreate(const Utf8Str &strPath, uint32_t uMode, uint int vrc = VINF_SUCCESS; - GuestProcessStartupInfo procInfo; - procInfo.mFlags = ProcessCreateFlag_Hidden; - procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKDIR); - - try +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + if (mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS) { - procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - /* Construct arguments. */ - if (uFlags) + GuestWaitEvent *pEvent = NULL; + vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent); + if (RT_FAILURE(vrc)) + return vrc; + + uint32_t fFlags = GSTCTL_CREATEDIRECTORY_F_NONE; + if (uFlags & DirectoryCreateFlag_Parents) + fFlags |= GSTCTL_CREATEDIRECTORY_F_PARENTS; + Assert(!(fFlags & ~GSTCTL_CREATEDIRECTORY_F_VALID_MASK)); + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + HGCMSvcSetU32(&paParms[i++], pEvent->ContextID()); + HGCMSvcSetPv (&paParms[i++], (void*)strPath.c_str(), (ULONG)strPath.length() + 1); + HGCMSvcSetU32(&paParms[i++], fFlags); + HGCMSvcSetU32(&paParms[i++], uMode); + + alock.release(); /* Drop lock before sending. */ + + vrc = i_sendMessage(HOST_MSG_DIR_CREATE, i, paParms); + if (RT_SUCCESS(vrc)) { - if (uFlags & DirectoryCreateFlag_Parents) - procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */ - else - vrc = VERR_INVALID_PARAMETER; + vrc = pEvent->Wait(30 * 1000); + if (RT_SUCCESS(vrc)) + { + // TODO + } } + } + else + { +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + GuestProcessStartupInfo procInfo; + procInfo.mFlags = ProcessCreateFlag_Hidden; + procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKDIR); - if ( RT_SUCCESS(vrc) - && uMode) + try { - procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */ + procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ - char szMode[16]; - if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode)) + /* Construct arguments. */ + if (uFlags) { - procInfo.mArguments.push_back(Utf8Str(szMode)); + if (uFlags & DirectoryCreateFlag_Parents) + procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */ + else + vrc = VERR_INVALID_PARAMETER; } - else - vrc = VERR_BUFFER_OVERFLOW; + + if ( RT_SUCCESS(vrc) + && uMode) + { + procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */ + + char szMode[16]; + if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode)) + { + procInfo.mArguments.push_back(Utf8Str(szMode)); + } + else + vrc = VERR_BUFFER_OVERFLOW; + } + + procInfo.mArguments.push_back("--"); /* '--version' is a valid directory name. */ + procInfo.mArguments.push_back(strPath); /* The directory we want to create. */ + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; } - procInfo.mArguments.push_back("--"); /* '--version' is a valid directory name. */ - procInfo.mArguments.push_back(strPath); /* The directory we want to create. */ - } - catch (std::bad_alloc &) - { - vrc = VERR_NO_MEMORY; + if (RT_SUCCESS(vrc)) + vrc = GuestProcessToolbox::run(this, procInfo, pvrcGuest); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS } - - if (RT_SUCCESS(vrc)) - vrc = GuestProcessTool::run(this, procInfo, pvrcGuest); +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ LogFlowFuncLeaveRC(vrc); return vrc; @@ -1180,71 +1223,119 @@ int GuestSession::i_fsCreateTemp(const Utf8Str &strTemplate, const Utf8Str &strP LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool, fMode=%o, fSecure=%RTbool\n", strTemplate.c_str(), strPath.c_str(), fDirectory, fMode, fSecure)); - GuestProcessStartupInfo procInfo; - procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; - try + int vrc; + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + if (mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS) { - procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKTEMP); - procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ - procInfo.mArguments.push_back(Utf8Str("--machinereadable")); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + GuestWaitEvent *pEvent = NULL; + vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent); + if (RT_FAILURE(vrc)) + return vrc; + + uint32_t fFlags = GSTCTL_CREATETEMP_F_NONE; if (fDirectory) - procInfo.mArguments.push_back(Utf8Str("-d")); - if (strPath.length()) /* Otherwise use /tmp or equivalent. */ - { - procInfo.mArguments.push_back(Utf8Str("-t")); - procInfo.mArguments.push_back(strPath); - } - /* Note: Secure flag and mode cannot be specified at the same time. */ + fFlags |= GSTCTL_CREATETEMP_F_DIRECTORY; if (fSecure) + fFlags |= GSTCTL_CREATETEMP_F_SECURE; + Assert(!(fFlags & ~GSTCTL_CREATETEMP_F_VALID_MASK)); + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[8]; + int i = 0; + HGCMSvcSetU32(&paParms[i++], pEvent->ContextID()); + HGCMSvcSetStr(&paParms[i++], strTemplate.c_str()); + HGCMSvcSetStr(&paParms[i++], strPath.c_str()); + HGCMSvcSetU32(&paParms[i++], fFlags); + HGCMSvcSetU32(&paParms[i++], fMode); + + alock.release(); /* Drop lock before sending. */ + + vrc = i_sendMessage(HOST_MSG_FS_CREATE_TEMP, i, paParms); + if (RT_SUCCESS(vrc)) { - procInfo.mArguments.push_back(Utf8Str("--secure")); + vrc = pEvent->Wait(30 * 1000); + if (RT_SUCCESS(vrc)) + { + // TODO + } } - else + } + else + { +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + GuestProcessStartupInfo procInfo; + procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; + try { - procInfo.mArguments.push_back(Utf8Str("--mode")); + procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKTEMP); + procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ + procInfo.mArguments.push_back(Utf8Str("--machinereadable")); + if (fDirectory) + procInfo.mArguments.push_back(Utf8Str("-d")); + if (strPath.length()) /* Otherwise use /tmp or equivalent. */ + { + procInfo.mArguments.push_back(Utf8Str("-t")); + procInfo.mArguments.push_back(strPath); + } + /* Note: Secure flag and mode cannot be specified at the same time. */ + if (fSecure) + { + procInfo.mArguments.push_back(Utf8Str("--secure")); + } + else + { + procInfo.mArguments.push_back(Utf8Str("--mode")); - /* Note: Pass the mode unmodified down to the guest. See @ticketref{21394}. */ - char szMode[16]; - int vrc2 = RTStrPrintf2(szMode, sizeof(szMode), "%d", fMode); - AssertRCReturn(vrc2, vrc2); - procInfo.mArguments.push_back(szMode); + /* Note: Pass the mode unmodified down to the guest. See @ticketref{21394}. */ + char szMode[16]; + vrc = RTStrPrintf2(szMode, sizeof(szMode), "%d", fMode); + AssertRCReturn(vrc, vrc); + procInfo.mArguments.push_back(szMode); + } + procInfo.mArguments.push_back("--"); /* strTemplate could be '--help'. */ + procInfo.mArguments.push_back(strTemplate); + } + catch (std::bad_alloc &) + { + Log(("Out of memory!\n")); + return VERR_NO_MEMORY; } - procInfo.mArguments.push_back("--"); /* strTemplate could be '--help'. */ - procInfo.mArguments.push_back(strTemplate); - } - catch (std::bad_alloc &) - { - Log(("Out of memory!\n")); - return VERR_NO_MEMORY; - } - /** @todo Use an internal HGCM command for this operation, since - * we now can run in a user-dedicated session. */ - int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; - GuestCtrlStreamObjects stdOut; - int vrc = GuestProcessTool::runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &vrcGuest); - if (!GuestProcess::i_isGuestError(vrc)) - { - GuestFsObjData objData; - if (!stdOut.empty()) + GuestCtrlStreamObjects stdOut; + vrc = GuestProcessToolbox::runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &vrcGuest); + if (!GuestProcess::i_isGuestError(vrc)) { - vrc = objData.FromMkTemp(stdOut.at(0)); - if (RT_FAILURE(vrc)) + GuestFsObjData objData; + if (!stdOut.empty()) { - vrcGuest = vrc; - if (pvrcGuest) - *pvrcGuest = vrcGuest; - vrc = VERR_GSTCTL_GUEST_ERROR; + vrc = objData.FromToolboxMkTemp(stdOut.at(0)); + if (RT_FAILURE(vrc)) + { + vrcGuest = vrc; + if (pvrcGuest) + *pvrcGuest = vrcGuest; + vrc = VERR_GSTCTL_GUEST_ERROR; + } } - } - else - vrc = VERR_BROKEN_PIPE; + else + vrc = VERR_BROKEN_PIPE; - if (RT_SUCCESS(vrc)) - strName = objData.mName; + if (RT_SUCCESS(vrc)) + strName = objData.mName; + } + else if (pvrcGuest) + *pvrcGuest = vrcGuest; +#else + RT_NOREF(strName); + vrc = VERR_NOT_SUPPORTED; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS } - else if (pvrcGuest) - *pvrcGuest = vrcGuest; +#endif LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest)); return vrc; @@ -1284,7 +1375,10 @@ int GuestSession::i_directoryOpen(const GuestDirectoryOpenInfo &openInfo, ComObj return vrc; } - /* We need to release the write lock first before initializing the directory object below, + /** + * If VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT is enabled: + * + * We need to release the write lock first before opening the directory object below, * as we're starting a guest process as part of it. This in turn will try to acquire the session's * write lock. */ alock.release(); @@ -1293,6 +1387,9 @@ int GuestSession::i_directoryOpen(const GuestDirectoryOpenInfo &openInfo, ComObj AssertPtr(pConsole); vrc = pDirectory->init(pConsole, this /* Parent */, idObject, openInfo); + if (RT_SUCCESS(vrc)) + vrc = pDirectory->i_open(pvrcGuest); + if (RT_FAILURE(vrc)) { /* Make sure to acquire the write lock again before unregistering the object. */ @@ -1587,46 +1684,86 @@ int GuestSession::i_fileRemove(const Utf8Str &strPath, int *pvrcGuest) { LogFlowThisFunc(("strPath=%s\n", strPath.c_str())); - GuestProcessStartupInfo procInfo; - GuestProcessStream streamOut; + int vrc; + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + if (mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; - procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM); + GuestWaitEvent *pEvent = NULL; + vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent); + if (RT_FAILURE(vrc)) + return vrc; - try - { - procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ - procInfo.mArguments.push_back(Utf8Str("--machinereadable")); - procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */ - procInfo.mArguments.push_back(strPath); /* The file we want to remove. */ + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + HGCMSvcSetU32(&paParms[i++], pEvent->ContextID()); + HGCMSvcSetPv (&paParms[i++], (void*)strPath.c_str(), (ULONG)strPath.length() + 1); + + alock.release(); /* Drop lock before sending. */ + + vrc = i_sendMessage(HOST_MSG_FILE_REMOVE, i, paParms); + if (RT_SUCCESS(vrc)) + { + vrc = pEvent->Wait(30 * 1000); + if (RT_SUCCESS(vrc)) + { + // TODO + } + } } - catch (std::bad_alloc &) + else { - return VERR_NO_MEMORY; - } +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + GuestProcessStartupInfo procInfo; + GuestToolboxStream streamOut; - int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; - GuestCtrlStreamObjects stdOut; - int vrc = GuestProcessTool::runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &vrcGuest); - if (GuestProcess::i_isGuestError(vrc)) - { - if (!stdOut.empty()) + procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; + procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM); + + try { - GuestFsObjData objData; - vrc = objData.FromRm(stdOut.at(0)); - if (RT_FAILURE(vrc)) + procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ + procInfo.mArguments.push_back(Utf8Str("--machinereadable")); + procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */ + procInfo.mArguments.push_back(strPath); /* The file we want to remove. */ + } + catch (std::bad_alloc &) + { + return VERR_NO_MEMORY; + } + + GuestCtrlStreamObjects stdOut; + vrc = GuestProcessToolbox::runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &vrcGuest); + if (GuestProcess::i_isGuestError(vrc)) + { + if (!stdOut.empty()) { - vrcGuest = vrc; - if (pvrcGuest) - *pvrcGuest = vrcGuest; - vrc = VERR_GSTCTL_GUEST_ERROR; + GuestFsObjData objData; + vrc = objData.FromToolboxRm(stdOut.at(0)); + if (RT_FAILURE(vrc)) + { + vrcGuest = vrc; + if (pvrcGuest) + *pvrcGuest = vrcGuest; + vrc = VERR_GSTCTL_GUEST_ERROR; + } } + else + vrc = VERR_BROKEN_PIPE; } - else - vrc = VERR_BROKEN_PIPE; + else if (pvrcGuest) + *pvrcGuest = vrcGuest; +#else + RT_NOREF(pvrcGuest); + vrc = VERR_NOT_SUPPORTED; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS } - else if (pvrcGuest) - *pvrcGuest = vrcGuest; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest)); return vrc; @@ -1820,48 +1957,133 @@ int GuestSession::i_fsQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, Gu { LogFlowThisFunc(("strPath=%s\n", strPath.c_str())); - /** @todo Merge this with IGuestFile::queryInfo(). */ - GuestProcessStartupInfo procInfo; - procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; - try + int vrc; + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; + +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS + if (mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS) { - procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT); - procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ - procInfo.mArguments.push_back(Utf8Str("--machinereadable")); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + GuestWaitEvent *pEvent = NULL; + vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent); + if (RT_FAILURE(vrc)) + return vrc; + + uint32_t fFlags = GSTCTL_QUERYINFO_F_NONE; if (fFollowSymlinks) - procInfo.mArguments.push_back(Utf8Str("-L")); - procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */ - procInfo.mArguments.push_back(strPath); + fFlags |= GSTCTL_QUERYINFO_F_FOLLOW_LINK; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + HGCMSvcSetU32(&paParms[i++], pEvent->ContextID()); + HGCMSvcSetPv (&paParms[i++], (void*)strPath.c_str(), (ULONG)strPath.length() + 1); + HGCMSvcSetU32(&paParms[i++], GSTCTLFSOBJATTRADD_UNIX /* Implicit */); + HGCMSvcSetU32(&paParms[i++], fFlags); + + alock.release(); /* Drop lock before sending. */ + + vrc = i_sendMessage(HOST_MSG_FS_QUERY_INFO, i, paParms); + if (RT_SUCCESS(vrc)) + { + vrc = pEvent->Wait(30 * 1000); + if (RT_SUCCESS(vrc)) + { + // TODO + #if 0 + const ComPtr<IEvent> pThisEvent = pEvent->Event(); + if (pThisEvent.isNotNull()) /* Make sure that we actually have an event associated. */ + { + if (pType) + { + HRESULT hrc = pThisEvent->COMGETTER(Type)(pType); + if (FAILED(hrc)) + vrc = VERR_COM_UNEXPECTED; + } + if ( RT_SUCCESS(vrc) + && ppEvent) + pThisEvent.queryInterfaceTo(ppEvent); + + unconst(pThisEvent).setNull(); + } + + if (pEvent->Payload().Size() == sizeof(GSTCTLFSOBJINFO)) + { + HRESULT hrc1 = pFileEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data)); + ComAssertComRC(hrc1); + + objData.Init(strPath); + + PGSTCTLFSOBJINFO + + objData.FromGuestFsObjInfo((PGSTCTLFSOBJINFO)pEvent->Payload().Raw()); + } + else + vrc = VERR_INVALID_PARAMETER; + #endif + } + else + { + if (vrc == VERR_GSTCTL_GUEST_ERROR) + { + if (pvrcGuest) + *pvrcGuest = pEvent->GuestResult(); + } + } + } + unregisterWaitEvent(pEvent); } - catch (std::bad_alloc &) + else { - Log(("Out of memory!\n")); - return VERR_NO_MEMORY; - } +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT + /** @todo Merge this with IGuestFile::queryInfo(). */ + GuestProcessStartupInfo procInfo; + procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; + try + { + procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT); + procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */ + procInfo.mArguments.push_back(Utf8Str("--machinereadable")); + if (fFollowSymlinks) + procInfo.mArguments.push_back(Utf8Str("-L")); + procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */ + procInfo.mArguments.push_back(strPath); + } + catch (std::bad_alloc &) + { + Log(("Out of memory!\n")); + return VERR_NO_MEMORY; + } - int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; - GuestCtrlStreamObjects stdOut; - int vrc = GuestProcessTool::runEx(this, procInfo, - &stdOut, 1 /* cStrmOutObjects */, - &vrcGuest); - if (!GuestProcess::i_isGuestError(vrc)) - { - if (!stdOut.empty()) + GuestCtrlStreamObjects stdOut; + vrc = GuestProcessToolbox::runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &vrcGuest); + if (!GuestProcess::i_isGuestError(vrc)) { - vrc = objData.FromStat(stdOut.at(0)); - if (RT_FAILURE(vrc)) + if (!stdOut.empty()) { - vrcGuest = vrc; - if (pvrcGuest) - *pvrcGuest = vrcGuest; - vrc = VERR_GSTCTL_GUEST_ERROR; + vrc = objData.FromToolboxStat(stdOut.at(0)); + if (RT_FAILURE(vrc)) + { + vrcGuest = vrc; + if (pvrcGuest) + *pvrcGuest = vrcGuest; + vrc = VERR_GSTCTL_GUEST_ERROR; + } } + else + vrc = VERR_BROKEN_PIPE; } - else - vrc = VERR_BROKEN_PIPE; + else if (pvrcGuest) + *pvrcGuest = vrcGuest; +#else + RT_NOREF(fFollowSymlinks, objData, pvrcGuest); + vrc = VERR_NOT_SUPPORTED; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */ +#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS } - else if (pvrcGuest) - *pvrcGuest = vrcGuest; +#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */ LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest)); return vrc; @@ -3801,7 +4023,7 @@ HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULO { case VERR_GSTCTL_GUEST_ERROR: { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolMkTemp, vrcGuest, aPath.c_str()); + GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Temporary guest directory creation failed: %s"), GuestBase::getErrorAsString(ge).c_str()); break; @@ -3846,7 +4068,7 @@ HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSym break; default: { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, vrcGuest, aPath.c_str()); + GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying directory existence failed: %s"), GuestBase::getErrorAsString(ge).c_str()); break; @@ -4189,7 +4411,7 @@ HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks default: { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, vrcGuest, aPath.c_str()); + GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file existence failed: %s"), GuestBase::getErrorAsString(ge).c_str()); break; @@ -4342,7 +4564,7 @@ HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymli { if (GuestProcess::i_isGuestError(vrc)) { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, vrcGuest, aPath.c_str()); + GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file size failed: %s"), GuestBase::getErrorAsString(ge).c_str()); } @@ -4397,7 +4619,7 @@ HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlink hrc = S_OK; /* Ignore these vrc values. */ else { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, vrcGuest, aPath.c_str()); + GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file existence information failed: %s"), GuestBase::getErrorAsString(ge).c_str()); } @@ -4420,16 +4642,16 @@ HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSyml LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks))); - GuestFsObjData Info; + GuestFsObjData objData; int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; - int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &vrcGuest); + int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &vrcGuest); if (RT_SUCCESS(vrc)) { ComObjPtr<GuestFsObjInfo> ptrFsObjInfo; hrc = ptrFsObjInfo.createObject(); if (SUCCEEDED(hrc)) { - vrc = ptrFsObjInfo->init(Info); + vrc = ptrFsObjInfo->init(objData); if (RT_SUCCESS(vrc)) hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam()); else @@ -4440,7 +4662,7 @@ HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSyml { if (GuestProcess::i_isGuestError(vrc)) { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, vrcGuest, aPath.c_str()); + GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file information failed: %s"), GuestBase::getErrorAsString(ge).c_str()); } @@ -4468,7 +4690,7 @@ HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath) { if (GuestProcess::i_isGuestError(vrc)) { - GuestErrorInfo ge(GuestErrorInfo::Type_ToolRm, vrcGuest, aPath.c_str()); + GuestErrorInfo ge(GuestErrorInfo::Type_File, vrcGuest, aPath.c_str()); hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Removing guest file failed: %s"), GuestBase::getErrorAsString(ge).c_str()); } diff --git a/src/VBox/Main/src-client/GuestSessionImplTasks.cpp b/src/VBox/Main/src-client/GuestSessionImplTasks.cpp index 29b558aaff8..320052af32c 100644 --- a/src/VBox/Main/src-client/GuestSessionImplTasks.cpp +++ b/src/VBox/Main/src-client/GuestSessionImplTasks.cpp @@ -549,7 +549,7 @@ int GuestSessionTask::fileCopyFromGuest(const Utf8Str &strSrc, const Utf8Str &st { if (vrc == VERR_GSTCTL_GUEST_ERROR) setProgressErrorMsg(VBOX_E_IPRT_ERROR, tr("Guest file lookup failed"), - GuestErrorInfo(GuestErrorInfo::Type_ToolStat, vrcGuest, strSrc.c_str())); + GuestErrorInfo(GuestErrorInfo::Type_Fs, vrcGuest, strSrc.c_str())); else setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(tr("Guest file lookup for \"%s\" failed: %Rrc"), strSrc.c_str(), vrc)); @@ -1246,7 +1246,7 @@ int FsList::AddDirFromGuest(const Utf8Str &strPath, const Utf8Str &strSubDir /* vrc = VINF_SUCCESS; } - int vrc2 = pDir->i_closeInternal(&vrcGuest); + int vrc2 = pDir->i_close(&vrcGuest); if (RT_SUCCESS(vrc)) vrc = vrc2; @@ -1543,7 +1543,7 @@ HRESULT GuestSessionTaskCopyFrom::Init(const Utf8Str &strTaskDesc) { if (vrc == VERR_GSTCTL_GUEST_ERROR) strErrorInfo = GuestBase::getErrorAsString(tr("Guest source lookup failed"), - GuestErrorInfo(GuestErrorInfo::Type_ToolStat, vrcGuest, strSrc.c_str())); + GuestErrorInfo(GuestErrorInfo::Type_Fs, vrcGuest, strSrc.c_str())); else strErrorInfo.printf(tr("Guest source lookup for \"%s\" failed: %Rrc"), strSrc.c_str(), vrc); @@ -2629,15 +2629,15 @@ int GuestSessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, Gues LogRel(("Running %s ...\n", procInfo.mName.c_str())); - GuestProcessTool procTool; + GuestProcessToolbox procToRun; int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; - int vrc = procTool.init(pSession, procInfo, false /* Async */, &vrcGuest); + int vrc = procToRun.init(pSession, procInfo, false /* Async */, &vrcGuest); if (RT_SUCCESS(vrc)) { if (RT_SUCCESS(vrcGuest)) - vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &vrcGuest); + vrc = procToRun.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &vrcGuest); if (RT_SUCCESS(vrc)) - vrc = procTool.getTerminationStatus(); + vrc = procToRun.getTerminationStatus(); } if (RT_FAILURE(vrc)) @@ -2647,7 +2647,7 @@ int GuestSessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, Gues case VERR_GSTCTL_PROCESS_EXIT_CODE: setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(tr("Running update file \"%s\" on guest failed: %Rrc"), - procInfo.mExecutable.c_str(), procTool.getRc())); + procInfo.mExecutable.c_str(), procToRun.getRc())); break; case VERR_GSTCTL_GUEST_ERROR: diff --git a/src/VBox/Main/testcase/Makefile.kmk b/src/VBox/Main/testcase/Makefile.kmk index 30cd1dfea6a..51f082c0aa5 100644 --- a/src/VBox/Main/testcase/Makefile.kmk +++ b/src/VBox/Main/testcase/Makefile.kmk @@ -224,7 +224,7 @@ tstGuestCtrlContextID_INCS = ../include \ # tstGuestCtrlParseBuffer_TEMPLATE = VBoxMainClientTstExe tstGuestCtrlParseBuffer_INTERMEDIATES = $(VBOX_MAIN_APIWRAPPER_GEN_HDRS) -tstGuestCtrlParseBuffer_DEFS += VBOX_WITH_HGCM VBOX_WITH_GUEST_CONTROL VBOX_GUESTCTRL_TEST_CASE +tstGuestCtrlParseBuffer_DEFS += VBOX_WITH_HGCM VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT VBOX_WITH_GUEST_CONTROL VBOX_GUESTCTRL_TEST_CASE tstGuestCtrlParseBuffer_SOURCES = \ tstGuestCtrlParseBuffer.cpp \ ../src-client/GuestCtrlPrivate.cpp diff --git a/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp b/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp index cc422ff4fe4..ec80dbccff8 100644 --- a/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp +++ b/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp @@ -163,12 +163,12 @@ int manualTest(void) { RTTestIPrintf(RTTESTLVL_DEBUG, "Manual test #%d\n", iTest); - GuestProcessStream stream; + GuestToolboxStream stream; rc = stream.AddData((BYTE *)s_aTest[iTest].pbData, s_aTest[iTest].cbData); for (;;) { - GuestProcessStreamBlock block; + GuestToolboxStreamBlock block; rc = stream.ParseBlock(block); RTTestIPrintf(RTTESTLVL_DEBUG, "\tReturned with rc=%Rrc, numItems=%ld\n", rc, block.GetCount()); @@ -212,7 +212,7 @@ int main() { RTTestIPrintf(RTTESTLVL_DEBUG, "=> Test #%u\n", iTest); - GuestProcessStream stream; + GuestToolboxStream stream; if (RT_FAILURE(g_aTestBlocks[iTest].iResult)) RTTestDisableAssertions(hTest); int iResult = stream.AddData((BYTE *)g_aTestBlocks[iTest].pbData, g_aTestBlocks[iTest].cbData); @@ -220,7 +220,7 @@ int main() RTTestRestoreAssertions(hTest); if (RT_SUCCESS(iResult)) { - GuestProcessStreamBlock curBlock; + GuestToolboxStreamBlock curBlock; iResult = stream.ParseBlock(curBlock); if (iResult != g_aTestBlocks[iTest].iResult) RTTestFailed(hTest, "Block #%u: Returned %Rrc, expected %Rrc", iTest, iResult, g_aTestBlocks[iTest].iResult); @@ -256,7 +256,7 @@ int main() { RTTestIPrintf(RTTESTLVL_DEBUG, "=> Block test #%u\n", iTest); - GuestProcessStream stream; + GuestToolboxStream stream; int iResult = stream.AddData((BYTE*)g_aTestStream[iTest].pbData, g_aTestStream[iTest].cbData); if (RT_SUCCESS(iResult)) { @@ -264,7 +264,7 @@ int main() uint8_t uSafeCouunter = 0; do { - GuestProcessStreamBlock curBlock; + GuestToolboxStreamBlock curBlock; iResult = stream.ParseBlock(curBlock); RTTestIPrintf(RTTESTLVL_DEBUG, "Block #%u: Returned with %Rrc", iTest, iResult); if (RT_SUCCESS(iResult)) |