diff options
Diffstat (limited to 'open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c')
-rw-r--r-- | open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c | 3274 |
1 files changed, 0 insertions, 3274 deletions
diff --git a/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c b/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c deleted file mode 100644 index 5857ed74..00000000 --- a/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c +++ /dev/null @@ -1,3274 +0,0 @@ -/********************************************************* - * Copyright (C) 2008 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation version 2 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 GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *********************************************************/ - -/* - * vnopscommon.c -- - * - * Common VFS vnop implementations that are shared between both Mac OS and FreeBSD. - */ - -#include <sys/param.h> // for everything -#include <sys/vnode.h> // for struct vnode -#include <sys/dirent.h> // for struct dirent - -#include "fsutil.h" -#include "debug.h" -#include "vnopscommon.h" -#include "transport.h" -#include "cpName.h" -#include "os.h" - -/* Local function prototypes */ -int HgfsGetNextDirEntry(HgfsSuperInfo *sip, HgfsHandle handle, - uint32_t offset, char *nameOut, size_t nameSize, - HgfsFileType *type, Bool *done); -int HgfsDirOpen(HgfsSuperInfo *sip, struct vnode *vp, HgfsOpenType openType); -int HgfsFileOpen(HgfsSuperInfo *sip, struct vnode *vp, - int flag, int permissions, HgfsOpenType openType); -int HgfsDirClose(HgfsSuperInfo *sip, struct vnode *vp); -int HgfsFileClose(HgfsSuperInfo *sip, struct vnode *vp, int flag); -int HgfsDoRead(HgfsSuperInfo *sip, HgfsHandle handle, uint64_t offset, - uint32_t size, struct uio *uiop); -int HgfsDoWrite(HgfsSuperInfo *sip, HgfsHandle handle, int ioflag, - uint64_t offset, uint32_t size, struct uio *uiop); -int HgfsDelete(HgfsSuperInfo *sip, const char *filename, HgfsOp op); -static int HgfsDoGetattrInt(const char *path, const HgfsHandle handle, HgfsSuperInfo *sip, - HgfsAttrV2 *hgfsAttrV2); -static int HgfsDoGetattrByName(const char *path, HgfsSuperInfo *sip, HgfsAttrV2 *hgfsAttrV2); -int HgfsReadlinkInt(struct vnode *vp, struct uio *uiop); -static int HgfsQueryAttrInt(const char *path, HgfsHandle handle, HgfsSuperInfo *sip, - HgfsKReqHandle req); -static int HgfsRenewHandle(struct vnode *vp, HgfsSuperInfo *sip, HgfsHandle *handle); -static int HgfsRefreshDirHandle(struct vnode *vp, HgfsSuperInfo *sip, HgfsHandle *handle); -static int HgfsGetNewHandle(struct vnode *vp, HgfsSuperInfo *sip, HgfsFile *fp, - HgfsHandle *handle); - - -#if 0 -static int HgfsDoGetattrByHandle(HgfsHandle handle, HgfsSuperInfo *sip, HgfsAttrV2 *hgfsAttrV2); -#endif - -#define HGFS_CREATE_DIR_MASK (HGFS_CREATE_DIR_VALID_FILE_NAME | \ - HGFS_CREATE_DIR_VALID_SPECIAL_PERMS | \ - HGFS_CREATE_DIR_VALID_OWNER_PERMS | \ - HGFS_CREATE_DIR_VALID_GROUP_PERMS | \ - HGFS_CREATE_DIR_VALID_OTHER_PERMS) - -/* - *---------------------------------------------------------------------------- - * - * HgfsRenameInt -- - * - * Renames the provided source name in the source directory with the - * destination name in the destination directory. A RENAME request is sent - * to the Hgfs server. - * - * Results: - * Returns 0 on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsRenameInt(struct vnode *fvp, // IN: "from" file - struct vnode *tdvp, // IN: "to" parent directory - struct vnode *tvp, // IN: "to" file - struct componentname *tcnp) // IN: "to" pathname info -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(fvp); - HgfsKReqHandle req; - HgfsRequest *requestHeader; - HgfsRequestRenameV3 *request; - HgfsReplyRenameV3 *reply; - HgfsFileNameV3 *newNameP; - char *srcFullPath = NULL; // will point to fvp's filename; don't free - char *dstFullPath = NULL; // allocated from M_TEMP; free when done. - uint32 srcFullPathLen; - uint32 dstFullPathLen; - uint32 reqBufferSize; - uint32 reqSize; - uint32 repSize; - int ret; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s -> %.*s/%.*s).\n", - HGFS_VP_TO_FILENAME_LENGTH(fvp), HGFS_VP_TO_FILENAME(fvp), - HGFS_VP_TO_FILENAME_LENGTH(tdvp), HGFS_VP_TO_FILENAME(tdvp), - (int)tcnp->cn_namelen, tcnp->cn_nameptr); - - /* No cross-device renaming. */ - if (HGFS_VP_TO_MP(fvp) != HGFS_VP_TO_MP(tdvp)) { - ret = EXDEV; - goto out; - } - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - goto out; - } - - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestRenameV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - /* Initialize the request header */ - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_RENAME_V3); - request->hints = 0; - request->reserved = 0; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - reqBufferSize = HGFS_PACKET_MAX - (reqSize - 2); - - /* Make the full path of the source. */ - srcFullPath = HGFS_VP_TO_FILENAME(fvp); - srcFullPathLen = HGFS_VP_TO_FILENAME_LENGTH(fvp); - - /* Make the full path of the destination. */ - dstFullPath = os_malloc(MAXPATHLEN, M_WAITOK); - if (!dstFullPath) { - ret = ENOMEM; - goto destroyOut; - } - - ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(tdvp), - HGFS_VP_TO_FILENAME_LENGTH(tdvp), - tcnp->cn_nameptr, - tcnp->cn_namelen, - dstFullPath, - MAXPATHLEN); - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "could not construct full path of dest.\n"); - ret = ENAMETOOLONG; - goto destroyOut; - } - dstFullPathLen = ret; - - /* Ensure both names will fit in one request. */ - if ((reqSize + srcFullPathLen + dstFullPathLen) > HGFS_PACKET_MAX) { - DEBUG(VM_DEBUG_FAIL, "names too big for one request.\n"); - ret = EPROTO; - goto destroyOut; - } - - request->oldName.flags = 0; - request->oldName.fid = HGFS_INVALID_HANDLE; - request->oldName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; - - /* - * Convert an input string to utf8 precomposed form, convert it to - * the cross platform name format and finally unescape any illegal - * filesystem characters. - */ - ret = HgfsNameToWireEncoding(srcFullPath, srcFullPathLen + 1, - request->oldName.name, reqBufferSize); - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "Couldn't encode to wire format\n"); - ret = -ret; - goto destroyOut; - } - request->oldName.length = ret; - reqSize += ret; - reqBufferSize -= ret; - - /* - * The new name is placed directly after the old name in the packet and we - * access it through this pointer. - */ - newNameP = (HgfsFileNameV3 *)((char *)&request->oldName + - sizeof request->oldName + - request->oldName.length); - newNameP->flags = 0; - newNameP->fid = HGFS_INVALID_HANDLE; - newNameP->caseType = HGFS_FILE_NAME_CASE_SENSITIVE; - - ret = HgfsNameToWireEncoding(dstFullPath, dstFullPathLen + 1, - newNameP->name, reqBufferSize); - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "Couldn't encode to wire format.\n"); - ret = -ret; - goto destroyOut; - } - newNameP->length = ret; - reqSize += ret; - - /* The request's size includes the header, request and both filenames. */ - HgfsKReq_SetPayloadSize(req, reqSize); - - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* HgfsSubmitRequest() destroys the request if necessary. */ - goto out; - } - - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - - ret = HgfsGetStatus(req, repSize); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - goto destroyOut; - } - - /* Successfully renamed file on the server. */ - -destroyOut: - HgfsKReq_ReleaseRequest(sip->reqs, req); - -out: - if (dstFullPath != NULL) { - os_free(dstFullPath, MAXPATHLEN); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%d).\n", ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsReaddirInt -- - * - * Reads as many entries from the directory as will fit in to the provided - * buffer. Each directory entry is read by calling HgfsGetNextDirEntry(). - * - * "The vop_readdir() method reads chunks of the directory into a uio - * structure. Each chunk can contain as many entries as will fit within - * the size supplied by the uio structure. The uio_resid structure member - * shows the size of the getdents request in bytes, which is divided by the - * size of the directory entry made by the vop_readdir() method to - * calculate how many directory entries to return." (Solaris Internals, - * p555) - * - * Results: - * Returns 0 on success and a non-zero error code on failure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsReaddirInt(struct vnode *vp, // IN : Directory vnode to get entries from. - struct uio *uiop, // IN/OUT: Buffer to place dirents in. - int *eofp) // IN/OUT: Have all entries been read? -{ - - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - HgfsHandle handle; - uint64_t offset; - Bool done; - char *fullName = NULL; /* Hashed to generate inode number */ - int ret = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* uio_offset is a signed quantity. */ - if (HGFS_UIOP_TO_OFFSET(uiop) < 0) { - DEBUG(VM_DEBUG_FAIL, "fed negative offset.\n"); - ret = EINVAL; - goto out; - } - - /* - * In order to fill the user's buffer with directory entries, we must - * iterate on HGFS_OP_SEARCH_READ requests until either the user's buffer is - * full or there are no more entries. Each call to HgfsGetNextDirEntry() - * fills in the name and attribute structure for the next entry. We then - * escape that name and place it in a kernel buffer that's the same size as - * the user's buffer. Once there are no more entries or no more room in the - * buffer, we copy it to user space. - */ - - /* - * We need to get the handle for this open directory to send to the Hgfs - * server in our requests. - */ - ret = HgfsGetOpenFileHandle(vp, &handle); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "could not get handle.\n"); - ret = EINVAL; - goto out; - } - - if (HGFS_UIOP_TO_OFFSET(uiop) == 0) { - ret = HgfsRefreshDirHandle(vp, sip, &handle); - } - - /* - * Allocate 1K (MAXPATHLEN) buffer for inode number generation. - */ - fullName = os_malloc(MAXPATHLEN, M_WAITOK); - if (!fullName) { - ret = ENOMEM; - goto out; - } - - /* - * Loop until one of the following conditions is met: - * o An error occurs while reading a directory entry - * o There are no more directory entries to read - * o The buffer is full and cannot hold the next entry - * - * We request dentries from the Hgfs server based on their index in the - * directory. The offset value is initialized to the value specified in - * the user's io request and is incremented each time through the loop. - * - * dirp is incremented by the record length each time through the loop and - * is used to determine where in the kernel buffer we write to. - */ - for (offset = HGFS_UIOP_TO_OFFSET(uiop), done = 0; /* Nothing */ ; offset++) { - struct dirent dirent, *dirp = &dirent; - char nameBuf[sizeof dirp->d_name]; - HgfsFileType fileType = HGFS_FILE_TYPE_REGULAR; - - DEBUG(VM_DEBUG_COMM, - "HgfsReaddir: getting directory entry at offset %"FMT64"u.\n", offset); - - DEBUG(VM_DEBUG_HANDLE, "** handle=%d, file=%s\n", - handle, HGFS_VP_TO_FILENAME(vp)); - - bzero(dirp, sizeof *dirp); - - ret = HgfsGetNextDirEntry(sip, handle, offset, nameBuf, sizeof nameBuf, - &fileType, &done); - /* If the filename was too long, we skip to the next entry ... */ - if (ret == EOVERFLOW) { - continue; - } else if (ret == EBADF) { - /* - * If we got invalid handle from the server, this was because user - * enabled/disabled the shared folders. We should get a new handle - * from the server, now. - */ - ret = HgfsRenewHandle(vp, sip, &handle); - if (ret == 0) { - /* - * Now we have valid handle, let's try again from the same - * offset. - */ - offset--; - continue; - } else { - ret = EBADF; - goto out; - } - } else if (ret) { - if (ret != EPROTO) { - ret = EINVAL; - } - DEBUG(VM_DEBUG_FAIL, "failure occurred in HgfsGetNextDirEntry\n"); - goto out; - /* - * ... and if there are no more entries, we set the end of file pointer - * and break out of the loop. - */ - } else if (done == TRUE) { - DEBUG(VM_DEBUG_COMM, "Done reading directory entries.\n"); - if (eofp != NULL) { - *eofp = TRUE; - } - break; - } - /* - * Convert an input string to utf8 decomposed form and then escape its - * buffer. - */ - ret = HgfsNameFromWireEncoding(nameBuf, strlen(nameBuf), dirp->d_name, - sizeof dirp->d_name); - /* - * If the name didn't fit in the buffer or illegal utf8 characters - * were encountered, skip to the next entry. - */ - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "HgfsNameFromWireEncoding failed.\n"); - continue; - } - - /* Fill in the directory entry. */ - dirp->d_namlen = ret; - dirp->d_reclen = sizeof(*dirp); // NB: d_namlen must be set first! - dirp->d_type = - (fileType == HGFS_FILE_TYPE_REGULAR) ? DT_REG : - (fileType == HGFS_FILE_TYPE_DIRECTORY) ? DT_DIR : - DT_UNKNOWN; - - /* - * Make sure there is enough room in the buffer for the entire directory - * entry. If not, we just break out of the loop and copy what we have an set - * the return value to be 0. - */ - if (dirp->d_reclen > HGFS_UIOP_TO_RESID(uiop)) { - DEBUG(VM_DEBUG_INFO, "ran out of room in the buffer.\n"); - ret = 0; - break; - } - - - ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp), // Directorie's name - HGFS_VP_TO_FILENAME_LENGTH(vp), // Length - dirp->d_name, // Name of file - dirp->d_namlen, // Length of filename - fullName, // Destination buffer - MAXPATHLEN); // Size of this buffer - - /* Skip this entry if the full path was too long. */ - if (ret < 0) { - continue; - } - - /* - * Place the node id, which serves the purpose of inode number, for this - * filename directory entry. As long as we are using a dirent64, this is - * okay since ino_t is also a u_longlong_t. - */ - HgfsNodeIdGet(&sip->fileHashTable, fullName, (uint32_t)ret, - &dirp->d_fileno); - - /* Copy out this directory entry. */ - ret = uiomove((caddr_t)dirp, dirp->d_reclen, uiop); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "uiomove failed.\n"); - goto out; - } - } - - /* - * uiomove(9) will have incremented the uio offset by the number of bytes - * written. We reset it here to the fs-specific offset in our directory so - * the next time we are called it is correct. (Note, this does not break - * anything and /is/ how this field is intended to be used.) - */ - HGFS_UIOP_SET_OFFSET(uiop, offset); - - DEBUG(VM_DEBUG_EXIT, "done (ret=%d, *eofp=%d).\n", ret, *eofp); -out: - if (fullName != NULL) { - os_free(fullName, MAXPATHLEN); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsGetattrInt -- - * - * "Gets the attributes for the supplied vnode." (Solaris Internals, p536) - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsGetattrInt(struct vnode *vp, // IN : vnode of the file - HgfsVnodeAttr *vap) // OUT: attributes container -{ - - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - HgfsAttrV2 hgfsAttrV2; - int ret = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* XXX It would be nice to do a GetattrByHandle when possible here. */ - ret = HgfsDoGetattrByName(HGFS_VP_TO_FILENAME(vp), sip, &hgfsAttrV2); - - if (!ret) { - /* - * HgfsDoGetattr obtained attributes from the hgfs server so - * map the attributes into BSD attributes. - */ - - HgfsAttrToBSD(vp, &hgfsAttrV2, vap); - } - - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - -/* - *---------------------------------------------------------------------------- - * - * HgfsSetattrInt -- - * - * Maps the Mac OS/FreeBsd attributes to Hgfs attributes (by calling - * HgfsSetattrCopy()) and sends a set attribute request to the Hgfs server. - * - * Results: - * Returns 0 on success and a non-zero error code on error. - * - * Side effects: - * The file on the host will have new attributes. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsSetattrInt(struct vnode *vp, // IN : vnode of the file - HgfsVnodeAttr *vap) // IN : attributes container -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - HgfsKReqHandle req; - HgfsRequest *requestHeader; - HgfsRequestSetattrV3 *request; - HgfsReplySetattrV3 *reply; - uint32 reqSize; - uint32 reqBufferSize; - uint32 repSize; - char *fullPath = NULL; - uint32 fullPathLen; - int ret; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - ASSERT(vp); - ASSERT(vap); - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - goto out; - } - - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestSetattrV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_SETATTR_V3); - - request->reserved = 0; - - /* - * Fill the attributes and hint fields of the request. If no updates are - * needed then we will just return success without sending the request. - */ - if (HgfsSetattrCopy(vap, &request->attr, &request->hints) == FALSE) { - DEBUG(VM_DEBUG_COMM, "don't need to update attributes.\n"); - ret = 0; - goto destroyOut; - } - - fullPath = HGFS_VP_TO_FILENAME(vp); - fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp); - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - reqBufferSize = HGFS_NAME_BUFFER_SIZET(HGFS_PACKET_MAX, reqSize); - - /* - * Convert an input string to utf8 precomposed form, convert it to - * the cross platform name format and finally unescape any illegal - * filesystem characters. - */ - ret = HgfsNameToWireEncoding(fullPath, fullPathLen + 1, - request->fileName.name, - reqBufferSize); - - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "Could not encode to wire format"); - ret = -ret; - goto destroyOut; - } - - request->fileName.fid = HGFS_INVALID_HANDLE; - request->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; - request->fileName.flags = 0; - request->fileName.length = ret; - - reqSize += ret; - - /* The request's size includes the header, request and filename. */ - HgfsKReq_SetPayloadSize(req, reqSize); - - if (!request->attr.mask) { - /* they were trying to set filerev or vaflags, which we ignore */ - ret = 0; - goto destroyOut; - } - - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* HgfsSubmitRequest() destroys the request if necessary. */ - goto out; - } - - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - - ret = HgfsGetStatus(req, repSize); - if (ret) { - if (ret == EPROTO) { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - } - goto destroyOut; - } else { - if (HGFS_VATTR_SIZE_IS_ACTIVE(vap, HGFS_VA_DATA_SIZE)) { - HgfsSetFileSize(vp, vap->HGFS_VA_DATA_SIZE); - } - } - -destroyOut: - HgfsKReq_ReleaseRequest(sip->reqs, req); -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsVopRmdir -- - * - * Removes the specified name from the provided vnode. Sends a DELETE - * request by calling HgfsDelete() with the filename and correct opcode to - * indicate deletion of a directory. - * - * "Removes the directory pointed to by the supplied vnode." (Solaris - * Internals, p537) - * - * Results: - * Returns 0 on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsRmdirInt(struct vnode *dvp, // IN: parent directory - struct vnode *vp, // IN: directory to remove - struct componentname *cnp) // IN: Only used for debugging -{ - int ret = 0; - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(dvp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s/%.*s)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - ret = HgfsDelete(sip, HGFS_VP_TO_FILENAME(vp), HGFS_OP_DELETE_DIR_V3); - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s/%.*s -> %d)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsRemoveInt -- - * - * Composes the full pathname of this file and sends a DELETE_FILE request - * by calling HgfsDelete(). - * - * Results: - * Returns 0 on success or a non-zero error code on error. - * - * Side effects: - * If successful, the file specified will be deleted from the host's - * filesystem. - * - *---------------------------------------------------------------------------- - */ - -int HgfsRemoveInt(struct vnode *vp) // IN: Vnode to delete -{ - - int ret = 0; - - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* Removing directories is a no-no; save that for VNOP_RMDIR. */ - if (HGFS_VP_TO_VTYPE(vp) == VDIR) { - DEBUG(VM_DEBUG_FAIL, "HgfsRemove(). on dir ret EPERM\n"); - ret = EPERM; - goto out; - } - - os_FlushRange(vp, 0, HGFS_VP_TO_FILESIZE(vp)); - os_SetSize(vp, 0); - - /* We can now send the delete request. */ - ret = HgfsDelete(sip, HGFS_VP_TO_FILENAME(vp), HGFS_OP_DELETE_FILE_V3); - -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsCloseInt -- - * - * Called by HgfsVnopClose under Mac OS or HgfsVopClose under FreeBSD to - * close a file. - * - * "Closes the file given by the supplied vnode. When this is the last - * close, some filesystems use vnop_close() to initiate a writeback of - * outstanding dirty pages by checking the reference cound in the vnode." - * (Solaris Internals, p536) - * - * Results: - * Always returns 0 success. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsCloseInt(struct vnode *vp, // IN: Vnode to close. - int mode) // IN: Mode of vnode being closed. -{ - int ret = 0; - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s, %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - mode); - - /* - * If we are closing a directory we need to send a SEARCH_CLOSE request, - * but if we are closing a regular file we need to send a CLOSE request. - * Other file types are not supported by the Hgfs protocol. - */ - - switch (HGFS_VP_TO_VTYPE(vp)) { - case VDIR: - ret = HgfsDirClose(sip, vp); - break; - - case VREG: - ret = HgfsFileClose(sip, vp, mode); - break; - - default: - DEBUG(VM_DEBUG_FAIL, "unsupported filetype %d.\n", - HGFS_VP_TO_VTYPE(vp)); - ret = EINVAL; - break; - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d -> 0)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return 0; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsOpenInt -- - * - * Invoked when open(2) is called on a file in our filesystem. Sends an - * OPEN request to the Hgfs server with the filename of this vnode. - * - * "Opens a file referenced by the supplied vnode. The open() system call - * has already done a vnop_lookup() on the path name, which returned a vnode - * pointer and then calls to vnop_open(). This function typically does very - * little since most of the real work was performed by vnop_lookup()." - * (Solaris Internals, p537) - * - * Results: - * Returns 0 on success and an error code on error. - * - * Side effects: - * If the HgfsFile for this file does not already have a handle, it is - * given one that can be used for future read and write requests. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsOpenInt(struct vnode *vp, // IN: Vnode to open. - int mode, // IN: Mode of vnode being opened. - HgfsOpenType openType) // IN: TRUE if called outside of VNOP_OPEN. -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - int ret = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s, %d, %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - mode, openType); - - switch(HGFS_VP_TO_VTYPE(vp)) { - case VDIR: - DEBUG(VM_DEBUG_LOG, "opening a directory\n"); - ret = HgfsDirOpen(sip, vp, openType); - break; - - case VREG: - /* - * If HgfsCreate() was called prior to this then is would set permissions - * in HgfsFile that we need to pass to HgfsFileOpen. - * If HgfsCreate has not been called then file already exists and permissions - * are ignored by HgfsFileOpen. - */ - DEBUG(VM_DEBUG_LOG, "opening a file with flag %x\n", mode); - ret = HgfsFileOpen(sip, vp, mode, HGFS_VP_TO_PERMISSIONS(vp), openType); - break; - - default: - DEBUG(VM_DEBUG_FAIL, - "HgfsOpen: unrecognized file of type %d.\n", HGFS_VP_TO_VTYPE(vp)); - ret = EINVAL; - break; - } - - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), - HGFS_VP_TO_FILENAME(vp), ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsLookupInt -- - * - * Looks in the provided directory for the specified filename. If we cannot - * determine the vnode locally (i.e, the vnode is not the root vnode of the - * filesystem provided by dvp or in our hashtable), we send a getattr - * request to the server and allocate a vnode and internal filesystem state - * for this file. - * - * Results: - * Returns zero on success and ENOENT if the file cannot be found - * If file is found, a vnode representing the file is returned in vpp. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsLookupInt(struct vnode *dvp, // IN : directory vnode - struct vnode **vpp, // OUT: ptr to vnode if it exists - struct componentname *cnp) // IN : pathname to component -{ - HgfsAttrV2 attrV2; - HgfsSuperInfo *sip; - char *path = NULL; - int ret = 0; - int len = 0; - - ASSERT(dvp); - ASSERT(vpp); - ASSERT(cnp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s, %.*s).\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr); - - if (cnp->cn_flags & ISDOTDOT) { - HgfsFile *fp = HGFS_VP_TO_FP(dvp); - ASSERT(fp); - if (fp->parent == NULL) { - return EIO; // dvp is root directory - } else { -#if defined __FreeBSD__ - vref(fp->parent); -#else - vnode_get(fp->parent); -#endif - *vpp = fp->parent; - return 0; - } - } - if (cnp->cn_namelen == 1 && *cnp->cn_nameptr == '.') { -#if defined __FreeBSD__ - vref(dvp); -#else - vnode_get(dvp); -#endif - *vpp = dvp; - return 0; - } - - /* - * Get pointer to the superinfo. If the device is not attached, - * hgfsInstance will not be valid and we immediately return an error. - */ - sip = HGFS_VP_TO_SIP(dvp); - if (!sip) { - DEBUG(VM_DEBUG_FAIL, "couldn't acquire superinfo.\n"); - return ENOTSUP; - } - - /* Snag a pathname buffer */ - path = os_malloc(MAXPATHLEN, M_WAITOK); - if (!path) { - return ENOMEM; - } - - /* Construct the full path for this lookup. */ - len = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp), // Path to this file - HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of path - cnp->cn_nameptr, // File's name - cnp->cn_namelen, // Filename length - path, // Destination buffer - MAXPATHLEN); // Size of dest buffer - if (len < 0) { - DEBUG(VM_DEBUG_FAIL, "LookupInt length is less than zero\n"); - ret = EINVAL; - goto out; - } - - DEBUG(VM_DEBUG_LOAD, "full path is \"%s\"\n", path); - - /* See if the lookup is really for the root vnode. */ - if (strcmp(path, "/") == 0) { - DEBUG(VM_DEBUG_INFO, "returning the root vnode.\n"); - *vpp = sip->rootVnode; - /* - * If we are returning the root vnode, then we need to get a reference - * to it. Under Mac OS this gets an I/O Count. - */ - HGFS_VPP_GET_IOCOUNT(vpp); - goto out; - }; - - /* Send a Getattr request to the Hgfs server. */ - ret = HgfsDoGetattrByName(path, sip, &attrV2); - - /* - * If this is the final pathname component & the user is attempt a CREATE - * or RENAME, just return without a leaf vnode. (This differs from - * Solaris where ENOENT would be returned in all cases.) - */ - if (ret == ENOENT) { - if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && - cnp->cn_flags & ISLASTCN) { - ret = EJUSTRETURN; - DEBUG(VM_DEBUG_FAIL, "GetattrByName error %d for \"%s\".\n", ret, path); - goto out; - } - } - - /* Got an error from HgfsDoGetattrByName, return it to the caller. */ - if (ret) { - DEBUG(VM_DEBUG_FAIL, "GetattrByName error %d for \"%s\".\n", ret, path); - goto out; - } - - ret = HgfsVnodeGet(vpp, // Location to write vnode's address - dvp, // Parent vnode - sip, // Superinfo - HGFS_VP_TO_MP(dvp), // VFS for our filesystem - path, // Full name of the file - attrV2.type, // Type of file - &sip->fileHashTable, // File hash table - FALSE, // Not a new file creation - 0, // No permissions - not a new file - attrV2.size); // File size - - if (ret) { - DEBUG(VM_DEBUG_FAIL, "couldn't create vnode for \"%s\".\n", path); - goto out; - } - - /* - * Either we will have a cache hit or called HgfsVnodeGet. Both of these - * paths guarantees that *vpp will be set to a vnode. - */ - ASSERT(*vpp); - - DEBUG(VM_DEBUG_LOAD, "assigned vnode %p to %s\n", *vpp, path); - - ret = 0; /* Return success */ - -out: - if (path != NULL) { - os_free(path, MAXPATHLEN); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s, %.*s -> %d)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsCreateInt -- - * - * Called by either HgfsVnopCreate under Mac OS or HgfsVopCreate under - * FreeBSD when the user is trying to create a file by calling open() with - * the O_CREAT flag specified. - * - * The kernel calls the open entry point which calls (HgfsOpenInt()) after - * calling this function, so here all we do is consruct the vnode and - * save the filename and permission bits for the file to be created within - * our filesystem internal state. - * - * Results: - * Returns zero on success and an appropriate error code on error. - * - * Side effects: - * If the file doesn't exist, a vnode will be created. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsCreateInt(struct vnode *dvp, // IN : Directory vnode - struct vnode **vpp, // OUT: Pointer to new vnode - struct componentname *cnp, // IN : Location to create new vnode - int mode) // IN : Mode of vnode being created. -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(dvp); - char *fullname = NULL; // allocated from M_TEMP; free when done. - int ret = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s/%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr); - - if (*vpp != NULL) { - DEBUG(VM_DEBUG_FAIL, "vpp (%p) not null\n", vpp); - ret = EEXIST; - goto out; - } - - /* If we have gotten to this point then we know that we need to create a - * new vnode. The actual file will be created on the HGFS server in the - * HgfsOpenInt call should happen right after this call. - */ - fullname = os_malloc(MAXPATHLEN, M_WAITOK); - if (!fullname) { - ret = ENOMEM; - goto out; - } - - ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp), // Name of directory to create in - HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of name - cnp->cn_nameptr, // Name of file to create - cnp->cn_namelen, // Length of new filename - fullname, // Buffer to write full name - MAXPATHLEN); // Size of this buffer - - if (ret >= 0) { - /* Create the vnode for this file. */ - ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullname, - HGFS_FILE_TYPE_REGULAR, &sip->fileHashTable, TRUE, - mode, 0); - /* HgfsVnodeGet() guarantees this. */ - ASSERT(ret != 0 || *vpp); - /* - * NOTE: This is a temporary workaround. - * This condition may occur because we look up vnodes by file name in the - * vnode cache. - * There is a race condition when file is already deleted but still referenced - - * thus vnode still exist. If a new file with the same name is created I - * can neither use the vnode of the deleted file nor insert a new vnode with - * the same name - thus I fail the request. This behavior is not correct and will - * be fixed after further restructuring if the source code. - */ - if (ret == EEXIST) { - ret = EIO; - } - } else { - DEBUG(VM_DEBUG_FAIL, "couldn't create full path name.\n"); - ret = ENAMETOOLONG; - } - -out: - if (fullname != NULL) { - os_free(fullname, MAXPATHLEN); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s/%.*s -> %d)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsReadInt -- - * - * Called by HgfsVnopRead under Mac OS or HgfsVopRead under FreeBSD to read - * a file. - * - * We call HgfsDoRead() to fill the user's buffer until the request is met - * or the file has no more data. This is done since we can only transfer - * HGFS_IO_MAX bytes in any one request. - * - * "Reads the range supplied for the given vnode. vop_read() typically - * maps the requested range of a file into kernel memory and then uses - * vop_getpage() to do the real work." (Solaris Internals, p537) - * - * Results: - * Returns zero on success and an error code on failure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsReadInt(struct vnode *vp, // IN : Vnode to read from - struct uio *uiop, // IN/OUT: Buffer to write data into. - Bool pagingIo) // IN: True if the read is a result of a page fault -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - HgfsHandle handle; - uint64_t offset; - int ret; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* We can't read from directories, that's what readdir() is for. */ - if (HGFS_VP_TO_VTYPE(vp) != VREG) { - DEBUG(VM_DEBUG_FAIL, "Can only read regular files.\n"); - ret = (HGFS_VP_TO_VTYPE(vp) == VDIR) ? EISDIR : EPERM; - DEBUG(VM_DEBUG_FAIL, "Read not a reg file type %d ret %d.\n", HGFS_VP_TO_VTYPE(vp), ret); - return ret; - } - - /* off_t is a signed quantity */ - if (HGFS_UIOP_TO_OFFSET(uiop) < 0) { - DEBUG(VM_DEBUG_FAIL, "given negative offset.\n"); - return EINVAL; - } - - /* This is where the user wants to start reading from in the file. */ - offset = HGFS_UIOP_TO_OFFSET(uiop); - - /* - * We need to get the handle for the requests sent to the Hgfs server. Note - * that this is guaranteed to not change until a close(2) is called on this - * vnode, so it's safe and correct to acquire it outside the loop below. - */ - ret = HgfsGetOpenFileHandle(vp, &handle); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "could not get handle.\n"); - return EINVAL; - } - - /* Flush mmaped data to maintain data coherence between mmap and read. */ - if (!pagingIo) { - ret = os_FlushRange(vp, offset, HGFS_UIOP_TO_RESID(uiop)); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "could not flush data.\n"); - return EINVAL; - } - } - - /* - * Here we loop around HgfsDoRead with requests less than or equal to - * HGFS_IO_MAX until one of the following conditions is met: - * (1) All the requested data has been read - * (2) The file has no more data - * (3) An error occurred - * - * Since HgfsDoRead() calls uiomove(9), we know condition (1) is met when - * the uio structure's uio_resid is decremented to zero. If HgfsDoRead() - * returns 0 we know condition (2) was met, and if it returns less than 0 we - * know condtion (3) was met. - */ - do { - uint32_t size; - - /* Request at most HGFS_IO_MAX bytes */ - size = (HGFS_UIOP_TO_RESID(uiop) > HGFS_IO_MAX) ? HGFS_IO_MAX : - HGFS_UIOP_TO_RESID(uiop); - - DEBUG(VM_DEBUG_INFO, "offset=%"FMT64"d, uio_offset=%"FMT64"d\n", - offset, HGFS_UIOP_TO_OFFSET(uiop)); - DEBUG(VM_DEBUG_HANDLE, "** handle=%d, file=%s to read %u\n", - handle, HGFS_VP_TO_FILENAME(vp), size); - - if (size == 0) { - /* For a zero byte length read we return success. */ - DEBUG(VM_DEBUG_EXIT, "size of 0 ret -> 0.\n"); - return 0; - } - - /* Send one read request. */ - ret = HgfsDoRead(sip, handle, offset, size, uiop); - if (ret == 0) { - /* On end of file we return success */ - DEBUG(VM_DEBUG_EXIT, "end of file reached.\n"); - return 0; - } else if (ret == -EBADF) { // Stale host handle - ret = HgfsRenewHandle(vp, sip, &handle); - if (ret == 0) { - ret = HgfsDoRead(sip, handle, offset, size, uiop); - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "Failed to read from a fresh handle.\n"); - return -ret; - } - } else { - DEBUG(VM_DEBUG_FAIL, "Failed to get a fresh handle.\n"); - return EBADF; - } - } else if (ret < 0) { - /* - * HgfsDoRead() returns the negative of an appropriate error code to - * differentiate between success and error cases. We flip the sign - * and return the appropriate error code. See the HgfsDoRead() - * function header for a fuller explanation. - */ - DEBUG(VM_DEBUG_FAIL, "HgfsDoRead() failed, error %d.\n", ret); - return -ret; - } - - /* Bump the offset past where we have already read. */ - offset += ret; - } while (HGFS_UIOP_TO_RESID(uiop)); - - /* We fulfilled the user's read request, so return success. */ - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> 0)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - return 0; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsWriteInt -- - * - * Called by HgfsVnopWrite under Mac OS or HgfsVopWrite under FreeBSD. - * - * We call HgfsDoWrite() once with requests less than or equal to - * HGFS_IO_MAX bytes until the user's write request has completed. - * - * Results: - * Returns 0 on success and error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsWriteInt(struct vnode *vp, // IN : the vnode of the file - struct uio *uiop, // IN/OUT: location of data to be written - int ioflag, // IN : hints & other directives - Bool pagingIo) // IN: True if the write is originated by memory manager -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - HgfsHandle handle; - uint64_t offset; - int ret = 0; - int error = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* Skip write requests for 0 bytes. */ - if (HGFS_UIOP_TO_RESID(uiop) == 0) { - DEBUG(VM_DEBUG_INFO, "write of 0 bytes requested.\n"); - error = 0; - goto out; - } - - DEBUG(VM_DEBUG_INFO, "file is %s\n", HGFS_VP_TO_FILENAME(vp)); - - /* Off_t is a signed type. */ - if (HGFS_UIOP_TO_OFFSET(uiop) < 0) { - DEBUG(VM_DEBUG_FAIL, "given negative offset.\n"); - error = EINVAL; - goto out; - } - - /* This is where the user will begin writing into the file. */ - offset = HGFS_UIOP_TO_OFFSET(uiop); - - /* Get the handle we need to supply the Hgfs server. */ - ret = HgfsGetOpenFileHandle(vp, &handle); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "could not get handle.\n"); - error = EINVAL; - goto out; - } - - /* Flush mmaped data to maintain data coherence between mmap and read. */ - if (!pagingIo && (ioflag & IO_APPEND) == 0) { - ret = os_FlushRange(vp, offset, HGFS_UIOP_TO_RESID(uiop)); - } - - /* - * We loop around calls to HgfsDoWrite() until either (1) we have written all - * of our data or (2) an error has occurred. HGFS_UIOP_TO_RESID(uiop) is decremented - * by uiomove(9F) inside HgfsDoWrite(), so condition (1) is met when it - * reaches zero. Condition (2) occurs when HgfsDoWrite() returns less than - * zero. - */ - do { - uint32_t size; - - DEBUG(VM_DEBUG_INFO, "** offset=%"FMT64"d, uio_offset=%"FMT64"d\n", - offset, HGFS_UIOP_TO_OFFSET(uiop)); - DEBUG(VM_DEBUG_HANDLE, "** handle=%d, file=%s\n", - handle, HGFS_VP_TO_FILENAME(vp)); - - /* Write at most HGFS_IO_MAX bytes. */ - size = (HGFS_UIOP_TO_RESID(uiop) > HGFS_IO_MAX) ? HGFS_IO_MAX : HGFS_UIOP_TO_RESID(uiop); - - /* Send one write request. */ - ret = HgfsDoWrite(sip, handle, ioflag, offset, size, uiop); - if (ret == -EBADF) { // Stale host handle - ret = HgfsRenewHandle(vp, sip, &handle); - if (ret == 0) { - ret = HgfsDoWrite(sip, handle, ioflag, offset, size, uiop); - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "Failed to write to a fresh handle.\n"); - error = -ret; - break; - } - } else { - DEBUG(VM_DEBUG_FAIL, "Failed to get a fresh handle, error %d.\n", ret); - error = EBADF; - break; - } - } else if (ret < 0) { - /* - * As in HgfsRead(), we need to flip the sign. See the comment in the - * function header of HgfsDoWrite() for a more complete explanation. - */ - DEBUG(VM_DEBUG_INFO, "HgfsDoWrite failed, returning %d\n", -ret); - error = -ret; - break; - } - - /* Increment the offest by the amount already written. */ - offset += ret; - - } while (HGFS_UIOP_TO_RESID(uiop)); - - /* Need to notify memory manager if written data extended the file. */ - if (!pagingIo && (offset > HGFS_VP_TO_FILESIZE(vp))) { - if ((ioflag & IO_APPEND) == 0) { - os_SetSize(vp, offset); - } else { - off_t oldSize = HGFS_VP_TO_FILESIZE(vp); - off_t writtenData = offset - HGFS_UIOP_TO_OFFSET(uiop); - os_SetSize(vp, oldSize + writtenData); - } - } - -out: - /* We have completed the user's write request, so return. */ - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", - HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), error); - - return error; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsMkdirInt -- - * - * Makes a directory named dirname in the directory specified by the dvp - * vnode by sending a CREATE_DIR request, then allocates a vnode for this - * new directory and writes its address into vpp. - * - * Results: - * Returns 0 on success and a non-zero error code on failure. - * - * Side effects: - * If successful, a directory is created on the host's filesystem. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsMkdirInt(struct vnode *dvp, // IN : directory vnode - struct vnode **vpp, // OUT: pointer to new directory vnode - struct componentname *cnp, // IN : pathname to component - int mode) // IN : mode to create dir -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(dvp); - HgfsKReqHandle req; - HgfsRequest *requestHeader; - HgfsRequestCreateDirV3 *request; - HgfsReplyCreateDirV3 *reply; - uint32 reqSize; - uint32 repSize; - uint32 reqBufferSize; - char *fullName = NULL; // allocated from M_TEMP; free when done. - uint32 fullNameLen; - int ret; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s/%.*s,%d)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr, mode); - - /* - * We need to construct the full path of the directory to create then send - * a CREATE_DIR request. If successful we will create a vnode and fill in - * vpp with a pointer to it. - * - * Note that unlike in HgfsCreate(), *vpp is always NULL. - */ - - /* Construct the complete path of the directory to create. */ - fullName = os_malloc(MAXPATHLEN, M_WAITOK); - if (!fullName) { - ret = ENOMEM; - goto out; - } - - ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp), // Parent directory - HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of name - cnp->cn_nameptr, // Name of file to create - cnp->cn_namelen, // Length of filename - fullName, // Buffer to write full name - MAXPATHLEN); // Size of this buffer - - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "couldn't create full path name.\n"); - ret = ENAMETOOLONG; - goto out; - } - fullNameLen = ret; - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - goto out; - } - - /* Initialize the request's contents. */ - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestCreateDirV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_CREATE_DIR_V3); - - request->fileAttr = 0; - request->mask = HGFS_CREATE_DIR_MASK; - request->specialPerms = (mode & (S_ISUID | S_ISGID | S_ISVTX)) >> - HGFS_ATTR_SPECIAL_PERM_SHIFT; - request->ownerPerms = (mode & S_IRWXU) >> HGFS_ATTR_OWNER_PERM_SHIFT; - request->groupPerms = (mode & S_IRWXG) >> HGFS_ATTR_GROUP_PERM_SHIFT; - request->otherPerms = mode & S_IRWXO; - request->fileName.flags = 0; - request->fileName.fid = HGFS_INVALID_HANDLE; - request->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - reqBufferSize = HGFS_NAME_BUFFER_SIZET(HGFS_PACKET_MAX, reqSize); - - /* - * Convert an input string to utf8 precomposed form, convert it to - * the cross platform name format and finally unescape any illegal - * filesystem characters. - */ - ret = HgfsNameToWireEncoding(fullName, fullNameLen + 1, - request->fileName.name, - reqBufferSize); - - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL,"Could not encode to wire format"); - ret = -ret; - goto destroyOut; - } - - request->fileName.length = ret; - reqSize += ret; - - /* Set the size of this request. */ - HgfsKReq_SetPayloadSize(req, reqSize); - - /* Send the request to guestd. */ - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* Request is destroyed in HgfsSubmitRequest() if necessary. */ - goto out; - } - - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - - ret = HgfsGetStatus(req, repSize); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - goto destroyOut; - } - - ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullName, - HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable, TRUE, - mode, 0); - if (ret) { - ret = EIO; - DEBUG(VM_DEBUG_FAIL, "Error encountered creating vnode ret = %d\n", ret); - goto destroyOut; - } - - ASSERT(*vpp); - ret = 0; - -destroyOut: - HgfsKReq_ReleaseRequest(sip->reqs, req); -out: - if (fullName != NULL) { - os_free(fullName, MAXPATHLEN); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s/%.*s -> %d)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDirOpen -- - * - * Invoked when HgfsOpen() is called with a vnode of type VDIR. - * - * Sends a SEARCH_OPEN request to the Hgfs server. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer - struct vnode *vp, // IN: Vnode of directory to open - HgfsOpenType openType) // IN: type of VNOP_OPEN. -{ - char *fullPath; - uint32 fullPathLen; - int ret; - HgfsFile *fp; - HgfsHandle handle; - - ASSERT(sip); - ASSERT(vp); - - fp = HGFS_VP_TO_FP(vp); - ASSERT(fp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s,%d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - openType); - - /* - * If the directory is already opened then we are done. - * There is no different open modes for directories thus the handle is compatible. - */ - os_rw_lock_lock_exclusive(fp->handleLock); - ret = HgfsCheckAndReferenceHandle(vp, 0, openType); - if (ret == ENOENT) { // Handle is not set, need to get one from the host - - if (HGFS_IS_ROOT_VNODE(sip, vp)) { - fullPath = ""; - fullPathLen = 0; - } else { - fullPath = HGFS_VP_TO_FILENAME(vp); - fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp); - } - - ret = HgfsSendOpenDirRequest(sip, fullPath, fullPathLen, &handle); - if (ret == 0) { - /* - * We successfully received a reply, so we need to save the handle in - * this file's HgfsOpenFile and return success. - */ - HgfsSetOpenFileHandle(vp, handle, HGFS_OPEN_MODE_READ_ONLY, openType); - } - } - os_rw_lock_unlock_exclusive(fp->handleLock); - - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", - HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsRequestHostFileHandle -- - * - * Sends a open request to the server to get a file handle. - * If client needs a readonly handle the function first asks for - * read-write handle since this handle may be shared between multiple - * file descriptors. If getting read-write handle fails the function - * sends another request for readonly handle. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsRequestHostFileHandle(HgfsSuperInfo *sip, // IN: Superinfo pointer - struct vnode *vp, // IN: Vnode of file to open - int *openMode, // IN OUT: open mode - int openFlags, // IN: flags for the open request - int permissions, // IN: Permissions for new files - HgfsHandle *handle) // OUT: file handle -{ - char *fullPath; - uint32 fullPathLen; - int ret; - - fullPath = HGFS_VP_TO_FILENAME(vp); - fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s, %d, %d, %o)\n", fullPathLen, fullPath, - *openMode, openFlags, permissions); - - /* First see if we can get the most permissive read/write open mode */ - ret = HgfsSendOpenRequest(sip, HGFS_OPEN_MODE_READ_WRITE, openFlags, - permissions, fullPath, fullPathLen, handle); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "Open failed %d, re-submitting original mode = %d.\n", - ret, *openMode); - if (ret == EACCES && HGFS_OPEN_MODE_READ_WRITE != *openMode) { - /* - * Failed to open in read/write open mode because of denied access. - * It means file's permissions do not allow opening for read/write. - * However caller does not need this mode and may be satisfied with - * less permissive mode. - * Try exact open mode now. - */ - DEBUG(VM_DEBUG_FAIL, "RW mode failed, re-submitting original mode = %d.\n", - *openMode); - ret = HgfsSendOpenRequest(sip, *openMode, openFlags, - permissions, fullPath, fullPathLen, handle); - } - } else { - *openMode = HGFS_OPEN_MODE_READ_WRITE; - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", fullPathLen, fullPath, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsFileOpen -- - * - * Invoked when HgfsOpen() is called with a vnode of type VREG. Sends - * a OPEN request to the Hgfs server. - * - * Note that this function doesn't need to handle creations since the - * HgfsCreate() entry point is called by the kernel for that. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer - struct vnode *vp, // IN: Vnode of file to open - int flag, // IN: Flags of open - int permissions, // IN: Permissions of open (only when creating) - HgfsOpenType openType) // IN: initiator type for the open -{ - int ret; - int openMode; - int openFlags; - HgfsHandle handle; - HgfsFile *fp; - - ASSERT(sip); - ASSERT(vp); - - fp = HGFS_VP_TO_FP(vp); - ASSERT(fp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s,%d,%d,%o)\n", - HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - flag, openType, permissions); - - /* - * Check if the user is trying to create a new share. This check was - * mainly implemented to address the issue with Mac OS. When the user - * attempts to create a file in the root folder, the server returns ENOENT - * error code. However, Mac OS specifically checks for this case. If Mac OS asks for - * the creation of a new file and if it gets ENOENT as a return error code, - * then it assumes that the error was because of some race condition and tries it - * again. Thus, returning ENOENT to the Mac OS puts the guest kernel into infinite - * loop. In order to resolve this issue, before passing on the request to the - * server, we validate if user is attempting to create a new share. If yes, - * we return EACCES as the error code. - */ - if (HgfsAttemptToCreateShare(HGFS_VP_TO_FILENAME(vp), flag)) { - DEBUG (VM_DEBUG_LOG, "An attempt to create a new share was made.\n"); - ret = EACCES; - goto out; - } - - /* Convert FreeBSD modes to Hgfs modes */ - openMode = HgfsGetOpenMode((uint32_t)flag); - if (openMode < 0) { - DEBUG(VM_DEBUG_FAIL, "HgfsGetOpenMode failed.\n"); - ret = EINVAL; - goto out; - } - DEBUG(VM_DEBUG_COMM, "open mode is %x\n", openMode); - - /* Convert FreeBSD flags to Hgfs flags */ - openFlags = HgfsGetOpenFlags((uint32_t)flag); - if (openFlags < 0) { - DEBUG(VM_DEBUG_FAIL, "HgfsGetOpenFlags failed.\n"); - ret = EINVAL; - goto out; - } - - os_rw_lock_lock_exclusive(fp->handleLock); - /* - * If the file is already opened, verify that it is opened in a compatible mode. - * If it is true then add reference to vnode and grant the access, otherwise - * deny the access. - */ - ret = HgfsCheckAndReferenceHandle(vp, openMode, openType); - if (ret == ENOENT) { // Handle is not set, need to get one from the host - ret = HgfsRequestHostFileHandle(sip, vp, &openMode, openFlags, - permissions, &handle); - /* - * We successfully received a reply, so we need to save the handle in - * this file's HgfsOpenFile and return success. - */ - if (ret == 0) { - HgfsSetOpenFileHandle(vp, handle, openMode, openType); - } - } - - os_rw_lock_unlock_exclusive(fp->handleLock); - -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsRefreshDirHandle -- - * - * Refresh the directory HgfsHandle for the vnode. - * Needed when original handle become stale because the directory contents - * have changed either in the VM or on the remote server. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsRefreshDirHandle(struct vnode *vp, // IN: Vnode of file to open - HgfsSuperInfo *sip, // IN: Superinfo pointer - HgfsHandle *handle) // IN/OUT: handle to close and reopen -{ - int ret = 0; - HgfsFile *fp; - - ASSERT(vp); - fp = HGFS_VP_TO_FP(vp); - ASSERT(fp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - os_rw_lock_lock_exclusive(fp->handleLock); - - ASSERT(HGFS_VP_TO_VTYPE(vp) == VDIR); - - if (fp->handle != *handle) { - /* Handle has been refreshed in another thread. */ - *handle = fp->handle; - } else { - /* Close the existing handle and open a new one from the host. */ - HgfsCloseServerDirHandle(sip, *handle); - ret = HgfsGetNewHandle(vp, sip, fp, handle); - } - - os_rw_lock_unlock_exclusive(fp->handleLock); - - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsRenewHandle -- - * - * Renew the HgfsHandle for the vnode. Needed when original handle - * become stale because HGFS has been disabled and re-enabled or VM - * has been suspened and then resumed. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsRenewHandle(struct vnode *vp, // IN: Vnode of file to open - HgfsSuperInfo *sip, // IN: Superinfo pointer - HgfsHandle *handle) // IN/OUT: Pointer to the stale handle -{ - int result = 0; - HgfsFile *fp; - - ASSERT(vp); - fp = HGFS_VP_TO_FP(vp); - ASSERT(fp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - os_rw_lock_lock_exclusive(fp->handleLock); - - if (fp->handle != *handle) { - /* Handle has been refreshed in another thread. */ - *handle = fp->handle; - } else { - result = HgfsGetNewHandle(vp, sip, fp, handle); - } - os_rw_lock_unlock_exclusive(fp->handleLock); - - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - result); - return result; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsGetNewHandle -- - * - * Get a new HgfsHandle for the vnode. Needed when original handle - * become stale because HGFS has been disabled and re-enabled or VM - * has been suspened and then resumed, or folder contents have changed. - * - * NOTE: file lock must be acquired when calling this function. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsGetNewHandle(struct vnode *vp, // IN: Vnode of file to open - HgfsSuperInfo *sip, // IN: Superinfo pointer - HgfsFile *fp, // IN/OUT: file node pointer - HgfsHandle *handle) // IN OUT: Pointer to the stale handle -{ - int ret = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* Retrieve a new handle from the host. */ - if (HGFS_VP_TO_VTYPE(vp) == VREG) { - ret = HgfsRequestHostFileHandle(sip, vp, (int *)&fp->mode, - HGFS_OPEN, 0, handle); - } else if (HGFS_VP_TO_VTYPE(vp) == VDIR) { - char *fullPath = HGFS_VP_TO_FILENAME(vp); - uint32 fullPathLen = HGFS_VP_TO_FILENAME_LENGTH(vp); - - ret = HgfsSendOpenDirRequest(sip, fullPath, fullPathLen, handle); - } else { - goto out; - } - - if (ret == 0) { - fp->handle = *handle; - } - -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDirClose -- - * - * Invoked when HgfsClose() is called with a vnode of type VDIR. - * - * Sends an SEARCH_CLOSE request to the Hgfs server. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer - struct vnode *vp) // IN: Vnode of directory to close -{ - int ret = 0; - HgfsHandle handleToClose; - - ASSERT(sip); - ASSERT(vp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* - * Check to see if we should close the file handle on the host ( which happen when - * the reference count of the current handle become 0. - */ - if (HgfsReleaseOpenFileHandle(vp, OPENREQ_OPEN, &handleToClose) == 0) { - ret = HgfsCloseServerDirHandle(sip, handleToClose); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsFileClose -- - * - * Invoked when HgfsClose() is called with a vnode of type VREG. - * - * Sends a CLOSE request to the Hgfs server. - * - * Results: - * Returns zero on success and an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsFileClose(HgfsSuperInfo *sip, // IN: Superinfo pointer - struct vnode *vp, // IN: Vnode of file to close - int flags) // IN: The mode flags for the close -{ - int ret = 0; - HgfsHandle handleToClose; - - ASSERT(sip); - ASSERT(vp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s,%d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - flags); - - /* - * Check to see if we should close the file handle on the host ( which happen when - * the reference count of the current handle become 0. - */ - if (HgfsReleaseOpenFileHandle(vp, OPENREQ_OPEN, &handleToClose) == 0) { - ret = HgfsCloseServerFileHandle(sip, handleToClose); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDoRead -- - * - * Sends a single READ request to the Hgfs server and writes the contents - * into the user's buffer if successful. - * - * This function is called repeatedly by HgfsRead() with requests of size - * less than or equal to HGFS_IO_MAX. - * - * Note that we return the negative of an appropriate error code in this - * function so we can differentiate between success and failure. On success - * we need to return the number of bytes read, but FreeBSD's error codes are - * positive so we negate them before returning. If callers want to return - * these error codes to the Kernel, they will need to flip their sign. - * - * Results: - * Returns number of bytes read on success and a negative value on error. - * - * Side effects: - * On success, size bytes are written into the user's buffer. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDoRead(HgfsSuperInfo *sip, // IN: Superinfo pointer - HgfsHandle handle, // IN: Server's handle to read from - uint64_t offset, // IN: File offset to read at - uint32_t size, // IN: Number of bytes to read - struct uio *uiop) // IN: Defines user's read request -{ - HgfsKReqHandle req; - HgfsRequest *requestHeader; - HgfsReply *replyHeader; - HgfsRequestReadV3 *request; - HgfsReplyReadV3 *reply; - uint32 reqSize; - int ret; - - ASSERT(sip); - ASSERT(uiop); - ASSERT(size <= HGFS_IO_MAX); // HgfsRead() should guarantee this - - DEBUG(VM_DEBUG_ENTRY, "Enter(%u,%"FMT64"u,%u)\n", handle, offset, size); - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - ret = -ret; - goto out; - } - - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestReadV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_READ_V3); - - /* Indicate which file, where in the file, and how much to read. */ - request->file = handle; - request->offset = offset; - request->requiredSize = size; - request->reserved = 0; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - - HgfsKReq_SetPayloadSize(req, reqSize); - - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* - * We need to flip the sign of the return value to indicate error; see - * the comment in the function header. HgfsSubmitRequest() handles - * destroying the request if necessary, so we don't here. - */ - DEBUG(VM_DEBUG_FAIL, " hgfssubmitrequest failed\n"); - ret = -ret; - goto out; - } - - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - reply = (HgfsReplyReadV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader); - - ret = HgfsGetStatus(req, sizeof *replyHeader); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - if (ret != EPROTO && ret != EBADF) { - ret = EACCES; - } - ret = -ret; - goto destroyOut; - } - - /* - * Now perform checks on the actualSize. There are three cases: - * o actualSize is less than or equal to size, which indicates success - * o actualSize is zero, which indicates the end of the file (and success) - * o actualSize is greater than size, which indicates a server error - */ - if (reply->actualSize <= size) { - /* If we didn't get any data, we don't need to copy to the user. */ - if (reply->actualSize == 0) { - goto success; - } - - /* Perform the copy to the user */ - ret = uiomove(reply->payload, reply->actualSize, uiop); - if (ret) { - ret = -EIO; - goto destroyOut; - } - - /* We successfully copied the payload to the user's buffer */ - goto success; - - } else { - /* We got too much data: server error. */ - DEBUG(VM_DEBUG_FAIL, "received too much data in payload.\n"); - ret = -EPROTO; - goto destroyOut; - } - -success: - ret = reply->actualSize; -destroyOut: - HgfsKReq_ReleaseRequest(sip->reqs, req); -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%u -> %d)\n", handle, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDoWrite -- - * - * Sends a single WRITE request to the Hgfs server with the contents of - * the user's buffer. - * - * This function is called repeatedly by HgfsWrite() with requests of size - * less than or equal to HGFS_IO_MAX. - * - * Note that we return the negative of an appropriate error code in this - * function so we can differentiate between success and failure. On success - * we need to return the number of bytes written, but FreeBSD's error codes are - * positive so we negate them before returning. If callers want to return - * these error codes to the kernel, they will need to flip their sign. - * - * Results: - * Returns number of bytes written on success and a negative value on error. - * - * Side effects: - * On success, size bytes are written to the file specified by the handle. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDoWrite(HgfsSuperInfo *sip, // IN: Superinfo pointer - HgfsHandle handle, // IN: Handle representing file to write to - int ioflag, // IN: Flags for write - uint64_t offset, // IN: Where in the file to begin writing - uint32_t size, // IN: How much data to write - struct uio *uiop) // IN: Describes user's write request -{ - HgfsKReqHandle req; - HgfsRequest *requestHeader; - HgfsReply *replyHeader; - HgfsRequestWriteV3 *request; - HgfsReplyWriteV3 *reply; - uint32 reqSize; - uint32 repSize; - int ret; - - ASSERT(sip); - ASSERT(uiop); - ASSERT(size <= HGFS_IO_MAX); // HgfsWrite() guarantees this - - DEBUG(VM_DEBUG_ENTRY, "Enter(%u,%d,%"FMT64"u,%u)\n", handle, ioflag, offset, size); - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - ret = -ret; - goto out; - } - - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestWriteV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_WRITE_V3); - - request->file = handle; - request->flags = 0; - request->offset = offset; - request->requiredSize = size; - request->reserved = 0; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - - if (ioflag & IO_APPEND) { - DEBUG(VM_DEBUG_COMM, "writing in append mode.\n"); - request->flags |= HGFS_WRITE_APPEND; - } - - DEBUG(VM_DEBUG_COMM, "requesting write of %d bytes.\n", size); - - /* Copy the data the user wants to write into the payload. */ - ret = uiomove(request->payload, request->requiredSize, uiop); - if (ret) { - DEBUG(VM_DEBUG_FAIL, - "HgfsDoWrite: uiomove(9F) failed copying data from user.\n"); - ret = -EIO; - goto destroyOut; - } - - /* We subtract one so request's 'char payload[1]' member isn't double counted. */ - HgfsKReq_SetPayloadSize(req, reqSize + request->requiredSize - 1); - - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* - * As in HgfsDoRead(), we need to flip the sign of the error code - * returned by HgfsSubmitRequest(). - */ - DEBUG(VM_DEBUG_FAIL, "HgfsSubmitRequest failed.\n"); - ret = -ret; - goto out; - } - - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - - ret = HgfsGetStatus(req, sizeof *replyHeader); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - if (ret != EPROTO && ret != EBADF) { - ret = EACCES; - } - ret = -ret; - goto destroyOut; - } - - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - - if (HgfsKReq_GetPayloadSize(req) != repSize) { - DEBUG(VM_DEBUG_FAIL, - "Error: invalid size of reply on successful reply.\n"); - ret = -EPROTO; - goto destroyOut; - } - - reply = (HgfsReplyWriteV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader); - - /* The write was completed successfully, so return the amount written. */ - ret = reply->actualSize; - -destroyOut: - HgfsKReq_ReleaseRequest(sip->reqs, req); -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%u -> %d)\n", handle, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDelete -- - * - * Sends a request to delete a file or directory. - * - * Results: - * Returns 0 on success or an error code on error. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDelete(HgfsSuperInfo *sip, // IN: Superinfo - const char *filename, // IN: Full name of file to remove - HgfsOp op) // IN: Hgfs operation this delete is for -{ - HgfsKReqHandle req; - HgfsRequest *requestHeader; - HgfsReply *replyHeader; - HgfsRequestDeleteV3 *request; - HgfsReplyDeleteV3 *reply; - uint32 reqSize; - uint32 repSize; - uint32 reqBufferSize; - int ret; - - ASSERT(sip); - ASSERT(filename); - ASSERT((op == HGFS_OP_DELETE_FILE_V3) || (op == HGFS_OP_DELETE_DIR_V3)); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%s,%d)\n", filename, op); - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - goto out; - } - - /* Initialize the request's contents. */ - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestDeleteV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, op); - request->hints = 0; - request->fileName.fid = HGFS_INVALID_HANDLE; - request->fileName.flags = 0; - request->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE; - request->reserved = 0; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - reqBufferSize = HGFS_NAME_BUFFER_SIZET(HGFS_PACKET_MAX, reqSize); - - /* - * Convert an input string to utf8 precomposed form, convert it to - * the cross platform name format and finally unescape any illegal - * filesystem characters. - */ - ret = HgfsNameToWireEncoding(filename, strlen(filename) + 1, - request->fileName.name, - reqBufferSize); - - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "Could not encode to wire format"); - ret = -ret; - goto destroyOut; - } - - request->fileName.length = ret; - reqSize += ret; - - /* Set the size of our request. */ - HgfsKReq_SetPayloadSize(req, reqSize); - - DEBUG(VM_DEBUG_COMM, "deleting \"%s\"\n", filename); - - /* Submit our request to guestd. */ - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* HgfsSubmitRequest() handles destroying the request if necessary. */ - goto out; - } - - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - - ret = HgfsGetStatus(req, repSize); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - goto destroyOut; - } - -destroyOut: - HgfsKReq_ReleaseRequest(sip->reqs, req); -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%s -> %d)\n", filename, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsGetNextDirEntry -- - * - * Writes the name of the directory entry matching the handle and offset to - * nameOut. Also records the entry's type (file, directory) in type. This - * requires sending a SEARCH_READ request. - * - * Results: - * Returns zero on success and an error code on error. The done value is - * set if there are no more directory entries. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer - HgfsHandle handle, // IN: Handle for request - uint32_t offset, // IN: Offset - char *nameOut, // OUT: Location to write name - size_t nameSize, // IN : Size of nameOut - HgfsFileType *type, // OUT: Entry's type - Bool *done) // OUT: Whether there are any more -{ - HgfsKReqHandle req; - HgfsRequest *requestHeader; - HgfsReply *replyHeader; - HgfsRequestSearchReadV3 *request; - HgfsReplySearchReadV3 *reply; - HgfsDirEntry *dirent; - uint32 reqSize; - uint32 repSize; - int ret; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%u,%u)\n", handle, offset); - - ASSERT(sip); - ASSERT(nameOut); - ASSERT(done); - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - DEBUG(VM_DEBUG_FAIL, "couldn't get req.\n"); - goto out; - } - - /* - * Fill out the search read request that will return a single directory - * entry for the provided handle at the given offset. - */ - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestSearchReadV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_SEARCH_READ_V3); - - request->search = handle; - request->offset = offset; - request->flags = 0; - request->reserved = 0; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - HgfsKReq_SetPayloadSize(req, reqSize); - - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* HgfsSubmitRequest will destroy the request if necessary. */ - DEBUG(VM_DEBUG_FAIL, "HgfsSubmitRequest failed.\n"); - goto out; - } - - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - - ret = HgfsGetStatus(req, sizeof *replyHeader); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - goto destroyOut; - } - - DEBUG(VM_DEBUG_COMM, "received reply for ID %d\n", - replyHeader->id); - DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", replyHeader->status); - - reply = (HgfsReplySearchReadV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader); - reply->count = 1; - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply) + sizeof *dirent; - dirent = (HgfsDirEntry *)reply->payload; - - /* Make sure we got an entire reply (excluding filename) */ - if (HgfsKReq_GetPayloadSize(req) < repSize) { - DEBUG(VM_DEBUG_FAIL, "server didn't provide entire reply.\n"); - ret = EFAULT; - goto destroyOut; - } - - /* See if there are no more filenames to read */ - if (dirent->fileName.length <= 0) { - DEBUG(VM_DEBUG_LOG, "no more directory entries.\n"); - *done = TRUE; - ret = 0; /* return success */ - goto destroyOut; - } - - /* Make sure filename isn't too long */ - if ((dirent->fileName.length >= nameSize) || - (dirent->fileName.length > HGFS_PAYLOAD_MAX(repSize)) ) { - DEBUG(VM_DEBUG_FAIL, "filename is too long.\n"); - ret = EOVERFLOW; - goto destroyOut; - } - - /* - * Everything is all right, copy filename to caller's buffer. Note that even though - * the hgfs SearchRead reply holds lots of information about the file's attributes, - * FreeBSD directory entries do not currently need any of that information except the - * file type. - */ - memcpy(nameOut, dirent->fileName.name, dirent->fileName.length); - nameOut[dirent->fileName.length] = '\0'; - *type = dirent->attr.type; - ret = 0; - -destroyOut: - HgfsKReq_ReleaseRequest(sip->reqs, req); -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%u -> %d)\n", handle, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsReadlinkInt -- - * - * Reads a symbolic link target. - * - * Results: - * Either 0 on success or a BSD error code on failure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsReadlinkInt(struct vnode *vp, // IN : File vnode - struct uio *uiop) // OUT: Attributes from hgfs server -{ - HgfsKReqHandle req = NULL; - int ret = 0; - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - HgfsReplyGetattrV3 *reply; - HgfsReply *replyHeader; - uint32 outLength; - char* outBuffer = NULL; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp)); - - /* This operation is valid only for symbolic links. */ - if (HGFS_VP_TO_VTYPE(vp) != VLNK) { - DEBUG(VM_DEBUG_FAIL, "Must be a symbolic link.\n"); - ret = EINVAL; - goto out; - } - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - goto out; - } - - ret = HgfsQueryAttrInt(HGFS_VP_TO_FILENAME(vp), 0, sip, req); - if (ret != 0) { - DEBUG(VM_DEBUG_FAIL, "Error %d reading symlink name.\n", ret); - goto out; - } - - outLength = HGFS_UIOP_TO_RESID(uiop); - outBuffer = os_malloc(outLength, M_WAITOK); - if (outBuffer != NULL) { - DEBUG(VM_DEBUG_FAIL, "No memory for symlink name.\n"); - ret = ENOMEM; - goto out; - } - - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - reply = (HgfsReplyGetattrV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader); - if (reply->symlinkTarget.name[reply->symlinkTarget.length - 1] == '\0') { - ret = EINVAL; // Not a well formed name - goto out; - } - - ret = HgfsNameFromWireEncoding(reply->symlinkTarget.name, - reply->symlinkTarget.length, - outBuffer, outLength); - if (ret < 0) { - ret = -ret; // HgfsNameFromWireEncoding returns negative error code - DEBUG(VM_DEBUG_FAIL, "Error converting link wire format length is %d, name is %s\n", - reply->symlinkTarget.length, reply->symlinkTarget.name); - goto out; - } - - ret = uiomove(outBuffer, MIN(ret, outLength), uiop); - if (ret != 0) { - DEBUG(VM_DEBUG_FAIL, "Failed %d copying into user buffer.\n", ret); - } - -out: - if (outBuffer != NULL) { - os_free(outBuffer, outLength); - } - if (req != NULL) { - HgfsKReq_ReleaseRequest(sip->reqs, req); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d\n", HGFS_VP_TO_FILENAME_LENGTH(vp), HGFS_VP_TO_FILENAME(vp), - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsSymlnikInt -- - * - * Creates symbolic link on the host. - * - * Results: - * Either 0 on success or a BSD error code on failure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsSymlinkInt(struct vnode *dvp, // IN : directory vnode - struct vnode **vpp, // OUT: pointer to new symlink vnode - struct componentname *cnp, // IN : pathname to component - char *targetName) // IN : Symbolic link target -{ - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(dvp); - HgfsKReqHandle req = NULL; - HgfsRequest *requestHeader; - HgfsReply *replyHeader; - HgfsRequestSymlinkCreateV3 *request; - HgfsReplySymlinkCreateV3 *reply; - uint32 reqSize; - uint32 repSize; - uint32 reqBufferSize; - int ret; - char *fullName = NULL; - uint32 fullNameLen; - HgfsFileNameV3 *fileNameP; - int nameOffset; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s/%.*s,%s)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr, - targetName); - - fullName = os_malloc(MAXPATHLEN, M_WAITOK); - if (!fullName) { - ret = ENOMEM; - goto out; - } - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - goto out; - } - - ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp), // Parent directory - HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of name - cnp->cn_nameptr, // Name of file to create - cnp->cn_namelen, // Length of filename - fullName, // Buffer to write full name - MAXPATHLEN); // Size of this buffer - - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "couldn't create full path name.\n"); - ret = ENAMETOOLONG; - goto out; - } - fullNameLen = ret; - - /* Initialize the request's contents. */ - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestSymlinkCreateV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_CREATE_SYMLINK_V3); - - request->reserved = 0; - - request->symlinkName.flags = 0; - request->symlinkName.fid = HGFS_INVALID_HANDLE; - request->symlinkName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - reqBufferSize = HGFS_NAME_BUFFER_SIZET(HGFS_PACKET_MAX, reqSize); - - /* - * Convert an input string to utf8 precomposed form, convert it to - * the cross platform name format and finally unescape any illegal - * filesystem characters. - */ - ret = HgfsNameToWireEncoding(fullName, fullNameLen + 1, - request->symlinkName.name, - reqBufferSize); - - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL,"Could not encode file name to wire format"); - ret = -ret; - goto out; - } - request->symlinkName.length = ret; - reqSize += ret; - - fileNameP = (HgfsFileNameV3 *)((char*)&request->symlinkName + - sizeof request->symlinkName + - request->symlinkName.length); - fileNameP->flags = 0; - fileNameP->fid = HGFS_INVALID_HANDLE; - fileNameP->caseType = HGFS_FILE_NAME_CASE_SENSITIVE; - - /* - * Currently we have different name formats for file names and for symbolic - * link targets. Flie names are always absolute and on-wire representation does - * not include leading path separator. HgfsNameToWireEncoding removes - * leading path separator from the name. However symbolic link targets may be - * either absolute or relative. To distinguish between them the leading path separator - * must be preserved for absolute symbolic link target. - * In the long term we should fix the protocol and have only one name - * format which is suitable for all names. - * The following code compensates for this problem before there is such - * universal name representation. - */ - if (*targetName == '/') { - fileNameP->length = 1; - reqSize += 1; - *fileNameP->name = '\0'; - targetName++; - } else { - fileNameP->length = 0; - } - /* - * Convert symbolic link target to utf8 precomposed form, convert it to - * the cross platform name format and finally unescape any illegal - * filesystem characters. - */ - nameOffset = fileNameP->name - (char*)requestHeader; - ret = HgfsNameToWireEncoding(targetName, strlen(targetName) + 1, - fileNameP->name + fileNameP->length, - HGFS_PACKET_MAX - nameOffset - - fileNameP->length); - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL,"Could not encode file name to wire format"); - ret = -ret; - goto out; - } - fileNameP->length += ret; - - reqSize += ret; - - /* Set the size of this request. */ - HgfsKReq_SetPayloadSize(req, reqSize); - - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* Request is destroyed in HgfsSubmitRequest() if necessary. */ - req = NULL; - goto out; - } - - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - reply = (HgfsReplySymlinkCreateV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader); - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply); - ret = HgfsGetStatus(req, repSize); - if (ret == 0) { - ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullName, - HGFS_FILE_TYPE_SYMLINK, &sip->fileHashTable, TRUE, 0, 0); - if (ret) { - ret = EIO; - } - } else { - DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); - } - - ASSERT(ret != 0 || *vpp != NULL); - -out: - if (req) { - HgfsKReq_ReleaseRequest(sip->reqs, req); - } - if (fullName != NULL) { - os_free(fullName, MAXPATHLEN); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s/%.*s -> %d)\n", - HGFS_VP_TO_FILENAME_LENGTH(dvp), HGFS_VP_TO_FILENAME(dvp), - (int)cnp->cn_namelen, cnp->cn_nameptr, - ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDoGetattrByName -- - * - * Send a name getattr request to the hgfs server and put the result in - * hgfsAttr. - * - * Results: - * Either 0 on success or a BSD error code on failure. The hgfsAttr field - * is only filled out on success. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsDoGetattrByName(const char *path, // IN : Path to get attributes for - HgfsSuperInfo *sip, // IN : SuperInfo block of hgfs mount. - HgfsAttrV2 *hgfsAttrV2) // OUT: Attributes from hgfs server -{ - int ret; - DEBUG(VM_DEBUG_ENTRY, "Enter(%s)\n", path); - ret = HgfsDoGetattrInt(path, 0, sip, hgfsAttrV2); - DEBUG(VM_DEBUG_EXIT, "Exit(%s -> %d)\n", path, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDoGetattrByName -- - * - * Send a handle getattr request to the hgfs server and put the result in - * hgfsAttr. - * - * Results: - * Either 0 on success or a BSD error code on failure. The hgfsAttr field - * is only filled out on success. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -#if 0 -static int -HgfsDoGetattrByHandle(HgfsHandle handle, // IN : Hgfs handle for attr request - HgfsSuperInfo *sip, // IN : SuperInfo block for hgfs mount - HgfsAttrV2 *hgfsAttrV2) // OUT: Attributes from hgfs server -{ - int ret; - DEBUG(VM_DEBUG_ENTRY, "Enter(%u)\n", handle); - ret = HgfsDoGetattrInt(NULL, handle, sip, hgfsAttrV2); - DEBUG(VM_DEBUG_EXIT, "Exit(%u -> %d)\n", handle, ret); -} -#endif - -/* - *---------------------------------------------------------------------------- - * - * HgfsDoGetattrInt -- - * - * Internal function that actually sends a getattr request to the hgfs - * server and puts the results in hgfsAttrV2. This function should only - * be called by HgfsDoGetattrByName or HgfsDoGetattrByHandle and will do - * a getattr by filename if path is non-NULL. Otherwise it does a getattr by - * handle. - * - * - * Results: - * Either 0 on success or a BSD error code on failure. The hgfsAttr field - * is only filled out on success. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsDoGetattrInt(const char *path, // IN : Path to get attributes for - HgfsHandle handle, // IN : Handle to get attribues for - HgfsSuperInfo *sip, // IN : SuperInfo block for hgfs mount - HgfsAttrV2 *hgfsAttrV2) // OUT: Attributes from hgfs server -{ - HgfsKReqHandle req; - int ret = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%s,%u)\n", (path != NULL ? path : "null"), handle); - ASSERT(hgfsAttrV2); - - req = HgfsKReq_AllocateRequest(sip->reqs, &ret); - if (!req) { - return ret; - } - - ret = HgfsQueryAttrInt(path, handle, sip, req); - if (ret == 0) { - HgfsReplyGetattrV3 *reply; - HgfsReply *replyHeader; - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - reply = (HgfsReplyGetattrV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader); - - /* Fill out hgfsAttrV2 with the results from the server. */ - memcpy(hgfsAttrV2, &reply->attr, sizeof *hgfsAttrV2); - HgfsKReq_ReleaseRequest(sip->reqs, req); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%s,%u -> %d)\n", (path != NULL ? path : "null"), handle, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsQueryAttrInt -- - * - * Internal function that actually sends a getattr request to the hgfs - * server and puts the results in hgfsAttrV2. This function does - * a getattr by filename if path is non-NULL. Otherwise it does a getattr by - * handle. - * - * - * Results: - * Either 0 on success or a BSD error code on failure. When function - * succeeds a valid hgfs request is returned and it must be de-allocaed - * by the caller. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsQueryAttrInt(const char *path, // IN : Path to get attributes for - HgfsHandle handle, // IN : Handle to get attribues for - HgfsSuperInfo *sip, // IN : SuperInfo block for hgfs mount - HgfsKReqHandle req) // IN/OUT: preacllocated hgfs request -{ - HgfsRequest *requestHeader; - HgfsReply *replyHeader; - HgfsRequestGetattrV3 *request; - HgfsReplyGetattrV3 *reply; - uint32 reqSize; - uint32 repSize; - uint32 reqBufferSize; - int ret = 0; - - DEBUG(VM_DEBUG_ENTRY, "Enter(%s,%u)\n", (path != NULL ? path : "null"), handle); - requestHeader = (HgfsRequest *)HgfsKReq_GetPayload(req); - request = (HgfsRequestGetattrV3 *)HGFS_REQ_GET_PAYLOAD_V3(requestHeader); - - HGFS_INIT_REQUEST_HDR(requestHeader, req, HGFS_OP_GETATTR_V3); - request->reserved = 0; - - reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); - reqBufferSize = HGFS_NAME_BUFFER_SIZET(HGFS_PACKET_MAX, reqSize); - - /* - * Per the calling conventions of this function, if the path is NULL then - * this is a Getattr by handle. - */ - if (path == NULL) { - request->hints = HGFS_ATTR_HINT_USE_FILE_DESC; - request->fileName.fid = handle; - request->fileName.flags = HGFS_FILE_NAME_USE_FILE_DESC; - request->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE; - request->fileName.length = 0; - - } else { - /* Do a Getattr by path. */ - request->hints = 0; - request->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; - request->fileName.fid = HGFS_INVALID_HANDLE; - request->fileName.flags = 0; - - /* - * Convert an input string to utf8 precomposed form, convert it to - * the cross platform name format and finally unescape any illegal - * filesystem characters. - */ - ret = HgfsNameToWireEncoding(path, strlen(path) + 1, - request->fileName.name, - reqBufferSize); - - if (ret < 0) { - DEBUG(VM_DEBUG_FAIL, "Could not encode to wire format"); - ret = -ret; - goto destroyOut; - } - request->fileName.length = ret; - reqSize += ret; - } - - /* Packet size includes the header, request and its payload. */ - HgfsKReq_SetPayloadSize(req, reqSize); - - DEBUG(VM_DEBUG_COMM, "sending getattr request for ID %d\n", - requestHeader->id); - DEBUG(VM_DEBUG_COMM, " fileName.length: %d\n", request->fileName.length); - DEBUG(VM_DEBUG_COMM, " fileName.name: \"%s\"\n", request->fileName.name); - - /* - * Submit the request and wait for the reply. HgfsSubmitRequest handles - * destroying the request on both error and interrupt cases. - */ - ret = HgfsSubmitRequest(sip, req); - if (ret) { - /* HgfsSubmitRequest destroys the request if necessary */ - goto out; - } - - replyHeader = (HgfsReply *)HgfsKReq_GetPayload(req); - - ret = HgfsGetStatus(req, sizeof *replyHeader); - if (ret) { - if (ret == EPROTO) { - DEBUG(VM_DEBUG_FAIL, "Error encountered for ID = %d\n" - "with status %d.\n", replyHeader->id, replyHeader->status); - } - goto destroyOut; - } - - reply = (HgfsReplyGetattrV3 *)HGFS_REP_GET_PAYLOAD_V3(replyHeader); - - DEBUG(VM_DEBUG_COMM, "received reply for ID %d\n", replyHeader->id); - DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", replyHeader->status); - DEBUG(VM_DEBUG_COMM, " file type: %d\n", reply->attr.type); - DEBUG(VM_DEBUG_COMM, " file size: %llu\n", (long long unsigned)reply->attr.size); - DEBUG(VM_DEBUG_COMM, " permissions: %o\n", reply->attr.ownerPerms); - DEBUG(VM_DEBUG_COMM, " permissions: %o\n", reply->attr.groupPerms); - DEBUG(VM_DEBUG_COMM, " permissions: %o\n", reply->attr.otherPerms); - DEBUG(VM_DEBUG_COMM, " hostFileId: %llu\n", (long long unsigned)reply->attr.hostFileId); - - repSize = HGFS_REP_PAYLOAD_SIZE_V3(reply) + reply->symlinkTarget.length; - - /* The GetAttr succeeded, ensure packet contains correct amount of data. */ - if (HgfsKReq_GetPayloadSize(req) != repSize) { - DEBUG(VM_DEBUG_COMM, "HgfsLookup: invalid packet size received for \"%s\".\n", - path); - ret = EFAULT; - goto destroyOut; - } - -destroyOut: - if (ret != 0) { - HgfsKReq_ReleaseRequest(sip->reqs, req); - } - -out: - DEBUG(VM_DEBUG_EXIT, "Exit(%s,%u -> %d)\n", (path != NULL ? path : "null"), handle, ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * IsModeCompatible -- - * - * Checks if the requested mode is compatible with permissions. - * - * Results: - * Returns TRUE if the mode is compatible, FALSE otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static Bool -IsModeCompatible(HgfsAccessMode mode, // IN: Requested open mode - uint32 permissions) // IN: Effective user permissions -{ - if ((permissions & HGFS_PERM_READ) == 0) { - if ((mode & (HGFS_MODE_GENERIC_READ | - HGFS_MODE_READ_DATA | - HGFS_MODE_LIST_DIRECTORY | - HGFS_MODE_READ_ATTRIBUTES | - HGFS_MODE_READ_EXTATTRIBUTES | - HGFS_MODE_READ_SECURITY)) != 0) { - return FALSE; - } - } - - if ((permissions & HGFS_PERM_WRITE) == 0) { - if ((mode & (HGFS_MODE_GENERIC_WRITE | - HGFS_MODE_WRITE_DATA | - HGFS_MODE_APPEND_DATA | - HGFS_MODE_DELETE | - HGFS_MODE_ADD_SUBDIRECTORY | - HGFS_MODE_DELETE_CHILD | - HGFS_MODE_WRITE_ATTRIBUTES | - HGFS_MODE_WRITE_EXTATTRIBUTES | - HGFS_MODE_WRITE_SECURITY | - HGFS_MODE_TAKE_OWNERSHIP | - HGFS_MODE_ADD_FILE)) != 0) { - return FALSE; - } - } - - if ((permissions & HGFS_PERM_EXEC) == 0) { - if ((mode & (HGFS_MODE_GENERIC_EXECUTE | - HGFS_MODE_TRAVERSE_DIRECTORY)) != 0) { - return FALSE; - } - } - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsAccessInt -- - * - * Check to ensure the user has the specified type of access to the file. - * - * Results: - * Returns 0 if access is allowed and a non-zero error code otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsAccessInt(struct vnode *vp, // IN: Vnode to check access for - HgfsAccessMode mode) // IN: Access mode requested. -{ - int ret = 0; - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - HgfsAttrV2 hgfsAttrV2; - - DEBUG(VM_DEBUG_ENTRY, "HgfsAccessInt(%.*s,%d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), - HGFS_VP_TO_FILENAME(vp), mode); - - ret = HgfsDoGetattrByName(HGFS_VP_TO_FILENAME(vp), sip, &hgfsAttrV2); - if (ret == 0) { - uint32 effectivePermissions; - if (hgfsAttrV2.mask & HGFS_ATTR_VALID_EFFECTIVE_PERMS) { - effectivePermissions = hgfsAttrV2.effectivePerms; - } else { - /* - * If the server did not return actual effective permissions then - * need to calculate ourselves. However we should avoid unnecessary denial of - * access so perform optimistic permissions calculation. - * It is safe since host enforces necessary restrictions regardless of - * the client's decisions. - */ - effectivePermissions = - hgfsAttrV2.ownerPerms | hgfsAttrV2.groupPerms | hgfsAttrV2.otherPerms; - } - if (!IsModeCompatible(mode, effectivePermissions)) { - ret = EACCES; - DEBUG(VM_DEBUG_FAIL, "HgfsAccessInt denied access: %s (%d, %d)\n", - HGFS_VP_TO_FILENAME(vp), mode, effectivePermissions); - } - } else { - DEBUG(VM_DEBUG_FAIL, "HgfsAccessInt failed getting attrib: %s (%d)\n", - HGFS_VP_TO_FILENAME(vp), ret); - } - - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), - HGFS_VP_TO_FILENAME(vp), ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsMmapInt -- - * - * HgfsMmapInt is invoked invoked from HgfsVnopMmap to verify parameters - * and mark vnode as mmapped if necessary. - * - * Results: - * Zero on success or non-zero error code otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsMmapInt(struct vnode *vp, - int accessMode) -{ - int ret; - HgfsFile *fp; - - ASSERT(vp); - fp = HGFS_VP_TO_FP(vp); - - ASSERT(fp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s,%d)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), - HGFS_VP_TO_FILENAME(vp), accessMode); - - /* - * If the directory is already opened then we are done. - * There is no different open modes for directories thus the handle is compatible. - */ - os_rw_lock_lock_exclusive(fp->handleLock); - - ret = HgfsCheckAndReferenceHandle(vp, accessMode, OPENREQ_MMAP); - os_rw_lock_unlock_exclusive(fp->handleLock); - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d).\n", HGFS_VP_TO_FILENAME_LENGTH(vp), - HGFS_VP_TO_FILENAME(vp), ret); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsMnomapInt -- - * - * HgfsMnomapInt is invoked invoked from HgfsVnopNomap to tear down memory - * mapping and dereference file handle. - * - * Results: - * Zero on success or non-zero error code otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsMnomapInt(struct vnode *vp) -{ - int ret = 0; - HgfsHandle handleToClose; - HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - - ASSERT(vp); - - DEBUG(VM_DEBUG_ENTRY, "Enter(%.*s)\n", HGFS_VP_TO_FILENAME_LENGTH(vp), - HGFS_VP_TO_FILENAME(vp)); - - /* - * Check to see if we should close the file handle on the host, which happen when - * the reference count of the current handle become 0. - */ - if (HgfsReleaseOpenFileHandle(vp, OPENREQ_MMAP, &handleToClose) == 0) { - ret = HgfsCloseServerFileHandle(sip, handleToClose); - } - DEBUG(VM_DEBUG_EXIT, "Exit(%.*s -> %d).\n", HGFS_VP_TO_FILENAME_LENGTH(vp), - HGFS_VP_TO_FILENAME(vp), ret); - return ret; -} |