From: VMware, Inc <> Date: Thu, 17 Jun 2010 22:03:49 +0000 (-0700) Subject: Adding create/destroy session support. X-Git-Tag: 2010.06.16-268169~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e6111dccc2e600820568ce9abd1cffb3d99379e1;p=thirdparty%2Fopen-vm-tools.git Adding create/destroy session support. 1. Implementing handlers for creating and destroying sessions in server. 2. Implementing sending create/destroy session requests in the Windows client. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/lib/hgfs/hgfsUtil.c b/open-vm-tools/lib/hgfs/hgfsUtil.c index 4ad5815e9..c80850992 100644 --- a/open-vm-tools/lib/hgfs/hgfsUtil.c +++ b/open-vm-tools/lib/hgfs/hgfsUtil.c @@ -233,6 +233,7 @@ HgfsConvertFromInternalStatus(HgfsInternalStatus status) // IN return HGFS_STATUS_INVALID_PARAMETER; case ERROR_NOT_SAME_DEVICE: return HGFS_STATUS_NOT_SAME_DEVICE; + case ERROR_INTERNAL_ERROR: case HGFS_INTERNAL_STATUS_ERROR: default: return HGFS_STATUS_GENERIC_ERROR; @@ -276,6 +277,7 @@ HgfsConvertFromInternalStatus(HgfsInternalStatus status) // IN return HGFS_STATUS_INVALID_PARAMETER; case EXDEV: return HGFS_STATUS_NOT_SAME_DEVICE; + case EINTERNAL: case HGFS_INTERNAL_STATUS_ERROR: default: return HGFS_STATUS_GENERIC_ERROR; diff --git a/open-vm-tools/lib/hgfsServer/Makefile.am b/open-vm-tools/lib/hgfsServer/Makefile.am index d39942f0e..4455e5b23 100644 --- a/open-vm-tools/lib/hgfsServer/Makefile.am +++ b/open-vm-tools/lib/hgfsServer/Makefile.am @@ -22,6 +22,7 @@ libHgfsServer_la_SOURCES += hgfsServer.c libHgfsServer_la_SOURCES += hgfsServerLinux.c libHgfsServer_la_SOURCES += hgfsServerPacketUtil.c libHgfsServer_la_SOURCES += hgfsDirNotifyStub.c +libHgfsServer_la_SOURCES += hgfsServerParameters.c AM_CFLAGS = AM_CFLAGS += -DVMTOOLS_USE_GLIB diff --git a/open-vm-tools/lib/hgfsServer/hgfsServer.c b/open-vm-tools/lib/hgfsServer/hgfsServer.c index 0e9e67166..c7029e980 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServer.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServer.c @@ -25,6 +25,9 @@ #include "cpNameLite.h" #include "hgfsServerInt.h" #include "hgfsServerPolicy.h" +#include "hgfsUtil.h" +#include "hgfsVirtualDir.h" +#include "hgfsEscape.h" #include "codeset.h" #include "config.h" #include "file.h" @@ -35,6 +38,7 @@ #include "userlock.h" #include "poll.h" #include "libMutexRank.h" +#include "vm_basic_asm.h" #if defined(_WIN32) #include @@ -69,6 +73,9 @@ #define HGFS_ASSERT_CLIENT(op) #endif +#define HGFS_ASSERT_INPUT(input) ASSERT(input && input->packet && input->metaPacket && \ + input->session && \ + (!input->payloadSize || input->payload)) /* * Define this to enable an ASSERT if server gets an op lower than @@ -182,6 +189,12 @@ HgfsServerSessionCallbacks hgfsServerSessionCBTable = { static Bool hgfsChangeNotificationSupported = FALSE; +/* + * Limit payload to 16M + header. + * This limit ensures that list of shared pages fits into VMCI datagram. + * Client may impose a lower limit in create session request. + */ +#define MAX_SERVER_PACKET_SIZE_V4 (0x1000000 + sizeof(HgfsHeader)) /* Local functions. */ @@ -225,17 +238,34 @@ static HgfsHandle HgfsFileNode2Handle(HgfsFileNode const *fileNode); static HgfsFileNode *HgfsHandle2FileNode(HgfsHandle handle, HgfsSessionInfo *session); static void HgfsServerExitSessionInternal(HgfsSessionInfo *session); -static Bool HgfsValidatePacket(char const *packetIn, size_t packetSize, Bool v4header); -static void HgfsPackReplyHeaderV4(HgfsInternalStatus status, - uint32 payloadSize, - HgfsHeader const *packetIn, - HgfsHeader *header); -static void HgfsServer_ProcessRequest(void *data); -void HgfsServer_ReplyWithError(HgfsPacket *packet, - const char *metaPacket, - HgfsStatus status, - Bool v4header, - HgfsSessionInfo *session); +static Bool HgfsIsShareRoot(char const *cpName, size_t cpNameSize); +static void HgfsServerCompleteRequest(HgfsInternalStatus status, + size_t replyPayloadSize, + HgfsInputParam *input); + +/* + * Opcode handlers + */ + +static void HgfsServerOpen(HgfsInputParam *input); +static void HgfsServerRead(HgfsInputParam *input); +static void HgfsServerWrite(HgfsInputParam *input); +static void HgfsServerSearchOpen(HgfsInputParam *input); +static void HgfsServerSearchRead(HgfsInputParam *input); +static void HgfsServerGetattr(HgfsInputParam *input); +static void HgfsServerSetattr(HgfsInputParam *input); +static void HgfsServerCreateDir(HgfsInputParam *input); +static void HgfsServerDeleteFile(HgfsInputParam *input); +static void HgfsServerDeleteDir(HgfsInputParam *input); +static void HgfsServerRename(HgfsInputParam *input); +static void HgfsServerQueryVolume(HgfsInputParam *input); +static void HgfsServerSymlinkCreate(HgfsInputParam *input); +static void HgfsServerServerLockChange(HgfsInputParam *input); +static void HgfsServerWriteWin32Stream(HgfsInputParam *input); +static void HgfsServerCreateSession(HgfsInputParam *input); +static void HgfsServerDestroySession(HgfsInputParam *input); +static void HgfsServerClose(HgfsInputParam *input); +static void HgfsServerSearchClose(HgfsInputParam *input); /* @@ -254,7 +284,7 @@ void HgfsServer_ReplyWithError(HgfsPacket *packet, *---------------------------------------------------------------------------- */ -static void +void HgfsServerSessionGet(HgfsSessionInfo *session) // IN: session context { ASSERT(session); @@ -2500,47 +2530,34 @@ HgfsUpdateNodeNames(const char *oldLocalName, // IN: Name of file to look for *----------------------------------------------------------------------------- */ -static HgfsInternalStatus +static void HgfsServerClose(HgfsInputParam *input) // IN: Input params { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; HgfsHandle file; - char *packetOut = NULL; - size_t replySize; - HgfsInternalStatus status = 0; - HgfsOp op; + HgfsInternalStatus status = HGFS_ERROR_SUCCESS; + size_t replyPayloadSize = 0; - ASSERT(packetIn); - ASSERT(session); + HGFS_ASSERT_INPUT(input); - if (!HgfsUnpackCloseRequest(packetIn, packetSize, &op, &file)) { - status = HGFS_INTERNAL_STATUS_ERROR; - goto error; - } + if (HgfsUnpackCloseRequest(input->payload, input->payloadSize, input->op, &file)) { - LOG(4, ("%s: close fh %u\n", __FUNCTION__, file)); + LOG(4, ("%s: close fh %u\n", __FUNCTION__, file)); - if (!HgfsRemoveFromCache(file, session)) { - LOG(4, ("%s: Could not remove the node from cache.\n", __FUNCTION__)); - status = HGFS_INTERNAL_STATUS_ERROR; + if (!HgfsRemoveFromCache(file, input->session)) { + LOG(4, ("%s: Could not remove the node from cache.\n", __FUNCTION__)); + status = HGFS_ERROR_INTERNAL; + } else { + HgfsFreeFileNode(file, input->session); + if (!HgfsPackCloseReply(input->packet, input->metaPacket, input->op, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } } else { - HgfsFreeFileNode(file, session); - } - - if (!HgfsPackCloseReply(input->packet, packetIn, status, op, - &packetOut, &replySize, session) || - !HgfsPacketSend(input->packet, packetOut, replySize, session, 0)) { - status = HGFS_INTERNAL_STATUS_ERROR; - goto error; + status = HGFS_ERROR_INTERNAL; } - return 0; - -error: - HSPU_PutReplyPacket(input->packet, session); - return status; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } @@ -2561,47 +2578,37 @@ error: *----------------------------------------------------------------------------- */ -static HgfsInternalStatus +static void HgfsServerSearchClose(HgfsInputParam *input) // IN: Input params { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; HgfsHandle search; - char *packetOut = NULL; - size_t replySize; - HgfsInternalStatus status = 0; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(session); + HgfsInternalStatus status; + size_t replyPayloadSize = 0; - if (!HgfsUnpackSearchCloseRequest(packetIn, packetSize, &op, &search)) { - status = HGFS_INTERNAL_STATUS_ERROR; - goto error; - } + HGFS_ASSERT_INPUT(input); - LOG(4, ("%s: close search #%u\n", __FUNCTION__, search)); + if (HgfsUnpackSearchCloseRequest(input->payload, input->payloadSize, input->op, + &search)) { - if (!HgfsRemoveSearch(search, session)) { - /* Invalid handle */ - LOG(4, ("%s: invalid handle %u\n", __FUNCTION__, search)); - status = HGFS_INTERNAL_STATUS_ERROR; - goto error; - } + LOG(4, ("%s: close search #%u\n", __FUNCTION__, search)); - if (!HgfsPackSearchCloseReply(input->packet, packetIn, status, op, - &packetOut, &replySize, session) || - !HgfsPacketSend(input->packet, packetOut, replySize, session, 0)) { - status = HGFS_INTERNAL_STATUS_ERROR; - goto error; + if (HgfsRemoveSearch(search, input->session)) { + if (HgfsPackSearchCloseReply(input->packet, input->metaPacket, input->op, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_SUCCESS; + } else { + status = HGFS_ERROR_INTERNAL; + } + } else { + /* Invalid handle */ + LOG(4, ("%s: invalid handle %u\n", __FUNCTION__, search)); + status = HGFS_ERROR_INTERNAL; + } + } else { + status = HGFS_ERROR_INTERNAL; } - return 0; - -error: - HSPU_PutReplyPacket(input->packet, session); - return status; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } @@ -2609,9 +2616,7 @@ error: /* Opcode handlers, indexed by opcode */ static struct { - HgfsInternalStatus - (*handler)(HgfsInputParam *input); - + void (*handler)(HgfsInputParam *input); /* Minimal size of the request packet */ unsigned int minReqSize; @@ -2663,19 +2668,112 @@ static struct { { HgfsServerServerLockChange, sizeof (HgfsRequestServerLockChange), REQ_SYNC }, { HgfsServerWriteWin32Stream, HGFS_SIZEOF_OP(HgfsRequestWriteWin32StreamV3), REQ_SYNC }, /* - * XXX - * Will be replaced with the real thing when during merge with another outstanding - * change. - * For now just set min size big enough so request gets rejected when - * such request comes from the client. + * Starting from HGFS_OP_CREATE_SESSION_V4 (all V4 commands and above) the + * second field is the minimum size for actual HGFS operational request + * and not the minimum size of operational request with a header. */ - { NULL, 0xffffff, REQ_ASYNC }, // Implemented in another change - { NULL, 0xffffff, REQ_ASYNC }, // Implemented in another change - { HgfsServerRead, HGFS_SIZEOF_OP(HgfsRequestReadV3), REQ_SYNC }, - { HgfsServerWrite, HGFS_SIZEOF_OP(HgfsRequestWriteV3), REQ_SYNC }, + { HgfsServerCreateSession, offsetof(HgfsRequestCreateSessionV4, reserved), REQ_SYNC}, + { HgfsServerDestroySession, offsetof(HgfsRequestDestroySessionV4, reserved), REQ_SYNC}, + { HgfsServerRead, sizeof (HgfsRequestReadV3), REQ_SYNC}, + { HgfsServerWrite, sizeof (HgfsRequestWriteV3), REQ_SYNC}, + }; +/* + *----------------------------------------------------------------------------- + * + * HgfsServerCompleteRequest -- + * + * Performs all necessary action which needed for completing HGFS request: + * 1. Sends reply to the guest. + * 2. Release allocated objects, mapped guest memory. + * 3. Dereference objects that were referenced. + * + * Results: + * None. + * + * Side effects: + * Reference to Session is dropped. + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsServerCompleteRequest(HgfsInternalStatus status, // IN: Status of the request + size_t replyPayloadSize, // IN: sizeof the reply payload + HgfsInputParam *input) // INOUT: request context +{ + char *packetOut; + size_t replyPacketSize; + size_t replySize; + + HGFS_ASSERT_INPUT(input); + + if (input->v4header) { + HgfsHeader *header; + replySize = sizeof *header + replyPayloadSize; + replyPacketSize = replySize; + header = HSPU_GetReplyPacket(input->packet, &replyPacketSize, input->session); + packetOut = (char *)header; + + ASSERT_DEVEL(header && (replySize <= replyPacketSize)); + if (header && (sizeof *header <= replyPacketSize)) { + HgfsPackReplyHeaderV4(status, replyPayloadSize, input->op, + input->session->sessionId, input->id, header); + } + } else { + HgfsReply *reply; + replySize = sizeof *reply + replyPayloadSize; + replyPacketSize = replySize; + reply = HSPU_GetReplyPacket(input->packet, &replyPacketSize, input->session); + packetOut = (char *)reply; + + ASSERT_DEVEL(reply && (replySize <= replyPacketSize)); + if (reply && (sizeof *reply <= replyPacketSize)) { + reply->id = input->id; + reply->status = HgfsConvertFromInternalStatus(status); + } + } + if (!HgfsPacketSend(input->packet, packetOut, replySize, input->session, 0)) { + /* Send failed. Drop the reply. */ + LOG(4, ("Error sending reply\n")); + } + + HgfsServerSessionPut(input->session); + free(input); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsServerProcessRequest -- + * + * Dispatch an incoming packet (in packetIn) to a handler function. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsServerProcessRequest(void *context) +{ + HgfsInputParam *input = (HgfsInputParam *)context; + if (!input->metaPacket) { + input->metaPacket = HSPU_GetMetaPacket(input->packet, + &input->metaPacketSize, + input->session); + } + (*handlers[input->op].handler)(input); +} + + /* *----------------------------------------------------------------------------- * @@ -2687,8 +2785,8 @@ static struct { * a packet containing only a reply header with error code. * * The handler function can send the reply packet either using HgfsPacketSend - * or HgfsPackAndSendPacket helper functions. This function would return error - * as a reply if the op handler do not return HGFS_STATUS_SUCCESS. + * helper functions. This function would return error + * as a reply if the op handler do not return HGFS_ERROR_SUCCESS. * * NOTE: If any op handler needs to keep packetIn around for sending replies * at a later point (possibly in a different thread context), it should @@ -2711,14 +2809,8 @@ HgfsServerSessionReceive(HgfsPacket *packet, // IN: Hgfs Packet void *clientData) // IN: session info { HgfsSessionInfo *session = (HgfsSessionInfo *)clientData; - HgfsRequest *request; - HgfsHandle id; - HgfsOp op; - HgfsStatus status; - Bool v4header = FALSE; - HgfsInputParam *input; - size_t metaPacketSize; - char *metaPacket; + HgfsInternalStatus status; + HgfsInputParam *input = NULL; ASSERT(session); @@ -2728,235 +2820,65 @@ HgfsServerSessionReceive(HgfsPacket *packet, // IN: Hgfs Packet return; } - metaPacket = HSPU_GetMetaPacket(packet, &metaPacketSize, session); - request = (HgfsRequest *)metaPacket; - ASSERT_DEVEL(request); - if (!request) { - /* - * How can I return error back to the client, clearly the client is either broken or - * malicious ? We cannot continue from here. - */ - return; - } - - /* - * Error out if less than HgfsRequest size. - */ - if (metaPacketSize < sizeof *request) { - if (metaPacketSize >= sizeof id) { - id = request->id; - } else { - id = 0; - } - status = HGFS_STATUS_PROTOCOL_ERROR; - ASSERT_DEVEL(0); - goto err; - } - - /* Increment the session's reference count until we send the reply. */ HgfsServerSessionGet(session); - packet->id = id = request->id; - op = request->op; - - /* If it is a V4 packet then handle it appropriately. */ - if (HGFS_V4_LEGACY_OPCODE == op) { - HgfsHeader *header = (HgfsHeader *)metaPacket; - if (metaPacketSize < sizeof *header) { - status = HGFS_STATUS_PROTOCOL_ERROR; - ASSERT_DEVEL(0); - goto err; - } - op = header->op; - v4header = TRUE; - } - - if (!HgfsValidatePacket(metaPacket, metaPacketSize, v4header)) { - status = HGFS_STATUS_PROTOCOL_ERROR; - LOG(4, ("%s: %d: Possible BUG! malformed packet.\n", __FUNCTION__, - __LINE__)); - goto err; + if (!HgfsParseRequest(packet, session, &input, &status)) { + LOG(4, ("%s: %d: Can't generate any response for the guest, just exit.\n ", + __FUNCTION__, __LINE__)); + HgfsServerSessionPut(session); + return; } - HGFS_ASSERT_MINIMUM_OP(op); - if (op < sizeof handlers / sizeof handlers[0]) { - if (metaPacketSize >= handlers[op].minReqSize) { - input = Util_SafeMalloc(sizeof *input); - input->metaPacket = NULL; - input->metaPacketSize = 0; - input->session = session; - input->packet = packet; - input->v4header = v4header; - input->op = op; - - /* - * Do the decision making here, whether we want to process request - * synchronously or asynchronously. Various factors to consider: - * - * - Use hints from the client, for instance, windows OS explicitly - * tells the file system whether request is async or not. - * - Determine statically - Simple to reason out, Simple to code - */ - if (packet->supportsAsync && - ((handlers[op].reqType == REQ_ASYNC) || HGFS_DEBUG_ASYNC)) { + HGFS_ASSERT_MINIMUM_OP(input->op); + if (HGFS_ERROR_SUCCESS == status) { + HGFS_ASSERT_INPUT(input); + if (HgfsValidatePacket(input->metaPacket, input->metaPacketSize, input->v4header) && + (input->op < ARRAYSIZE(handlers)) && + (input->metaPacketSize >= handlers[input->op].minReqSize)) { + /* Initial validation passed, process the client request now. */ + packet->processedAsync = packet->supportsAsync && + (handlers[input->op].reqType == REQ_ASYNC); + if (packet->processedAsync) { + LOG(4, ("%s: %d: @@Async\n", __FUNCTION__, __LINE__)); +#ifndef VMX86_TOOLS /* * Asynchronous processing is supported by the transport. * We can release mappings here and reacquire when needed. */ HSPU_PutMetaPacket(packet, session); - packet->processedAsync = TRUE; - LOG(4, ("%s: %d: @@Async\n", __FUNCTION__, __LINE__)); -#ifndef VMX86_TOOLS - /* Remove pending requests during poweroff */ + input->metaPacket = NULL; + + /* Remove pending requests during poweroff. */ Poll_Callback(POLL_CS_MAIN, POLL_FLAG_REMOVE_AT_POWEROFF, - HgfsServer_ProcessRequest, + HgfsServerProcessRequest, input, POLL_REALTIME, 1000, NULL); #else - /* Tools code should never process request async */ + /* Tools code should never process request async. */ ASSERT(0); #endif - /* free(input) in HgfsServer_ProcessRequest */ } else { LOG(4, ("%s: %d: ##Sync\n", __FUNCTION__, __LINE__)); - packet->processedAsync = FALSE; - input->metaPacket = metaPacket; - input->metaPacketSize = metaPacketSize; - - HgfsServer_ProcessRequest(input); - /* free(input) in HgfsServer_ProcessRequest */ + HgfsServerProcessRequest(input); } - return; } else { /* * The input packet is smaller than the minimal size needed for the * operation. */ - - status = HGFS_STATUS_PROTOCOL_ERROR; - LOG(4, ("%s: %d: Possible BUG! Smaller packet.\n", __FUNCTION__, - __LINE__)); + status = HGFS_ERROR_PROTOCOL; + LOG(4, ("%s: %d: Possible BUG! Malformed packet.\n", __FUNCTION__, __LINE__)); } - } else { - /* Unknown opcode */ - status = HGFS_STATUS_PROTOCOL_ERROR; - LOG(4, ("%s: %d: Possible BUG! Invalid opcode.\n", __FUNCTION__, - __LINE__)); } - HGFS_ASSERT_CLIENT(op); + HGFS_ASSERT_CLIENT(input->op); -err: /* Send error if we fail to process the op. */ - if (status != HGFS_STATUS_SUCCESS) { - HgfsServer_ReplyWithError(packet, metaPacket, status, v4header, session); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsServer_ProcessRequest -- - * - * Reply with an error packet - * - * Results: - * None. - * - * Side effects: - * Guest memory mappings may be established. - * - *----------------------------------------------------------------------------- - */ - -static void -HgfsServer_ProcessRequest(void *data) -{ - HgfsStatus status; - HgfsInternalStatus internalStatus; - HgfsInputParam *input = (HgfsInputParam *)data; - - if (!input->metaPacket) { - input->metaPacket = HSPU_GetMetaPacket(input->packet, - &input->metaPacketSize, - input->session); - } - ASSERT(input->metaPacket); - - internalStatus = (*handlers[input->op].handler)(input); - status = HgfsConvertFromInternalStatus(internalStatus); - if (status != HGFS_STATUS_SUCCESS) { - HgfsServer_ReplyWithError(input->packet, input->metaPacket, - status, input->v4header, input->session); - } - free(input); -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsServer_ReplyWithError -- - * - * Reply with an error packet - * - * Results: - * TRUE if succeeded, FALSE if failed. - * - * Side effects: - * - *----------------------------------------------------------------------------- - */ - -void -HgfsServer_ReplyWithError(HgfsPacket *packet, - const char *metaPacket, - HgfsStatus status, - Bool v4header, - HgfsSessionInfo *session) -{ - char *packetOut; - uint32 replySize; - size_t replyPacketSize; - - if (v4header) { - HgfsHeader *header; - replyPacketSize = sizeof *header; - header = HSPU_GetReplyPacket(packet, &replyPacketSize, session); - if (!header || sizeof *header > replyPacketSize) { - /* - * Transport should probably check for minimum hgfs packet size. - * How should we send an error back if there is no meta packet ? - */ - return; - } - - HgfsPackReplyHeaderV4(status, 0, (HgfsHeader *)metaPacket, header); - packetOut = (char *)header; - replySize = sizeof *header; - } else { - HgfsReply *reply; - replyPacketSize = sizeof *reply; - reply = HSPU_GetReplyPacket(packet, &replyPacketSize, session); - if (!reply || sizeof *reply > replyPacketSize) { - /* - * Transport should probably check for minimum hgfs packet size. - * How should we send an error back if there is no meta packet ? - */ - return; - } - reply->id = packet->id; - reply->status = status; - packetOut = (char *)reply; - replySize = sizeof *reply; - } - LOG(0, ("Error occured for id = %u %d status\n", (uint32)packet->id, status)); - if (!HgfsPacketSend(packet, packetOut, replySize, session, 0)) { - /* Send failed. Drop the reply. */ - HSPU_PutReplyPacket(packet, session); + if (HGFS_ERROR_SUCCESS != status) { + LOG(4, ("Error %d occured parsing the packet\n", (uint32)status)); + HgfsServerCompleteRequest(status, 0, input); } } @@ -3056,6 +2978,29 @@ HgfsServer_ExitState(void) } +/* + *----------------------------------------------------------------------------- + * + * HgfsGenerateSessionId -- + * + * Generates unique session id. + * + * Results: + * Unique 64-bit value. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static uint64 +HgfsGenerateSessionId(void) +{ + return RDTSC(); +} + + /* *----------------------------------------------------------------------------- * @@ -3121,6 +3066,8 @@ HgfsServerSessionConnect(void *transportData, // IN: tra return FALSE; } + session->sessionId = HgfsGenerateSessionId(); + session->maxPacketSize = MAX_SERVER_PACKET_SIZE_V4; /* * Initialize the node handling components. */ @@ -3474,6 +3421,7 @@ HgfsServerSessionSendComplete(HgfsPacket *packet, // IN/OUT: Hgfs packet HgfsSessionInfo *session = (HgfsSessionInfo *)clientData; HSPU_PutMetaPacket(packet, session); HSPU_PutReplyPacket(packet, session); + HSPU_PutDataPacketBuf(packet, session); } @@ -3520,51 +3468,10 @@ HgfsPacketSend(HgfsPacket *packet, // IN/OUT: Hgfs Packet #endif } - HgfsServerSessionPut(session); - return result; } -/* - *----------------------------------------------------------------------------- - * - * HgfsPackAndSendPacket -- - * - * Packs up the reply with id and status and sends the packet. - * - * Results: - * TRUE on success, FALSE otherwise. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackAndSendPacket(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char *packetOut, // IN: Output packet to send - size_t packetOutLen, // IN: Output packet size - HgfsInternalStatus status, // IN: status - HgfsHandle id, // IN: id of the request packet - HgfsSessionInfo *session, // IN: session info - HgfsSendFlags flags) // IN: flags how to send -{ - HgfsReply *reply = (HgfsReply *)packetOut; - - ASSERT(packet); - ASSERT(packetOut); - ASSERT(session); - ASSERT(packetOutLen <= HGFS_LARGE_PACKET_MAX); - - reply->id = id; - reply->status = HgfsConvertFromInternalStatus(status); - - return HgfsPacketSend(packet, packetOut, packetOutLen, session, flags); -} - - /* *----------------------------------------------------------------------------- * @@ -4363,7 +4270,7 @@ HgfsServerSearchRealDir(char const *baseDir, // IN: Directory to search rootDir, session); if (!search) { LOG(4, ("%s: failed to get new search\n", __FUNCTION__)); - status = HGFS_INTERNAL_STATUS_ERROR; + status = HGFS_ERROR_INTERNAL; goto out; } @@ -4372,7 +4279,7 @@ HgfsServerSearchRealDir(char const *baseDir, // IN: Directory to search &configOptions); if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, shareName)); - status = HGFS_INTERNAL_STATUS_ERROR; + status = HGFS_ERROR_INTERNAL; HgfsRemoveSearchInternal(search, session); goto out; } @@ -4440,7 +4347,7 @@ HgfsServerSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator search = HgfsAddNewSearch("", type, "", "", session); if (!search) { LOG(4, ("%s: failed to get new search\n", __FUNCTION__)); - status = HGFS_INTERNAL_STATUS_ERROR; + status = HGFS_ERROR_INTERNAL; goto out; } @@ -4448,7 +4355,7 @@ HgfsServerSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator if (result < 0) { LOG(4, ("%s: couldn't get dents\n", __FUNCTION__)); HgfsRemoveSearchInternal(search, session); - status = HGFS_INTERNAL_STATUS_ERROR; + status = HGFS_ERROR_INTERNAL; goto out; } @@ -4731,14 +4638,12 @@ HgfsCreateAndCacheFileNode(HgfsFileOpenInfo *openInfo, // IN: Open info struct /* *----------------------------------------------------------------------------- * - * HgfsValidatePacket -- + * HgfsAllocInitReply -- * - * Validates that packet is not malformed. Checks consistency of various - * fields and sizes. + * Allocates hgfs reply packet and calculates pointer to HGFS payload. * * Results: - * TRUE if the packet is correct. - * FALSE if the packet is malformed. + * TRUE on success, FALSE on failure. * * Side effects: * None @@ -4746,28 +4651,37 @@ HgfsCreateAndCacheFileNode(HgfsFileOpenInfo *openInfo, // IN: Open info struct *----------------------------------------------------------------------------- */ -static Bool -HgfsValidatePacket(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - Bool v4header) // IN: header version -{ - HgfsRequest *request = (HgfsRequest *)packetIn; - Bool result = TRUE; - - if (packetSize < sizeof *request) { - return FALSE; +Bool +HgfsAllocInitReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + size_t payloadSize, // IN: payload size + void **payload, // OUT: pointer to the reply payload + HgfsSessionInfo *session) // IN: Session Info +{ + HgfsRequest *request = (HgfsRequest *)packetHeader; + size_t replyPacketSize; + size_t headerSize = 0; /* Replies prior to V3 do not have a header. */ + Bool result = FALSE; + char *reply; + + if (HGFS_V4_LEGACY_OPCODE == request->op) { + headerSize = sizeof(HgfsHeader); + } else if (request->op < HGFS_OP_CREATE_SESSION_V4 && + request->op > HGFS_OP_RENAME_V2) { + headerSize = sizeof(HgfsReply); } - if (v4header) { - HgfsHeader *header = (HgfsHeader *)packetIn; - ASSERT(packetSize >= header->packetSize); - ASSERT(header->packetSize >= header->headerSize); - result = packetSize >= offsetof(HgfsHeader, requestId) && - header->headerSize >= offsetof(HgfsHeader, reserved) && - header->packetSize >= header->headerSize && - packetSize >= header->packetSize; - } else { - result = packetSize >= sizeof *request; + replyPacketSize = headerSize + payloadSize; + reply = HSPU_GetReplyPacket(packet, &replyPacketSize, session); + + if (reply && (replyPacketSize >= headerSize + payloadSize)) { + result = TRUE; + if (payloadSize > 0) { + *payload = reply + headerSize; + } else { + *payload = NULL; + } } + return result; } @@ -4775,13 +4689,13 @@ HgfsValidatePacket(char const *packetIn, // IN: request packet /* *----------------------------------------------------------------------------- * - * HgfsGetPayloadSize -- + * HgfsServerRead -- * - * Returns size of the payload based on incoming packet and total - * packet size. + * Handle a Read request. * * Results: - * Size of the payload in bytes. + * HGFS_ERROR_SUCCESS on success. + * HGFS error code on failure. * * Side effects: * None @@ -4789,37 +4703,89 @@ HgfsValidatePacket(char const *packetIn, // IN: request packet *----------------------------------------------------------------------------- */ -size_t -HgfsGetPayloadSize(char const *packetIn, // IN: request packet - size_t packetSize) // IN: request packet size +static void +HgfsServerRead(HgfsInputParam *input) // IN: Input params { - HgfsRequest *request = (HgfsRequest *)packetIn; - size_t result; - ASSERT(packetSize >= sizeof *request); - if (request->op < HGFS_OP_CREATE_SESSION_V4) { - result = packetSize - sizeof *request; + HgfsInternalStatus status; + HgfsHandle file; + uint64 offset; + uint32 requiredSize; + size_t replyPayloadSize = 0; + + HGFS_ASSERT_INPUT(input); + + if (!HgfsUnpackReadRequest(input->payload, input->payloadSize, input->op, &file, + &offset, &requiredSize)) { + status = HGFS_ERROR_PROTOCOL; } else { - HgfsHeader *header = (HgfsHeader *)packetIn; - ASSERT(packetSize >= header->packetSize); - ASSERT(header->packetSize >= header->headerSize); - result = header->packetSize - header->headerSize; + switch(input->op) { + case HGFS_OP_READ_FAST_V4: + case HGFS_OP_READ_V3: { + HgfsReplyReadV3 *reply; + void *payload; + uint32 inlineDataSize = + (HGFS_OP_READ_FAST_V4 == input->op) ? 0 : requiredSize; + + if (!HgfsAllocInitReply(input->packet, input->metaPacket, + sizeof *reply + inlineDataSize, (void **)&reply, + input->session)) { + status = HGFS_ERROR_PROTOCOL; + } else { + if (inlineDataSize > 0) { + payload = reply->payload; + } else { + payload = HSPU_GetDataPacketBuf(input->packet, BUF_WRITEABLE, + input->session); + } + if (payload) { + status = HgfsPlatformReadFile(file, input->session, offset, + requiredSize, payload, + &reply->actualSize); + if (HGFS_ERROR_SUCCESS == status) { + replyPayloadSize = sizeof *reply + + ((inlineDataSize > 0) ? reply->actualSize : 0); + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + } + break; + } + case HGFS_OP_READ: { + HgfsReplyRead *reply; + + if (HgfsAllocInitReply(input->packet, input->metaPacket, + sizeof *reply + requiredSize, (void **)&reply, + input->session)) { + status = HgfsPlatformReadFile(file, input->session, offset, requiredSize, + reply->payload, &reply->actualSize); + if (HGFS_ERROR_SUCCESS == status) { + replyPayloadSize = sizeof *reply + reply->actualSize; + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + break; + } + default: + NOT_IMPLEMENTED(); + status = HGFS_ERROR_PROTOCOL; + } } - return result; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsParseRequest -- + * HgfsServerWrite -- * - * Returns requested operation and pointer to the payload based on - * incoming packet and total packet size. + * Handle a Write request. * * Results: - * TRUE if the packet has been successfully processed. - * FALSE if the incoming request is malformed. + * None. * * Side effects: * None @@ -4827,187 +4793,273 @@ HgfsGetPayloadSize(char const *packetIn, // IN: request packet *----------------------------------------------------------------------------- */ -Bool -HgfsParseRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - void const **payload, // OUT: payload - size_t *payloadSize, // OUT: payloadSize - HgfsOp *op) // OUT: operation -{ - HgfsRequest *request = (HgfsRequest *)packetIn; - Bool result = FALSE; - ASSERT(packetSize >= sizeof *request); - *payload = NULL; - *payloadSize = 0; - if (request->op < HGFS_OP_OPEN_V3) { - /* Legacy requests do not have a separate header. */ - *payload = packetIn; - *op = request->op; - *payloadSize = packetSize; - result = TRUE; - } else if (request->op < HGFS_OP_CREATE_SESSION_V4) { - /* V3 header. */ - if (packetSize > sizeof *request) { - *payload = HGFS_REQ_GET_PAYLOAD_V3(packetIn); - *payloadSize = packetSize - ((char *)*payload - packetIn); +static void +HgfsServerWrite(HgfsInputParam *input) // IN: Input params +{ + uint32 numberBytesToWrite; + HgfsInternalStatus status; + HgfsWriteFlags flags; + uint64 offset; + char *dataToWrite; + uint32 replyActualSize; + size_t replyPayloadSize = 0; + HgfsHandle file; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackWriteRequest(input, &file, &offset, &numberBytesToWrite, + &flags, &dataToWrite)) { + + status = HgfsPlatformWriteFile(file, input->session, offset, numberBytesToWrite, + flags, dataToWrite, &replyActualSize); + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackWriteReply(input->packet, input->metaPacket, input->op, + replyActualSize, &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } } - *op = request->op; - result = TRUE; } else { - /* V4 header. */ - if (request->op == HGFS_V4_LEGACY_OPCODE) { - HgfsHeader *header = (HgfsHeader *)packetIn; - if (packetSize >= header->packetSize && - header->packetSize >= header->headerSize) { - *op = header->op; - result = TRUE; - if (header->packetSize > header->headerSize) { - *payload = packetIn + header->headerSize; - *payloadSize = header->packetSize - header->headerSize; - } - } - } + status = HGFS_ERROR_PROTOCOL; } - return result; + + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsUnpackOpenPayloadV1 -- + * HgfsQueryVolume -- * - * Unpack and validate payload for hgfs open request V1 to the HgfsFileOpenInfo - * structure that is used to pass around open request information. + * Performs actual work to get free space and capacity for a volume or + * a group of volumes. * * Results: - * TRUE on success. - * FALSE on failure. + * Zero on success. + * Non-zero on failure. * * Side effects: * None * + * *----------------------------------------------------------------------------- */ +HgfsInternalStatus +HgfsQueryVolume(HgfsSessionInfo *session, // IN: session info + char *fileName, // IN: cpName for the volume + size_t fileNameLength, // IN: cpName length + uint32 caseFlags, // IN: case sensitive/insensitive name + uint64 *freeBytes, // OUT: free space in bytes + uint64 *totalBytes) // OUT: capacity in bytes +{ + HgfsInternalStatus status = HGFS_ERROR_SUCCESS; + uint64 outFreeBytes; + uint64 outTotalBytes; + char *utf8Name = NULL; + size_t utf8NameLen; + HgfsNameStatus nameStatus; + Bool firstShare = TRUE; + HgfsShareInfo shareInfo; + HgfsHandle handle; + VolumeInfoType infoType; + DirectoryEntry *dent; + size_t failed = 0; + size_t shares = 0; + int offset = 0; + HgfsInternalStatus firstErr = HGFS_ERROR_SUCCESS; + Bool success; + + /* It is now safe to read the file name field. */ + nameStatus = HgfsServerGetShareInfo(fileName, + fileNameLength, + caseFlags, + &shareInfo, + &utf8Name, + &utf8NameLen); + + switch (nameStatus) { + case HGFS_NAME_STATUS_INCOMPLETE_BASE: + /* + * This is the base of our namespace. Clients can request a + * QueryVolumeInfo on it, on individual shares, or on just about + * any pathname. + */ -Bool -HgfsUnpackOpenPayloadV1(HgfsRequestOpen *requestV1, // IN: request payload - size_t payloadSize, // IN: request payload size - HgfsFileOpenInfo *openInfo) // IN/OUT: open info struct -{ - size_t extra; + LOG(4,("%s: opened search on base\n", __FUNCTION__)); + status = HgfsServerSearchVirtualDir(HgfsServerPolicy_GetShares, + HgfsServerPolicy_GetSharesInit, + HgfsServerPolicy_GetSharesCleanup, + DIRECTORY_SEARCH_TYPE_BASE, + session, + &handle); + if (status != 0) { + return status; + } - /* Enforced by the dispatch function. */ - if (payloadSize < sizeof *requestV1) { - return FALSE; - } + /* + * If we're outside the Tools, find out if we're to compute the minimum + * values across all shares, or the maximum values. + */ + infoType = VOLUME_INFO_TYPE_MIN; +#ifndef VMX86_TOOLS + { + char *volumeInfoType = Config_GetString("min", + "tools.hgfs.volumeInfoType"); + if (!Str_Strcasecmp(volumeInfoType, "max")) { + infoType = VOLUME_INFO_TYPE_MAX; + } + free(volumeInfoType); + } +#endif - extra = payloadSize - sizeof *requestV1; + /* + * Now go through all shares and get share paths on the server. + * Then retrieve space info for each share's volume. + */ + offset = 0; + while ((dent = HgfsGetSearchResult(handle, session, offset, + TRUE)) != NULL) { + char const *sharePath; + size_t sharePathLen; + uint64 currentFreeBytes = 0; + uint64 currentTotalBytes = 0; + size_t length; - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ - if (requestV1->fileName.length > extra) { - /* The input packet is smaller than the request. */ - return FALSE; - } + length = strlen(dent->d_name); - /* For OpenV1 requests, we know exactly what fields we expect. */ - openInfo->mask = HGFS_OPEN_VALID_MODE | - HGFS_OPEN_VALID_FLAGS | - HGFS_OPEN_VALID_OWNER_PERMS | - HGFS_OPEN_VALID_FILE_NAME; - openInfo->mode = requestV1->mode; - openInfo->cpName = requestV1->fileName.name; - openInfo->cpNameSize = requestV1->fileName.length; - openInfo->flags = requestV1->flags; - openInfo->ownerPerms = requestV1->permissions; - return TRUE; -} + /* + * Now that the server is passing '.' and ".." around as dents, we + * need to make sure to handle them properly. In particular, they + * should be ignored within QueryVolume, as they're not real shares. + */ + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) { + LOG(4, ("%s: Skipping fake share %s\n", __FUNCTION__, + dent->d_name)); + free(dent); + continue; + } + /* + * The above check ignores '.' and '..' so we do not include them in + * the share count here. + */ + shares++; -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackOpenPayloadV2 -- - * - * Unpack and validate payload for hgfs open request V2 to the HgfsFileOpenInfo - * structure that is used to pass around open request information. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ + /* + * Check permission on the share and get the share path. It is not + * fatal if these do not succeed. Instead we ignore the failures + * (apart from logging them) until we have processed all shares. Only + * then do we check if there were any failures; if all shares failed + * to process then we bail out with an error code. + */ -Bool -HgfsUnpackOpenPayloadV2(HgfsRequestOpenV2 *requestV2, // IN: request payload - size_t payloadSize, // IN: request payload size - HgfsFileOpenInfo *openInfo) // IN/OUT: open info struct -{ - size_t extra; + nameStatus = HgfsServerPolicy_GetSharePath(dent->d_name, length, + HGFS_OPEN_MODE_READ_ONLY, + &sharePathLen, + &sharePath); + free(dent); + if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { + LOG(4, ("%s: No such share or access denied\n", __FUNCTION__)); + if (0 == firstErr) { + firstErr = HgfsPlatformConvertFromNameStatus(nameStatus); + } + failed++; + continue; + } - /* Enforced by the dispatch function. */ - if (payloadSize < sizeof *requestV2) { - return FALSE; - } + /* + * Pick the drive with amount of space available and return that + * according to different volume info type. + */ - extra = payloadSize - sizeof *requestV2; - if (!(requestV2->mask & HGFS_OPEN_VALID_FILE_NAME)) { - /* We do not support open requests without a valid file name. */ - return FALSE; - } + if (!HgfsServerStatFs(sharePath, sharePathLen, + ¤tFreeBytes, ¤tTotalBytes)) { + LOG(4, ("%s: error getting volume information\n", + __FUNCTION__)); + if (0 == firstErr) { + firstErr = HGFS_ERROR_IO; + } + failed++; + continue; + } - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ - if (requestV2->fileName.length > extra) { - /* The input packet is smaller than the request. */ - return FALSE; + /* + * Pick the drive with amount of space available and return that + * according to different volume info type. + */ + switch (infoType) { + case VOLUME_INFO_TYPE_MIN: + if ((outFreeBytes > currentFreeBytes) || firstShare) { + firstShare = FALSE; + outFreeBytes = currentFreeBytes; + outTotalBytes = currentTotalBytes; + } + break; + case VOLUME_INFO_TYPE_MAX: + if ((outFreeBytes < currentFreeBytes)) { + outFreeBytes = currentFreeBytes; + outTotalBytes = currentTotalBytes; + } + break; + default: + NOT_IMPLEMENTED(); + } + } + if (!HgfsRemoveSearch(handle, session)) { + LOG(4, ("%s: could not close search on base\n", __FUNCTION__)); + } + if (shares == failed) { + if (firstErr != 0) { + /* + * We failed to query any of the shares. We return the error] + * from the first share failure. + */ + status = firstErr; + } + /* No shares but no error, return zero for sizes and success. */ + } + break; + case HGFS_NAME_STATUS_COMPLETE: + ASSERT(utf8Name); + LOG(4,("%s: querying path %s\n", __FUNCTION__, utf8Name)); + success = HgfsServerStatFs(utf8Name, utf8NameLen, + &outFreeBytes, &outTotalBytes); + free(utf8Name); + if (!success) { + LOG(4, ("%s: error getting volume information\n", __FUNCTION__)); + status = HGFS_ERROR_IO; + } + break; + default: + LOG(4,("%s: file access check failed\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); } - /* - * Copy all the fields into our carrier struct. Some will probably be - * garbage, but it's simpler to copy everything now and check the - * valid bits before reading later. - */ + *freeBytes = outFreeBytes; + *totalBytes = outTotalBytes; - openInfo->mask = requestV2->mask; - openInfo->mode = requestV2->mode; - openInfo->cpName = requestV2->fileName.name; - openInfo->cpNameSize = requestV2->fileName.length; - openInfo->flags = requestV2->flags; - openInfo->specialPerms = requestV2->specialPerms; - openInfo->ownerPerms = requestV2->ownerPerms; - openInfo->groupPerms = requestV2->groupPerms; - openInfo->otherPerms = requestV2->otherPerms; - openInfo->attr = requestV2->attr; - openInfo->allocationSize = requestV2->allocationSize; - openInfo->desiredAccess = requestV2->desiredAccess; - openInfo->shareAccess = requestV2->shareAccess; - openInfo->desiredLock = requestV2->desiredLock; - return TRUE; + return status; } /* *----------------------------------------------------------------------------- * - * HgfsUnpackOpenPayloadV3 -- + * HgfsServerQueryVolume -- * - * Unpack and validate payload for hgfs open request V3 to the HgfsFileOpenInfo - * structure that is used to pass around open request information. + * Handle a Query Volume request. + * + * Right now we only handle the volume space request. Call Wiper library + * to get the volume information. + * It is possible that shared folders can belong to different volumes on + * the server. If this is the case, default to return the space information + * of the volume that has the least amount of the available space, but it's + * configurable with a config option (tools.hgfs.volumeInfoType). 2 possible + * options, min and max. * * Results: - * TRUE on success. - * FALSE on failure. + * None. * * Side effects: * None @@ -5015,69 +5067,62 @@ HgfsUnpackOpenPayloadV2(HgfsRequestOpenV2 *requestV2, // IN: request payload *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackOpenPayloadV3(HgfsRequestOpenV3 *requestV3, // IN: request payload - size_t payloadSize, // IN: request payload size - HgfsFileOpenInfo *openInfo) // IN/OUT: open info struct +static void +HgfsServerQueryVolume(HgfsInputParam *input) // IN: Input params { - size_t extra; - - /* Enforced by the dispatch function. */ - if (payloadSize < sizeof *requestV3) { - return FALSE; - } - - extra = payloadSize - sizeof *requestV3; + HgfsInternalStatus status; + size_t replyPayloadSize = 0; + HgfsHandle file; + char *fileName; + size_t fileNameLength; + uint32 caseFlags; + Bool useHandle; + uint64 freeBytes; + uint64 totalBytes; - if (!(requestV3->mask & HGFS_OPEN_VALID_FILE_NAME)) { - /* We do not support open requests without a valid file name. */ - return FALSE; - } + HGFS_ASSERT_INPUT(input); - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ - if (requestV3->fileName.length > extra) { - /* The input packet is smaller than the request. */ - return FALSE; + if (HgfsUnpackQueryVolumeRequest(input->payload, input->payloadSize, input->op, + &useHandle, &fileName, + &fileNameLength, &caseFlags, &file)) { + /* + * We don't yet support file handle for this operation. + * Clients should retry using the file name. + */ + if (useHandle) { + LOG(4, ("%s: Doesn't support file handle.\n", __FUNCTION__)); + status = HGFS_ERROR_INVALID_PARAMETER; + } else { + status = HgfsQueryVolume(input->session, fileName, fileNameLength, caseFlags, + &freeBytes, &totalBytes); + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackQueryVolumeReply(input->packet, input->metaPacket, + input->op, freeBytes, totalBytes, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } + } else { + status = HGFS_ERROR_PROTOCOL; } - /* - * Copy all the fields into our carrier struct. Some will probably be - * garbage, but it's simpler to copy everything now and check the - * valid bits before reading later. - */ - openInfo->mask = requestV3->mask; - openInfo->mode = requestV3->mode; - openInfo->cpName = requestV3->fileName.name; - openInfo->cpNameSize = requestV3->fileName.length; - openInfo->caseFlags = requestV3->fileName.caseType; - openInfo->flags = requestV3->flags; - openInfo->specialPerms = requestV3->specialPerms; - openInfo->ownerPerms = requestV3->ownerPerms; - openInfo->groupPerms = requestV3->groupPerms; - openInfo->otherPerms = requestV3->otherPerms; - openInfo->attr = requestV3->attr; - openInfo->allocationSize = requestV3->allocationSize; - openInfo->desiredAccess = requestV3->desiredAccess; - openInfo->shareAccess = requestV3->shareAccess; - openInfo->desiredLock = requestV3->desiredLock; - return TRUE; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsUnpackOpenRequest -- + * HgfsSymlinkCreate -- * - * Unpack hgfs open request to the HgfsFileOpenInfo structure that is used - * to pass around open request information. + * Platform independent function that verifies whether symbolic link creation + * is allowed for the specific shared folder and then calls platform specific + * HgfsPlatformSymlinkCreate to do the actual job. * * Results: - * TRUE on success. - * FALSE on failure. + * Zero on success. + * Non-zero on failure. * * Side effects: * None @@ -5085,65 +5130,83 @@ HgfsUnpackOpenPayloadV3(HgfsRequestOpenV3 *requestV3, // IN: request payload *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackOpenRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsFileOpenInfo *openInfo) // IN/OUT: open info structure -{ - void const *payload; - size_t payloadSize; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(openInfo); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, &op)) { - return FALSE; - } - - openInfo->requestType = op; - openInfo->caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; +HgfsInternalStatus +HgfsSymlinkCreate(HgfsSessionInfo *session, // IN: session info, + char *srcFileName, // IN: symbolic link file name + uint32 srcFileNameLength, // IN: symbolic link name length + uint32 srcCaseFlags, // IN: symlink case flags + char *trgFileName, // IN: symbolic link target name + uint32 trgFileNameLength, // IN: target name length + uint32 trgCaseFlags) // IN: target case flags +{ + HgfsShareInfo shareInfo; + HgfsInternalStatus status = 0; + HgfsNameStatus nameStatus; + HgfsShareOptions configOptions; + char *localSymlinkName = NULL; + size_t localSymlinkNameLen; + char localTargetName[HGFS_PACKET_MAX]; - switch (op) { - case HGFS_OP_OPEN_V3: { - HgfsRequestOpenV3 *requestV3 = (HgfsRequestOpenV3 *)payload; - LOG(4, ("%s: HGFS_OP_OPEN_V3\n", __FUNCTION__)); + /* + * It is now safe to read the symlink file name and the + * "targetName" field + */ - if (!HgfsUnpackOpenPayloadV3(requestV3, payloadSize, openInfo)) { - return FALSE; + nameStatus = HgfsServerGetShareInfo(srcFileName, + srcFileNameLength, + srcCaseFlags, + &shareInfo, + &localSymlinkName, + &localSymlinkNameLen); + if (nameStatus == HGFS_NAME_STATUS_COMPLETE) { + if (shareInfo.writePermissions ) { + /* Get the config options. */ + nameStatus = HgfsServerPolicy_GetShareOptions(srcFileName, srcFileNameLength, + &configOptions); + if (nameStatus == HGFS_NAME_STATUS_COMPLETE) { + /* Prohibit symlink ceation if symlink following is enabled. */ + if (HgfsServerPolicy_IsShareOptionSet(configOptions, HGFS_SHARE_FOLLOW_SYMLINKS)) { + status = HGFS_ERROR_ACCESS_DENIED; + } + } else { + LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, srcFileName)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); } - break; - } - case HGFS_OP_OPEN_V2: { - HgfsRequestOpenV2 *requestV2 = (HgfsRequestOpenV2 *)payload; - - if (!HgfsUnpackOpenPayloadV2(requestV2, payloadSize, openInfo)) { - return FALSE; + } else { + status = HgfsPlatformFileExists(localSymlinkName); + if (status != 0) { + if (status == HGFS_ERROR_FILE_NOT_FOUND) { + status = HGFS_ERROR_ACCESS_DENIED; + } + } else { + status = HGFS_ERROR_FILE_EXIST; } - break; + LOG(4, ("%s: failed access check, error %d\n", __FUNCTION__, status)); } - case HGFS_OP_OPEN: { - HgfsRequestOpen *requestV1 = (HgfsRequestOpen *)payload; + } else { + LOG(4, ("%s: symlink name access check failed\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } + if (HGFS_ERROR_SUCCESS == status) { + /* Convert from CPName-lite to normal and NUL-terminate. */ + memcpy(localTargetName, trgFileName, trgFileNameLength); + CPNameLite_ConvertFrom(localTargetName, trgFileNameLength, DIRSEPC); + localTargetName[trgFileNameLength] = '\0'; - if (!HgfsUnpackOpenPayloadV1(requestV1, payloadSize, openInfo)) { - return FALSE; - } - break; - } - default: - NOT_REACHED(); - return FALSE; + status = HgfsPlatformSymlinkCreate(localSymlinkName, localTargetName); } - return TRUE; + free(localSymlinkName); + return status; } + /* *----------------------------------------------------------------------------- * - * HgfsPackReplyHeaderV4 -- + * HgfsServerSymlinkCreate -- * - * Pack hgfs header that corresponds an incoming packet. + * Handle a SymlinkCreate request. * * Results: * None. @@ -5155,31 +5218,60 @@ HgfsUnpackOpenRequest(char const *packetIn, // IN: request packet */ static void -HgfsPackReplyHeaderV4(HgfsInternalStatus status, // IN: reply status - uint32 payloadSize, // IN: size of the reply payload - HgfsHeader const *packetIn, // IN: original incoming packet - HgfsHeader *header) // OUT: outgoing packet header -{ - memset(header, 0, sizeof *header); - header->version = 1; - header->dummy = HGFS_V4_LEGACY_OPCODE; - header->packetSize = payloadSize + sizeof *header; - header->headerSize = sizeof *header; - header->requestId = packetIn->requestId; - header->op = packetIn->op; - header->status = HgfsConvertFromInternalStatus(status); - header->flags = 0; - header->information = status; - header->sessionId = packetIn->sessionId; +HgfsServerSymlinkCreate(HgfsInputParam *input) // IN: Input params +{ + HgfsInternalStatus status; + HgfsHandle srcFile; + char *srcFileName; + size_t srcFileNameLength; + uint32 srcCaseFlags; + Bool srcUseHandle; + HgfsHandle trgFile; + char *trgFileName; + size_t trgFileNameLength; + uint32 trgCaseFlags; + Bool trgUseHandle; + size_t replyPayloadSize = 0; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackSymlinkCreateRequest(input->payload, input->payloadSize, input->op, + &srcUseHandle, &srcFileName, + &srcFileNameLength, &srcCaseFlags, &srcFile, + &trgUseHandle, &trgFileName, + &trgFileNameLength, &trgCaseFlags, &trgFile)) { + /* + * We don't yet support file handle for this operation. + * Clients should retry using the file name. + */ + if (srcUseHandle || trgUseHandle) { + LOG(4, ("%s: Doesn't support file handle.\n", __FUNCTION__)); + status = HGFS_ERROR_INVALID_PARAMETER; + } else { + status = HgfsSymlinkCreate(input->session, srcFileName, srcFileNameLength, + srcCaseFlags, trgFileName, trgFileNameLength, + trgCaseFlags); + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackSymlinkCreateReply(input->packet, input->metaPacket, input->op, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsPackLegacyReplyHeader -- + * HgfsServerSearchOpen -- * - * Pack pre-V4 reply header. + * Handle a search open request. * * Results: * None. @@ -5190,53 +5282,154 @@ HgfsPackReplyHeaderV4(HgfsInternalStatus status, // IN: reply status *----------------------------------------------------------------------------- */ -void -HgfsPackLegacyReplyHeader(HgfsInternalStatus status, // IN: reply status - HgfsHandle id, // IN: original packet id - HgfsReply *header) // OUT: outgoing packet header +static void +HgfsServerSearchOpen(HgfsInputParam *input) // IN: Input params { - memset(header, 0, sizeof *header); - header->status = HgfsConvertFromInternalStatus(status); - header->id = id; + HgfsInternalStatus status; + size_t replyPayloadSize = 0; + char *dirName; + uint32 dirNameLength; + uint32 caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + HgfsHandle search; + HgfsNameStatus nameStatus; + HgfsShareInfo shareInfo; + char *baseDir = NULL; + size_t baseDirLen; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackSearchOpenRequest(input->payload, input->payloadSize, input->op, + &dirName, &dirNameLength, &caseFlags)) { + nameStatus = HgfsServerGetShareInfo(dirName, dirNameLength, caseFlags, &shareInfo, + &baseDir, &baseDirLen); + status = HgfsPlatformSearchDir(nameStatus, dirName, dirNameLength, caseFlags, + &shareInfo, baseDir, baseDirLen, + input->session, &search); + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackSearchOpenReply(input->packet, input->metaPacket, input->op, search, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsPackOpenReplyV3 -- + * HgfsValidateRenameFile -- * - * Pack hgfs open V3 reply payload to the HgfsReplyOpenV3 structure. + * Validates if the file can can participate in rename process either as + * as a source or as a target. * * Results: - * None. + * HGFS_ERROR_SUCCESS if rename operation is allowed. + * Appropriate error code otherwise. * * Side effects: - * None + * Allcates locaFileName which must be freed by the caller. * *----------------------------------------------------------------------------- */ -static void -HgfsPackOpenReplyV3(HgfsFileOpenInfo *openInfo, // IN: open info struct - HgfsReplyOpenV3 *reply) // OUT: size of packet -{ - reply->file = openInfo->file; - reply->reserved = 0; - if (openInfo->mask & HGFS_OPEN_VALID_SERVER_LOCK) { - reply->acquiredLock = openInfo->acquiredLock; +HgfsInternalStatus +HgfsValidateRenameFile(Bool useHandle, // IN: + HgfsHandle fileHandle, // IN: + char *cpName, // IN: + size_t cpNameLength, // IN: + uint32 caseFlags, // IN: + HgfsSessionInfo *session, // IN: Session info + fileDesc* descr, // OUT: + HgfsShareInfo *shareInfo, // OUT: + char **localFileName, // OUT: + size_t *localNameLength) // OUT: +{ + HgfsInternalStatus status; + Bool sharedFolderOpen = FALSE; + HgfsServerLock serverLock = HGFS_LOCK_NONE; + HgfsNameStatus nameStatus; + + + if (useHandle) { + status = HgfsPlatformGetFd(fileHandle, session, FALSE, descr); + + if (HGFS_ERROR_SUCCESS != status) { + LOG(4, ("%s: could not map cached handle %d, error %u\n", + __FUNCTION__, fileHandle, status)); + } else if (!HgfsHandle2FileNameMode(fileHandle, session, &shareInfo->writePermissions, + &shareInfo->readPermissions, localFileName, + localNameLength)) { + /* + * HgfsPlatformRename requires valid source file name even when file handle + * is specified. + * Also the name will be required to update the nodes on a successful + * rename operation. + */ + LOG(4, ("%s: could not get file name for fd %d\n", __FUNCTION__, + *descr)); + status = HGFS_ERROR_INVALID_HANDLE; + } else if (HgfsHandleIsSharedFolderOpen(fileHandle, session, &sharedFolderOpen) && + sharedFolderOpen) { + LOG(4, ("%s: Cannot rename shared folder\n", __FUNCTION__)); + status = HGFS_ERROR_ACCESS_DENIED; + } } else { - reply->acquiredLock = HGFS_LOCK_NONE; + nameStatus = HgfsServerGetShareInfo(cpName, + cpNameLength, + caseFlags, + shareInfo, + localFileName, + localNameLength); + if (HGFS_NAME_STATUS_COMPLETE != nameStatus) { + LOG(4, ("%s: access check failed\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } else if (HgfsServerIsSharedFolderOnly(cpName, cpNameLength)) { + /* Guest OS is not allowed to rename shared folder. */ + LOG(4, ("%s: Cannot rename shared folder\n", __FUNCTION__)); + status = HGFS_ERROR_ACCESS_DENIED; + } else { + status = HGFS_ERROR_SUCCESS; + } + } + + ASSERT(*localFileName != NULL || HGFS_ERROR_SUCCESS != status); + + if (HGFS_ERROR_SUCCESS == status) { + if (HgfsFileHasServerLock(*localFileName, session, &serverLock, descr)) { + /* + * XXX: Before renaming the file, check to see if we are holding + * an oplock on both the old and new files. If one of them is oplocked, and + * we commence with the rename, we'll trigger an oplock break that'll + * deadlock us. The client should be smart enough to break necessary oplocks + * on the source and target files before calling rename, so we'll return + * an error. + */ + + LOG (4, ("%s: File has an outstanding oplock. Client " + "should remove this oplock and try again.\n", __FUNCTION__)); + status = HGFS_ERROR_PATH_BUSY; + } } + + return status; } /* *----------------------------------------------------------------------------- * - * HgfsPackOpenV2Reply -- + * HgfsServerRename -- * - * Pack hgfs open V2 reply payload to the HgfsReplyOpenV3 structure. + * Handle a Rename request. + * + * Simply converts the new and old names to local filenames, calls + * platform specific function to rename/move the file, and returns an + * appropriate response to the driver. * * Results: * None. @@ -5248,24 +5441,122 @@ HgfsPackOpenReplyV3(HgfsFileOpenInfo *openInfo, // IN: open info struct */ static void -HgfsPackOpenV2Reply(HgfsFileOpenInfo *openInfo, // IN: open info struct - HgfsReplyOpenV2 *reply) // OUT: reply payload -{ - reply->file = openInfo->file; - if (openInfo->mask & HGFS_OPEN_VALID_SERVER_LOCK) { - reply->acquiredLock = openInfo->acquiredLock; +HgfsServerRename(HgfsInputParam *input) // IN: Input params +{ + char *utf8OldName = NULL; + size_t utf8OldNameLen; + char *utf8NewName = NULL; + size_t utf8NewNameLen; + char *cpOldName; + size_t cpOldNameLen; + char *cpNewName; + size_t cpNewNameLen; + HgfsInternalStatus status; + fileDesc srcFileDesc; + fileDesc targetFileDesc; + HgfsHandle srcFile; + HgfsHandle targetFile; + HgfsRenameHint hints; + uint32 oldCaseFlags; + uint32 newCaseFlags; + HgfsShareInfo shareInfo; + size_t replyPayloadSize = 0; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackRenameRequest(input->payload, input->payloadSize, input->op, &cpOldName, + &cpOldNameLen, &cpNewName, &cpNewNameLen, + &hints, &srcFile, &targetFile, &oldCaseFlags, + &newCaseFlags)) { + status = HgfsValidateRenameFile((hints & HGFS_RENAME_HINT_USE_SRCFILE_DESC) != 0, + srcFile, + cpOldName, + cpOldNameLen, + oldCaseFlags, + input->session, + &srcFileDesc, + &shareInfo, + &utf8OldName, + &utf8OldNameLen); + if (HGFS_ERROR_SUCCESS == status) { + /* + * Renaming a file requires both read and write permssions for the + * original file. + * However the error code must be different depending on the existence + * of the file with the same name. + */ + if (!shareInfo.writePermissions || !shareInfo.readPermissions) { + status = HgfsPlatformFileExists(utf8OldName); + if (HGFS_ERROR_SUCCESS == status) { + status = HGFS_ERROR_ACCESS_DENIED; + } + LOG(4, ("HgfsServerRename: failed access check, error %d\n", status)); + } else { + status = + HgfsValidateRenameFile((hints & HGFS_RENAME_HINT_USE_TARGETFILE_DESC) != 0, + targetFile, + cpNewName, + cpNewNameLen, + newCaseFlags, + input->session, + &targetFileDesc, + &shareInfo, + &utf8NewName, + &utf8NewNameLen); + if (HGFS_ERROR_SUCCESS == status) { + /* + * Renaming a file requires both read and write permssions for + * the target directory. + * However the error code must be different depending on the existence + * of the target directory - if the destination directory exists then + * ERROR_ACCESS_DENIED should be returned regardless if the destination + * file exists. + */ + if (!shareInfo.writePermissions || !shareInfo.readPermissions) { + status = HgfsPlatformFileExists(utf8NewName); + if (HGFS_ERROR_SUCCESS == status || + HGFS_ERROR_FILE_NOT_FOUND == status) { + status = HGFS_ERROR_ACCESS_DENIED; + } + LOG(4, ("HgfsServerRename: failed access check, error %d\n", status)); + } + } + } + } } else { - reply->acquiredLock = HGFS_LOCK_NONE; + status = HGFS_ERROR_PROTOCOL; + } + + /* If all pre-conditions are met go ahead with actual rename. */ + if (HGFS_ERROR_SUCCESS == status) { + status = HgfsPlatformRename(utf8OldName, srcFileDesc, utf8NewName, + targetFileDesc, hints); + if (HGFS_ERROR_SUCCESS == status) { + /* Update all file nodes that refer to this file to contain the new name. */ + HgfsUpdateNodeNames(utf8OldName, utf8NewName, input->session); + if (!HgfsPackRenameReply(input->packet, input->metaPacket, input->op, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } } + + free(utf8OldName); + free(utf8NewName); + + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsPackOpenV1Reply -- + * HgfsServerCreateDir -- + * + * Handle a CreateDir request. * - * Pack hgfs open V1 reply payload to the HgfsReplyOpenV3 structure. + * Simply converts to the local filename, calls platform specific + * code to create a directory, and returns an appropriate response to the driver. * * Results: * None. @@ -5277,2024 +5568,77 @@ HgfsPackOpenV2Reply(HgfsFileOpenInfo *openInfo, // IN: open info struct */ static void -HgfsPackOpenV1Reply(HgfsFileOpenInfo *openInfo, // IN: open info struct - HgfsReplyOpen *reply) // OUT: reply payload +HgfsServerCreateDir(HgfsInputParam *input) // IN: Input params { - reply->file = openInfo->file; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsAllocInitReply -- - * - * Allocates hgfs reply packet and initializes its header. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsAllocInitReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - size_t payloadSize, // IN: payload size - HgfsInternalStatus status, // IN: reply status - char **packetOut, // OUT: allocated reply - void **payload, // OUT: pointer to the reply payload - size_t *packetSize, // OUT: size of the allocated packet - HgfsSessionInfo *session) // IN: Session Info -{ - HgfsRequest *request = (HgfsRequest *)packetIn; - size_t replyPacketSize; - size_t headerSize = 0; /* Replies prior to V3 do not have a header. */ - HgfsInternalStatus dummyStatus; - - if (HGFS_V4_LEGACY_OPCODE == request->op) { - headerSize = sizeof(HgfsHeader); - } else if (request->op < HGFS_OP_CREATE_SESSION_V4 && - request->op > HGFS_OP_RENAME_V2) { - headerSize = sizeof(HgfsReply); - } - replyPacketSize = *packetSize = headerSize + payloadSize; - *packetOut = HSPU_GetReplyPacket(packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, *packetSize, replyPacketSize, dummyStatus, exit); - - *payload = *packetOut + headerSize; - if (HGFS_V4_LEGACY_OPCODE == request->op) { - HgfsPackReplyHeaderV4(status, - payloadSize, - (HgfsHeader const *)packetIn, - (HgfsHeader *)*packetOut); - } else if (request->op < HGFS_OP_CREATE_SESSION_V4) { - HgfsRequest const *request = (HgfsRequest const *)packetIn; - HgfsPackLegacyReplyHeader(status, request->id, (HgfsReply *)*packetOut); - } - - return TRUE; -exit: - return FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackOpenReply -- - * - * Pack hgfs open reply to the HgfsReplyOpen{V2} structure. - * - * Results: - * Always TRUE. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackOpenReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsFileOpenInfo *openInfo, // IN: open info struct - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session info -{ - Bool result; - ASSERT(packetIn); - ASSERT(openInfo); - ASSERT(packetSize); - - *packetOut = NULL; - *packetSize = 0; - - switch (openInfo->requestType) { - case HGFS_OP_OPEN_V3: { - HgfsReplyOpenV3 *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - if (result == FALSE) { - goto error; - } - HgfsPackOpenReplyV3(openInfo, reply); - break; - } - case HGFS_OP_OPEN_V2: { - HgfsReplyOpenV2 *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - if (result == FALSE) { - goto error; - } - HgfsPackOpenV2Reply(openInfo, reply); - break; - } - case HGFS_OP_OPEN: { - HgfsReplyOpen *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - if (result == FALSE) { - goto error; - } - HgfsPackOpenV1Reply(openInfo, reply); - break; - } - default: - goto error; - } - - return TRUE; -error: - return FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackClosePayload -- - * - * Unpack hgfs close payload to get the handle which need to be closed. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackClosePayload(HgfsRequestClose *request, // IN: payload - size_t payloadSize, // IN: payload size - HgfsHandle* file) // OUT: HGFS handle to close -{ - LOG(4, ("%s: HGFS_OP_CLOSE\n", __FUNCTION__)); - if (payloadSize >= sizeof *request) { - *file = request->file; - return TRUE; - } - return FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackClosePayloadV3 -- - * - * Unpack hgfs close payload V3 to get the handle which need to be closed. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackClosePayloadV3(HgfsRequestCloseV3 *requestV3, // IN: payload - size_t payloadSize, // IN: payload size - HgfsHandle* file) // OUT: HGFS handle to close -{ - LOG(4, ("%s: HGFS_OP_CLOSE_V3\n", __FUNCTION__)); - if (payloadSize >= sizeof *requestV3) { - *file = requestV3->file; - return TRUE; - } - return FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackCloseRequest -- - * - * Unpack hgfs close request to get the handle to close. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackCloseRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: request type - HgfsHandle *file) // OUT: Handle to close -{ - void const *payload; - size_t payloadSize; - - ASSERT(packetIn); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, op)) { - return FALSE; - } - - switch (*op) { - case HGFS_OP_CLOSE_V3: { - HgfsRequestCloseV3 *requestV3 = (HgfsRequestCloseV3 *)payload; - - if (!HgfsUnpackClosePayloadV3(requestV3, payloadSize, file)) { - return FALSE; - } - break; - } - case HGFS_OP_CLOSE: { - HgfsRequestClose *requestV1 = (HgfsRequestClose *)payload; - - if (!HgfsUnpackClosePayload(requestV1, payloadSize, file)) { - return FALSE; + HgfsInternalStatus status; + HgfsNameStatus nameStatus; + HgfsCreateDirInfo info; + char *utf8Name; + size_t utf8NameLen; + size_t replyPayloadSize = 0; + HgfsShareInfo shareInfo; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackCreateDirRequest(input->payload, input->payloadSize, + input->op, &info)) { + nameStatus = HgfsServerGetShareInfo(info.cpName, info.cpNameSize, info.caseFlags, + &shareInfo, &utf8Name, &utf8NameLen); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + ASSERT(utf8Name); + + LOG(4, ("%s: making dir \"%s\"", __FUNCTION__, utf8Name)); + /* + * For read-only shares we must never attempt to create a directory. + * However the error code must be different depending on the existence + * of the file with the same name. + */ + if (shareInfo.writePermissions) { + status = HgfsPlatformCreateDir(&info, utf8Name); + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackCreateDirReply(input->packet, input->metaPacket, info.requestType, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_PROTOCOL; + } + } + } else { + status = HgfsPlatformFileExists(utf8Name); + if (HGFS_ERROR_SUCCESS == status) { + status = HGFS_ERROR_FILE_EXIST; + } else if (HGFS_ERROR_FILE_NOT_FOUND == status) { + status = HGFS_ERROR_ACCESS_DENIED; + } } - break; - } - default: - NOT_REACHED(); - return FALSE; - } - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackCloseReply -- - * - * Pack hgfs close reply to the HgfsReplyClose(V3) structure. - * - * Results: - * TRUE on success, FALSE on failure - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackCloseReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info -{ - Bool result; - ASSERT(packetIn); - ASSERT(packetSize); - - *packetOut = NULL; - *packetSize = 0; - - switch (op) { - case HGFS_OP_CLOSE_V3: { - HgfsReplyCloseV3 *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - case HGFS_OP_CLOSE: { - HgfsReplyClose *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - default: - result = FALSE; - NOT_REACHED(); - } - - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackSearchClosePayload -- - * - * Unpack hgfs search close payload to get the search handle which need to be closed. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackSearchClosePayload(HgfsRequestSearchClose *request, // IN: payload - size_t payloadSize, // IN: payload size - HgfsHandle* search) // OUT: search to close -{ - LOG(4, ("%s: HGFS_OP_SEARCH_CLOSE\n", __FUNCTION__)); - if (payloadSize >= sizeof *request) { - *search = request->search; - return TRUE; - } - return FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackClosePayloadV3 -- - * - * Unpack hgfs search close payload V3 to get the search handle which need to - * be closed. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackSearchClosePayloadV3(HgfsRequestSearchCloseV3 *requestV3, // IN: payload - size_t payloadSize, // IN: payload size - HgfsHandle* search) // OUT: search -{ - LOG(4, ("%s: HGFS_OP_SEARCH_CLOSE_V3\n", __FUNCTION__)); - if (payloadSize >= sizeof *requestV3) { - *search = requestV3->search; - return TRUE; - } - return FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackSearchCloseRequest -- - * - * Unpack hgfs search close request to get the search handle. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackSearchCloseRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: request type - HgfsHandle *search) // OUT: search to close -{ - void const *payload; - size_t payloadSize; - - ASSERT(packetIn); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, op)) { - return FALSE; - } - - switch (*op) { - case HGFS_OP_SEARCH_CLOSE_V3: { - HgfsRequestSearchCloseV3 *requestV3 = (HgfsRequestSearchCloseV3 *)payload; - - if (!HgfsUnpackSearchClosePayloadV3(requestV3, payloadSize, search)) { - return FALSE; - } - break; - } - case HGFS_OP_SEARCH_CLOSE: { - HgfsRequestSearchClose *requestV1 = (HgfsRequestSearchClose *)payload; - - if (!HgfsUnpackSearchClosePayload(requestV1, payloadSize, search)) { - return FALSE; - } - break; - } - default: - NOT_REACHED(); - return FALSE; - } - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackSearchCloseReply -- - * - * Pack hgfs SearchClose reply into a HgfsReplySearchClose(V3) structure. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackSearchCloseReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info -{ - Bool result; - ASSERT(packetIn); - ASSERT(packetSize); - - *packetOut = NULL; - *packetSize = 0; - - switch (op) { - case HGFS_OP_SEARCH_CLOSE_V3: { - HgfsReplyCloseV3 *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - case HGFS_OP_SEARCH_CLOSE: { - HgfsReplyClose *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - default: - NOT_REACHED(); - result = FALSE; - } - - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackFileName -- - * - * Unpack HgfsFileName into a pointer to a CPName and size of the name. - * Verifies that input buffer has enough space to hold the name. - * - * Results: - * TRUE on success, FALSE on failure (buffer too small). - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackFileName(HgfsFileName *name, // IN: file name - size_t maxNameSize, // IN: space allocated for the name - char **cpName, // OUT: CP name - size_t *cpNameSize) // OUT: CP name size -{ - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ - if (name->length > maxNameSize) { - /* The input packet is smaller than the request. */ - return FALSE; - } - *cpName = name->name; - *cpNameSize = name->length; - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackFileNameV3 -- - * - * Unpack HgfsFileNameV3 into a pointer to a CPName and size of the name - * or into file handle. - * Verifies that input buffer has enough space to hold the name. - * - * Results: - * TRUE on success, FALSE on failure (buffer too small). - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackFileNameV3(HgfsFileNameV3 *name, // IN: file name - size_t maxNameSize, // IN: space allocated for the name - Bool *useHandle, // OUT: file name or handle returned? - char **cpName, // OUT: CP name - size_t *cpNameSize, // OUT: CP name size - HgfsHandle *file, // OUT: HGFS file handle - uint32 *caseFlags) // OUT: case-sensitivity flags -{ - /* - * If we've been asked to reuse a handle, we don't need to look at, let - * alone test the filename or its length. - */ - if (name->flags & HGFS_FILE_NAME_USE_FILE_DESC) { - *file = name->fid; - *cpName = NULL; - *cpNameSize = 0; - *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - *useHandle = TRUE; - } else { - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ - if (name->length > maxNameSize) { - /* The input packet is smaller than the request */ - return FALSE; - } - *file = HGFS_INVALID_HANDLE; - *cpName = name->name; - *cpNameSize = name->length; - *caseFlags = name->caseType; - *useHandle = FALSE; - } - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackDeletePayloadV3 -- - * - * Unpack hgfs delete request V3 payload and initialize a corresponding - * HgfsHandle or file name to tell us which to delete. Hints - * holds flags to specify a handle or name for the file or - * directory to delete. - * - * Since the structure of the get delete request packet is the same - * for Delete File or Directory of the protocol, code is identical for - * both operations. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackDeletePayloadV3(HgfsRequestDeleteV3 *requestV3, // IN: request payload - size_t payloadSize, // IN: payload size - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsDeleteHint *hints, // OUT: delete hints - HgfsHandle *file, // OUT: file handle - uint32 *caseFlags) // OUT: case-sensitivity flags -{ - Bool result; - Bool useHandle; - - if (payloadSize < sizeof *requestV3) { - return FALSE; - } - - *hints = requestV3->hints; - - result = HgfsUnpackFileNameV3(&requestV3->fileName, - payloadSize - sizeof *requestV3, - &useHandle, - cpName, - cpNameSize, - file, - caseFlags); - if (useHandle) { - *hints |= HGFS_DELETE_HINT_USE_FILE_DESC; - } - - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackDeletePayloadV2 -- - * - * Unpack hgfs delete request V2 payload and initialize a corresponding - * HgfsHandle or file name to tell us which to delete. Hints - * holds flags to specify a handle or name for the file or - * directory to delete. - * - * Since the structure of the get delete request packet is the same - * for Delete File or Directory of the protocol, code is identical for - * both operations. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackDeletePayloadV2(HgfsRequestDeleteV2 *requestV2, // IN: request payload - size_t payloadSize, // IN: payload size - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsDeleteHint *hints, // OUT: delete hints - HgfsHandle *file) // OUT: file handle -{ - Bool result = TRUE; - - /* Enforced by the dispatch function. */ - ASSERT(payloadSize >= sizeof *requestV2); - - *file = HGFS_INVALID_HANDLE; - *hints = requestV2->hints; - - /* - * If we've been asked to reuse a handle, we don't need to look at, let - * alone test the filename or its length. - */ - - if (requestV2->hints & HGFS_DELETE_HINT_USE_FILE_DESC) { - *file = requestV2->file; - *cpName = NULL; - *cpNameSize = 0; - } else { - result = HgfsUnpackFileName(&requestV2->fileName, - payloadSize - sizeof *requestV2, - cpName, - cpNameSize); - } - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackDeletePayloadV1 -- - * - * Unpack hgfs delete request V1 payload and initialize a corresponding - * file name to tell us which to delete. - * - * Since the structure of the get delete request packet is the same - * for Delete File or Directory of the protocol, code is identical for - * both operations. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackDeletePayloadV1(HgfsRequestDelete *requestV1, // IN: request payload - size_t payloadSize, // IN: payload size - char **cpName, // OUT: cpName - size_t *cpNameSize) // OUT: cpName size -{ - return HgfsUnpackFileName(&requestV1->fileName, - payloadSize - sizeof *requestV1, - cpName, - cpNameSize); -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackDeleteRequest -- - * - * Unpack hgfs delete request and initialize a corresponding - * HgfsHandle or file name to tell us which to delete. Hints - * holds flags to specify a handle or name for the file or - * directory to delete. - * - * Since the structure of the get delete request packet is the same - * for Delete File or Directory of the protocol, code is identical for - * both operations. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackDeleteRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: requested operation - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsDeleteHint *hints, // OUT: delete hints - HgfsHandle *file, // OUT: file handle - uint32 *caseFlags) // OUT: case-sensitivity flags -{ - void const *payload; - size_t payloadSize; - - ASSERT(packetIn); - ASSERT(cpName); - ASSERT(cpNameSize); - ASSERT(file); - ASSERT(hints); - ASSERT(caseFlags); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, op)) { - return FALSE; - } - - *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - *hints = 0; - *file = HGFS_INVALID_HANDLE; - - switch (*op) { - case HGFS_OP_DELETE_FILE_V3: - case HGFS_OP_DELETE_DIR_V3: { - HgfsRequestDeleteV3 *requestV3 = (HgfsRequestDeleteV3 *)payload; - - if (!HgfsUnpackDeletePayloadV3(requestV3, - payloadSize, - cpName, - cpNameSize, - hints, - file, - caseFlags)) { - return FALSE; - } - break; - } - case HGFS_OP_DELETE_FILE_V2: - case HGFS_OP_DELETE_DIR_V2: { - HgfsRequestDeleteV2 *requestV2 = (HgfsRequestDeleteV2 *)payload; - - if (!HgfsUnpackDeletePayloadV2(requestV2, - payloadSize, - cpName, - cpNameSize, - hints, - file)) { - return FALSE; - } - break; - } - case HGFS_OP_DELETE_FILE: - case HGFS_OP_DELETE_DIR: { - HgfsRequestDelete *requestV1 = (HgfsRequestDelete *)payload; - - if (!HgfsUnpackDeletePayloadV1(requestV1, - payloadSize, - cpName, - cpNameSize)) { - return FALSE; - } - break; - } - default: - NOT_REACHED(); - return FALSE; - } - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackDeleteReply -- - * - * Pack hgfs delete reply. - * Since the structure of the delete reply packet hasn't changed in - * version 2 of the protocol, HgfsReplyDeleteV2 is identical to - * HgfsReplyDelete. So use HgfsReplyDelete type to access packetIn to - * keep the code simple. - * - * Results: - * TRUE if valid op version reply filled, FALSE otherwise. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackDeleteReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: requested operation - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info -{ - Bool result = TRUE; - ASSERT(packetIn); - ASSERT(packetSize); - - *packetOut = NULL; - *packetSize = 0; - - /* No reply payload, just header. */ - switch (op) { - case HGFS_OP_DELETE_FILE_V3: - case HGFS_OP_DELETE_DIR_V3: { - HgfsReplyDeleteV3 *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - case HGFS_OP_DELETE_FILE_V2: - case HGFS_OP_DELETE_FILE: - case HGFS_OP_DELETE_DIR_V2: - case HGFS_OP_DELETE_DIR: { - HgfsReplyDelete *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - default: - LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); - result = FALSE; - NOT_REACHED(); - break; - } - - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackRenamePayloadV3 -- - * - * Unpack hgfs rename request V3 payload and initialize a corresponding - * HgfsHandles or file names to tell us old and new names/handles. Hints - * holds flags to specify a handle or name for the file or - * directory to rename. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackRenamePayloadV3(HgfsRequestRenameV3 *requestV3, // IN: request payload - size_t payloadSize, // IN: payload size - char **cpOldName, // OUT: rename src - size_t *cpOldNameLen, // OUT: rename src size - char **cpNewName, // OUT: rename dst - size_t *cpNewNameLen, // OUT: rename dst size - HgfsRenameHint *hints, // OUT: rename hints - HgfsHandle *srcFile, // OUT: src file handle - HgfsHandle *targetFile, // OUT: target file handle - uint32 *oldCaseFlags, // OUT: source case flags - uint32 *newCaseFlags) // OUT: dest. case flags -{ - size_t extra; - HgfsFileNameV3 *newName; - Bool useHandle; - - LOG(4, ("%s: HGFS_OP_RENAME_V3\n", __FUNCTION__)); - - if (payloadSize < sizeof *requestV3) { - return FALSE; - } - extra = payloadSize - sizeof *requestV3; - - *hints = requestV3->hints; - - /* - * Get the old and new filenames from the request. - * - * Getting the new filename is somewhat inconvenient, because we - * don't know where request->newName actually starts, thanks to the - * fact that request->oldName is of variable length. We get around - * this by using an HgfsFileName*, assigning it to the correct address - * just after request->oldName ends, and using that to access the - * new name. - */ - - /* - * If we've been asked to reuse a handle, we don't need to look at, let - * alone test the filename or its length. This applies to the source - * and the target. - */ - if (!HgfsUnpackFileNameV3(&requestV3->oldName, - extra, - &useHandle, - cpOldName, - cpOldNameLen, - srcFile, - oldCaseFlags)) { - return FALSE; - } - if (useHandle) { - *hints |= HGFS_RENAME_HINT_USE_SRCFILE_DESC; - newName = &requestV3->newName; - } else { - newName = (HgfsFileNameV3 *)(requestV3->oldName.name + 1 + *cpOldNameLen); - extra -= *cpOldNameLen; - } - if (!HgfsUnpackFileNameV3(newName, - extra, - &useHandle, - cpNewName, - cpNewNameLen, - targetFile, - newCaseFlags)) { - return FALSE; - } - if (useHandle) { - *hints |= HGFS_RENAME_HINT_USE_TARGETFILE_DESC; - } - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackRenamePayloadV2 -- - * - * Unpack hgfs rename request V2 payload and initialize a corresponding - * HgfsHandle or file name to tell us which to delete. Hints - * holds flags to specify a handle or name for the file or - * directory to rename. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackRenamePayloadV2(HgfsRequestRenameV2 *requestV2, // IN: request payload - size_t payloadSize, // IN: payload size - char **cpOldName, // OUT: rename src - size_t *cpOldNameLen, // OUT: rename src size - char **cpNewName, // OUT: rename dst - size_t *cpNewNameLen, // OUT: rename dst size - HgfsRenameHint *hints, // OUT: rename hints - HgfsHandle *srcFile, // OUT: src file handle - HgfsHandle *targetFile) // OUT: target file handle -{ - HgfsFileName *newName; - size_t extra; - - /* Enforced by the dispatch function. */ - if (payloadSize < sizeof *requestV2) { - return FALSE; - } - extra = payloadSize - sizeof *requestV2; - - *hints = requestV2->hints; - - /* - * If we've been asked to reuse a handle, we don't need to look at, let - * alone test the filename or its length. This applies to the source - * and the target. - */ - - if (*hints & HGFS_RENAME_HINT_USE_SRCFILE_DESC) { - *srcFile = requestV2->srcFile; - *cpOldName = NULL; - *cpOldNameLen = 0; - } else { - if (!HgfsUnpackFileName(&requestV2->oldName, - extra, - cpOldName, - cpOldNameLen)) { - return FALSE; - } - extra -= *cpOldNameLen; - } - - if (*hints & HGFS_RENAME_HINT_USE_TARGETFILE_DESC) { - *targetFile = requestV2->targetFile; - *cpNewName = NULL; - *cpNewNameLen = 0; - } else { - newName = (HgfsFileName *)((char *)(&requestV2->oldName + 1) - + *cpOldNameLen); - if (!HgfsUnpackFileName(newName, - extra, - cpNewName, - cpNewNameLen)) { - return FALSE; - } - } - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackRenamePayloadV1 -- - * - * Unpack hgfs rename request V1 payload and initialize a corresponding - * old and new file names. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackRenamePayloadV1(HgfsRequestRename *requestV1, // IN: request payload - size_t payloadSize, // IN: payload size - char **cpOldName, // OUT: rename src - size_t *cpOldNameLen, // OUT: rename src size - char **cpNewName, // OUT: rename dst - size_t *cpNewNameLen) // OUT: rename dst size -{ - HgfsFileName *newName; - uint32 extra; - - if (payloadSize < sizeof *requestV1) { - return FALSE; - } - - extra = payloadSize - sizeof *requestV1; - - if (!HgfsUnpackFileName(&requestV1->oldName, - extra, - cpOldName, - cpOldNameLen)) { - return FALSE; - } - - extra -= requestV1->oldName.length; - newName = (HgfsFileName *)((char *)(&requestV1->oldName + 1) - + requestV1->oldName.length); - - return HgfsUnpackFileName(newName, extra, cpNewName, cpNewNameLen); -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackRenameRequest -- - * - * Unpack hgfs rename request and initialize a corresponding - * HgfsHandle or file name to tell us which to rename. Hints - * holds flags to specify a handle or name for the file or - * directory to rename. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackRenameRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: requested operation - char **cpOldName, // OUT: rename src - size_t *cpOldNameLen, // OUT: rename src size - char **cpNewName, // OUT: rename dst - size_t *cpNewNameLen, // OUT: rename dst size - HgfsRenameHint *hints, // OUT: rename hints - HgfsHandle *srcFile, // OUT: src file handle - HgfsHandle *targetFile, // OUT: target file handle - uint32 *oldCaseFlags, // OUT: source case-sensitivity flags - uint32 *newCaseFlags) // OUT: dest. case-sensitivity flags -{ - void const *payload; - size_t payloadSize; - - ASSERT(packetIn); - ASSERT(cpOldName); - ASSERT(cpOldNameLen); - ASSERT(cpNewName); - ASSERT(cpNewNameLen); - ASSERT(srcFile); - ASSERT(targetFile); - ASSERT(hints); - ASSERT(oldCaseFlags); - ASSERT(newCaseFlags); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, op)) { - return FALSE; - } - - /* Default values for legacy requests. */ - *oldCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - *newCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - *hints = 0; - - switch (*op) { - case HGFS_OP_RENAME_V3: - { - HgfsRequestRenameV3 *requestV3 = (HgfsRequestRenameV3 *)payload; - - if (!HgfsUnpackRenamePayloadV3(requestV3, - payloadSize, - cpOldName, - cpOldNameLen, - cpNewName, - cpNewNameLen, - hints, - srcFile, - targetFile, - oldCaseFlags, - newCaseFlags)) { - return FALSE; - } - break; - } - case HGFS_OP_RENAME_V2: - { - HgfsRequestRenameV2 *requestV2 = (HgfsRequestRenameV2 *)payload; - - if (!HgfsUnpackRenamePayloadV2(requestV2, - payloadSize, - cpOldName, - cpOldNameLen, - cpNewName, - cpNewNameLen, - hints, - srcFile, - targetFile)) { - return FALSE; - } - break; - } - - case HGFS_OP_RENAME: - { - HgfsRequestRename *requestV1 = (HgfsRequestRename *)payload; - - if (!HgfsUnpackRenamePayloadV1(requestV1, - payloadSize, - cpOldName, - cpOldNameLen, - cpNewName, - cpNewNameLen)) { - return FALSE; - } - break; - } - - default: - return FALSE; - } - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackRenameReply -- - * - * Pack hgfs rename reply. - * Since the structure of the rename reply packet hasn't changed in - * version 2 of the protocol, HgfsReplyRenameV2 is identical to - * HgfsReplyRename. So use HgfsReplyRename type to access packetIn to - * keep the code simple. - * - * Results: - * TRUE if valid op and reply set, FALSE otherwise. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackRenameReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: requested operation - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info -{ - Bool result = TRUE; - ASSERT(packetIn); - ASSERT(packetSize); - - *packetOut = NULL; - *packetSize = 0; - - switch (op) { - case HGFS_OP_RENAME_V3: { - HgfsReplyRenameV3 *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - case HGFS_OP_RENAME_V2: - case HGFS_OP_RENAME: { - HgfsReplyRename *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - default: - LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); - result = FALSE; - NOT_REACHED(); - break; - } - - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackGetattrPayloadV3 -- - * - * Unpack hgfs get attr request V3 payload and initialize a corresponding - * HgfsHandle or file name to tell us which file to get attributes. Hints - * holds flags to specify a handle or name for the file or - * directory to get attributes. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackGetattrPayloadV3(HgfsRequestGetattrV3 *requestV3,// IN: request payload - size_t payloadSize, // IN: payload size - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsAttrHint *hints, // OUT: getattr hints - HgfsHandle *file, // OUT: file handle - uint32 *caseFlags) // OUT: case-sensitivity flags -{ - Bool result; - Bool useHandle; - - if (payloadSize < sizeof *requestV3) { - return FALSE; - } - - *hints = requestV3->hints; - - result = HgfsUnpackFileNameV3(&requestV3->fileName, - payloadSize - sizeof *requestV3, - &useHandle, - cpName, - cpNameSize, - file, - caseFlags); - if (useHandle) { - *hints |= HGFS_ATTR_HINT_USE_FILE_DESC; - } - - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackGetattrPayloadV2 -- - * - * Unpack hgfs Getattr request V2 payload and initialize a corresponding - * HgfsHandle or file name to tell us which to get attributes. Hints - * holds flags to specify a handle or name for the file or - * directory to get attributes. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackGetattrPayloadV2(HgfsRequestGetattrV2 *requestV2,// IN: request payload - size_t payloadSize, // IN: payload size - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsAttrHint *hints, // OUT: delete hints - HgfsHandle *file) // OUT: file handle -{ - Bool result = TRUE; - - if (payloadSize < sizeof *requestV2) { - return FALSE; - } - - - *file = HGFS_INVALID_HANDLE; - *hints = requestV2->hints; - - /* - * If we've been asked to reuse a handle, we don't need to look at, let - * alone test the filename or its length. - */ - - if (requestV2->hints & HGFS_ATTR_HINT_USE_FILE_DESC) { - *file = requestV2->file; - *cpName = NULL; - *cpNameSize = 0; - } else { - result = HgfsUnpackFileName(&requestV2->fileName, - payloadSize - sizeof *requestV2, - cpName, - cpNameSize); - } - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackGetattrPayloadV1 -- - * - * Unpack hgfs getattr request V1 payload and initialize a corresponding - * file name to tell us which to get attributes. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackGetattrPayloadV1(HgfsRequestGetattr *requestV1, // IN: request payload - size_t payloadSize, // IN: payload size - char **cpName, // OUT: cpName - size_t *cpNameSize) // OUT: cpName size -{ - return HgfsUnpackFileName(&requestV1->fileName, - payloadSize - sizeof *requestV1, - cpName, - cpNameSize); -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackAttrV2 -- - * - * Packs attr version 2 reply structure. - * - * Results: - * None. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -HgfsPackAttrV2(HgfsFileAttrInfo *attr, // IN: attr stucture - HgfsAttrV2 *attr2) // OUT: attr in payload -{ - attr2->mask = attr->mask; - attr2->type = attr->type; - attr2->size = attr->size; - attr2->creationTime = attr->creationTime; - attr2->accessTime = attr->accessTime; - attr2->writeTime = attr->writeTime; - attr2->attrChangeTime = attr->attrChangeTime; - attr2->specialPerms = attr->specialPerms; - attr2->ownerPerms = attr->ownerPerms; - attr2->groupPerms = attr->groupPerms; - attr2->otherPerms = attr->otherPerms; - attr2->flags = attr->flags; - attr2->allocationSize = attr->allocationSize; - attr2->userId = attr->userId; - attr2->groupId = attr->groupId; - attr2->hostFileId = attr->hostFileId; - attr2->volumeId = attr->volumeId; - attr2->effectivePerms = attr->effectivePerms; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackAttrV2 -- - * - * Unpacks attr version 2 reply structure. - * - * Results: - * None. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -HgfsUnpackAttrV2(HgfsAttrV2 *attr2, // IN: attr in payload - HgfsFileAttrInfo *attr) // OUT: attr stucture -{ - attr->mask = attr2->mask; - attr->type = attr2->type; - attr->size = attr2->size; - attr->creationTime = attr2->creationTime; - attr->accessTime = attr2->accessTime; - attr->writeTime = attr2->writeTime; - attr->attrChangeTime = attr2->attrChangeTime; - attr->specialPerms = attr2->specialPerms; - attr->ownerPerms = attr2->ownerPerms; - attr->groupPerms = attr2->groupPerms; - attr->otherPerms = attr2->otherPerms; - attr->flags = attr2->flags; - attr->allocationSize = attr2->allocationSize; - attr->userId = attr2->userId; - attr->groupId = attr2->groupId; - attr->hostFileId = attr2->hostFileId; - attr->volumeId = attr2->volumeId; - attr->effectivePerms = attr2->effectivePerms; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsInitFileAttr -- - * - * Initializes HgfsFileAttrInfo structure. - * - * Results: - * None. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -HgfsInitFileAttr(HgfsOp op, // IN: request type - HgfsFileAttrInfo *attr) // OUT: attr stucture -{ - /* Initialize all fields with 0. */ - memset(attr, 0, sizeof *attr); - - /* Explicitly initialize fields which need it. */ - attr->requestType = op; - attr->mask = HGFS_ATTR_VALID_NONE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackGetattrReplyPayloadV3 -- - * - * Packs Getattr V3 reply payload. - * - * Results: - * None. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -HgfsPackGetattrReplyPayloadV3(HgfsFileAttrInfo *attr, // IN: attr stucture - const char *utf8TargetName, // IN: optional target name - uint32 utf8TargetNameLen, // IN: file name length - HgfsReplyGetattrV3 *reply) // OUT: payload -{ - LOG(4, ("%s: attr type: %u\n", __FUNCTION__, reply->attr.type)); - - HgfsPackAttrV2(attr, &reply->attr); - reply->reserved = 0; - - if (utf8TargetName) { - memcpy(reply->symlinkTarget.name, utf8TargetName, utf8TargetNameLen); - CPNameLite_ConvertTo(reply->symlinkTarget.name, utf8TargetNameLen, - DIRSEPC); - } else { - ASSERT(utf8TargetNameLen == 0); - } - reply->symlinkTarget.length = utf8TargetNameLen; - reply->symlinkTarget.name[utf8TargetNameLen] = '\0'; - reply->symlinkTarget.flags = 0; - reply->symlinkTarget.fid = 0; - reply->symlinkTarget.caseType = HGFS_FILE_NAME_DEFAULT_CASE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackGetattrReplyPayloadV2 -- - * - * Packs rename reply payload V2 requests. - * - * Results: - * None. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -HgfsPackGetattrReplyPayloadV2(HgfsHandle id, // IN: id of the request - HgfsInternalStatus status, // IN: error code - HgfsFileAttrInfo *attr, // IN: attr stucture - const char *utf8TargetName, // IN: optional target name - uint32 utf8TargetNameLen, // IN: file name length - HgfsReplyGetattrV2 *reply) // OUT: payload -{ - reply->header.status = HgfsConvertFromInternalStatus(status); - reply->header.id = id; - - HgfsPackAttrV2(attr, &reply->attr); - - if (utf8TargetName) { - memcpy(reply->symlinkTarget.name, utf8TargetName, utf8TargetNameLen); - CPNameLite_ConvertTo(reply->symlinkTarget.name, utf8TargetNameLen, - DIRSEPC); - } else { - ASSERT(utf8TargetNameLen == 0); - } - reply->symlinkTarget.length = utf8TargetNameLen; - reply->symlinkTarget.name[utf8TargetNameLen] = '\0'; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackGetattrReplyPayloadV1 -- - * - * Packs rename reply payload for V1 requests. - * - * Results: - * None. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -HgfsPackGetattrReplyPayloadV1(HgfsHandle id, // IN: id of the request - HgfsInternalStatus status, // IN: error code - HgfsFileAttrInfo *attr, // IN: attr stucture - HgfsReplyGetattr *reply) // OUT: reply info -{ - reply->header.status = HgfsConvertFromInternalStatus(status); - reply->header.id = id; - - /* In GetattrV1, symlinks are treated as regular files. */ - if (attr->type == HGFS_FILE_TYPE_SYMLINK) { - reply->attr.type = HGFS_FILE_TYPE_REGULAR; - } else { - reply->attr.type = attr->type; - } - - reply->attr.size = attr->size; - reply->attr.creationTime = attr->creationTime; - reply->attr.accessTime = attr->accessTime; - reply->attr.writeTime = attr->writeTime; - reply->attr.attrChangeTime = attr->attrChangeTime; - reply->attr.permissions = attr->ownerPerms; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsValidateReplySize -- - * - * Verify if the size of a reply does not exceed maximum supported size. - * - * Results: - * TRUE if the packet size is acceptable, FALSE otherwise. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsValidateReplySize(char const *packetIn, - HgfsOp op, - size_t packetSize) -{ - HgfsRequest *request = (HgfsRequest *)packetIn; - /* - * XXX: - * At the moment assume that V4 protocol does not impose any restrictions - * on the packet size. - * May need to review later. - */ - if (HGFS_V4_LEGACY_OPCODE == request->op) { - return TRUE; - } - - if (HGFS_OP_READ_V3 == op) { - return packetSize <= HGFS_LARGE_PACKET_MAX; - } - - return packetSize <= HGFS_PACKET_MAX; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackGetattrRequest -- - * - * Unpack hgfs getattr request and initialize a corresponding - * HgfsFileAttrInfo structure that is used to pass around getattr request - * information. - * - * Since the structure of the get attributes request packet hasn't changed - * in version 2 of the protocol, HgfsRequestGetattrV2 is identical to - * HgfsRequestGetattr. So use HgfsRequestGetattr type to access packetIn to - * keep the code simple. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackGetattrRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsFileAttrInfo *attrInfo, // IN/OUT: getattr info - HgfsAttrHint *hints, // OUT: getattr hints - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsHandle *file, // OUT: file handle - uint32 *caseType) // OUT: case-sensitivity flags -{ - void const *payload; - size_t payloadSize; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(attrInfo); - ASSERT(cpName); - ASSERT(cpNameSize); - ASSERT(file); - ASSERT(caseType); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, &op)) { - return FALSE; - } - - HgfsInitFileAttr(op, attrInfo); - - /* Default values for legacy requests. */ - *caseType = HGFS_FILE_NAME_DEFAULT_CASE; - *hints = 0; - *file = HGFS_INVALID_HANDLE; - - switch (op) { - case HGFS_OP_GETATTR_V3: { - HgfsRequestGetattrV3 *requestV3 = (HgfsRequestGetattrV3 *)payload; - - if (!HgfsUnpackGetattrPayloadV3(requestV3, - payloadSize, - cpName, - cpNameSize, - hints, - file, - caseType)) { - return FALSE; - } - LOG(4, ("%s: HGFS_OP_GETATTR_V3: %u\n", __FUNCTION__, *caseType)); - break; - } - - case HGFS_OP_GETATTR_V2: { - HgfsRequestGetattrV2 *requestV2 = (HgfsRequestGetattrV2 *)payload; - - if (!HgfsUnpackGetattrPayloadV2(requestV2, - payloadSize, - cpName, - cpNameSize, - hints, - file)) { - return FALSE; - } - break; - } - - case HGFS_OP_GETATTR: { - HgfsRequestGetattr *requestV1 = (HgfsRequestGetattr *)payload; - - if (!HgfsUnpackGetattrPayloadV1(requestV1,payloadSize, cpName, cpNameSize)) { - return FALSE; - } - break; - } - - default: - return FALSE; - } - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackGetattrReply -- - * - * Pack hgfs getattr reply to the HgfsReplyGetattr structure. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackGetattrReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsFileAttrInfo *attr, // IN: attr stucture - const char *utf8TargetName, // IN: optional target name - uint32 utf8TargetNameLen, // IN: file name length - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info -{ - Bool result; - ASSERT(packetIn); - ASSERT(attr); - - *packetOut = NULL; - *packetSize = 0; - - switch (attr->requestType) { - case HGFS_OP_GETATTR_V3: { - HgfsReplyGetattrV3 *reply; - uint32 payloadSize = sizeof *reply + utf8TargetNameLen; - - result = HgfsAllocInitReply(packet, packetIn, payloadSize, status, packetOut, - (void **)&reply, packetSize, session); - if (result == FALSE) { - goto error; - } - - if (!HgfsValidateReplySize(packetIn, attr->requestType, *packetSize)) { - free(reply); - goto error; - } - HgfsPackGetattrReplyPayloadV3(attr, utf8TargetName, utf8TargetNameLen, reply); - break; - } - - case HGFS_OP_GETATTR_V2: { - HgfsReplyGetattrV2 *reply; - HgfsRequest *request = (HgfsRequest *)packetIn; - uint32 payloadSize = sizeof *reply + utf8TargetNameLen; - - result = HgfsAllocInitReply(packet, packetIn, payloadSize, status, packetOut, - (void **)&reply, packetSize, session); - if (result == FALSE) { - goto error; - } - - if (!HgfsValidateReplySize(packetIn, attr->requestType, *packetSize)) { - free(reply); - goto error; - } - HgfsPackGetattrReplyPayloadV2(request->id, - status, - attr, - utf8TargetName, - utf8TargetNameLen, - reply); - break; - } - - case HGFS_OP_GETATTR: { - HgfsReplyGetattr *reply; - HgfsRequest *request = (HgfsRequest *)packetIn; + } else { + LOG(4, ("%s: shared folder does not exist.\n", __FUNCTION__)); - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - if (result == FALSE) { - goto error; + if (HGFS_NAME_STATUS_DOES_NOT_EXIST == nameStatus) { + status = HGFS_ERROR_ACCESS_DENIED; + } else { + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } } - HgfsPackGetattrReplyPayloadV1(request->id, status, attr, reply); - break; - } - - default: - LOG(4, ("%s: Invalid GetAttr op.\n", __FUNCTION__)); - NOT_REACHED(); - result = FALSE; + } else { + status = HGFS_ERROR_PROTOCOL; } -error: - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackSearchReadReplyPayloadV3 -- - * - * Packs SearchRead V3 reply payload. - * - * Results: - * None. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -HgfsPackSearchReadReplyPayloadV3(HgfsFileAttrInfo *attr, // IN: attr stucture - const char *utf8Name, // IN: file name - uint32 utf8NameLen, // IN: file name length - HgfsReplySearchReadV3 *reply) // OUT: payload -{ - HgfsDirEntry *dirent = (HgfsDirEntry *)reply->payload; - - reply->count = 1; - reply->reserved = 0; - - dirent->fileName.length = (uint32)utf8NameLen; - dirent->fileName.flags = 0; - dirent->fileName.fid = 0; - dirent->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE; - dirent->nextEntry = 0; - - if (utf8NameLen != 0) { - memcpy(dirent->fileName.name, utf8Name, utf8NameLen); - dirent->fileName.name[utf8NameLen] = 0; - - HgfsPackAttrV2(attr, &dirent->attr); - } + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsPackSearchReadReplyPayloadV2 -- - * - * Packs SearchRead V2 reply payload. - * - * Results: - * None. - * - * Side effects: - * None + * HgfsServerDeleteFile -- * - *----------------------------------------------------------------------------- - */ - -void -HgfsPackSearchReadReplyPayloadV2(HgfsFileAttrInfo *attr, // IN: attr stucture - const char *utf8Name, // IN: file name - uint32 utf8NameLen, // IN: file name length - HgfsReplySearchReadV2 *reply) // OUT: payload -{ - reply->fileName.length = (uint32)utf8NameLen; - - if (utf8NameLen != 0) { - memcpy(reply->fileName.name, utf8Name, utf8NameLen); - reply->fileName.name[utf8NameLen] = 0; - HgfsPackAttrV2(attr, &reply->attr); - } -} - - -/* - *----------------------------------------------------------------------------- + * Handle a Delete File request. * - * HgfsPackSearchReadReplyPayloadV1 -- + * Simply converts to the local filename, calls DeleteFile on the + * file or calls the Windows native API Delete, and returns an + * appropriate response to the driver. * - * Packs SearchRead V1 reply payload. + * Enforcing read-only shares restrictions * * Results: * None. @@ -7305,400 +5649,91 @@ HgfsPackSearchReadReplyPayloadV2(HgfsFileAttrInfo *attr, // IN: attr stuct *----------------------------------------------------------------------------- */ -void -HgfsPackSearchReadReplyPayloadV1(HgfsFileAttrInfo *attr, // IN: attr stucture - const char *utf8Name, // IN: file name - uint32 utf8NameLen, // IN: file name length - HgfsReplySearchRead *reply) // OUT: payload +static void +HgfsServerDeleteFile(HgfsInputParam *input) // IN: Input params { - reply->fileName.length = (uint32)utf8NameLen; + char *cpName; + size_t cpNameSize; + HgfsServerLock serverLock = HGFS_LOCK_NONE; + fileDesc fileDesc; + HgfsHandle file; + HgfsDeleteHint hints = 0; + HgfsInternalStatus status; + HgfsNameStatus nameStatus; + uint32 caseFlags; + size_t replyPayloadSize = 0; + HgfsShareInfo shareInfo; - if (utf8NameLen != 0) { - memcpy(reply->fileName.name, utf8Name, utf8NameLen); - reply->fileName.name[utf8NameLen] = 0; + HGFS_ASSERT_INPUT(input); - /* In SearchReadV1, symlinks are treated as regular files. */ - if (attr->type == HGFS_FILE_TYPE_SYMLINK) { - reply->attr.type = HGFS_FILE_TYPE_REGULAR; + if (HgfsUnpackDeleteRequest(input->payload, input->payloadSize, input->op, &cpName, + &cpNameSize, &hints, &file, &caseFlags)) { + if (hints & HGFS_DELETE_HINT_USE_FILE_DESC) { + status = HgfsPlatformDeleteFileByHandle(file, input->session); } else { - reply->attr.type = attr->type; - } - reply->attr.size = attr->size; - reply->attr.creationTime = attr->creationTime; - reply->attr.accessTime = attr->accessTime; - reply->attr.writeTime = attr->writeTime; - reply->attr.attrChangeTime = attr->attrChangeTime; - reply->attr.permissions = attr->ownerPerms; - } -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackSearchReadRequest -- - * - * Unpack hgfs search read request and initialize a corresponding - * HgfsFileAttrInfo structure that is used to pass around attribute - * information. - * - * Since the structure of the search read request packet hasn't changed in - * version 2 of the protocol, HgfsRequestSearchReadV2 is identical to - * HgfsRequestSearchRead. So use HgfsRequestSearchRead type to access - * packetIn to keep the code simple. - * - * Results: - * Always TRUE. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackSearchReadRequest(const char *packetIn, // IN: request packet - size_t packetSize, // IN: packet size - HgfsFileAttrInfo *attr, // OUT: unpacked attr struct - HgfsHandle *hgfsSearchHandle, // OUT: hgfs search handle - uint32 *offset) // OUT: entry offset -{ - void const *payload; - size_t payloadSize; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(attr); - ASSERT(hgfsSearchHandle); - ASSERT(offset); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, &op)) { - return FALSE; - } - - HgfsInitFileAttr(op, attr); - - - if (op == HGFS_OP_SEARCH_READ_V3) { - HgfsRequestSearchReadV3 *request = (HgfsRequestSearchReadV3 *)payload; - - /* Enforced by the dispatch function. */ - ASSERT(payloadSize >= sizeof *request); - - *hgfsSearchHandle = request->search; - *offset = request->offset; - - LOG(4, ("%s: HGFS_OP_SEARCH_READ_V3\n", __FUNCTION__)); - } else { - HgfsRequestSearchRead *request = (HgfsRequestSearchRead *)payload; - - /* Enforced by the dispatch function. */ - ASSERT(payloadSize >= sizeof *request); - - *hgfsSearchHandle = request->search; - *offset = request->offset; - } - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsPackSearchReadReply -- - * - * Pack hgfs search read reply to the HgfsReplySearchRead{V2} structure. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsPackSearchReadReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - const char *utf8Name, // IN: file name - size_t utf8NameLen, // IN: file name length - HgfsFileAttrInfo *attr, // IN: file attr struct - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info -{ - Bool result = TRUE; - ASSERT(packetIn); - - *packetOut = NULL; - *packetSize = 0; - - switch (attr->requestType) { - case HGFS_OP_SEARCH_READ_V3: { - HgfsReplySearchReadV3 *reply; - uint32 payloadSize = sizeof *reply + utf8NameLen + sizeof(HgfsDirEntry); - - if (!HgfsAllocInitReply(packet, packetIn, payloadSize, status, packetOut, - (void **)&reply, packetSize, session)) { - result = FALSE; - goto error; - } - - if (!HgfsValidateReplySize(packetIn, attr->requestType, *packetSize)) { - free(reply); - result = FALSE; - goto error; - } - HgfsPackSearchReadReplyPayloadV3(attr, utf8Name, utf8NameLen, reply); - break; - } - - case HGFS_OP_SEARCH_READ_V2: { - HgfsReplySearchReadV2 *reply; - uint32 payloadSize = sizeof *reply + utf8NameLen; - - if (!HgfsAllocInitReply(packet, packetIn, payloadSize, status, packetOut, - (void **)&reply, packetSize, session)) { - result = FALSE; - goto error; - } - - if (!HgfsValidateReplySize(packetIn, attr->requestType, *packetSize)) { - free(reply); - result = FALSE; - goto error; - } - HgfsPackSearchReadReplyPayloadV2(attr, - utf8Name, - utf8NameLen, - reply); - break; - } + char *utf8Name = NULL; + size_t utf8NameLen; - case HGFS_OP_SEARCH_READ: { - HgfsReplySearchRead *reply; - uint32 payloadSize = sizeof *reply + utf8NameLen; - - if (!HgfsAllocInitReply(packet, packetIn, payloadSize, status, packetOut, - (void **)&reply, packetSize, session)) { - result = FALSE; - goto error; + nameStatus = HgfsServerGetShareInfo(cpName, cpNameSize, caseFlags, &shareInfo, + &utf8Name, &utf8NameLen); + if (nameStatus == HGFS_NAME_STATUS_COMPLETE) { + /* + * Deleting a file needs both read and write permssions. + * However the error code must be different depending on the existence + * of the file with the same name. + */ + if (!shareInfo.writePermissions || !shareInfo.readPermissions) { + status = HgfsPlatformFileExists(utf8Name); + if (HGFS_ERROR_SUCCESS == status) { + status = HGFS_ERROR_ACCESS_DENIED; + } + LOG(4, ("HgfsServerDeleteFile: failed access check, error %d\n", status)); + } else if (HgfsFileHasServerLock(utf8Name, input->session, &serverLock, + &fileDesc)) { + /* + * XXX: If the file has an oplock, the client should have broken it on + * its own by now. Sorry! + */ + LOG (4, ("%s: File has an outstanding oplock. Client should " + "remove this oplock and try again.\n", __FUNCTION__)); + status = HGFS_ERROR_PATH_BUSY; + } else { + LOG(4, ("%s: deleting \"%s\"\n", __FUNCTION__, utf8Name)); + status = HgfsPlatformDeleteFileByName(utf8Name); + } + free(utf8Name); + } else { + LOG(4, ("%s: Shared folder does not exist.\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } } - - if (!HgfsValidateReplySize(packetIn, attr->requestType, *packetSize)) { - free(reply); - result = FALSE; - goto error; + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackDeleteReply(input->packet, input->metaPacket, input->op, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } } - HgfsPackSearchReadReplyPayloadV1(attr, - utf8Name, - utf8NameLen, - reply); - break; - } - - default: { - LOG(4, ("%s: Invalid SearchRead Op.", __FUNCTION__)); - NOT_REACHED(); - result = FALSE; - } - } - -error: - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackSetattrPayloadV3 -- - * - * Unpack hgfs set attr request V3 payload and initialize a corresponding - * HgfsHandle or file name to tell us which file to set attributes. Hints - * holds flags to specify a handle or name for the file or - * directory to set attributes. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackSetattrPayloadV3(HgfsRequestSetattrV3 *requestV3,// IN: request payload - size_t payloadSize, // IN: payload size - HgfsFileAttrInfo *attr, // OUT: setattr info - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsAttrHint *hints, // OUT: getattr hints - HgfsHandle *file, // OUT: file handle - uint32 *caseFlags) // OUT: case-sensitivity flags -{ - Bool result; - Bool useHandle; - - if (payloadSize < sizeof *requestV3) { - return FALSE; - } - - *hints = requestV3->hints; - - HgfsUnpackAttrV2(&requestV3->attr, attr); - - result = HgfsUnpackFileNameV3(&requestV3->fileName, - payloadSize - sizeof *requestV3, - &useHandle, - cpName, - cpNameSize, - file, - caseFlags); - if (useHandle) { - *hints |= HGFS_ATTR_HINT_USE_FILE_DESC; - } - - LOG(4, ("%s: unpacking HGFS_OP_SETATTR_V3, %u\n", __FUNCTION__, - *caseFlags)); - return result; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackSetattrPayloadV2 -- - * - * Unpack hgfs Setattr request V2 payload and initialize a corresponding - * HgfsHandle or file name to tell us which to set attributes. Hints - * holds flags to specify a handle or name for the file or - * directory to set attributes. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackSetattrPayloadV2(HgfsRequestSetattrV2 *requestV2,// IN: request payload - size_t payloadSize, // IN: payload size - HgfsFileAttrInfo *attr, // OUT: setattr info - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsAttrHint *hints, // OUT: delete hints - HgfsHandle *file) // OUT: file handle -{ - Bool result = TRUE; - - /* Enforced by the dispatch function. */ - if (payloadSize < sizeof *requestV2) { - return FALSE; - } - - LOG(4, ("%s: unpacking HGFS_OP_SETATTR_V2\n", __FUNCTION__)); - - *file = HGFS_INVALID_HANDLE; - *hints = requestV2->hints; - - HgfsUnpackAttrV2(&requestV2->attr, attr); - - if (requestV2->hints & HGFS_ATTR_HINT_USE_FILE_DESC) { - *file = requestV2->file; - *cpName = NULL; - *cpNameSize = 0; } else { - result = HgfsUnpackFileName(&requestV2->fileName, - payloadSize - sizeof *requestV2, - cpName, - cpNameSize); + status = HGFS_ERROR_PROTOCOL; } - return result; -} - -/* - *----------------------------------------------------------------------------- - * - * HgfsUnpackSetattrPayloadV1 -- - * - * Unpack hgfs setattr request V1 payload and initialize a corresponding - * file name to tell us which to set attributes. - * - * Results: - * TRUE on success. - * FALSE on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -HgfsUnpackSetattrPayloadV1(HgfsRequestSetattr *requestV1, // IN: request payload - size_t payloadSize, // IN: payload size - HgfsFileAttrInfo *attr, // OUT: setattr info - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsAttrHint *hints) // OUT: setattr hints -{ - LOG(4, ("%s: unpacking HGFS_OP_SETATTR\n", __FUNCTION__)); - - attr->mask = 0; - attr->mask |= requestV1->update & HGFS_ATTR_SIZE ? HGFS_ATTR_VALID_SIZE : 0; - attr->mask |= requestV1->update & HGFS_ATTR_CREATE_TIME ? - HGFS_ATTR_VALID_CREATE_TIME : 0; - attr->mask |= requestV1->update & HGFS_ATTR_ACCESS_TIME ? - HGFS_ATTR_VALID_ACCESS_TIME : 0; - attr->mask |= requestV1->update & HGFS_ATTR_WRITE_TIME ? - HGFS_ATTR_VALID_WRITE_TIME : 0; - attr->mask |= requestV1->update & HGFS_ATTR_CHANGE_TIME ? - HGFS_ATTR_VALID_CHANGE_TIME : 0; - attr->mask |= requestV1->update & HGFS_ATTR_PERMISSIONS ? - HGFS_ATTR_VALID_OWNER_PERMS : 0; - *hints |= requestV1->update & HGFS_ATTR_ACCESS_TIME_SET ? - HGFS_ATTR_HINT_SET_ACCESS_TIME : 0; - *hints |= requestV1->update & HGFS_ATTR_WRITE_TIME_SET ? - HGFS_ATTR_HINT_SET_WRITE_TIME : 0; - - attr->type = requestV1->attr.type; - attr->size = requestV1->attr.size; - attr->creationTime = requestV1->attr.creationTime; - attr->accessTime = requestV1->attr.accessTime; - attr->writeTime = requestV1->attr.writeTime; - attr->attrChangeTime = requestV1->attr.attrChangeTime; - attr->ownerPerms = requestV1->attr.permissions; - return HgfsUnpackFileName(&requestV1->fileName, - payloadSize - sizeof *requestV1, - cpName, - cpNameSize); + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsUnpackSetattrRequest -- + * HgfsServerDeleteDir -- * - * Unpack hgfs setattr request and initialize a corresponding - * HgfsFileAttrInfo structure that is used to pass around setattr request - * information. + * Handle a Delete Dir request. + * + * Simply converts to the local filename, calls RemoveDirectory on the + * directory or Windows native API delete if we have a valid handle, + * and returns an appropriate response to the driver. * * Results: - * TRUE on success. - * FALSE on failure. + * None. * * Side effects: * None @@ -7706,103 +5741,103 @@ HgfsUnpackSetattrPayloadV1(HgfsRequestSetattr *requestV1, // IN: request payloa *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackSetattrRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsFileAttrInfo *attr, // OUT: setattr info - HgfsAttrHint *hints, // OUT: setattr hints - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsHandle *file, // OUT: server file ID - uint32 *caseType) // OUT: case-sensitivity flags +static void +HgfsServerDeleteDir(HgfsInputParam *input) // IN: Input params { - void const *payload; - size_t payloadSize; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(attr); - ASSERT(cpName); - ASSERT(cpNameSize); - ASSERT(file); - ASSERT(caseType); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, &op)) { - return FALSE; - } - - attr->requestType = op; - - /* Default values for legacy requests. */ - *caseType = HGFS_FILE_NAME_DEFAULT_CASE; - *hints = 0; - *file = HGFS_INVALID_HANDLE; - - switch (op) { - case HGFS_OP_SETATTR_V3: - { - HgfsRequestSetattrV3 *requestV3 = (HgfsRequestSetattrV3 *)payload; - if (!HgfsUnpackSetattrPayloadV3(requestV3, - payloadSize, - attr, - cpName, - cpNameSize, - hints, - file, - caseType)) { - return FALSE; + char *cpName; + size_t cpNameSize; + HgfsInternalStatus status; + HgfsNameStatus nameStatus; + HgfsHandle file; + HgfsDeleteHint hints = 0; + fileDesc fileDesc; + Bool sharedFolderOpen = FALSE; + uint32 caseFlags; + size_t replyPayloadSize = 0; + HgfsShareInfo shareInfo; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackDeleteRequest(input->payload, input->payloadSize, input->op, &cpName, + &cpNameSize, &hints, &file, &caseFlags)) { + if (hints & HGFS_DELETE_HINT_USE_FILE_DESC) { + + status = HgfsPlatformGetFd(file, input->session, FALSE, &fileDesc); + + if (HGFS_ERROR_SUCCESS == status) { + if (HgfsHandleIsSharedFolderOpen(file, input->session, &sharedFolderOpen) && + sharedFolderOpen) { + LOG(4, ("%s: Cannot delete shared folder\n", __FUNCTION__)); + status = HGFS_ERROR_ACCESS_DENIED; + } else { + status = HgfsPlatformDeleteDirByHandle(file, input->session); + if (HGFS_ERROR_SUCCESS != status) { + LOG(4, ("%s: error deleting directory %d: %d\n", __FUNCTION__, + file, status)); + } + } + } else { + LOG(4, ("%s: could not map cached handle %u, error %u\n", + __FUNCTION__, file, status)); } - break; - } - - case HGFS_OP_SETATTR_V2: - { - HgfsRequestSetattrV2 *requestV2 = (HgfsRequestSetattrV2 *)payload; - if (!HgfsUnpackSetattrPayloadV2(requestV2, - payloadSize, - attr, - cpName, - cpNameSize, - hints, - file)) { - return FALSE; + } else { + char *utf8Name = NULL; + size_t utf8NameLen; + + nameStatus = HgfsServerGetShareInfo(cpName, cpNameSize, caseFlags, &shareInfo, + &utf8Name, &utf8NameLen); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + ASSERT(utf8Name); + /* Guest OS is not allowed to delete shared folder. */ + if (HgfsServerIsSharedFolderOnly(cpName, cpNameSize)){ + LOG(4, ("%s: Cannot delete shared folder\n", __FUNCTION__)); + status = HGFS_ERROR_ACCESS_DENIED; + } else if (!shareInfo.writePermissions || !shareInfo.readPermissions) { + /* + * Deleting a directory requires both read and write permissions. + * However the error code must be different depending on the existence + * of the file with the same name. + */ + status = HgfsPlatformFileExists(utf8Name); + if (HGFS_ERROR_SUCCESS == status) { + status = HGFS_ERROR_ACCESS_DENIED; + } + LOG(4, ("HgfsServerDeleteDir: failed access check, error %d\n", status)); + } else { + LOG(4, ("%s: removing \"%s\"\n", __FUNCTION__, utf8Name)); + status = HgfsPlatformDeleteDirByName(utf8Name); + } + free(utf8Name); + } else { + LOG(4, ("%s: access check failed\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); } - break; } - case HGFS_OP_SETATTR: - { - HgfsRequestSetattr *requestV1 = (HgfsRequestSetattr *)payload; - if (!HgfsUnpackSetattrPayloadV1(requestV1, - payloadSize, - attr, - cpName, - cpNameSize, - hints)) { - return FALSE; + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackDeleteReply(input->packet, input->metaPacket, input->op, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; } - break; } - default: - return FALSE; + } else { + status = HGFS_ERROR_PROTOCOL; } - return TRUE; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsPackSetattrReply -- + * HgfsServerServerLockChange -- * - * Pack hgfs setattr reply. - * Since the structure of the set attributes reply packet hasn't changed in - * version 2 of the protocol, HgfsReplySetattrV2 is identical to - * HgfsReplySetattr. So use HgfsReplySetattr type to access packetIn to - * keep the code simple. + * Called by the client when it wants to either acquire an oplock on a file + * that was previously opened, or when it wants to release/downgrade an + * oplock on a file that was previously oplocked. * * Results: - * TRUE if valid op and reply set, FALSE otherwise. + * None. * * Side effects: * None @@ -7810,62 +5845,25 @@ HgfsUnpackSetattrRequest(char const *packetIn, // IN: request packet *----------------------------------------------------------------------------- */ -Bool -HgfsPackSetattrReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info +static void +HgfsServerServerLockChange(HgfsInputParam *input) // IN: Input params { - Bool result = TRUE; - - ASSERT(packetIn); - - *packetOut = NULL; - *packetSize = 0; - - switch (op) { - case HGFS_OP_SETATTR_V3: { - HgfsReplySetattrV3 *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - case HGFS_OP_SETATTR_V2: - case HGFS_OP_SETATTR: { - HgfsReplySetattr *reply; - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - default: - result = FALSE; - LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); - NOT_REACHED(); - break; - } - ASSERT(result); + HGFS_ASSERT_INPUT(input); - return result; + HgfsServerCompleteRequest(HGFS_ERROR_NOT_SUPPORTED, 0, input); } /* *----------------------------------------------------------------------------- * - * HgfsUnpackCreateDirPayloadV3 -- + * HgfsServerWriteWin32Stream -- * - * Unpack hgfs create directory request V3 payload and initialize a corresponding - * file name to tell us which directory to create. + * Handle a write request in the WIN32_STREAM_ID format. * * Results: - * TRUE on success. - * FALSE on failure. + * ERROR_SUCCESS or an appropriate Win32 error code. * * Side effects: * None @@ -7873,57 +5871,47 @@ HgfsPackSetattrReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackCreateDirPayloadV3(HgfsRequestCreateDirV3 *requestV3, // IN: request payload - size_t payloadSize, // IN: payload size - HgfsCreateDirInfo *info) // IN/OUT: info struct +static void +HgfsServerWriteWin32Stream(HgfsInputParam *input) // IN: Input params { - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ - - LOG(4, ("%s: HGFS_OP_CREATE_DIR_V3\n", __FUNCTION__)); - ASSERT(payloadSize >= sizeof *requestV3); - if (requestV3->fileName.length > payloadSize - sizeof *requestV3) { - /* The input packet is smaller than the request. */ - return FALSE; - } - if (!(requestV3->mask & HGFS_CREATE_DIR_VALID_FILE_NAME)) { - /* We do not support requests without a valid file name. */ - return FALSE; + uint32 actualSize; + HgfsInternalStatus status; + HgfsHandle file; + char *dataToWrite; + Bool doSecurity; + size_t replyPayloadSize = 0; + size_t requiredSize; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackWriteWin32StreamRequest(input->payload, input->payloadSize, input->op, &file, + &dataToWrite, &requiredSize, &doSecurity)) { + status = HgfsPlatformWriteWin32Stream(file, dataToWrite, (uint32)requiredSize, + doSecurity, &actualSize, input->session); + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackWriteWin32StreamReply(input->packet, input->metaPacket, input->op, + actualSize, &replyPayloadSize, + input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } else { + status = HGFS_ERROR_PROTOCOL; } - /* - * Copy all the fields into our carrier struct. Some will probably be - * garbage, but it's simpler to copy everything now and check the - * valid bits before reading later. - */ - - info->mask = requestV3->mask; - info->cpName = requestV3->fileName.name; - info->cpNameSize = requestV3->fileName.length; - info->caseFlags = requestV3->fileName.caseType; - info->specialPerms = requestV3->specialPerms; - info->fileAttr = requestV3->fileAttr; - info->ownerPerms = requestV3->ownerPerms; - info->groupPerms = requestV3->groupPerms; - info->otherPerms = requestV3->otherPerms; - return TRUE; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsUnpackCreateDirPayloadV2 -- + * HgfsServerGetattr -- * - * Unpack hgfs create directory request V2 payload and initialize a corresponding - * file name to tell us which directory to create. + * Handle a Getattr request. * * Results: - * TRUE on success. - * FALSE on failure. + * None. * * Side effects: * None @@ -7931,56 +5919,125 @@ HgfsUnpackCreateDirPayloadV3(HgfsRequestCreateDirV3 *requestV3, // IN: request p *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackCreateDirPayloadV2(HgfsRequestCreateDirV2 *requestV2, // IN: request payload - size_t payloadSize, // IN: payload size - HgfsCreateDirInfo *info) // IN/OUT: info struct +static void +HgfsServerGetattr(HgfsInputParam *input) // IN: Input params { - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ + char *localName; + HgfsAttrHint hints = 0; + HgfsFileAttrInfo attr; + HgfsInternalStatus status = 0; + HgfsNameStatus nameStatus; + char *cpName; + size_t cpNameSize; + char *targetName = NULL; + uint32 targetNameLen = 0; + HgfsHandle file; /* file handle from driver */ + uint32 caseFlags = 0; + HgfsShareOptions configOptions; + size_t localNameLen; + HgfsShareInfo shareInfo; + size_t replyPayloadSize = 0; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackGetattrRequest(input->payload, input->payloadSize, input->op, &attr, + &hints, &cpName, &cpNameSize, &file, &caseFlags)) { + /* Client wants us to reuse an existing handle. */ + if (hints & HGFS_ATTR_HINT_USE_FILE_DESC) { + fileDesc fd; + + targetNameLen = 0; + status = HgfsPlatformGetFd(file, input->session, FALSE, &fd); + if (HGFS_ERROR_SUCCESS == status) { + status = HgfsPlatformGetattrFromFd(fd, input->session, &attr); + } else { + LOG(4, ("%s: Could not get file descriptor\n", __FUNCTION__)); + } - LOG(4, ("%s: HGFS_OP_CREATE_DIR_V2\n", __FUNCTION__)); - ASSERT(payloadSize >= sizeof *requestV2); - if (requestV2->fileName.length > payloadSize - sizeof *requestV2) { - /* The input packet is smaller than the request. */ - return FALSE; - } - if (!(requestV2->mask & HGFS_CREATE_DIR_VALID_FILE_NAME)) { - /* We do not support requests without a valid file name. */ - return FALSE; - } + } else { + /* + * Depending on whether this file/dir is real or virtual, either + * forge its attributes or look them up in the actual filesystem. + */ + nameStatus = HgfsServerGetShareInfo(cpName, cpNameSize, caseFlags, &shareInfo, + &localName, &localNameLen); + switch (nameStatus) { + case HGFS_NAME_STATUS_INCOMPLETE_BASE: + /* + * This is the base of our namespace; make up fake status for + * this directory. + */ - /* - * Copy all the fields into our carrier struct. Some will probably be - * garbage, but it's simpler to copy everything now and check the - * valid bits before reading later. - */ + LOG(4, ("%s: getting attrs for base dir\n", __FUNCTION__)); + HgfsPlatformGetDefaultDirAttrs(&attr); + break; - info->mask = requestV2->mask; - info->cpName = requestV2->fileName.name; - info->cpNameSize = requestV2->fileName.length; - info->specialPerms = requestV2->specialPerms; - info->ownerPerms = requestV2->ownerPerms; - info->groupPerms = requestV2->groupPerms; - info->otherPerms = requestV2->otherPerms; - info->fileAttr = 0; - return TRUE; + case HGFS_NAME_STATUS_COMPLETE: + /* This is a regular lookup; proceed as usual */ + ASSERT(localName); + + /* Get the config options. */ + nameStatus = HgfsServerPolicy_GetShareOptions(cpName, cpNameSize, + &configOptions); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + status = HgfsPlatformGetattrFromName(localName, configOptions, cpName, &attr, + &targetName); + } else { + LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, cpName)); + status = HGFS_ERROR_FILE_NOT_FOUND; + } + free(localName); + + if (HGFS_ERROR_SUCCESS == status && + !HgfsServerPolicy_CheckMode(HGFS_OPEN_MODE_READ_ONLY, + shareInfo.writePermissions, + shareInfo.readPermissions)) { + status = HGFS_ERROR_ACCESS_DENIED; + } else if (status != HGFS_ERROR_SUCCESS) { + /* + * If it is a dangling share server should not return + * HGFS_ERROR_FILE_NOT_FOUND + * to the client because it causes confusion: a name that is returned + * by directory enumeration should not produce "name not found" + * error. + * Replace it with a more appropriate error code: no such device. + */ + if (status == HGFS_ERROR_FILE_NOT_FOUND && + HgfsIsShareRoot(cpName, cpNameSize)) { + status = HGFS_ERROR_IO; + } + } + break; + + default: + status = HgfsPlatformHandleIncompleteName(nameStatus, &attr); + } + targetNameLen = targetName ? strlen(targetName) : 0; + + } + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackGetattrReply(input->packet, input->metaPacket, &attr, targetName, + targetNameLen, &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsUnpackCreateDirPayloadV1 -- + * HgfsServerSetattr -- * - * Unpack hgfs create directory request V1 payload and initialize a corresponding - * file name to tell us which directory to create. + * Handle a Setattr request. * * Results: - * TRUE on success. - * FALSE on failure. + * None. * * Side effects: * None @@ -7988,45 +6045,102 @@ HgfsUnpackCreateDirPayloadV2(HgfsRequestCreateDirV2 *requestV2, // IN: request p *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackCreateDirPayloadV1(HgfsRequestCreateDir *requestV1, // IN: request payload - size_t payloadSize, // IN: payload size - HgfsCreateDirInfo *info) // IN/OUT: info struct +static void +HgfsServerSetattr(HgfsInputParam *input) // IN: Input params { - /* - * The request file name length is user-provided, so this test must be - * carefully written to prevent wraparounds. - */ + HgfsInternalStatus status = HGFS_ERROR_SUCCESS; + HgfsNameStatus nameStatus; + HgfsFileAttrInfo attr; + char *cpName; + size_t cpNameSize = 0; + HgfsAttrHint hints = 0; + HgfsOpenMode shareMode; + uint32 caseFlags = 0; + HgfsShareInfo shareInfo; + HgfsHandle file; + size_t replyPayloadSize = 0; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackSetattrRequest(input->payload, input->payloadSize, input->op, &attr, + &hints, &cpName, &cpNameSize, &file, &caseFlags)) { + /* Client wants us to reuse an existing handle. */ + if (hints & HGFS_ATTR_HINT_USE_FILE_DESC) { + if (HgfsHandle2ShareMode(file, input->session, &shareMode)) { + if (HGFS_OPEN_MODE_READ_ONLY != shareMode) { + status = HgfsPlatformSetattrFromFd(file, input->session, &attr, hints); + } else { + status = HGFS_ERROR_ACCESS_DENIED; + } + } else { + LOG(4, ("%s: could not get share mode fd %d\n", __FUNCTION__, + file)); + status = HGFS_ERROR_INVALID_HANDLE; + } + } else { /* Client wants us to open a new handle for this operation. */ + char *utf8Name = NULL; + size_t utf8NameLen; - LOG(4, ("%s: HGFS_OP_CREATE_DIR_V1\n", __FUNCTION__)); - ASSERT(payloadSize >= sizeof *requestV1); - if (requestV1->fileName.length > payloadSize - sizeof *requestV1) { - /* The input packet is smaller than the request. */ - return FALSE; + nameStatus = HgfsServerGetShareInfo(cpName, cpNameSize, caseFlags, &shareInfo, + &utf8Name, &utf8NameLen); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + fileDesc hFile; + HgfsServerLock serverLock = HGFS_LOCK_NONE; + HgfsShareOptions configOptions; + + /* + * XXX: If the client has an oplock on this file, it must reuse the + * handle for the oplocked node (or break the oplock) prior to making + * a setattr request. Fail this request. + */ + if (!HgfsServerPolicy_CheckMode(HGFS_OPEN_MODE_WRITE_ONLY, + shareInfo.writePermissions, + shareInfo.readPermissions)) { + status = HGFS_ERROR_ACCESS_DENIED; + } else if (HGFS_NAME_STATUS_COMPLETE != + HgfsServerPolicy_GetShareOptions(cpName, cpNameSize, + &configOptions)) { + LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, cpName)); + status = HGFS_ERROR_FILE_NOT_FOUND; + } else if (HgfsFileHasServerLock(utf8Name, input->session, &serverLock, &hFile)) { + LOG(4, ("%s: An open, oplocked handle exists for " + "this file. The client should retry with that handle\n", + __FUNCTION__)); + status = HGFS_ERROR_PATH_BUSY; + } else { + status = HgfsPlatformSetattrFromName(utf8Name, &attr, configOptions, hints); + } + free(utf8Name); + } else { + LOG(4, ("%s: file not found.\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } + } + + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackSetattrReply(input->packet, input->metaPacket, attr.requestType, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } else { + status = HGFS_ERROR_PROTOCOL; } - /* For CreateDirV1 requests, we know exactly what fields we expect. */ - info->mask = HGFS_CREATE_DIR_VALID_OWNER_PERMS | HGFS_CREATE_DIR_VALID_FILE_NAME; - info->cpName = requestV1->fileName.name; - info->cpNameSize = requestV1->fileName.length; - info->ownerPerms = requestV1->permissions; - info->fileAttr = 0; - return TRUE; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsUnpackCreateDirRequest -- + * HgfsServerValidateOpenParameters -- * - * Unpack hgfs CreateDir request and initialize a corresponding - * HgfsCreateDirInfo structure that is used to pass around CreateDir request - * information. + * Performs sanity check of the input parameters. * * Results: - * TRUE on success. - * FALSE on failure. + * HGFS_ERROR_SUCCESS if the parameters are valid. + * Appropriate error code otherwise. * * Side effects: * None @@ -8034,75 +6148,176 @@ HgfsUnpackCreateDirPayloadV1(HgfsRequestCreateDir *requestV1, // IN: request pay *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackCreateDirRequest(char const *packetIn, // IN: incoming packet - size_t packetSize, // IN: size of packet - HgfsCreateDirInfo *info) // IN/OUT: info struct -{ - void const *payload; - size_t payloadSize; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(info); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, &op)) { - return FALSE; +static HgfsInternalStatus +HgfsServerValidateOpenParameters(HgfsFileOpenInfo *openInfo, // IN/OUT: openfile info + Bool *denyCreatingFile, // OUT: No new files + int *followSymlinks) // OUT: Host resolves link +{ + size_t utf8NameLen; + HgfsInternalStatus status; + + *followSymlinks = 0; + *denyCreatingFile = FALSE; + + if ((openInfo->mask & HGFS_OPEN_VALID_MODE)) { + HgfsNameStatus nameStatus; + /* It is now safe to read the file name. */ + nameStatus = HgfsServerGetShareInfo(openInfo->cpName, + openInfo->cpNameSize, + openInfo->caseFlags, + &openInfo->shareInfo, + &openInfo->utf8Name, + &utf8NameLen); + if (HGFS_NAME_STATUS_COMPLETE == nameStatus) { + if (openInfo->mask & HGFS_OPEN_VALID_FLAGS) { + HgfsOpenFlags savedOpenFlags = openInfo->flags; + + if (HgfsServerCheckOpenFlagsForShare(openInfo, &openInfo->flags)) { + HgfsShareOptions configOptions; + + /* Get the config options. */ + nameStatus = HgfsServerPolicy_GetShareOptions(openInfo->cpName, + openInfo->cpNameSize, + &configOptions); + if (nameStatus == HGFS_NAME_STATUS_COMPLETE) { + *followSymlinks = + HgfsServerPolicy_IsShareOptionSet(configOptions, + HGFS_SHARE_FOLLOW_SYMLINKS); + *denyCreatingFile = savedOpenFlags != openInfo->flags; + status = HGFS_ERROR_SUCCESS; + } else { + LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, openInfo->cpName)); + *denyCreatingFile = TRUE; + status = HGFS_ERROR_FILE_NOT_FOUND; + } + } else { + /* Incompatible open mode with share mode. */ + status = HGFS_STATUS_ACCESS_DENIED; + } + } else { + status = HGFS_ERROR_PROTOCOL; + } + } else { + LOG(4, ("%s: Shared folder not found\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } + } else { + LOG(4, ("%s: filename or mode not provided\n", __FUNCTION__)); + status = HGFS_ERROR_PROTOCOL; } + return status; +} - info->requestType = op; - /* Default value for legacy requests. */ - info->caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - switch (op) { - case HGFS_OP_CREATE_DIR_V3: - { - HgfsRequestCreateDirV3 *requestV3 = (HgfsRequestCreateDirV3 *)payload; - if (!HgfsUnpackCreateDirPayloadV3(requestV3, - payloadSize, - info)) { - return FALSE; - } - break; - } +/* + *----------------------------------------------------------------------------- + * + * HgfsServerOpen -- + * + * Handle an Open request. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ - case HGFS_OP_CREATE_DIR_V2: - { - HgfsRequestCreateDirV2 *requestV2 = (HgfsRequestCreateDirV2 *)payload; - if (!HgfsUnpackCreateDirPayloadV2(requestV2, - payloadSize, - info)) { - return FALSE; - } - break; - } - case HGFS_OP_CREATE_DIR: - { - HgfsRequestCreateDir *requestV1 = (HgfsRequestCreateDir *)payload; - if (!HgfsUnpackCreateDirPayloadV1(requestV1, - payloadSize, - info)) { - return FALSE; +static void +HgfsServerOpen(HgfsInputParam *input) // IN: Input params +{ + HgfsInternalStatus status; + fileDesc newHandle; + HgfsLocalId localId; + HgfsFileOpenInfo openInfo; + fileDesc fileDesc; + HgfsServerLock serverLock = HGFS_LOCK_NONE; + size_t replyPayloadSize = 0; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackOpenRequest(input->payload, input->payloadSize, input->op, &openInfo)) { + int followSymlinks; + Bool denyCreatingFile; + + status = HgfsServerValidateOpenParameters(&openInfo, &denyCreatingFile, + &followSymlinks); + if (HGFS_ERROR_SUCCESS == status) { + ASSERT(openInfo.utf8Name); + LOG(4, ("%s: opening \"%s\", mode %u, flags %u, perms %u%u%u%u attr %u\n", + __FUNCTION__, openInfo.utf8Name, openInfo.mode, + openInfo.mask & HGFS_OPEN_VALID_FLAGS ? openInfo.flags : 0, + (openInfo.mask & HGFS_OPEN_VALID_SPECIAL_PERMS) ? + openInfo.specialPerms : 0, + (openInfo.mask & HGFS_OPEN_VALID_OWNER_PERMS) ? + openInfo.ownerPerms : 0, + (openInfo.mask & HGFS_OPEN_VALID_GROUP_PERMS) ? + openInfo.groupPerms : 0, + (openInfo.mask & HGFS_OPEN_VALID_OTHER_PERMS) ? + openInfo.otherPerms : 0, + openInfo.mask & HGFS_OPEN_VALID_FILE_ATTR ? (uint32)openInfo.attr : 0)); + /* + * XXX: Before opening the file, see if we already have this file opened on + * the server with an oplock on it. If we do, we must fail the new open + * request, otherwise we will trigger an oplock break that the guest cannot + * handle at this time (since the HGFS server is running in the context of + * the vcpu thread), and we'll deadlock. + * + * Until we overcome this limitation via Crosstalk, we will be extra smart + * in the client drivers so as to prevent open requests on handles that + * already have an oplock. And the server will protect itself like so. + * + * XXX: With some extra effort, we could allow a second open for read here, + * since that won't break a shared oplock, but the clients should already + * realize that the second open can be avoided via sharing handles, too. + */ + if (!HgfsFileHasServerLock(openInfo.utf8Name, input->session, &serverLock, + &fileDesc)) { + /* See if the name is valid, and if so add it and return the handle. */ + status = HgfsPlatformValidateOpen(&openInfo, followSymlinks, input->session, + &localId, &newHandle); + if (status == HGFS_ERROR_SUCCESS) { + ASSERT(newHandle >= 0); + + /* + * Open succeeded, so make new node and return its handle. If we fail, + * it's almost certainly an internal server error. + */ + + if (HgfsCreateAndCacheFileNode(&openInfo, &localId, newHandle, + FALSE, input->session)) { + if (!HgfsPackOpenReply(input->packet, input->metaPacket, &openInfo, + &replyPayloadSize, input->session)) { + status = HGFS_ERROR_INTERNAL; + } + } + } else if (denyCreatingFile && HGFS_ERROR_FILE_NOT_FOUND == status) { + status = HGFS_ERROR_ACCESS_DENIED; + } + } else { + status = HGFS_ERROR_PATH_BUSY; } - break; + free(openInfo.utf8Name); } - default: - return FALSE; + } else { + status = HGFS_ERROR_PROTOCOL; } - return TRUE; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsPackCreateDirReply -- + * HgfsGetDirEntry -- * - * Pack hgfs CreateDir reply. + * Gets a directory entry at specified offset. * * Results: - * TRUE if valid op and reply set, FALSE otherwise. + * None. * * Side effects: * None @@ -8110,66 +6325,217 @@ HgfsUnpackCreateDirRequest(char const *packetIn, // IN: incoming packet *----------------------------------------------------------------------------- */ -Bool -HgfsPackCreateDirReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: create dir operation version - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN: Session Info -{ - Bool result = TRUE; +static HgfsInternalStatus +HgfsGetDirEntry(HgfsHandle hgfsSearchHandle, + uint32 requestedOffset, + HgfsSearch *search, + HgfsShareOptions configOptions, + HgfsSessionInfo *session, + HgfsFileAttrInfo *attr, + char **entryName, + size_t *nameLength) +{ + HgfsInternalStatus status; + DirectoryEntry *dent; + + dent = HgfsGetSearchResult(hgfsSearchHandle, session, requestedOffset, FALSE); + if (dent) { + unsigned int length; + char *fullName; + char *sharePath; + size_t sharePathLen; + size_t fullNameLen; + HgfsServerLock serverLock = HGFS_LOCK_NONE; + fileDesc fileDesc; + + length = strlen(dent->d_name); + + /* Each type of search gets a dent's attributes in a different way. */ + switch (search->type) { + case DIRECTORY_SEARCH_TYPE_DIR: - ASSERT(packetIn); + /* + * Construct the UTF8 version of the full path to the file, and call + * HgfsGetattrFromName to get the attributes of the file. + */ + fullNameLen = search->utf8DirLen + 1 + length; + fullName = (char *)malloc(fullNameLen + 1); + if (fullName) { + memcpy(fullName, search->utf8Dir, search->utf8DirLen); + fullName[search->utf8DirLen] = DIRSEPC; + memcpy(&fullName[search->utf8DirLen + 1], dent->d_name, length + 1); - *packetOut = NULL; - *packetSize = 0; + LOG(4, ("%s: about to stat \"%s\"\n", __FUNCTION__, fullName)); - switch (op) { - case HGFS_OP_CREATE_DIR_V3: { - HgfsReplyCreateDirV3 *reply; + /* + * XXX: It is unreasonable to make the caller either 1) pass existing + * handles for directory objects as part of the SearchRead, or 2) + * prior to calling SearchRead on a directory, break all oplocks on + * that directory's objects. + * + * To compensate for that, if we detect that this directory object + * has an oplock, we'll quietly reuse the handle. Note that this + * requires clients who take out an exclusive oplock to open a + * handle with read as well as write access, otherwise we'll fail + * further down in HgfsStat. + * + * XXX: We could open a new handle safely if its a shared oplock. + * But isn't this handle sharing always desirable? + */ + if (HgfsFileHasServerLock(fullName, session, &serverLock, &fileDesc)) { + LOG(4, ("%s: Reusing existing oplocked handle " + "to avoid oplock break deadlock\n", __FUNCTION__)); + status = HgfsPlatformGetattrFromFd(fileDesc, session, attr); + } else { + status = HgfsPlatformGetattrFromName(fullName, configOptions, + search->utf8ShareName, attr, NULL); + } - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - case HGFS_OP_CREATE_DIR_V2: { - HgfsReplyCreateDirV2 *reply; + if (HGFS_ERROR_SUCCESS != status) { + HgfsOp savedOp = attr->requestType; + LOG(4, ("%s: stat FAILED %s (%d)\n", __FUNCTION__, fullName, status)); + memset(attr, 0, sizeof *attr); + attr->requestType = savedOp; + attr->type = HGFS_FILE_TYPE_REGULAR; + attr->mask = 0; + } - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - case HGFS_OP_CREATE_DIR: { - HgfsReplyCreateDir *reply; + free(fullName); + status = HGFS_ERROR_SUCCESS; + } else { + LOG(4, ("%s: could not allocate space for \"%s\\%s\"\n", + __FUNCTION__, search->utf8Dir, dent->d_name)); + status = HGFS_ERROR_NOT_ENOUGH_MEMORY; + } + break; - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - break; - } - default: - result = FALSE; - LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); - NOT_REACHED(); - break; - } + case DIRECTORY_SEARCH_TYPE_BASE: - return result; + /* + * For a search enumerating all shares, give the default attributes + * for '.' and ".." (which aren't really shares anyway). Each real + * share gets resolved into its full path, and gets its attributes + * via HgfsGetattrFromName. + */ + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) { + LOG(4, ("%s: assigning %s default attributes\n", + __FUNCTION__, dent->d_name)); + HgfsPlatformGetDefaultDirAttrs(attr); + status = HGFS_ERROR_SUCCESS; + } else { + HgfsNameStatus nameStatus; + + /* Check permission on the share and get the share path */ + nameStatus = + HgfsServerPolicy_GetSharePath(dent->d_name, length, + HGFS_OPEN_MODE_READ_ONLY, + &sharePathLen, + (char const **)&sharePath); + if (nameStatus == HGFS_NAME_STATUS_COMPLETE) { + + /* + * Server needs to produce list of shares that is consistent with + * the list defined in UI. If a share can't be accessed because of + * problems on the host, the server still enumerates it and + * returns to the client. + */ + /* + * XXX: We will open a new handle for this, but it should be safe + * from oplock-induced deadlock because these are all directories, + * and thus cannot have oplocks placed on them. + */ + status = HgfsPlatformGetattrFromName(sharePath, configOptions, + dent->d_name, attr, NULL); + + /* + * For some reason, Windows marks drives as hidden and system. So + * if one of the top level shared folders is mapped to a drive + * letter (like C:\), then GetFileAttributesEx() will return hidden + * and system attributes for that drive. We don't want that + * since we want the users to see all top level shared folders. + * Even in the case when the top level shared folder is mapped + * to a non-drive hidden/system directory, we still want to display + * it to the user. So make sure that system and hidden attributes + * are not set. + * Note, that for network shares this doesn't apply, since each + * top level network share is a separate mount point that doesn't + * have such attributes. So when we finally have per share + * mounting, this hack will go away. + * + * See bug 125065. + */ + attr->flags &= ~(HGFS_ATTR_HIDDEN | HGFS_ATTR_SYSTEM); + + if (HGFS_ERROR_SUCCESS != status) { + /* + * The dent no longer exists. Log the event. + */ + + LOG(4, ("%s: stat FAILED\n", __FUNCTION__)); + status = HGFS_ERROR_SUCCESS; + } + } else { + LOG(4, ("%s: No such share or access denied\n", __FUNCTION__)); + status = HgfsPlatformConvertFromNameStatus(nameStatus); + } + } + break; + case DIRECTORY_SEARCH_TYPE_OTHER: + + /* + * The POSIX implementation of HgfsSearchOpen could not have created + * this kind of search. + */ +#if !defined(_WIN32) + NOT_IMPLEMENTED(); +#endif + /* + * All "other" searches get the default attributes. This includes + * an enumeration of drive, and the root enumeration (which contains + * a "drive" dent and a "unc" dent). + */ + HgfsPlatformGetDefaultDirAttrs(attr); + break; + default: + NOT_IMPLEMENTED(); + break; + } + + /* + * We need to unescape the name before sending it back to the client + */ + if (HGFS_ERROR_SUCCESS == status) { + *entryName = strdup(dent->d_name); + *nameLength = HgfsEscape_Undo(dent->d_name, length + 1); + LOG(4, ("%s: dent name is \"%s\" len = %"FMTSZ"u\n", __FUNCTION__, + *entryName, *nameLength)); + } else { + *entryName = NULL; + *nameLength = 0; + LOG(4, ("%s: error %d getting dent\n", __FUNCTION__, status)); + } + + free(dent); + } else { + /* End of directory entries marker. */ + *entryName = NULL; + *nameLength = 0; + status = HGFS_ERROR_SUCCESS; + } + return status; } /* *----------------------------------------------------------------------------- * - * HgfsUnpackWriteWin32StreamPayloadV3 -- + * HgfsServerSearchRead -- * - * Unpack hgfs write stream request V3 payload and initialize a corresponding - * file name to tell us which directory to create. + * Handle a "Search Read" request. * * Results: - * TRUE on success. - * FALSE on failure. + * None. * * Side effects: * None @@ -8177,41 +6543,77 @@ HgfsPackCreateDirReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackWriteWin32StreamPayloadV3(HgfsRequestWriteWin32StreamV3 *requestV3, // IN: - size_t payloadSize, // IN: - HgfsHandle *file, // OUT: - char **data, // OUT: - size_t *dataSize, // OUT: - Bool *doSecurity) // OUT: +static void +HgfsServerSearchRead(HgfsInputParam *input) // IN: Input params { - LOG(4, ("%s: HGFS_OP_WRITE_WIN32_STREAM_V3\n", __FUNCTION__)); - if (payloadSize < sizeof *requestV3) { - return FALSE; - } + uint32 requestedOffset; + HgfsFileAttrInfo attr; + HgfsInternalStatus status; + HgfsNameStatus nameStatus; + HgfsHandle hgfsSearchHandle; + HgfsSearch search; + HgfsShareOptions configOptions = 0; + size_t replyPayloadSize = 0; + char *entryName; + size_t nameLength; + + HGFS_ASSERT_INPUT(input); + + if (HgfsUnpackSearchReadRequest(input->payload, input->payloadSize, input->op, &attr, + &hgfsSearchHandle, &requestedOffset)) { + LOG(4, ("%s: read search #%u, offset %u\n", __FUNCTION__, + hgfsSearchHandle, requestedOffset)); + if (HgfsGetSearchCopy(hgfsSearchHandle, input->session, &search)) { + /* Get the config options. */ + status = HGFS_ERROR_SUCCESS; + if (search.utf8ShareNameLen != 0) { + nameStatus = HgfsServerPolicy_GetShareOptions(search.utf8ShareName, + search.utf8ShareNameLen, + &configOptions); + if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { + LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, + search.utf8ShareName)); + status = HGFS_ERROR_FILE_NOT_FOUND; + } + } - if (payloadSize >= requestV3->requiredSize + sizeof *requestV3) { - *file = requestV3->file; - *data = requestV3->payload; - *dataSize = requestV3->requiredSize; - *doSecurity = (requestV3->flags & HGFS_WIN32_STREAM_IGNORE_SECURITY) == 0; - return TRUE; + if (HGFS_ERROR_SUCCESS == status) { + status = HgfsGetDirEntry(hgfsSearchHandle, requestedOffset, &search, + configOptions, input->session, &attr, &entryName, + &nameLength); + if (HGFS_ERROR_SUCCESS == status) { + if (!HgfsPackSearchReadReply(input->packet, input->metaPacket, entryName, + nameLength, &attr, &replyPayloadSize, + input->session)) { + status = HGFS_ERROR_INTERNAL; + } + free(entryName); + } + } + + free(search.utf8Dir); + free(search.utf8ShareName); + } else { + LOG(4, ("%s: handle %u is invalid\n", __FUNCTION__, hgfsSearchHandle)); + status = HGFS_ERROR_INVALID_HANDLE; + } + } else { + status = HGFS_ERROR_PROTOCOL; } - return FALSE; + HgfsServerCompleteRequest(status, replyPayloadSize, input); } + /* *----------------------------------------------------------------------------- * - * HgfsUnpackWriteWin32StreamRequest -- + * HgfsServerCreateSession -- * - * Unpack hgfs SendFileUsingReader request. Returns file to write to, data - * and whether to restore the security stream. + * Handle a "Create session" request. * * Results: - * TRUE on success. - * FALSE on failure. + * None. * * Side effects: * None @@ -8219,53 +6621,45 @@ HgfsUnpackWriteWin32StreamPayloadV3(HgfsRequestWriteWin32StreamV3 *requestV3, // *----------------------------------------------------------------------------- */ -Bool -HgfsUnpackWriteWin32StreamRequest(char const *packetIn, // IN: incoming packet - size_t packetSize, // IN: size of packet - HgfsOp *op, // OUT: request type - HgfsHandle *file, // OUT: file to write to - char **data, // OUT: data to write - size_t *dataSize, // OUT: size of data - Bool *doSecurity) // OUT: restore sec.str. +static void +HgfsServerCreateSession(HgfsInputParam *input) // IN: Input params { - void const *payload; - size_t payloadSize; + size_t replyPayloadSize = 0; + HgfsCreateSessionInfo info; + HgfsInternalStatus status; - ASSERT(packetIn); - ASSERT(file); - ASSERT(data); - ASSERT(dataSize); - ASSERT(doSecurity); - - if (!HgfsParseRequest(packetIn, packetSize, &payload, &payloadSize, op)) { - return FALSE; - } + HGFS_ASSERT_INPUT(input); - if (*op != HGFS_OP_WRITE_WIN32_STREAM_V3) { - /* The only supported version for the moment is V3. */ - NOT_REACHED(); - return FALSE; + if (HgfsUnpackCreateSessionRequest(input->payload, input->payloadSize, + input->op, &info)) { + LOG(4, ("%s: create session\n", __FUNCTION__)); + if (info.maxPacketSize < input->session->maxPacketSize) { + input->session->maxPacketSize = info.maxPacketSize; + } + if (HgfsPackCreateSessionReply(input->packet, input->metaPacket, + &replyPayloadSize, input->session)) { + HgfsServerSessionGet(input->session); + status = HGFS_ERROR_SUCCESS; + } else { + status = HGFS_ERROR_INTERNAL; + } + } else { + status = HGFS_ERROR_PROTOCOL; } - return HgfsUnpackWriteWin32StreamPayloadV3((HgfsRequestWriteWin32StreamV3 *)payload, - payloadSize, - file, - data, - dataSize, - doSecurity); + HgfsServerCompleteRequest(status, replyPayloadSize, input); } /* *----------------------------------------------------------------------------- * - * HgfsPackWriteWin32StreamReply -- + * HgfsServerDestroySession -- * - * Pack hgfs SendFileUsingReader reply. - * Returns the actual amount of data written in the reply. + * Handle a "Destroy session" request. * * Results: - * TRUE if valid op and reply set, FALSE otherwise. + * None. * * Side effects: * None @@ -8273,40 +6667,16 @@ HgfsUnpackWriteWin32StreamRequest(char const *packetIn, // IN: incoming packet *----------------------------------------------------------------------------- */ -Bool -HgfsPackWriteWin32StreamReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: request type - uint32 actualSize, // IN: amount written - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session) // IN:Session Info +static void +HgfsServerDestroySession(HgfsInputParam *input) // IN: Input params { - HgfsReplyWriteWin32StreamV3 *reply; - Bool result; + HGFS_ASSERT_INPUT(input); - ASSERT(packetIn); - - *packetOut = NULL; - *packetSize = 0; - - if (op != HGFS_OP_WRITE_WIN32_STREAM_V3) { - NOT_REACHED(); - return FALSE; - } - - result = HgfsAllocInitReply(packet, packetIn, sizeof *reply, status, packetOut, - (void **)&reply, packetSize, session); - if (result != FALSE) { - reply->actualSize = actualSize; - } - - return result; + HgfsServerCompleteRequest(HGFS_ERROR_SUCCESS, 0, input); + HgfsServerSessionPut(input->session); } - /* *----------------------------------------------------------------------------- * @@ -8439,6 +6809,39 @@ Hgfs_NotificationCallback(HgfsSharedFolderHandle sharedFolder, mask)); } + +/* + *----------------------------------------------------------------------------- + * + * HgfsIsShareRoot -- + * + * Checks if the cpName represents the root directory for a share. + * Components in CPName format are separated by NUL characters. + * CPName for the root of a share contains only one component thus + * it does not have any embedded '\0' characters in the name. + * + * Results: + * TRUE if it is the root directory, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +HgfsIsShareRoot(char const *cpName, // IN: name to test + size_t cpNameSize) // IN: length of the name +{ + size_t i; + for (i = 0; i < cpNameSize; i++) { + if (cpName[i] == '\0') { + return FALSE; + } + } + return TRUE; +} + #ifdef HGFS_OPLOCKS /* *----------------------------------------------------------------------------- diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h index 1d982fa76..e978f82bf 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h +++ b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h @@ -76,22 +76,6 @@ #include "vm_atomic.h" #include "userlock.h" -#ifdef _WIN32 -# define HGFS_REPLYPKT_STATUS RPC_S_PROTOCOL_ERROR -#else -# define HGFS_REPLYPKT_STATUS EPROTO -#endif - -#define HGFS_REPLYPKT_CHECK(replyPacket, replySize, replyPacketSize, status, label) \ - do { \ - LOG(4, ("%s: \n", __FUNCTION__)); \ - ASSERT_DEVEL(replyPacket); \ - if (!replyPacket || ((replySize) > (replyPacketSize))) { \ - status = HGFS_REPLYPKT_STATUS; \ - goto label; \ - } \ - } while(0) - #define HGFS_DEBUG_ASYNC (0) /* @@ -114,8 +98,8 @@ typedef struct HgfsLocalId { } HgfsLocalId; typedef enum { - REQ_ASYNC, /* Hint that request should be processed Async */ - REQ_SYNC, /* " Sync */ + REQ_ASYNC, /* Hint that request should be processed Async. */ + REQ_SYNC, /* " Sync. */ } RequestHint; @@ -296,6 +280,12 @@ typedef enum { typedef struct HgfsSessionInfo { + /* Unique session id. */ + uint64 sessionId; + + /* Max packet size that is supported by both client and server. */ + uint32 maxPacketSize; + /* Transport session context. */ void *transportData; @@ -430,6 +420,10 @@ typedef struct HgfsCreateDirInfo { HgfsAttrFlags fileAttr; /* Various flags and Windows 'attributes' */ } HgfsCreateDirInfo; +typedef struct HgfsCreateSessionInfo { + uint32 maxPacketSize; +} HgfsCreateSessionInfo; + /* Server lock related structure */ typedef struct { @@ -438,16 +432,17 @@ typedef struct { HgfsServerLock serverLock; } ServerLockData; -typedef -struct HgfsInputParam { +typedef struct HgfsInputParam { const char *metaPacket; size_t metaPacketSize; HgfsSessionInfo *session; HgfsPacket *packet; - Bool v4header; + void const *payload; + size_t payloadSize; HgfsOp op; -} -HgfsInputParam; + uint32 id; + Bool v4header; +} HgfsInputParam; Bool HgfsCreateAndCacheFileNode(HgfsFileOpenInfo *openInfo, // IN: Open info struct @@ -529,73 +524,42 @@ HgfsServerSearchVirtualDir(HgfsGetNameFunc *getName, // IN: Name enumerator HgfsSessionInfo *session, // IN: Session info HgfsHandle *handle); // OUT: Search handle -/* - * Opcode handlers - */ - -HgfsInternalStatus -HgfsServerOpen(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerRead(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerWrite(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerSearchOpen(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerSearchRead(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerGetattr(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerSetattr(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerCreateDir(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerDeleteFile(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerDeleteDir(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerRename(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerQueryVolume(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerSymlinkCreate(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerServerLockChange(HgfsInputParam *input); // IN: Input params - -HgfsInternalStatus -HgfsServerWriteWin32Stream(HgfsInputParam *input); // IN: Input params /* Unpack/pack requests/reply helper functions. */ Bool -HgfsUnpackOpenRequest(char const *packetIn, // IN: incoming packet - size_t packetSize, // IN: size of packet - HgfsFileOpenInfo *openInfo); // IN/OUT: open info struct +HgfsParseRequest(HgfsPacket *packet, // IN: request packet + HgfsSessionInfo *session, // IN: current session + HgfsInputParam **input, // OUT: request parameters + HgfsInternalStatus *status); // OUT: error code + +void +HgfsPackLegacyReplyHeader(HgfsInternalStatus status, // IN: reply status + HgfsHandle id, // IN: original packet id + HgfsReply *header); // OUT: outgoing packet header +Bool +HgfsAllocInitReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: incoming packet header + size_t payloadSize, // IN: payload size + void **payload, // OUT: size of the allocated packet + HgfsSessionInfo *session); // IN: Session Info +Bool +HgfsUnpackOpenRequest(void const *packet, // IN: incoming packet + size_t packetSize, // IN: size of packet + HgfsOp op, // IN: request type + HgfsFileOpenInfo *openInfo); // IN/OUT: open info struct Bool -HgfsPackOpenReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsFileOpenInfo *openInfo, // IN: open info struct - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session); // IN: Session info +HgfsPackOpenReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsFileOpenInfo *openInfo, // IN: open info struct + size_t *payloadSize, // OUT: outgoing packet size + HgfsSessionInfo *session); // IN: Session Info + Bool -HgfsUnpackGetattrRequest(char const *packetIn, // IN: request packet +HgfsUnpackGetattrRequest(void const *packetHeader, // IN: packet header size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type HgfsFileAttrInfo *attrInfo, // IN/OUT: unpacked attr struct HgfsAttrHint *hints, // OUT: getattr hints char **cpName, // OUT: cpName @@ -604,9 +568,9 @@ HgfsUnpackGetattrRequest(char const *packetIn, // IN: request packet uint32 *caseFlags); // OUT: case-sensitivity flags Bool -HgfsUnpackDeleteRequest(char const *packetIn, // IN: request packet +HgfsUnpackDeleteRequest(void const *packet, // IN: request packet size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: requested operation + HgfsOp op, // IN: requested operation char **cpName, // OUT: cpName size_t *cpNameSize, // OUT: cpName size HgfsDeleteHint *hints, // OUT: delete hints @@ -615,17 +579,15 @@ HgfsUnpackDeleteRequest(char const *packetIn, // IN: request packet Bool HgfsPackDeleteReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status + char const *packetHeader, // IN: packet header HgfsOp op, // IN: requested operation - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + size_t *payloadSize, // OUT: size of HGFS packet HgfsSessionInfo *session); // IN: Session Info Bool -HgfsUnpackRenameRequest(char const *packetIn, // IN: request packet +HgfsUnpackRenameRequest(void const *packet, // IN: request packet size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: requested operation + HgfsOp op, // IN: requested operation char **cpOldName, // OUT: rename src size_t *cpOldNameLen, // OUT: rename src size char **cpNewName, // OUT: rename dst @@ -638,123 +600,193 @@ HgfsUnpackRenameRequest(char const *packetIn, // IN: request packet Bool HgfsPackRenameReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status + char const *packetHeader, // IN: packet header HgfsOp op, // IN: requested operation - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + size_t *payloadSize, // OUT: size of packet HgfsSessionInfo *session); // IN: Session Info Bool HgfsPackGetattrReply(HgfsPacket *packet, // IN/OUT: Hgfs packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status + char const *packetHeader, // IN: packet header HgfsFileAttrInfo *attr, // IN: attr stucture const char *utf8TargetName, // IN: optional target name uint32 utf8TargetNameLen, // IN: file name length - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + size_t *payloadSize, // OUT: size of HGFS packet HgfsSessionInfo *session); // IN: Session Info Bool -HgfsUnpackSearchReadRequest(const char *packetIn, // IN: request packet +HgfsPackSymlinkCreateReply(HgfsPacket *packet, // IN/OUT: Hgfs packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + size_t *payloadSize, // OUT: size of HGFS packet + HgfsSessionInfo *session); // IN: Session Info +Bool +HgfsUnpackSearchReadRequest(const void *packet, // IN: request packet size_t packetSize, // IN: packet size + HgfsOp op, // IN: requested operation HgfsFileAttrInfo *attr, // OUT: unpacked attr struct HgfsHandle *hgfsSearchHandle, // OUT: hgfs search handle uint32 *offset); // OUT: entry offset Bool HgfsPackSearchReadReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status + char const *packetHeader, // IN: packet header const char *utf8Name, // IN: file name size_t utf8NameLen, // IN: file name length HgfsFileAttrInfo *attr, // IN: file attr struct - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + size_t *payloadSize, // OUT: size of packet HgfsSessionInfo *session); // IN: Session Info Bool -HgfsUnpackSetattrRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsFileAttrInfo *attr, // IN/OUT: getattr info - HgfsAttrHint *hints, // OUT: setattr hints - char **cpName, // OUT: cpName - size_t *cpNameSize, // OUT: cpName size - HgfsHandle *file, // OUT: server file ID - uint32 *caseFlags); // OUT: case-sensitivity flags +HgfsUnpackSetattrRequest(void const *packet, // IN: request packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: requested operation + HgfsFileAttrInfo *attr, // IN/OUT: getattr info + HgfsAttrHint *hints, // OUT: setattr hints + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsHandle *file, // OUT: server file ID + uint32 *caseFlags); // OUT: case-sensitivity flags Bool HgfsPackSetattrReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status + char const *packetHeader, // IN: packet header HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + size_t *payloadSize, // OUT: size of packet HgfsSessionInfo *session); // IN: Session Info Bool -HgfsUnpackCreateDirRequest(char const *packetIn, // IN: incoming packet +HgfsUnpackCreateDirRequest(void const *packet, // IN: HGFS packet size_t packetSize, // IN: size of packet + HgfsOp op, // IN: requested operation HgfsCreateDirInfo *info); // IN/OUT: info struct Bool HgfsPackCreateDirReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: create dir operation version - HgfsInternalStatus status , // IN: reply status + char const *packetHeader, // IN: packet header HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + size_t *payloadSize, // OUT: size of packet HgfsSessionInfo *session); // IN: Session Info Bool -HgfsUnpackWriteWin32StreamRequest(char const *packetIn, // IN: incoming packet +HgfsPackQueryVolumeReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + uint64 freeBytes, // IN: volume free space + uint64 totalBytes, // IN: volume capacity + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session Info +Bool +HgfsUnpackQueryVolumeRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + Bool *useHandle, // OUT: use handle + char **fileName, // OUT: file name + size_t *fileNameLength, // OUT: file name length + uint32 *caseFlags, // OUT: case sensitivity + HgfsHandle *file); // OUT: Handle to the volume +Bool +HgfsUnpackSymlinkCreateRequest(void const *packet, // IN: request packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + Bool *srcUseHandle, // OUT: use source handle + char **srcFileName, // OUT: source file name + size_t *srcFileNameLength, // OUT: source file name length + uint32 *srcCaseFlags, // OUT: source case sensitivity + HgfsHandle *srcFile, // OUT: source file handle + Bool *tgUseHandle, // OUT: use target handle + char **tgFileName, // OUT: target file name + size_t *tgFileNameLength, // OUT: target file name length + uint32 *tgCaseFlags, // OUT: target case sensitivity + HgfsHandle *tgFile); // OUT: target file handle +Bool +HgfsUnpackWriteWin32StreamRequest(void const *packet, // IN: HGFS packet size_t packetSize, // IN: size of packet - HgfsOp *op, // OUT: request type + HgfsOp op, // IN: request type HgfsHandle *file, // OUT: file to write to char **payload, // OUT: data to write size_t *requiredSize, // OUT: size of data Bool *doSecurity); // OUT: restore sec.str. - +Bool +HgfsUnpackCreateSessionRequest(void const *packetHeader, // IN: request packet + size_t packetSize, // IN: size of packet + HgfsOp op, // IN: request type + HgfsCreateSessionInfo *info); // IN/OUT: info struct Bool HgfsPackWriteWin32StreamReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status + char const *packetHeader, // IN: packet headert HgfsOp op, // IN: request type - uint32 actualSize, // IN: amount written - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + uint32 actualSize, // IN: amount written + size_t *payloadSize, // OUT: size of packet HgfsSessionInfo *session); // IN:Session Info Bool -HgfsUnpackCloseRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: request type - HgfsHandle *file); // OUT: Handle to close - +HgfsUnpackCloseRequest(void const *packet, // IN: request packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + HgfsHandle *file); // OUT: Handle to close +Bool +HgfsUnpackSearchOpenRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + char **dirName, // OUT: directory name + uint32 *dirNameLength, // OUT: name length + uint32 *caseFlags); // OUT: case flags Bool HgfsPackCloseReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status + char const *packetHeader, // IN: packet header HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet + size_t *payloadSize, // OUT: size of packet HgfsSessionInfo *session); // IN: Session Info Bool -HgfsUnpackSearchCloseRequest(char const *packetIn, // IN: request packet - size_t packetSize, // IN: request packet size - HgfsOp *op, // OUT: request type - HgfsHandle *file); // OUT: Handle to close - +HgfsUnpackSearchCloseRequest(void const *packet, // IN: request packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + HgfsHandle *file); // OUT: Handle to close Bool -HgfsPackSearchCloseReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet - char const *packetIn, // IN: incoming packet - HgfsInternalStatus status, // IN: reply status - HgfsOp op, // IN: request type - char **packetOut, // OUT: outgoing packet - size_t *packetSize, // OUT: size of packet - HgfsSessionInfo *session); // IN: Session Info - +HgfsPackSearchOpenReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + HgfsHandle search, // IN: search handle + size_t *packetSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session Info +Bool +HgfsPackSearchCloseReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + size_t *packetSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session Info +Bool +HgfsPackWriteReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + uint32 actualSize, // IN: number of bytes that were written + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session info +Bool +HgfsUnpackReadRequest(void const *packet, // IN: HGFS request + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + HgfsHandle *file, // OUT: Handle to close + uint64 *offset, // OUT: offset to read from + uint32 *length); // OUT: length of data to read +Bool +HgfsUnpackWriteRequest(HgfsInputParam *input, // IN: Input params + HgfsHandle *file, // OUT: Handle to write to + uint64 *offset, // OUT: offset to write to + uint32 *length, // OUT: length of data to write + HgfsWriteFlags *flags, // OUT: write flags + char **data); // OUT: data to be written +Bool +HgfsPackCreateSessionReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session Info +Bool +HgfsPackDestorySessionReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session); // IN: Session Info /* Node cache functions. */ Bool @@ -928,7 +960,125 @@ HgfsPacketSend(HgfsPacket *packet, // IN/OUT: Hgfs Packet Bool HgfsServerCheckOpenFlagsForShare(HgfsFileOpenInfo *openInfo, // IN: Hgfs file handle HgfsOpenFlags *flags); // IN/OUT: open mode +HgfsInternalStatus +HgfsPlatformConvertFromNameStatus(HgfsNameStatus status); // IN: name status +HgfsInternalStatus +HgfsPlatformSymlinkCreate(char *localSymlinkName, // IN: symbolic link file name + char *localTargetName); // IN: symlink target name +HgfsInternalStatus +HgfsPlatformGetattrFromName(char *fileName, // IN: file name + HgfsShareOptions configOptions, // IN: configuration options + char *shareName, // IN: share name + HgfsFileAttrInfo *attr, // OUT: file attributes + char **targetName); // OUT: Symlink target +HgfsInternalStatus +HgfsPlatformSearchDir(HgfsNameStatus nameStatus, // IN: name status + char *dirName, // IN: relative directory name + uint32 dirNameLength, // IN: length of dirName + uint32 caseFlags, // IN: case flags + HgfsShareInfo *shareInfo, // IN: sharfed folder information + char *baseDir, // IN: name of the shared directory + uint32 baseDirLen, // IN: length of the baseDir + HgfsSessionInfo *session, // IN: session info + HgfsHandle *handle); // OUT: search handle +HgfsInternalStatus +HgfsAccess(char *fileName, // IN: local file path + char *shareName, // IN: Name of the share + size_t shareNameLen); // IN: Length of the share name +HgfsInternalStatus +HgfsPlatformReadFile(HgfsHandle file, // IN: Hgfs file handle + HgfsSessionInfo *session, // IN: session info + uint64 offset, // IN: file offset to read from + uint32 requiredSize, // IN: length of data to read + void* payload, // OUT: buffer for the read data + uint32 *actualSize); // OUT: actual length read +HgfsInternalStatus +HgfsPlatformWriteFile(HgfsHandle file, // IN: Hgfs file handle + HgfsSessionInfo *session, // IN: session info + uint64 offset, // IN: file offset to write to + uint32 requiredSize, // IN: length of data to write + HgfsWriteFlags flags, // IN: write flags + void* payload, // IN: data to be written + uint32 *actualSize); // OUT: actual length written +HgfsInternalStatus +HgfsPlatformWriteWin32Stream(HgfsHandle file, // IN: packet header + char *dataToWrite, // IN: data to write + size_t requiredSize, // IN: data size + Bool doSecurity, // IN: write ACL + uint32 *actualSize, // OUT: written data size + HgfsSessionInfo *session); // IN: session info +Bool +HgfsValidatePacket(char const *packetIn, // IN: HGFS packet + size_t packetSize, // IN: HGFS packet size + Bool v4header); // IN: HGFS header type +void +HgfsPackReplyHeaderV4(HgfsInternalStatus status, // IN: platfrom independent HGFS status code + uint32 payloadSize, // IN: size of HGFS operational packet + HgfsOp op, // IN: request type + uint64 sessionId, // IN: session id + uint32 requestId, // IN: request id + HgfsHeader *header); // OUT: packet header +HgfsInternalStatus +HgfsPlatformGetFd(HgfsHandle hgfsHandle, // IN: HGFS file handle + HgfsSessionInfo *session, // IN: Session info + Bool append, // IN: Open with append flag + fileDesc *fd); // OUT: Opened file descriptor +HgfsInternalStatus +HgfsPlatformFileExists(char *utf8LocalName); // IN: Full file path utf8 encoding + +/* + * NOTE. + * This function requires valid localSrcName and localTargetName even when + * srcFile and targetFile are specified. + * Depending on some various conditions it may fall back on using file names + * instead of file handles. + */ +HgfsInternalStatus +HgfsPlatformRename(char *localSrcName, // IN: local path to source file + fileDesc srcFile, // IN: source file handle + char *localTargetName, // IN: local path to target file + fileDesc targetFile, // IN: target file handle + HgfsRenameHint hints); // IN: rename hints +HgfsInternalStatus +HgfsPlatformCreateDir(HgfsCreateDirInfo *info, // IN: direcotry properties + char *utf8Name); // IN: full path for the new directory +HgfsInternalStatus +HgfsPlatformDeleteFileByHandle(HgfsHandle file, // IN: file being deleted + HgfsSessionInfo *session); // IN: session info +HgfsInternalStatus +HgfsPlatformDeleteFileByName(char const *utf8Name); // IN: full file path in utf8 encoding +HgfsInternalStatus +HgfsPlatformDeleteDirByHandle(HgfsHandle dir, // IN: directory being deleted + HgfsSessionInfo *session); // IN: session info +HgfsInternalStatus +HgfsPlatformDeleteDirByName(char const *utf8Name); // IN: full file path in utf8 encoding +HgfsInternalStatus +HgfsPlatformHandleIncompleteName(HgfsNameStatus nameStatus, // IN: name status + HgfsFileAttrInfo *attr); // OUT: attributes +void +HgfsPlatformGetDefaultDirAttrs(HgfsFileAttrInfo *attr); // OUT: attributes +HgfsInternalStatus +HgfsPlatformGetattrFromFd(fileDesc fileDesc, // IN: file descriptor to query + HgfsSessionInfo *session, // IN: session info + HgfsFileAttrInfo *attr); // OUT: file attributes +HgfsInternalStatus +HgfsPlatformSetattrFromFd(HgfsHandle file, // IN: file descriptor + HgfsSessionInfo *session, // IN: session info + HgfsFileAttrInfo *attr, // IN: attrs to set + HgfsAttrHint hints); // IN: attr hints +HgfsInternalStatus +HgfsPlatformSetattrFromName(char *utf8Name, // IN: local file path + HgfsFileAttrInfo *attr, // IN: attrs to set + HgfsShareOptions configOptions, // IN: share options + HgfsAttrHint hints); // IN: attr hints +HgfsInternalStatus +HgfsPlatformValidateOpen(HgfsFileOpenInfo *openInfo, // IN: Open info struct + Bool followLinks, // IN: follow symlinks on the host + HgfsSessionInfo *session, // IN: Session info + HgfsLocalId *localId, // OUT: Local unique file ID + fileDesc *newHandle); // OUT: Handle to the file +void HgfsServerSessionGet(HgfsSessionInfo *session); // IN: session context void * HSPU_GetBuf(HgfsPacket *packet, // IN/OUT: Hgfs Packet @@ -951,16 +1101,16 @@ HSPU_GetDataPacketBuf(HgfsPacket *packet, // IN/OUT: Hgfs Packet void HSPU_PutPacket(HgfsPacket *packet, // IN/OUT: Hgfs Packet - HgfsSessionInfo *session); // IN: Session Info + HgfsSessionInfo *session); // IN: Session Info void HSPU_PutBuf(HgfsPacket *packet, // IN/OUT: Hgfs Packet uint32 startIndex, // IN: Start of iov void **buf, // IN/OUT: Buffer to be freed size_t *bufSize, // IN: Size of the buffer - Bool *isAllocated, // IN: Was buffer allocated ? - MappingType mappingType, // IN: Readable/ Writeable ? - HgfsSessionInfo *session); // IN: Session info + Bool *isAllocated, // IN: Was buffer allocated ? + MappingType mappingType, // IN: Readable/ Writeable ? + HgfsSessionInfo *session); // IN: Session info void HSPU_PutDataPacketBuf(HgfsPacket *packet, // IN/OUT: Hgfs Packet diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c b/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c index 1e6cad560..7ee215427 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c @@ -270,24 +270,9 @@ static const int HgfsServerOpenMode[] = { }; /* Local functions. */ -static HgfsInternalStatus HgfsConvertFromNameStatus(HgfsNameStatus status); - static HgfsInternalStatus HgfsGetattrResolveAlias(char const *fileName, char **targetName); -static HgfsInternalStatus HgfsGetattrFromName(char *fileName, - HgfsShareOptions configOptions, - char *shareName, - HgfsFileAttrInfo *attr, - char **targetName); -static HgfsInternalStatus HgfsAccess(char *fileName, - char *shareName, - size_t shareNameLen); - -static HgfsInternalStatus HgfsGetattrFromFd(int fd, - HgfsSessionInfo *session, - HgfsFileAttrInfo *attr); - static void HgfsStatToFileAttr(struct stat *stats, uint64 *creationTime, HgfsFileAttrInfo *attr); @@ -331,19 +316,6 @@ static HgfsInternalStatus HgfsSetattrTimes(struct stat *statBuf, struct timeval *modTime, Bool *timesChanged); -static HgfsInternalStatus HgfsSetattrFromFd(HgfsHandle file, - HgfsSessionInfo *session, - HgfsFileAttrInfo *attr, - HgfsAttrHint hints); - -static HgfsInternalStatus HgfsSetattrFromName(char *cpName, - size_t cpNameSize, - HgfsFileAttrInfo *attr, - HgfsAttrHint hints, - uint32 caseFlags, - HgfsSessionInfo *session); - -static Bool HgfsIsShareRoot(char const *cpName, size_t cpNameSize); static HgfsInternalStatus HgfsGetHiddenXAttr(char const *fileName, Bool *attribute); static HgfsInternalStatus HgfsSetHiddenXAttr(char const *fileName, Bool value); static HgfsInternalStatus HgfsEffectivePermissions(char *fileName, @@ -455,7 +427,7 @@ HgfsServerSigOplockBreak(int sigNum, // IN: Signal number /* *----------------------------------------------------------------------------- * - * HgfsConvertFromNameStatus -- + * HgfsPlatformConvertFromNameStatus -- * * This function converts between a status code used in processing a cross * platform filename, and a platform-specific status code. @@ -473,8 +445,8 @@ HgfsServerSigOplockBreak(int sigNum, // IN: Signal number *----------------------------------------------------------------------------- */ -static HgfsInternalStatus -HgfsConvertFromNameStatus(HgfsNameStatus status) // IN +HgfsInternalStatus +HgfsPlatformConvertFromNameStatus(HgfsNameStatus status) // IN { switch(status) { case HGFS_NAME_STATUS_COMPLETE: @@ -507,7 +479,7 @@ HgfsConvertFromNameStatus(HgfsNameStatus status) // IN /* *----------------------------------------------------------------------------- * - * HgfsServerGetDefaultDirAttrs -- + * HgfsPlatformGetDefaultDirAttrs -- * * Get default directory attributes. Permissions are Read and * Execute permission only. @@ -521,8 +493,8 @@ HgfsConvertFromNameStatus(HgfsNameStatus status) // IN *----------------------------------------------------------------------------- */ -static void -HgfsServerGetDefaultDirAttrs(HgfsFileAttrInfo *attr) // OUT +void +HgfsPlatformGetDefaultDirAttrs(HgfsFileAttrInfo *attr) // OUT { struct timeval tv; uint64 hgfsTime; @@ -798,7 +770,7 @@ HgfsCheckFileNode(char const *localName, // IN /* *----------------------------------------------------------------------------- * - * HgfsGetFd -- + * HgfsPlatformGetFd -- * * Returns the file descriptor associated with the node. If the node is * cached then it just returns the cached file descriptor (checking for @@ -815,11 +787,11 @@ HgfsCheckFileNode(char const *localName, // IN *----------------------------------------------------------------------------- */ -static HgfsInternalStatus -HgfsGetFd(HgfsHandle hgfsHandle, // IN: HGFS file handle - HgfsSessionInfo *session, // IN: Session info - Bool append, // IN: Open with append flag - fileDesc *fd) // OUT: Opened file descriptor +HgfsInternalStatus +HgfsPlatformGetFd(HgfsHandle hgfsHandle, // IN: HGFS file handle + HgfsSessionInfo *session, // IN: Session info + Bool append, // IN: Open with append flag + fileDesc *fd) // OUT: Opened file descriptor { int newFd = -1, openFlags = 0; HgfsFileNode node; @@ -959,12 +931,12 @@ HgfsGetFd(HgfsHandle hgfsHandle, // IN: HGFS file handle *----------------------------------------------------------------------------- */ -static HgfsInternalStatus -HgfsValidateOpen(HgfsFileOpenInfo *openInfo, // IN: Open info struct - int followSymlinks, // IN: followSymlinks config option - HgfsSessionInfo *session, // IN: session info - HgfsLocalId *localId, // OUT: Local unique file ID - int *fileDesc) // OUT: Handle to the file +HgfsInternalStatus +HgfsPlatformValidateOpen(HgfsFileOpenInfo *openInfo, // IN: Open info struct + Bool followSymlinks, // IN: followSymlinks config option + HgfsSessionInfo *session, // IN: session info + HgfsLocalId *localId, // OUT: Local unique file ID + fileDesc *fileDesc) // OUT: Handle to the file { struct stat fileStat; int fd; @@ -2143,7 +2115,7 @@ HgfsFStat(int fd, // IN: file descriptor /* *----------------------------------------------------------------------------- * - * HgfsGetattrFromName -- + * HgfsPlatformGetattrFromName -- * * Performs a stat operation on the given filename, and, if it is a symlink, * allocates the target filename on behalf of the caller and performs a @@ -2167,12 +2139,12 @@ HgfsFStat(int fd, // IN: file descriptor *----------------------------------------------------------------------------- */ -static HgfsInternalStatus -HgfsGetattrFromName(char *fileName, // IN/OUT: Input filename - HgfsShareOptions configOptions, // IN: Share config options - char *shareName, // IN: Share name - HgfsFileAttrInfo *attr, // OUT: Struct to copy into - char **targetName) // OUT: Symlink target filename +HgfsInternalStatus +HgfsPlatformGetattrFromName(char *fileName, // IN/OUT: Input filename + HgfsShareOptions configOptions, // IN: Share config options + char *shareName, // IN: Share name + HgfsFileAttrInfo *attr, // OUT: Struct to copy into + char **targetName) // OUT: Symlink target { HgfsInternalStatus status = 0; struct stat stats; @@ -2304,7 +2276,7 @@ HgfsGetattrFromName(char *fileName, // IN/OUT: Input filenam targetName, NULL)) { LOG(4, ("%s: Unable to normalize form C \"%s\"\n", __FUNCTION__, myTargetName)); - status = HgfsConvertFromNameStatus(HGFS_NAME_STATUS_FAILURE); + status = HgfsPlatformConvertFromNameStatus(HGFS_NAME_STATUS_FAILURE); goto exit; } #else @@ -2342,54 +2314,10 @@ exit: return status; } - -/* - *----------------------------------------------------------------------------- - * - * HgfsAccess -- - * - * Check is a file with the given name exists and accessible, error code - * otherwise. - * The function does not follow symlinks unless HGFS_SHARE_FOLLOW_SYMLINKS - * flag is specified for the shared folder. - * - * Results: - * Zero on success. - * Non-zero on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static HgfsInternalStatus -HgfsAccess(char *fileName, // IN: local file path - char *shareName, // IN: Name of the share - size_t shareNameLen) // IN: Length of the share name -{ - HgfsFileAttrInfo attr; - HgfsShareOptions configOptions; - HgfsNameStatus nameStatus; - HgfsInternalStatus status; - - /* Get the config options. */ - nameStatus = HgfsServerPolicy_GetShareOptions(shareName, shareNameLen, - &configOptions); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, shareName)); - status = ENOENT; - } else { - status = HgfsGetattrFromName(fileName, configOptions, shareName, - &attr, NULL); - } - return status; -} - /* *----------------------------------------------------------------------------- * - * HgfsGetattrFromFd -- + * HgfsPlatformGetattrFromFd -- * * Performs a stat operation on the given file desc. * Does necessary translation between Unix file stats and the @@ -2405,10 +2333,10 @@ HgfsAccess(char *fileName, // IN: local file path *----------------------------------------------------------------------------- */ -static HgfsInternalStatus -HgfsGetattrFromFd(int fd, // IN: file descriptor - HgfsSessionInfo *session, // IN: session info - HgfsFileAttrInfo *attr) // OUT: FileAttrInfo to copy into +HgfsInternalStatus +HgfsPlatformGetattrFromFd(fileDesc fileDesc, // IN: file descriptor + HgfsSessionInfo *session, // IN: session info + HgfsFileAttrInfo *attr) // OUT: FileAttrInfo to copy into { HgfsInternalStatus status = 0; struct stat stats; @@ -2422,9 +2350,9 @@ HgfsGetattrFromFd(int fd, // IN: file descriptor ASSERT(attr); ASSERT(session); - LOG(4, ("%s: getting attrs for %u\n", __FUNCTION__, fd)); + LOG(4, ("%s: getting attrs for %u\n", __FUNCTION__, fileDesc)); - error = HgfsFStat(fd, &stats, &creationTime); + error = HgfsFStat(fileDesc, &stats, &creationTime); if (error) { LOG(4, ("%s: error stating file: %s\n", __FUNCTION__, strerror(error))); status = error; @@ -2459,14 +2387,14 @@ HgfsGetattrFromFd(int fd, // IN: file descriptor * are cached, for setting attributes, renaming and deletion. */ - if (!HgfsFileDesc2Handle(fd, session, &handle)) { - LOG(4, ("%s: could not get HGFS handle for fd %u\n", __FUNCTION__, fd)); + if (!HgfsFileDesc2Handle(fileDesc, session, &handle)) { + LOG(4, ("%s: could not get HGFS handle for fd %u\n", __FUNCTION__, fileDesc)); status = EBADF; goto exit; } if (!HgfsHandle2ShareMode(handle, session, &shareMode)) { - LOG(4, ("%s: could not get share mode fd %u\n", __FUNCTION__, fd)); + LOG(4, ("%s: could not get share mode fd %u\n", __FUNCTION__, fileDesc)); status = EBADF; goto exit; } @@ -2846,7 +2774,7 @@ exit: /* *----------------------------------------------------------------------------- * - * HgfsSetattrFromFd -- + * HgfsPlatformSetattrFromFd -- * * Handle a Setattr request by file descriptor. * @@ -2860,11 +2788,11 @@ exit: *----------------------------------------------------------------------------- */ -static HgfsInternalStatus -HgfsSetattrFromFd(HgfsHandle file, // IN: file descriptor - HgfsSessionInfo *session, // IN: session info - HgfsFileAttrInfo *attr, // OUT: attrs to set - HgfsAttrHint hints) // IN: attr hints +HgfsInternalStatus +HgfsPlatformSetattrFromFd(HgfsHandle file, // IN: file descriptor + HgfsSessionInfo *session, // IN: session info + HgfsFileAttrInfo *attr, // OUT: attrs to set + HgfsAttrHint hints) // IN: attr hints { HgfsInternalStatus status = 0, timesStatus; int error; @@ -2878,28 +2806,16 @@ HgfsSetattrFromFd(HgfsHandle file, // IN: file descriptor Bool idChanged = FALSE; int fd; HgfsServerLock serverLock; - HgfsOpenMode shareMode; ASSERT(session); ASSERT(file != HGFS_INVALID_HANDLE); - status = HgfsGetFd(file, session, FALSE, &fd); + status = HgfsPlatformGetFd(file, session, FALSE, &fd); if (status != 0) { LOG(4, ("%s: Could not get file descriptor\n", __FUNCTION__)); goto exit; } - if (!HgfsHandle2ShareMode(file, session, &shareMode)) { - LOG(4, ("%s: could not get share mode fd %u\n", __FUNCTION__, fd)); - status = EBADF; - goto exit; - } - - if (shareMode == HGFS_OPEN_MODE_READ_ONLY) { - status = EACCES; - goto exit; - } - /* We need the old stats so that we can preserve times. */ if (fstat(fd, &statBuf) == -1) { error = errno; @@ -3024,7 +2940,7 @@ exit: /* *----------------------------------------------------------------------------- * - * HgfsSetattrFromName -- + * HgfsPlatformSetattrFromName -- * * Handle a Setattr request by name. * @@ -3037,18 +2953,13 @@ exit: * *----------------------------------------------------------------------------- */ -static HgfsInternalStatus -HgfsSetattrFromName(char *cpName, // IN: Name - size_t cpNameSize, // IN: Name length - HgfsFileAttrInfo *attr, // IN: attrs to set - HgfsAttrHint hints, // IN: attr hints - uint32 caseFlags, // IN: case-sensitivity flags - HgfsSessionInfo *session) // IN: session info +HgfsInternalStatus +HgfsPlatformSetattrFromName(char *localName, // IN: Name + HgfsFileAttrInfo *attr, // IN: attrs to set + HgfsShareOptions configOptions, // IN: share options + HgfsAttrHint hints) // IN: attr hints { HgfsInternalStatus status = 0, timesStatus; - HgfsNameStatus nameStatus; - int error, fd; - char *localName; struct stat statBuf; struct timeval times[2]; mode_t newPermissions; @@ -3057,32 +2968,10 @@ HgfsSetattrFromName(char *cpName, // IN: Name Bool permsChanged = FALSE; Bool timesChanged = FALSE; Bool idChanged = FALSE; - HgfsServerLock serverLock; - HgfsShareOptions configOptions; - size_t localNameLen; - HgfsShareInfo shareInfo; - - nameStatus = HgfsServerGetShareInfo(cpName, - cpNameSize, - caseFlags, - &shareInfo, - &localName, - &localNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: access check failed\n", __FUNCTION__)); - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; - } + int error; ASSERT(localName); - /* Get the config options. */ - nameStatus = HgfsServerPolicy_GetShareOptions(cpName, cpNameSize, - &configOptions); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, cpName)); - goto exit; - } if (!HgfsServerPolicy_IsShareOptionSet(configOptions, HGFS_SHARE_FOLLOW_SYMLINKS)) { @@ -3098,7 +2987,7 @@ HgfsSetattrFromName(char *cpName, // IN: Name if (File_IsSymLink(localName)) { LOG(4, ("%s: pathname contains a symlink\n", __FUNCTION__)); status = EINVAL; - goto exit_free; + goto exit; } } @@ -3110,14 +2999,7 @@ HgfsSetattrFromName(char *cpName, // IN: Name LOG(4, ("%s: error stating file \"%s\": %s\n", __FUNCTION__, localName, strerror(error))); status = error; - goto exit_free; - } - - if (!HgfsServerPolicy_CheckMode(HGFS_OPEN_MODE_WRITE_ONLY, - shareInfo.writePermissions, - shareInfo.readPermissions)) { - status = EACCES; - goto exit_free; + goto exit; } /* @@ -3160,17 +3042,7 @@ HgfsSetattrFromName(char *cpName, // IN: Name } if (attr->mask & HGFS_ATTR_VALID_SIZE) { - /* - * XXX: Truncating the file will trigger an oplock break. The client - * should have predicted this and removed the oplock prior to sending - * the truncate request. At this point, the server must safeguard itself - * against deadlock. - */ - if (HgfsFileHasServerLock(localName, session, &serverLock, &fd)) { - LOG(4, ("%s: Client attempted to truncate an oplocked file\n", - __FUNCTION__)); - status = EBUSY; - } else if (Posix_Truncate(localName, attr->size) < 0) { + if (Posix_Truncate(localName, attr->size) < 0) { error = errno; LOG(4, ("%s: error truncating file \"%s\": %s\n", __FUNCTION__, localName, strerror(error))); @@ -3202,13 +3074,77 @@ HgfsSetattrFromName(char *cpName, // IN: Name status = timesStatus; } -exit_free: - free(localName); exit: return status; } +HgfsInternalStatus +HgfsPlatformWriteWin32Stream(HgfsHandle file, // IN: packet header + char *dataToWrite, // IN: request type + size_t requiredSize, + Bool doSecurity, + uint32 *actualSize, + HgfsSessionInfo *session) +{ + return EPROTO; +} + +/* + *----------------------------------------------------------------------------- + * + * HgfsConvertToUtf8FormC -- + * + * Converts file name coming from OS to Utf8 form C. + * The function NOOP on Linux where the name is already in correct + * encoding. + * On Mac OS the default encoding is Utf8 form D thus a convertion to + * Utf8 for C is required. + * + * Results: + * TRUE on success. Buffer has name in Utf8 form C encoding. + * FALSE on error. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsConvertToUtf8FormC(char *buffer, // IN/OUT: name to normalize + size_t bufferSize) // IN: size of the name buffer +{ +#if defined(__APPLE__) + size_t entryNameLen; + char *entryName = NULL; + Bool result; + + /* + * HGFS clients receive names in unicode normal form C, + * (precomposed) so Mac hosts must convert from normal form D + * (decomposed). + */ + + if (CodeSet_Utf8FormDToUtf8FormC(buffer, bufferSize, &entryName, &entryNameLen)) { + result = entryNameLen < bufferSize; + if (result) { + memcpy(buffer, entryName, entryNameLen + 1); + } + free(entryName); + } else { + LOG(4, ("%s: Unable to normalize form C \"%s\"\n", __FUNCTION__, buffer)); + result = FALSE; + } + + return result; +#else + /* NOOP on Linux where the name is already has the correct encoding. */ + return TRUE; +#endif /* defined(__APPLE__) */ +} + + /* *----------------------------------------------------------------------------- * @@ -3295,13 +3231,25 @@ HgfsServerScandir(char const *baseDir, // IN: Directory to search in status = ENOMEM; goto exit; } - memcpy(myDents[myNumDents], newDent, newDent->d_reclen); - /* - * Dent is done. Bump the offset to the batched buffer to process the - * next dent within it. - */ - myNumDents++; + if (HgfsConvertToUtf8FormC(newDent->d_name, sizeof newDent->d_name)) { + memcpy(myDents[myNumDents], newDent, newDent->d_reclen); + /* + * Dent is done. Bump the offset to the batched buffer to process the + * next dent within it. + */ + myNumDents++; + } else { + /* + * XXX: + * HGFS discards all file names that can't be converted to utf8. + * It is not desirable since it causes many problems like + * failure to delete directories which contain such files. + * Need to change this to a more reasonable behavior, similar + * to name escaping which is used to deal with illegal file names. + */ + free(myDents[myNumDents]); + } offset += newDent->d_reclen; } } @@ -3383,176 +3331,9 @@ HgfsServerScandir(char const *baseDir, // IN: Directory to search in /* *----------------------------------------------------------------------------- * - * HgfsServerOpen -- - * - * Handle an Open request. - * - * Results: - * Zero on success. - * Non-zero on failure. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -HgfsInternalStatus -HgfsServerOpen(HgfsInputParam *input) // IN: Input params -{ - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsNameStatus nameStatus; - HgfsInternalStatus status; - int newFd = -1; - HgfsLocalId localId; - HgfsFileOpenInfo openInfo; - char *localName = NULL; - HgfsServerLock serverLock = HGFS_LOCK_NONE; - HgfsShareOptions configOptions; - int followSymlinks; - char *packetOut; - size_t packetOutSize; - HgfsOpenFlags savedOpenFlags = 0; - size_t localNameLen; - - ASSERT(packetIn); - ASSERT(session); - - if (!HgfsUnpackOpenRequest(packetIn, packetSize, &openInfo)) { - status = EPROTO; - goto exit; - } - - /* HGFS_OPEN_VALID_FILE_NAME is checked in the unpack function. */ - if (!(openInfo.mask & HGFS_OPEN_VALID_MODE)) { - LOG(4, ("%s: filename or mode not provided\n", __FUNCTION__)); - status = EINVAL; - goto exit; - } - - nameStatus = HgfsServerGetShareInfo(openInfo.cpName, - openInfo.cpNameSize, - openInfo.caseFlags, - &openInfo.shareInfo, - &localName, - &localNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: access check failed\n", __FUNCTION__)); - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; - } - - if (openInfo.mask & HGFS_OPEN_VALID_FLAGS) { - savedOpenFlags = openInfo.flags; - if (!HgfsServerCheckOpenFlagsForShare(&openInfo, &openInfo.flags)) { - /* Incompatible open mode with share mode. */ - status = EACCES; - goto exit; - } - } - - ASSERT(localName); - openInfo.utf8Name = localName; - - LOG(4, ("%s: opening \"%s\", mode %u, flags %u, perms %u%u%u%u\n", - __FUNCTION__, openInfo.utf8Name, - (openInfo.mask & HGFS_OPEN_VALID_MODE) ? openInfo.mode : 0, - (openInfo.mask & HGFS_OPEN_VALID_FLAGS) ? openInfo.flags : 0, - (openInfo.mask & HGFS_OPEN_VALID_SPECIAL_PERMS) ? - openInfo.specialPerms : 0, - (openInfo.mask & HGFS_OPEN_VALID_OWNER_PERMS) ? - openInfo.ownerPerms : 0, - (openInfo.mask & HGFS_OPEN_VALID_GROUP_PERMS) ? - openInfo.groupPerms : 0, - (openInfo.mask & HGFS_OPEN_VALID_OTHER_PERMS) ? - openInfo.otherPerms : 0)); - - /* - * XXX: Before opening the file, see if we already have this file opened on - * the server with an oplock on it. If we do, we must fail the new open - * request, otherwise we will trigger an oplock break that the guest cannot - * handle at this time (since the HGFS server is running in the context of - * the vcpu thread), and we'll deadlock. - * - * Until we overcome this limitation via Crosstalk, we will be extra smart - * in the client drivers so as to prevent open requests on handles that - * already have an oplock. And the server will protect itself like so. - * - * XXX: With some extra effort, we could allow a second open for read here, - * since that won't break a shared oplock, but the clients should already - * realize that the second open can be avoided via sharing handles, too. - */ - if (HgfsFileHasServerLock(localName, session, &serverLock, &newFd)) { - LOG (4, ("%s: Client tried to open new handle for oplocked file %s.\n", - __FUNCTION__, localName)); - status = EBUSY; - goto exit; - } - - /* Get the config options. */ - nameStatus = HgfsServerPolicy_GetShareOptions(openInfo.cpName, - openInfo.cpNameSize, - &configOptions); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, openInfo.cpName)); - status = ENOENT; - goto exit; - } - - followSymlinks = HgfsServerPolicy_IsShareOptionSet(configOptions, - HGFS_SHARE_FOLLOW_SYMLINKS); - - /* See if the name is valid, and if so add it and return the handle. */ - status = HgfsValidateOpen(&openInfo, followSymlinks, session, &localId, - &newFd); - if (status == 0) { - ASSERT(newFd >= 0); - - /* - * Open succeeded, so make new node and return its handle. If we fail, - * it's almost certainly an internal server error. - */ - - if (!HgfsCreateAndCacheFileNode(&openInfo, &localId, newFd, FALSE, - session)) { - status = HGFS_INTERNAL_STATUS_ERROR; - goto exit; - } - - if (HgfsPackOpenReply(input->packet, packetIn, status, &openInfo, &packetOut, - &packetOutSize, session)) { - if (!HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); - } - } else { - status = EPROTO; - } - } else { - /* - * The open failed, if we modified the open flags, force the return - * status to be access denied, not the error for the modified open. - */ - if (openInfo.mask & HGFS_OPEN_VALID_FLAGS && - savedOpenFlags != openInfo.flags && - status == ENOENT) { - status = EACCES; - } - } - - exit: - free(localName); - return status; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsServerRead -- + * HgfsPlatformReadFile -- * - * Handle a Read request. + * Reads data from a file. * * Results: * Zero on success. @@ -3565,131 +3346,35 @@ HgfsServerOpen(HgfsInputParam *input) // IN: Input params */ HgfsInternalStatus -HgfsServerRead(HgfsInputParam *input) // IN: Input params +HgfsPlatformReadFile(HgfsHandle file, // IN: Hgfs file handle + HgfsSessionInfo *session, // IN: session info + uint64 offset, // IN: file offset to read from + uint32 requiredSize, // IN: length of data to read + void* payload, // OUT: buffer for the read data + uint32 *actualSize) // OUT: actual length read { - const char *packetIn = input->metaPacket; - HgfsSessionInfo *session = input->session; - HgfsRequest *header = (HgfsRequest *)packetIn; int fd; int error; HgfsInternalStatus status; - uint32 extra; Bool sequentialOpen; - HgfsHandle file; - uint64 offset; - uint32 requiredSize; - char *payload; - uint32 *replyActualSize; - size_t replySize; - size_t replyPacketSize; - char *packetOut; - - ASSERT(packetIn); - ASSERT(session); - - if (header->op == HGFS_OP_READ_FAST_V4) { - HgfsRequestReadV3 *request = - (HgfsRequestReadV3 *)HGFS_REQ_GET_PAYLOAD_V3(packetIn); - HgfsReplyReadV3 *reply; - - file = request->file; - offset = request->offset; - requiredSize = request->requiredSize; - - replySize = HGFS_REP_PAYLOAD_SIZE_V3(reply) - 1; - /* Get a data packet buffer that is writeable */ - payload = HSPU_GetDataPacketBuf(input->packet, BUF_WRITEABLE, session); - if (!payload) { - ASSERT_DEVEL(payload); - status = EPROTO; - goto error; - } - replyPacketSize = replySize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize, replyPacketSize, status, error); - reply = (HgfsReplyReadV3 *)HGFS_REP_GET_PAYLOAD_V3(packetOut); - - replyActualSize = &reply->actualSize; - reply->reserved = 0; - - } else if (header->op == HGFS_OP_READ_V3) { - HgfsRequestReadV3 *request = - (HgfsRequestReadV3 *)HGFS_REQ_GET_PAYLOAD_V3(packetIn); - HgfsReplyReadV3 *reply; - - file = request->file; - offset = request->offset; - requiredSize = request->requiredSize; - - replySize = HGFS_REP_PAYLOAD_SIZE_V3(reply) - 1; - ASSERT(HGFS_LARGE_PACKET_MAX >= replySize); - extra = HGFS_LARGE_PACKET_MAX - replySize; - - /* - * requiredSize is user-provided, so this test must be carefully - * written to prevent wraparounds. - */ - if (requiredSize > extra) { - /* - * The client wants to read more bytes than our payload can handle. - * Truncate the request - */ - requiredSize = extra; - } - replyPacketSize = replySize + requiredSize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize + requiredSize, replyPacketSize, status, error); - reply = (HgfsReplyReadV3 *)HGFS_REP_GET_PAYLOAD_V3(packetOut); - payload = reply->payload; - replyActualSize = &reply->actualSize; - reply->reserved = 0; - } else { - HgfsRequestRead *request = (HgfsRequestRead *)packetIn; - HgfsReplyRead *reply; - file = request->file; - offset = request->offset; - requiredSize = request->requiredSize; - - replySize = sizeof *reply - 1; - ASSERT(HGFS_PACKET_MAX >= replySize); - extra = HGFS_PACKET_MAX - replySize; - - /* - * requiredSize is user-provided, so this test must be carefully - * written to prevent wraparounds. - */ - if (requiredSize > extra) { - /* - * The client wants to read more bytes than our payload can handle. - * Truncate the request - */ - requiredSize = extra; - } + ASSERT(session); - replyPacketSize = replySize + requiredSize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize + requiredSize, replyPacketSize, status, error); - reply = (HgfsReplyRead *)packetOut; - payload = reply->payload; - replyActualSize = &reply->actualSize; - } LOG(4, ("%s: read fh %u, offset %"FMT64"u, count %u\n", __FUNCTION__, file, offset, requiredSize)); /* Get the file descriptor from the cache */ - status = HgfsGetFd(file, session, FALSE, &fd); + status = HgfsPlatformGetFd(file, session, FALSE, &fd); if (status != 0) { LOG(4, ("%s: Could not get file descriptor\n", __FUNCTION__)); - goto error; + return status; } if (!HgfsHandleIsSequentialOpen(file, session, &sequentialOpen)) { LOG(4, ("%s: Could not get sequenial open status\n", __FUNCTION__)); - status = EBADF; - goto error; + return EBADF; } #if defined(GLIBC_VERSION_21) || defined(__APPLE__) @@ -3707,7 +3392,9 @@ HgfsServerRead(HgfsInputParam *input) // IN: Input params MXUser_AcquireExclLock(session->fileIOLock); - if (!sequentialOpen) { + if (sequentialOpen) { + error = 0; // No error from seek + } else { # ifdef linux { uint64 res; @@ -3720,45 +3407,26 @@ HgfsServerRead(HgfsInputParam *input) // IN: Input params # else error = lseek(fd, offset, 0); # endif + } - if (error < 0) { - status = errno; - LOG(4, ("%s: could not seek to %"FMT64"u: %s\n", __FUNCTION__, - offset, strerror(status))); - MXUser_ReleaseExclLock(session->fileIOLock); - goto error; - } + if (error >= 0) { + error = read(fd, payload, requiredSize); + } else { + LOG(4, ("%s: could not seek to %"FMT64"u: %s\n", __FUNCTION__, + offset, strerror(status))); } - error = read(fd, payload, requiredSize); MXUser_ReleaseExclLock(session->fileIOLock); #endif if (error < 0) { status = errno; LOG(4, ("%s: error reading from file: %s\n", __FUNCTION__, strerror(status))); - goto error; - } - - LOG(4, ("%s: read %d bytes\n", __FUNCTION__, error)); - *replyActualSize = error; - - if (header->op == HGFS_OP_READ_FAST_V4) { - HSPU_PutDataPacketBuf(input->packet, session); } else { - replySize += error; - } - - /* Send the reply. */ - if (!HgfsPackAndSendPacket(input->packet, packetOut, replySize, 0, - header->id, session, 0)) { - status = 0; - goto error; + LOG(4, ("%s: read %d bytes\n", __FUNCTION__, error)); + *actualSize = error; } - return 0; -error: - HSPU_PutReplyPacket(input->packet, session); return status; } @@ -3766,9 +3434,9 @@ error: /* *----------------------------------------------------------------------------- * - * HgfsServerWrite -- + * HgfsPlatformWriteFile -- * - * Handle a Write request. + * Performs actual writing data to a file. * * Results: * Zero on success. @@ -3781,143 +3449,36 @@ error: */ HgfsInternalStatus -HgfsServerWrite(HgfsInputParam *input) // IN: Input params +HgfsPlatformWriteFile(HgfsHandle file, // IN: Hgfs file handle + HgfsSessionInfo *session, // IN: session info + uint64 offset, // IN: file offset to write to + uint32 requiredSize, // IN: length of data to write + HgfsWriteFlags flags, // IN: write flags + void* payload, // IN: data to be written + uint32 *actualSize) // OUT: actual length written { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsRequest *header = (HgfsRequest *)packetIn; HgfsInternalStatus status; int fd; - int error; + int error = 0; Bool sequentialOpen; - HgfsHandle file; - HgfsWriteFlags flags; - uint64 offset; - uint32 requiredSize; - char *payload; - uint32 *actualSize; - size_t replySize; - size_t replyPacketSize; - char *packetOut; - - ASSERT(packetIn); - ASSERT(session); - - if (header->op == HGFS_OP_WRITE_FAST_V4) { - HgfsRequestWriteV3 *request; - HgfsReplyWriteV3 *reply; - - replySize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - replyPacketSize = replySize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize, replyPacketSize, status, error); - - request = (HgfsRequestWriteV3 *)HGFS_REQ_GET_PAYLOAD_V3(packetIn); - reply = (HgfsReplyWriteV3 *)HGFS_REP_GET_PAYLOAD_V3(packetOut); - - /* Enforced by the dispatch function */ - ASSERT(packetSize >= HGFS_REQ_PAYLOAD_SIZE_V3(request) - 1); - - file = request->file; - flags = request->flags; - offset = request->offset; - requiredSize = request->requiredSize; - reply->reserved = 0; - actualSize = &reply->actualSize; - /* Get a data packet buffer that is readable */ - payload = HSPU_GetDataPacketBuf(input->packet, BUF_READABLE, session); - if (!payload) { - ASSERT_DEVEL(payload); - status = EPROTO; - goto error; - } - } else if (header->op == HGFS_OP_WRITE_V3) { - HgfsRequestWriteV3 *request; - HgfsReplyWriteV3 *reply; - uint32 extra; - - replySize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - replyPacketSize = replySize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize, replyPacketSize, status, error); - - request = (HgfsRequestWriteV3 *)HGFS_REQ_GET_PAYLOAD_V3(packetIn); - reply = (HgfsReplyWriteV3 *)HGFS_REP_GET_PAYLOAD_V3(packetOut); + LOG(4, ("%s: write fh %u, offset %"FMT64"u, count %u\n", + __FUNCTION__, file, offset, requiredSize)); - /* Enforced by the dispatch function */ - ASSERT(packetSize >= HGFS_REQ_PAYLOAD_SIZE_V3(request) - 1); - extra = packetSize - (HGFS_REQ_PAYLOAD_SIZE_V3(request) - 1); + /* Get the file desriptor from the cache */ + status = HgfsPlatformGetFd(file, session, + ((flags & HGFS_WRITE_APPEND) ? TRUE : FALSE), + &fd); - file = request->file; - flags = request->flags; - offset = request->offset; - requiredSize = request->requiredSize; - reply->reserved = 0; - actualSize = &reply->actualSize; + if (status != 0) { + LOG(4, ("%s: Could not get file descriptor\n", __FUNCTION__)); + return status; + } - payload = request->payload; - - /* - * requiredSize is user-provided, so this test must be carefully - * written to prevent wraparounds. - */ - if (requiredSize > extra) { - requiredSize = extra; - } - - } else { - HgfsRequestWrite *request; - HgfsReplyWrite *reply; - uint32 extra; - - replySize = sizeof *reply; - replyPacketSize = replySize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize, replyPacketSize, status, error); - - request = (HgfsRequestWrite *)packetIn; - reply = (HgfsReplyWrite *)packetOut; - - /* Enforced by the dispatch function */ - ASSERT(packetSize >= sizeof *request - 1); - extra = packetSize - (sizeof *request - 1); - - file = request->file; - flags = request->flags; - offset = request->offset; - payload = request->payload; - requiredSize = request->requiredSize; - actualSize = &reply->actualSize; - - /* - * requiredSize is user-provided, so this test must be carefully - * written to prevent wraparounds. - */ - if (requiredSize > extra) { - requiredSize = extra; - } - } - - LOG(4, ("%s: write fh %u, offset %"FMT64"u, count %u\n", - __FUNCTION__, file, offset, requiredSize)); - - /* Get the file desriptor from the cache */ - status = HgfsGetFd(file, session, ((flags & HGFS_WRITE_APPEND) ? - TRUE : FALSE), - &fd); - - if (status != 0) { - LOG(4, ("%s: Could not get file descriptor\n", __FUNCTION__)); - goto error; - } - - if (!HgfsHandleIsSequentialOpen(file, session, &sequentialOpen)) { - LOG(4, ("%s: Could not get sequential open status\n", __FUNCTION__)); - status = EBADF; - goto error; - } + if (!HgfsHandleIsSequentialOpen(file, session, &sequentialOpen)) { + LOG(4, ("%s: Could not get sequential open status\n", __FUNCTION__)); + return EBADF; + } #if defined(GLIBC_VERSION_21) || defined(__APPLE__) /* Write to the file. */ @@ -3947,42 +3508,25 @@ HgfsServerWrite(HgfsInputParam *input) // IN: Input params error = lseek(fd, offset, 0); # endif + } + + if (error < 0) { + status = errno; + LOG(4, ("%s: could not seek to %"FMT64"u: %s\n", __FUNCTION__, + offset, strerror(status))); + } else { + error = write(fd, payload, requiredSize); if (error < 0) { status = errno; - LOG(4, ("%s: could not seek to %"FMT64"u: %s\n", __FUNCTION__, - offset, strerror(status))); - MXUser_ReleaseExclLock(session->fileIOLock); - goto error; + LOG(4, ("%s: error writing to file: %s\n", __FUNCTION__, + strerror(status))); + } else { + LOG(4, ("%s: wrote %d bytes\n", __FUNCTION__, error)); + *actualSize = error; } } - - error = write(fd, payload, requiredSize); MXUser_ReleaseExclLock(session->fileIOLock); #endif - if (error < 0) { - status = errno; - LOG(4, ("%s: error writing to file: %s\n", __FUNCTION__, - strerror(status))); - goto error; - } - - LOG(4, ("%s: wrote %d bytes\n", __FUNCTION__, error)); - *actualSize = error; - status = 0; - - if (header->op == HGFS_OP_WRITE_FAST_V4) { - HSPU_PutDataPacketBuf(input->packet, session); - } - - if (!HgfsPackAndSendPacket(input->packet, packetOut, replySize, - 0, header->id, session, 0)) { - goto error; - } - - return 0; - -error: - HSPU_PutReplyPacket(input->packet, session); return status; } @@ -3990,9 +3534,9 @@ error: /* *----------------------------------------------------------------------------- * - * HgfsServerSearchOpen -- + * HgfsPlatformSearchDir -- * - * Handle a "Search Open" request. + * Handle platform specific logic needed to perform search open request. * * Results: * Zero on success. @@ -4005,87 +3549,17 @@ error: */ HgfsInternalStatus -HgfsServerSearchOpen(HgfsInputParam *input) // IN: Input params +HgfsPlatformSearchDir(HgfsNameStatus nameStatus, // IN: name status + char *dirName, // IN: relative directory name + uint32 dirNameLength, // IN: length of dirName + uint32 caseFlags, // IN: case flags + HgfsShareInfo *shareInfo, // IN: sharfed folder information + char *baseDir, // IN: name of the shared directory + uint32 baseDirLen, // IN: length of the baseDir + HgfsSessionInfo *session, // IN: session info + HgfsHandle *handle) // OUT: search handle { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsRequest *header; - HgfsHandle *replySearch; - uint32 extra; - size_t baseDirLen; - char *baseDir; - HgfsHandle handle; - HgfsInternalStatus status; - HgfsNameStatus nameStatus; - char *dirName; - uint32 dirNameLength; - HgfsCaseType caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - size_t replySize; - size_t replyPacketSize; - char *packetOut; - HgfsShareInfo shareInfo; - - ASSERT(packetIn); - ASSERT(session); - header = (HgfsRequest *)packetIn; - - if (header->op == HGFS_OP_SEARCH_OPEN_V3) { - HgfsRequestSearchOpenV3 *requestV3; - HgfsReplySearchOpenV3 *replyV3; - - replySize = HGFS_REP_PAYLOAD_SIZE_V3(replyV3); - replyPacketSize = replySize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize, replyPacketSize, status, exit); - - requestV3 = (HgfsRequestSearchOpenV3 *)HGFS_REQ_GET_PAYLOAD_V3(packetIn); - replyV3 = (HgfsReplySearchOpenV3 *)HGFS_REP_GET_PAYLOAD_V3(packetOut); - - /* Enforced by the dispatch function */ - ASSERT(packetSize >= HGFS_REQ_PAYLOAD_SIZE_V3(requestV3)); - extra = packetSize - HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); - - caseFlags = requestV3->dirName.caseType; - dirName = requestV3->dirName.name; - dirNameLength = requestV3->dirName.length; - replySearch = &replyV3->search; - replyV3->reserved = 0; - LOG(4, ("%s: HGFS_OP_SEARCH_OPEN_V3\n", __FUNCTION__)); - } else { - HgfsRequestSearchOpen *request = (HgfsRequestSearchOpen *)packetIn; - - replySize = sizeof (HgfsReplySearchOpen); - replyPacketSize = replySize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, replySize, replyPacketSize, status, exit); - - /* Enforced by the dispatch function */ - ASSERT(packetSize >= sizeof *request); - extra = packetSize - sizeof *request; - - dirName = request->dirName.name; - dirNameLength = request->dirName.length; - replySearch = &((HgfsReplySearchOpen *)packetOut)->search; - } - - /* - * request->dirName.length is user-provided, so this test must be carefully - * written to prevent wraparounds. - */ - if (dirNameLength > extra) { - /* The input packet is smaller than the request */ - status = EPROTO; - goto exit; - } - - /* It is now safe to read the file name. */ - nameStatus = HgfsServerGetShareInfo(dirName, - dirNameLength, - caseFlags, - &shareInfo, - &baseDir, - &baseDirLen); + HgfsInternalStatus status = 0; switch (nameStatus) { case HGFS_NAME_STATUS_COMPLETE: { @@ -4101,41 +3575,38 @@ HgfsServerSearchOpen(HgfsInputParam *input) // IN: Input params /* Get the first component. */ len = CPName_GetComponent(dirName, inEnd, (char const **) &next); - if (len < 0) { + if (len >= 0) { + if (*inEnd != '\0') { + /* + * NT4 clients can send the name without a nul-terminator. + * The space for the nul is included and tested for in the size + * calculations above. Size of structure (includes a single + * character of the name) and the full dirname length. + */ + *inEnd = '\0'; + } + + LOG(4, ("%s: dirName: %s.\n", __FUNCTION__, dirName)); + status = HgfsServerSearchRealDir(baseDir, + baseDirLen, + dirName, + shareInfo->rootDir, + session, + handle); + } else { LOG(4, ("%s: get first component failed\n", __FUNCTION__)); status = ENOENT; - goto exit; - } - - if (*inEnd != '\0') { - /* - * NT4 clients can send the name without a nul-terminator. - * The space for the nul is included and tested for in the size - * calculations above. Size of structure (includes a single - * character of the name) and the full dirname length. - */ - *inEnd = '\0'; } - - LOG(4, ("%s: dirName: %s.\n", __FUNCTION__, dirName)); - status = HgfsServerSearchRealDir(baseDir, - baseDirLen, - dirName, - shareInfo.rootDir, - session, - &handle); - free(baseDir); /* * If the directory exists but shared folder is write only * then return access denied, otherwise preserve the original * error code. */ - if (!shareInfo.readPermissions && HGFS_NAME_STATUS_COMPLETE == status) { + if (!shareInfo->readPermissions && HGFS_NAME_STATUS_COMPLETE == status) { status = HGFS_NAME_STATUS_ACCESS_DENIED; } if (status != 0) { LOG(4, ("%s: couldn't scandir\n", __FUNCTION__)); - goto exit; } break; } @@ -4152,38 +3623,21 @@ HgfsServerSearchOpen(HgfsInputParam *input) // IN: Input params HgfsServerPolicy_GetSharesCleanup, DIRECTORY_SEARCH_TYPE_BASE, session, - &handle); + handle); if (status != 0) { LOG(4, ("%s: couldn't enumerate shares\n", __FUNCTION__)); - goto exit; } break; default: LOG(4, ("%s: access check failed\n", __FUNCTION__)); - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; + status = HgfsPlatformConvertFromNameStatus(nameStatus); } if (DOLOG(4)) { - HgfsServerDumpDents(handle, session); + HgfsServerDumpDents(*handle, session); } - /* - * Return handle to the search object as the reply to the search - * open. - */ - *replySearch = handle; - - if (!HgfsPackAndSendPacket(input->packet, packetOut, replySize, - 0, header->id, session, 0)) { - status = 0; - goto exit; - } - return 0; - -exit: - HSPU_PutReplyPacket(input->packet, session); return status; } @@ -4191,13 +3645,12 @@ exit: /* *----------------------------------------------------------------------------- * - * HgfsServerSearchRead -- + * HgfsPlatformHandleIncompleteName -- * - * Handle a "Search Read" request. + * Returns platform error that matches HgfsNameStatus. * * Results: - * Zero on success. - * Non-zero on failure. + * Non-zero error code. * * Side effects: * None @@ -4206,249 +3659,22 @@ exit: */ HgfsInternalStatus -HgfsServerSearchRead(HgfsInputParam *input) // IN: Input params +HgfsPlatformHandleIncompleteName(HgfsNameStatus nameStatus, // IN: name status + HgfsFileAttrInfo *attr) // OUT: unused { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - uint32 requestedOffset; - HgfsFileAttrInfo attr; - HgfsInternalStatus status; - HgfsNameStatus nameStatus; - HgfsHandle hgfsSearchHandle; - DirectoryEntry *dent; - HgfsSearch search; - HgfsShareOptions configOptions = 0; - char *packetOut; - size_t packetOutSize; - - ASSERT(packetIn); - ASSERT(session); - - if (!HgfsUnpackSearchReadRequest(packetIn, packetSize, &attr, - &hgfsSearchHandle, &requestedOffset)) { - return EPROTO; - } - - LOG(4, ("%s: read search #%u, offset %u\n", __FUNCTION__, - hgfsSearchHandle, requestedOffset)); - - if (!HgfsGetSearchCopy(hgfsSearchHandle, session, &search)) { - LOG(4, ("%s: handle %u is invalid\n", __FUNCTION__, hgfsSearchHandle)); - return EBADF; - } - - /* Get the config options. */ - if (search.utf8ShareNameLen != 0) { - nameStatus = HgfsServerPolicy_GetShareOptions(search.utf8ShareName, - search.utf8ShareNameLen, - &configOptions); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, - search.utf8ShareName)); - free(search.utf8Dir); - free(search.utf8ShareName); - return ENOENT; - } - } - - while ((dent = HgfsGetSearchResult(hgfsSearchHandle, session, - requestedOffset, FALSE)) != NULL) { - unsigned int length; - char *fullName; - char *sharePath; - size_t sharePathLen; - size_t fullNameLen; - size_t entryNameLen; - char *entryName = NULL; - Bool freeEntryName = FALSE; - - length = strlen(dent->d_name); - - /* Each type of search gets a dent's attributes in a different way. */ - switch (search.type) { - case DIRECTORY_SEARCH_TYPE_DIR: - - /* - * Construct the UTF8 version of the full path to the file, and call - * HgfsGetattrFromName to get the attributes of the file. - */ - fullNameLen = search.utf8DirLen + 1 + length; - fullName = (char *)malloc(fullNameLen + 1); - if (!fullName) { - LOG(4, ("%s: could not allocate space for \"%s\\%s\"\n", - __FUNCTION__, search.utf8Dir, dent->d_name)); - free(search.utf8Dir); - free(search.utf8ShareName); - free(dent); - return ENOMEM; - } - memcpy(fullName, search.utf8Dir, search.utf8DirLen); - fullName[search.utf8DirLen] = DIRSEPC; - memcpy(&fullName[search.utf8DirLen + 1], dent->d_name, length + 1); - - LOG(4, ("%s: about to stat \"%s\"\n", __FUNCTION__, fullName)); - - status = HgfsGetattrFromName(fullName, configOptions, - search.utf8ShareName, &attr, NULL); - if (status != 0) { - HgfsOp savedOp = attr.requestType; - LOG(4, ("%s: stat FAILED %s (%d)\n", __FUNCTION__, - fullName, status)); - memset(&attr, 0, sizeof attr); - attr.requestType = savedOp; - attr.type = HGFS_FILE_TYPE_REGULAR; - attr.mask = 0; - } - - free(fullName); - -#if defined(__APPLE__) - /* - * HGFS clients receive names in unicode normal form C, - * (precomposed) so Mac hosts must convert from normal form D - * (decomposed). - */ - - if (!CodeSet_Utf8FormDToUtf8FormC((const char *)dent->d_name, length, - &entryName, &entryNameLen)) { - LOG(4, ("%s: Unable to normalize form C \"%s\"\n", __FUNCTION__, - dent->d_name)); - /* Skip this entry and continue. */ - free(dent); - continue; - } - - freeEntryName = TRUE; -#else /* defined(__APPLE__) */ - entryName = dent->d_name; - entryNameLen = length; -#endif /* defined(__APPLE__) */ - break; - case DIRECTORY_SEARCH_TYPE_BASE: - - /* - * For a search enumerating all shares, give the default attributes - * for '.' and ".." (which aren't really shares anyway). Each real - * share gets resolved into its full path, and gets its attributes - * via HgfsGetattrFromName. - */ - if (strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0) { - LOG(4, ("%s: assigning %s default attributes\n", - __FUNCTION__, dent->d_name)); - HgfsServerGetDefaultDirAttrs(&attr); - } else { - /* Check permission on the share and get the share path */ - nameStatus = - HgfsServerPolicy_GetSharePath(dent->d_name, length, - HGFS_OPEN_MODE_READ_ONLY, - &sharePathLen, - (char const **)&sharePath); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: No such share or access denied\n", __FUNCTION__)); - free(dent); - free(search.utf8Dir); - free(search.utf8ShareName); - return HgfsConvertFromNameStatus(nameStatus); - } - - /* - * Server needs to produce list of shares that is consistent with - * the list defined in UI. If a share can't be accessed because of - * problems on the host, the server still enumerates it and - * returns to the client. - */ - status = HgfsGetattrFromName(sharePath, configOptions, dent->d_name, - &attr, NULL); - if (status != 0) { - /* - * The dent no longer exists. Log the event. - */ - - LOG(4, ("%s: stat FAILED\n", __FUNCTION__)); - } - } - - /* - * No conversion needed on OS X because dent->d_name is the shareName - * that was converted to normal form C in hgfsServerPolicyHost. - */ - entryName = dent->d_name; - entryNameLen = length; - break; - case DIRECTORY_SEARCH_TYPE_OTHER: - - /* - * The POSIX implementation of HgfsSearchOpen could not have created - * this kind of search. - */ - NOT_IMPLEMENTED(); - break; - default: - NOT_IMPLEMENTED(); - break; - } - - free(search.utf8Dir); - free(search.utf8ShareName); - LOG(4, ("%s: dent name is \"%s\" len = %"FMTSZ"u\n", __FUNCTION__, - entryName, entryNameLen)); - - /* - * We need to unescape the name before sending it back to the client - */ - entryNameLen = HgfsEscape_Undo(entryName, entryNameLen + 1); - - /* - * XXX: HgfsPackSearchReadReply will error out if the dent we - * give it is too large for the packet. Prior to - * HgfsPackSearchReadReply, we'd skip the dent and return the next - * one with success. Now we return an error. This may be a non-issue - * since what filesystems allow dent lengths as high as 6144 bytes? - */ - status = 0; - if (!HgfsPackSearchReadReply(input->packet, packetIn, status, entryName, entryNameLen, - &attr, &packetOut, &packetOutSize, session)) { - status = EPROTO; - } - - if (freeEntryName) { - free(entryName); - } - free(dent); - - if (status == 0 && - !HgfsPacketSend(input->packet, - packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); - } - return status; - } - - /* No entry at this offset */ - free(search.utf8Dir); - free(search.utf8ShareName); - LOG(4, ("%s: no entry\n", __FUNCTION__)); - status = 0; - if (!HgfsPackSearchReadReply(input->packet, packetIn, status, NULL, 0, &attr, - &packetOut, &packetOutSize, session)) { - status = EPROTO; - } - if (status == 0 && - !HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); - } - return status; + return HgfsPlatformConvertFromNameStatus(nameStatus); } /* *----------------------------------------------------------------------------- * - * HgfsServerGetattr -- + * HgfsPlatformDeleteFileByName -- + * + * POSIX specific implementation of a delete file request which accepts + * utf8 file path as a parameter. * - * Handle a Getattr request. + * Simply calls Posix_Unlink. * * Results: * Zero on success. @@ -4461,125 +3687,16 @@ HgfsServerSearchRead(HgfsInputParam *input) // IN: Input params */ HgfsInternalStatus -HgfsServerGetattr(HgfsInputParam *input) // IN: Input params +HgfsPlatformDeleteFileByName(char const *utf8Name) // IN: full file path in uf8 encoding { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - char *localName; - HgfsAttrHint hints = 0; - HgfsFileAttrInfo attr; - HgfsInternalStatus status = 0; - HgfsNameStatus nameStatus; - char *cpName; - size_t cpNameSize; - char *targetName = NULL; - uint32 targetNameLen; - HgfsHandle file = HGFS_INVALID_HANDLE; /* file handle from driver */ - uint32 caseFlags = 0; - HgfsShareOptions configOptions; - char *packetOut; - size_t packetOutSize; - size_t localNameLen; - HgfsShareInfo shareInfo; - - ASSERT(packetIn); - ASSERT(session); - - if (!HgfsUnpackGetattrRequest(packetIn, packetSize, &attr, &hints, &cpName, - &cpNameSize, &file, &caseFlags)) { - status = EPROTO; - goto exit; - } - - /* Client wants us to reuse an existing handle. */ - if (hints & HGFS_ATTR_HINT_USE_FILE_DESC) { - int fd; - status = HgfsGetFd(file, session, FALSE, &fd); - - if (status != 0) { - LOG(4, ("%s: Could not get file descriptor\n", __FUNCTION__)); - goto exit; - } - status = HgfsGetattrFromFd(fd, session, &attr); - targetNameLen = 0; - - } else { - /* - * Depending on whether this file/dir is real or virtual, either - * forge its attributes or look them up in the actual filesystem. - */ - nameStatus = HgfsServerGetShareInfo(cpName, - cpNameSize, - caseFlags, - &shareInfo, - &localName, - &localNameLen); - - switch (nameStatus) { - case HGFS_NAME_STATUS_INCOMPLETE_BASE: - /* - * This is the base of our namespace; make up fake status for - * this directory. - */ - - LOG(4, ("%s: getting attrs for base dir\n", __FUNCTION__)); - HgfsServerGetDefaultDirAttrs(&attr); - break; - - case HGFS_NAME_STATUS_COMPLETE: - /* This is a regular lookup; proceed as usual */ - ASSERT(localName); - - /* Get the config options. */ - nameStatus = HgfsServerPolicy_GetShareOptions(cpName, cpNameSize, - &configOptions); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, cpName)); - free(localName); - status = ENOENT; - goto exit; - } - - status = HgfsGetattrFromName(localName, configOptions, cpName, &attr, - &targetName); - free(localName); - if (status == 0 && - !HgfsServerPolicy_CheckMode(HGFS_OPEN_MODE_READ_ONLY, - shareInfo.writePermissions, - shareInfo.readPermissions)) { - status = EACCES; - } else if (status != 0) { - /* - * If it is a dangling share server should not return ENOENT - * to the client because it causes confusion: a name that is returned - * by directory enumeration should not produce "name not found" - * error. - * Replace it with a more appropriate error code: no such device. - */ - if (status == ENOENT && HgfsIsShareRoot(cpName, cpNameSize)) { - status = ENXIO; - } - goto exit; - } - break; - - default: - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; - } - targetNameLen = targetName ? strlen(targetName) : 0; - } - status = HgfsPackGetattrReply(input->packet, packetIn, status, &attr, targetName, - targetNameLen, &packetOut, &packetOutSize, session) ? 0 : EPROTO; - free(targetName); + HgfsInternalStatus status; - if (status == 0 && - !HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); + LOG(4, ("%s: unlinking \"%s\"\n", __FUNCTION__, utf8Name)); + status = Posix_Unlink(utf8Name); + if (status) { + status = errno; + LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(status))); } - -exit: return status; } @@ -4587,9 +3704,13 @@ exit: /* *----------------------------------------------------------------------------- * - * HgfsServerSetattr -- + * HgfsPlatformDeleteFileByHandle -- + * + * POSIX specific implementation of a delete file request which accepts + * HgfsHandle as a parameter. * - * Handle a Setattr request. + * File handle must have appropriate access mode to allow file deletion. + * Shared folder restrictions are enforced here as well. * * Results: * Zero on success. @@ -4602,62 +3723,40 @@ exit: */ HgfsInternalStatus -HgfsServerSetattr(HgfsInputParam *input) // IN: Input params +HgfsPlatformDeleteFileByHandle(HgfsHandle file, // IN: File being deleted + HgfsSessionInfo *session) // IN: session info { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; HgfsInternalStatus status; - HgfsFileAttrInfo attr; - char *cpName; - size_t cpNameSize = 0; - HgfsAttrHint hints = 0; - HgfsHandle file = HGFS_INVALID_HANDLE; - uint32 caseFlags = 0; - char *packetOut; - size_t packetOutSize; - - ASSERT(packetIn); - ASSERT(session); - - LOG(4, ("NEW LIB! HgfsServerSetAttr Called!\n")); - - if (!HgfsUnpackSetattrRequest(packetIn, packetSize, &attr, &hints, - &cpName, &cpNameSize, &file, &caseFlags)) { - status = EPROTO; - goto exit; - } + Bool readPermissions; + Bool writePermissions; + char *localName; + size_t localNameSize; - if (file != HGFS_INVALID_HANDLE) { - status = HgfsSetattrFromFd(file, session, &attr, hints); + if (!HgfsHandle2FileNameMode(file, session, &writePermissions, + &readPermissions, &localName, &localNameSize)) { + if (writePermissions && readPermissions) { + status = HgfsPlatformDeleteFileByName(localName); + } else { + status = EPERM; + } + free(localName); } else { - status = HgfsSetattrFromName(cpName, cpNameSize, &attr, hints, - caseFlags, session); - } - if (!HgfsPackSetattrReply(input->packet, packetIn, status, attr.requestType, - &packetOut, &packetOutSize, session)) { - status = EPROTO; - goto exit; - } - if (!HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); - /* We can't send the packet, ignore the error if any. */ + LOG(4, ("%s: could not map cached file handle %u\n", __FUNCTION__, file)); + status = EBADF; } - status = 0; - -exit: return status; } + /* *----------------------------------------------------------------------------- * - * HgfsServerCreateDir -- + * HgfsPlatformDeleteDirByName -- * - * Handle a CreateDir request. + * POSIX specific implementation of a delete directory request which accepts + * utf8 file path as a parameter. * - * Simply converts to the local filename, calls mkdir on the - * file, and returns an appropriate response to the driver. + * Simply calls Posix_Rmdir. * * Results: * Zero on success. @@ -4670,120 +3769,30 @@ exit: */ HgfsInternalStatus -HgfsServerCreateDir(HgfsInputParam *input) // IN: Input params +HgfsPlatformDeleteDirByName(char const *utf8Name) // IN: full file path in uf8 encoding { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsNameStatus nameStatus; - HgfsCreateDirInfo info; - char *localName; - int error; - mode_t permissions; - char *packetOut; - size_t packetOutSize; - size_t localNameLen; - HgfsShareInfo shareInfo; - - ASSERT(packetIn); - ASSERT(session); - - if (!HgfsUnpackCreateDirRequest(packetIn, packetSize, &info)) { - return EPROTO; - } - - nameStatus = HgfsServerGetShareInfo(info.cpName, - info.cpNameSize, - info.caseFlags, - &shareInfo, - &localName, - &localNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: access check failed\n", __FUNCTION__)); - - return HgfsConvertFromNameStatus(nameStatus); - } - - ASSERT(localName); - - /* - * For read-only shares we must never attempt to create a directory. - * However the error code must be different depending on the existence - * of the file or directory with the same name. - */ - if (!shareInfo.writePermissions) { - int error = HgfsAccess(localName, info.cpName, info.cpNameSize); - if (error != 0) { - if (error == ENOENT) { - error = EACCES; - } - } else { - error = EEXIST; - } - LOG(4, ("HgfsServerCreateDir: failed access check, error %d\n", error)); - free(localName); - return error; - } - - /* - * Create mode_t for use in mkdir(). If owner permissions are missing, use - * read/write/execute for the owner permissions. If group or other - * permissions are missing, use the owner permissions. - * - * This sort of makes sense. If the Windows driver wants to make a dir - * read-only, it probably intended for the dir to be 666. Since creating - * a directory requires a valid mode, it's highly unlikely that we'll ever - * be creating a directory without owner permissions. - */ - permissions = ~ALLPERMS; - permissions |= info.mask & HGFS_CREATE_DIR_VALID_SPECIAL_PERMS ? - info.specialPerms << 9 : 0; - permissions |= info.mask & HGFS_CREATE_DIR_VALID_OWNER_PERMS ? - info.ownerPerms << 6 : S_IRWXU; - permissions |= info.mask & HGFS_CREATE_DIR_VALID_GROUP_PERMS ? - info.groupPerms << 3 : (permissions & S_IRWXU) >> 3; - permissions |= info.mask & HGFS_CREATE_DIR_VALID_OTHER_PERMS ? - info.otherPerms : (permissions & S_IRWXU) >> 6; - - LOG(4, ("%s: making dir \"%s\", mode %"FMTMODE"\n", __FUNCTION__, - localName, permissions)); - - error = Posix_Mkdir(localName, permissions); - if ((info.mask & HGFS_CREATE_DIR_VALID_FILE_ATTR) && - (info.fileAttr & HGFS_ATTR_HIDDEN)) { - /* - * Set hidden attribute when requested. - * Do not fail directory creation if setting hidden attribute fails. - */ - HgfsSetHiddenXAttr(localName, TRUE); - } + HgfsInternalStatus status; - free(localName); - if (error) { - error = errno; - LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(error))); - return error; - } - if (!HgfsPackCreateDirReply(input->packet, packetIn, 0, info.requestType, - &packetOut, &packetOutSize, session)) { - return EPROTO; - } - if (!HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); + LOG(4, ("%s: removing \"%s\"\n", __FUNCTION__, utf8Name)); + status = Posix_Rmdir(utf8Name); + if (status) { + status = errno; + LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(status))); } - return 0; + return status; } /* *----------------------------------------------------------------------------- * - * HgfsServerDeleteFile -- + * HgfsPlatformDeleteDirByHandle -- * - * Handle a Delete File request. + * POSIX specific implementation of a Delete directory request which accepts + * HgfsHandle as a parameter. * - * Simply converts to the local filename, calls unlink on the - * file, and returns an appropriate response to the driver. + * File handle must have appropriate access mode to allow file deletion. + * Shared folder restrictions are enforced here as well. * * Results: * Zero on success. @@ -4796,106 +3805,41 @@ HgfsServerCreateDir(HgfsInputParam *input) // IN: Input params */ HgfsInternalStatus -HgfsServerDeleteFile(HgfsInputParam *input) // IN: Input params +HgfsPlatformDeleteDirByHandle(HgfsHandle file, // IN: File being deleted + HgfsSessionInfo *session) // IN: session info { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsNameStatus nameStatus; + HgfsInternalStatus status; + Bool readPermissions; + Bool writePermissions; char *localName; - int error; - HgfsHandle file = HGFS_INVALID_HANDLE; - HgfsDeleteHint hints = 0; - char *cpName; - size_t cpNameSize; - uint32 caseFlags; - char *packetOut; - size_t packetOutSize; - size_t localNameLen; - HgfsShareInfo shareInfo; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(session); + size_t localNameSize; - if (!HgfsUnpackDeleteRequest(packetIn, packetSize, &op, &cpName, &cpNameSize, - &hints, &file, &caseFlags)) { - return EPROTO; - } - - if (hints & HGFS_DELETE_HINT_USE_FILE_DESC) { - if (!HgfsHandle2FileNameMode(file, session, &shareInfo.writePermissions, - &shareInfo.readPermissions, &cpName, &cpNameSize)) { - LOG(4, ("%s: could not map cached file handle %u\n", __FUNCTION__, - file)); - return EBADF; - } - localName = cpName; - - } else { - nameStatus = HgfsServerGetShareInfo(cpName, - cpNameSize, - caseFlags, - &shareInfo, - &localName, - &localNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: access check failed\n", __FUNCTION__)); - - return HgfsConvertFromNameStatus(nameStatus); - } - } - - ASSERT(localName); - - /* - * Deleting a file needs both read and write permssions. - * However the error code must be different depending on the existence - * of the file with the same name. - */ - if (!shareInfo.writePermissions || !shareInfo.readPermissions) { - int error = HgfsAccess(localName, cpName, cpNameSize); - if (error == 0) { - error = EACCES; + if (!HgfsHandle2FileNameMode(file, session, &writePermissions, + &readPermissions, &localName, &localNameSize)) { + if (writePermissions && readPermissions) { + status = HgfsPlatformDeleteDirByName(localName); + } else { + status = EPERM; } - LOG(4, ("HgfsServerDeleteFile: failed access check, error %d\n", error)); free(localName); - return error; - } - - LOG(4, ("%s: unlinking \"%s\"\n", __FUNCTION__, localName)); - error = Posix_Unlink(localName); - free(localName); - if (error) { - error = errno; - LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(error))); - - return error; - } - if (!HgfsPackDeleteReply(input->packet, packetIn, 0, op, &packetOut, - &packetOutSize, session)) { - return EPROTO; - } - if (!HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); + } else { + LOG(4, ("%s: could not map cached file handle %u\n", __FUNCTION__, file)); + status = EBADF; } - return 0; + return status; } /* *----------------------------------------------------------------------------- * - * HgfsServerDeleteDir -- - * - * Handle a Delete Dir request. + * HgfsPlatformFileExists -- * - * Simply converts to the local filename, calls rmdir on the - * file, and returns an appropriate response to the driver. + * Platform specific function that that verifies if a file or directory exists. * * Results: - * Zero on success. - * Non-zero on failure. + * 0 if user has permissions to traverse the parent directory and + * the file exists, POSIX error code otherwise. * * Side effects: * None @@ -4904,322 +3848,54 @@ HgfsServerDeleteFile(HgfsInputParam *input) // IN: Input params */ HgfsInternalStatus -HgfsServerDeleteDir(HgfsInputParam *input) // IN: Input params +HgfsPlatformFileExists(char *localTargetName) // IN: Full file path utf8 encoding { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsNameStatus nameStatus; - char *localName = NULL; - int error; - HgfsHandle file = HGFS_INVALID_HANDLE; - HgfsDeleteHint hints = 0; - char *cpName; - size_t cpNameSize; - uint32 caseFlags; - char *packetOut; - size_t packetOutSize; - size_t localNameLen; - HgfsShareInfo shareInfo; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(session); - - if (!HgfsUnpackDeleteRequest(packetIn, packetSize, &op, &cpName, &cpNameSize, - &hints, &file, &caseFlags)) { - return EPROTO; - } - - if (hints & HGFS_DELETE_HINT_USE_FILE_DESC) { - if (!HgfsHandle2FileNameMode(file, session, &shareInfo.writePermissions, - &shareInfo.readPermissions, &cpName, &cpNameSize)) { - LOG(4, ("%s: could not map cached file handle %u\n", __FUNCTION__, - file)); - return EBADF; - } - localName = cpName; - - } else { - nameStatus = HgfsServerGetShareInfo(cpName, - cpNameSize, - caseFlags, - &shareInfo, - &localName, - &localNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: access check failed\n", __FUNCTION__)); - - return HgfsConvertFromNameStatus(nameStatus); - } - } - - /* Guest OS is not allowed to delete shared folder. */ - if (HgfsServerIsSharedFolderOnly(cpName, cpNameSize)){ - LOG(4, ("%s: Cannot delete shared folder\n", __FUNCTION__)); - free(localName); - return EPERM; - } - - /* - * Deleting a directory needs both read and write permssions. - * However the error code must be different depending on the existence - * of the file with the same name. - */ - if (!shareInfo.writePermissions || !shareInfo.readPermissions) { - int error = HgfsAccess(localName, cpName, cpNameSize); - if (error == 0) { - error = EACCES; - } - LOG(4, ("%s: : failed access check, error %d\n", __FUNCTION__, error)); - free(localName); - return error; - } - - ASSERT(localName); - - LOG(4, ("%s: removing \"%s\"\n", __FUNCTION__, localName)); - error = Posix_Rmdir(localName); - free(localName); - if (error) { - error = errno; - LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(error))); - - return error; - } - - if (!HgfsPackDeleteReply(input->packet, packetIn, 0, op, &packetOut, - &packetOutSize, session)) { - return EPROTO; - } - if (!HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); - } - return 0; + return Posix_Access(localTargetName, F_OK); } /* *----------------------------------------------------------------------------- * - * HgfsServerRename -- + * HgfsPlatformRename -- * - * Handle a Rename request. - * - * Simply converts the new and old names to local filenames, calls - * rename(2), and returns an appropriate response to the driver. + * POSIX version of the function that renames a file or directory. * * Results: - * Zero on success. - * Non-zero on failure. - * - * Side effects: - * None + * 0 on success, POSIX error code otherwise. * - *----------------------------------------------------------------------------- - */ - -HgfsInternalStatus -HgfsServerRename(HgfsInputParam *input) // IN: Input params -{ - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsNameStatus nameStatus; - char *localOldName = NULL; - size_t localOldNameLen; - char *localNewName = NULL; - size_t localNewNameLen; - char *cpOldName; - size_t cpOldNameLen; - char *cpNewName; - size_t cpNewNameLen; - HgfsHandle srcFile = HGFS_INVALID_HANDLE; - HgfsHandle targetFile = HGFS_INVALID_HANDLE; - HgfsRenameHint hints = 0; - int error; - HgfsInternalStatus status; - Bool sharedFolderOpen = FALSE; - uint32 oldCaseFlags = 0; - uint32 newCaseFlags = 0; - char *packetOut; - size_t packetOutSize; - HgfsShareInfo shareInfo; - HgfsOp op; - - ASSERT(packetIn); - ASSERT(session); - - if (!HgfsUnpackRenameRequest(packetIn, packetSize, &op, &cpOldName, &cpOldNameLen, - &cpNewName, &cpNewNameLen, &hints, &srcFile, - &targetFile, &oldCaseFlags, &newCaseFlags)) { - return EPROTO; - } - - if (hints & HGFS_RENAME_HINT_USE_SRCFILE_DESC) { - size_t localOldNameLen; - - if (!HgfsHandle2FileNameMode(srcFile, session, &shareInfo.writePermissions, - &shareInfo.readPermissions, &localOldName, - &localOldNameLen)) { - LOG(4, ("%s: could not map cached source file handle %u\n", - __FUNCTION__, srcFile)); - return EBADF; - } - - /* Guest OS is not allowed to rename shared folder. */ - if (HgfsHandleIsSharedFolderOpen(srcFile, session, &sharedFolderOpen) && - sharedFolderOpen) { - LOG(4, ("%s: Cannot rename shared folder\n", __FUNCTION__)); - status = EPERM; - goto exit; - } - - } else { - - nameStatus = HgfsServerGetShareInfo(cpOldName, - cpOldNameLen, - oldCaseFlags, - &shareInfo, - &localOldName, - &localOldNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - return HgfsConvertFromNameStatus(nameStatus); - } - - ASSERT(localOldName); - - /* Guest OS is not allowed to rename shared folder. */ - if (HgfsServerIsSharedFolderOnly(cpOldName, cpOldNameLen)) { - LOG(4, ("%s: Cannot rename shared folder\n", __FUNCTION__)); - status = EPERM; - goto exit; - } - - /* - * Renaming a file requires both read and write permissions for the original file. - * However the error code must be different depending on the existence - * of the file with the same name. - */ - if (!shareInfo.writePermissions || !shareInfo.readPermissions) { - status = HgfsAccess(localOldName, cpOldName, cpOldNameLen); - if (status == 0) { - status = EACCES; - } - LOG(4, ("HgfsServerRename: failed access check, error %d\n", status)); - goto exit; - } - } - - if (hints & HGFS_RENAME_HINT_USE_TARGETFILE_DESC) { - size_t localNewNameLen; - - if (!HgfsHandle2FileNameMode(targetFile, session, &shareInfo.writePermissions, - &shareInfo.readPermissions, &localNewName, - &localNewNameLen)) { - LOG(4, ("%s: could not map cached target file handle %u\n", - __FUNCTION__, targetFile)); - status = EBADF; - goto exit; - } - - /* Guest OS is not allowed to rename shared folder. */ - if (HgfsHandleIsSharedFolderOpen(targetFile, session, &sharedFolderOpen) && - sharedFolderOpen) { - LOG(4, ("%s: Cannot rename shared folder\n", __FUNCTION__)); - status = EPERM; - goto exit; - } - - } else { - - nameStatus = HgfsServerGetShareInfo(cpNewName, - cpNewNameLen, - newCaseFlags, - &shareInfo, - &localNewName, - &localNewNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: new name access check failed\n", __FUNCTION__)); - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; - } - } + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ - ASSERT(localNewName); +HgfsInternalStatus +HgfsPlatformRename(char *localSrcName, // IN: local path to source file + fileDesc srcFile, // IN: source file handle + char *localTargetName, // IN: local path to target file + fileDesc targetFile, // IN: target file handle + HgfsRenameHint hints) // IN: rename hints +{ + HgfsInternalStatus status = 0; + int error; if (hints & HGFS_RENAME_HINT_NO_REPLACE_EXISTING) { - HgfsFileAttrInfo attr; - HgfsShareOptions configOptions; - - /* Get the config options. */ - nameStatus = HgfsServerPolicy_GetShareOptions(cpNewName, cpNewNameLen, - &configOptions); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, cpNewName)); - status = ENOENT; - goto exit; - } - - /* - * We were asked to avoid replacing an existing file, - * so fail if the target exists. - */ - - status = HgfsGetattrFromName(localNewName, configOptions, cpNewName, - &attr, NULL); - if (status == 0) { - /* The target exists, and so must fail the rename. */ - LOG(4, ("%s: error: target %s exists\n", __FUNCTION__, localNewName)); + status = HgfsPlatformFileExists(localTargetName); + if (0 == status) { status = EEXIST; - goto exit; } } - /* - * Renaming a file requires both read and write permssions for the target file. - * However the error code must be different depending on the existence - * of the file with the same name. - */ - if (!shareInfo.writePermissions || !shareInfo.readPermissions) { - status = HgfsAccess(localNewName, cpNewName, cpNewNameLen); - if (status == 0 || status == ENOENT) { - status = EACCES; + if (0 == status) { + LOG(4, ("%s: renaming \"%s\" to \"%s\"\n", __FUNCTION__, + localSrcName, localTargetName)); + error = Posix_Rename(localSrcName, localTargetName); + if (error) { + status = errno; + LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(status))); } - LOG(4, ("HgfsServerRename: failed access check, error %d\n", status)); - goto exit; - } - - LOG(4, ("%s: renaming \"%s\" to \"%s\"\n", __FUNCTION__, - localOldName, localNewName)); - error = Posix_Rename(localOldName, localNewName); - if (error) { - error = errno; - LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(error))); - status = error; - goto exit; - } - - /* - * Update all file nodes referring to this filename to the new name. - * - * XXX: Note that this operation can fail (out of memory), but we'd like - * the client to see success anyway, because the rename succeeded. - */ - status = 0; - HgfsUpdateNodeNames(localOldName, localNewName, session); - if (!HgfsPackRenameReply(input->packet, packetIn, status, op, &packetOut, - &packetOutSize, session)) { - status = EPROTO; - goto exit; - } - if (!HgfsPacketSend(input->packet, packetOut, packetOutSize, session, 0)) { - HSPU_PutReplyPacket(input->packet, session); } - - exit: - free(localOldName); - free(localNewName); return status; } @@ -5227,17 +3903,13 @@ HgfsServerRename(HgfsInputParam *input) // IN: Input params /* *----------------------------------------------------------------------------- * - * HgfsServerQueryVolume -- + * HgfsPlatformCreateDir -- * - * Handle a Query Volume request. + * POSIX specific code that implements create directory request. * - * Right now we only handle the volume space request. Call Wiper library - * to get the volume information. - * It is possible that shared folders can belong to different volumes on - * the server. If this is the case, default to return the space information - * of the volume that has the least amount of the available space, but it's - * configurable with a config option (tools.hgfs.volumeInfoType). 2 possible - * options, min and max. + * It invokes POSIX to create the directory and then assigns + * file attributes to the new directory if attributes are specified + * by the guest. * * Results: * Zero on success. @@ -5250,282 +3922,59 @@ HgfsServerRename(HgfsInputParam *input) // IN: Input params */ HgfsInternalStatus -HgfsServerQueryVolume(HgfsInputParam *input) // IN: Input params +HgfsPlatformCreateDir(HgfsCreateDirInfo *info, // IN: direcotry properties + char *utf8Name) // IN: full path for the new directory { - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsRequest *header; - uint32 extra; - char *utf8Name = NULL; - size_t utf8NameLen; - HgfsHandle handle; - int offset = 0; - uint64 outFreeBytes = 0; - uint64 outTotalBytes = 0; - VolumeInfoType infoType; - DirectoryEntry *dent; + mode_t permissions; HgfsInternalStatus status; - Bool firstShare = TRUE; - Bool success; - HgfsNameStatus nameStatus; - size_t failed = 0; - size_t shares = 0; - HgfsInternalStatus firstErr = 0; - char *fileName; - uint32 fileNameLength; - uint32 caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - uint64 *freeBytes; - uint64 *totalBytes; - char *packetOut; - size_t packetOutSize; - size_t replyPacketSize; - HgfsShareInfo shareInfo; - - ASSERT(packetIn); - ASSERT(session); - - header = (HgfsRequest *)packetIn; - - if (header->op == HGFS_OP_QUERY_VOLUME_INFO_V3) { - HgfsRequestQueryVolumeV3 *requestV3 = - (HgfsRequestQueryVolumeV3 *)HGFS_REQ_GET_PAYLOAD_V3(packetIn); - HgfsReplyQueryVolumeV3 *replyV3; - - packetOutSize = HGFS_REP_PAYLOAD_SIZE_V3(replyV3); - replyPacketSize = packetOutSize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, packetOutSize, replyPacketSize, status, exit); - - replyV3 = (HgfsReplyQueryVolumeV3 *)HGFS_REP_GET_PAYLOAD_V3(packetOut); - - /* - * We don't yet support file handle for this operation. - * Clients should retry using the file name. - */ - if (requestV3->fileName.flags & HGFS_FILE_NAME_USE_FILE_DESC) { - LOG(4, ("%s: Doesn't support file handle.\n", __FUNCTION__)); - status = EPARAMETERNOTSUPPORTED; - goto exit; - } - - freeBytes = &replyV3->freeBytes; - totalBytes = &replyV3->totalBytes; - replyV3->reserved = 0; - - /* Enforced by the dispatch function. */ - ASSERT(packetSize >= HGFS_REQ_PAYLOAD_SIZE_V3(requestV3)); - extra = packetSize - HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); - - caseFlags = requestV3->fileName.caseType; - fileName = requestV3->fileName.name; - fileNameLength = requestV3->fileName.length; - LOG(4, ("%s: HGFS_OP_QUERY_VOLUME_INFO_V3\n", __FUNCTION__)); - } else { - HgfsRequestQueryVolume *request = (HgfsRequestQueryVolume *)packetIn; - HgfsReplyQueryVolume *reply; - - packetOutSize = sizeof *reply; - replyPacketSize = packetOutSize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, packetOutSize, replyPacketSize, status, exit); - - reply = (HgfsReplyQueryVolume *)packetOut; - - freeBytes = &reply->freeBytes; - totalBytes = &reply->totalBytes; - - /* Enforced by the dispatch function. */ - ASSERT(packetSize >= sizeof *request); - extra = packetSize - sizeof *request; - fileName = request->fileName.name; - fileNameLength = request->fileName.length; - } /* - * request->fileName.length is user-provided, so this test must be carefully - * written to prevent wraparounds. + * Create mode_t for use in mkdir(). If owner permissions are missing, use + * read/write/execute for the owner permissions. If group or other + * permissions are missing, use the owner permissions. + * + * This sort of makes sense. If the Windows driver wants to make a dir + * read-only, it probably intended for the dir to be 666. Since creating + * a directory requires a valid mode, it's highly unlikely that we'll ever + * be creating a directory without owner permissions. */ - if (fileNameLength > extra) { - /* The input packet is smaller than the request. */ - status = EPROTO; - goto exit; - } - - /* It is now safe to read the file name field. */ - nameStatus = HgfsServerGetShareInfo(fileName, - fileNameLength, - caseFlags, - &shareInfo, - &utf8Name, - &utf8NameLen); - switch (nameStatus) { - case HGFS_NAME_STATUS_INCOMPLETE_BASE: - /* - * This is the base of our namespace. Clients can request a - * QueryVolumeInfo on it, on individual shares, or on just about - * any pathname. - */ - - LOG(4,("%s: opened search on base\n", __FUNCTION__)); - status = HgfsServerSearchVirtualDir(HgfsServerPolicy_GetShares, - HgfsServerPolicy_GetSharesInit, - HgfsServerPolicy_GetSharesCleanup, - DIRECTORY_SEARCH_TYPE_BASE, - session, - &handle); - if (status != 0) { - goto exit; - } + permissions = ~ALLPERMS; + permissions |= info->mask & HGFS_CREATE_DIR_VALID_SPECIAL_PERMS ? + info->specialPerms << 9 : 0; + permissions |= info->mask & HGFS_CREATE_DIR_VALID_OWNER_PERMS ? + info->ownerPerms << 6 : S_IRWXU; + permissions |= info->mask & HGFS_CREATE_DIR_VALID_GROUP_PERMS ? + info->groupPerms << 3 : (permissions & S_IRWXU) >> 3; + permissions |= info->mask & HGFS_CREATE_DIR_VALID_OTHER_PERMS ? + info->otherPerms : (permissions & S_IRWXU) >> 6; - /* - * If we're outside the Tools, find out if we're to compute the minimum - * values across all shares, or the maximum values. - */ - infoType = VOLUME_INFO_TYPE_MIN; -#ifndef VMX86_TOOLS - { - char *volumeInfoType = Config_GetString("min", - "tools.hgfs.volumeInfoType"); - if (!Str_Strcasecmp(volumeInfoType, "max")) { - infoType = VOLUME_INFO_TYPE_MAX; - } - free(volumeInfoType); - } -#endif + LOG(4, ("%s: making dir \"%s\", mode %"FMTMODE"\n", __FUNCTION__, + utf8Name, permissions)); + status = Posix_Mkdir(utf8Name, permissions); + if ((info->mask & HGFS_CREATE_DIR_VALID_FILE_ATTR) && + (info->fileAttr & HGFS_ATTR_HIDDEN) && 0 == status) { /* - * Now go through all shares and get share paths on the server. - * Then retrieve space info for each share's volume. + * Set hidden attribute when requested. + * Do not fail directory creation if setting hidden attribute fails. */ - offset = 0; - while ((dent = HgfsGetSearchResult(handle, session, offset, - TRUE)) != NULL) { - char const *sharePath; - size_t sharePathLen; - uint64 freeBytes = 0; - uint64 totalBytes = 0; - unsigned int length; - - length = strlen(dent->d_name); - - /* - * Now that the server is passing '.' and ".." around as dents, we - * need to make sure to handle them properly. In particular, they - * should be ignored within QueryVolume, as they're not real shares. - */ - if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) { - LOG(4, ("%s: Skipping fake share %s\n", __FUNCTION__, - dent->d_name)); - free(dent); - continue; - } - - /* - * The above check ignores '.' and '..' so we do not include them in - * the share count here. - */ - shares++; - - /* - * Check permission on the share and get the share path. It is not - * fatal if these do not succeed. Instead we ignore the failures - * (apart from logging them) until we have processed all shares. Only - * then do we check if there were any failures; if all shares failed - * to process then we bail out with an error code. - */ - - nameStatus = HgfsServerPolicy_GetSharePath(dent->d_name, length, - HGFS_OPEN_MODE_READ_ONLY, - &sharePathLen, - &sharePath); - free(dent); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: No such share or access denied\n", __FUNCTION__)); - if (0 == firstErr) { - firstErr = HgfsConvertFromNameStatus(nameStatus); - } - failed++; - continue; - } - - if (!HgfsServerStatFs(sharePath, sharePathLen, - &freeBytes, &totalBytes)) { - LOG(4, ("%s: error getting volume information\n", - __FUNCTION__)); - if (0 == firstErr) { - firstErr = EIO; - } - failed++; - continue; - } - - /* - * Pick the drive with amount of space available and return that - * according to different volume info type. - */ - switch (infoType) { - case VOLUME_INFO_TYPE_MIN: - if ((outFreeBytes > freeBytes) || firstShare) { - firstShare = FALSE; - outFreeBytes = freeBytes; - outTotalBytes = totalBytes; - } - break; - case VOLUME_INFO_TYPE_MAX: - if ((outFreeBytes < freeBytes)) { - outFreeBytes = freeBytes; - outTotalBytes = totalBytes; - } - break; - default: - NOT_IMPLEMENTED(); - } - } - if (!HgfsRemoveSearch(handle, session)) { - LOG(4, ("%s: could not close search on base\n", __FUNCTION__)); - } - break; - case HGFS_NAME_STATUS_COMPLETE: - ASSERT(utf8Name); - LOG(4,("%s: querying path %s\n", __FUNCTION__, utf8Name)); - success = HgfsServerStatFs(utf8Name, utf8NameLen, - &outFreeBytes, &outTotalBytes); - free(utf8Name); - if (!success) { - LOG(4, ("%s: error getting volume information\n", __FUNCTION__)); - status = EIO; - goto exit; - } - break; - default: - LOG(4,("%s: file access check failed\n", __FUNCTION__)); - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; + HgfsSetHiddenXAttr(utf8Name, TRUE); } - *freeBytes = outFreeBytes; - *totalBytes = outTotalBytes; - status = 0; - - if (!HgfsPackAndSendPacket(input->packet, packetOut, packetOutSize, - status, header->id, session, 0)) { - goto exit; + if (status) { + status = errno; + LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(status))); } return status; - -exit: - HSPU_PutReplyPacket(input->packet, session); - return status; } /* *----------------------------------------------------------------------------- * - * HgfsServerSymlinkCreate -- + * HgfsPlatformSymlinkCreate -- * - * Handle a SymlinkCreate request. + * Platform specific function that actually creates the symbolic link. * * Results: * Zero on success. @@ -5538,202 +3987,18 @@ exit: */ HgfsInternalStatus -HgfsServerSymlinkCreate(HgfsInputParam *input) // IN: Input params -{ - const char *packetIn = input->metaPacket; - size_t packetSize = input->metaPacketSize; - HgfsSessionInfo *session = input->session; - HgfsRequest *header; - uint32 extra; - char *localSymlinkName = NULL; - char localTargetName[HGFS_PACKET_MAX]; - int error; - HgfsNameStatus nameStatus; - uint32 caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; - char *symlinkName; - uint32 symlinkNameLength; - char *targetName; - uint32 targetNameLength; - HgfsShareOptions configOptions; - char *packetOut = NULL; - size_t packetOutSize; - size_t replyPacketSize; +HgfsPlatformSymlinkCreate(char *localSymlinkName, // IN: symbolic link file name + char *localTargetName) // IN: symlink target name +{ HgfsInternalStatus status = 0; - size_t localSymlinkNameLen; - HgfsShareInfo shareInfo; - - ASSERT(packetIn); - ASSERT(session); - header = (HgfsRequest *)packetIn; - - if (header->op == HGFS_OP_CREATE_SYMLINK_V3) { - HgfsRequestSymlinkCreateV3 *requestV3; - HgfsReplySymlinkCreateV3 *replyV3; - HgfsFileNameV3 *targetNameP; - requestV3 = (HgfsRequestSymlinkCreateV3 *)HGFS_REQ_GET_PAYLOAD_V3(packetIn); - LOG(4, ("%s: HGFS_OP_CREATE_SYMLINK_V3\n", __FUNCTION__)); - - /* Enforced by the dispatch function. */ - ASSERT(packetSize >= HGFS_REQ_PAYLOAD_SIZE_V3(requestV3)); - extra = packetSize - HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); - - caseFlags = requestV3->symlinkName.caseType; - symlinkName = requestV3->symlinkName.name; - symlinkNameLength = requestV3->symlinkName.length; - - /* - * targetName starts after symlinkName + the variable length array - * in symlinkName. - */ - targetNameP = (HgfsFileNameV3 *)(symlinkName + 1 + symlinkNameLength); - targetName = targetNameP->name; - targetNameLength = targetNameP->length; - - /* - * We don't yet support file handle for this operation. - * Clients should retry using the file name. - */ - if (requestV3->symlinkName.flags & HGFS_FILE_NAME_USE_FILE_DESC || - targetNameP->flags & HGFS_FILE_NAME_USE_FILE_DESC) { - LOG(4, ("%s: Doesn't support file handle.\n", __FUNCTION__)); - return EPARAMETERNOTSUPPORTED; - } - - packetOutSize = HGFS_REP_PAYLOAD_SIZE_V3(replyV3); - replyPacketSize = packetOutSize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, packetOutSize, replyPacketSize, status, exit); - - replyV3 = (HgfsReplySymlinkCreateV3 *)HGFS_REP_GET_PAYLOAD_V3(packetOut); - replyV3->reserved = 0; - - } else { - HgfsRequestSymlinkCreate *request; - HgfsFileName *targetNameP; - request = (HgfsRequestSymlinkCreate *)packetIn; - - /* Enforced by the dispatch function. */ - ASSERT(packetSize >= sizeof *request); - extra = packetSize - sizeof *request; - - symlinkName = request->symlinkName.name; - symlinkNameLength = request->symlinkName.length; - - /* - * targetName starts after symlinkName + the variable length array - * in symlinkName. - */ - - targetNameP = (HgfsFileName *)(symlinkName + 1 + symlinkNameLength); - targetName = targetNameP->name; - targetNameLength = targetNameP->length; - packetOutSize = sizeof(struct HgfsReplySymlinkCreate); - replyPacketSize = packetOutSize; - packetOut = HSPU_GetReplyPacket(input->packet, &replyPacketSize, session); - HGFS_REPLYPKT_CHECK(packetOut, packetOutSize, replyPacketSize, status, exit); - } - - /* - * request->symlinkName.length is user-provided, so this test must - * be carefully written to prevent wraparounds. - */ - - if (symlinkNameLength > extra) { - /* The input packet is smaller than the request */ - status = EPROTO; - goto exit; - } - - /* - * It is now safe to read the symlink file name and the - * "targetName" field - */ - - nameStatus = HgfsServerGetShareInfo(symlinkName, - symlinkNameLength, - caseFlags, - &shareInfo, - &localSymlinkName, - &localSymlinkNameLen); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: symlink name access check failed\n", - __FUNCTION__)); - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; - } - - if (!shareInfo.writePermissions ) { - status = HgfsAccess(localSymlinkName, symlinkName, symlinkNameLength); - if (status != 0) { - if (status == ENOENT) { - status = EACCES; - } - } else { - status = EEXIST; - } - LOG(4, ("HgfsServerCreateDir: failed access check, error %d\n", status)); - goto exit; - } - - ASSERT(localSymlinkName); - extra -= symlinkNameLength; - - /* - * targetNameLength is user-provided, so this test must be carefully - * written to prevent wraparounds. - */ - if (targetNameLength > extra) { - /* The input packet is smaller than the request */ - status = EPROTO; - goto exit; - } - - /* It is now safe to read the target file name */ - - /* Get the config options. */ - nameStatus = HgfsServerPolicy_GetShareOptions(symlinkName, symlinkNameLength, - &configOptions); - if (nameStatus != HGFS_NAME_STATUS_COMPLETE) { - LOG(4, ("%s: no matching share: %s.\n", __FUNCTION__, symlinkName)); - status = HgfsConvertFromNameStatus(nameStatus); - goto exit; - } - - /* Convert from CPName-lite to normal and NUL-terminate. */ - memcpy(localTargetName, targetName, targetNameLength); - CPNameLite_ConvertFrom(localTargetName, targetNameLength, DIRSEPC); - localTargetName[targetNameLength] = '\0'; - - /* Prohibit symlink ceation if symlink following is enabled. */ - if (HgfsServerPolicy_IsShareOptionSet(configOptions, - HGFS_SHARE_FOLLOW_SYMLINKS)) { - status = EPERM; - goto exit; - } - - LOG(4, ("%s: creating \"%s\" linked to \"%s\"\n", __FUNCTION__, - localSymlinkName, localTargetName)); + int error; /* XXX: Should make use of targetNameP->flags? */ error = Posix_Symlink(localTargetName, localSymlinkName); if (error) { status = errno; LOG(4, ("%s: error: %s\n", __FUNCTION__, strerror(errno))); - goto exit; - } - - status = 0; - if (!HgfsPackAndSendPacket(input->packet, packetOut, packetOutSize, - status, header->id, - session, 0)) { - goto exit; } - free(localSymlinkName); - return status; - -exit: - free(localSymlinkName); - HSPU_PutReplyPacket(input->packet, session); return status; } @@ -5890,32 +4155,11 @@ exit: */ HgfsInternalStatus -HgfsServerWriteWin32Stream(HgfsInputParam *input) // IN: Input params -{ - return EOPNOTSUPP; -} - - -/* - *----------------------------------------------------------------------------- - * - * HgfsServerServerLockChange -- - * - * Called by the client when it wants to either acquire an oplock on a file - * that was previously opened, or when it wants to release/downgrade an - * oplock on a file that was previously oplocked. - * - * Results: - * EOPNOTSUPP, because this is unimplemented. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -HgfsInternalStatus -HgfsServerServerLockChange(HgfsInputParam *input) // IN: Input params +HgfsServerWriteWin32Stream(char const *packetIn, // IN: incoming packet + HgfsOp op, // IN: request type + const void *payload, // IN: HGFS operational packet (without header) + size_t payloadSize, // IN: size of HGFS operational packet + HgfsSessionInfo *session) // IN: session info { return EOPNOTSUPP; } @@ -5981,39 +4225,6 @@ HgfsAckOplockBreak(ServerLockData *lockData, // IN: server lock info } #endif - -/* - *----------------------------------------------------------------------------- - * - * HgfsIsShareRoot -- - * - * Checks if the cpName represents the root directory for a share. - * Components in CPName format are separated by NUL characters. - * CPName for the root of a share contains only one component thus - * it does not have any embedded '\0' characters in the name. - * - * Results: - * TRUE if it is the root directory, FALSE otherwise. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static Bool -HgfsIsShareRoot(char const *cpName, // IN: name to test - size_t cpNameSize) // IN: length of the name -{ - size_t i; - for (i = 0; i < cpNameSize; i++) { - if (cpName[i] == '\0') { - return FALSE; - } - } - return TRUE; -} - #if defined(__APPLE__) /* *----------------------------------------------------------------------------- diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerPacketUtil.c b/open-vm-tools/lib/hgfsServer/hgfsServerPacketUtil.c index 1e2889baf..3b17f2f92 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerPacketUtil.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServerPacketUtil.c @@ -71,16 +71,16 @@ HSPU_GetReplyPacket(HgfsPacket *packet, // IN/OUT: Hgfs Packet ASSERT_DEVEL(*replyPacketSize <= packet->replyPacketSize); } else if (session->channelCbTable && session->channelCbTable->getWriteVa) { /* Can we write directly into guest memory ? */ + ASSERT_DEVEL(packet->metaPacket); if (packet->metaPacket) { LOG(10, ("%s Using meta packet for reply packet\n", __FUNCTION__)); ASSERT_DEVEL(*replyPacketSize <= packet->metaPacketSize); packet->replyPacket = packet->metaPacket; packet->replyPacketSize = packet->metaPacketSize; - } else { - /* This should really never happen in existing scenarios */ - ASSERT_DEVEL(0); LOG(10, ("%s Mapping meta packet for reply packet\n", __FUNCTION__)); - packet->replyPacket = HSPU_GetBuf(packet, 0, &packet->metaPacket, + packet->replyPacket = HSPU_GetBuf(packet, + 0, + &packet->metaPacket, packet->metaPacketSize, &packet->metaPacketIsAllocated, BUF_WRITEABLE, @@ -436,9 +436,9 @@ HSPU_PutBuf(HgfsPacket *packet, // IN/OUT: Hgfs Packet uint32 startIndex, // IN: Start of iov void **buf, // IN/OUT: Buffer to be freed size_t *bufSize, // IN: Size of the buffer - Bool *isAllocated, // IN: Was buffer allocated ? - uint32 mappingType, // IN: Readable / Writeable ? - HgfsSessionInfo *session) // IN: Session info + Bool *isAllocated, // IN: Was buffer allocated ? + MappingType mappingType, // IN: Readable / Writeable ? + HgfsSessionInfo *session) // IN: Session info { uint32 iovCount = 0; int size = *bufSize; diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c b/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c new file mode 100644 index 000000000..813cf001b --- /dev/null +++ b/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c @@ -0,0 +1,4819 @@ +/********************************************************* + * Copyright (C) 2010 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation version 2.1 and no later version. + * + * 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 Lesser GNU General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *********************************************************/ + +#include +#include + +#include "vmware.h" +#include "str.h" +#include "cpName.h" +#include "cpNameLite.h" +#include "hgfsServerInt.h" +#include "hgfsServerPolicy.h" +#include "codeset.h" +#include "config.h" +#include "file.h" +#include "util.h" +#include "wiper.h" +#include "vm_basic_asm.h" + +#define LOGLEVEL_MODULE hgfs +#include "loglevel_user.h" + +#ifdef _WIN32 +#define HGFS_REQUEST_WIN32_SUPPORTED HGFS_REQUEST_SUPPORTED +#define HGFS_REQUEST_POSIX_SUPPORTED HGFS_REQUEST_NOT_SUPPORTED +#else +#define HGFS_REQUEST_WIN32_SUPPORTED HGFS_REQUEST_NOT_SUPPORTED +#define HGFS_REQUEST_POSIX_SUPPORTED HGFS_REQUEST_SUPPORTED +#endif + +#define HGFS_ASSERT_PACK_PARAMS \ + do { \ + ASSERT(packet); \ + ASSERT(packetHeader); \ + ASSERT(session); \ + ASSERT(payloadSize); \ + } while(0) + +/* Capabilities of the HGFS server. */ +static HgfsCapability hgfsServerCapabilities[] = +{ + {HGFS_OP_OPEN, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_READ, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_WRITE, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_CLOSE, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SEARCH_OPEN, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SEARCH_READ, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SEARCH_CLOSE, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_GETATTR, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SETATTR, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_CREATE_DIR, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_DELETE_FILE, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_DELETE_DIR, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_RENAME, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_QUERY_VOLUME_INFO, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_OPEN_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_GETATTR_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SETATTR_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SEARCH_READ_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_CREATE_SYMLINK, HGFS_REQUEST_POSIX_SUPPORTED}, + {HGFS_OP_SERVER_LOCK_CHANGE, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_CREATE_DIR_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_DELETE_FILE_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_DELETE_DIR_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_RENAME_V2, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_OPEN_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_READ_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_WRITE_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_CLOSE_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SEARCH_OPEN_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SEARCH_READ_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SEARCH_CLOSE_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_GETATTR_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_SETATTR_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_CREATE_DIR_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_DELETE_FILE_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_DELETE_DIR_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_RENAME_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_QUERY_VOLUME_INFO_V3, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_CREATE_SYMLINK_V3, HGFS_REQUEST_POSIX_SUPPORTED}, + {HGFS_OP_SERVER_LOCK_CHANGE_V3, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_WRITE_WIN32_STREAM_V3, HGFS_REQUEST_WIN32_SUPPORTED}, + {HGFS_OP_CREATE_SESSION_V4, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_DESTROY_SESSION_V4, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_READ_FAST_V4, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_WRITE_FAST_V4, HGFS_REQUEST_SUPPORTED}, + {HGFS_OP_OPEN_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_DIRECTORY_READ_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_ENUMERATE_STREAMS_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_GETATTR_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_SETATTR_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_DELETE_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_LINKMOVE_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_FSCTL_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_ACCESS_CHECK_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_FSYNC_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_QUERY_VOLUME_INFO_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_OPLOCK_ACQUIRE_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_OPLOCK_BREAK_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_LOCK_BYTE_RANGE_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_UNLOCK_BYTE_RANGE_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_QUERY_EAS_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_SET_EAS_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_SET_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_REMOVE_WATCH_V4, HGFS_REQUEST_NOT_SUPPORTED}, + {HGFS_OP_NOTIFY_V4, HGFS_REQUEST_NOT_SUPPORTED}, +}; + +/* + *----------------------------------------------------------------------------- + * + * HgfsValidatePacket -- + * + * Validates that packet is not malformed. Checks consistency of various + * fields and sizes. + * + * Results: + * TRUE if the packet is correct. + * FALSE if the packet is malformed. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsValidatePacket(char const *packetIn, // IN: request packet + size_t packetSize, // IN: request packet size + Bool v4header) // IN: HGFS header type +{ + HgfsRequest *request = (HgfsRequest *)packetIn; + Bool result = TRUE; + if (packetSize < sizeof *request) { + LOG(4, ("%s: Malformed HGFS packet received - packet too small!\n", __FUNCTION__)); + return FALSE; + } + if (v4header) { + HgfsHeader *header = (HgfsHeader *)packetIn; + ASSERT(packetSize >= header->packetSize); + ASSERT(header->packetSize >= header->headerSize); + result = packetSize >= offsetof(HgfsHeader, requestId) && + header->headerSize >= offsetof(HgfsHeader, reserved) && + header->packetSize >= header->headerSize && + packetSize >= header->packetSize; + } else { + result = packetSize >= sizeof *request; + } + if (!result) { + LOG(4, ("%s: Malformed HGFS packet received!\n", __FUNCTION__)); + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsValidateReplySize -- + * + * Verify if the size of a reply does not exceed maximum supported size. + * + * Results: + * TRUE if the packet size is acceptable, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsValidateReplySize(char const *packetIn, + HgfsOp op, + size_t packetSize) +{ + Bool result; + HgfsRequest *request = (HgfsRequest *)packetIn; + + if (HGFS_V4_LEGACY_OPCODE != request->op) { + if (HGFS_OP_READ_V3 == op) { + result = packetSize <= HGFS_LARGE_PACKET_MAX; + } else { + result = packetSize <= HGFS_PACKET_MAX; + } + } else { + result = TRUE; + } + if (!result) { + LOG(4, ("%s: Reply exceeded maximum support size!\n", __FUNCTION__)); + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsGetPayloadSize -- + * + * Returns size of the payload based on incoming packet and total + * packet size. + * + * Results: + * Size of the payload in bytes. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +size_t +HgfsGetPayloadSize(char const *packetIn, // IN: request packet + size_t packetSize) // IN: request packet size +{ + HgfsRequest *request = (HgfsRequest *)packetIn; + size_t result; + ASSERT(packetSize >= sizeof *request); + if (request->op < HGFS_OP_CREATE_SESSION_V4) { + result = packetSize - sizeof *request; + } else { + HgfsHeader *header = (HgfsHeader *)packetIn; + ASSERT(packetSize >= header->packetSize); + ASSERT(header->packetSize >= header->headerSize); + result = header->packetSize - header->headerSize; + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsParseRequest -- + * + * Returns requested operation and pointer to the payload based on + * incoming packet and total packet size. + * + * Results: + * TRUE if a reply can be sent. + * FALSE if incoming packet does not allow sending any response. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsParseRequest(HgfsPacket *packet, // IN: request packet + HgfsSessionInfo *session, // IN: current session + HgfsInputParam **input, // OUT: request parameters + HgfsInternalStatus *status) // OUT: error code +{ + HgfsRequest *request; + size_t packetSize; + HgfsInternalStatus result = HGFS_ERROR_SUCCESS; + HgfsInputParam *localInput; + + request = (HgfsRequest *) HSPU_GetMetaPacket(packet, &packetSize, session); + ASSERT_DEVEL(request); + + if (!request) { + /* + * How can I return error back to the client, clearly the client is either broken or + * malicious? We cannot continue from here. + */ + return FALSE; + } + + *input = Util_SafeMalloc(sizeof *localInput); + localInput = *input; + + memset(localInput, 0, sizeof *localInput); + localInput->metaPacket = (char *)request; + localInput->metaPacketSize = packetSize; + localInput->session = session; + localInput->packet = packet; + + /* + * Error out if less than HgfsRequest size. + */ + if (packetSize < sizeof *request) { + if (packetSize >= sizeof request->id) { + localInput->id = request->id; + } + ASSERT_DEVEL(0); + return HGFS_ERROR_PROTOCOL; + } + + if (request->op < HGFS_OP_OPEN_V3) { + /* Legacy requests do not have a separate header. */ + localInput->payload = request; + localInput->op = request->op; + localInput->payloadSize = packetSize; + localInput->id = request->id; + } else if (request->op < HGFS_OP_CREATE_SESSION_V4) { + /* V3 header. */ + if (packetSize > sizeof *request) { + localInput->payload = HGFS_REQ_GET_PAYLOAD_V3(request); + localInput->payloadSize = packetSize - + ((char *)localInput->payload - (char *)request); + } + localInput->op = request->op; + localInput->id = request->id; + } else if (HGFS_V4_LEGACY_OPCODE == request->op) { + /* V4 header. */ + HgfsHeader *header = (HgfsHeader *)request; + localInput->v4header = TRUE; + localInput->id = header->requestId; + + if (packetSize >= offsetof(HgfsHeader, sessionId) + sizeof header->sessionId) { + if (header->sessionId != session->sessionId && + header->op != HGFS_OP_CREATE_SESSION_V4) { + LOG(4, ("%s: HGFS packet with invalid session id!\n", __FUNCTION__)); + result = HGFS_ERROR_STALE_SESSION; + } else if (packetSize < header->packetSize || + header->packetSize < header->headerSize) { + LOG(4, ("%s: Malformed HGFS packet received - inconsistent header" + " and packet sizes!\n", __FUNCTION__)); + result = HGFS_ERROR_PROTOCOL; + } + } else { + LOG(4, ("%s: Malformed HGFS packet received - header is too small!\n", + __FUNCTION__)); + result = HGFS_ERROR_PROTOCOL; + } + + if (HGFS_ERROR_SUCCESS == result) { // Passed all tests + localInput->op = header->op; + localInput->payload = (char *)request + header->headerSize; + localInput->payloadSize = header->packetSize - header->headerSize; + } + } else { + LOG(4, ("%s: Malformed HGFS packet received - invalid legacy opcode!\n", + __FUNCTION__)); + result = HGFS_ERROR_PROTOCOL; + } + if (HGFS_ERROR_SUCCESS != result) { + LOG(4, ("%s: Malformed HGFS packet received!\n", __FUNCTION__)); + } + *status = result; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackOpenPayloadV1 -- + * + * Unpack and validate payload for hgfs open request V1 to the HgfsFileOpenInfo + * structure that is used to pass around open request information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackOpenPayloadV1(HgfsRequestOpen *requestV1, // IN: request payload + size_t payloadSize, // IN: request payload size + HgfsFileOpenInfo *openInfo) // IN/OUT: open info struct +{ + size_t extra; + + /* Enforced by the dispatch function. */ + if (payloadSize < sizeof *requestV1) { + LOG(4, ("%s: Malformed HGFS packet received - payload too small\n", __FUNCTION__)); + return FALSE; + } + + extra = payloadSize - sizeof *requestV1; + + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + if (requestV1->fileName.length > extra) { + /* The input packet is smaller than the request. */ + LOG(4, ("%s: Malformed HGFS packet received - payload too small to hold file name\n", __FUNCTION__)); + return FALSE; + } + + /* For OpenV1 requests, we know exactly what fields we expect. */ + openInfo->mask = HGFS_OPEN_VALID_MODE | + HGFS_OPEN_VALID_FLAGS | + HGFS_OPEN_VALID_OWNER_PERMS | + HGFS_OPEN_VALID_FILE_NAME; + openInfo->mode = requestV1->mode; + openInfo->cpName = requestV1->fileName.name; + openInfo->cpNameSize = requestV1->fileName.length; + openInfo->flags = requestV1->flags; + openInfo->ownerPerms = requestV1->permissions; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackOpenPayloadV2 -- + * + * Unpack and validate payload for hgfs open request V2 to the HgfsFileOpenInfo + * structure that is used to pass around open request information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackOpenPayloadV2(HgfsRequestOpenV2 *requestV2, // IN: request payload + size_t payloadSize, // IN: request payload size + HgfsFileOpenInfo *openInfo) // IN/OUT: open info struct +{ + size_t extra; + + /* Enforced by the dispatch function. */ + if (payloadSize < sizeof *requestV2) { + LOG(4, ("%s: Malformed HGFS packet received - payload too small\n", __FUNCTION__)); + return FALSE; + } + + extra = payloadSize - sizeof *requestV2; + + if (!(requestV2->mask & HGFS_OPEN_VALID_FILE_NAME)) { + /* We do not support open requests without a valid file name. */ + LOG(4, ("%s: Malformed HGFS packet received - invalid mask\n", __FUNCTION__)); + return FALSE; + } + + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + if (requestV2->fileName.length > extra) { + /* The input packet is smaller than the request. */ + LOG(4, ("%s: Malformed HGFS packet received - payload too small to hold file name\n", __FUNCTION__)); + return FALSE; + } + + /* + * Copy all the fields into our carrier struct. Some will probably be + * garbage, but it's simpler to copy everything now and check the + * valid bits before reading later. + */ + + openInfo->mask = requestV2->mask; + openInfo->mode = requestV2->mode; + openInfo->cpName = requestV2->fileName.name; + openInfo->cpNameSize = requestV2->fileName.length; + openInfo->flags = requestV2->flags; + openInfo->specialPerms = requestV2->specialPerms; + openInfo->ownerPerms = requestV2->ownerPerms; + openInfo->groupPerms = requestV2->groupPerms; + openInfo->otherPerms = requestV2->otherPerms; + openInfo->attr = requestV2->attr; + openInfo->allocationSize = requestV2->allocationSize; + openInfo->desiredAccess = requestV2->desiredAccess; + openInfo->shareAccess = requestV2->shareAccess; + openInfo->desiredLock = requestV2->desiredLock; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackOpenPayloadV3 -- + * + * Unpack and validate payload for hgfs open request V3 to the HgfsFileOpenInfo + * structure that is used to pass around open request information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackOpenPayloadV3(HgfsRequestOpenV3 *requestV3, // IN: request payload + size_t payloadSize, // IN: request payload size + HgfsFileOpenInfo *openInfo) // IN/OUT: open info struct +{ + size_t extra; + + /* Enforced by the dispatch function. */ + if (payloadSize < sizeof *requestV3) { + LOG(4, ("%s: Malformed HGFS packet received - payload too small\n", __FUNCTION__)); + return FALSE; + } + + extra = payloadSize - sizeof *requestV3; + + if (!(requestV3->mask & HGFS_OPEN_VALID_FILE_NAME)) { + /* We do not support open requests without a valid file name. */ + LOG(4, ("%s: Malformed HGFS packet received - incorrect mask\n", __FUNCTION__)); + return FALSE; + } + + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + if (requestV3->fileName.length > extra) { + /* The input packet is smaller than the request. */ + LOG(4, ("%s: Malformed HGFS packet received - payload too small to hold file name\n", __FUNCTION__)); + return FALSE; + } + + /* + * Copy all the fields into our carrier struct. Some will probably be + * garbage, but it's simpler to copy everything now and check the + * valid bits before reading later. + */ + openInfo->mask = requestV3->mask; + openInfo->mode = requestV3->mode; + openInfo->cpName = requestV3->fileName.name; + openInfo->cpNameSize = requestV3->fileName.length; + openInfo->caseFlags = requestV3->fileName.caseType; + openInfo->flags = requestV3->flags; + openInfo->specialPerms = requestV3->specialPerms; + openInfo->ownerPerms = requestV3->ownerPerms; + openInfo->groupPerms = requestV3->groupPerms; + openInfo->otherPerms = requestV3->otherPerms; + openInfo->attr = requestV3->attr; + openInfo->allocationSize = requestV3->allocationSize; + openInfo->desiredAccess = requestV3->desiredAccess; + openInfo->shareAccess = requestV3->shareAccess; + openInfo->desiredLock = requestV3->desiredLock; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackOpenRequest -- + * + * Unpack hgfs open request to the HgfsFileOpenInfo structure that is used + * to pass around open request information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackOpenRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: packet size + HgfsOp op, // IN: requested operation + HgfsFileOpenInfo *openInfo) // IN/OUT: open info structure +{ + Bool result; + + ASSERT(packet); + ASSERT(openInfo); + + openInfo->requestType = op; + openInfo->caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + + switch (op) { + case HGFS_OP_OPEN_V3: { + HgfsRequestOpenV3 *requestV3 = (HgfsRequestOpenV3 *)packet; + LOG(4, ("%s: HGFS_OP_OPEN_V3\n", __FUNCTION__)); + + result = HgfsUnpackOpenPayloadV3(requestV3, packetSize, openInfo); + break; + } + case HGFS_OP_OPEN_V2: { + HgfsRequestOpenV2 *requestV2 = (HgfsRequestOpenV2 *)packet; + LOG(4, ("%s: HGFS_OP_OPEN_V2\n", __FUNCTION__)); + + result = HgfsUnpackOpenPayloadV2(requestV2, packetSize, openInfo); + break; + } + case HGFS_OP_OPEN: { + HgfsRequestOpen *requestV1 = (HgfsRequestOpen *)packet; + LOG(4, ("%s: HGFS_OP_OPEN\n", __FUNCTION__)); + + result = HgfsUnpackOpenPayloadV1(requestV1, packetSize, openInfo); + break; + } + default: + NOT_REACHED(); + result = FALSE; + } + + if (!result) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + } + return result; +} + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackReplyHeaderV4 -- + * + * Pack hgfs header that corresponds an incoming packet. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackReplyHeaderV4(HgfsInternalStatus status, // IN: reply status + uint32 payloadSize, // IN: size of the reply payload + HgfsOp op, // IN: request type + uint64 sessionId, // IN: session id + uint32 requestId, // IN: request id + HgfsHeader *header) // OUT: outgoing packet header +{ + memset(header, 0, sizeof *header); + header->version = 1; + header->dummy = HGFS_V4_LEGACY_OPCODE; + header->packetSize = payloadSize + sizeof *header; + header->headerSize = sizeof *header; + header->requestId = requestId; + header->op = op; + header->status = HgfsConvertFromInternalStatus(status); + header->flags = 0; + header->information = status; + header->sessionId = sessionId; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackLegacyReplyHeader -- + * + * Pack pre-V4 reply header. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackLegacyReplyHeader(HgfsInternalStatus status, // IN: reply status + HgfsHandle id, // IN: original packet id + HgfsReply *header) // OUT: outgoing packet header +{ + memset(header, 0, sizeof *header); + header->status = HgfsConvertFromInternalStatus(status); + header->id = id; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackOpenReplyV3 -- + * + * Pack hgfs open V3 reply payload to the HgfsReplyOpenV3 structure. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsPackOpenReplyV3(HgfsFileOpenInfo *openInfo, // IN: open info struct + HgfsReplyOpenV3 *reply) // OUT: size of packet +{ + reply->file = openInfo->file; + reply->reserved = 0; + if (openInfo->mask & HGFS_OPEN_VALID_SERVER_LOCK) { + reply->acquiredLock = openInfo->acquiredLock; + } else { + reply->acquiredLock = HGFS_LOCK_NONE; + } +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackOpenV2Reply -- + * + * Pack hgfs open V2 reply payload to the HgfsReplyOpenV3 structure. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsPackOpenV2Reply(HgfsFileOpenInfo *openInfo, // IN: open info struct + HgfsReplyOpenV2 *reply) // OUT: reply payload +{ + reply->file = openInfo->file; + if (openInfo->mask & HGFS_OPEN_VALID_SERVER_LOCK) { + reply->acquiredLock = openInfo->acquiredLock; + } else { + reply->acquiredLock = HGFS_LOCK_NONE; + } +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackOpenV1Reply -- + * + * Pack hgfs open V1 reply payload to the HgfsReplyOpenV3 structure. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HgfsPackOpenV1Reply(HgfsFileOpenInfo *openInfo, // IN: open info struct + HgfsReplyOpen *reply) // OUT: reply payload +{ + reply->file = openInfo->file; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackOpenReply -- + * + * Pack hgfs open reply to the HgfsReplyOpen{V2} structure. + * + * Results: + * Always TRUE. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackOpenReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsFileOpenInfo *openInfo, // IN: open info struct + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + ASSERT(openInfo); + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + switch (openInfo->requestType) { + case HGFS_OP_OPEN_V3: { + HgfsReplyOpenV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + HgfsPackOpenReplyV3(openInfo, reply); + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_OPEN_V2: { + HgfsReplyOpenV2 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + HgfsPackOpenV2Reply(openInfo, reply); + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_OPEN: { + HgfsReplyOpen *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + HgfsPackOpenV1Reply(openInfo, reply); + *payloadSize = sizeof *reply; + } + break; + } + default: + NOT_REACHED(); + result = FALSE; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackClosePayload -- + * + * Unpack hgfs close payload to get the handle which need to be closed. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackClosePayload(HgfsRequestClose *request, // IN: payload + size_t payloadSize, // IN: payload size + HgfsHandle* file) // OUT: HGFS handle to close +{ + LOG(4, ("%s: HGFS_OP_CLOSE\n", __FUNCTION__)); + if (payloadSize >= sizeof *request) { + *file = request->file; + return TRUE; + } + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackClosePayloadV3 -- + * + * Unpack hgfs close payload V3 to get the handle which need to be closed. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackClosePayloadV3(HgfsRequestCloseV3 *requestV3, // IN: payload + size_t payloadSize, // IN: payload size + HgfsHandle* file) // OUT: HGFS handle to close +{ + LOG(4, ("%s: HGFS_OP_CLOSE_V3\n", __FUNCTION__)); + if (payloadSize >= sizeof *requestV3) { + *file = requestV3->file; + return TRUE; + } + LOG(4, ("%s: Too small HGFS packet\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackCloseRequest -- + * + * Unpack hgfs close request to get the handle to close. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackCloseRequest(void const *packet, // IN: request packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + HgfsHandle *file) // OUT: Handle to close +{ + ASSERT(packet); + + switch (op) { + case HGFS_OP_CLOSE_V3: { + HgfsRequestCloseV3 *requestV3 = (HgfsRequestCloseV3 *)packet; + + if (!HgfsUnpackClosePayloadV3(requestV3, packetSize, file)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_CLOSE: { + HgfsRequestClose *requestV1 = (HgfsRequestClose *)packet; + + if (!HgfsUnpackClosePayload(requestV1, packetSize, file)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + default: + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackCloseReply -- + * + * Pack hgfs close reply to the HgfsReplyClose(V3) structure. + * + * Results: + * Always TRUE. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackCloseReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + size_t *payloadSize, // OUT: size of packet excluding header + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_CLOSE_V3: { + HgfsReplyCloseV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_CLOSE: { + HgfsReplyClose *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + default: + NOT_REACHED(); + result = FALSE; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSearchClosePayload -- + * + * Unpack hgfs search close payload to get the search handle which need to be closed. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSearchClosePayload(HgfsRequestSearchClose *request, // IN: payload + size_t payloadSize, // IN: payload size + HgfsHandle* search) // OUT: search to close +{ + LOG(4, ("%s: HGFS_OP_SEARCH_CLOSE\n", __FUNCTION__)); + if (payloadSize >= sizeof *request) { + *search = request->search; + return TRUE; + } + LOG(4, ("%s: Too small HGFS packet\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackClosePayloadV3 -- + * + * Unpack hgfs search close payload V3 to get the search handle which need to + * be closed. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSearchClosePayloadV3(HgfsRequestSearchCloseV3 *requestV3, // IN: payload + size_t payloadSize, // IN: payload size + HgfsHandle* search) // OUT: search +{ + LOG(4, ("%s: HGFS_OP_SEARCH_CLOSE_V3\n", __FUNCTION__)); + if (payloadSize >= sizeof *requestV3) { + *search = requestV3->search; + return TRUE; + } + LOG(4, ("%s: Too small HGFS packet\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSearchCloseRequest -- + * + * Unpack hgfs search close request to get the search handle. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSearchCloseRequest(void const *packet, // IN: request packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + HgfsHandle *search) // OUT: search to close +{ + ASSERT(packet); + + switch (op) { + case HGFS_OP_SEARCH_CLOSE_V3: { + HgfsRequestSearchCloseV3 *requestV3 = (HgfsRequestSearchCloseV3 *)packet; + + if (!HgfsUnpackSearchClosePayloadV3(requestV3, packetSize, search)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_SEARCH_CLOSE: { + HgfsRequestSearchClose *requestV1 = (HgfsRequestSearchClose *)packet; + + if (!HgfsUnpackSearchClosePayload(requestV1, packetSize, search)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + default: + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSearchCloseReply -- + * + * Pack hgfs SearchClose reply into a HgfsReplySearchClose(V3) structure. + * + * Results: + * Always TRUE, except when it is called with a + * wrong op or insufficient output buffer (which is a programming error). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackSearchCloseReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_SEARCH_CLOSE_V3: { + HgfsReplyCloseV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_SEARCH_CLOSE: { + HgfsReplyClose *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + default: + NOT_REACHED(); + result = FALSE; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackFileName -- + * + * Unpack HgfsFileName into a pointer to a CPName and size of the name. + * Verifies that input buffer has enough space to hold the name. + * + * Results: + * TRUE on success, FALSE on failure (buffer too small). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackFileName(HgfsFileName *name, // IN: file name + size_t maxNameSize, // IN: space allocated for the name + char **cpName, // OUT: CP name + size_t *cpNameSize) // OUT: CP name size +{ + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + if (name->length > maxNameSize) { + /* The input packet is smaller than the request. */ + return FALSE; + } + *cpName = name->name; + *cpNameSize = name->length; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackFileNameV3 -- + * + * Unpack HgfsFileNameV3 into a pointer to a CPName and size of the name + * or into file handle. + * Verifies that input buffer has enough space to hold the name. + * + * Results: + * TRUE on success, FALSE on failure (buffer too small). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackFileNameV3(HgfsFileNameV3 *name, // IN: file name + size_t maxNameSize, // IN: space allocated for the name + Bool *useHandle, // OUT: file name or handle returned? + char **cpName, // OUT: CP name + size_t *cpNameSize, // OUT: CP name size + HgfsHandle *file, // OUT: HGFS file handle + uint32 *caseFlags) // OUT: case-sensitivity flags +{ + /* + * If we've been asked to reuse a handle, we don't need to look at, let + * alone test the filename or its length. + */ + if (name->flags & HGFS_FILE_NAME_USE_FILE_DESC) { + *file = name->fid; + *cpName = NULL; + *cpNameSize = 0; + *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + *useHandle = TRUE; + } else { + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + if (name->length > maxNameSize) { + /* The input packet is smaller than the request */ + LOG(4, ("%s: Error unpacking file name - buffer too small\n", __FUNCTION__)); + return FALSE; + } + *file = HGFS_INVALID_HANDLE; + *cpName = name->name; + *cpNameSize = name->length; + *caseFlags = name->caseType; + *useHandle = FALSE; + } + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackDeletePayloadV3 -- + * + * Unpack hgfs delete request V3 payload and initialize a corresponding + * HgfsHandle or file name to tell us which to delete. Hints + * holds flags to specify a handle or name for the file or + * directory to delete. + * + * Since the structure of the get delete request packet is the same + * for Delete File or Directory of the protocol, code is identical for + * both operations. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackDeletePayloadV3(HgfsRequestDeleteV3 *requestV3, // IN: request payload + size_t payloadSize, // IN: payload size + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsDeleteHint *hints, // OUT: delete hints + HgfsHandle *file, // OUT: file handle + uint32 *caseFlags) // OUT: case-sensitivity flags +{ + Bool result; + Bool useHandle; + + if (payloadSize < sizeof *requestV3) { + return FALSE; + } + + *hints = requestV3->hints; + + result = HgfsUnpackFileNameV3(&requestV3->fileName, + payloadSize - sizeof *requestV3, + &useHandle, + cpName, + cpNameSize, + file, + caseFlags); + if (useHandle) { + *hints |= HGFS_DELETE_HINT_USE_FILE_DESC; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackDeletePayloadV2 -- + * + * Unpack hgfs delete request V2 payload and initialize a corresponding + * HgfsHandle or file name to tell us which to delete. Hints + * holds flags to specify a handle or name for the file or + * directory to delete. + * + * Since the structure of the get delete request packet is the same + * for Delete File or Directory of the protocol, code is identical for + * both operations. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackDeletePayloadV2(HgfsRequestDeleteV2 *requestV2, // IN: request payload + size_t payloadSize, // IN: payload size + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsDeleteHint *hints, // OUT: delete hints + HgfsHandle *file) // OUT: file handle +{ + Bool result = TRUE; + + /* Enforced by the dispatch function. */ + ASSERT(payloadSize >= sizeof *requestV2); + + *file = HGFS_INVALID_HANDLE; + *hints = requestV2->hints; + + /* + * If we've been asked to reuse a handle, we don't need to look at, let + * alone test the filename or its length. + */ + + if (requestV2->hints & HGFS_DELETE_HINT_USE_FILE_DESC) { + *file = requestV2->file; + *cpName = NULL; + *cpNameSize = 0; + } else { + result = HgfsUnpackFileName(&requestV2->fileName, + payloadSize - sizeof *requestV2, + cpName, + cpNameSize); + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackDeletePayloadV1 -- + * + * Unpack hgfs delete request V1 payload and initialize a corresponding + * file name to tell us which to delete. + * + * Since the structure of the get delete request packet is the same + * for Delete File or Directory of the protocol, code is identical for + * both operations. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackDeletePayloadV1(HgfsRequestDelete *requestV1, // IN: request payload + size_t payloadSize, // IN: payload size + char **cpName, // OUT: cpName + size_t *cpNameSize) // OUT: cpName size +{ + return HgfsUnpackFileName(&requestV1->fileName, + payloadSize - sizeof *requestV1, + cpName, + cpNameSize); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackDeleteRequest -- + * + * Unpack hgfs delete request and initialize a corresponding + * HgfsHandle or file name to tell us which to delete. Hints + * holds flags to specify a handle or name for the file or + * directory to delete. + * + * Since the structure of the get delete request packet is the same + * for Delete File or Directory of the protocol, code is identical for + * both operations. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackDeleteRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: requested operation + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsDeleteHint *hints, // OUT: delete hints + HgfsHandle *file, // OUT: file handle + uint32 *caseFlags) // OUT: case-sensitivity flags +{ + ASSERT(packet); + ASSERT(cpName); + ASSERT(cpNameSize); + ASSERT(file); + ASSERT(hints); + ASSERT(caseFlags); + + *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + *hints = 0; + *file = HGFS_INVALID_HANDLE; + + switch (op) { + case HGFS_OP_DELETE_FILE_V3: + case HGFS_OP_DELETE_DIR_V3: { + HgfsRequestDeleteV3 *requestV3 = (HgfsRequestDeleteV3 *)packet; + + if (!HgfsUnpackDeletePayloadV3(requestV3, + packetSize, + cpName, + cpNameSize, + hints, + file, + caseFlags)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_DELETE_FILE_V2: + case HGFS_OP_DELETE_DIR_V2: { + HgfsRequestDeleteV2 *requestV2 = (HgfsRequestDeleteV2 *)packet; + + if (!HgfsUnpackDeletePayloadV2(requestV2, + packetSize, + cpName, + cpNameSize, + hints, + file)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_DELETE_FILE: + case HGFS_OP_DELETE_DIR: { + HgfsRequestDelete *requestV1 = (HgfsRequestDelete *)packet; + + if (!HgfsUnpackDeletePayloadV1(requestV1, + packetSize, + cpName, + cpNameSize)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + default: + NOT_REACHED(); + LOG(4, ("%s: Invalid opcode\n", __FUNCTION__)); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackDeleteReply -- + * + * Pack hgfs delete reply. + * Since the structure of the delete reply packet hasn't changed in + * version 2 of the protocol, HgfsReplyDeleteV2 is identical to + * HgfsReplyDelete. So use HgfsReplyDelete type to access packetIn to + * keep the code simple. + * + * Results: + * TRUE if valid op version reply filled, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackDeleteReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: requested operation + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + /* No reply payload, just header. */ + switch (op) { + case HGFS_OP_DELETE_FILE_V3: + case HGFS_OP_DELETE_DIR_V3: { + HgfsReplyDeleteV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_DELETE_FILE_V2: + case HGFS_OP_DELETE_FILE: + case HGFS_OP_DELETE_DIR_V2: + case HGFS_OP_DELETE_DIR: { + HgfsReplyDelete *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + default: + LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); + result = FALSE; + NOT_REACHED(); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackRenamePayloadV3 -- + * + * Unpack hgfs rename request V3 payload and initialize a corresponding + * HgfsHandles or file names to tell us old and new names/handles. Hints + * holds flags to specify a handle or name for the file or + * directory to rename. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackRenamePayloadV3(HgfsRequestRenameV3 *requestV3, // IN: request payload + size_t payloadSize, // IN: payload size + char **cpOldName, // OUT: rename src + size_t *cpOldNameLen, // OUT: rename src size + char **cpNewName, // OUT: rename dst + size_t *cpNewNameLen, // OUT: rename dst size + HgfsRenameHint *hints, // OUT: rename hints + HgfsHandle *srcFile, // OUT: src file handle + HgfsHandle *targetFile, // OUT: target file handle + uint32 *oldCaseFlags, // OUT: source case flags + uint32 *newCaseFlags) // OUT: dest. case flags +{ + size_t extra; + HgfsFileNameV3 *newName; + Bool useHandle; + + LOG(4, ("%s: HGFS_OP_RENAME_V3\n", __FUNCTION__)); + + if (payloadSize < sizeof *requestV3) { + return FALSE; + } + extra = payloadSize - sizeof *requestV3; + + *hints = requestV3->hints; + + /* + * Get the old and new filenames from the request. + * + * Getting the new filename is somewhat inconvenient, because we + * don't know where request->newName actually starts, thanks to the + * fact that request->oldName is of variable length. We get around + * this by using an HgfsFileName*, assigning it to the correct address + * just after request->oldName ends, and using that to access the + * new name. + */ + + /* + * If we've been asked to reuse a handle, we don't need to look at, let + * alone test the filename or its length. This applies to the source + * and the target. + */ + if (!HgfsUnpackFileNameV3(&requestV3->oldName, + extra, + &useHandle, + cpOldName, + cpOldNameLen, + srcFile, + oldCaseFlags)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + if (useHandle) { + *hints |= HGFS_RENAME_HINT_USE_SRCFILE_DESC; + newName = &requestV3->newName; + } else { + newName = (HgfsFileNameV3 *)(requestV3->oldName.name + 1 + *cpOldNameLen); + extra -= *cpOldNameLen; + } + if (!HgfsUnpackFileNameV3(newName, + extra, + &useHandle, + cpNewName, + cpNewNameLen, + targetFile, + newCaseFlags)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + if (useHandle) { + *hints |= HGFS_RENAME_HINT_USE_TARGETFILE_DESC; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackRenamePayloadV2 -- + * + * Unpack hgfs rename request V2 payload and initialize a corresponding + * HgfsHandle or file name to tell us which to delete. Hints + * holds flags to specify a handle or name for the file or + * directory to rename. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackRenamePayloadV2(HgfsRequestRenameV2 *requestV2, // IN: request payload + size_t payloadSize, // IN: payload size + char **cpOldName, // OUT: rename src + size_t *cpOldNameLen, // OUT: rename src size + char **cpNewName, // OUT: rename dst + size_t *cpNewNameLen, // OUT: rename dst size + HgfsRenameHint *hints, // OUT: rename hints + HgfsHandle *srcFile, // OUT: src file handle + HgfsHandle *targetFile) // OUT: target file handle +{ + HgfsFileName *newName; + size_t extra; + + /* Enforced by the dispatch function. */ + if (payloadSize < sizeof *requestV2) { + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; + } + extra = payloadSize - sizeof *requestV2; + + *hints = requestV2->hints; + + /* + * If we've been asked to reuse a handle, we don't need to look at, let + * alone test the filename or its length. This applies to the source + * and the target. + */ + + if (*hints & HGFS_RENAME_HINT_USE_SRCFILE_DESC) { + *srcFile = requestV2->srcFile; + *cpOldName = NULL; + *cpOldNameLen = 0; + } else { + if (!HgfsUnpackFileName(&requestV2->oldName, + extra, + cpOldName, + cpOldNameLen)) { + LOG(4, ("%s: Error decoding HGFS packet - not enough room for file name\n", __FUNCTION__)); + return FALSE; + } + extra -= *cpOldNameLen; + } + + if (*hints & HGFS_RENAME_HINT_USE_TARGETFILE_DESC) { + *targetFile = requestV2->targetFile; + *cpNewName = NULL; + *cpNewNameLen = 0; + } else { + newName = (HgfsFileName *)((char *)(&requestV2->oldName + 1) + + *cpOldNameLen); + if (!HgfsUnpackFileName(newName, + extra, + cpNewName, + cpNewNameLen)) { + LOG(4, ("%s: Error decoding HGFS packet - not enough room for file name\n", __FUNCTION__)); + return FALSE; + } + } + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackRenamePayloadV1 -- + * + * Unpack hgfs rename request V1 payload and initialize a corresponding + * old and new file names. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackRenamePayloadV1(HgfsRequestRename *requestV1, // IN: request payload + size_t payloadSize, // IN: payload size + char **cpOldName, // OUT: rename src + size_t *cpOldNameLen, // OUT: rename src size + char **cpNewName, // OUT: rename dst + size_t *cpNewNameLen) // OUT: rename dst size +{ + HgfsFileName *newName; + uint32 extra; + + if (payloadSize < sizeof *requestV1) { + return FALSE; + } + + extra = payloadSize - sizeof *requestV1; + + if (!HgfsUnpackFileName(&requestV1->oldName, + extra, + cpOldName, + cpOldNameLen)) { + LOG(4, ("%s: Error decoding HGFS packet - not enough room for file name\n", __FUNCTION__)); + return FALSE; + } + + extra -= requestV1->oldName.length; + newName = (HgfsFileName *)((char *)(&requestV1->oldName + 1) + + requestV1->oldName.length); + + return HgfsUnpackFileName(newName, extra, cpNewName, cpNewNameLen); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackRenameRequest -- + * + * Unpack hgfs rename request and initialize a corresponding + * HgfsHandle or file name to tell us which to rename. Hints + * holds flags to specify a handle or name for the file or + * directory to rename. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackRenameRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: requested operation + char **cpOldName, // OUT: rename src + size_t *cpOldNameLen, // OUT: rename src size + char **cpNewName, // OUT: rename dst + size_t *cpNewNameLen, // OUT: rename dst size + HgfsRenameHint *hints, // OUT: rename hints + HgfsHandle *srcFile, // OUT: src file handle + HgfsHandle *targetFile, // OUT: target file handle + uint32 *oldCaseFlags, // OUT: source case-sensitivity flags + uint32 *newCaseFlags) // OUT: dest. case-sensitivity flags +{ + ASSERT(packet); + ASSERT(cpOldName); + ASSERT(cpOldNameLen); + ASSERT(cpNewName); + ASSERT(cpNewNameLen); + ASSERT(srcFile); + ASSERT(targetFile); + ASSERT(hints); + ASSERT(oldCaseFlags); + ASSERT(newCaseFlags); + + /* Default values for legacy requests. */ + *oldCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + *newCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + *hints = 0; + + switch (op) { + case HGFS_OP_RENAME_V3: + { + HgfsRequestRenameV3 *requestV3 = (HgfsRequestRenameV3 *)packet; + + if (!HgfsUnpackRenamePayloadV3(requestV3, + packetSize, + cpOldName, + cpOldNameLen, + cpNewName, + cpNewNameLen, + hints, + srcFile, + targetFile, + oldCaseFlags, + newCaseFlags)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_RENAME_V2: + { + HgfsRequestRenameV2 *requestV2 = (HgfsRequestRenameV2 *)packet; + + if (!HgfsUnpackRenamePayloadV2(requestV2, + packetSize, + cpOldName, + cpOldNameLen, + cpNewName, + cpNewNameLen, + hints, + srcFile, + targetFile)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + + case HGFS_OP_RENAME: + { + HgfsRequestRename *requestV1 = (HgfsRequestRename *)packet; + + if (!HgfsUnpackRenamePayloadV1(requestV1, + packetSize, + cpOldName, + cpOldNameLen, + cpNewName, + cpNewNameLen)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + + default: + LOG(4, ("%s: Invalid opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackRenameReply -- + * + * Pack hgfs rename reply. + * Since the structure of the rename reply packet hasn't changed in + * version 2 of the protocol, HgfsReplyRenameV2 is identical to + * HgfsReplyRename. So use HgfsReplyRename type to access packetIn to + * keep the code simple. + * + * Results: + * TRUE if valid op and reply set, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackRenameReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: requested operation + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_RENAME_V3: { + HgfsReplyRenameV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_RENAME_V2: + case HGFS_OP_RENAME: { + HgfsReplyRename *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + default: + LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); + result = FALSE; + NOT_REACHED(); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackGetattrPayloadV3 -- + * + * Unpack hgfs get attr request V3 payload and initialize a corresponding + * HgfsHandle or file name to tell us which file to get attributes. Hints + * holds flags to specify a handle or name for the file or + * directory to get attributes. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackGetattrPayloadV3(HgfsRequestGetattrV3 *requestV3,// IN: request payload + size_t payloadSize, // IN: payload size + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsAttrHint *hints, // OUT: getattr hints + HgfsHandle *file, // OUT: file handle + uint32 *caseFlags) // OUT: case-sensitivity flags +{ + Bool result; + Bool useHandle; + + if (payloadSize < sizeof *requestV3) { + return FALSE; + } + + *hints = requestV3->hints; + + result = HgfsUnpackFileNameV3(&requestV3->fileName, + payloadSize - sizeof *requestV3, + &useHandle, + cpName, + cpNameSize, + file, + caseFlags); + if (useHandle) { + *hints |= HGFS_ATTR_HINT_USE_FILE_DESC; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackGetattrPayloadV2 -- + * + * Unpack hgfs Getattr request V2 payload and initialize a corresponding + * HgfsHandle or file name to tell us which to get attributes. Hints + * holds flags to specify a handle or name for the file or + * directory to get attributes. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackGetattrPayloadV2(HgfsRequestGetattrV2 *requestV2,// IN: request payload + size_t payloadSize, // IN: payload size + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsAttrHint *hints, // OUT: delete hints + HgfsHandle *file) // OUT: file handle +{ + Bool result = TRUE; + + if (payloadSize < sizeof *requestV2) { + return FALSE; + } + + + *file = HGFS_INVALID_HANDLE; + *hints = requestV2->hints; + + /* + * If we've been asked to reuse a handle, we don't need to look at, let + * alone test the filename or its length. + */ + + if (requestV2->hints & HGFS_ATTR_HINT_USE_FILE_DESC) { + *file = requestV2->file; + *cpName = NULL; + *cpNameSize = 0; + } else { + result = HgfsUnpackFileName(&requestV2->fileName, + payloadSize - sizeof *requestV2, + cpName, + cpNameSize); + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackGetattrPayloadV1 -- + * + * Unpack hgfs getattr request V1 payload and initialize a corresponding + * file name to tell us which to get attributes. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackGetattrPayloadV1(HgfsRequestGetattr *requestV1, // IN: request payload + size_t payloadSize, // IN: payload size + char **cpName, // OUT: cpName + size_t *cpNameSize) // OUT: cpName size +{ + return HgfsUnpackFileName(&requestV1->fileName, + payloadSize - sizeof *requestV1, + cpName, + cpNameSize); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackAttrV2 -- + * + * Packs attr version 2 reply structure. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackAttrV2(HgfsFileAttrInfo *attr, // IN: attr stucture + HgfsAttrV2 *attr2) // OUT: attr in payload +{ + attr2->mask = attr->mask; + attr2->type = attr->type; + attr2->size = attr->size; + attr2->creationTime = attr->creationTime; + attr2->accessTime = attr->accessTime; + attr2->writeTime = attr->writeTime; + attr2->attrChangeTime = attr->attrChangeTime; + attr2->specialPerms = attr->specialPerms; + attr2->ownerPerms = attr->ownerPerms; + attr2->groupPerms = attr->groupPerms; + attr2->otherPerms = attr->otherPerms; + attr2->flags = attr->flags; + attr2->allocationSize = attr->allocationSize; + attr2->userId = attr->userId; + attr2->groupId = attr->groupId; + attr2->hostFileId = attr->hostFileId; + attr2->volumeId = attr->volumeId; + attr2->effectivePerms = attr->effectivePerms; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackAttrV2 -- + * + * Unpacks attr version 2 reply structure. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsUnpackAttrV2(HgfsAttrV2 *attr2, // IN: attr in payload + HgfsFileAttrInfo *attr) // OUT: attr stucture +{ + attr->mask = attr2->mask; + attr->type = attr2->type; + attr->size = attr2->size; + attr->creationTime = attr2->creationTime; + attr->accessTime = attr2->accessTime; + attr->writeTime = attr2->writeTime; + attr->attrChangeTime = attr2->attrChangeTime; + attr->specialPerms = attr2->specialPerms; + attr->ownerPerms = attr2->ownerPerms; + attr->groupPerms = attr2->groupPerms; + attr->otherPerms = attr2->otherPerms; + attr->flags = attr2->flags; + attr->allocationSize = attr2->allocationSize; + attr->userId = attr2->userId; + attr->groupId = attr2->groupId; + attr->hostFileId = attr2->hostFileId; + attr->volumeId = attr2->volumeId; + attr->effectivePerms = attr2->effectivePerms; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsInitFileAttr -- + * + * Initializes HgfsFileAttrInfo structure. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsInitFileAttr(HgfsOp op, // IN: request type + HgfsFileAttrInfo *attr) // OUT: attr stucture +{ + /* Initialize all fields with 0. */ + memset(attr, 0, sizeof *attr); + + /* Explicitly initialize fields which need it. */ + attr->requestType = op; + attr->mask = HGFS_ATTR_VALID_NONE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackGetattrReplyPayloadV3 -- + * + * Packs Getattr V3 reply payload. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackGetattrReplyPayloadV3(HgfsFileAttrInfo *attr, // IN: attr stucture + const char *utf8TargetName, // IN: optional target name + uint32 utf8TargetNameLen, // IN: file name length + HgfsReplyGetattrV3 *reply) // OUT: payload +{ + LOG(4, ("%s: attr type: %u\n", __FUNCTION__, reply->attr.type)); + + HgfsPackAttrV2(attr, &reply->attr); + reply->reserved = 0; + + if (utf8TargetName) { + memcpy(reply->symlinkTarget.name, utf8TargetName, utf8TargetNameLen); + CPNameLite_ConvertTo(reply->symlinkTarget.name, utf8TargetNameLen, + DIRSEPC); + } else { + ASSERT(utf8TargetNameLen == 0); + } + reply->symlinkTarget.length = utf8TargetNameLen; + reply->symlinkTarget.name[utf8TargetNameLen] = '\0'; + reply->symlinkTarget.flags = 0; + reply->symlinkTarget.fid = 0; + reply->symlinkTarget.caseType = HGFS_FILE_NAME_DEFAULT_CASE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackGetattrReplyPayloadV2 -- + * + * Packs rename reply payload V2 requests. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackGetattrReplyPayloadV2(HgfsFileAttrInfo *attr, // IN: attr stucture + const char *utf8TargetName, // IN: optional target name + uint32 utf8TargetNameLen, // IN: file name length + HgfsReplyGetattrV2 *reply) // OUT: payload +{ + HgfsPackAttrV2(attr, &reply->attr); + + if (utf8TargetName) { + memcpy(reply->symlinkTarget.name, utf8TargetName, utf8TargetNameLen); + CPNameLite_ConvertTo(reply->symlinkTarget.name, utf8TargetNameLen, + DIRSEPC); + } else { + ASSERT(utf8TargetNameLen == 0); + } + reply->symlinkTarget.length = utf8TargetNameLen; + reply->symlinkTarget.name[utf8TargetNameLen] = '\0'; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackGetattrReplyPayloadV1 -- + * + * Packs rename reply payload for V1 requests. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackGetattrReplyPayloadV1(HgfsFileAttrInfo *attr, // IN: attr stucture + HgfsReplyGetattr *reply) // OUT: reply info +{ + /* In GetattrV1, symlinks are treated as regular files. */ + if (attr->type == HGFS_FILE_TYPE_SYMLINK) { + reply->attr.type = HGFS_FILE_TYPE_REGULAR; + } else { + reply->attr.type = attr->type; + } + + reply->attr.size = attr->size; + reply->attr.creationTime = attr->creationTime; + reply->attr.accessTime = attr->accessTime; + reply->attr.writeTime = attr->writeTime; + reply->attr.attrChangeTime = attr->attrChangeTime; + reply->attr.permissions = attr->ownerPerms; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackGetattrRequest -- + * + * Unpack hgfs getattr request and initialize a corresponding + * HgfsFileAttrInfo structure that is used to pass around getattr request + * information. + * + * Since the structure of the get attributes request packet hasn't changed + * in version 2 of the protocol, HgfsRequestGetattrV2 is identical to + * HgfsRequestGetattr. So use HgfsRequestGetattr type to access packetIn to + * keep the code simple. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackGetattrRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN request type + HgfsFileAttrInfo *attrInfo, // IN/OUT: getattr info + HgfsAttrHint *hints, // OUT: getattr hints + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsHandle *file, // OUT: file handle + uint32 *caseType) // OUT: case-sensitivity flags +{ + ASSERT(packet); + ASSERT(attrInfo); + ASSERT(cpName); + ASSERT(cpNameSize); + ASSERT(file); + ASSERT(caseType); + + HgfsInitFileAttr(op, attrInfo); + + /* Default values for legacy requests. */ + *caseType = HGFS_FILE_NAME_DEFAULT_CASE; + *hints = 0; + *file = HGFS_INVALID_HANDLE; + + switch (op) { + case HGFS_OP_GETATTR_V3: { + HgfsRequestGetattrV3 *requestV3 = (HgfsRequestGetattrV3 *)packet; + + if (!HgfsUnpackGetattrPayloadV3(requestV3, + packetSize, + cpName, + cpNameSize, + hints, + file, + caseType)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + LOG(4, ("%s: HGFS_OP_GETATTR_V3: %u\n", __FUNCTION__, *caseType)); + break; + } + + case HGFS_OP_GETATTR_V2: { + HgfsRequestGetattrV2 *requestV2 = (HgfsRequestGetattrV2 *)packet; + + if (!HgfsUnpackGetattrPayloadV2(requestV2, + packetSize, + cpName, + cpNameSize, + hints, + file)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + + case HGFS_OP_GETATTR: { + HgfsRequestGetattr *requestV1 = (HgfsRequestGetattr *)packet; + + if (!HgfsUnpackGetattrPayloadV1(requestV1, packetSize, cpName, cpNameSize)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + + default: + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackGetattrReply -- + * + * Pack hgfs getattr reply to the HgfsReplyGetattr structure. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackGetattrReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsFileAttrInfo *attr, // IN: attr stucture + const char *utf8TargetName, // IN: optional target name + uint32 utf8TargetNameLen, // IN: file name length + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + switch (attr->requestType) { + case HGFS_OP_GETATTR_V3: { + HgfsReplyGetattrV3 *reply; + + *payloadSize = sizeof *reply + utf8TargetNameLen; + result = HgfsAllocInitReply(packet, packetHeader, *payloadSize, + (void **)&reply, session); + if (result) { + HgfsPackGetattrReplyPayloadV3(attr, utf8TargetName, utf8TargetNameLen, reply); + } + break; + } + + case HGFS_OP_GETATTR_V2: { + HgfsReplyGetattrV2 *reply; + *payloadSize = sizeof *reply + utf8TargetNameLen; + + result = HgfsAllocInitReply(packet, packetHeader, *payloadSize, + (void **)&reply, session); + if (result) { + HgfsPackGetattrReplyPayloadV2(attr, + utf8TargetName, + utf8TargetNameLen, + reply); + } + break; + } + + case HGFS_OP_GETATTR: { + HgfsReplyGetattr *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + HgfsPackGetattrReplyPayloadV1(attr, reply); + *payloadSize = sizeof *reply; + } + break; + } + + default: + LOG(4, ("%s: Invalid GetAttr op.\n", __FUNCTION__)); + NOT_REACHED(); + + result = FALSE; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSearchReadReplyPayloadV3 -- + * + * Packs SearchRead V3 reply payload. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackSearchReadReplyPayloadV3(HgfsFileAttrInfo *attr, // IN: attr stucture + const char *utf8Name, // IN: file name + uint32 utf8NameLen, // IN: file name length + HgfsReplySearchReadV3 *reply) // OUT: payload +{ + HgfsDirEntry *dirent = (HgfsDirEntry *)reply->payload; + + reply->count = 1; + reply->reserved = 0; + + dirent->fileName.length = (uint32)utf8NameLen; + dirent->fileName.flags = 0; + dirent->fileName.fid = 0; + dirent->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE; + dirent->nextEntry = 0; + + if (utf8NameLen != 0) { + memcpy(dirent->fileName.name, utf8Name, utf8NameLen); + dirent->fileName.name[utf8NameLen] = 0; + + HgfsPackAttrV2(attr, &dirent->attr); + } +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSearchReadReplyPayloadV2 -- + * + * Packs SearchRead V2 reply payload. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackSearchReadReplyPayloadV2(HgfsFileAttrInfo *attr, // IN: attr stucture + const char *utf8Name, // IN: file name + uint32 utf8NameLen, // IN: file name length + HgfsReplySearchReadV2 *reply) // OUT: payload +{ + reply->fileName.length = (uint32)utf8NameLen; + + if (utf8NameLen != 0) { + memcpy(reply->fileName.name, utf8Name, utf8NameLen); + reply->fileName.name[utf8NameLen] = 0; + HgfsPackAttrV2(attr, &reply->attr); + } +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSearchReadReplyPayloadV1 -- + * + * Packs SearchRead V1 reply payload. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsPackSearchReadReplyPayloadV1(HgfsFileAttrInfo *attr, // IN: attr stucture + const char *utf8Name, // IN: file name + uint32 utf8NameLen, // IN: file name length + HgfsReplySearchRead *reply) // OUT: payload +{ + reply->fileName.length = (uint32)utf8NameLen; + + if (utf8NameLen != 0) { + memcpy(reply->fileName.name, utf8Name, utf8NameLen); + reply->fileName.name[utf8NameLen] = 0; + + /* In SearchReadV1, symlinks are treated as regular files. */ + if (attr->type == HGFS_FILE_TYPE_SYMLINK) { + reply->attr.type = HGFS_FILE_TYPE_REGULAR; + } else { + reply->attr.type = attr->type; + } + reply->attr.size = attr->size; + reply->attr.creationTime = attr->creationTime; + reply->attr.accessTime = attr->accessTime; + reply->attr.writeTime = attr->writeTime; + reply->attr.attrChangeTime = attr->attrChangeTime; + reply->attr.permissions = attr->ownerPerms; + } +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSearchReadRequest -- + * + * Unpack hgfs search read request and initialize a corresponding + * HgfsFileAttrInfo structure that is used to pass around attribute + * information. + * + * Since the structure of the search read request packet hasn't changed in + * version 2 of the protocol, HgfsRequestSearchReadV2 is identical to + * HgfsRequestSearchRead. So use HgfsRequestSearchRead type to access + * packetIn to keep the code simple. + * + * Results: + * Always TRUE. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSearchReadRequest(const void *packet, // IN: request packet + size_t packetSize, // IN: packet size + HgfsOp op, // IN: reqest type + HgfsFileAttrInfo *attr, // OUT: unpacked attr struct + HgfsHandle *hgfsSearchHandle, // OUT: hgfs search handle + uint32 *offset) // OUT: entry offset +{ + ASSERT(packet); + ASSERT(attr); + ASSERT(hgfsSearchHandle); + ASSERT(offset); + + HgfsInitFileAttr(op, attr); + + + if (op == HGFS_OP_SEARCH_READ_V3) { + HgfsRequestSearchReadV3 *request = (HgfsRequestSearchReadV3 *)packet; + + /* Enforced by the dispatch function. */ + ASSERT(packetSize >= sizeof *request); + + *hgfsSearchHandle = request->search; + *offset = request->offset; + + LOG(4, ("%s: HGFS_OP_SEARCH_READ_V3\n", __FUNCTION__)); + } else { + HgfsRequestSearchRead *request = (HgfsRequestSearchRead *)packet; + + /* Enforced by the dispatch function. */ + ASSERT(packetSize >= sizeof *request); + + *hgfsSearchHandle = request->search; + *offset = request->offset; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSearchReadReply -- + * + * Pack hgfs search read reply to the HgfsReplySearchRead{V2} structure. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackSearchReadReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + const char *utf8Name, // IN: file name + size_t utf8NameLen, // IN: file name length + HgfsFileAttrInfo *attr, // IN: file attr struct + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + switch (attr->requestType) { + case HGFS_OP_SEARCH_READ_V3: { + HgfsReplySearchReadV3 *reply; + *payloadSize = sizeof *reply + utf8NameLen + sizeof(HgfsDirEntry); + + result = HgfsAllocInitReply(packet, packetHeader, *payloadSize, + (void **)&reply, session); + if (result) { + HgfsPackSearchReadReplyPayloadV3(attr, utf8Name, utf8NameLen, reply); + } + break; + } + + case HGFS_OP_SEARCH_READ_V2: { + HgfsReplySearchReadV2 *reply; + *payloadSize = sizeof *reply + utf8NameLen; + + result = HgfsAllocInitReply(packet, packetHeader, *payloadSize, + (void **)&reply, session); + if (result) { + HgfsPackSearchReadReplyPayloadV2(attr, + utf8Name, + utf8NameLen, + reply); + } + break; + } + + case HGFS_OP_SEARCH_READ: { + HgfsReplySearchRead *reply; + *payloadSize = sizeof *reply + utf8NameLen; + + result = HgfsAllocInitReply(packet, packetHeader, *payloadSize, + (void **)&reply, session); + if (result) { + HgfsPackSearchReadReplyPayloadV1(attr, utf8Name, utf8NameLen, reply); + } + break; + } + + default: { + LOG(4, ("%s: Invalid SearchRead Op.", __FUNCTION__)); + NOT_REACHED(); + result = FALSE; + } + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSetattrPayloadV3 -- + * + * Unpack hgfs set attr request V3 payload and initialize a corresponding + * HgfsHandle or file name to tell us which file to set attributes. Hints + * holds flags to specify a handle or name for the file or + * directory to set attributes. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSetattrPayloadV3(HgfsRequestSetattrV3 *requestV3,// IN: request payload + size_t payloadSize, // IN: payload size + HgfsFileAttrInfo *attr, // OUT: setattr info + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsAttrHint *hints, // OUT: getattr hints + HgfsHandle *file, // OUT: file handle + uint32 *caseFlags) // OUT: case-sensitivity flags +{ + Bool result; + Bool useHandle; + + if (payloadSize < sizeof *requestV3) { + return FALSE; + } + + *hints = requestV3->hints; + + HgfsUnpackAttrV2(&requestV3->attr, attr); + + result = HgfsUnpackFileNameV3(&requestV3->fileName, + payloadSize - sizeof *requestV3, + &useHandle, + cpName, + cpNameSize, + file, + caseFlags); + if (useHandle) { + *hints |= HGFS_ATTR_HINT_USE_FILE_DESC; + } + + LOG(4, ("%s: unpacking HGFS_OP_SETATTR_V3, %u\n", __FUNCTION__, + *caseFlags)); + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSetattrPayloadV2 -- + * + * Unpack hgfs Setattr request V2 payload and initialize a corresponding + * HgfsHandle or file name to tell us which to set attributes. Hints + * holds flags to specify a handle or name for the file or + * directory to set attributes. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSetattrPayloadV2(HgfsRequestSetattrV2 *requestV2,// IN: request payload + size_t payloadSize, // IN: payload size + HgfsFileAttrInfo *attr, // OUT: setattr info + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsAttrHint *hints, // OUT: delete hints + HgfsHandle *file) // OUT: file handle +{ + Bool result = TRUE; + + /* Enforced by the dispatch function. */ + if (payloadSize < sizeof *requestV2) { + return FALSE; + } + + LOG(4, ("%s: unpacking HGFS_OP_SETATTR_V2\n", __FUNCTION__)); + + *file = HGFS_INVALID_HANDLE; + *hints = requestV2->hints; + + HgfsUnpackAttrV2(&requestV2->attr, attr); + + if (requestV2->hints & HGFS_ATTR_HINT_USE_FILE_DESC) { + *file = requestV2->file; + *cpName = NULL; + *cpNameSize = 0; + } else { + result = HgfsUnpackFileName(&requestV2->fileName, + payloadSize - sizeof *requestV2, + cpName, + cpNameSize); + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSetattrPayloadV1 -- + * + * Unpack hgfs setattr request V1 payload and initialize a corresponding + * file name to tell us which to set attributes. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSetattrPayloadV1(HgfsRequestSetattr *requestV1, // IN: request payload + size_t payloadSize, // IN: payload size + HgfsFileAttrInfo *attr, // OUT: setattr info + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsAttrHint *hints) // OUT: setattr hints +{ + LOG(4, ("%s: unpacking HGFS_OP_SETATTR\n", __FUNCTION__)); + + attr->mask = 0; + attr->mask |= requestV1->update & HGFS_ATTR_SIZE ? HGFS_ATTR_VALID_SIZE : 0; + attr->mask |= requestV1->update & HGFS_ATTR_CREATE_TIME ? + HGFS_ATTR_VALID_CREATE_TIME : 0; + attr->mask |= requestV1->update & HGFS_ATTR_ACCESS_TIME ? + HGFS_ATTR_VALID_ACCESS_TIME : 0; + attr->mask |= requestV1->update & HGFS_ATTR_WRITE_TIME ? + HGFS_ATTR_VALID_WRITE_TIME : 0; + attr->mask |= requestV1->update & HGFS_ATTR_CHANGE_TIME ? + HGFS_ATTR_VALID_CHANGE_TIME : 0; + attr->mask |= requestV1->update & HGFS_ATTR_PERMISSIONS ? + HGFS_ATTR_VALID_OWNER_PERMS : 0; + *hints |= requestV1->update & HGFS_ATTR_ACCESS_TIME_SET ? + HGFS_ATTR_HINT_SET_ACCESS_TIME : 0; + *hints |= requestV1->update & HGFS_ATTR_WRITE_TIME_SET ? + HGFS_ATTR_HINT_SET_WRITE_TIME : 0; + + attr->type = requestV1->attr.type; + attr->size = requestV1->attr.size; + attr->creationTime = requestV1->attr.creationTime; + attr->accessTime = requestV1->attr.accessTime; + attr->writeTime = requestV1->attr.writeTime; + attr->attrChangeTime = requestV1->attr.attrChangeTime; + attr->ownerPerms = requestV1->attr.permissions; + return HgfsUnpackFileName(&requestV1->fileName, + payloadSize - sizeof *requestV1, + cpName, + cpNameSize); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSetattrRequest -- + * + * Unpack hgfs setattr request and initialize a corresponding + * HgfsFileAttrInfo structure that is used to pass around setattr request + * information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSetattrRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + HgfsFileAttrInfo *attr, // OUT: setattr info + HgfsAttrHint *hints, // OUT: setattr hints + char **cpName, // OUT: cpName + size_t *cpNameSize, // OUT: cpName size + HgfsHandle *file, // OUT: server file ID + uint32 *caseType) // OUT: case-sensitivity flags +{ + ASSERT(packet); + ASSERT(attr); + ASSERT(cpName); + ASSERT(cpNameSize); + ASSERT(file); + ASSERT(caseType); + + attr->requestType = op; + + /* Default values for legacy requests. */ + *caseType = HGFS_FILE_NAME_DEFAULT_CASE; + *hints = 0; + *file = HGFS_INVALID_HANDLE; + + switch (op) { + case HGFS_OP_SETATTR_V3: + { + HgfsRequestSetattrV3 *requestV3 = (HgfsRequestSetattrV3 *)packet; + if (!HgfsUnpackSetattrPayloadV3(requestV3, + packetSize, + attr, + cpName, + cpNameSize, + hints, + file, + caseType)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + + case HGFS_OP_SETATTR_V2: + { + HgfsRequestSetattrV2 *requestV2 = (HgfsRequestSetattrV2 *)packet; + if (!HgfsUnpackSetattrPayloadV2(requestV2, + packetSize, + attr, + cpName, + cpNameSize, + hints, + file)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_SETATTR: + { + HgfsRequestSetattr *requestV1 = (HgfsRequestSetattr *)packet; + if (!HgfsUnpackSetattrPayloadV1(requestV1, + packetSize, + attr, + cpName, + cpNameSize, + hints)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + default: + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSetattrReply -- + * + * Pack hgfs setattr reply. + * Since the structure of the set attributes reply packet hasn't changed in + * version 2 of the protocol, HgfsReplySetattrV2 is identical to + * HgfsReplySetattr. So use HgfsReplySetattr type to access packetIn to + * keep the code simple. + * + * Results: + * TRUE if valid op and reply set, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackSetattrReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_SETATTR_V3: { + HgfsReplySetattrV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_SETATTR_V2: + case HGFS_OP_SETATTR: { + HgfsReplySetattr *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + default: + result = FALSE; + LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); + NOT_REACHED(); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackCreateDirPayloadV3 -- + * + * Unpack hgfs create directory request V3 payload and initialize a corresponding + * file name to tell us which directory to create. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackCreateDirPayloadV3(HgfsRequestCreateDirV3 *requestV3, // IN: request payload + size_t payloadSize, // IN: payload size + HgfsCreateDirInfo *info) // IN/OUT: info struct +{ + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + + LOG(4, ("%s: HGFS_OP_CREATE_DIR_V3\n", __FUNCTION__)); + ASSERT(payloadSize >= sizeof *requestV3); + if (requestV3->fileName.length > payloadSize - sizeof *requestV3) { + /* The input packet is smaller than the request. */ + return FALSE; + } + if (!(requestV3->mask & HGFS_CREATE_DIR_VALID_FILE_NAME)) { + /* We do not support requests without a valid file name. */ + LOG(4, ("%s: Incorrect mask %x\n", __FUNCTION__, (uint32)requestV3->mask)); + return FALSE; + } + + /* + * Copy all the fields into our carrier struct. Some will probably be + * garbage, but it's simpler to copy everything now and check the + * valid bits before reading later. + */ + + info->mask = requestV3->mask; + info->cpName = requestV3->fileName.name; + info->cpNameSize = requestV3->fileName.length; + info->caseFlags = requestV3->fileName.caseType; + info->specialPerms = requestV3->specialPerms; + info->fileAttr = requestV3->fileAttr; + info->ownerPerms = requestV3->ownerPerms; + info->groupPerms = requestV3->groupPerms; + info->otherPerms = requestV3->otherPerms; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackCreateDirPayloadV2 -- + * + * Unpack hgfs create directory request V2 payload and initialize a corresponding + * file name to tell us which directory to create. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackCreateDirPayloadV2(HgfsRequestCreateDirV2 *requestV2, // IN: request payload + size_t payloadSize, // IN: payload size + HgfsCreateDirInfo *info) // IN/OUT: info struct +{ + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + + LOG(4, ("%s: HGFS_OP_CREATE_DIR_V2\n", __FUNCTION__)); + ASSERT(payloadSize >= sizeof *requestV2); + if (requestV2->fileName.length > payloadSize - sizeof *requestV2) { + /* The input packet is smaller than the request. */ + return FALSE; + } + if (!(requestV2->mask & HGFS_CREATE_DIR_VALID_FILE_NAME)) { + /* We do not support requests without a valid file name. */ + LOG(4, ("%s: Incorrect mask %x\n", __FUNCTION__, (uint32)requestV2->mask)); + return FALSE; + } + + /* + * Copy all the fields into our carrier struct. Some will probably be + * garbage, but it's simpler to copy everything now and check the + * valid bits before reading later. + */ + + info->mask = requestV2->mask; + info->cpName = requestV2->fileName.name; + info->cpNameSize = requestV2->fileName.length; + info->specialPerms = requestV2->specialPerms; + info->ownerPerms = requestV2->ownerPerms; + info->groupPerms = requestV2->groupPerms; + info->otherPerms = requestV2->otherPerms; + info->fileAttr = 0; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackCreateDirPayloadV1 -- + * + * Unpack hgfs create directory request V1 payload and initialize a corresponding + * file name to tell us which directory to create. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackCreateDirPayloadV1(HgfsRequestCreateDir *requestV1, // IN: request payload + size_t payloadSize, // IN: payload size + HgfsCreateDirInfo *info) // IN/OUT: info struct +{ + /* + * The request file name length is user-provided, so this test must be + * carefully written to prevent wraparounds. + */ + + LOG(4, ("%s: HGFS_OP_CREATE_DIR_V1\n", __FUNCTION__)); + ASSERT(payloadSize >= sizeof *requestV1); + if (requestV1->fileName.length > payloadSize - sizeof *requestV1) { + /* The input packet is smaller than the request. */ + LOG(4, ("%s: HGFS packet too small for the file name\n", __FUNCTION__)); + return FALSE; + } + + /* For CreateDirV1 requests, we know exactly what fields we expect. */ + info->mask = HGFS_CREATE_DIR_VALID_OWNER_PERMS | HGFS_CREATE_DIR_VALID_FILE_NAME; + info->cpName = requestV1->fileName.name; + info->cpNameSize = requestV1->fileName.length; + info->ownerPerms = requestV1->permissions; + info->fileAttr = 0; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackCreateDirRequest -- + * + * Unpack hgfs CreateDir request and initialize a corresponding + * HgfsCreateDirInfo structure that is used to pass around CreateDir request + * information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackCreateDirRequest(void const *packet, // IN: incoming packet + size_t packetSize, // IN: size of packet + HgfsOp op, // IN: request type + HgfsCreateDirInfo *info) // IN/OUT: info struct +{ + ASSERT(packet); + ASSERT(info); + + info->requestType = op; + /* Default value for legacy requests. */ + info->caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + + switch (op) { + case HGFS_OP_CREATE_DIR_V3: + { + HgfsRequestCreateDirV3 *requestV3 = (HgfsRequestCreateDirV3 *)packet; + if (!HgfsUnpackCreateDirPayloadV3(requestV3, + packetSize, + info)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + + case HGFS_OP_CREATE_DIR_V2: + { + HgfsRequestCreateDirV2 *requestV2 = (HgfsRequestCreateDirV2 *)packet; + if (!HgfsUnpackCreateDirPayloadV2(requestV2, + packetSize, + info)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_CREATE_DIR: + { + HgfsRequestCreateDir *requestV1 = (HgfsRequestCreateDir *)packet; + if (!HgfsUnpackCreateDirPayloadV1(requestV1, + packetSize, + info)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + default: + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackCreateDirReply -- + * + * Pack hgfs CreateDir reply. + * + * Results: + * TRUE if valid op and reply set, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackCreateDirReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_CREATE_DIR_V3: { + HgfsReplyCreateDirV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_CREATE_DIR_V2: { + HgfsReplyCreateDirV2 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_CREATE_DIR: { + HgfsReplyCreateDir *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + default: + result = FALSE; + LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); + NOT_REACHED(); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackWriteWin32StreamPayloadV3 -- + * + * Unpack hgfs write stream request V3 payload and initialize a corresponding + * file name to tell us which directory to create. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackWriteWin32StreamPayloadV3(HgfsRequestWriteWin32StreamV3 *requestV3, // IN: + size_t payloadSize, // IN: + HgfsHandle *file, // OUT: + char **data, // OUT: + size_t *dataSize, // OUT: + Bool *doSecurity) // OUT: +{ + LOG(4, ("%s: HGFS_OP_WRITE_WIN32_STREAM_V3\n", __FUNCTION__)); + if (payloadSize < sizeof *requestV3) { + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; + } + + if (payloadSize >= requestV3->requiredSize + sizeof *requestV3) { + *file = requestV3->file; + *data = requestV3->payload; + *dataSize = requestV3->requiredSize; + *doSecurity = (requestV3->flags & HGFS_WIN32_STREAM_IGNORE_SECURITY) == 0; + return TRUE; + } + + LOG(4, ("%s: HGFS packet too small - user data do not fit\n", __FUNCTION__)); + return FALSE; +} + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackWriteWin32StreamRequest -- + * + * Unpack hgfs SendFileUsingReader request. Returns file to write to, data + * and whether to restore the security stream. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackWriteWin32StreamRequest(void const *packet, // IN: incoming packet + size_t packetSize, // IN: size of packet + HgfsOp op, // IN: request type + HgfsHandle *file, // OUT: file to write to + char **data, // OUT: data to write + size_t *dataSize, // OUT: size of data + Bool *doSecurity) // OUT: restore sec.str. +{ + ASSERT(packet); + ASSERT(file); + ASSERT(data); + ASSERT(dataSize); + ASSERT(doSecurity); + + if (op != HGFS_OP_WRITE_WIN32_STREAM_V3) { + /* The only supported version for the moment is V3. */ + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + return FALSE; + } + + return HgfsUnpackWriteWin32StreamPayloadV3((HgfsRequestWriteWin32StreamV3 *)packet, + packetSize, + file, + data, + dataSize, + doSecurity); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackWriteWin32StreamReply -- + * + * Pack hgfs SendFileUsingReader reply. + * Returns the actual amount of data written in the reply. + * + * Results: + * TRUE if valid op and reply set, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackWriteWin32StreamReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + uint32 actualSize, // IN: amount written + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + HgfsReplyWriteWin32StreamV3 *reply; + Bool result; + + *payloadSize = 0; + + if (HGFS_OP_WRITE_WIN32_STREAM_V3 == op) { + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->actualSize = actualSize; + *payloadSize = sizeof *reply; + } + } else { + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + result = FALSE; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackReadPayload -- + * + * Unpack hgfs read payload to get the file handle and file offset to read from and + * the length of data to read. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackReadPayload(HgfsRequestRead *request, // IN: payload + size_t payloadSize, // IN: payload size + HgfsHandle* file, // OUT: HGFS handle to close + uint64 *offset, // OUT: offset to read from + uint32 *length) // OUT: length of data to read +{ + LOG(4, ("%s: HGFS_OP_READ\n", __FUNCTION__)); + if (payloadSize >= sizeof *request) { + *file = request->file; + *offset = request->offset; + *length = request->requiredSize; + return TRUE; + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackReadPayloadV3 -- + * + * Unpack hgfs read payload V3 to get parameters needed to perform read. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackReadPayloadV3(HgfsRequestReadV3 *requestV3, // IN: payload + size_t payloadSize, // IN: payload size + HgfsHandle* file, // OUT: HGFS handle to close + uint64 *offset, // OUT: offset to read from + uint32 *length) // OUT: length of data to read +{ + LOG(4, ("%s: HGFS_OP_READ_V3\n", __FUNCTION__)); + if (payloadSize >= sizeof *requestV3) { + *file = requestV3->file; + *offset = requestV3->offset; + *length = requestV3->requiredSize; + return TRUE; + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackReadRequest -- + * + * Unpack hgfs read request. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackReadRequest(void const *packet, // IN: HGFS request + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + HgfsHandle *file, // OUT: Handle to close + uint64 *offset, // OUT: offset to read from + uint32 *length) // OUT: length of data to read +{ + Bool result; + + ASSERT(packet); + + switch (op) { + case HGFS_OP_READ_FAST_V4: + case HGFS_OP_READ_V3: { + HgfsRequestReadV3 *requestV3 = (HgfsRequestReadV3 *)packet; + + result = HgfsUnpackReadPayloadV3(requestV3, packetSize, file, offset, length); + break; + } + case HGFS_OP_READ: { + HgfsRequestRead *requestV1 = (HgfsRequestRead *)packet; + + result = HgfsUnpackReadPayload(requestV1, packetSize, file, offset, length); + break; + } + default: + NOT_REACHED(); + result = FALSE; + } + + if (!result) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackWritePayload -- + * + * Unpack hgfs write payload to get the file handle, file offset, of data to write, + * write flags and pointer to the data to write. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackWritePayload(HgfsRequestWrite *request, // IN: request payload + size_t payloadSize, // IN: request payload size + HgfsHandle* file, // OUT: HGFS handle to write to + uint64 *offset, // OUT: offset to read from + uint32 *length, // OUT: length of data to write + HgfsWriteFlags *flags, // OUT: write flags + char **data) // OUT: data to be written +{ + LOG(4, ("%s: HGFS_OP_WRITE\n", __FUNCTION__)); + if (payloadSize >= sizeof *request) { + if (sizeof *request + request->requiredSize - 1 <= payloadSize) { + *file = request->file; + *flags = request->flags; + *offset = request->offset; + *data = request->payload; + *length = request->requiredSize; + return TRUE; + } + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackWritePayloadV3 -- + * + * Unpack hgfs write payload V3. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackWritePayloadV3(HgfsRequestWriteV3 *requestV3, // IN: payload + size_t payloadSize, // IN: request payload size + HgfsHandle* file, // OUT: HGFS handle write to + uint64 *offset, // OUT: offset to read from + uint32 *length, // OUT: length of data to write + HgfsWriteFlags *flags, // OUT: write flags + char **data) // OUT: data to be written +{ + LOG(4, ("%s: HGFS_OP_WRITE_V3\n", __FUNCTION__)); + if (payloadSize >= sizeof *requestV3) { + if (sizeof *requestV3 + requestV3->requiredSize - 1 <= payloadSize) { + *file = requestV3->file; + *flags = requestV3->flags; + *offset = requestV3->offset; + *data = requestV3->payload; + *length = requestV3->requiredSize; + return TRUE; + } + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackWriteFastPayloadV4 -- + * + * Unpack hgfs write fast payload V4. + * The only difference from V3 payload is that data to write are + * provided in the payload but located in a separate buffer. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackWriteFastPayloadV4(HgfsRequestWriteV3 *requestV3, // IN: payload + size_t payloadSize, // IN: request payload size + HgfsHandle* file, // OUT: HGFS handle write to + uint64 *offset, // OUT: offset to write to + uint32 *length, // OUT: size of data to write + HgfsWriteFlags *flags) // OUT: write flags +{ + LOG(4, ("%s: HGFS_OP_WRITE_V3\n", __FUNCTION__)); + if (payloadSize >= sizeof *requestV3) { + *file = requestV3->file; + *flags = requestV3->flags; + *offset = requestV3->offset; + *length = requestV3->requiredSize; + return TRUE; + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackWriteRequest -- + * + * Unpack hgfs write request to get parameters and data to write. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackWriteRequest(HgfsInputParam *input, // IN: Input params + HgfsHandle *file, // OUT: Handle to write to + uint64 *offset, // OUT: offset to write to + uint32 *length, // OUT: length of data to write + HgfsWriteFlags *flags, // OUT: write flags + char **data) // OUT: data to be written +{ + Bool result; + + ASSERT(input); + + switch (input->op) { + case HGFS_OP_WRITE_FAST_V4: { + HgfsRequestWriteV3 *requestV3 = (HgfsRequestWriteV3 *)input->payload; + + result = HgfsUnpackWriteFastPayloadV4(requestV3, input->payloadSize, file, + offset, length, flags); + if (result) { + *data = HSPU_GetDataPacketBuf(input->packet, + BUF_READABLE, + input->session); + if (NULL == *data) { + LOG(4, ("%s: Failed to get data in guest memory\n", __FUNCTION__)); + result = FALSE; + } + } + break; + } + case HGFS_OP_WRITE_V3: { + HgfsRequestWriteV3 *requestV3 = (HgfsRequestWriteV3 *)input->payload; + + result = HgfsUnpackWritePayloadV3(requestV3, input->payloadSize, file, offset, + length, flags, data); + break; + } + case HGFS_OP_WRITE: { + HgfsRequestWrite *requestV1 = (HgfsRequestWrite *)input->payload; + + result = HgfsUnpackWritePayload(requestV1, input->payloadSize, file, offset, + length, flags, data); + break; + } + default: + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, input->op)); + NOT_REACHED(); + result = FALSE; + } + + if (!result) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackWriteReply -- + * + * Pack hgfs write reply to the HgfsReplyWrite structure. + * + * Results: + * TRUE is there are no bugs in the code. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackWriteReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + uint32 actualSize, // IN: number of bytes that were written + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_WRITE_FAST_V4: + case HGFS_OP_WRITE_V3: { + HgfsReplyWriteV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->actualSize = actualSize; + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_WRITE: { + HgfsReplyWrite *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->actualSize = actualSize; + *payloadSize = sizeof *reply; + } + break; + } + default: + NOT_REACHED(); + result = FALSE; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackQueryVolumePayload -- + * + * Unpack hgfs query volume payload to get the file name which must be used to query + * volume information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackQueryVolumePayload(HgfsRequestQueryVolume *request, // IN: request payload + size_t payloadSize, // IN: request payload size + char **fileName, // OUT: volume name + size_t *nameLength) // OUT: volume name length +{ + LOG(4, ("%s: HGFS_OP_QUERY_VOLUME_INFO\n", __FUNCTION__)); + if (payloadSize >= sizeof *request) { + return HgfsUnpackFileName(&request->fileName, + payloadSize - sizeof *request + 1, + fileName, + nameLength); + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackQueryVolumePayloadV3 -- + * + * Unpack hgfs query volume payload V3. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackQueryVolumePayloadV3(HgfsRequestQueryVolumeV3 *requestV3, // IN: payload + size_t payloadSize, // IN: payload size + Bool *useHandle, // OUT: use handle + HgfsHandle* file, // OUT: HGFS handle + char **fileName, // OUT: volume name + size_t *nameLength, // OUT: name length + uint32 * caseFlags) // OUT: case flags +{ + LOG(4, ("%s: HGFS_OP_QUERY_VOLUME_INFO_V3\n", __FUNCTION__)); + if (payloadSize >= sizeof *requestV3) { + return HgfsUnpackFileNameV3(&requestV3->fileName, + payloadSize - sizeof *requestV3 + 1, + useHandle, + fileName, + nameLength, + file, + caseFlags); + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackQueryVolumeRequest -- + * + * Unpack hgfs query volume information request to get parameters related to + * query volume operation. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackQueryVolumeRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + Bool *useHandle, // OUT: use handle + char **fileName, // OUT: file name + size_t *fileNameLength, // OUT: file name length + uint32 *caseFlags, // OUT: case sensitivity + HgfsHandle *file) // OUT: Handle to the volume +{ + ASSERT(packet); + + switch (op) { + case HGFS_OP_QUERY_VOLUME_INFO_V3: { + HgfsRequestQueryVolumeV3 *requestV3 = (HgfsRequestQueryVolumeV3 *)packet; + + if (!HgfsUnpackQueryVolumePayloadV3(requestV3, packetSize, useHandle, file, + fileName, fileNameLength, caseFlags)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_QUERY_VOLUME_INFO: { + HgfsRequestQueryVolume *requestV1 = (HgfsRequestQueryVolume *)packet; + + if (!HgfsUnpackQueryVolumePayload(requestV1, packetSize, fileName, + fileNameLength)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + *file = HGFS_INVALID_HANDLE; + *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + *useHandle = FALSE; + break; + } + default: + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackQueryVolumeReply -- + * + * Pack hgfs query volume reply. + * + * Results: + * TRUE if valid op and reply set, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackQueryVolumeReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + uint64 freeBytes, // IN: volume free space + uint64 totalBytes, // IN: volume capacity + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_QUERY_VOLUME_INFO_V3: { + HgfsReplyQueryVolumeV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->freeBytes = freeBytes; + reply->totalBytes = totalBytes; + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_QUERY_VOLUME_INFO: { + HgfsReplyQueryVolume *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->freeBytes = freeBytes; + reply->totalBytes = totalBytes; + *payloadSize = sizeof *reply; + } + break; + } + default: + result = FALSE; + LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); + NOT_REACHED(); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSymlinkCreatePayload -- + * + * Unpack hgfs symbolic link payload to get symbolic link file name + * and symbolic link target. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSymlinkCreatePayload(HgfsRequestSymlinkCreate *request, // IN: request payload + size_t payloadSize, // IN: payload size + char **srcFileName, // OUT: link file name + size_t *srcNameLength, // OUT: file name length + char **tgFileName, // OUT: target file name + size_t *tgNameLength) // OUT: target name length +{ + uint32 prefixSize; + + LOG(4, ("%s: HGFS_OP_CREATE_SYMLINK_V3\n", __FUNCTION__)); + prefixSize = offsetof(HgfsRequestSymlinkCreate, symlinkName.name); + if (payloadSize >= prefixSize) { + if (HgfsUnpackFileName(&request->symlinkName, + payloadSize - prefixSize, + srcFileName, + srcNameLength)) { + HgfsFileName *targetName = (HgfsFileName *)(*srcFileName + 1 + *srcNameLength); + prefixSize = ((char *)targetName - (char *)request) + offsetof(HgfsFileName, name); + + return HgfsUnpackFileName(targetName, + payloadSize - prefixSize, + tgFileName, + tgNameLength); + }; + } + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSymlinkCreatePayloadV3 -- + * + * Unpack hgfs create symbolic link payload V3. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSymlinkCreatePayloadV3(HgfsRequestSymlinkCreateV3 *requestV3, // IN: + size_t payloadSize, // IN: + Bool *srcUseHandle, // OUT: + HgfsHandle* srcFile, // OUT: + char **srcFileName, // OUT: + size_t *srcNameLength, // OUT: + uint32 *srcCaseFlags, // OUT: + Bool *tgUseHandle, // OUT: + HgfsHandle* tgFile, // OUT: + char **tgFileName, // OUT: + size_t *tgNameLength, // OUT: + uint32 * tgCaseFlags) // OUT: +{ + uint32 prefixSize; + + LOG(4, ("%s: HGFS_OP_CREATE_SYMLINK_V3\n", __FUNCTION__)); + prefixSize = offsetof(HgfsRequestSymlinkCreateV3, symlinkName.name); + if (payloadSize >= prefixSize) { + if (HgfsUnpackFileNameV3(&requestV3->symlinkName, + payloadSize - prefixSize, + srcUseHandle, + srcFileName, + srcNameLength, + srcFile, + srcCaseFlags)) { + HgfsFileNameV3 *targetName = (HgfsFileNameV3 *)(*srcFileName + 1 + + *srcNameLength); + prefixSize = ((char *)targetName - (char *)requestV3) + + offsetof(HgfsFileNameV3, name); + + return HgfsUnpackFileNameV3(targetName, + payloadSize - prefixSize, + tgUseHandle, + tgFileName, + tgNameLength, + tgFile, + tgCaseFlags); + } + } + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSymlinkCreateRequest -- + * + * Unpack hgfs symbolic link creation request to get parameters related to + * creating the symbolic link. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSymlinkCreateRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + Bool *srcUseHandle, // OUT: use source handle + char **srcFileName, // OUT: source file name + size_t *srcFileNameLength, // OUT: source file name length + uint32 *srcCaseFlags, // OUT: source case sensitivity + HgfsHandle *srcFile, // OUT: source file handle + Bool *tgUseHandle, // OUT: use target handle + char **tgFileName, // OUT: target file name + size_t *tgFileNameLength, // OUT: target file name length + uint32 *tgCaseFlags, // OUT: target case sensitivity + HgfsHandle *tgFile) // OUT: target file handle +{ + ASSERT(packet); + + switch (op) { + case HGFS_OP_CREATE_SYMLINK_V3: { + HgfsRequestSymlinkCreateV3 *requestV3 = (HgfsRequestSymlinkCreateV3 *)packet; + + if (!HgfsUnpackSymlinkCreatePayloadV3(requestV3, packetSize, + srcUseHandle, srcFile, + srcFileName, srcFileNameLength, srcCaseFlags, + tgUseHandle, tgFile, + tgFileName, tgFileNameLength, tgCaseFlags)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_CREATE_SYMLINK: { + HgfsRequestSymlinkCreate *requestV1 = (HgfsRequestSymlinkCreate *)packet; + + if (!HgfsUnpackSymlinkCreatePayload(requestV1, packetSize, srcFileName, + srcFileNameLength, tgFileName, tgFileNameLength)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + *srcFile = HGFS_INVALID_HANDLE; + *srcCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + *srcUseHandle = FALSE; + *tgFile = HGFS_INVALID_HANDLE; + *tgCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + *tgUseHandle = FALSE; + break; + } + default: + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSymlinkCreateReply -- + * + * Pack hgfs symbolic link creation reply. + * + * Results: + * TRUE if valid op and reply set, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackSymlinkCreateReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_CREATE_SYMLINK_V3: { + HgfsReplyQueryVolumeV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_CREATE_SYMLINK: { + HgfsReplyQueryVolume *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + *payloadSize = sizeof *reply; + } + break; + } + default: + result = FALSE; + LOG(4, ("%s: invalid op code %d\n", __FUNCTION__, op)); + NOT_REACHED(); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSearchOpenPayload -- + * + * Unpack hgfs search open payload to get name of directory to open. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSearchOpenPayload(HgfsRequestSearchOpen *request, // IN: payload + size_t payloadSize, // IN: payload size + char **dirName, // OUT: directory name + uint32 *dirNameLength) // OUT: name length +{ + LOG(4, ("%s: HGFS_OP_SEARCH_OPEN\n", __FUNCTION__)); + if (payloadSize >= sizeof *request) { + if (sizeof *request + request->dirName.length - 1 <= payloadSize) { + *dirName = request->dirName.name; + *dirNameLength = request->dirName.length; + return TRUE; + } + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSearchOpenPayloadV3 -- + * + * Unpack hgfs search open payload V3 to get name of directory to open and + * case flags. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSearchOpenPayloadV3(HgfsRequestSearchOpenV3 *requestV3, // IN: payload + size_t payloadSize, // IN: payload size + char **dirName, // OUT: directory name + uint32 *dirNameLength, // OUT: name length + uint32 *caseFlags) // OUT: case flags +{ + LOG(4, ("%s: HGFS_OP_SEARCH_OPEN_V3\n", __FUNCTION__)); + if (payloadSize >= sizeof *requestV3) { + if (sizeof *requestV3 + requestV3->dirName.length - 1 <= payloadSize) { + *dirName = requestV3->dirName.name; + *dirNameLength = requestV3->dirName.length; + *caseFlags = requestV3->dirName.flags; + return TRUE; + } + } + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackSearchOpenRequest -- + * + * Unpack hgfs search open request to get directory name and case flags. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackSearchOpenRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: request packet size + HgfsOp op, // IN: request type + char **dirName, // OUT: directory name + uint32 *dirNameLength, // OUT: name length + uint32 *caseFlags) // OUT: case flags +{ + ASSERT(packet); + + switch (op) { + case HGFS_OP_SEARCH_OPEN_V3: { + HgfsRequestSearchOpenV3 *requestV3 = (HgfsRequestSearchOpenV3 *)packet; + + if (!HgfsUnpackSearchOpenPayloadV3(requestV3, packetSize, dirName, + dirNameLength, caseFlags)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + break; + } + case HGFS_OP_SEARCH_OPEN: { + HgfsRequestSearchOpen *requestV1 = (HgfsRequestSearchOpen *)packet; + + if (!HgfsUnpackSearchOpenPayload(requestV1, packetSize, dirName, + dirNameLength)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE; + break; + } + default: + LOG(4, ("%s: Incorrect opcode %d\n", __FUNCTION__, op)); + NOT_REACHED(); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackSearchOpenReply -- + * + * Pack hgfs search open reply. + * + * Results: + * TRUE unless it is invoked for a wrong op (which indicates a bug in the code). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackSearchOpenReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + HgfsOp op, // IN: request type + HgfsHandle search, // IN: search handle + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + switch (op) { + case HGFS_OP_SEARCH_OPEN_V3: { + HgfsReplySearchOpenV3 *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->search = search; + *payloadSize = sizeof *reply; + } + break; + } + case HGFS_OP_SEARCH_OPEN: { + HgfsReplySearchOpen *reply; + + result = HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); + if (result) { + reply->search = search; + *payloadSize = sizeof *reply; + } + break; + } + default: + NOT_REACHED(); + result = FALSE; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackCreateSessionPayloadV4 -- + * + * Unpack hgfs create session request V4 payload. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackCreateSessionPayloadV4(HgfsRequestCreateSessionV4 *requestV4, // IN: payload + size_t payloadSize, // IN: + HgfsCreateSessionInfo *info) // IN/OUT: info +{ + LOG(4, ("%s: HGFS_OP_CREATE_SESSION_V4\n", __FUNCTION__)); + if (payloadSize < offsetof(HgfsRequestCreateSessionV4, reserved)) { + /* The input packet is smaller than the request. */ + return FALSE; + } + + if (requestV4->numCapabilities) { + if (payloadSize < offsetof(HgfsRequestCreateSessionV4, capabilities) + + requestV4->numCapabilities * sizeof(HgfsCapability)) { + LOG(4, ("%s: HGFS packet too small\n", __FUNCTION__)); + return FALSE; + } + } + + info->maxPacketSize = requestV4->maxPacketSize; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsUnpackCreateSessionRequest -- + * + * Unpack hgfs CreateSession request and initialize a corresponding + * HgfsCreateDirInfo structure that is used to pass around CreateDir request + * information. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsUnpackCreateSessionRequest(void const *packet, // IN: HGFS packet + size_t packetSize, // IN: size of packet + HgfsOp op, // IN: request type + HgfsCreateSessionInfo *info) // IN/OUT: info struct +{ + HgfsRequestCreateSessionV4 *requestV4; + + ASSERT(packet); + ASSERT(info); + + ASSERT(op == HGFS_OP_CREATE_SESSION_V4); + + requestV4 = (HgfsRequestCreateSessionV4 *)packet; + if (!HgfsUnpackCreateSessionPayloadV4(requestV4, packetSize, info)) { + LOG(4, ("%s: Error decoding HGFS packet\n", __FUNCTION__)); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackCreateSessionReply -- + * + * Pack hgfs CreateSession reply. + * + * Results: + * Always TRUE. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackCreateSessionReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + Bool result; + HgfsReplyCreateSessionV4 *reply; + uint32 numCapabilities = ARRAYSIZE(hgfsServerCapabilities); + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = offsetof(HgfsReplyCreateSessionV4, capabilities) + + sizeof hgfsServerCapabilities; + + result = HgfsAllocInitReply(packet, packetHeader, *payloadSize, + (void **)&reply, session); + if (result) { + reply->sessionId = session->sessionId; + reply->numCapabilities = numCapabilities; + reply->maxPacketSize = session->maxPacketSize; + reply->identityOffset = 0; + reply->reserved = 0; + memcpy(reply->capabilities, hgfsServerCapabilities, + sizeof hgfsServerCapabilities); + } + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsPackDestroySessionReply -- + * + * Pack hgfs CreateSession reply. + * + * Results: + * Always TRUE. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsPackDestorySessionReply(HgfsPacket *packet, // IN/OUT: Hgfs Packet + char const *packetHeader, // IN: packet header + size_t *payloadSize, // OUT: size of packet + HgfsSessionInfo *session) // IN: Session info +{ + HgfsReplyDestroySessionV4 *reply; + + HGFS_ASSERT_PACK_PARAMS; + + *payloadSize = 0; + + return HgfsAllocInitReply(packet, packetHeader, sizeof *reply, + (void **)&reply, session); +} diff --git a/open-vm-tools/lib/include/hgfsProto.h b/open-vm-tools/lib/include/hgfsProto.h index 73bb61e80..f974c5f63 100644 --- a/open-vm-tools/lib/include/hgfsProto.h +++ b/open-vm-tools/lib/include/hgfsProto.h @@ -118,7 +118,6 @@ typedef enum { HGFS_OP_CREATE_SYMLINK_V3, /* Create a symlink */ HGFS_OP_SERVER_LOCK_CHANGE_V3, /* Change the oplock on a file */ HGFS_OP_WRITE_WIN32_STREAM_V3, /* Write WIN32_STREAM_ID format data to file */ - /* * Operations for version 4, deprecating version 3 operations. */ @@ -1663,6 +1662,10 @@ struct HgfsHeader { #include "vmware_pack_end.h" HgfsHeader; +/* + * If no flags are set then the capability is not supported by the host. + */ +#define HGFS_REQUEST_NOT_SUPPORTED 0 /* * Flag HGFS_REQUEST_SUPPORTED is set for every requests that are supported by the host. */ @@ -1761,18 +1764,19 @@ HgfsIdentity; typedef #include "vmware_pack_begin.h" -struct HgfsRequestCreateSession { +struct HgfsRequestCreateSessionV4 { uint32 numCapabilities; /* Number of capabilities to follow. */ uint32 maxPacketSize; /* Maximum packet size supported. */ uint64 reserved; /* Reserved for future use. */ HgfsCapability capabilities[1]; /* Array of HgfsCapabilities. */ } #include "vmware_pack_end.h" -HgfsRequestCreateSession; +HgfsRequestCreateSessionV4; +#define HGFS_INVALID_SESSION_ID 0xffffffff typedef #include "vmware_pack_begin.h" -struct HgfsReplyCreateSession { +struct HgfsReplyCreateSessionV4 { uint64 sessionId; /* Session ID. */ uint32 numCapabilities; /* Number of capabilities to follow. */ uint32 maxPacketSize; /* Maximum packet size supported. */ @@ -1781,24 +1785,23 @@ struct HgfsReplyCreateSession { HgfsCapability capabilities[1]; /* Array of HgfsCapabilities. */ } #include "vmware_pack_end.h" -HgfsReplyCreateSession; +HgfsReplyCreateSessionV4; typedef #include "vmware_pack_begin.h" -struct HgfsRequestDestroySession { - uint64 sessionId; /* Session ID. */ +struct HgfsRequestDestroySessionV4 { uint64 reserved; /* Reserved for future use. */ } #include "vmware_pack_end.h" -HgfsRequestDestroySession; +HgfsRequestDestroySessionV4; typedef #include "vmware_pack_begin.h" -struct HgfsReplyDestroySession { +struct HgfsReplyDestroySessionV4 { uint64 reserved; /* Reserved for future use. */ } #include "vmware_pack_end.h" -HgfsReplyDestroySession; +HgfsReplyDestroySessionV4; /* Adds new error status: HGFS_STATUS_INVALID_SESSION. */ diff --git a/open-vm-tools/lib/include/hgfsUtil.h b/open-vm-tools/lib/include/hgfsUtil.h index e95894d80..ef9096d5e 100644 --- a/open-vm-tools/lib/include/hgfsUtil.h +++ b/open-vm-tools/lib/include/hgfsUtil.h @@ -80,11 +80,48 @@ struct timespec { # include # endif typedef int HgfsInternalStatus; + /* + * There is no internal error in Linux. + * Define a const that is converted to HGFS_INTERNAL_STATUS_ERROR. + */ +# define EINTERNAL 1001 #else # include typedef DWORD HgfsInternalStatus; #endif +#if defined _WIN32 +#define HGFS_ERROR_SUCCESS ERROR_SUCCESS +#define HGFS_ERROR_IO ERROR_IO_DEVICE +#define HGFS_ERROR_ACCESS_DENIED ERROR_ACCESS_DENIED +#define HGFS_ERROR_INVALID_PARAMETER ERROR_INVALID_PARAMETER +#define HGFS_ERROR_INVALID_HANDLE ERROR_INVALID_HANDLE +#define HGFS_ERROR_PROTOCOL RPC_S_PROTOCOL_ERROR +#define HGFS_ERROR_STALE_SESSION ERROR_CONNECTION_INVALID +#define HGFS_ERROR_BUSY ERROR_RETRY +#define HGFS_ERROR_PATH_BUSY ERROR_RETRY +#define HGFS_ERROR_FILE_NOT_FOUND ERROR_FILE_NOT_FOUND +#define HGFS_ERROR_FILE_EXIST ERROR_ALREADY_EXISTS +#define HGFS_ERROR_NOT_SUPPORTED ERROR_NOT_SUPPORTED +#define HGFS_ERROR_NOT_ENOUGH_MEMORY ERROR_NOT_ENOUGH_MEMORY +#define HGFS_ERROR_INTERNAL ERROR_INTERNAL_ERROR +#else +#define HGFS_ERROR_SUCCESS 0 +#define HGFS_ERROR_IO EIO +#define HGFS_ERROR_ACCESS_DENIED EACCES +#define HGFS_ERROR_INVALID_PARAMETER EINVAL +#define HGFS_ERROR_INVALID_HANDLE EINVAL +#define HGFS_ERROR_PROTOCOL EPROTO +#define HGFS_ERROR_STALE_SESSION ENETRESET +#define HGFS_ERROR_BUSY EBUSY +#define HGFS_ERROR_PATH_BUSY EBUSY +#define HGFS_ERROR_FILE_NOT_FOUND ENOENT +#define HGFS_ERROR_FILE_EXIST EEXIST +#define HGFS_ERROR_NOT_SUPPORTED EOPNOTSUPP +#define HGFS_ERROR_NOT_ENOUGH_MEMORY ENOMEM +#define HGFS_ERROR_INTERNAL EINTERNAL +#endif // _WIN32 + /* * Unfortunately, we need a catch-all "generic error" to use with * HgfsInternalStatus, because there are times when cross-platform code needs